Commit 2577ff7f authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Refactor plugins feature and make some doc improvements

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parent 4eed9a12
...@@ -4,6 +4,12 @@ class PluginWorker ...@@ -4,6 +4,12 @@ class PluginWorker
sidekiq_options retry: false sidekiq_options retry: false
def perform(file_name, data) def perform(file_name, data)
Gitlab::Plugin.execute(file_name, data) success, message = Gitlab::Plugin.execute(file_name, data)
unless success
Gitlab::PluginLogger.error("Plugin Error => #{file_name}: #{message}")
end
true
end end
end end
...@@ -11,7 +11,7 @@ A plugin will run on each event so it's up to you to filter events or projects w ...@@ -11,7 +11,7 @@ A plugin will run on each event so it's up to you to filter events or projects w
## Setup ## Setup
Plugins must be placed directly into `plugins` directory, subdirectories will be ignored. There is an `example` directory insider `plugins` where you can find some basic examples. Plugins must be placed directly into `plugins` directory, subdirectories will be ignored. There is an `example` directory inside `plugins` where you can find some basic examples.
Follow the steps below to set up a custom hook: Follow the steps below to set up a custom hook:
...@@ -20,11 +20,12 @@ Follow the steps below to set up a custom hook: ...@@ -20,11 +20,12 @@ Follow the steps below to set up a custom hook:
`/home/git/gitlab/plugins/`. For Omnibus installs the path is `/home/git/gitlab/plugins/`. For Omnibus installs the path is
usually `/opt/gitlab/embedded/service/gitlab-rails/plugins`. usually `/opt/gitlab/embedded/service/gitlab-rails/plugins`.
1. Inside the `plugins` directory, create a file with a name of your choice, but without spaces or special characters. 1. Inside the `plugins` directory, create a file with a name of your choice, but without spaces or special characters.
1. Make the hook file executable and make sure it's owned by git. 1. Make the hook file executable and make sure it's owned by the git user.
1. Write the code to make the plugin function as expected. Plugin can be 1. Write the code to make the plugin function as expected. Plugin can be
in any language. Ensure the 'shebang' at the top properly reflects the language in any language. Ensure the 'shebang' at the top properly reflects the language
type. For example, if the script is in Ruby the shebang will probably be type. For example, if the script is in Ruby the shebang will probably be
`#!/usr/bin/env ruby`. `#!/usr/bin/env ruby`.
1. The data to the plugin will be provided as JSON on STDIN. It will be exactly same as one for [system hooks]
That's it! Assuming the plugin code is properly implemented the hook will fire That's it! Assuming the plugin code is properly implemented the hook will fire
as appropriate. Plugins file list is updated for each event. There is no need to restart GitLab to apply a new plugin. as appropriate. Plugins file list is updated for each event. There is no need to restart GitLab to apply a new plugin.
......
...@@ -18,15 +18,9 @@ module Gitlab ...@@ -18,15 +18,9 @@ module Gitlab
end end
exit_status = result.status&.exitstatus exit_status = result.status&.exitstatus
[exit_status.zero?, result.stderr]
unless exit_status.zero?
Rails.logger.error("Plugin Error => #{file}: #{result.stderr}")
end
exit_status.zero?
rescue => e rescue => e
Rails.logger.error("Plugin Error => #{file}: #{e.message}") [false, e.message]
false
end end
end end
end end
module Gitlab
class PluginLogger < Gitlab::Logger
def self.file_name_noext
'plugin'
end
end
end
...@@ -4,12 +4,12 @@ namespace :plugins do ...@@ -4,12 +4,12 @@ namespace :plugins do
puts 'Validating plugins from /plugins directory' puts 'Validating plugins from /plugins directory'
Gitlab::Plugin.files.each do |file| Gitlab::Plugin.files.each do |file|
result = Gitlab::Plugin.execute(file, Gitlab::DataBuilder::Push::SAMPLE_DATA) success, message = Gitlab::Plugin.execute(file, Gitlab::DataBuilder::Push::SAMPLE_DATA)
if result if success
puts "* #{file} succeed (zero exit code)" puts "* #{file} succeed (zero exit code)."
else else
puts "* #{file} failure (non-zero exit code)" puts "* #{file} failure (non-zero exit code). #{message}"
end end
end end
end end
......
...@@ -5,6 +5,9 @@ describe Gitlab::Plugin do ...@@ -5,6 +5,9 @@ describe Gitlab::Plugin do
let(:data) { Gitlab::DataBuilder::Push::SAMPLE_DATA } let(:data) { Gitlab::DataBuilder::Push::SAMPLE_DATA }
let(:plugin) { Rails.root.join('plugins', 'test.rb') } let(:plugin) { Rails.root.join('plugins', 'test.rb') }
let(:tmp_file) { Tempfile.new('plugin-dump') } let(:tmp_file) { Tempfile.new('plugin-dump') }
let(:result) { described_class.execute(plugin.to_s, data) }
let(:success) { result.first }
let(:message) { result.last }
let(:plugin_source) do let(:plugin_source) do
<<~EOS <<~EOS
...@@ -22,8 +25,6 @@ describe Gitlab::Plugin do ...@@ -22,8 +25,6 @@ describe Gitlab::Plugin do
FileUtils.rm(plugin) FileUtils.rm(plugin)
end end
subject { described_class.execute(plugin.to_s, data) }
context 'successful execution' do context 'successful execution' do
before do before do
File.chmod(0o777, plugin) File.chmod(0o777, plugin)
...@@ -33,17 +34,19 @@ describe Gitlab::Plugin do ...@@ -33,17 +34,19 @@ describe Gitlab::Plugin do
tmp_file.close! tmp_file.close!
end end
it { is_expected.to be true } it { expect(success).to be true }
it { expect(message).to be_empty }
it 'ensures plugin received data via stdin' do it 'ensures plugin received data via stdin' do
subject result
expect(File.read(tmp_file.path)).to eq(data.to_json) expect(File.read(tmp_file.path)).to eq(data.to_json)
end end
end end
context 'non-executable' do context 'non-executable' do
it { is_expected.to be false } it { expect(success).to be false }
it { expect(message).to include('Permission denied') }
end end
context 'non-zero exit' do context 'non-zero exit' do
...@@ -58,7 +61,8 @@ describe Gitlab::Plugin do ...@@ -58,7 +61,8 @@ describe Gitlab::Plugin do
File.chmod(0o777, plugin) File.chmod(0o777, plugin)
end end
it { is_expected.to be false } it { expect(success).to be false }
it { expect(message).to be_empty }
end end
end end
end end
...@@ -3,16 +3,22 @@ require 'spec_helper' ...@@ -3,16 +3,22 @@ require 'spec_helper'
describe PluginWorker do describe PluginWorker do
include RepoHelpers include RepoHelpers
subject { described_class.new }
let(:filename) { 'my_plugin.rb' } let(:filename) { 'my_plugin.rb' }
let(:data) { { 'event_name' => 'project_create' } }
subject { described_class.new }
describe '#perform' do describe '#perform' do
it 'executes Gitlab::Plugin with expected values' do it 'executes Gitlab::Plugin with expected values' do
data = { 'event_name' => 'project_create' } allow(Gitlab::Plugin).to receive(:execute).with(filename, data).and_return([true, ''])
expect(subject.perform(filename, data)).to be_truthy
end
allow(Gitlab::Plugin).to receive(:execute).with(filename, data).and_return(true) it 'logs message in case of plugin execution failure' do
allow(Gitlab::Plugin).to receive(:execute).with(filename, data).and_return([false, 'permission denied'])
expect(Gitlab::PluginLogger).to receive(:error)
expect(subject.perform(filename, data)).to be_truthy expect(subject.perform(filename, data)).to be_truthy
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment