cyclid 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/cyclid/controllers/organizations.rb +1 -1
- data/app/cyclid/controllers/organizations/config.rb +23 -2
- data/app/cyclid/job/runner.rb +1 -1
- data/app/cyclid/log_buffer.rb +1 -1
- data/app/cyclid/plugins.rb +10 -0
- data/app/cyclid/plugins/action/email.rb +6 -1
- data/app/cyclid/plugins/action/slack.rb +5 -0
- data/app/cyclid/plugins/api.rb +42 -49
- data/app/cyclid/plugins/api/github.rb +28 -15
- data/app/cyclid/plugins/api/github/README.md +62 -0
- data/app/cyclid/plugins/api/github/callback.rb +19 -4
- data/app/cyclid/plugins/api/github/config.rb +45 -0
- data/app/cyclid/plugins/api/github/helpers.rb +139 -0
- data/app/cyclid/plugins/api/github/methods.rb +16 -138
- data/app/cyclid/plugins/api/github/oauth.rb +117 -0
- data/app/cyclid/plugins/api/github/pull_request.rb +134 -0
- data/app/cyclid/plugins/api/github/push.rb +121 -0
- data/app/cyclid/plugins/dispatcher/local.rb +3 -3
- data/app/cyclid/plugins/provisioner/debian.rb +15 -14
- data/app/cyclid/plugins/provisioner/ubuntu.rb +15 -14
- data/app/cyclid/plugins/source/git.rb +1 -1
- data/app/cyclid/plugins/transport/ssh.rb +3 -1
- data/app/cyclid/sinatra/api_helpers.rb +6 -0
- data/lib/cyclid/version.rb +1 -1
- metadata +22 -3
- data/app/cyclid/plugins/api/github/status.rb +0 -67
@@ -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
|
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
|
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
|
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
|
46
|
+
if env.key? :packages
|
47
47
|
success = transport.exec \
|
48
|
-
"sudo -E apt-get install -y #{
|
49
|
-
|
50
|
-
|
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
|
-
|
72
|
-
# Import the signing key
|
73
|
-
key_id = repo[:key_id]
|
72
|
+
return unless repo.key? :key_id
|
74
73
|
|
75
|
-
|
76
|
-
|
77
|
-
raise "failed to import key #{key_id}" unless success
|
74
|
+
# Import the signing key
|
75
|
+
key_id = repo[:key_id]
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
49
|
+
if env.key? :packages
|
50
50
|
success = transport.exec \
|
51
|
-
"sudo -E apt-get install -y #{
|
52
|
-
|
53
|
-
|
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
|
-
|
80
|
-
# Import the signing key
|
81
|
-
key_id = repo[:key_id]
|
80
|
+
return unless repo.key? :key_id
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
raise "failed to import key #{key_id}" unless success
|
82
|
+
# Import the signing key
|
83
|
+
key_id = repo[:key_id]
|
86
84
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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{^.*\/(
|
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::
|
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
|
data/lib/cyclid/version.rb
CHANGED
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.
|
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-
|
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/
|
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
|