cyclid 0.2.1 → 0.2.2

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.
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2016 Liqwyd Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'octokit'
17
+
18
+ # Top level module for the core Cyclid code.
19
+ module Cyclid
20
+ # Module for the Cyclid API
21
+ module API
22
+ # Module for Cyclid Plugins
23
+ module Plugins
24
+ # Container for the Sinatra related controllers modules
25
+ module ApiExtension
26
+ # Github plugin method callbacks
27
+ module GithubMethods
28
+ # Handle a Pull Request event
29
+ module PullRequest
30
+ # Handle a Github Pull Request event
31
+ def event_pull_request(config)
32
+ # Safely load the JSON event data
33
+ @payload = parse_request_body
34
+ Cyclid.logger.debug "hook payload=#{@payload.inspect}"
35
+
36
+ # Do we know what to do with this action?
37
+ action = @payload['action'] || nil
38
+
39
+ Cyclid.logger.debug "action=#{action}"
40
+ return true unless action == 'opened' \
41
+ or action == 'reopened' \
42
+ or action == 'synchronize'
43
+
44
+ # Get the list of files in the root of the repository in the
45
+ # Pull Request branch
46
+ clone_url = URI(pr_clone_url)
47
+
48
+ # Get the authentication key
49
+ auth_token = find_oauth_token(config, clone_url)
50
+
51
+ return_failure(400, "can not find a valid OAuth token for #{clone_url}") \
52
+ if auth_token.nil?
53
+
54
+ # Create an Octokit client
55
+ @client = Octokit::Client.new(access_token: auth_token)
56
+
57
+ # Set the PR to 'pending'
58
+ @client.create_status(pr_repository, pr_sha, 'pending',
59
+ context: 'Cyclid', description: 'Preparing build')
60
+
61
+ # Get the Pull Request
62
+ tree = @client.tree(pr_repository, pr_sha, recursive: false)
63
+ Cyclid.logger.debug "tree=#{tree.to_hash}"
64
+
65
+ # Find the Cyclid job file (if it exists)
66
+ job_sha, job_type = find_job_file(tree)
67
+ Cyclid.logger.debug "job_sha=#{job_sha}"
68
+
69
+ if job_sha.nil?
70
+ @client.create_status(pr_repository, pr_sha, 'error',
71
+ context: 'Cyclid', description: 'No Cyclid job file found')
72
+
73
+ return_failure(400, 'not a Cyclid repository')
74
+ end
75
+
76
+ # Get the job file
77
+ begin
78
+ job_definition = load_job_file(pr_repository, job_sha, job_type)
79
+
80
+ # Insert this repository & branch into the sources
81
+ #
82
+ # XXX Could this cause collisions between the existing sources in
83
+ # the job definition? Not entirely sure what the workflow will
84
+ # look like.
85
+ job_sources = job_definition['sources'] || []
86
+ job_sources << { 'type' => 'git',
87
+ 'url' => clone_url.to_s,
88
+ 'branch' => pr_ref,
89
+ 'token' => auth_token }
90
+ job_definition['sources'] = job_sources
91
+
92
+ Cyclid.logger.debug "sources=#{job_definition['sources']}"
93
+ rescue StandardError => ex
94
+ Cyclid.logger.error "failed to retrieve Github Pull Request job: #{ex}"
95
+
96
+ @client.create_status(pr_repository, pr_sha, 'error',
97
+ context: 'Cyclid',
98
+ description: "Couldn't retrieve Cyclid job file")
99
+ return_failure(400, 'not a Cyclid repository')
100
+ end
101
+
102
+ Cyclid.logger.debug "job_definition=#{job_definition}"
103
+
104
+ begin
105
+ # Retrieve the plugin configuration
106
+ plugins_config = Cyclid.config.plugins
107
+ github_config = load_github_config(plugins_config)
108
+
109
+ ui_url = github_config[:ui_url]
110
+ linkback_url = "#{ui_url}/#{organization_name}"
111
+
112
+ # Inject some useful context data
113
+ ctx = { gh_event: 'pull_request',
114
+ gh_user: pull_request['user']['login'],
115
+ gh_ref: pr_ref,
116
+ gh_comment: pull_request['body'] }
117
+
118
+ callback = GithubCallback.new(auth_token, pr_repository, pr_sha, linkback_url)
119
+ job_from_definition(job_definition, callback, ctx)
120
+ rescue StandardError
121
+ @client.create_status(pr_repository, pr_sha, 'error',
122
+ context: 'Cyclid', description: 'An unknown error occurred')
123
+
124
+ return_failure(500, 'job failed')
125
+ end
126
+
127
+ return true
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2016 Liqwyd Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'octokit'
17
+
18
+ # Top level module for the core Cyclid code.
19
+ module Cyclid
20
+ # Module for the Cyclid API
21
+ module API
22
+ # Module for Cyclid Plugins
23
+ module Plugins
24
+ # Container for the Sinatra related controllers modules
25
+ module ApiExtension
26
+ # Github plugin method callbacks
27
+ module GithubMethods
28
+ # Handle a Pull Request event
29
+ module Push
30
+ # Handle a Github Push event
31
+ def event_push(config)
32
+ # Safely load the JSON event data
33
+ @payload = parse_request_body
34
+ Cyclid.logger.debug "hook payload=#{@payload.inspect}"
35
+
36
+ # Get the list of files in the root of the repository in the
37
+ # push branch
38
+ clone_url = URI(push_clone_url)
39
+
40
+ # Get the authentication key
41
+ auth_token = find_oauth_token(config, clone_url)
42
+
43
+ return_failure(400, "can not find a valid OAuth token for #{clone_url}") \
44
+ if auth_token.nil?
45
+
46
+ # Create an Octokit client
47
+ @client = Octokit::Client.new(access_token: auth_token)
48
+
49
+ # Get the push head
50
+ tree = @client.tree(push_repository, push_sha, recursive: false)
51
+ Cyclid.logger.debug "tree=#{tree.to_hash}"
52
+
53
+ # Find the Cyclid job file (if it exists)
54
+ job_sha, job_type = find_job_file(tree)
55
+ Cyclid.logger.debug "job_sha=#{job_sha}"
56
+
57
+ if job_sha.nil?
58
+ @client.create_status(push_repository, push_sha, 'error',
59
+ context: 'Cyclid', description: 'No Cyclid job file found')
60
+ return_failure(400, 'not a Cyclid repository')
61
+ end
62
+
63
+ # Get the job file
64
+ begin
65
+ job_definition = load_job_file(push_repository, job_sha, job_type)
66
+
67
+ # Insert this repository & branch into the sources
68
+ #
69
+ # XXX Could this cause collisions between the existing sources in
70
+ # the job definition? Not entirely sure what the workflow will
71
+ # look like.
72
+ job_sources = job_definition['sources'] || []
73
+ job_sources << { 'type' => 'git',
74
+ 'url' => clone_url.to_s,
75
+ 'branch' => push_ref,
76
+ 'token' => auth_token }
77
+ job_definition['sources'] = job_sources
78
+
79
+ Cyclid.logger.debug "sources=#{job_definition['sources']}"
80
+ rescue StandardError => ex
81
+ Cyclid.logger.error "failed to retrieve Github Push job: #{ex}"
82
+
83
+ @client.create_status(push_repository, push_sha, 'error',
84
+ context: 'Cyclid',
85
+ description: "Couldn't retrieve Cyclid job file")
86
+ return_failure(400, 'not a Cyclid repository')
87
+ end
88
+
89
+ Cyclid.logger.debug "job_definition=#{job_definition}"
90
+
91
+ begin
92
+ # Retrieve the plugin configuration
93
+ plugins_config = Cyclid.config.plugins
94
+ github_config = load_github_config(plugins_config)
95
+
96
+ ui_url = github_config[:ui_url]
97
+ linkback_url = "#{ui_url}/#{organization_name}"
98
+
99
+ # Inject some useful context data
100
+ ctx = { gh_event: 'push',
101
+ gh_user: @payload['sender']['login'],
102
+ gh_ref: push_ref,
103
+ gh_comment: push_head_commit['message'] }
104
+
105
+ callback = GithubCallback.new(auth_token, push_repository, push_sha, linkback_url)
106
+ job_from_definition(job_definition, callback, ctx)
107
+ rescue StandardError
108
+ @client.create_status(push_repository, push_sha, 'error',
109
+ context: 'Cyclid', description: 'An unknown error occurred')
110
+
111
+ return_failure(500, 'job failed')
112
+ end
113
+
114
+ return true
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -103,7 +103,7 @@ module Cyclid
103
103
  @job_record.save!
104
104
 
105
105
  # Ping the callback status_changed hook, if required
106
- @callback.status_changed(@job_id, status) if @callback
106
+ @callback&.status_changed(@job_id, status)
107
107
  end
108
108
 
109
109
  # Set the JobRecord ended
@@ -114,7 +114,7 @@ module Cyclid
114
114
 
115
115
  # Ping the callback completion hook, if required
116
116
  def completion(success)
117
- @callback.completion(@job_id, success) if @callback
117
+ @callback&.completion(@job_id, success)
118
118
  end
119
119
 
120
120
  # Write data to the log buffer
@@ -122,7 +122,7 @@ module Cyclid
122
122
  @log_buffer.write data
123
123
 
124
124
  # Ping the callback log_write hook, if required
125
- @callback.log_write(@job_id, data) if @callback
125
+ @callback&.log_write(@job_id, data)
126
126
  end
127
127
  end
128
128
  end
@@ -43,11 +43,12 @@ module Cyclid
43
43
  raise 'failed to update repositories' unless success
44
44
  end
45
45
 
46
- env[:packages].each do |package|
46
+ if env.key? :packages
47
47
  success = transport.exec \
48
- "sudo -E apt-get install -y #{package}"
49
- raise "failed to install package #{package}" unless success
50
- end if env.key? :packages
48
+ "sudo -E apt-get install -y #{env[:packages].join(' ')}" \
49
+
50
+ raise "failed to install packages #{env[:packages].join(' ')}" unless success
51
+ end
51
52
  rescue StandardError => ex
52
53
  Cyclid.logger.error "failed to provision #{buildhost[:name]}: #{ex}"
53
54
  raise
@@ -68,18 +69,18 @@ module Cyclid
68
69
  "echo '#{fragment}' | sudo tee -a /etc/apt/sources.list.d/cyclid.list"
69
70
  raise "failed to add repository #{url}" unless success
70
71
 
71
- if repo.key? :key_id
72
- # Import the signing key
73
- key_id = repo[:key_id]
72
+ return unless repo.key? :key_id
74
73
 
75
- success = transport.exec \
76
- "gpg --keyserver keyserver.ubuntu.com --recv-keys #{key_id}"
77
- raise "failed to import key #{key_id}" unless success
74
+ # Import the signing key
75
+ key_id = repo[:key_id]
78
76
 
79
- success = transport.exec \
80
- "gpg -a --export #{key_id} | sudo apt-key add -"
81
- raise "failed to add repository key #{key_id}" unless success
82
- end
77
+ success = transport.exec \
78
+ "gpg --keyserver keyserver.ubuntu.com --recv-keys #{key_id}"
79
+ raise "failed to import key #{key_id}" unless success
80
+
81
+ success = transport.exec \
82
+ "gpg -a --export #{key_id} | sudo apt-key add -"
83
+ raise "failed to add repository key #{key_id}" unless success
83
84
  end
84
85
 
85
86
  # Register this plugin
@@ -46,11 +46,12 @@ module Cyclid
46
46
  raise 'failed to update repositories' unless success
47
47
  end
48
48
 
49
- env[:packages].each do |package|
49
+ if env.key? :packages
50
50
  success = transport.exec \
51
- "sudo -E apt-get install -y #{package}"
52
- raise "failed to install package #{package}" unless success
53
- end if env.key? :packages
51
+ "sudo -E apt-get install -y #{env[:packages].join(' ')}" \
52
+
53
+ raise "failed to install packages #{env[:packages].join(' ')}" unless success
54
+ end
54
55
  rescue StandardError => ex
55
56
  Cyclid.logger.error "failed to provision #{buildhost[:name]}: #{ex}"
56
57
  raise
@@ -76,18 +77,18 @@ module Cyclid
76
77
  "echo '#{fragment}' | sudo tee -a /etc/apt/sources.list.d/cyclid.list"
77
78
  raise "failed to add repository #{url}" unless success
78
79
 
79
- if repo.key? :key_id
80
- # Import the signing key
81
- key_id = repo[:key_id]
80
+ return unless repo.key? :key_id
82
81
 
83
- success = transport.exec \
84
- "gpg --keyserver keyserver.ubuntu.com --recv-keys #{key_id}"
85
- raise "failed to import key #{key_id}" unless success
82
+ # Import the signing key
83
+ key_id = repo[:key_id]
86
84
 
87
- success = transport.exec \
88
- "gpg -a --export #{key_id} | sudo apt-key add -"
89
- raise "failed to add repository key #{key_id}" unless success
90
- end
85
+ success = transport.exec \
86
+ "gpg --keyserver keyserver.ubuntu.com --recv-keys #{key_id}"
87
+ raise "failed to import key #{key_id}" unless success
88
+
89
+ success = transport.exec \
90
+ "gpg -a --export #{key_id} | sudo apt-key add -"
91
+ raise "failed to add repository key #{key_id}" unless success
91
92
  end
92
93
 
93
94
  # Register this plugin
@@ -48,7 +48,7 @@ module Cyclid
48
48
 
49
49
  branch = source[:branch]
50
50
 
51
- match = url.path.match(%r{^.*\/(\w*)})
51
+ match = url.path.match(%r{^.*\/([^\.]*)})
52
52
  source_dir = "#{ctx[:workspace]}/#{match[1]}"
53
53
 
54
54
  success = transport.exec("git fetch origin #{branch}:#{branch}", source_dir)
@@ -52,8 +52,10 @@ module Cyclid
52
52
  keys: keys,
53
53
  timeout: 5)
54
54
  break unless @session.nil?
55
- rescue Net::SSH::AuthenticationFailed
55
+ rescue Net::SSH::Exception
56
56
  Cyclid.logger.debug 'SSH authentication failed'
57
+ rescue StandardError => ex
58
+ Cyclid.logger.debug "SSH connection failed: #{ex}"
57
59
  end
58
60
 
59
61
  sleep 5
@@ -22,6 +22,12 @@ module Cyclid
22
22
  module API
23
23
  # Sinatra helpers
24
24
  module APIHelpers
25
+ # Return the raw request body
26
+ def request_body
27
+ request.body.rewind
28
+ request.body.read
29
+ end
30
+
25
31
  # Safely parse & validate the request body
26
32
  def parse_request_body
27
33
  # Parse the the request
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Cyclid
3
3
  module Api
4
- VERSION = '0.2.1'
4
+ VERSION = '0.2.2'
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cyclid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kristian Van Der Vliet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-05 00:00:00.000000000 Z
11
+ date: 2016-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -276,6 +276,20 @@ dependencies:
276
276
  - - "~>"
277
277
  - !ruby/object:Gem::Version
278
278
  version: '1.5'
279
+ - !ruby/object:Gem::Dependency
280
+ name: octokit
281
+ requirement: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - "~>"
284
+ - !ruby/object:Gem::Version
285
+ version: '4.3'
286
+ type: :runtime
287
+ prerelease: false
288
+ version_requirements: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - "~>"
291
+ - !ruby/object:Gem::Version
292
+ version: '4.3'
279
293
  - !ruby/object:Gem::Dependency
280
294
  name: cyclid-core
281
295
  requirement: !ruby/object:Gem::Requirement
@@ -357,9 +371,14 @@ files:
357
371
  - app/cyclid/plugins/action/slack/note.erb
358
372
  - app/cyclid/plugins/api.rb
359
373
  - app/cyclid/plugins/api/github.rb
374
+ - app/cyclid/plugins/api/github/README.md
360
375
  - app/cyclid/plugins/api/github/callback.rb
376
+ - app/cyclid/plugins/api/github/config.rb
377
+ - app/cyclid/plugins/api/github/helpers.rb
361
378
  - app/cyclid/plugins/api/github/methods.rb
362
- - app/cyclid/plugins/api/github/status.rb
379
+ - app/cyclid/plugins/api/github/oauth.rb
380
+ - app/cyclid/plugins/api/github/pull_request.rb
381
+ - app/cyclid/plugins/api/github/push.rb
363
382
  - app/cyclid/plugins/builder.rb
364
383
  - app/cyclid/plugins/builder/mist.rb
365
384
  - app/cyclid/plugins/dispatcher.rb