txgh-server 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b61f30ae875a1c12d037ce7123cece67a1bd4a12
4
- data.tar.gz: 35536bb56e5cee37208c0c8a8f64e60347a0f88a
3
+ metadata.gz: 0a618f6c8f91abd2996e3e7589eeee8d644703f9
4
+ data.tar.gz: db946191d261517d34c339aa98424e46a72d0b0e
5
5
  SHA512:
6
- metadata.gz: 5d1655be5e922b9e6157bbc4791ca9dddddc8316df4459d4d3418e127076c2b8933de70dd43de9caab17a6d08a4e978fbde5455425ff43c62c3a832412caa3c0
7
- data.tar.gz: 1f6ebab2ba51bf04f9e49fb6d03afcb7a7faef63492bcd6a1d84672b7aa5a6a02b5cf43ddbc085ee39338106065ecac198fc4f862c5d97636ab2f5370d72d01b
6
+ metadata.gz: d09779408d47e28a776cbd8dc4e2ac36c1f9f56a9d2288f0beffddfc9f6ac3d84897b20dd968e5569506a47df5340e227adc499d888753a5ffcb9ff26e5ae33d
7
+ data.tar.gz: 38494763dc17346acb93efab18249efb1f9be9b03416b2ab1d8177fb6465c962a7b05e8541a4532af4bc785371f343a65d2a07f6362417be27157dba9ad4781c
@@ -1,3 +1,3 @@
1
1
  module TxghServer
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -0,0 +1,50 @@
1
+ module TxghServer
2
+ module Webhooks
3
+ module Github
4
+ class DeleteAttributes
5
+ ATTRIBUTES = [
6
+ :repo_name, :ref, :ref_type
7
+ ]
8
+
9
+ class << self
10
+ def from_webhook_payload(payload)
11
+ new(
12
+ ATTRIBUTES.each_with_object({}) do |attr, ret|
13
+ ret[attr] = public_send(attr, payload)
14
+ end
15
+ )
16
+ end
17
+
18
+ def repo_name(payload)
19
+ payload.fetch('repository').fetch('full_name')
20
+ end
21
+
22
+ def ref(payload)
23
+ payload.fetch('ref')
24
+ end
25
+
26
+ def ref_type(payload)
27
+ payload.fetch('ref_type')
28
+ end
29
+ end
30
+
31
+ attr_reader *ATTRIBUTES
32
+
33
+ def initialize(options = {})
34
+ ATTRIBUTES.each do |attr|
35
+ instance_variable_set(
36
+ "@#{attr}", options.fetch(attr) { options.fetch(attr.to_s) }
37
+ )
38
+ end
39
+ end
40
+
41
+ def to_h
42
+ ATTRIBUTES.each_with_object({}) do |attr, ret|
43
+ ret[attr] = public_send(attr)
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,9 +1,17 @@
1
1
  module TxghServer
2
2
  module Webhooks
3
3
  module Github
4
- class DeleteHandler < Handler
4
+ class DeleteHandler
5
+ include ResponseHelpers
5
6
 
6
- include Txgh::CategorySupport
7
+ attr_reader :project, :repo, :logger, :attributes
8
+
9
+ def initialize(project, repo, logger, attributes)
10
+ @project = project
11
+ @repo = repo
12
+ @logger = logger
13
+ @attributes = attributes
14
+ end
7
15
 
8
16
  def execute
9
17
  perform_delete if should_handle_request?
@@ -22,13 +30,13 @@ module TxghServer
22
30
 
23
31
  def should_handle_request?
24
32
  # ref_type can be either 'branch' or 'tag' - we only care about branches
25
- payload['ref_type'] == 'branch' &&
33
+ attributes.ref_type == 'branch' &&
26
34
  repo.should_process_ref?(branch) &&
27
35
  project.auto_delete_resources?
28
36
  end
29
37
 
30
38
  def branch
31
- Txgh::Utils.absolute_branch(payload['ref'].sub(/^refs\//, ''))
39
+ Txgh::Utils.absolute_branch(attributes.ref.sub(/^refs\//, ''))
32
40
  end
33
41
 
34
42
  end
@@ -6,7 +6,10 @@ module TxghServer
6
6
  class PingHandler
7
7
  include ResponseHelpers
8
8
 
9
- def initialize(options = {})
9
+ attr_reader :logger
10
+
11
+ def initialize(logger)
12
+ @logger = logger
10
13
  end
11
14
 
12
15
  def execute
@@ -0,0 +1,77 @@
1
+ require 'set'
2
+
3
+ module TxghServer
4
+ module Webhooks
5
+ module Github
6
+ class PushAttributes
7
+ ATTRIBUTES = [
8
+ :repo_name, :ref, :before, :after,
9
+ :added_files, :modified_files, :author
10
+ ]
11
+
12
+ class << self
13
+ def from_webhook_payload(payload)
14
+ new(
15
+ ATTRIBUTES.each_with_object({}) do |attr, ret|
16
+ ret[attr] = public_send(attr, payload)
17
+ end
18
+ )
19
+ end
20
+
21
+ def repo_name(payload)
22
+ payload.fetch('repository').fetch('full_name')
23
+ end
24
+
25
+ def ref(payload)
26
+ payload.fetch('ref')
27
+ end
28
+
29
+ def before(payload)
30
+ payload.fetch('before')
31
+ end
32
+
33
+ def after(payload)
34
+ payload.fetch('after')
35
+ end
36
+
37
+ def added_files(payload)
38
+ extract_files(payload, 'added')
39
+ end
40
+
41
+ def modified_files(payload)
42
+ extract_files(payload, 'modified')
43
+ end
44
+
45
+ def author(payload)
46
+ payload.fetch('head_commit').fetch('committer').fetch('name')
47
+ end
48
+
49
+ def extract_files(payload, state)
50
+ payload.fetch('commits').flat_map { |c| c[state] }.uniq
51
+ end
52
+ end
53
+
54
+ def files
55
+ @files ||= added_files + modified_files
56
+ end
57
+
58
+ attr_reader *ATTRIBUTES
59
+
60
+ def initialize(options = {})
61
+ ATTRIBUTES.each do |attr|
62
+ instance_variable_set(
63
+ "@#{attr}", options.fetch(attr) { options.fetch(attr.to_s) }
64
+ )
65
+ end
66
+ end
67
+
68
+ def to_h
69
+ ATTRIBUTES.each_with_object({}) do |attr, ret|
70
+ ret[attr] = public_send(attr)
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,10 +1,19 @@
1
- require 'base64'
2
1
  require 'set'
3
2
 
4
3
  module TxghServer
5
4
  module Webhooks
6
5
  module Github
7
- class PushHandler < Handler
6
+ class PushHandler
7
+ include ResponseHelpers
8
+
9
+ attr_reader :project, :repo, :logger, :attributes
10
+
11
+ def initialize(project, repo, logger, attributes)
12
+ @project = project
13
+ @repo = repo
14
+ @logger = logger
15
+ @attributes = attributes
16
+ end
8
17
 
9
18
  def execute
10
19
  # Check if the branch in the hook data is the configured branch we want
@@ -14,31 +23,11 @@ module TxghServer
14
23
  if should_process?
15
24
  logger.info('found branch in github request')
16
25
 
17
- tx_resources = tx_resources_for(branch)
18
-
19
- modified_resources = added_and_modified_resources_for(tx_resources)
20
- modified_resources += l10n_resources_for(tx_resources)
21
-
22
- if repo.github_config_branch.include?('tags/')
23
- modified_resources += tag_resources_for(tx_resources)
24
- end
25
-
26
- # Handle DBZ 'L10N' special case
27
- if branch.include?("L10N")
28
- logger.info('processing L10N tag')
29
-
30
- # Create a new branch off tag commit
31
- if branch.include?('tags/L10N')
32
- repo.api.create_ref(repo.name, 'heads/L10N', payload['head_commit']['id'])
33
- end
34
- end
35
-
36
26
  updater = Txgh::ResourceUpdater.new(project, repo, logger)
37
- categories = { 'author' => payload['head_commit']['committer']['name'] }
38
- ref = repo.api.get_ref(repo.name, branch)
27
+ categories = { 'author' => attributes.author }
39
28
 
40
- modified_resources.each do |resource|
41
- updater.update_resource(resource, ref[:object][:sha], categories)
29
+ added_and_modified_resources.each do |resource|
30
+ updater.update_resource(resource, categories)
42
31
  end
43
32
  end
44
33
 
@@ -47,47 +36,20 @@ module TxghServer
47
36
 
48
37
  private
49
38
 
50
- def tag_resources_for(tx_resources)
51
- payload['head_commit']['modified'].each_with_object(Set.new) do |modified, ret|
52
- logger.info("processing modified file: #{modified}")
53
-
54
- if tx_resources.include?(modified)
55
- ret << tx_resources[modified]
56
- end
57
- end
58
- end
59
-
60
- def l10n_resources_for(tx_resources)
61
- payload['head_commit']['modified'].each_with_object(Set.new) do |modified, ret|
62
- if tx_resources.include?(modified)
63
- logger.info("setting new resource: #{tx_resources[modified].L10N_resource_slug}")
64
- ret << tx_resources[modified]
65
- end
66
- end
67
- end
68
-
69
39
  # finds the resources that were updated in each commit
70
- def added_and_modified_resources_for(tx_resources)
71
- payload['commits'].each_with_object(Set.new) do |commit, ret|
72
- logger.info('processing commit')
40
+ def added_and_modified_resources
41
+ attributes.files.each_with_object(Set.new) do |file, ret|
42
+ logger.info("processing added/modified file: #{file}")
73
43
 
74
- (commit['modified'] + commit['added']).each do |file|
75
- logger.info("processing added/modified file: #{file}")
76
-
77
- if tx_resources.include?(file)
78
- ret << tx_resources[file]
79
- end
44
+ if tx_resources.include?(file)
45
+ ret << tx_resources[file]
80
46
  end
81
47
  end
82
48
  end
83
49
 
84
50
  # Build an index of known Tx resources, by source file
85
- def tx_resources_for(branch)
86
- tx_config.resources.each_with_object({}) do |resource, ret|
87
- logger.info('processing resource')
88
-
89
- # If we're processing by branch, create a branch resource. Otherwise,
90
- # use the original resource.
51
+ def tx_resources
52
+ @tx_resources ||= tx_config.resources.each_with_object({}) do |resource, ret|
91
53
  ret[resource.source_file] = if repo.process_all_branches?
92
54
  Txgh::TxBranchResource.new(resource, branch) # maybe find instead?
93
55
  else
@@ -101,7 +63,7 @@ module TxghServer
101
63
  end
102
64
 
103
65
  def branch
104
- @ref ||= payload['ref'].sub(/^refs\//, '')
66
+ @ref ||= attributes.ref.sub(/^refs\//, '')
105
67
  end
106
68
 
107
69
  def should_process?
@@ -115,7 +77,7 @@ module TxghServer
115
77
  def should_process_commit?
116
78
  # return false if 'after' commit sha is all zeroes (indicates branch
117
79
  # has been deleted)
118
- !(payload.fetch('after', '') =~ /\A0+\z/)
80
+ !((attributes.after || '') =~ /\A0+\z/)
119
81
  end
120
82
 
121
83
  end
@@ -4,48 +4,14 @@ module TxghServer
4
4
  module Webhooks
5
5
  module Github
6
6
  class RequestHandler
7
- class << self
8
-
9
- include ResponseHelpers
7
+ include ResponseHelpers
10
8
 
9
+ class << self
11
10
  def handle_request(request, logger)
12
- case request.env['HTTP_X_GITHUB_EVENT']
13
- when 'push'
14
- handle_push(request, logger)
15
- when 'delete'
16
- handle_delete(request, logger)
17
- when 'ping'
18
- handle_ping(request, logger)
19
- else
20
- handle_unexpected
21
- end
11
+ new(request, logger).handle_request
22
12
  end
23
-
24
- private
25
-
26
- def handle_push(request, logger)
27
- klass = TxghServer::Webhooks::Github::PushHandler
28
- new(request, logger).handle(klass)
29
- end
30
-
31
- def handle_delete(request, logger)
32
- klass = TxghServer::Webhooks::Github::DeleteHandler
33
- new(request, logger).handle(klass)
34
- end
35
-
36
- def handle_ping(request, logger)
37
- klass = TxghServer::Webhooks::Github::PingHandler
38
- new(request, logger).handle(klass)
39
- end
40
-
41
- def handle_unexpected
42
- respond_with_error(400, 'Unexpected event type')
43
- end
44
-
45
13
  end
46
14
 
47
- include ResponseHelpers
48
-
49
15
  attr_reader :request, :logger
50
16
 
51
17
  def initialize(request, logger)
@@ -53,16 +19,18 @@ module TxghServer
53
19
  @logger = logger
54
20
  end
55
21
 
56
- def handle(klass)
22
+ def handle_request
57
23
  handle_safely do
58
- handler = klass.new(
59
- project: config.transifex_project,
60
- repo: config.github_repo,
61
- payload: payload,
62
- logger: logger
63
- )
64
-
65
- handler.execute
24
+ case request.env['HTTP_X_GITHUB_EVENT']
25
+ when 'push'
26
+ handle_push
27
+ when 'delete'
28
+ handle_delete
29
+ when 'ping'
30
+ handle_ping
31
+ else
32
+ handle_unexpected
33
+ end
66
34
  end
67
35
  end
68
36
 
@@ -78,16 +46,38 @@ module TxghServer
78
46
  respond_with_error(500, "Internal server error: #{e.message}", e)
79
47
  end
80
48
 
49
+ def handle_push
50
+ attributes = PushAttributes.from_webhook_payload(payload)
51
+ PushHandler.new(project, repo, logger, attributes).execute
52
+ end
53
+
54
+ def handle_delete
55
+ attributes = DeleteAttributes.from_webhook_payload(payload)
56
+ DeleteHandler.new(project, repo, logger, attributes).execute
57
+ end
58
+
59
+ def handle_ping
60
+ PingHandler.new(logger).execute
61
+ end
62
+
63
+ def handle_unexpected
64
+ respond_with_error(400, 'Unexpected event type')
65
+ end
66
+
81
67
  def payload
82
- @payload ||= if request.params[:payload]
83
- JSON.parse(request.params[:payload])
84
- else
85
- JSON.parse(request.body.read)
68
+ @payload ||= begin
69
+ if request.params[:payload]
70
+ JSON.parse(request.params[:payload])
71
+ else
72
+ JSON.parse(request.body.read)
73
+ end
74
+ rescue JSON::ParserError
75
+ {}
86
76
  end
87
77
  end
88
78
 
89
79
  def github_repo_name
90
- payload['repository']['full_name']
80
+ payload.fetch('repository', {})['full_name']
91
81
  end
92
82
 
93
83
  def config
@@ -98,6 +88,10 @@ module TxghServer
98
88
  config.github_repo
99
89
  end
100
90
 
91
+ def project
92
+ config.transifex_project
93
+ end
94
+
101
95
  def authentic_request?
102
96
  if repo.webhook_protected?
103
97
  GithubRequestAuth.authentic_request?(
@@ -1,11 +1,13 @@
1
1
  module TxghServer
2
2
  module Webhooks
3
3
  module Github
4
- autoload :DeleteHandler, 'txgh-server/webhooks/github/delete_handler'
5
- autoload :Handler, 'txgh-server/webhooks/github/handler'
6
- autoload :PingHandler, 'txgh-server/webhooks/github/ping_handler'
7
- autoload :PushHandler, 'txgh-server/webhooks/github/push_handler'
8
- autoload :RequestHandler, 'txgh-server/webhooks/github/request_handler'
4
+ autoload :DeleteAttributes, 'txgh-server/webhooks/github/delete_attributes'
5
+ autoload :DeleteHandler, 'txgh-server/webhooks/github/delete_handler'
6
+ autoload :Handler, 'txgh-server/webhooks/github/handler'
7
+ autoload :PingHandler, 'txgh-server/webhooks/github/ping_handler'
8
+ autoload :PushHandler, 'txgh-server/webhooks/github/push_handler'
9
+ autoload :PushAttributes, 'txgh-server/webhooks/github/push_attributes'
10
+ autoload :RequestHandler, 'txgh-server/webhooks/github/request_handler'
9
11
  end
10
12
  end
11
13
  end
@@ -55,7 +55,7 @@ module TxghServer
55
55
 
56
56
  def tx_config
57
57
  @tx_config ||= Txgh::Config::TxManager.tx_config(project, repo, branch)
58
- rescue ConfigNotFoundError, TxghError
58
+ rescue Txgh::ConfigNotFoundError, Txgh::TxghError
59
59
  nil
60
60
  end
61
61
 
@@ -185,9 +185,9 @@ describe TxghServer::WebhookEndpoints do
185
185
 
186
186
  before(:each) do
187
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)
188
+ receive(:new) do |project, repo, logger, attributes|
189
+ expect(project.name).to eq(project_name)
190
+ expect(repo.name).to eq(repo_name)
191
191
  handler
192
192
  end
193
193
  )
@@ -199,6 +199,7 @@ describe TxghServer::WebhookEndpoints do
199
199
  )
200
200
 
201
201
  payload = GithubPayloadBuilder.push_payload(repo_name, ref)
202
+ payload.add_commit
202
203
 
203
204
  sign_with payload.to_json
204
205
  header 'X-GitHub-Event', 'push'
@@ -217,8 +218,11 @@ describe TxghServer::WebhookEndpoints do
217
218
  end
218
219
 
219
220
  it 'returns invalid request if event unrecognized' do
221
+ payload = GithubPayloadBuilder.push_payload(repo_name, ref)
222
+
223
+ sign_with payload.to_json
220
224
  header 'X-GitHub-Event', 'foobar'
221
- post '/github'
225
+ post '/github', payload.to_json
222
226
 
223
227
  expect(last_response.status).to eq(400)
224
228
  end
@@ -8,7 +8,7 @@ require 'helpers/integration_setup'
8
8
  require 'uri'
9
9
  require 'yaml'
10
10
 
11
- include Txgh
11
+ include TxghServer
12
12
 
13
13
  describe 'hook integration tests', integration: true do
14
14
  include Rack::Test::Methods
@@ -83,10 +83,6 @@ describe 'hook integration tests', integration: true do
83
83
  File.read(payload_path.join('github_postbody_release.json'))
84
84
  end
85
85
 
86
- let(:github_postbody_l10n) do
87
- File.read(payload_path.join('github_postbody_l10n.json'))
88
- end
89
-
90
86
  let(:project_name) { 'test-project-88' }
91
87
  let(:repo_name) { 'txgh-bot/txgh-test-resources' }
92
88
 
@@ -146,14 +142,4 @@ describe 'hook integration tests', integration: true do
146
142
  expect(last_response).to be_ok
147
143
  end
148
144
  end
149
-
150
- it 'verifies the github l10n hook endpoint works' do
151
- VCR.use_cassette('github_l10n_hook_endpoint') do
152
- sign_github_request(github_postbody_l10n)
153
- header 'X-GitHub-Event', 'push'
154
- header 'content-type', 'application/x-www-form-urlencoded'
155
- post '/github', github_postbody_l10n
156
- expect(last_response).to be_ok
157
- end
158
- end
159
145
  end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'helpers/github_payload_builder'
3
+
4
+ include TxghServer
5
+ include TxghServer::Webhooks::Github
6
+
7
+ describe DeleteAttributes do
8
+ let(:repo_name) { 'my_repo' }
9
+ let(:ref) { 'heads/my_ref' }
10
+
11
+ let(:payload) do
12
+ GithubPayloadBuilder.delete_payload(repo_name, ref)
13
+ end
14
+
15
+ describe '#from_webhook_payload' do
16
+ let(:attributes) { DeleteAttributes.from_webhook_payload(payload.to_h) }
17
+
18
+ it 'pulls out repo name' do
19
+ expect(attributes.repo_name).to eq(repo_name)
20
+ end
21
+
22
+ it 'pulls out ref' do
23
+ expect(attributes.ref).to eq("refs/#{ref}")
24
+ end
25
+
26
+ it 'pulls out ref type' do
27
+ expect(attributes.ref_type).to eq('branch')
28
+ end
29
+ end
30
+ end
@@ -9,12 +9,11 @@ describe DeleteHandler do
9
9
  include StandardTxghSetup
10
10
 
11
11
  let(:handler) do
12
- DeleteHandler.new(
13
- project: transifex_project,
14
- repo: github_repo,
15
- payload: payload.to_h,
16
- logger: logger
17
- )
12
+ DeleteHandler.new(transifex_project, github_repo, logger, attributes)
13
+ end
14
+
15
+ let(:attributes) do
16
+ DeleteAttributes.from_webhook_payload(payload.to_h)
18
17
  end
19
18
 
20
19
  let(:payload) do
@@ -1,11 +1,12 @@
1
1
  require 'spec_helper'
2
+ require 'helpers/nil_logger'
2
3
 
3
4
  include TxghServer
4
5
  include TxghServer::Webhooks::Github
5
6
 
6
7
  describe PingHandler do
7
8
  let(:handler) do
8
- PingHandler.new
9
+ PingHandler.new(NilLogger.new)
9
10
  end
10
11
 
11
12
  it 'responds with a 200 success' do
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'helpers/github_payload_builder'
3
+
4
+ include TxghServer
5
+ include TxghServer::Webhooks::Github
6
+
7
+ describe PushAttributes do
8
+ let(:repo_name) { 'my_repo' }
9
+ let(:ref) { 'heads/my_ref' }
10
+ let(:added) { ['added_file.txt'] }
11
+ let(:modified) { ['modified_file.txt'] }
12
+
13
+ let(:payload) do
14
+ GithubPayloadBuilder.push_payload(repo_name, ref).tap do |payload|
15
+ payload.add_commit(added: added, modified: modified)
16
+ end
17
+ end
18
+
19
+ describe '#from_webhook_payload' do
20
+ let(:attributes) { PushAttributes.from_webhook_payload(payload.to_h) }
21
+
22
+ it 'pulls out repo name' do
23
+ expect(attributes.repo_name).to eq(repo_name)
24
+ end
25
+
26
+ it 'pulls out ref' do
27
+ expect(attributes.ref).to eq("refs/#{ref}")
28
+ end
29
+
30
+ it 'pulls out before sha' do
31
+ expect(attributes.before).to eq(payload.to_h['before'])
32
+ end
33
+
34
+ it 'pulls out after sha' do
35
+ expect(attributes.after).to eq(payload.to_h['after'])
36
+ end
37
+
38
+ it 'pulls out added files' do
39
+ expect(attributes.added_files.to_a).to eq(added)
40
+ end
41
+
42
+ it 'pulls out modified files' do
43
+ expect(attributes.modified_files.to_a).to eq(modified)
44
+ end
45
+
46
+ it 'combines all files into one handy array' do
47
+ expect(attributes.files.sort).to eq((added + modified).sort)
48
+ end
49
+
50
+ it 'pulls out the author' do
51
+ expect(attributes.author).to eq('Test User')
52
+ end
53
+ end
54
+ end