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