cyclid 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|