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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33d614a9893bc411011e194512153b90d56082a0
|
4
|
+
data.tar.gz: 0d607fd8d0892da8031255600cbd6703b42ffc5c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90e8b58bb414d9116671741ce28d67106ac2075bda3a129a9a8775a555e30921d0455fa0c6ab10f50387952cdfb27e48c33ce9ea2f172c3384f90c954d0e47e2
|
7
|
+
data.tar.gz: d9b11717018b8f45f690fe2dd553603cfbaa59967cf65cb7e7ea1c9384f0f528e762440a1397c3eacf44c59184bc693ec17be594b683244a87d0a3b78c343af4
|
@@ -25,6 +25,14 @@ module Cyclid
|
|
25
25
|
# rubocop:disable Metrics/LineLength
|
26
26
|
# @!group Organizations
|
27
27
|
|
28
|
+
# @!method get_organizations_organization_configs
|
29
|
+
# @overload GET /organizations/:organization/configs
|
30
|
+
# @macro rest
|
31
|
+
# @param [String] organization Name of the organization.
|
32
|
+
# Get the list of plugins which support per-organization configurations.
|
33
|
+
# @return The list of plugins.
|
34
|
+
# @return [404] The organization or plugin does not exist.
|
35
|
+
|
28
36
|
# @!method get_organizations_organization_configs_type_plugin
|
29
37
|
# @overload GET /organizations/:organization/configs/:type/:plugin
|
30
38
|
# @macro rest
|
@@ -63,8 +71,21 @@ module Cyclid
|
|
63
71
|
include Errors::HTTPErrors
|
64
72
|
include Constants::JobStatus
|
65
73
|
|
74
|
+
# Return a list of plugins which have configs
|
75
|
+
app.get do
|
76
|
+
authorized_for!(params[:name], Operations::READ)
|
77
|
+
|
78
|
+
configs = []
|
79
|
+
Cyclid.plugins.all.each do |plugin|
|
80
|
+
configs << { type: plugin.human_name, name: plugin.name } \
|
81
|
+
if plugin.config?
|
82
|
+
end
|
83
|
+
|
84
|
+
return configs.to_json
|
85
|
+
end
|
86
|
+
|
66
87
|
# Get the current configuration for the given plugin.
|
67
|
-
app.get '/:plugin' do
|
88
|
+
app.get '/:type/:plugin' do
|
68
89
|
authorized_for!(params[:name], Operations::READ)
|
69
90
|
|
70
91
|
org = Organization.find_by(name: params[:name])
|
@@ -96,7 +117,7 @@ module Cyclid
|
|
96
117
|
end
|
97
118
|
|
98
119
|
# Update the plugin configuration
|
99
|
-
app.put '/:plugin' do
|
120
|
+
app.put '/:type/:plugin' do
|
100
121
|
authorized_for!(params[:name], Operations::ADMIN)
|
101
122
|
|
102
123
|
payload = parse_request_body
|
data/app/cyclid/job/runner.rb
CHANGED
data/app/cyclid/log_buffer.rb
CHANGED
data/app/cyclid/plugins.rb
CHANGED
@@ -40,6 +40,11 @@ module Cyclid
|
|
40
40
|
Cyclid.plugins.register(self)
|
41
41
|
end
|
42
42
|
|
43
|
+
# Does this plugin support configuration data?
|
44
|
+
def config?
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
43
48
|
# Get the configuration for the given org
|
44
49
|
def get_config(org)
|
45
50
|
# If the organization was passed by name, convert it into an Organization object
|
@@ -123,3 +128,8 @@ module Cyclid
|
|
123
128
|
end
|
124
129
|
|
125
130
|
require_rel 'plugins/*.rb'
|
131
|
+
|
132
|
+
# Load all plugins from Gems
|
133
|
+
Gem.find_files('cyclid/plugins/**/*.rb').each do |path|
|
134
|
+
require path
|
135
|
+
end
|
@@ -157,6 +157,11 @@ module Cyclid
|
|
157
157
|
|
158
158
|
# Static methods for handling plugin config data
|
159
159
|
class << self
|
160
|
+
# This plugin has configuration data
|
161
|
+
def config?
|
162
|
+
true
|
163
|
+
end
|
164
|
+
|
160
165
|
# Update the plugin configuration
|
161
166
|
def update_config(current, new)
|
162
167
|
current.merge! new
|
@@ -194,7 +199,7 @@ module Cyclid
|
|
194
199
|
description: 'SMTP server username',
|
195
200
|
default: nil }
|
196
201
|
schema << { name: 'password',
|
197
|
-
type: '
|
202
|
+
type: 'password',
|
198
203
|
description: 'SMTP server password',
|
199
204
|
default: nil }
|
200
205
|
|
@@ -99,6 +99,11 @@ module Cyclid
|
|
99
99
|
|
100
100
|
# Static methods for handling plugin config data
|
101
101
|
class << self
|
102
|
+
# This plugin has configuration data
|
103
|
+
def config?
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
102
107
|
# Update the plugin configuration
|
103
108
|
def update_config(current, new)
|
104
109
|
current.merge! new
|
data/app/cyclid/plugins/api.rb
CHANGED
@@ -26,64 +26,43 @@ module Cyclid
|
|
26
26
|
class Controller < Module
|
27
27
|
attr_reader :plugin_methods
|
28
28
|
|
29
|
-
def initialize(methods = nil)
|
29
|
+
def initialize(methods = nil, custom_routes = [])
|
30
30
|
@plugin_methods = methods
|
31
|
+
|
32
|
+
# Provide default routes for the four basic HTTP verbs; for most
|
33
|
+
# plugins they only need to implement the matching method for the
|
34
|
+
# verb(s) they'll respond too.
|
35
|
+
default_routes = [{ verb: :get, path: nil, func: 'get' },
|
36
|
+
{ verb: :post, path: nil, func: 'post' },
|
37
|
+
{ verb: :put, path: nil, func: 'put' },
|
38
|
+
{ verb: :delete, path: nil, func: 'delete' }]
|
39
|
+
|
40
|
+
# ...but more complex plugins can add additional routes with their
|
41
|
+
# own paths & methods, if required.
|
42
|
+
@routes = default_routes.concat custom_routes
|
31
43
|
end
|
32
44
|
|
33
45
|
# Sinatra callback
|
34
46
|
def registered(app)
|
35
47
|
include Errors::HTTPErrors
|
36
48
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
42
|
-
if org.nil?
|
43
|
-
|
44
|
-
config = controller_plugin.get_config(org)
|
45
|
-
|
46
|
-
get(http_headers(request.env), config['config'])
|
47
|
-
end
|
48
|
-
|
49
|
-
app.post do
|
50
|
-
Cyclid.logger.debug 'ApiExtension::Controller::post'
|
49
|
+
@routes.each do |route|
|
50
|
+
verb = route[:verb]
|
51
|
+
path = route[:path]
|
52
|
+
func = route[:func]
|
51
53
|
|
52
|
-
|
54
|
+
app.send(verb, path) do
|
55
|
+
Cyclid.logger.debug "ApiExtension::Controller::#{verb} #{path}"
|
53
56
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
+
org = Organization.find_by(name: params[:name])
|
58
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
59
|
+
if org.nil?
|
57
60
|
|
58
|
-
|
61
|
+
config = controller_plugin.get_config(org)
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
app.put do
|
64
|
-
Cyclid.logger.debug 'ApiExtension::Controller::put'
|
65
|
-
|
66
|
-
payload = parse_request_body
|
67
|
-
|
68
|
-
org = Organization.find_by(name: params[:name])
|
69
|
-
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
70
|
-
if org.nil?
|
71
|
-
|
72
|
-
config = controller_plugin.get_config(org)
|
73
|
-
|
74
|
-
put(payload, http_headers(request.env), config['config'])
|
75
|
-
end
|
76
|
-
|
77
|
-
app.delete do
|
78
|
-
Cyclid.logger.debug 'ApiExtension::Controller::delete'
|
79
|
-
|
80
|
-
org = Organization.find_by(name: params[:name])
|
81
|
-
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
82
|
-
if org.nil?
|
83
|
-
|
84
|
-
config = controller_plugin.get_config(org)
|
85
|
-
|
86
|
-
delete(http_headers(request.env), config['config'])
|
63
|
+
meth = method(func)
|
64
|
+
meth.call(http_headers(request.env), config['config'])
|
65
|
+
end
|
87
66
|
end
|
88
67
|
|
89
68
|
app.helpers do
|
@@ -107,13 +86,13 @@ module Cyclid
|
|
107
86
|
end
|
108
87
|
|
109
88
|
# POST callback
|
110
|
-
def post(
|
89
|
+
def post(_headers, _config)
|
111
90
|
authorize('post')
|
112
91
|
return_failure(405, 'not implemented')
|
113
92
|
end
|
114
93
|
|
115
94
|
# PUT callback
|
116
|
-
def put(
|
95
|
+
def put(_headers, _config)
|
117
96
|
authorize('put')
|
118
97
|
return_failure(405, 'not implemented')
|
119
98
|
end
|
@@ -173,6 +152,20 @@ module Cyclid
|
|
173
152
|
|
174
153
|
return http_headers
|
175
154
|
end
|
155
|
+
|
156
|
+
# Return the current organization name
|
157
|
+
def organization_name
|
158
|
+
params[:name]
|
159
|
+
end
|
160
|
+
|
161
|
+
# Find & return the Organization model
|
162
|
+
def retrieve_organization(name = nil)
|
163
|
+
name ||= organization_name
|
164
|
+
org = Organization.find_by(name: name)
|
165
|
+
halt_with_json_response(404, INVALID_ORG, 'organization does not exist') \
|
166
|
+
if org.nil?
|
167
|
+
return org
|
168
|
+
end
|
176
169
|
end
|
177
170
|
end
|
178
171
|
|
@@ -14,7 +14,6 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
16
|
require_rel 'github/methods'
|
17
|
-
require_rel 'github/status'
|
18
17
|
require_rel 'github/callback'
|
19
18
|
|
20
19
|
# Top level module for the core Cyclid code.
|
@@ -25,12 +24,20 @@ module Cyclid
|
|
25
24
|
module Plugins
|
26
25
|
# API extension for Github hooks
|
27
26
|
class Github < Api
|
28
|
-
# Return an instance of the Github API controller
|
29
|
-
def self.controller
|
30
|
-
return ApiExtension::Controller.new(ApiExtension::GithubMethods)
|
31
|
-
end
|
32
|
-
|
33
27
|
class << self
|
28
|
+
# Return an instance of the Github API controller
|
29
|
+
def controller
|
30
|
+
routes = [{ verb: :get, path: '/oauth/request', func: 'oauth_request' },
|
31
|
+
{ verb: :get, path: '/oauth/callback', func: 'oauth_callback' }]
|
32
|
+
|
33
|
+
return ApiExtension::Controller.new(ApiExtension::GithubMethods, routes)
|
34
|
+
end
|
35
|
+
|
36
|
+
# This plugin has configuration data
|
37
|
+
def config?
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
34
41
|
# Merge the given config into the current config & validate
|
35
42
|
def update_config(config, new)
|
36
43
|
Cyclid.logger.debug "config=#{config} new=#{new}"
|
@@ -70,11 +77,14 @@ module Cyclid
|
|
70
77
|
config['repository_tokens'] = merged
|
71
78
|
end
|
72
79
|
|
73
|
-
if new.key? '
|
74
|
-
Cyclid.logger.debug 'updating
|
75
|
-
config['
|
80
|
+
if new.key? 'oauth_token'
|
81
|
+
Cyclid.logger.debug 'updating OAuth token'
|
82
|
+
config['oauth_token'] = new['oauth_token']
|
76
83
|
end
|
77
84
|
|
85
|
+
# Remove any old keys
|
86
|
+
config.delete 'hmac_secret' if config.key? 'hmac_secret'
|
87
|
+
|
78
88
|
return config
|
79
89
|
end
|
80
90
|
|
@@ -82,7 +92,7 @@ module Cyclid
|
|
82
92
|
def default_config
|
83
93
|
config = {}
|
84
94
|
config['repository_tokens'] = []
|
85
|
-
config['
|
95
|
+
config['oauth_token'] = nil
|
86
96
|
|
87
97
|
return config
|
88
98
|
end
|
@@ -92,13 +102,16 @@ module Cyclid
|
|
92
102
|
schema = []
|
93
103
|
schema << { name: 'repository_tokens',
|
94
104
|
type: 'hash-list',
|
95
|
-
description: '
|
105
|
+
description: 'Individual repository personal OAuth tokens',
|
96
106
|
default: [] }
|
97
|
-
schema << { name: '
|
98
|
-
type: '
|
99
|
-
description: 'Github
|
107
|
+
schema << { name: 'oauth_token',
|
108
|
+
type: 'password',
|
109
|
+
description: 'Organization Github OAuth token',
|
100
110
|
default: nil }
|
101
|
-
|
111
|
+
schema << { name: 'oauth_request',
|
112
|
+
type: 'link-relative',
|
113
|
+
description: 'Authorize with Github',
|
114
|
+
default: '/oauth/request' }
|
102
115
|
return schema
|
103
116
|
end
|
104
117
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
Github API plugin
|
2
|
+
---
|
3
|
+
|
4
|
+
This is the Github API plugin for Cyclid. It adds API end points which support Github OAuth authentication and webhook events:
|
5
|
+
|
6
|
+
| Verb | Path | Notes |
|
7
|
+
| --- | --- | --- |
|
8
|
+
| **GET** | / |Github webhook callback. |
|
9
|
+
| **GET** | /oauth/request | Start the OAuth authorization process. |
|
10
|
+
| **GET** | /oauth/callback | The OUth authorization calback. |
|
11
|
+
|
12
|
+
# OAuth
|
13
|
+
|
14
|
+
Initiate the Github OAuth "Web" flow with a GET to the /oauth/request endpoint. This will cause a redirect to Github where the user can authorise the OAuth request.
|
15
|
+
|
16
|
+
When the request has been authorised the user will be redirected back to the /oauth/callback endpoint, which in turn will then redirect to the Cyclid UI.
|
17
|
+
|
18
|
+
# Server Configuration
|
19
|
+
|
20
|
+
The server-side configuration for this plugin is:
|
21
|
+
|
22
|
+
```
|
23
|
+
github:
|
24
|
+
client_id: <Github OAuth client ID>
|
25
|
+
client_secret: <Github OAuth client secret>
|
26
|
+
api_url: <This API>
|
27
|
+
ui_url: <Cyclid UI instance attached to this API>
|
28
|
+
```
|
29
|
+
The `client_id` and `client_secret` can be obtained from Github when you [register Cyclid as an application](https://github.com/settings/applications/new) and should be provided here to enable OAuth authorization flow.
|
30
|
+
|
31
|
+
The `api_url` should be the publically resolvable name of the Cyclid API server, and the `ui_url` should be the publically resolvable name of a Cyclid UI server which is configured to use the API server. These URLs are both used to generate appropriate redirects during the OAuth authorization process.
|
32
|
+
|
33
|
+
Note that the `api_url` provided must match the "Authorization callback URL" setting you provide to Github when you register Cyclid as an application; this includes both the scheme (http: or https:) and any port number (Cyclid by default uses port 8361). So for example:
|
34
|
+
|
35
|
+
```
|
36
|
+
api_url: https://cyclid.example.com:8361
|
37
|
+
```
|
38
|
+
|
39
|
+
# Plugin configuration
|
40
|
+
|
41
|
+
The per-organization plugin configuration is:
|
42
|
+
|
43
|
+
```
|
44
|
+
Individual repository personal OAuth tokens
|
45
|
+
None
|
46
|
+
Organization Github OAuth token: abcdef123456789
|
47
|
+
```
|
48
|
+
|
49
|
+
The individual repository OAuth tokens can be used to provide a privately generated OAuth token for a single repository. OAuth tokens are matched by the repository URL.
|
50
|
+
|
51
|
+
The organization OAuth token is the token normally generated via. the normal Github OAuth authorization process. This is the token which will be used by default.
|
52
|
+
|
53
|
+
# Webhook events
|
54
|
+
|
55
|
+
The plugin currently supports the following webhook events:
|
56
|
+
|
57
|
+
* ping
|
58
|
+
* status
|
59
|
+
* pull_request
|
60
|
+
* push
|
61
|
+
|
62
|
+
The `ping` and `status` events return immediatly with a 200 response. The `pull_request` and `push` events may cause a new job to be submitted, if a Cyclid job file can be found in the repository which generated the event.
|
@@ -13,6 +13,8 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
|
16
|
+
require 'octokit'
|
17
|
+
|
16
18
|
# Top level module for the core Cyclid code.
|
17
19
|
module Cyclid
|
18
20
|
# Module for the Cyclid API
|
@@ -24,9 +26,16 @@ module Cyclid
|
|
24
26
|
# Notifier callback for Github. Updates the external Github Pull
|
25
27
|
# Request status as the job progresses.
|
26
28
|
class GithubCallback < Plugins::Notifier::Callback
|
27
|
-
def initialize(
|
28
|
-
@statuses = statuses
|
29
|
+
def initialize(auth_token, repo, sha, linkback_url)
|
29
30
|
@auth_token = auth_token
|
31
|
+
@repo = repo
|
32
|
+
@sha = sha
|
33
|
+
@linkback_url = linkback_url
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return or create an Octokit client
|
37
|
+
def client
|
38
|
+
@client ||= Octokit::Client.new(access_token: @auth_token)
|
30
39
|
end
|
31
40
|
|
32
41
|
# Job status has changed
|
@@ -45,7 +54,10 @@ module Cyclid
|
|
45
54
|
return false
|
46
55
|
end
|
47
56
|
|
48
|
-
|
57
|
+
target_url = "#{@linkback_url}/job/#{job_id}"
|
58
|
+
client.create_status(@repo, @sha, state, context: 'Cyclid',
|
59
|
+
target_url: target_url,
|
60
|
+
description: message)
|
49
61
|
end
|
50
62
|
|
51
63
|
# Job has completed
|
@@ -57,7 +69,10 @@ module Cyclid
|
|
57
69
|
state = 'failure'
|
58
70
|
message = "Job ##{job_id} failed."
|
59
71
|
end
|
60
|
-
|
72
|
+
target_url = "#{@linkback_url}/job/#{job_id}"
|
73
|
+
client.create_status(@repo, @sha, state, context: 'Cyclid',
|
74
|
+
target_url: target_url,
|
75
|
+
description: message)
|
61
76
|
end
|
62
77
|
end
|
63
78
|
end
|