codebot 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE.md +32 -0
- data/.github/ISSUE_TEMPLATE/formatter_issue.md +20 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +26 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/CONTRIBUTING.md +15 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +75 -0
- data/LICENSE +21 -0
- data/README.md +230 -0
- data/Rakefile +29 -0
- data/bin/console +8 -0
- data/codebot.gemspec +49 -0
- data/exe/codebot +7 -0
- data/lib/codebot.rb +8 -0
- data/lib/codebot/channel.rb +134 -0
- data/lib/codebot/command_error.rb +17 -0
- data/lib/codebot/config.rb +125 -0
- data/lib/codebot/configuration_error.rb +17 -0
- data/lib/codebot/core.rb +76 -0
- data/lib/codebot/cryptography.rb +38 -0
- data/lib/codebot/event.rb +62 -0
- data/lib/codebot/ext/cinch/ssl_extensions.rb +37 -0
- data/lib/codebot/formatter.rb +242 -0
- data/lib/codebot/formatters.rb +109 -0
- data/lib/codebot/formatters/.rubocop.yml +2 -0
- data/lib/codebot/formatters/commit_comment.rb +43 -0
- data/lib/codebot/formatters/fork.rb +40 -0
- data/lib/codebot/formatters/gitlab_issue_hook.rb +56 -0
- data/lib/codebot/formatters/gitlab_job_hook.rb +77 -0
- data/lib/codebot/formatters/gitlab_merge_request_hook.rb +57 -0
- data/lib/codebot/formatters/gitlab_note_hook.rb +119 -0
- data/lib/codebot/formatters/gitlab_pipeline_hook.rb +51 -0
- data/lib/codebot/formatters/gitlab_push_hook.rb +83 -0
- data/lib/codebot/formatters/gitlab_wiki_page_hook.rb +56 -0
- data/lib/codebot/formatters/gollum.rb +67 -0
- data/lib/codebot/formatters/issue_comment.rb +41 -0
- data/lib/codebot/formatters/issues.rb +41 -0
- data/lib/codebot/formatters/ping.rb +79 -0
- data/lib/codebot/formatters/public.rb +30 -0
- data/lib/codebot/formatters/pull_request.rb +71 -0
- data/lib/codebot/formatters/pull_request_review_comment.rb +49 -0
- data/lib/codebot/formatters/push.rb +172 -0
- data/lib/codebot/formatters/watch.rb +38 -0
- data/lib/codebot/integration.rb +195 -0
- data/lib/codebot/integration_manager.rb +225 -0
- data/lib/codebot/ipc_client.rb +83 -0
- data/lib/codebot/ipc_server.rb +79 -0
- data/lib/codebot/irc_client.rb +102 -0
- data/lib/codebot/irc_connection.rb +156 -0
- data/lib/codebot/message.rb +37 -0
- data/lib/codebot/metadata.rb +15 -0
- data/lib/codebot/network.rb +240 -0
- data/lib/codebot/network_manager.rb +181 -0
- data/lib/codebot/options.rb +49 -0
- data/lib/codebot/options/base.rb +55 -0
- data/lib/codebot/options/core.rb +126 -0
- data/lib/codebot/options/integration.rb +101 -0
- data/lib/codebot/options/network.rb +109 -0
- data/lib/codebot/payload.rb +32 -0
- data/lib/codebot/request.rb +51 -0
- data/lib/codebot/sanitizers.rb +130 -0
- data/lib/codebot/serializable.rb +101 -0
- data/lib/codebot/shortener.rb +43 -0
- data/lib/codebot/thread_controller.rb +70 -0
- data/lib/codebot/user_error.rb +13 -0
- data/lib/codebot/validation_error.rb +17 -0
- data/lib/codebot/web_listener.rb +107 -0
- data/lib/codebot/web_server.rb +58 -0
- data/webhook.png +0 -0
- metadata +249 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'codebot/command_error'
|
4
|
+
|
5
|
+
module Codebot
|
6
|
+
# This class manages the networks associated with a configuration.
|
7
|
+
class NetworkManager
|
8
|
+
# @return [Config] the configuration managed by this class
|
9
|
+
attr_reader :config
|
10
|
+
|
11
|
+
# Constructs a new network manager for a specified configuration.
|
12
|
+
#
|
13
|
+
# @param config [Config] the configuration to manage
|
14
|
+
def initialize(config)
|
15
|
+
@config = config
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates a new network from the given parameters.
|
19
|
+
#
|
20
|
+
# @param params [Hash] the parameters to initialize the network with
|
21
|
+
def create(params)
|
22
|
+
network = Network.new(params.merge(config: {}))
|
23
|
+
@config.transaction do
|
24
|
+
check_name_available!(network.name)
|
25
|
+
@config.networks << network
|
26
|
+
network_feedback(network, :created) unless params[:quiet]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Updates a network with the given parameters.
|
31
|
+
#
|
32
|
+
# @param name [String] the current name of the network to update
|
33
|
+
# @param params [Hash] the parameters to update the network with
|
34
|
+
def update(name, params)
|
35
|
+
@config.transaction do
|
36
|
+
network = find_network!(name)
|
37
|
+
unless params[:name].nil?
|
38
|
+
check_name_available_except!(params[:name], network)
|
39
|
+
end
|
40
|
+
network.update!(params)
|
41
|
+
network_feedback(network, :updated) unless params[:quiet]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Destroys a network.
|
46
|
+
#
|
47
|
+
# @param name [String] the name of the network to destroy
|
48
|
+
# @param params [Hash] the command-line options
|
49
|
+
def destroy(name, params)
|
50
|
+
@config.transaction do
|
51
|
+
network = find_network!(name)
|
52
|
+
@config.networks.delete network
|
53
|
+
network_feedback(network, :destroyed) unless params[:quiet]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Lists all networks, or networks with names containing the given search
|
58
|
+
# term.
|
59
|
+
#
|
60
|
+
# @param search [String, nil] an optional search term
|
61
|
+
def list(search)
|
62
|
+
@config.transaction do
|
63
|
+
networks = @config.networks.dup
|
64
|
+
unless search.nil?
|
65
|
+
networks.select! { |net| net.name.downcase.include? search.downcase }
|
66
|
+
end
|
67
|
+
puts 'No networks found' if networks.empty?
|
68
|
+
networks.each { |net| show_network net }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Finds a network given its name.
|
73
|
+
#
|
74
|
+
# @param name [String] the name to search for
|
75
|
+
# @return [Network, nil] the network, or +nil+ if none was found
|
76
|
+
def find_network(name)
|
77
|
+
@config.networks.find { |net| net.name_eql? name }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Finds a network given its name.
|
81
|
+
#
|
82
|
+
# @param name [String] the name to search for
|
83
|
+
# @raise [CommandError] if no network with the given name exists
|
84
|
+
# @return [Network] the network
|
85
|
+
def find_network!(name)
|
86
|
+
network = find_network(name)
|
87
|
+
return network unless network.nil?
|
88
|
+
|
89
|
+
raise CommandError, "a network with the name #{name.inspect} " \
|
90
|
+
'does not exist'
|
91
|
+
end
|
92
|
+
|
93
|
+
# Checks that all channels associated with an integration belong to a valid
|
94
|
+
# network.
|
95
|
+
#
|
96
|
+
# @param integration [Integration] the integration to check
|
97
|
+
def check_channels!(integration)
|
98
|
+
integration.channels.map(&:network).map(&:name).each do |network|
|
99
|
+
find_network!(network)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# Checks that the specified name is available for use.
|
106
|
+
#
|
107
|
+
# @param name [String] the name to check for
|
108
|
+
# @raise [CommandError] if the name is already taken
|
109
|
+
def check_name_available!(name)
|
110
|
+
return if name.nil? || !find_network(name)
|
111
|
+
|
112
|
+
raise CommandError, "a network with the name #{name.inspect} " \
|
113
|
+
'already exists'
|
114
|
+
end
|
115
|
+
|
116
|
+
# Checks that the specified name is available for use by the specified
|
117
|
+
# network.
|
118
|
+
#
|
119
|
+
# @param name [String] the name to check for
|
120
|
+
# @param network [Network] the network to ignore
|
121
|
+
# @raise [CommandError] if the name is already taken
|
122
|
+
def check_name_available_except!(name, network)
|
123
|
+
return if name.nil? || network.name_eql?(name) || !find_network(name)
|
124
|
+
|
125
|
+
raise CommandError, "a network with the name #{name.inspect} " \
|
126
|
+
'already exists'
|
127
|
+
end
|
128
|
+
|
129
|
+
# Displays feedback about a change made to a network.
|
130
|
+
#
|
131
|
+
# @param network [Network] the network
|
132
|
+
# @param action [#to_s] the action (+:created+, +:updated+ or +:destroyed+)
|
133
|
+
def network_feedback(network, action)
|
134
|
+
puts "Network was successfully #{action}"
|
135
|
+
show_network(network)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Prints information about a network.
|
139
|
+
#
|
140
|
+
# @param network [Network] the network
|
141
|
+
def show_network(network) # rubocop:disable Metrics/AbcSize
|
142
|
+
puts "Network: #{network.name}"
|
143
|
+
security = "#{network.secure ? 'secure' : 'insecure'} connection"
|
144
|
+
password = network.server_password
|
145
|
+
puts "\tServer: #{network.host}:#{network.real_port} (#{security})"
|
146
|
+
puts "\tPassword: #{'*' * password.length}" unless password.to_s.empty?
|
147
|
+
puts "\tNickname: #{network.nick}"
|
148
|
+
puts "\tBind to: #{network.bind}" unless network.bind.to_s.empty?
|
149
|
+
puts "\tUser modes: #{network.modes}" unless network.modes.to_s.empty?
|
150
|
+
show_network_sasl(network)
|
151
|
+
show_network_nickserv(network)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Prints information about the SASL authentication settings for a network.
|
155
|
+
#
|
156
|
+
# @param network [Network] the network
|
157
|
+
def show_network_sasl(network)
|
158
|
+
puts "\tSASL authentication #{network.sasl? ? 'enabled' : 'disabled'}"
|
159
|
+
return unless network.sasl?
|
160
|
+
|
161
|
+
puts "\t\tUsername: #{network.sasl_username}"
|
162
|
+
puts "\t\tPassword: #{'*' * network.sasl_password.to_s.length}"
|
163
|
+
end
|
164
|
+
|
165
|
+
def nickserv_status(network)
|
166
|
+
network.nickserv? ? 'enabled' : 'disabled'
|
167
|
+
end
|
168
|
+
|
169
|
+
# Prints information about the NickServ authentication
|
170
|
+
# settings for a network.
|
171
|
+
#
|
172
|
+
# @param network [Network] the network
|
173
|
+
def show_network_nickserv(network)
|
174
|
+
puts "\tNickServ authentication #{nickserv_status(network)}"
|
175
|
+
return unless network.nickserv?
|
176
|
+
|
177
|
+
puts "\t\tUsername: #{network.nickserv_username}"
|
178
|
+
puts "\t\tPassword: #{'*' * network.nickserv_password.to_s.length}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
require 'codebot/options/base'
|
6
|
+
|
7
|
+
module Codebot
|
8
|
+
# This module provides functionality for parsing command-line options.
|
9
|
+
module Options
|
10
|
+
# Creates a new {Core} from the specified command-line options. Errors of
|
11
|
+
# type {UserError} are handled if they occur in the given block.
|
12
|
+
#
|
13
|
+
# @param opts [Hash] the options to initialize the core with
|
14
|
+
# @param rehash [Boolean] whether to ask a running instance to rehash its
|
15
|
+
# configuration after invoking the block
|
16
|
+
# @yield [Core] the newly created {Core}
|
17
|
+
def self.with_core(opts, rehash = false)
|
18
|
+
core = ::Codebot::Core.new(
|
19
|
+
config_file: opts[:config],
|
20
|
+
ipc_pipe: opts[:pipe]
|
21
|
+
)
|
22
|
+
with_errors { yield core }
|
23
|
+
return unless rehash
|
24
|
+
|
25
|
+
with_ipc_client(opts) do |ipc|
|
26
|
+
break unless ipc.send_rehash(!opts[:pipe].nil?)
|
27
|
+
|
28
|
+
puts 'Rehashing the running instance...' unless opts[:quiet]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Invokes the given block, handling {UserError} errors.
|
33
|
+
def self.with_errors
|
34
|
+
yield
|
35
|
+
rescue UserError => e
|
36
|
+
STDERR.puts "Error: #{e.message}"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
|
40
|
+
# Creates a new {IPCClient} from the specified command-line options.
|
41
|
+
# Errors of type {UserError} are handled if they occur in the given block.
|
42
|
+
#
|
43
|
+
# @param opts [Hash] the options to initialize the client with
|
44
|
+
# @yield [IPCClient] the newly created {IPCClient}
|
45
|
+
def self.with_ipc_client(opts)
|
46
|
+
with_errors { yield IPCClient.new(opts[:pipe]) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
require 'codebot/options/core'
|
6
|
+
require 'codebot/options/network'
|
7
|
+
require 'codebot/options/integration'
|
8
|
+
require 'codebot/user_error'
|
9
|
+
|
10
|
+
module Codebot
|
11
|
+
module Options
|
12
|
+
# A class that handles the +codebot+ command. This class delegates handling
|
13
|
+
# of any commands to the respective class for the appropriate subcommand.
|
14
|
+
class Base < Thor
|
15
|
+
check_unknown_options!
|
16
|
+
|
17
|
+
class_option :config,
|
18
|
+
banner: 'FILE',
|
19
|
+
aliases: '-C',
|
20
|
+
desc: 'Use the specified alternate configuration file'
|
21
|
+
class_option :pipe,
|
22
|
+
banner: 'FILE',
|
23
|
+
aliases: '-P',
|
24
|
+
desc: 'Use the specified alternate named pipe'
|
25
|
+
class_option :quiet,
|
26
|
+
type: :boolean,
|
27
|
+
default: false,
|
28
|
+
aliases: '-q',
|
29
|
+
desc: 'Hide status information'
|
30
|
+
|
31
|
+
desc 'core [OPTIONS]', 'Manage a Codebot core'
|
32
|
+
subcommand 'core', Core
|
33
|
+
|
34
|
+
desc 'network [OPTIONS]', 'Manage IRC networks'
|
35
|
+
subcommand 'network', Network
|
36
|
+
|
37
|
+
desc 'integration [OPTIONS]', 'Manage integrations'
|
38
|
+
subcommand 'integration', Integration
|
39
|
+
|
40
|
+
# Ensures that thor uses the correct exit code.
|
41
|
+
#
|
42
|
+
# @return true
|
43
|
+
def self.exit_on_failure?
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
if Process.uid.zero? || Process.euid.zero?
|
48
|
+
STDERR.puts 'Running Codebot as root is extremely dangerous; ' \
|
49
|
+
"if you're trying to listen on a privileged port " \
|
50
|
+
'please use a gateway server instead'
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'codebot/ipc_client'
|
4
|
+
|
5
|
+
module Codebot
|
6
|
+
module Options
|
7
|
+
# A class that handles the +codebot core+ command.
|
8
|
+
class Core < Thor
|
9
|
+
check_unknown_options!
|
10
|
+
|
11
|
+
desc 'interactive', 'Run Codebot interactively'
|
12
|
+
|
13
|
+
# Runs Codebot interactively.
|
14
|
+
def interactive
|
15
|
+
run_core(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'start', 'Start a new Codebot instance in the background'
|
19
|
+
|
20
|
+
# Starts a new Codebot instance in the background.
|
21
|
+
def start
|
22
|
+
Options.with_errors { check_fork_supported! }
|
23
|
+
check_not_running!(options)
|
24
|
+
fork { run_core(false) }
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'stop', 'Stop a running Codebot instance'
|
28
|
+
|
29
|
+
# Stops a running Codebot instance.
|
30
|
+
def stop
|
31
|
+
Options.with_ipc_client(options, &:send_stop)
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'rehash', 'Reload the configuration of a running Codebot instance'
|
35
|
+
|
36
|
+
# Reloads the configuration of a running Codebot instance.
|
37
|
+
def rehash
|
38
|
+
Options.with_ipc_client(options, &:send_rehash)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Ensures that thor uses the correct exit code.
|
42
|
+
#
|
43
|
+
# @return true
|
44
|
+
def self.exit_on_failure?
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Ensures that a Codebot instance using the same pipe is not already
|
51
|
+
# running.
|
52
|
+
#
|
53
|
+
# @param opts [Hash] a hash containing the options that would be used for
|
54
|
+
# initializing a new core; specifically, a hash
|
55
|
+
# containing the +:pipe+ key to indicate the path to
|
56
|
+
# the named pipe used by the IPC server.
|
57
|
+
# @raise [CommandError] if the named pipe already exists
|
58
|
+
def check_not_running!(opts)
|
59
|
+
Options.with_ipc_client(opts) do |ipc|
|
60
|
+
break unless ipc.pipe_exist?
|
61
|
+
|
62
|
+
raise CommandError, 'named pipe already exists; if you are sure a ' \
|
63
|
+
'Codebot instance is not already running, you ' \
|
64
|
+
"can delete #{ipc.pipe.inspect}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Ensures that the current platform supports the Process::fork method,
|
69
|
+
# raising an error if it does not.
|
70
|
+
#
|
71
|
+
# @raise [CommandError] if forking is not supported
|
72
|
+
def check_fork_supported!
|
73
|
+
return if Process.respond_to?(:fork)
|
74
|
+
|
75
|
+
raise CommandError, 'this feature is not available on ' \
|
76
|
+
"#{RUBY_PLATFORM}; please use the " \
|
77
|
+
"'interactive' command instead"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Reopens the standard file descriptors to prevent the forked process
|
81
|
+
# from inheriting the file descriptors of the parent process. This method
|
82
|
+
# reopens streams using a method similar to the Unix dup2 function.
|
83
|
+
#
|
84
|
+
# @param sin [File] the file to redirect into the standard input stream,
|
85
|
+
# or +nil+ to detach and immediately close the stream.
|
86
|
+
# @param sout [File] the file to redirect the standard output stream to,
|
87
|
+
# or +nil+ to discard any data written to the stream.
|
88
|
+
# @param serr [File] the file to redirect the standard error stream to,
|
89
|
+
# or +nil+ to discard any data written to the stream.
|
90
|
+
def dup2_fds(sin: nil, sout: nil, serr: nil)
|
91
|
+
$stdin.reopen(sin || null_file('r'))
|
92
|
+
$stdout.reopen(sout || null_file('w'))
|
93
|
+
$stderr.reopen(serr || null_file('w'))
|
94
|
+
end
|
95
|
+
|
96
|
+
# Creates a new null file.
|
97
|
+
#
|
98
|
+
# @param mode [String] the mode to open the file in
|
99
|
+
# @return [File] the created file
|
100
|
+
def null_file(mode)
|
101
|
+
File.new(File::NULL, mode)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Initializes any missing environment variables to their default values.
|
105
|
+
def initialize_environment
|
106
|
+
ENV['CODEBOT_PORT'] ||= 4567.to_s
|
107
|
+
ENV['RACK_ENV'] ||= 'production'
|
108
|
+
end
|
109
|
+
|
110
|
+
# Starts the bot. Unless started in interactive mode, file descriptors
|
111
|
+
# are reopened from a null file.
|
112
|
+
#
|
113
|
+
# @param interactive [Boolean] whether to start the bot in the foreground
|
114
|
+
def run_core(interactive)
|
115
|
+
initialize_environment
|
116
|
+
check_not_running!(options)
|
117
|
+
dup2_fds unless interactive
|
118
|
+
Options.with_core(options) do |core|
|
119
|
+
core.trap_signals
|
120
|
+
core.start
|
121
|
+
core.join
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'codebot/integration_manager'
|
4
|
+
|
5
|
+
module Codebot
|
6
|
+
module Options
|
7
|
+
# A class that handles the +codebot integration+ command.
|
8
|
+
class Integration < Thor
|
9
|
+
check_unknown_options!
|
10
|
+
|
11
|
+
# Sets shared options for specifying properties belonging to the
|
12
|
+
# {::Codebot::Integration} class.
|
13
|
+
def self.shared_propery_options
|
14
|
+
option :endpoint, aliases: '-e',
|
15
|
+
desc: 'Set the endpoint for incoming webhooks'
|
16
|
+
option :secret, aliases: '-s',
|
17
|
+
desc: 'Set the secret for verifying webhook payloads'
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'create NAME', 'Add a new integration'
|
21
|
+
shared_propery_options
|
22
|
+
option :channels, aliases: '-c', type: :array,
|
23
|
+
desc: 'Set the channels to deliver notifications to'
|
24
|
+
|
25
|
+
# Creates a new integration with the specified name.
|
26
|
+
#
|
27
|
+
# @param name [String] the name of the new integration
|
28
|
+
def create(name)
|
29
|
+
Options.with_core(parent_options, true) do |core|
|
30
|
+
map_channels!(options, :channels)
|
31
|
+
IntegrationManager.new(core.config).create(options.merge(name: name))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'update NAME', 'Edit an integration'
|
36
|
+
option :name, aliases: '-n',
|
37
|
+
banner: 'NEW-NAME',
|
38
|
+
desc: 'Rename this integration'
|
39
|
+
shared_propery_options
|
40
|
+
option :add_channel, aliases: '-a', type: :array,
|
41
|
+
desc: 'Add a channel to this integration'
|
42
|
+
option :clear_channels, aliases: '-c', type: :boolean,
|
43
|
+
desc: 'Clear the channel list ' \
|
44
|
+
'(default: false)'
|
45
|
+
option :delete_channel, aliases: '-d', type: :array,
|
46
|
+
desc: 'Delete a channel from this integration'
|
47
|
+
|
48
|
+
# Updates the integration with the specified name.
|
49
|
+
#
|
50
|
+
# @param name [String] the name of the integration
|
51
|
+
def update(name)
|
52
|
+
Options.with_core(parent_options, true) do |core|
|
53
|
+
map_channels!(options, :add_channel)
|
54
|
+
IntegrationManager.new(core.config).update(name, options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'destroy NAME', 'Delete an integration'
|
59
|
+
|
60
|
+
# Destroys the integration with the specified name.
|
61
|
+
#
|
62
|
+
# @param name [String] the name of the integration
|
63
|
+
def destroy(name)
|
64
|
+
Options.with_core(parent_options, true) do |core|
|
65
|
+
IntegrationManager.new(core.config).destroy(name, options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'list [SEARCH]', 'List integrations'
|
70
|
+
|
71
|
+
# Lists all integrations, or integrations with names containing the given
|
72
|
+
# search term.
|
73
|
+
#
|
74
|
+
# @param search [String, nil] an optional search term
|
75
|
+
def list(search = nil)
|
76
|
+
Options.with_core(parent_options, true) do |core|
|
77
|
+
IntegrationManager.new(core.config).list(search)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Ensures that thor uses the correct exit code.
|
82
|
+
#
|
83
|
+
# @return true
|
84
|
+
def self.exit_on_failure?
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Destructively converts an array of channel identifiers contained in a
|
91
|
+
# hash into the serialized form of the channels contained in the array.
|
92
|
+
# If the value +hash[key]+ is +nil+, no action is taken.
|
93
|
+
#
|
94
|
+
# @param hash [Hash] the hash containing the array of identifiers
|
95
|
+
# @param key [Object] the key corresponding to the array of identifiers
|
96
|
+
def map_channels!(hash, key)
|
97
|
+
hash[key] = hash[key].map { |id| [id, {}] }.to_h unless hash[key].nil?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|