cyclid 0.2.0
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 +7 -0
- data/LICENSE +174 -0
- data/README.md +54 -0
- data/app/cyclid.rb +61 -0
- data/app/cyclid/config.rb +38 -0
- data/app/cyclid/controllers.rb +123 -0
- data/app/cyclid/controllers/auth.rb +34 -0
- data/app/cyclid/controllers/auth/token.rb +78 -0
- data/app/cyclid/controllers/health.rb +96 -0
- data/app/cyclid/controllers/organizations.rb +104 -0
- data/app/cyclid/controllers/organizations/collection.rb +134 -0
- data/app/cyclid/controllers/organizations/config.rb +128 -0
- data/app/cyclid/controllers/organizations/document.rb +135 -0
- data/app/cyclid/controllers/organizations/job.rb +266 -0
- data/app/cyclid/controllers/organizations/members.rb +145 -0
- data/app/cyclid/controllers/organizations/stages.rb +251 -0
- data/app/cyclid/controllers/users.rb +47 -0
- data/app/cyclid/controllers/users/collection.rb +131 -0
- data/app/cyclid/controllers/users/document.rb +133 -0
- data/app/cyclid/health_helpers.rb +40 -0
- data/app/cyclid/job.rb +3 -0
- data/app/cyclid/job/helpers.rb +67 -0
- data/app/cyclid/job/job.rb +164 -0
- data/app/cyclid/job/runner.rb +275 -0
- data/app/cyclid/job/stage.rb +67 -0
- data/app/cyclid/log_buffer.rb +104 -0
- data/app/cyclid/models.rb +3 -0
- data/app/cyclid/models/job_record.rb +25 -0
- data/app/cyclid/models/organization.rb +64 -0
- data/app/cyclid/models/plugin_config.rb +25 -0
- data/app/cyclid/models/stage.rb +42 -0
- data/app/cyclid/models/step.rb +29 -0
- data/app/cyclid/models/user.rb +60 -0
- data/app/cyclid/models/user_permissions.rb +28 -0
- data/app/cyclid/monkey_patches.rb +37 -0
- data/app/cyclid/plugin_registry.rb +75 -0
- data/app/cyclid/plugins.rb +125 -0
- data/app/cyclid/plugins/action.rb +48 -0
- data/app/cyclid/plugins/action/command.rb +89 -0
- data/app/cyclid/plugins/action/email.rb +207 -0
- data/app/cyclid/plugins/action/email/html.erb +58 -0
- data/app/cyclid/plugins/action/email/text.erb +13 -0
- data/app/cyclid/plugins/action/script.rb +90 -0
- data/app/cyclid/plugins/action/slack.rb +129 -0
- data/app/cyclid/plugins/action/slack/note.erb +5 -0
- data/app/cyclid/plugins/api.rb +195 -0
- data/app/cyclid/plugins/api/github.rb +111 -0
- data/app/cyclid/plugins/api/github/callback.rb +66 -0
- data/app/cyclid/plugins/api/github/methods.rb +201 -0
- data/app/cyclid/plugins/api/github/status.rb +67 -0
- data/app/cyclid/plugins/builder.rb +80 -0
- data/app/cyclid/plugins/builder/mist.rb +107 -0
- data/app/cyclid/plugins/dispatcher.rb +89 -0
- data/app/cyclid/plugins/dispatcher/local.rb +167 -0
- data/app/cyclid/plugins/provisioner.rb +40 -0
- data/app/cyclid/plugins/provisioner/debian.rb +90 -0
- data/app/cyclid/plugins/provisioner/ubuntu.rb +98 -0
- data/app/cyclid/plugins/source.rb +39 -0
- data/app/cyclid/plugins/source/git.rb +64 -0
- data/app/cyclid/plugins/transport.rb +63 -0
- data/app/cyclid/plugins/transport/ssh.rb +155 -0
- data/app/cyclid/sinatra/api_helpers.rb +66 -0
- data/app/cyclid/sinatra/auth_helpers.rb +127 -0
- data/app/cyclid/sinatra/warden/strategies/api_token.rb +62 -0
- data/app/cyclid/sinatra/warden/strategies/basic.rb +58 -0
- data/app/cyclid/sinatra/warden/strategies/hmac.rb +76 -0
- data/app/db.rb +51 -0
- data/bin/cyclid-db-init +107 -0
- data/db/schema.rb +92 -0
- data/lib/cyclid/app.rb +4 -0
- metadata +407 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# Add two methods to Hash
|
3
|
+
class Hash
|
4
|
+
# http://chrisholtz.com/blog/lets-make-a-ruby-hash-map-method-that-returns-a-hash-instead-of-an-array/
|
5
|
+
def hmap
|
6
|
+
inject({}) do |hash, (k, v)|
|
7
|
+
hash.merge(yield(k, v))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Interpolate the data in the ctx hash into any String values
|
12
|
+
def interpolate(ctx)
|
13
|
+
hmap do |key, value|
|
14
|
+
if value.is_a? String
|
15
|
+
{ key => value % ctx }
|
16
|
+
else
|
17
|
+
{ key => value }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Add a method to Array
|
24
|
+
class Array
|
25
|
+
# Interpolate the data in the ctx hash for each String & Hash item
|
26
|
+
def interpolate(ctx)
|
27
|
+
map do |entry|
|
28
|
+
if entry.is_a? Hash
|
29
|
+
entry.interpolate ctx
|
30
|
+
elsif entry.is_a? String
|
31
|
+
entry % @ctx
|
32
|
+
else
|
33
|
+
entry
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,75 @@
|
|
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
|
+
# Top level module for the core Cyclid code.
|
17
|
+
module Cyclid
|
18
|
+
# Module for the Cyclid API
|
19
|
+
module API
|
20
|
+
# Module for Cyclid Plugins
|
21
|
+
module Plugins
|
22
|
+
# Intelligent system-wide registry of available plugins with helper
|
23
|
+
# methods to find them again
|
24
|
+
class Registry
|
25
|
+
def initialize
|
26
|
+
@plugins = []
|
27
|
+
@types = []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Add a plugin to the registry
|
31
|
+
def register(plugin)
|
32
|
+
# XXX Perform sanity checks
|
33
|
+
@plugins << plugin
|
34
|
+
|
35
|
+
# Maintain a human<->type mapping
|
36
|
+
human_name = plugin.human_name
|
37
|
+
@types << { human: human_name, type: plugin.superclass }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Find a plugin from the registry
|
41
|
+
def find(name, type)
|
42
|
+
object_type = nil
|
43
|
+
|
44
|
+
# Convert a human name to a type, if required
|
45
|
+
if type.is_a? String
|
46
|
+
@types.each do |registered_type|
|
47
|
+
next unless registered_type[:human] == type
|
48
|
+
|
49
|
+
object_type = registered_type[:type]
|
50
|
+
break
|
51
|
+
end
|
52
|
+
else
|
53
|
+
object_type = type
|
54
|
+
end
|
55
|
+
|
56
|
+
raise "couldn't map plugin type #{type}" if object_type.nil?
|
57
|
+
|
58
|
+
@plugins.each do |plugin|
|
59
|
+
return plugin if plugin.name == name && plugin.superclass == object_type
|
60
|
+
end
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# Return a list of all plugins of a certain type
|
65
|
+
def all(type = nil)
|
66
|
+
list = []
|
67
|
+
@plugins.each do |plugin|
|
68
|
+
list << plugin if plugin.superclass == type or type.nil?
|
69
|
+
end
|
70
|
+
return list
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,125 @@
|
|
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 'require_all'
|
17
|
+
require 'active_support/core_ext'
|
18
|
+
|
19
|
+
require_relative 'health_helpers'
|
20
|
+
|
21
|
+
# Top level module for the core Cyclid code.
|
22
|
+
module Cyclid
|
23
|
+
# Module for the Cyclid API
|
24
|
+
module API
|
25
|
+
# Module for Cyclid Plugins
|
26
|
+
module Plugins
|
27
|
+
# Base class for Plugins
|
28
|
+
class Base
|
29
|
+
class << self
|
30
|
+
attr_reader :name
|
31
|
+
|
32
|
+
# Returns the 'human' name for the plugin type
|
33
|
+
def human_name
|
34
|
+
'base'
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add the (derived) plugin to the plugin registry
|
38
|
+
def register_plugin(name)
|
39
|
+
@name = name
|
40
|
+
Cyclid.plugins.register(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get the configuration for the given org
|
44
|
+
def get_config(org)
|
45
|
+
# If the organization was passed by name, convert it into an Organization object
|
46
|
+
org = Organization.find_by(name: org) if org.is_a? String
|
47
|
+
raise 'organization does not exist' if org.nil?
|
48
|
+
|
49
|
+
# XXX Plugins of different types can have the same name; we need to
|
50
|
+
# add a 'type' field and also find by the type.
|
51
|
+
config = org.plugin_configs.find_by(plugin: @name)
|
52
|
+
if config.nil?
|
53
|
+
# No config currently exists; create a new default config
|
54
|
+
config = PluginConfig.new(plugin: @name,
|
55
|
+
version: '1.0.0',
|
56
|
+
config: Oj.dump(default_config.stringify_keys))
|
57
|
+
config.save!
|
58
|
+
|
59
|
+
org.plugin_configs << config
|
60
|
+
end
|
61
|
+
|
62
|
+
# Convert the model to a hash, add the config schema, and convert the JSON config
|
63
|
+
# blob back into a hash
|
64
|
+
config_hash = config.serializable_hash
|
65
|
+
config_hash['schema'] = config_schema
|
66
|
+
config_hash['config'] = Oj.load(config.config)
|
67
|
+
|
68
|
+
return config_hash
|
69
|
+
rescue StandardError => ex
|
70
|
+
Cyclid.logger.error "couldn't get/create plugin config for #{@name}: #{ex}"
|
71
|
+
raise
|
72
|
+
end
|
73
|
+
|
74
|
+
# Set the configuration for the given org
|
75
|
+
def set_config(new_config, org)
|
76
|
+
new_config.stringify_keys!
|
77
|
+
|
78
|
+
config = org.plugin_configs.find_by(plugin: @name)
|
79
|
+
if config.nil?
|
80
|
+
# No config currently exists; create a new default config
|
81
|
+
config = PluginConfig.new(plugin: @name,
|
82
|
+
version: '1.0.0',
|
83
|
+
config: Oj.dump(default_config.stringify_keys))
|
84
|
+
config.save!
|
85
|
+
|
86
|
+
org.plugin_configs << config
|
87
|
+
end
|
88
|
+
|
89
|
+
# Let the plugin validate & merge the changes into the config hash
|
90
|
+
config_hash = config.serializable_hash
|
91
|
+
current_config = config_hash['config']
|
92
|
+
Cyclid.logger.debug "current_config=#{current_config}"
|
93
|
+
merged_config = update_config(Oj.load(current_config), new_config)
|
94
|
+
|
95
|
+
raise 'plugin rejected the configuration' if merged_config == false
|
96
|
+
|
97
|
+
Cyclid.logger.debug "merged_config=#{merged_config}"
|
98
|
+
|
99
|
+
# Update the stored configuration
|
100
|
+
config.config = Oj.dump(merged_config.stringify_keys)
|
101
|
+
config.save!
|
102
|
+
end
|
103
|
+
|
104
|
+
# Validite the given configuration items and merge them into the correct configuration,
|
105
|
+
# returning an updated complete configuration that can be stored.
|
106
|
+
def update_config(_current, _new)
|
107
|
+
return false
|
108
|
+
end
|
109
|
+
|
110
|
+
# Provide the default configuration state that should be used when creating a new config
|
111
|
+
def default_config
|
112
|
+
{}
|
113
|
+
end
|
114
|
+
|
115
|
+
# Get the schema for the configuration data that the plugin stores
|
116
|
+
def config_schema
|
117
|
+
{}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
require_rel 'plugins/*.rb'
|
@@ -0,0 +1,48 @@
|
|
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
|
+
# Top level module for the core Cyclid code.
|
17
|
+
module Cyclid
|
18
|
+
# Module for the Cyclid API
|
19
|
+
module API
|
20
|
+
# Module for Cyclid Plugins
|
21
|
+
module Plugins
|
22
|
+
# Base class for Action plugins
|
23
|
+
class Action < Base
|
24
|
+
def initialize(args = {})
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return the 'human' name for the plugin type
|
28
|
+
def self.human_name
|
29
|
+
'action'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Provide any additional run-time data, such as the transport &
|
33
|
+
# context, that the plugin will require for perform() but didn't get
|
34
|
+
# during initialize.
|
35
|
+
def prepare(args = {})
|
36
|
+
@transport = args[:transport]
|
37
|
+
@ctx = args[:ctx]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Run the Action.
|
41
|
+
def perform(log)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
require_rel 'action/*.rb'
|
@@ -0,0 +1,89 @@
|
|
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
|
+
# Top level module for the core Cyclid code.
|
17
|
+
module Cyclid
|
18
|
+
# Module for the Cyclid API
|
19
|
+
module API
|
20
|
+
# Module for Cyclid Plugins
|
21
|
+
module Plugins
|
22
|
+
# Command plugin
|
23
|
+
class Command < Action
|
24
|
+
Cyclid.logger.debug 'in the Command plugin'
|
25
|
+
|
26
|
+
def initialize(args = {})
|
27
|
+
args.symbolize_keys!
|
28
|
+
|
29
|
+
# At a bare minimum there has to be a command to execute.
|
30
|
+
raise 'a command action requires a command' unless args.include? :cmd
|
31
|
+
|
32
|
+
# The command & arguments can either be passed seperately, with the
|
33
|
+
# args as an array, or as a single string which we then split into
|
34
|
+
# a command & array of args.
|
35
|
+
if args.include? :args
|
36
|
+
@cmd = args[:cmd]
|
37
|
+
@args = args[:args]
|
38
|
+
else
|
39
|
+
cmd_args = args[:cmd].split
|
40
|
+
@cmd = cmd_args.shift
|
41
|
+
@args = cmd_args
|
42
|
+
end
|
43
|
+
|
44
|
+
Cyclid.logger.debug "cmd: '#{@cmd}' args: #{@args}"
|
45
|
+
|
46
|
+
@env = args[:env] if args.include? :env
|
47
|
+
@path = args[:path] if args.include? :path
|
48
|
+
end
|
49
|
+
|
50
|
+
# Note that we don't need to explicitly use the log for transport
|
51
|
+
# related tasks as the transport will take of writing any data from the
|
52
|
+
# commands into the log. The log is still passed in to perform() so that
|
53
|
+
# plugins can write their own data to it, as we do here by writing out
|
54
|
+
# the (optional) path & command that is being run.
|
55
|
+
def perform(log)
|
56
|
+
begin
|
57
|
+
# Export the environment data to the build host, if necesary
|
58
|
+
env = @env.interpolate(@ctx) if @env
|
59
|
+
@transport.export_env(env)
|
60
|
+
|
61
|
+
# Log the command being run (and the working directory, if one is
|
62
|
+
# set)
|
63
|
+
cmd_args = "#{@cmd} #{@args.join(' ')}"
|
64
|
+
log.write(@path.nil? ? "$ #{cmd_args}\n" : "$ #{@path} : #{cmd_args}\n")
|
65
|
+
|
66
|
+
# Interpolate any data from the job context
|
67
|
+
cmd_args = cmd_args % @ctx
|
68
|
+
|
69
|
+
# Interpolate the path if one is set
|
70
|
+
path = @path
|
71
|
+
path = path % @ctx unless path.nil?
|
72
|
+
|
73
|
+
# Run the command
|
74
|
+
success = @transport.exec(cmd_args, path)
|
75
|
+
rescue KeyError => ex
|
76
|
+
# Interpolation failed
|
77
|
+
log.write "#{ex.message}\n"
|
78
|
+
success = false
|
79
|
+
end
|
80
|
+
|
81
|
+
[success, @transport.exit_code]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Register this plugin
|
85
|
+
register_plugin 'command'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,207 @@
|
|
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 'mail'
|
17
|
+
require 'erb'
|
18
|
+
require 'premailer'
|
19
|
+
|
20
|
+
# Top level module for the core Cyclid code.
|
21
|
+
module Cyclid
|
22
|
+
# Module for the Cyclid API
|
23
|
+
module API
|
24
|
+
# Module for Cyclid Plugins
|
25
|
+
module Plugins
|
26
|
+
# Email notification plugin
|
27
|
+
class Email < Action
|
28
|
+
def initialize(args = {})
|
29
|
+
args.symbolize_keys!
|
30
|
+
|
31
|
+
raise 'an email action requires a message' unless args.include? :message
|
32
|
+
@message = args[:message]
|
33
|
+
|
34
|
+
raise 'an email action requires a recipient' unless args.include? :to
|
35
|
+
@to = args[:to]
|
36
|
+
|
37
|
+
@subject = args[:subject] || 'Cyclid notification'
|
38
|
+
@color = args[:color] || 'dodgerblue'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Send an email to the configured recipient; the message is rendered
|
42
|
+
# into text & HTML via. an ERB template which inserts additional
|
43
|
+
# information from the context
|
44
|
+
def perform(log)
|
45
|
+
begin
|
46
|
+
# Retrieve the server-wide email configuration
|
47
|
+
config = Cyclid.config.plugins
|
48
|
+
email_config = load_email_config(config)
|
49
|
+
|
50
|
+
Cyclid.logger.debug "sending via. #{email_config[:server]}:#{email_config[:port]} " \
|
51
|
+
"as #{email_config[:from]}"
|
52
|
+
|
53
|
+
# Add the job context
|
54
|
+
to = @to % @ctx
|
55
|
+
subject = @subject % @ctx
|
56
|
+
message = @message % @ctx
|
57
|
+
|
58
|
+
# Create a binding for the text & HTML ERB templates
|
59
|
+
info = { color: @color, title: subject }
|
60
|
+
|
61
|
+
bind = binding
|
62
|
+
bind.local_variable_set(:info, info)
|
63
|
+
bind.local_variable_set(:ctx, @ctx)
|
64
|
+
bind.local_variable_set(:message, message)
|
65
|
+
|
66
|
+
# Generate text email from a template
|
67
|
+
template_path = File.expand_path(File.join(__FILE__, '..', 'email', 'text.erb'))
|
68
|
+
template = ERB.new(File.read(template_path), nil, '%<>-')
|
69
|
+
|
70
|
+
text_body = template.result(bind)
|
71
|
+
|
72
|
+
# Generate the HTML email from a template
|
73
|
+
template_path = File.expand_path(File.join(__FILE__, '..', 'email', 'html.erb'))
|
74
|
+
template = ERB.new(File.read(template_path), nil, '%<>-')
|
75
|
+
|
76
|
+
html = template.result(bind)
|
77
|
+
|
78
|
+
# Run the HTML through Premailer to inline the styles
|
79
|
+
premailer = Premailer.new(html,
|
80
|
+
with_html_string: true,
|
81
|
+
warn_level: Premailer::Warnings::SAFE)
|
82
|
+
html_body = premailer.to_inline_css
|
83
|
+
|
84
|
+
# Create the email
|
85
|
+
mail = Mail.new
|
86
|
+
mail.from = email_config[:from]
|
87
|
+
mail.to = to
|
88
|
+
mail.subject = subject
|
89
|
+
mail.text_part do
|
90
|
+
body text_body
|
91
|
+
end
|
92
|
+
mail.html_part do
|
93
|
+
content_type 'text/html; charset=UTF8'
|
94
|
+
body html_body
|
95
|
+
end
|
96
|
+
|
97
|
+
# Deliver the email via. the configured server, using
|
98
|
+
# authentication if a username & password were provided.
|
99
|
+
log.write("sending email to #{@to}")
|
100
|
+
|
101
|
+
mail.delivery_method :smtp, address: email_config[:server],
|
102
|
+
port: email_config[:port],
|
103
|
+
user_name: email_config[:username],
|
104
|
+
password: email_config[:password]
|
105
|
+
mail.deliver
|
106
|
+
|
107
|
+
success = true
|
108
|
+
rescue StandardError => ex
|
109
|
+
log.write "#{ex.message}\n"
|
110
|
+
success = false
|
111
|
+
end
|
112
|
+
|
113
|
+
[success, 0]
|
114
|
+
end
|
115
|
+
|
116
|
+
# Register this plugin
|
117
|
+
register_plugin 'email'
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# Load the config for the email plugin and set defaults if they're not
|
122
|
+
# in the config
|
123
|
+
def load_email_config(config)
|
124
|
+
config.symbolize_keys!
|
125
|
+
|
126
|
+
server_config = config[:email] || {}
|
127
|
+
Cyclid.logger.debug "config=#{server_config}"
|
128
|
+
|
129
|
+
server_config.symbolize_keys!
|
130
|
+
|
131
|
+
# Set defaults where no server configuration is set
|
132
|
+
server_config[:server] ||= 'localhost'
|
133
|
+
server_config[:port] ||= 587
|
134
|
+
server_config[:from] ||= 'cyclid@cyclid.io'
|
135
|
+
|
136
|
+
server_config[:username] ||= nil
|
137
|
+
server_config[:password] ||= nil
|
138
|
+
|
139
|
+
# Load any organization specific configuration
|
140
|
+
plugin_data = self.class.get_config(@ctx[:organization])
|
141
|
+
Cyclid.logger.debug "using plugin config #{plugin_data}"
|
142
|
+
plugin_config = plugin_data['config']
|
143
|
+
|
144
|
+
# Merge the plugin configuration onto the server configuration (I.e.
|
145
|
+
# plugin configuration over-rides server configuration) to produce
|
146
|
+
# the final configuration
|
147
|
+
email_config = {}
|
148
|
+
email_config[:server] = plugin_config[:server] || server_config[:server]
|
149
|
+
email_config[:port] = plugin_config[:port] || server_config[:port]
|
150
|
+
email_config[:from] = plugin_config[:from] || server_config[:from]
|
151
|
+
|
152
|
+
email_config[:username] = plugin_config[:username] || server_config[:username]
|
153
|
+
email_config[:password] = plugin_config[:password] || server_config[:password]
|
154
|
+
|
155
|
+
return email_config
|
156
|
+
end
|
157
|
+
|
158
|
+
# Static methods for handling plugin config data
|
159
|
+
class << self
|
160
|
+
# Update the plugin configuration
|
161
|
+
def update_config(current, new)
|
162
|
+
current.merge! new
|
163
|
+
end
|
164
|
+
|
165
|
+
# Default configuration for the email plugin
|
166
|
+
def default_config
|
167
|
+
config = {}
|
168
|
+
config['server'] = nil
|
169
|
+
config['port'] = nil
|
170
|
+
config['from'] = nil
|
171
|
+
config['username'] = nil
|
172
|
+
config['password'] = nil
|
173
|
+
|
174
|
+
return config
|
175
|
+
end
|
176
|
+
|
177
|
+
# Config schema for the email plugin
|
178
|
+
def config_schema
|
179
|
+
schema = []
|
180
|
+
schema << { name: 'server',
|
181
|
+
type: 'string',
|
182
|
+
description: 'SMTP server for outgoing emails',
|
183
|
+
default: nil }
|
184
|
+
schema << { name: 'port',
|
185
|
+
type: 'integer',
|
186
|
+
description: 'SMTP server port to connect to',
|
187
|
+
default: nil }
|
188
|
+
schema << { name: 'from',
|
189
|
+
type: 'string',
|
190
|
+
description: 'Sender email address',
|
191
|
+
default: nil }
|
192
|
+
schema << { name: 'username',
|
193
|
+
type: 'string',
|
194
|
+
description: 'SMTP server username',
|
195
|
+
default: nil }
|
196
|
+
schema << { name: 'password',
|
197
|
+
type: 'string',
|
198
|
+
description: 'SMTP server password',
|
199
|
+
default: nil }
|
200
|
+
|
201
|
+
return schema
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|