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