From fd598fd6bda91343d7e271c0fe485ead9e0011d2 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Fri, 23 Feb 2018 21:23:27 +0100 Subject: [PATCH 01/53] Removed protected_tags webpack bundle --- .../pages/projects/settings/repository/show/index.js | 10 +++++++++- app/assets/javascripts/protected_tags/index.js | 9 --------- app/views/projects/protected_tags/_index.html.haml | 3 --- config/webpack.config.js | 1 - 4 files changed, 9 insertions(+), 14 deletions(-) delete mode 100644 app/assets/javascripts/protected_tags/index.js diff --git a/app/assets/javascripts/pages/projects/settings/repository/show/index.js b/app/assets/javascripts/pages/projects/settings/repository/show/index.js index d88527351c1..f46372faed8 100644 --- a/app/assets/javascripts/pages/projects/settings/repository/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/repository/show/index.js @@ -1,3 +1,11 @@ +/* eslint-disable no-new */ + +import ProtectedTagCreate from '~/protected_tags/protected_tag_create'; +import ProtectedTagEditList from '~/protected_tags/protected_tag_edit_list'; import initSettingsPanels from '~/settings_panels'; -document.addEventListener('DOMContentLoaded', initSettingsPanels); +document.addEventListener('DOMContentLoaded', () => { + new ProtectedTagCreate(); + new ProtectedTagEditList(); + initSettingsPanels(); +}); diff --git a/app/assets/javascripts/protected_tags/index.js b/app/assets/javascripts/protected_tags/index.js deleted file mode 100644 index b1618e24e49..00000000000 --- a/app/assets/javascripts/protected_tags/index.js +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-disable no-unused-vars */ - -import ProtectedTagCreate from './protected_tag_create'; -import ProtectedTagEditList from './protected_tag_edit_list'; - -$(() => { - const protectedtTagCreate = new ProtectedTagCreate(); - const protectedtTagEditList = new ProtectedTagEditList(); -}); diff --git a/app/views/projects/protected_tags/_index.html.haml b/app/views/projects/protected_tags/_index.html.haml index 74f7f63c941..6b284fda35c 100644 --- a/app/views/projects/protected_tags/_index.html.haml +++ b/app/views/projects/protected_tags/_index.html.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('protected_tags') - - content_for :create_protected_tag do = render 'projects/protected_tags/create_protected_tag' diff --git a/config/webpack.config.js b/config/webpack.config.js index 9db6ecd27af..7f3b279b062 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -71,7 +71,6 @@ var config = { profile: './profile/profile_bundle.js', project_import_gl: './projects/project_import_gitlab_project.js', protected_branches: './protected_branches', - protected_tags: './protected_tags', registry_list: './registry/index.js', sidebar: './sidebar/sidebar_bundle.js', snippet: './snippet/snippet_bundle.js', From 958c0a2c45d7b7e7c2fbb49feb59ff5c00fd2005 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Wed, 14 Feb 2018 15:39:59 -0500 Subject: [PATCH 02/53] Refactor pipelines show for webpack bundle tag removal --- .../pages/projects/pipelines/show/index.js | 71 ++++++++++++++++++- app/views/projects/pipelines/show.html.haml | 1 - 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/pages/projects/pipelines/show/index.js b/app/assets/javascripts/pages/projects/pipelines/show/index.js index fbe9824c34b..c02c8631bb2 100644 --- a/app/assets/javascripts/pages/projects/pipelines/show/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/show/index.js @@ -1,3 +1,72 @@ + +import Vue from 'vue'; +import Flash from '../../../../flash'; +import PipelinesMediator from '../../../../pipelines/pipeline_details_mediatior'; +import pipelineGraph from '../../../../pipelines/components/graph/graph_component.vue'; +import pipelineHeader from '../../../../pipelines/components/header_component.vue'; +import eventHub from '../../../../pipelines/event_hub'; import initPipelines from '../init_pipelines'; -document.addEventListener('DOMContentLoaded', initPipelines); +document.addEventListener('DOMContentLoaded', () => { + const dataset = document.querySelector('.js-pipeline-details-vue').dataset; + + const mediator = new PipelinesMediator({ endpoint: dataset.endpoint }); + + mediator.fetchPipeline(); + initPipelines(); + + // eslint-disable-next-line + new Vue({ + el: '#js-pipeline-graph-vue', + components: { + pipelineGraph, + }, + data() { + return { + mediator, + }; + }, + render(createElement) { + return createElement('pipeline-graph', { + props: { + isLoading: this.mediator.state.isLoading, + pipeline: this.mediator.store.state.pipeline, + }, + }); + }, + }); + + // eslint-disable-next-line + new Vue({ + el: '#js-pipeline-header-vue', + components: { + pipelineHeader, + }, + data() { + return { + mediator, + }; + }, + created() { + eventHub.$on('headerPostAction', this.postAction); + }, + beforeDestroy() { + eventHub.$off('headerPostAction', this.postAction); + }, + methods: { + postAction(action) { + this.mediator.service.postAction(action.path) + .then(() => this.mediator.refreshPipeline()) + .catch(() => new Flash('An error occurred while making the request.')); + }, + }, + render(createElement) { + return createElement('pipeline-header', { + props: { + isLoading: this.mediator.state.isLoading, + pipeline: this.mediator.store.state.pipeline, + }, + }); + }, + }); +}); diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index 2174154b207..ffb0ae95f9b 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -13,4 +13,3 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag('common_vue') - = webpack_bundle_tag('pipelines_details') From 57a7a89820217c1d9a99b1387f655c0ed996a345 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Wed, 14 Feb 2018 15:49:47 -0500 Subject: [PATCH 03/53] Remove webpack bundle --- app/assets/javascripts/pages/projects/pipelines/show/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/pages/projects/pipelines/show/index.js b/app/assets/javascripts/pages/projects/pipelines/show/index.js index c02c8631bb2..ff5702c76f2 100644 --- a/app/assets/javascripts/pages/projects/pipelines/show/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/show/index.js @@ -1,5 +1,6 @@ import Vue from 'vue'; +import { __ } from '../../../../locale'; import Flash from '../../../../flash'; import PipelinesMediator from '../../../../pipelines/pipeline_details_mediatior'; import pipelineGraph from '../../../../pipelines/components/graph/graph_component.vue'; @@ -57,7 +58,7 @@ document.addEventListener('DOMContentLoaded', () => { postAction(action) { this.mediator.service.postAction(action.path) .then(() => this.mediator.refreshPipeline()) - .catch(() => new Flash('An error occurred while making the request.')); + .catch(() => new Flash(__('An error occurred while making the request.'))); }, }, render(createElement) { From 17b3f5316d19d1f81088570136116e1102fc1d3e Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Fri, 16 Feb 2018 10:56:04 -0500 Subject: [PATCH 04/53] Fix conflicts --- config/webpack.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 0e96a2f6941..1cdd1d83baf 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -66,7 +66,6 @@ var config = { notebook_viewer: './blob/notebook_viewer.js', pdf_viewer: './blob/pdf_viewer.js', pipelines: './pipelines/pipelines_bundle.js', - pipelines_details: './pipelines/pipeline_details_bundle.js', profile: './profile/profile_bundle.js', project_import_gl: './projects/project_import_gitlab_project.js', protected_branches: './protected_branches', From 124ce3b6e31f2da678629eb9bf68c2f92aa1bd55 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Fri, 23 Feb 2018 16:17:43 -0500 Subject: [PATCH 05/53] Fix failing paths --- .../cycle_analytics/cycle_analytics_bundle.js | 4 ++-- .../pages/projects/pipelines/show/index.js | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js index 034f2923b3b..f0a9fab15e7 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js @@ -14,10 +14,10 @@ import CycleAnalyticsStore from './cycle_analytics_store'; Vue.use(Translate); -$(() => { +document.addEventListener('DOMContentLoaded', () => { const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed'; - gl.cycleAnalyticsApp = new Vue({ + const cycleAnalyticsApp = new Vue({ el: '#cycle-analytics', name: 'CycleAnalytics', components: { diff --git a/app/assets/javascripts/pages/projects/pipelines/show/index.js b/app/assets/javascripts/pages/projects/pipelines/show/index.js index ff5702c76f2..a49a71afd37 100644 --- a/app/assets/javascripts/pages/projects/pipelines/show/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/show/index.js @@ -1,11 +1,11 @@ import Vue from 'vue'; -import { __ } from '../../../../locale'; -import Flash from '../../../../flash'; -import PipelinesMediator from '../../../../pipelines/pipeline_details_mediatior'; -import pipelineGraph from '../../../../pipelines/components/graph/graph_component.vue'; -import pipelineHeader from '../../../../pipelines/components/header_component.vue'; -import eventHub from '../../../../pipelines/event_hub'; +import { __ } from '~/locale'; +import Flash from '~/flash'; +import PipelinesMediator from '~/pipelines/pipeline_details_mediator'; +import pipelineGraph from '~/pipelines/components/graph/graph_component.vue'; +import pipelineHeader from '~/pipelines/components/header_component.vue'; +import eventHub from '~/pipelines/event_hub'; import initPipelines from '../init_pipelines'; document.addEventListener('DOMContentLoaded', () => { From e6af20384b6c1a717fa1729a71d4634a308c14d2 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 8 Feb 2018 17:00:09 +0200 Subject: [PATCH 06/53] Add plugins dir Signed-off-by: Dmitriy Zaporozhets --- plugins/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/.gitkeep diff --git a/plugins/.gitkeep b/plugins/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d From 1ffa07e6f35fb7832ba8c6fd764da6c201e521bd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 8 Feb 2018 17:00:25 +0200 Subject: [PATCH 07/53] Ignore content in plugins dir Signed-off-by: Dmitriy Zaporozhets --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2004c2a09b4..fa39ae01ff0 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,4 @@ eslint-report.html /locale/**/LC_MESSAGES /locale/**/*.time_stamp /.rspec +/plugins/* From 9be0c2734ae3e3cc84196cf167a0c327c7cf8570 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 8 Feb 2018 17:51:02 +0200 Subject: [PATCH 08/53] Add external plugins support to extend system hooks Signed-off-by: Dmitriy Zaporozhets --- app/services/system_hooks_service.rb | 9 +++++++++ config/application.rb | 1 + config/initializers/9_plugins.rb | 29 ++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 config/initializers/9_plugins.rb diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index a6b7a6e1416..71de74e5a82 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -11,6 +11,15 @@ class SystemHooksService SystemHook.hooks_for(hooks_scope).find_each do |hook| hook.async_execute(data, 'system_hooks') end + + # Execute external plugins + PLUGINS.each do |plugin| + begin + plugin.new.execute(data) + rescue => e + Rails.logger.warn("GitLab -> Plugins -> #{plugin.class.name} raised an axception during execution. #{e}") + end + end end private diff --git a/config/application.rb b/config/application.rb index 918bd4d57cf..f2fc6270748 100644 --- a/config/application.rb +++ b/config/application.rb @@ -28,6 +28,7 @@ module Gitlab config.eager_load_paths.push(*%W[#{config.root}/lib #{config.root}/app/models/hooks #{config.root}/app/models/members + #{config.root}/plugins #{config.root}/app/models/project_services #{config.root}/app/workers/concerns #{config.root}/app/services/concerns diff --git a/config/initializers/9_plugins.rb b/config/initializers/9_plugins.rb new file mode 100644 index 00000000000..9f252ccd296 --- /dev/null +++ b/config/initializers/9_plugins.rb @@ -0,0 +1,29 @@ +class PluginsSystem + attr_accessor :plugins, :files + + def initialize + @files = Dir.glob(Rails.root.join('plugins', '*_plugin.rb')) + end + + def valid_plugins + files.map do |file| + file_name = File.basename(file, '.rb') + + # Just give sample data to method and expect it to not crash. + begin + klass = Object.const_get(file_name.classify) + klass.new.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA) + rescue => e + Rails.logger.warn("GitLab -> Plugins -> #{file_name} raised an exception during boot check. #{e}") + next + else + Rails.logger.info "GitLab -> Plugins -> #{file_name} passed boot check" + klass + end + end + end +end + +# Load external plugins from /plugins directory +# and set into PLUGINS variable +PLUGINS = PluginsSystem.new.valid_plugins From b985fe95b6c30bc83725e9b5e18a79a8ceb900d3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 8 Feb 2018 18:14:10 +0200 Subject: [PATCH 09/53] Add generator for plugins Signed-off-by: Dmitriy Zaporozhets --- generator_templates/plugins/template.rb | 16 ++++++++++++++++ lib/tasks/plugins.rake | 25 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 generator_templates/plugins/template.rb create mode 100644 lib/tasks/plugins.rake diff --git a/generator_templates/plugins/template.rb b/generator_templates/plugins/template.rb new file mode 100644 index 00000000000..16c87f2c2b2 --- /dev/null +++ b/generator_templates/plugins/template.rb @@ -0,0 +1,16 @@ +# Requirements +# * File name must end with _s.rb. For example, jenkins_plugin.rb. +# * All code should be inside class. No code should be executed on file load. +# * Class name must be same as file name. +# If file name is jenkins_plugin.rb then class name must be JenkinsPlugin. +# +# Reccomendations +# * Code should not depend on or use GitLab classes and other code. +# * Consider contributing your plugin to GitLab source code so we can test it +# and make sure it will work in further version. +# +class $NAMEPlugin + def execute(data) + # TODO: Implement me + end +end diff --git a/lib/tasks/plugins.rake b/lib/tasks/plugins.rake new file mode 100644 index 00000000000..fac6070ea9b --- /dev/null +++ b/lib/tasks/plugins.rake @@ -0,0 +1,25 @@ +namespace :plugins do + desc 'Generate skeleton for new plugin' + task generate: :environment do + ARGV.each { |a| task a.to_sym { } } + name = ARGV[1] + + unless name.present? + puts 'Error. You need to specify a name for the plugin' + exit 1 + end + + class_name = name.classify + param = name.underscore + file_path = Rails.root.join('plugins', param + '_plugin.rb') + template = File.read(Rails.root.join('generator_templates', 'plugins', 'template.rb')) + template.gsub!('$NAME', class_name) + + if File.write(file_path, template) + puts "Done. Your plugin saved under #{file_path}." + puts 'Feel free to edit it.' + else + puts "Failed to save #{file_path}." + end + end +end From 5bb435d0e75ad84e2fc379208cc36a25a4574453 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 19 Feb 2018 19:20:53 +0200 Subject: [PATCH 10/53] Remove plugin initializer and add plugins:validate rake task Signed-off-by: Dmitriy Zaporozhets --- app/services/system_hooks_service.rb | 2 +- config/initializers/9_plugins.rb | 29 ---------------------------- lib/gitlab/plugin.rb | 25 ++++++++++++++++++++++++ lib/tasks/plugins.rake | 15 ++++++++++++++ 4 files changed, 41 insertions(+), 30 deletions(-) delete mode 100644 config/initializers/9_plugins.rb create mode 100644 lib/gitlab/plugin.rb diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 71de74e5a82..ba46c0074e4 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -13,7 +13,7 @@ class SystemHooksService end # Execute external plugins - PLUGINS.each do |plugin| + Gitlab::Plugin.all.each do |plugin| begin plugin.new.execute(data) rescue => e diff --git a/config/initializers/9_plugins.rb b/config/initializers/9_plugins.rb deleted file mode 100644 index 9f252ccd296..00000000000 --- a/config/initializers/9_plugins.rb +++ /dev/null @@ -1,29 +0,0 @@ -class PluginsSystem - attr_accessor :plugins, :files - - def initialize - @files = Dir.glob(Rails.root.join('plugins', '*_plugin.rb')) - end - - def valid_plugins - files.map do |file| - file_name = File.basename(file, '.rb') - - # Just give sample data to method and expect it to not crash. - begin - klass = Object.const_get(file_name.classify) - klass.new.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA) - rescue => e - Rails.logger.warn("GitLab -> Plugins -> #{file_name} raised an exception during boot check. #{e}") - next - else - Rails.logger.info "GitLab -> Plugins -> #{file_name} passed boot check" - klass - end - end - end -end - -# Load external plugins from /plugins directory -# and set into PLUGINS variable -PLUGINS = PluginsSystem.new.valid_plugins diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb new file mode 100644 index 00000000000..cbc57a5cce3 --- /dev/null +++ b/lib/gitlab/plugin.rb @@ -0,0 +1,25 @@ +module Gitlab + module Plugin + def self.all + files.map do |file| + file_name = File.basename(file, '.rb') + + # Just give sample data to method and expect it to not crash. + begin + klass = Object.const_get(file_name.classify) + klass.new.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA) + rescue => e + Rails.logger.warn("GitLab -> Plugins -> #{file_name} raised an exception during boot check. #{e}") + next + else + Rails.logger.info "GitLab -> Plugins -> #{file_name} passed validation check" + klass + end + end + end + + def self.files + Dir.glob(Rails.root.join('plugins', '*_plugin.rb')) + end + end +end diff --git a/lib/tasks/plugins.rake b/lib/tasks/plugins.rake index fac6070ea9b..8728e232c9c 100644 --- a/lib/tasks/plugins.rake +++ b/lib/tasks/plugins.rake @@ -22,4 +22,19 @@ namespace :plugins do puts "Failed to save #{file_path}." end end + + desc 'Validate existing plugins' + task validate: :environment do + puts 'Validating plugins from /plugins directory' + + Gitlab::Plugin.all.each do |plugin| + begin + plugin.new.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA) + rescue => e + puts "- #{plugin} raised an exception during boot check. #{e}" + else + puts "- #{plugin} passed validation check" + end + end + end end From eff5746b5e7cf4075edd6d1c76fdcd24c1603bb4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 19 Feb 2018 19:55:54 +0200 Subject: [PATCH 11/53] Redesign plugins system Signed-off-by: Dmitriy Zaporozhets --- app/services/system_hooks_service.rb | 9 +------- app/workers/plugin_worker.rb | 9 ++++++++ lib/gitlab/plugin.rb | 34 +++++++++++++--------------- lib/tasks/plugins.rake | 12 +++++----- 4 files changed, 32 insertions(+), 32 deletions(-) create mode 100644 app/workers/plugin_worker.rb diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index ba46c0074e4..af8c02a10b7 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -12,14 +12,7 @@ class SystemHooksService hook.async_execute(data, 'system_hooks') end - # Execute external plugins - Gitlab::Plugin.all.each do |plugin| - begin - plugin.new.execute(data) - rescue => e - Rails.logger.warn("GitLab -> Plugins -> #{plugin.class.name} raised an axception during execution. #{e}") - end - end + Gitlab::Plugin.execute_all_async(data) end private diff --git a/app/workers/plugin_worker.rb b/app/workers/plugin_worker.rb new file mode 100644 index 00000000000..34a3c8d62ac --- /dev/null +++ b/app/workers/plugin_worker.rb @@ -0,0 +1,9 @@ +class PluginWorker + include ApplicationWorker + + sidekiq_options retry: false + + def perform(file_name, data) + Gitlab::Plugin.execute(file_name, data) + end +end diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb index cbc57a5cce3..9604cac4b20 100644 --- a/lib/gitlab/plugin.rb +++ b/lib/gitlab/plugin.rb @@ -1,25 +1,23 @@ module Gitlab module Plugin - def self.all - files.map do |file| - file_name = File.basename(file, '.rb') - - # Just give sample data to method and expect it to not crash. - begin - klass = Object.const_get(file_name.classify) - klass.new.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA) - rescue => e - Rails.logger.warn("GitLab -> Plugins -> #{file_name} raised an exception during boot check. #{e}") - next - else - Rails.logger.info "GitLab -> Plugins -> #{file_name} passed validation check" - klass - end - end - end - def self.files Dir.glob(Rails.root.join('plugins', '*_plugin.rb')) end + + def self.execute_all_async(data) + files.each do |file| + PluginWorker.perform_async(file, data) + end + end + + def self.execute(file, data) + # TODO: Implement + # + # Reuse some code from gitlab-shell https://gitlab.com/gitlab-org/gitlab-shell/blob/master/lib/gitlab_custom_hook.rb#L40 + # Pass data as STDIN (or JSON encode?) + # + # 1. Return true if 0 exit code + # 2. Return false if non-zero exit code + end end end diff --git a/lib/tasks/plugins.rake b/lib/tasks/plugins.rake index 8728e232c9c..9c9f1fece85 100644 --- a/lib/tasks/plugins.rake +++ b/lib/tasks/plugins.rake @@ -27,13 +27,13 @@ namespace :plugins do task validate: :environment do puts 'Validating plugins from /plugins directory' - Gitlab::Plugin.all.each do |plugin| - begin - plugin.new.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA) - rescue => e - puts "- #{plugin} raised an exception during boot check. #{e}" + Gitlab::Plugin.files.each do |file| + result = Gitlab::Plugin.execute(file, Gitlab::DataBuilder::Push::SAMPLE_DATA) + + if result + puts "* #{file} succeed (zero exit code)" else - puts "- #{plugin} passed validation check" + puts "* #{file} failure (non-zero exit code)" end end end From 645dceb0a233fc523ac16611fa3fec317d29a7e1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 23 Feb 2018 15:58:57 +0200 Subject: [PATCH 12/53] Run plugins as separate process and pass data via STDIN Signed-off-by: Dmitriy Zaporozhets --- .gitignore | 2 +- config/application.rb | 1 - generator_templates/plugins/template.rb | 16 -------------- lib/gitlab/plugin.rb | 28 ++++++++++++++++++------- lib/tasks/plugins.rake | 24 --------------------- plugins/available/save_to_file.clj | 3 +++ plugins/available/save_to_file.rb | 3 +++ plugins/{ => enabled}/.gitkeep | 0 8 files changed, 27 insertions(+), 50 deletions(-) delete mode 100644 generator_templates/plugins/template.rb create mode 100755 plugins/available/save_to_file.clj create mode 100755 plugins/available/save_to_file.rb rename plugins/{ => enabled}/.gitkeep (100%) diff --git a/.gitignore b/.gitignore index fa39ae01ff0..35ca92b1a93 100644 --- a/.gitignore +++ b/.gitignore @@ -66,4 +66,4 @@ eslint-report.html /locale/**/LC_MESSAGES /locale/**/*.time_stamp /.rspec -/plugins/* +/plugins/enabled/* diff --git a/config/application.rb b/config/application.rb index f2fc6270748..918bd4d57cf 100644 --- a/config/application.rb +++ b/config/application.rb @@ -28,7 +28,6 @@ module Gitlab config.eager_load_paths.push(*%W[#{config.root}/lib #{config.root}/app/models/hooks #{config.root}/app/models/members - #{config.root}/plugins #{config.root}/app/models/project_services #{config.root}/app/workers/concerns #{config.root}/app/services/concerns diff --git a/generator_templates/plugins/template.rb b/generator_templates/plugins/template.rb deleted file mode 100644 index 16c87f2c2b2..00000000000 --- a/generator_templates/plugins/template.rb +++ /dev/null @@ -1,16 +0,0 @@ -# Requirements -# * File name must end with _s.rb. For example, jenkins_plugin.rb. -# * All code should be inside class. No code should be executed on file load. -# * Class name must be same as file name. -# If file name is jenkins_plugin.rb then class name must be JenkinsPlugin. -# -# Reccomendations -# * Code should not depend on or use GitLab classes and other code. -# * Consider contributing your plugin to GitLab source code so we can test it -# and make sure it will work in further version. -# -class $NAMEPlugin - def execute(data) - # TODO: Implement me - end -end diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb index 9604cac4b20..1035d258907 100644 --- a/lib/gitlab/plugin.rb +++ b/lib/gitlab/plugin.rb @@ -1,23 +1,35 @@ module Gitlab module Plugin def self.files - Dir.glob(Rails.root.join('plugins', '*_plugin.rb')) + Dir.glob(Rails.root.join('plugins/enabled/*')) end def self.execute_all_async(data) files.each do |file| + puts file PluginWorker.perform_async(file, data) end end def self.execute(file, data) - # TODO: Implement - # - # Reuse some code from gitlab-shell https://gitlab.com/gitlab-org/gitlab-shell/blob/master/lib/gitlab_custom_hook.rb#L40 - # Pass data as STDIN (or JSON encode?) - # - # 1. Return true if 0 exit code - # 2. Return false if non-zero exit code + # Prepare the hook subprocess. Attach a pipe to its stdin, and merge + # both its stdout and stderr into our own stdout. + stdin_reader, stdin_writer = IO.pipe + hook_pid = spawn({}, file, in: stdin_reader, err: :out) + stdin_reader.close + + # Submit changes to the hook via its stdin. + begin + IO.copy_stream(StringIO.new(data.to_json), stdin_writer) + rescue Errno::EPIPE + # It is not an error if the hook does not consume all of its input. + end + + # Close the pipe to let the hook know there is no further input. + stdin_writer.close + + Process.wait(hook_pid) + $?.success? end end end diff --git a/lib/tasks/plugins.rake b/lib/tasks/plugins.rake index 9c9f1fece85..f4d7edb2eb2 100644 --- a/lib/tasks/plugins.rake +++ b/lib/tasks/plugins.rake @@ -1,28 +1,4 @@ namespace :plugins do - desc 'Generate skeleton for new plugin' - task generate: :environment do - ARGV.each { |a| task a.to_sym { } } - name = ARGV[1] - - unless name.present? - puts 'Error. You need to specify a name for the plugin' - exit 1 - end - - class_name = name.classify - param = name.underscore - file_path = Rails.root.join('plugins', param + '_plugin.rb') - template = File.read(Rails.root.join('generator_templates', 'plugins', 'template.rb')) - template.gsub!('$NAME', class_name) - - if File.write(file_path, template) - puts "Done. Your plugin saved under #{file_path}." - puts 'Feel free to edit it.' - else - puts "Failed to save #{file_path}." - end - end - desc 'Validate existing plugins' task validate: :environment do puts 'Validating plugins from /plugins directory' diff --git a/plugins/available/save_to_file.clj b/plugins/available/save_to_file.clj new file mode 100755 index 00000000000..a59d83749d3 --- /dev/null +++ b/plugins/available/save_to_file.clj @@ -0,0 +1,3 @@ +#!/usr/bin/env clojure +(let [in (slurp *in*)] + (spit "/tmp/clj-data.txt" in)) diff --git a/plugins/available/save_to_file.rb b/plugins/available/save_to_file.rb new file mode 100755 index 00000000000..61b0df9bfd6 --- /dev/null +++ b/plugins/available/save_to_file.rb @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +x = STDIN.read +File.write('/tmp/rb-data.txt', x) diff --git a/plugins/.gitkeep b/plugins/enabled/.gitkeep similarity index 100% rename from plugins/.gitkeep rename to plugins/enabled/.gitkeep From ba95015a09bc465533666b38609b4fb1e0177139 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Feb 2018 13:32:14 +0200 Subject: [PATCH 13/53] Reorganize plugins dir structure Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/plugin.rb | 5 +++-- plugins/enabled/.gitkeep | 0 plugins/{available => examples}/save_to_file.clj | 0 plugins/{available => examples}/save_to_file.rb | 0 4 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 plugins/enabled/.gitkeep rename plugins/{available => examples}/save_to_file.clj (100%) rename plugins/{available => examples}/save_to_file.rb (100%) diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb index 1035d258907..be5d6d6b1c1 100644 --- a/lib/gitlab/plugin.rb +++ b/lib/gitlab/plugin.rb @@ -1,12 +1,13 @@ module Gitlab module Plugin def self.files - Dir.glob(Rails.root.join('plugins/enabled/*')) + Dir.glob(Rails.root.join('plugins/*')).select do |entry| + File.file?(entry) + end end def self.execute_all_async(data) files.each do |file| - puts file PluginWorker.perform_async(file, data) end end diff --git a/plugins/enabled/.gitkeep b/plugins/enabled/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/plugins/available/save_to_file.clj b/plugins/examples/save_to_file.clj similarity index 100% rename from plugins/available/save_to_file.clj rename to plugins/examples/save_to_file.clj diff --git a/plugins/available/save_to_file.rb b/plugins/examples/save_to_file.rb similarity index 100% rename from plugins/available/save_to_file.rb rename to plugins/examples/save_to_file.rb From 84097def3c97f0e0d8993ff07375c89e4b91aea8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Feb 2018 13:32:35 +0200 Subject: [PATCH 14/53] Add /plugins to gitignore Signed-off-by: Dmitriy Zaporozhets --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 35ca92b1a93..fa39ae01ff0 100644 --- a/.gitignore +++ b/.gitignore @@ -66,4 +66,4 @@ eslint-report.html /locale/**/LC_MESSAGES /locale/**/*.time_stamp /.rspec -/plugins/enabled/* +/plugins/* From 3337130e015fba1d04a53e8e3a7098f966792f5f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Feb 2018 13:42:25 +0200 Subject: [PATCH 15/53] Use Gitlab::Popen instead of spawn [ci skip] Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/plugin.rb | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb index be5d6d6b1c1..5339c4dbbc8 100644 --- a/lib/gitlab/plugin.rb +++ b/lib/gitlab/plugin.rb @@ -13,24 +13,11 @@ module Gitlab end def self.execute(file, data) - # Prepare the hook subprocess. Attach a pipe to its stdin, and merge - # both its stdout and stderr into our own stdout. - stdin_reader, stdin_writer = IO.pipe - hook_pid = spawn({}, file, in: stdin_reader, err: :out) - stdin_reader.close - - # Submit changes to the hook via its stdin. - begin - IO.copy_stream(StringIO.new(data.to_json), stdin_writer) - rescue Errno::EPIPE - # It is not an error if the hook does not consume all of its input. + _output, exit_status = Gitlab::Popen.popen([file]) do |stdin| + stdin.write(data.to_json) end - # Close the pipe to let the hook know there is no further input. - stdin_writer.close - - Process.wait(hook_pid) - $?.success? + exit_status.zero? end end end From 4b998239a3fec56f93e24ca063d1196416aec938 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Feb 2018 14:15:51 +0200 Subject: [PATCH 16/53] Add plugin queue to sidekiq config [ci skip] Signed-off-by: Dmitriy Zaporozhets --- config/sidekiq_queues.yml | 1 + lib/gitlab/plugin.rb | 6 +++--- lib/tasks/plugins.rake | 5 +++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index f037e3d1221..4845dc28a4a 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -68,3 +68,4 @@ - [project_migrate_hashed_storage, 1] - [storage_migrator, 1] - [pages_domain_verification, 1] + - [plugin, 1] diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb index 5339c4dbbc8..3e82f5cb0d8 100644 --- a/lib/gitlab/plugin.rb +++ b/lib/gitlab/plugin.rb @@ -7,9 +7,9 @@ module Gitlab end def self.execute_all_async(data) - files.each do |file| - PluginWorker.perform_async(file, data) - end + args = files.map { |file| [file, data] } + + PluginWorker.bulk_perform_async(args) end def self.execute(file, data) diff --git a/lib/tasks/plugins.rake b/lib/tasks/plugins.rake index f4d7edb2eb2..11c41f13614 100644 --- a/lib/tasks/plugins.rake +++ b/lib/tasks/plugins.rake @@ -13,4 +13,9 @@ namespace :plugins do end end end + + desc 'Validate existing plugins' + task validate_async: :environment do + Gitlab::Plugin.execute_all_async(Gitlab::DataBuilder::Push::SAMPLE_DATA) + end end From 3073f1aa6e8658825af739440f7288deeef9169d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Feb 2018 16:57:10 +0200 Subject: [PATCH 17/53] Handle excpetion in PluginWorker Signed-off-by: Dmitriy Zaporozhets --- app/workers/plugin_worker.rb | 2 ++ spec/workers/plugin_worker_spec.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 spec/workers/plugin_worker_spec.rb diff --git a/app/workers/plugin_worker.rb b/app/workers/plugin_worker.rb index 34a3c8d62ac..cebdf8d0017 100644 --- a/app/workers/plugin_worker.rb +++ b/app/workers/plugin_worker.rb @@ -5,5 +5,7 @@ class PluginWorker def perform(file_name, data) Gitlab::Plugin.execute(file_name, data) + rescue => e + Rails.logger.error("#{self.class}: #{e.message}") end end diff --git a/spec/workers/plugin_worker_spec.rb b/spec/workers/plugin_worker_spec.rb new file mode 100644 index 00000000000..29c55482321 --- /dev/null +++ b/spec/workers/plugin_worker_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe PluginWorker do + include RepoHelpers + + subject { described_class.new } + + let(:filename) { 'my_plugin.rb' } + + describe '#perform' 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 + + it 'handles exception well' do + data = { 'event_name' => 'project_create' } + + allow(Gitlab::Plugin).to receive(:execute).with(filename, data).and_raise('Permission denied') + + expect { subject.perform(filename, data) }.to_not raise_error + end + end +end From 7424e0e5b4fd875588a2dd5a15ee5de1559c13db Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Feb 2018 17:33:19 +0200 Subject: [PATCH 18/53] Add specs for Gitlab::Plugin [ci skip] Signed-off-by: Dmitriy Zaporozhets --- spec/lib/gitlab/plugin_spec.rb | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 spec/lib/gitlab/plugin_spec.rb diff --git a/spec/lib/gitlab/plugin_spec.rb b/spec/lib/gitlab/plugin_spec.rb new file mode 100644 index 00000000000..1dc508f86ce --- /dev/null +++ b/spec/lib/gitlab/plugin_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe Gitlab::Plugin do + describe '.execute' do + let(:data) { Gitlab::DataBuilder::Push::SAMPLE_DATA } + let(:plugin) { Rails.root.join('plugins', 'test.rb') } + let(:tmp_file) { Tempfile.new('plugin-dump').path } + + before do + File.write(plugin, plugin_source) + File.chmod(0o777, plugin) + end + + after do + FileUtils.rm(plugin) + FileUtils.rm(tmp_file) + end + + subject { described_class.execute(plugin.to_s, data) } + + it { is_expected.to be true } + + it 'ensures plugin received data via stdin' do + subject + + expect(File.read(tmp_file)).to eq(data.to_json) + end + end + + private + + def plugin_source + <<-EOS +#!/usr/bin/env ruby +x = STDIN.read +File.write('#{tmp_file}', x) + EOS + end +end From 98831a4867f60f7b7ca02bebd8771ca23940e9c5 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Feb 2018 17:33:38 +0200 Subject: [PATCH 19/53] Exclude plugins dir from rubocop and codeclimate config Signed-off-by: Dmitriy Zaporozhets --- .codeclimate.yml | 1 + .rubocop.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.codeclimate.yml b/.codeclimate.yml index b02fe54a4ff..d80f57c9947 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -45,3 +45,4 @@ exclude_paths: - log/ - backups/ - coverage-javascript/ +- plugins/ diff --git a/.rubocop.yml b/.rubocop.yml index 24edb641657..293f61fb725 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -17,6 +17,7 @@ AllCops: - 'bin/**/*' - 'generator_templates/**/*' - 'builds/**/*' + - 'plugins/**/*' CacheRootDirectory: tmp # This cop checks whether some constant value isn't a From 79d911204ca92c759b696d0b8db8e8d54af6e2f9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 26 Feb 2018 18:17:17 +0200 Subject: [PATCH 20/53] Add plugin queue to all_queues.yml Signed-off-by: Dmitriy Zaporozhets --- app/workers/all_queues.yml | 1 + spec/workers/plugin_worker_spec.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 28a5e5da037..a9415410f8a 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -84,6 +84,7 @@ - new_note - pages - pages_domain_verification +- plugin - post_receive - process_commit - project_cache diff --git a/spec/workers/plugin_worker_spec.rb b/spec/workers/plugin_worker_spec.rb index 29c55482321..d64de05f211 100644 --- a/spec/workers/plugin_worker_spec.rb +++ b/spec/workers/plugin_worker_spec.rb @@ -21,7 +21,7 @@ describe PluginWorker do allow(Gitlab::Plugin).to receive(:execute).with(filename, data).and_raise('Permission denied') - expect { subject.perform(filename, data) }.to_not raise_error + expect { subject.perform(filename, data) }.not_to raise_error end end end From ac8a0fa061283f2eb25f1e673ab244f1009a71a4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 27 Feb 2018 14:33:32 +0200 Subject: [PATCH 21/53] Few code improvements for spec/lib/gitlab/plugin_spec.rb Signed-off-by: Dmitriy Zaporozhets --- spec/lib/gitlab/plugin_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/lib/gitlab/plugin_spec.rb b/spec/lib/gitlab/plugin_spec.rb index 1dc508f86ce..065b18858be 100644 --- a/spec/lib/gitlab/plugin_spec.rb +++ b/spec/lib/gitlab/plugin_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::Plugin do describe '.execute' do let(:data) { Gitlab::DataBuilder::Push::SAMPLE_DATA } let(:plugin) { Rails.root.join('plugins', 'test.rb') } - let(:tmp_file) { Tempfile.new('plugin-dump').path } + let(:tmp_file) { Tempfile.new('plugin-dump') } before do File.write(plugin, plugin_source) @@ -13,7 +13,7 @@ describe Gitlab::Plugin do after do FileUtils.rm(plugin) - FileUtils.rm(tmp_file) + tmp_file.close! end subject { described_class.execute(plugin.to_s, data) } @@ -23,17 +23,17 @@ describe Gitlab::Plugin do it 'ensures plugin received data via stdin' do subject - expect(File.read(tmp_file)).to eq(data.to_json) + expect(File.read(tmp_file.path)).to eq(data.to_json) end end private def plugin_source - <<-EOS -#!/usr/bin/env ruby -x = STDIN.read -File.write('#{tmp_file}', x) + <<~EOS + #!/usr/bin/env ruby + x = STDIN.read + File.write('#{tmp_file.path}', x) EOS end end From 865c9abf89e66d5bf71e437c3870174000c4205b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 27 Feb 2018 14:34:52 +0200 Subject: [PATCH 22/53] Add plugin feature to changelog [ci skip] Signed-off-by: Dmitriy Zaporozhets --- changelogs/unreleased/dz-system-hooks-plugins.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/dz-system-hooks-plugins.yml diff --git a/changelogs/unreleased/dz-system-hooks-plugins.yml b/changelogs/unreleased/dz-system-hooks-plugins.yml new file mode 100644 index 00000000000..e6eb1dfb03b --- /dev/null +++ b/changelogs/unreleased/dz-system-hooks-plugins.yml @@ -0,0 +1,5 @@ +--- +title: Add ability to use external plugins as an alternative to system hooks +merge_request: 17003 +author: +type: added From c8d8dffecc73c0553b960310bb6de8ceb9bfffe7 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 27 Feb 2018 12:51:15 +0000 Subject: [PATCH 23/53] Use v-if in the expand button for performance --- app/assets/javascripts/vue_shared/components/expand_button.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/vue_shared/components/expand_button.vue b/app/assets/javascripts/vue_shared/components/expand_button.vue index 3595a9389e9..c943c8d98a4 100644 --- a/app/assets/javascripts/vue_shared/components/expand_button.vue +++ b/app/assets/javascripts/vue_shared/components/expand_button.vue @@ -39,7 +39,7 @@ @click="onClick"> ... - + From 8c9e4d3a9610fba98dc707178bf4a79473429135 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 27 Feb 2018 16:01:17 +0200 Subject: [PATCH 24/53] Add documentation for plugins feature Signed-off-by: Dmitriy Zaporozhets --- doc/administration/plugins.md | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 doc/administration/plugins.md diff --git a/doc/administration/plugins.md b/doc/administration/plugins.md new file mode 100644 index 00000000000..0fea1d2e3ac --- /dev/null +++ b/doc/administration/plugins.md @@ -0,0 +1,50 @@ +# Plugins + +**Note:** Plugins must be configured on the filesystem of the GitLab +server. Only GitLab server administrators will be able to complete these tasks. +Please explore [system hooks] or [webhooks] as an option if you do not +have filesystem access. + +Introduced in GitLab 10.6. + +Plugin will run on each event so it's up to you to filter events or projects within a plugin code. You can have as many plugins as you want. Each plugin will be triggered by GitLab asyncronously in case of event. For list of events please see [system hooks] documentation. + +## 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. + +Follow the steps below to set up a custom hook: + +1. On the GitLab server, navigate to the project's plugin directory. + For an installation from source the path is usually + `/home/git/gitlab/plugins/`. For Omnibus installs the path is + 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 sepcial characters. +1. Make the hook file executable and make sure it's owned by git. +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 + type. For example, if the script is in Ruby the shebang will probably be + `#!/usr/bin/env ruby`. + +That's it! Assuming the plugin code is properly implemented the hook will fire +as appropriate. Plugins file list is updated on each event. There is no need to restart GitLab to apply a new plugin. + +## Validation + +Writing own plugin can be tricky and its easier if you can check it without altering the system. We provided a rake task you can use with staging enviromnent to test your plugin before using it in production. The rake task will use a sample data and execute each of plugins. By output you should be able to determine if system sees your plugin and if it was executed without errors. + +```bash +# Omnibus installations +sudo gitlab-rake plugins:validate + +# Installations from source +bundle exec rake plugins:validate RAILS_ENV=production +``` + + +[hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks +[system hooks]: ../system_hooks/system_hooks.md +[webhooks]: ../user/project/integrations/webhooks.md +[5073]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5073 +[93]: https://gitlab.com/gitlab-org/gitlab-shell/merge_requests/93 + From 2150b2cde2700a48095db4364d02f91c9b9a1456 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 27 Feb 2018 16:02:42 +0200 Subject: [PATCH 25/53] [ci skip] Add example of rake plugins:validate output to the doc Signed-off-by: Dmitriy Zaporozhets --- doc/administration/plugins.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/administration/plugins.md b/doc/administration/plugins.md index 0fea1d2e3ac..f71cecafb5f 100644 --- a/doc/administration/plugins.md +++ b/doc/administration/plugins.md @@ -41,6 +41,14 @@ sudo gitlab-rake plugins:validate bundle exec rake plugins:validate RAILS_ENV=production ``` +Example of output can be next: + +``` +-> bundle exec rake plugins:validate RAILS_ENV=production +Validating plugins from /plugins directory +* /home/git/gitlab/plugins/save_to_file.clj succeed (zero exit code) +* /home/git/gitlab/plugins/save_to_file.rb failure (non-zero exit code) +``` [hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks [system hooks]: ../system_hooks/system_hooks.md From f0a64399c1171d2757c8ce70a36517d556cd9233 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 27 Feb 2018 18:08:38 +0200 Subject: [PATCH 26/53] Refactor plugin execution method Signed-off-by: Dmitriy Zaporozhets --- app/workers/plugin_worker.rb | 2 -- lib/gitlab/plugin.rb | 11 +++++- spec/lib/gitlab/plugin_spec.rb | 57 +++++++++++++++++++++--------- spec/workers/plugin_worker_spec.rb | 8 ----- 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/app/workers/plugin_worker.rb b/app/workers/plugin_worker.rb index cebdf8d0017..34a3c8d62ac 100644 --- a/app/workers/plugin_worker.rb +++ b/app/workers/plugin_worker.rb @@ -5,7 +5,5 @@ class PluginWorker def perform(file_name, data) Gitlab::Plugin.execute(file_name, data) - rescue => e - Rails.logger.error("#{self.class}: #{e.message}") end end diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb index 3e82f5cb0d8..96332362a4c 100644 --- a/lib/gitlab/plugin.rb +++ b/lib/gitlab/plugin.rb @@ -13,11 +13,20 @@ module Gitlab end def self.execute(file, data) - _output, exit_status = Gitlab::Popen.popen([file]) do |stdin| + result = Gitlab::Popen.popen_with_detail([file]) do |stdin| stdin.write(data.to_json) end + exit_status = result.status&.exitstatus + + unless exit_status.zero? + Rails.logger.error("Plugin Error => #{file}: #{result.stderr}") + end + exit_status.zero? + rescue => e + Rails.logger.error("Plugin Error => #{file}: #{e.message}") + false end end end diff --git a/spec/lib/gitlab/plugin_spec.rb b/spec/lib/gitlab/plugin_spec.rb index 065b18858be..a01e1383e3b 100644 --- a/spec/lib/gitlab/plugin_spec.rb +++ b/spec/lib/gitlab/plugin_spec.rb @@ -6,34 +6,59 @@ describe Gitlab::Plugin do let(:plugin) { Rails.root.join('plugins', 'test.rb') } let(:tmp_file) { Tempfile.new('plugin-dump') } + let(:plugin_source) do + <<~EOS + #!/usr/bin/env ruby + x = STDIN.read + File.write('#{tmp_file.path}', x) + EOS + end + before do File.write(plugin, plugin_source) - File.chmod(0o777, plugin) end after do FileUtils.rm(plugin) - tmp_file.close! end subject { described_class.execute(plugin.to_s, data) } - it { is_expected.to be true } + context 'successful execution' do + before do + File.chmod(0o777, plugin) + end - it 'ensures plugin received data via stdin' do - subject + after do + tmp_file.close! + end - expect(File.read(tmp_file.path)).to eq(data.to_json) + it { is_expected.to be true } + + it 'ensures plugin received data via stdin' do + subject + + expect(File.read(tmp_file.path)).to eq(data.to_json) + end + end + + context 'non-executable' do + it { is_expected.to be false } + end + + context 'non-zero exit' do + let(:plugin_source) do + <<~EOS + #!/usr/bin/env ruby + exit 1 + EOS + end + + before do + File.chmod(0o777, plugin) + end + + it { is_expected.to be false } end end - - private - - def plugin_source - <<~EOS - #!/usr/bin/env ruby - x = STDIN.read - File.write('#{tmp_file.path}', x) - EOS - end end diff --git a/spec/workers/plugin_worker_spec.rb b/spec/workers/plugin_worker_spec.rb index d64de05f211..60631def8f3 100644 --- a/spec/workers/plugin_worker_spec.rb +++ b/spec/workers/plugin_worker_spec.rb @@ -15,13 +15,5 @@ describe PluginWorker do expect(subject.perform(filename, data)).to be_truthy end - - it 'handles exception well' do - data = { 'event_name' => 'project_create' } - - allow(Gitlab::Plugin).to receive(:execute).with(filename, data).and_raise('Permission denied') - - expect { subject.perform(filename, data) }.not_to raise_error - end end end From 4eed9a12216296709306ce29faf607d8aed2a913 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 27 Feb 2018 18:22:50 +0200 Subject: [PATCH 27/53] Fix few typos in plugins doc Signed-off-by: Dmitriy Zaporozhets --- doc/administration/plugins.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/administration/plugins.md b/doc/administration/plugins.md index f71cecafb5f..89ab0b9c8ea 100644 --- a/doc/administration/plugins.md +++ b/doc/administration/plugins.md @@ -7,7 +7,7 @@ have filesystem access. Introduced in GitLab 10.6. -Plugin will run on each event so it's up to you to filter events or projects within a plugin code. You can have as many plugins as you want. Each plugin will be triggered by GitLab asyncronously in case of event. For list of events please see [system hooks] documentation. +A plugin will run on each event so it's up to you to filter events or projects within a plugin code. You can have as many plugins as you want. Each plugin will be triggered by GitLab asynchronously in case of an event. For a list of events please see [system hooks] documentation. ## Setup @@ -19,7 +19,7 @@ Follow the steps below to set up a custom hook: For an installation from source the path is usually `/home/git/gitlab/plugins/`. For Omnibus installs the path is 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 sepcial 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. 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 @@ -27,11 +27,11 @@ Follow the steps below to set up a custom hook: `#!/usr/bin/env ruby`. That's it! Assuming the plugin code is properly implemented the hook will fire -as appropriate. Plugins file list is updated on 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. ## Validation -Writing own plugin can be tricky and its easier if you can check it without altering the system. We provided a rake task you can use with staging enviromnent to test your plugin before using it in production. The rake task will use a sample data and execute each of plugins. By output you should be able to determine if system sees your plugin and if it was executed without errors. +Writing own plugin can be tricky and its easier if you can check it without altering the system. We provided a rake task you can use with staging environment to test your plugin before using it in production. The rake task will use a sample data and execute each of plugins. By output you should be able to determine if system sees your plugin and if it was executed without errors. ```bash # Omnibus installations From e658ca9665e70c07772fc927a88938895a7fb453 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Tue, 27 Feb 2018 16:17:19 -0600 Subject: [PATCH 28/53] Add initPipelineDetails to pipelines build path --- .../cycle_analytics/cycle_analytics_bundle.js | 4 +- .../pages/projects/pipelines/builds/index.js | 6 +- .../pages/projects/pipelines/show/index.js | 70 +---------- .../pipelines/pipeline_details_bundle.js | 114 +++++++++--------- 4 files changed, 69 insertions(+), 125 deletions(-) diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js index f0a9fab15e7..034f2923b3b 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js @@ -14,10 +14,10 @@ import CycleAnalyticsStore from './cycle_analytics_store'; Vue.use(Translate); -document.addEventListener('DOMContentLoaded', () => { +$(() => { const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed'; - const cycleAnalyticsApp = new Vue({ + gl.cycleAnalyticsApp = new Vue({ el: '#cycle-analytics', name: 'CycleAnalytics', components: { diff --git a/app/assets/javascripts/pages/projects/pipelines/builds/index.js b/app/assets/javascripts/pages/projects/pipelines/builds/index.js index fbe9824c34b..7a57e417b41 100644 --- a/app/assets/javascripts/pages/projects/pipelines/builds/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/builds/index.js @@ -1,3 +1,7 @@ +import initPipelineDetails from '~/pipelines/pipeline_details_bundle'; import initPipelines from '../init_pipelines'; -document.addEventListener('DOMContentLoaded', initPipelines); +document.addEventListener('DOMContentLoaded', () => { + initPipelines(); + initPipelineDetails(); +}); diff --git a/app/assets/javascripts/pages/projects/pipelines/show/index.js b/app/assets/javascripts/pages/projects/pipelines/show/index.js index a49a71afd37..7a57e417b41 100644 --- a/app/assets/javascripts/pages/projects/pipelines/show/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/show/index.js @@ -1,73 +1,7 @@ - -import Vue from 'vue'; -import { __ } from '~/locale'; -import Flash from '~/flash'; -import PipelinesMediator from '~/pipelines/pipeline_details_mediator'; -import pipelineGraph from '~/pipelines/components/graph/graph_component.vue'; -import pipelineHeader from '~/pipelines/components/header_component.vue'; -import eventHub from '~/pipelines/event_hub'; +import initPipelineDetails from '~/pipelines/pipeline_details_bundle'; import initPipelines from '../init_pipelines'; document.addEventListener('DOMContentLoaded', () => { - const dataset = document.querySelector('.js-pipeline-details-vue').dataset; - - const mediator = new PipelinesMediator({ endpoint: dataset.endpoint }); - - mediator.fetchPipeline(); initPipelines(); - - // eslint-disable-next-line - new Vue({ - el: '#js-pipeline-graph-vue', - components: { - pipelineGraph, - }, - data() { - return { - mediator, - }; - }, - render(createElement) { - return createElement('pipeline-graph', { - props: { - isLoading: this.mediator.state.isLoading, - pipeline: this.mediator.store.state.pipeline, - }, - }); - }, - }); - - // eslint-disable-next-line - new Vue({ - el: '#js-pipeline-header-vue', - components: { - pipelineHeader, - }, - data() { - return { - mediator, - }; - }, - created() { - eventHub.$on('headerPostAction', this.postAction); - }, - beforeDestroy() { - eventHub.$off('headerPostAction', this.postAction); - }, - methods: { - postAction(action) { - this.mediator.service.postAction(action.path) - .then(() => this.mediator.refreshPipeline()) - .catch(() => new Flash(__('An error occurred while making the request.'))); - }, - }, - render(createElement) { - return createElement('pipeline-header', { - props: { - isLoading: this.mediator.state.isLoading, - pipeline: this.mediator.store.state.pipeline, - }, - }); - }, - }); + initPipelineDetails(); }); diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index 705a60b3ba2..1119a65e5be 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -9,65 +9,71 @@ import eventHub from './event_hub'; Vue.use(Translate); -document.addEventListener('DOMContentLoaded', () => { +export default () => { const dataset = document.querySelector('.js-pipeline-details-vue').dataset; const mediator = new PipelinesMediator({ endpoint: dataset.endpoint }); mediator.fetchPipeline(); - // eslint-disable-next-line - new Vue({ - el: '#js-pipeline-graph-vue', - components: { - pipelineGraph, - }, - data() { - return { - mediator, - }; - }, - render(createElement) { - return createElement('pipeline-graph', { - props: { - isLoading: this.mediator.state.isLoading, - pipeline: this.mediator.store.state.pipeline, - }, - }); - }, - }); - - // eslint-disable-next-line - new Vue({ - el: '#js-pipeline-header-vue', - components: { - pipelineHeader, - }, - data() { - return { - mediator, - }; - }, - created() { - eventHub.$on('headerPostAction', this.postAction); - }, - beforeDestroy() { - eventHub.$off('headerPostAction', this.postAction); - }, - methods: { - postAction(action) { - this.mediator.service.postAction(action.path) - .then(() => this.mediator.refreshPipeline()) - .catch(() => Flash(__('An error occurred while making the request.'))); + const pipelineGraphEl = document.querySelector('#js-pipeline-graph-vue'); + if (pipelineGraphEl) { + // eslint-disable-next-line + new Vue({ + el: pipelineGraphEl, + components: { + pipelineGraph, }, - }, - render(createElement) { - return createElement('pipeline-header', { - props: { - isLoading: this.mediator.state.isLoading, - pipeline: this.mediator.store.state.pipeline, + data() { + return { + mediator, + }; + }, + render(createElement) { + return createElement('pipeline-graph', { + props: { + isLoading: this.mediator.state.isLoading, + pipeline: this.mediator.store.state.pipeline, + }, + }); + }, + }); + } + + const pipelineHeaderEl = document.querySelector('#js-pipeline-header-vue'); + if (pipelineHeaderEl) { + // eslint-disable-next-line + new Vue({ + el: pipelineHeaderEl, + components: { + pipelineHeader, + }, + data() { + return { + mediator, + }; + }, + created() { + eventHub.$on('headerPostAction', this.postAction); + }, + beforeDestroy() { + eventHub.$off('headerPostAction', this.postAction); + }, + methods: { + postAction(action) { + this.mediator.service.postAction(action.path) + .then(() => this.mediator.refreshPipeline()) + .catch(() => Flash(__('An error occurred while making the request.'))); }, - }); - }, - }); -}); + }, + render(createElement) { + return createElement('pipeline-header', { + props: { + isLoading: this.mediator.state.isLoading, + pipeline: this.mediator.store.state.pipeline, + }, + }); + }, + }); + } +}; From c287c01821c4db4830dbdd5263907f08c2ed4cc4 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Tue, 27 Feb 2018 16:28:17 -0600 Subject: [PATCH 29/53] Remove unnecessary element checks --- .../pipelines/pipeline_details_bundle.js | 110 +++++++++--------- 1 file changed, 52 insertions(+), 58 deletions(-) diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index 1119a65e5be..6b26708148c 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -16,64 +16,58 @@ export default () => { mediator.fetchPipeline(); - const pipelineGraphEl = document.querySelector('#js-pipeline-graph-vue'); - if (pipelineGraphEl) { - // eslint-disable-next-line - new Vue({ - el: pipelineGraphEl, - components: { - pipelineGraph, - }, - data() { - return { - mediator, - }; - }, - render(createElement) { - return createElement('pipeline-graph', { - props: { - isLoading: this.mediator.state.isLoading, - pipeline: this.mediator.store.state.pipeline, - }, - }); - }, - }); - } - - const pipelineHeaderEl = document.querySelector('#js-pipeline-header-vue'); - if (pipelineHeaderEl) { - // eslint-disable-next-line - new Vue({ - el: pipelineHeaderEl, - components: { - pipelineHeader, - }, - data() { - return { - mediator, - }; - }, - created() { - eventHub.$on('headerPostAction', this.postAction); - }, - beforeDestroy() { - eventHub.$off('headerPostAction', this.postAction); - }, - methods: { - postAction(action) { - this.mediator.service.postAction(action.path) - .then(() => this.mediator.refreshPipeline()) - .catch(() => Flash(__('An error occurred while making the request.'))); + // eslint-disable-next-line + new Vue({ + el: '#js-pipeline-graph-vue', + components: { + pipelineGraph, + }, + data() { + return { + mediator, + }; + }, + render(createElement) { + return createElement('pipeline-graph', { + props: { + isLoading: this.mediator.state.isLoading, + pipeline: this.mediator.store.state.pipeline, }, + }); + }, + }); + + // eslint-disable-next-line + new Vue({ + el: '#js-pipeline-header-vue', + components: { + pipelineHeader, + }, + data() { + return { + mediator, + }; + }, + created() { + eventHub.$on('headerPostAction', this.postAction); + }, + beforeDestroy() { + eventHub.$off('headerPostAction', this.postAction); + }, + methods: { + postAction(action) { + this.mediator.service.postAction(action.path) + .then(() => this.mediator.refreshPipeline()) + .catch(() => Flash(__('An error occurred while making the request.'))); }, - render(createElement) { - return createElement('pipeline-header', { - props: { - isLoading: this.mediator.state.isLoading, - pipeline: this.mediator.store.state.pipeline, - }, - }); - }, - }); - } + }, + render(createElement) { + return createElement('pipeline-header', { + props: { + isLoading: this.mediator.state.isLoading, + pipeline: this.mediator.store.state.pipeline, + }, + }); + }, + }); }; From 182ed522152f2679fb035a331ec4ff92b0075d1a Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Feb 2018 00:56:16 -0600 Subject: [PATCH 30/53] remove unused help webpack bundle --- config/webpack.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index b01cfd6595e..1f7387dece5 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -47,7 +47,6 @@ function generateEntries() { cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', environments: './environments/environments_bundle.js', filtered_search: './filtered_search/filtered_search_bundle.js', - help: './help/help.js', monitoring: './monitoring/monitoring_bundle.js', mr_notes: './mr_notes/index.js', notebook_viewer: './blob/notebook_viewer.js', From 77926bc29db638f50cf322f91913f5a68228b1ea Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Feb 2018 00:57:52 -0600 Subject: [PATCH 31/53] remove unused project_import_gl webpack bundle --- config/webpack.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 1f7387dece5..7e2a2d7e3b4 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -52,7 +52,6 @@ function generateEntries() { notebook_viewer: './blob/notebook_viewer.js', pdf_viewer: './blob/pdf_viewer.js', pipelines_details: './pipelines/pipeline_details_bundle.js', - project_import_gl: './projects/project_import_gitlab_project.js', protected_branches: './protected_branches', protected_tags: './protected_tags', registry_list: './registry/index.js', From 7ed005a561351fa61c2572ffbb070741b826b8bd Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Feb 2018 01:02:04 -0600 Subject: [PATCH 32/53] remove unused filtered_search webpack bundle --- .../filtered_search/filtered_search_bundle.js | 10 ---------- config/webpack.config.js | 1 - spec/javascripts/test_bundle.js | 1 - 3 files changed, 12 deletions(-) delete mode 100644 app/assets/javascripts/filtered_search/filtered_search_bundle.js diff --git a/app/assets/javascripts/filtered_search/filtered_search_bundle.js b/app/assets/javascripts/filtered_search/filtered_search_bundle.js deleted file mode 100644 index 293154917fa..00000000000 --- a/app/assets/javascripts/filtered_search/filtered_search_bundle.js +++ /dev/null @@ -1,10 +0,0 @@ -import './dropdown_emoji'; -import './dropdown_hint'; -import './dropdown_non_user'; -import './dropdown_user'; -import './dropdown_utils'; -import './filtered_search_dropdown_manager'; -import './filtered_search_dropdown'; -import './filtered_search_manager'; -import './filtered_search_tokenizer'; -import './filtered_search_visual_tokens'; diff --git a/config/webpack.config.js b/config/webpack.config.js index 7e2a2d7e3b4..8321206e2cf 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -46,7 +46,6 @@ function generateEntries() { balsamiq_viewer: './blob/balsamiq_viewer.js', cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', environments: './environments/environments_bundle.js', - filtered_search: './filtered_search/filtered_search_bundle.js', monitoring: './monitoring/monitoring_bundle.js', mr_notes: './mr_notes/index.js', notebook_viewer: './blob/notebook_viewer.js', diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 94fcc6c7f2b..fb4946aeeea 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -126,7 +126,6 @@ if (process.env.BABEL_ENV === 'coverage') { './diff_notes/components/resolve_count.js', './dispatcher.js', './environments/environments_bundle.js', - './filtered_search/filtered_search_bundle.js', './graphs/graphs_bundle.js', './issuable/time_tracking/time_tracking_bundle.js', './main.js', From 97112250e1062746611d524a0799743d4cb13470 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Feb 2018 01:03:42 -0600 Subject: [PATCH 33/53] remove unused ui_development_kit webpack bundle --- config/webpack.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 8321206e2cf..051bc7922e2 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -57,7 +57,6 @@ function generateEntries() { sketch_viewer: './blob/sketch_viewer.js', stl_viewer: './blob/stl_viewer.js', terminal: './terminal/terminal_bundle.js', - ui_development_kit: './ui_development_kit.js', two_factor_auth: './two_factor_auth.js', From 6e49a757878ce24f9ef8c51f1f0c4f3e36a48081 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Feb 2018 01:08:18 -0600 Subject: [PATCH 34/53] remove common_vue from CommonsChunkPlugin --- config/webpack.config.js | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 051bc7922e2..ceba31ae0c8 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -225,34 +225,6 @@ var config = { return `${moduleNames[0]}-${hash.substr(0, 6)}`; }), - // create cacheable common library bundle for all vue chunks - new webpack.optimize.CommonsChunkPlugin({ - name: 'common_vue', - chunks: [ - 'boards', - 'cycle_analytics', - 'deploy_keys', - 'environments', - 'filtered_search', - 'groups', - 'monitoring', - 'mr_notes', - 'notebook_viewer', - 'pdf_viewer', - 'pipelines', - 'pipelines_details', - 'registry_list', - 'ide', - 'schedule_form', - 'schedules_index', - 'sidebar', - 'vue_merge_request_widget', - ], - minChunks: function(module, count) { - return module.resource && (/vue_shared/).test(module.resource); - }, - }), - // create cacheable common library bundles new webpack.optimize.CommonsChunkPlugin({ names: ['main', 'common', 'webpack_runtime'], From 5a3511ea78eb50176f4a5c4576ef7955663611c1 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Feb 2018 01:19:10 -0600 Subject: [PATCH 35/53] prefer let and const in webpack config --- config/webpack.config.js | 50 ++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index ceba31ae0c8..65785385554 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -1,33 +1,33 @@ 'use strict'; -var crypto = require('crypto'); -var fs = require('fs'); -var path = require('path'); -var glob = require('glob'); -var webpack = require('webpack'); -var StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin; -var CopyWebpackPlugin = require('copy-webpack-plugin'); -var CompressionPlugin = require('compression-webpack-plugin'); -var NameAllModulesPlugin = require('name-all-modules-plugin'); -var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; -var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); +const glob = require('glob'); +const webpack = require('webpack'); +const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin; +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const CompressionPlugin = require('compression-webpack-plugin'); +const NameAllModulesPlugin = require('name-all-modules-plugin'); +const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); -var ROOT_PATH = path.resolve(__dirname, '..'); -var IS_PRODUCTION = process.env.NODE_ENV === 'production'; -var IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1; -var DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost'; -var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808; -var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false'; -var WEBPACK_REPORT = process.env.WEBPACK_REPORT; -var NO_COMPRESSION = process.env.NO_COMPRESSION; +const ROOT_PATH = path.resolve(__dirname, '..'); +const IS_PRODUCTION = process.env.NODE_ENV === 'production'; +const IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1; +const DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost'; +const DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808; +const DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false'; +const WEBPACK_REPORT = process.env.WEBPACK_REPORT; +const NO_COMPRESSION = process.env.NO_COMPRESSION; -var autoEntriesCount = 0; -var watchAutoEntries = []; +let autoEntriesCount = 0; +let watchAutoEntries = []; function generateEntries() { // generate automatic entry points - var autoEntries = {}; - var pageEntries = glob.sync('pages/**/index.js', { cwd: path.join(ROOT_PATH, 'app/assets/javascripts') }); + const autoEntries = {}; + const pageEntries = glob.sync('pages/**/index.js', { cwd: path.join(ROOT_PATH, 'app/assets/javascripts') }); watchAutoEntries = [ path.join(ROOT_PATH, 'app/assets/javascripts/pages/'), ]; @@ -74,7 +74,7 @@ function generateEntries() { return Object.assign(manualEntries, autoEntries); } -var config = { +const config = { context: path.join(ROOT_PATH, 'app/assets/javascripts'), entry: generateEntries, @@ -166,7 +166,7 @@ var config = { new StatsWriterPlugin({ filename: 'manifest.json', transform: function(data, opts) { - var stats = opts.compiler.getStats().toJson({ + const stats = opts.compiler.getStats().toJson({ chunkModules: false, source: false, chunks: false, From 2577ff7fd656f301c4b1909f0a1c358c8c30a614 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 28 Feb 2018 12:16:23 +0200 Subject: [PATCH 36/53] Refactor plugins feature and make some doc improvements Signed-off-by: Dmitriy Zaporozhets --- app/workers/plugin_worker.rb | 8 +++++++- doc/administration/plugins.md | 5 +++-- lib/gitlab/plugin.rb | 10 ++-------- lib/gitlab/plugin_logger.rb | 8 ++++++++ lib/tasks/plugins.rake | 8 ++++---- spec/lib/gitlab/plugin_spec.rb | 16 ++++++++++------ spec/workers/plugin_worker_spec.rb | 14 ++++++++++---- 7 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 lib/gitlab/plugin_logger.rb diff --git a/app/workers/plugin_worker.rb b/app/workers/plugin_worker.rb index 34a3c8d62ac..bfcc683d99a 100644 --- a/app/workers/plugin_worker.rb +++ b/app/workers/plugin_worker.rb @@ -4,6 +4,12 @@ class PluginWorker sidekiq_options retry: false 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 diff --git a/doc/administration/plugins.md b/doc/administration/plugins.md index 89ab0b9c8ea..ed1a3480ffc 100644 --- a/doc/administration/plugins.md +++ b/doc/administration/plugins.md @@ -11,7 +11,7 @@ A plugin will run on each event so it's up to you to filter events or projects w ## 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: @@ -20,11 +20,12 @@ Follow the steps below to set up a custom hook: `/home/git/gitlab/plugins/`. For Omnibus installs the path is 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. 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 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 `#!/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 as appropriate. Plugins file list is updated for each event. There is no need to restart GitLab to apply a new plugin. diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb index 96332362a4c..0d1cb16b378 100644 --- a/lib/gitlab/plugin.rb +++ b/lib/gitlab/plugin.rb @@ -18,15 +18,9 @@ module Gitlab end exit_status = result.status&.exitstatus - - unless exit_status.zero? - Rails.logger.error("Plugin Error => #{file}: #{result.stderr}") - end - - exit_status.zero? + [exit_status.zero?, result.stderr] rescue => e - Rails.logger.error("Plugin Error => #{file}: #{e.message}") - false + [false, e.message] end end end diff --git a/lib/gitlab/plugin_logger.rb b/lib/gitlab/plugin_logger.rb new file mode 100644 index 00000000000..a106a2677ed --- /dev/null +++ b/lib/gitlab/plugin_logger.rb @@ -0,0 +1,8 @@ +module Gitlab + class PluginLogger < Gitlab::Logger + def self.file_name_noext + 'plugin' + end + end +end + diff --git a/lib/tasks/plugins.rake b/lib/tasks/plugins.rake index 11c41f13614..7a9de3afbec 100644 --- a/lib/tasks/plugins.rake +++ b/lib/tasks/plugins.rake @@ -4,12 +4,12 @@ namespace :plugins do puts 'Validating plugins from /plugins directory' 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 - puts "* #{file} succeed (zero exit code)" + if success + puts "* #{file} succeed (zero exit code)." else - puts "* #{file} failure (non-zero exit code)" + puts "* #{file} failure (non-zero exit code). #{message}" end end end diff --git a/spec/lib/gitlab/plugin_spec.rb b/spec/lib/gitlab/plugin_spec.rb index a01e1383e3b..33dd4f79130 100644 --- a/spec/lib/gitlab/plugin_spec.rb +++ b/spec/lib/gitlab/plugin_spec.rb @@ -5,6 +5,9 @@ describe Gitlab::Plugin do let(:data) { Gitlab::DataBuilder::Push::SAMPLE_DATA } let(:plugin) { Rails.root.join('plugins', 'test.rb') } 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 <<~EOS @@ -22,8 +25,6 @@ describe Gitlab::Plugin do FileUtils.rm(plugin) end - subject { described_class.execute(plugin.to_s, data) } - context 'successful execution' do before do File.chmod(0o777, plugin) @@ -33,17 +34,19 @@ describe Gitlab::Plugin do tmp_file.close! 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 - subject + result expect(File.read(tmp_file.path)).to eq(data.to_json) end end context 'non-executable' do - it { is_expected.to be false } + it { expect(success).to be false } + it { expect(message).to include('Permission denied') } end context 'non-zero exit' do @@ -58,7 +61,8 @@ describe Gitlab::Plugin do File.chmod(0o777, plugin) end - it { is_expected.to be false } + it { expect(success).to be false } + it { expect(message).to be_empty } end end end diff --git a/spec/workers/plugin_worker_spec.rb b/spec/workers/plugin_worker_spec.rb index 60631def8f3..9238a8199bc 100644 --- a/spec/workers/plugin_worker_spec.rb +++ b/spec/workers/plugin_worker_spec.rb @@ -3,16 +3,22 @@ require 'spec_helper' describe PluginWorker do include RepoHelpers - subject { described_class.new } - let(:filename) { 'my_plugin.rb' } + let(:data) { { 'event_name' => 'project_create' } } + + subject { described_class.new } describe '#perform' 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, '']) - allow(Gitlab::Plugin).to receive(:execute).with(filename, data).and_return(true) + expect(subject.perform(filename, data)).to be_truthy + end + 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 end end From 886d442b405a0db3dccc1b24e050244f65099826 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 28 Feb 2018 12:31:19 +0200 Subject: [PATCH 37/53] Improve plugins documentation and remove unnecessary rake task Signed-off-by: Dmitriy Zaporozhets --- doc/administration/plugins.md | 11 +++++++++-- lib/tasks/plugins.rake | 5 ----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/doc/administration/plugins.md b/doc/administration/plugins.md index ed1a3480ffc..c91ac3012b9 100644 --- a/doc/administration/plugins.md +++ b/doc/administration/plugins.md @@ -11,7 +11,8 @@ A plugin will run on each event so it's up to you to filter events or projects w ## Setup -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. +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: @@ -30,9 +31,15 @@ Follow the steps below to set up a custom hook: 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. +If a plugin executes with non-zero exit code or GitLab fails to execute it, a +message will be logged to `plugin.log`. + ## Validation -Writing own plugin can be tricky and its easier if you can check it without altering the system. We provided a rake task you can use with staging environment to test your plugin before using it in production. The rake task will use a sample data and execute each of plugins. By output you should be able to determine if system sees your plugin and if it was executed without errors. +Writing own plugin can be tricky and its easier if you can check it without altering the system. +We provided a rake task you can use with staging environment to test your plugin before using it in production. +The rake task will use a sample data and execute each of plugins. By output you should be able to determine if +system sees your plugin and if it was executed without errors. ```bash # Omnibus installations diff --git a/lib/tasks/plugins.rake b/lib/tasks/plugins.rake index 7a9de3afbec..e73dd7e68df 100644 --- a/lib/tasks/plugins.rake +++ b/lib/tasks/plugins.rake @@ -13,9 +13,4 @@ namespace :plugins do end end end - - desc 'Validate existing plugins' - task validate_async: :environment do - Gitlab::Plugin.execute_all_async(Gitlab::DataBuilder::Push::SAMPLE_DATA) - end end From a96ba41f229bd3606696e8e3a6500730e6cb8f63 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 28 Feb 2018 13:17:44 +0200 Subject: [PATCH 38/53] Remove trailing line from plugin logger Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/plugin_logger.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/gitlab/plugin_logger.rb b/lib/gitlab/plugin_logger.rb index a106a2677ed..c4f6ec3e21d 100644 --- a/lib/gitlab/plugin_logger.rb +++ b/lib/gitlab/plugin_logger.rb @@ -5,4 +5,3 @@ module Gitlab end end end - From e47a2ca30ab9be7ef77ac5fb6fe4a8224dab6f72 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 28 Feb 2018 17:10:28 +0100 Subject: [PATCH 39/53] Remove use of any_instance_of in runner spec In EE we prepend a module into BuildFinishedWorker. This would cause this spec to fail because RSpec doesn't support the use of "any_instance_of" with classes that include a prepended module. To work around this we just stub the "perform_async" class method instead of stubbing the "perform" instance method. --- spec/requests/api/runner_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 4021e537efc..72cafac3f90 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -700,7 +700,7 @@ describe API::Runner do context 'when tace is given' do it 'creates a trace artifact' do - allow_any_instance_of(BuildFinishedWorker).to receive(:perform).with(job.id) do + allow(BuildFinishedWorker).to receive(:perform_async).with(job.id) do CreateTraceArtifactWorker.new.perform(job.id) end From 54432de3b6574fd63da0723a2f2fc47b8e4037cc Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 28 Feb 2018 01:49:54 -0600 Subject: [PATCH 40/53] remove a bunch of superfluous common_vue bundles --- app/views/groups/merge_requests.html.haml | 3 --- app/views/projects/commit/_pipelines_list.haml | 3 --- app/views/projects/commit/show.html.haml | 2 -- app/views/projects/environments/folder.html.haml | 3 --- app/views/projects/environments/metrics.html.haml | 2 -- app/views/shared/issuable/_sidebar.html.haml | 2 -- app/views/shared/milestones/_sidebar.html.haml | 2 -- 7 files changed, 17 deletions(-) diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 520fe217ae1..4ccd16f3e11 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,8 +1,5 @@ - page_title "Merge Requests" -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' - - if group_merge_requests_count(state: 'all').zero? = render 'shared/empty_states/merge_requests', project_select_button: true - else diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml index a3fed25af28..68b35072f26 100644 --- a/app/views/projects/commit/_pipelines_list.haml +++ b/app/views/projects/commit/_pipelines_list.haml @@ -6,6 +6,3 @@ "empty-state-svg-path" => image_path('illustrations/pipelines_empty.svg'), "error-state-svg-path" => image_path('illustrations/pipelines_failed.svg'), } } - -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_vue') diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index ba86d943967..abb292f8f27 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -6,8 +6,6 @@ - @content_class = limited_container_width - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_description @commit.description -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_vue') .container-fluid{ class: [limited_container_width, container_class] } = render "commit_box" diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index d8054dbc372..1ac7dab6775 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -1,9 +1,6 @@ - @no_container = true - page_title "Environments" -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_vue') - #environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json), "folder-name" => @folder, "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index 91b3743e9e7..9d9759ebc5f 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -1,7 +1,5 @@ - @no_container = true - page_title "Metrics for environment", @environment.name -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' .prometheus-container{ class: container_class } .top-area diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index c1589027898..1a1e93bb0fe 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -1,6 +1,4 @@ - todo = issuable_todo(issuable) -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_vue') %aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: current_user.present? } }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } .issuable-sidebar{ data: { endpoint: "#{issuable_json_path(issuable)}" } } diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml index cd4188daf5b..a942ebc328b 100644 --- a/app/views/shared/milestones/_sidebar.html.haml +++ b/app/views/shared/milestones/_sidebar.html.haml @@ -1,7 +1,5 @@ - affix_offset = local_assigns.fetch(:affix_offset, "50") - project = local_assigns[:project] -- content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') %aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix", "always-show-toggle" => true }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } .issuable-sidebar.milestone-sidebar From aae4d8b014dc16fe1807b74957e6e586d67f0baf Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Wed, 28 Feb 2018 20:26:12 +0000 Subject: [PATCH 41/53] Remove webpack bundle tag for enviroments --- .../environments/{environments_bundle.js => index.js} | 4 ++-- app/assets/javascripts/pages/projects/environments/index.js | 3 +++ app/views/projects/environments/index.html.haml | 1 - config/webpack.config.js | 1 - 4 files changed, 5 insertions(+), 4 deletions(-) rename app/assets/javascripts/environments/{environments_bundle.js => index.js} (95%) create mode 100644 app/assets/javascripts/pages/projects/environments/index.js diff --git a/app/assets/javascripts/environments/environments_bundle.js b/app/assets/javascripts/environments/index.js similarity index 95% rename from app/assets/javascripts/environments/environments_bundle.js rename to app/assets/javascripts/environments/index.js index 2e0a4001b7c..afc4aba6554 100644 --- a/app/assets/javascripts/environments/environments_bundle.js +++ b/app/assets/javascripts/environments/index.js @@ -5,7 +5,7 @@ import Translate from '../vue_shared/translate'; Vue.use(Translate); -document.addEventListener('DOMContentLoaded', () => new Vue({ +export default () => new Vue({ el: '#environments-list-view', components: { environmentsComponent, @@ -36,4 +36,4 @@ document.addEventListener('DOMContentLoaded', () => new Vue({ }, }); }, -})); +}); diff --git a/app/assets/javascripts/pages/projects/environments/index.js b/app/assets/javascripts/pages/projects/environments/index.js new file mode 100644 index 00000000000..ace8af00ece --- /dev/null +++ b/app/assets/javascripts/pages/projects/environments/index.js @@ -0,0 +1,3 @@ +import initEnviroments from '~/environments/'; + +document.addEventListener('DOMContentLoaded', initEnviroments); diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 31cf173fa9c..0d656b25bc8 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -4,7 +4,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag("common_vue") - = webpack_bundle_tag("environments") #environments-list-view{ data: { environments_data: environments_list_data, "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, diff --git a/config/webpack.config.js b/config/webpack.config.js index d8ba3c06d3a..0d2b5dd3fa7 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -44,7 +44,6 @@ function generateEntries() { const manualEntries = { balsamiq_viewer: './blob/balsamiq_viewer.js', - environments: './environments/environments_bundle.js', monitoring: './monitoring/monitoring_bundle.js', mr_notes: './mr_notes/index.js', notebook_viewer: './blob/notebook_viewer.js', From 9d06504d4277ecf53e6f19014d060c6e7009dc0d Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Wed, 28 Feb 2018 21:23:58 +0000 Subject: [PATCH 42/53] Forbid all inline script tags in Linter::InlineJavaScript --- lib/haml_lint/inline_javascript.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/haml_lint/inline_javascript.rb b/lib/haml_lint/inline_javascript.rb index f5485eb89fa..872502bc8e3 100644 --- a/lib/haml_lint/inline_javascript.rb +++ b/lib/haml_lint/inline_javascript.rb @@ -8,7 +8,7 @@ unless Rails.env.production? include LinterRegistry def visit_filter(node) - return unless node.filter_type == 'javascript' + return unless node.tag_name == 'script' record_lint(node, 'Inline JavaScript is discouraged (https://docs.gitlab.com/ee/development/gotchas.html#do-not-use-inline-javascript-in-views)') end From 83b3e9fa40571c7475bc803b15e9b57ad2dc36b8 Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Wed, 28 Feb 2018 21:42:34 +0000 Subject: [PATCH 43/53] Update inline_javascript.rb --- lib/haml_lint/inline_javascript.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/haml_lint/inline_javascript.rb b/lib/haml_lint/inline_javascript.rb index 872502bc8e3..ff447513fd1 100644 --- a/lib/haml_lint/inline_javascript.rb +++ b/lib/haml_lint/inline_javascript.rb @@ -6,8 +6,8 @@ unless Rails.env.production? module HamlLint class Linter::InlineJavaScript < Linter include LinterRegistry - - def visit_filter(node) + + def visit_tag(node) return unless node.tag_name == 'script' record_lint(node, 'Inline JavaScript is discouraged (https://docs.gitlab.com/ee/development/gotchas.html#do-not-use-inline-javascript-in-views)') From 25283e71011490c674c54e64384957a692235e38 Mon Sep 17 00:00:00 2001 From: Connor Shea Date: Wed, 28 Feb 2018 15:58:12 -0700 Subject: [PATCH 44/53] Update inline_javascript.rb to lint uses of the javascript filter as well as script tags. --- lib/haml_lint/inline_javascript.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/haml_lint/inline_javascript.rb b/lib/haml_lint/inline_javascript.rb index ff447513fd1..4f776330e80 100644 --- a/lib/haml_lint/inline_javascript.rb +++ b/lib/haml_lint/inline_javascript.rb @@ -6,7 +6,13 @@ unless Rails.env.production? module HamlLint class Linter::InlineJavaScript < Linter include LinterRegistry - + + def visit_filter(node) + return unless node.filter_type == 'javascript' + + record_lint(node, 'Inline JavaScript is discouraged (https://docs.gitlab.com/ee/development/gotchas.html#do-not-use-inline-javascript-in-views)') + end + def visit_tag(node) return unless node.tag_name == 'script' From ad532918ed250ca341a2fd931598d1188adb35dd Mon Sep 17 00:00:00 2001 From: Connor Shea Date: Wed, 28 Feb 2018 15:59:50 -0700 Subject: [PATCH 45/53] Ignore InlineJavaScript linter in existing script tag locations. --- app/views/projects/branches/new.html.haml | 1 + app/views/projects/edit.html.haml | 1 + app/views/projects/graphs/charts.html.haml | 1 + app/views/projects/issues/show.html.haml | 1 + app/views/projects/pipelines/charts/_pipeline_times.haml | 1 + app/views/projects/pipelines/charts/_pipelines.haml | 1 + app/views/projects/pipelines/new.html.haml | 1 + app/views/projects/tags/new.html.haml | 1 + app/views/shared/boards/_show.html.haml | 1 + app/views/shared/issuable/_sidebar.html.haml | 3 +++ app/views/shared/notes/_notes_with_form.html.haml | 1 + app/views/u2f/_authenticate.html.haml | 1 + app/views/u2f/_register.html.haml | 1 + 13 files changed, 15 insertions(+) diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index e9d8fc75142..c7fc5a98ca8 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -28,4 +28,5 @@ .form-actions = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' +-# haml-lint:disable InlineJavaScript %script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index b947b91322d..a96485ab155 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -70,6 +70,7 @@ Enable or disable certain project features and choose access levels. .settings-content = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "sharing-permissions-form" }, authenticity_token: true do |f| + -# haml-lint:disable InlineJavaScript %script.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data(@project) .js-project-permissions-form = f.submit 'Save changes', class: "btn btn-save" diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml index d4b4a6203f3..14c47a5d91c 100644 --- a/app/views/projects/graphs/charts.html.haml +++ b/app/views/projects/graphs/charts.html.haml @@ -74,6 +74,7 @@ = _("Commits per day hour (UTC)") %canvas#hour-chart +-# haml-lint:disable InlineJavaScript %script#projectChartData{ type: "application/json" } - projectChartData = {}; - projectChartData['hour'] = @commits_per_time diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 77e6cb4d2cd..ec7e87219f5 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -55,6 +55,7 @@ .issue-details.issuable-details .detail-page-description.content-block + -# haml-lint:disable InlineJavaScript %script#js-issuable-app-initial-data{ type: "application/json" }= issuable_initial_data(@issue).to_json #js-issuable-app %h2.title= markdown_field(@issue, :title) diff --git a/app/views/projects/pipelines/charts/_pipeline_times.haml b/app/views/projects/pipelines/charts/_pipeline_times.haml index 510697c2ae9..c23fe6ff170 100644 --- a/app/views/projects/pipelines/charts/_pipeline_times.haml +++ b/app/views/projects/pipelines/charts/_pipeline_times.haml @@ -4,4 +4,5 @@ %canvas#build_timesChart{ height: 200 } +-# haml-lint:disable InlineJavaScript %script#pipelinesTimesChartsData{ type: "application/json" }= { :labels => @charts[:pipeline_times].labels, :values => @charts[:pipeline_times].pipeline_times }.to_json.html_safe diff --git a/app/views/projects/pipelines/charts/_pipelines.haml b/app/views/projects/pipelines/charts/_pipelines.haml index 2f4b6def155..14b3d47a9c2 100644 --- a/app/views/projects/pipelines/charts/_pipelines.haml +++ b/app/views/projects/pipelines/charts/_pipelines.haml @@ -26,6 +26,7 @@ = _("Pipelines for last year") %canvas#yearChart.padded{ height: 250 } +-# haml-lint:disable InlineJavaScript %script#pipelinesChartsData{ type: "application/json" } - chartData = [] - [:week, :month, :year].each do |scope| diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml index 4ad37d0e882..877101b05ca 100644 --- a/app/views/projects/pipelines/new.html.haml +++ b/app/views/projects/pipelines/new.html.haml @@ -20,4 +20,5 @@ = f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-cancel' +-# haml-lint:disable InlineJavaScript %script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 6e105a5521a..1827a3d323c 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -43,4 +43,5 @@ .form-actions = button_tag s_('TagsPage|Create tag'), class: 'btn btn-create' = link_to s_('TagsPage|Cancel'), project_tags_path(@project), class: 'btn btn-cancel' +-# haml-lint:disable InlineJavaScript %script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index 3312254f5fb..014b8de1dc9 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -7,6 +7,7 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' + -# haml-lint:disable InlineJavaScript %script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board" %script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 1a1e93bb0fe..adaddda13eb 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -117,10 +117,12 @@ = render partial: "shared/issuable/label_page_create" - if issuable.has_attribute?(:confidential) + -# haml-lint:disable InlineJavaScript %script#js-confidential-issue-data{ type: "application/json" }= { is_confidential: @issue.confidential, is_editable: can_edit_issuable }.to_json.html_safe #js-confidential-entry-point - if issuable.has_attribute?(:discussion_locked) + -# haml-lint:disable InlineJavaScript %script#js-lock-issue-data{ type: "application/json" }= { is_locked: issuable.discussion_locked?, is_editable: can_edit_issuable }.to_json.html_safe #js-lock-entry-point @@ -157,4 +159,5 @@ = _('Move') = icon('spinner spin', class: 'sidebar-move-issue-confirmation-loading-icon') + -# haml-lint:disable InlineJavaScript %script.js-sidebar-options{ type: "application/json" }= issuable_sidebar_options(issuable, can_edit_issuable).to_json.html_safe diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml index 0ceca367883..1db7c4e67cf 100644 --- a/app/views/shared/notes/_notes_with_form.html.haml +++ b/app/views/shared/notes/_notes_with_form.html.haml @@ -35,4 +35,5 @@ is locked. Only %b project members can comment. +-# haml-lint:disable InlineJavaScript %script.js-notes-data{ type: "application/json" }= initial_notes_data(autocomplete).to_json.html_safe diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml index f878bece2fa..7eb221620ad 100644 --- a/app/views/u2f/_authenticate.html.haml +++ b/app/views/u2f/_authenticate.html.haml @@ -1,6 +1,7 @@ #js-authenticate-u2f %a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' } Sign in via 2FA code +-# haml-lint:disable InlineJavaScript %script#js-authenticate-u2f-not-supported{ type: "text/template" } %p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer). diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml index 79e8f8d0e89..cc0e93c0755 100644 --- a/app/views/u2f/_register.html.haml +++ b/app/views/u2f/_register.html.haml @@ -1,5 +1,6 @@ #js-register-u2f +-# haml-lint:disable InlineJavaScript %script#js-register-u2f-not-supported{ type: "text/template" } %p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer). From 3e78a6137a327a7a8fee7cf2b1c6a7a547fac819 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 28 Feb 2018 10:49:51 -0800 Subject: [PATCH 46/53] Bump GitLab CI test image to use git v2.16 git 2.15.0 has some significant performance improvements when running `git fetch` on a repository that has lots of references. See https://gitlab.com/gitlab-com/migration/issues/216#note_60672987 for more details. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ae762e7aa6e..8a0c9802c15 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.6-golang-1.9-git-2.14-chrome-63.0-node-8.x-yarn-1.2-postgresql-9.6" +image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.6-golang-1.9-git-2.16-chrome-63.0-node-8.x-yarn-1.2-postgresql-9.6" .dedicated-runner: &dedicated-runner retry: 1 From 5ed53d4a612c6bd46d7c7b0f6f389c30a8b2c0a0 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 28 Feb 2018 14:55:50 -0800 Subject: [PATCH 47/53] Add update for packed-refs with git v2.16 --- spec/support/gitlab-git-test.git/packed-refs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/support/gitlab-git-test.git/packed-refs b/spec/support/gitlab-git-test.git/packed-refs index 507e4ce785a..ea50e4ad3f6 100644 --- a/spec/support/gitlab-git-test.git/packed-refs +++ b/spec/support/gitlab-git-test.git/packed-refs @@ -1,4 +1,4 @@ -# pack-refs with: peeled fully-peeled +# pack-refs with: peeled fully-peeled sorted 0b4bc9a49b562e85de7cc9e834518ea6828729b9 refs/heads/feature 12d65c8dd2b2676fa3ac47d955accc085a37a9c1 refs/heads/fix 6473c90867124755509e100d0d35ebdc85a0b6ae refs/heads/fix-blob-path From 4aa098f48c8bd4d6c63b311c8ac2061e4afcc452 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 28 Feb 2018 15:35:43 -0800 Subject: [PATCH 48/53] Tag migration specs with :sidekiq to clear queues between tests This was failing in https://gitlab.com/gitlab-org/gitlab-ee/-/jobs/55004375 --- spec/migrations/migrate_stages_statuses_spec.rb | 2 +- spec/migrations/schedule_build_stage_migration_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb index 79d2708f9ad..ce35276cbf5 100644 --- a/spec/migrations/migrate_stages_statuses_spec.rb +++ b/spec/migrations/migrate_stages_statuses_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require Rails.root.join('db', 'post_migrate', '20170711145558_migrate_stages_statuses.rb') -describe MigrateStagesStatuses, :migration do +describe MigrateStagesStatuses, :sidekiq, :migration do let(:jobs) { table(:ci_builds) } let(:stages) { table(:ci_stages) } let(:pipelines) { table(:ci_pipelines) } diff --git a/spec/migrations/schedule_build_stage_migration_spec.rb b/spec/migrations/schedule_build_stage_migration_spec.rb index 06657410173..e2ca35447fb 100644 --- a/spec/migrations/schedule_build_stage_migration_spec.rb +++ b/spec/migrations/schedule_build_stage_migration_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require Rails.root.join('db', 'post_migrate', '20180212101928_schedule_build_stage_migration') -describe ScheduleBuildStageMigration, :migration do +describe ScheduleBuildStageMigration, :sidekiq, :migration do let(:projects) { table(:projects) } let(:pipelines) { table(:ci_pipelines) } let(:stages) { table(:ci_stages) } From 5764f85cd116e32537ba368b18af0bfaaf31dcbe Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 1 Mar 2018 13:01:35 +0530 Subject: [PATCH 49/53] Add group related config and endpoint methods to be consistent with EE --- .../filtered_search_dropdown_manager.js | 27 ++++++++++++++++--- .../filtered_search_manager.js | 20 ++++++++------ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index c64553a1b92..ee49a7be0b2 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -10,13 +10,22 @@ import DropdownUser from './dropdown_user'; import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; export default class FilteredSearchDropdownManager { - constructor(baseEndpoint = '', tokenizer, page, isGroup, filteredSearchTokenKeys) { + constructor({ + baseEndpoint = '', + tokenizer, + page, + isGroup, + isGroupAncestor, + filteredSearchTokenKeys, + }) { this.container = FilteredSearchContainer.container; this.baseEndpoint = baseEndpoint.replace(/\/$/, ''); this.tokenizer = tokenizer; this.filteredSearchTokenKeys = filteredSearchTokenKeys || FilteredSearchTokenKeys; this.filteredSearchInput = this.container.querySelector('.filtered-search'); this.page = page; + this.groupsOnly = isGroup; + this.groupAncestor = isGroupAncestor; this.setupMapping(); @@ -59,7 +68,7 @@ export default class FilteredSearchDropdownManager { reference: null, gl: DropdownNonUser, extraArguments: { - endpoint: `${this.baseEndpoint}/milestones.json`, + endpoint: this.getMilestoneEndpoint(), symbol: '%', }, element: this.container.querySelector('#js-dropdown-milestone'), @@ -68,7 +77,7 @@ export default class FilteredSearchDropdownManager { reference: null, gl: DropdownNonUser, extraArguments: { - endpoint: `${this.baseEndpoint}/labels.json`, + endpoint: this.getLabelsEndpoint(), symbol: '~', preprocessing: DropdownUtils.duplicateLabelPreprocessing, }, @@ -90,6 +99,18 @@ export default class FilteredSearchDropdownManager { this.mapping = allowedMappings; } + getMilestoneEndpoint() { + const endpoint = `${this.baseEndpoint}/milestones.json`; + + return endpoint; + } + + getLabelsEndpoint() { + const endpoint = `${this.baseEndpoint}/labels.json`; + + return endpoint; + } + static addWordToInput(tokenName, tokenValue = '', clicked = false) { const input = FilteredSearchContainer.container.querySelector('.filtered-search'); diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index e294b629bd0..c6970d7837f 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -20,10 +20,13 @@ import DropdownUtils from './dropdown_utils'; export default class FilteredSearchManager { constructor({ page, + isGroup = false, + isGroupAncestor = false, filteredSearchTokenKeys = FilteredSearchTokenKeys, stateFiltersSelector = '.issues-state-filters', }) { - this.isGroup = false; + this.isGroup = isGroup; + this.isGroupAncestor = isGroupAncestor; this.states = ['opened', 'closed', 'merged', 'all']; this.page = page; @@ -75,13 +78,14 @@ export default class FilteredSearchManager { if (this.filteredSearchInput) { this.tokenizer = FilteredSearchTokenizer; - this.dropdownManager = new FilteredSearchDropdownManager( - this.filteredSearchInput.getAttribute('data-base-endpoint') || '', - this.tokenizer, - this.page, - this.isGroup, - this.filteredSearchTokenKeys, - ); + this.dropdownManager = new FilteredSearchDropdownManager({ + baseEndpoint: this.filteredSearchInput.getAttribute('data-base-endpoint') || '', + tokenizer: this.tokenizer, + page: this.page, + isGroup: this.isGroup, + isGroupAncestor: this.isGroupAncestor, + filteredSearchTokenKeys: this.filteredSearchTokenKeys, + }); this.recentSearchesRoot = new RecentSearchesRoot( this.recentSearchesStore, From 730d7dda3eeae43ee07881d10cde85649abc81b2 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 1 Mar 2018 13:03:41 +0530 Subject: [PATCH 50/53] Update FilteredSearchManager init config --- .../pages/search/init_filtered_search.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/pages/search/init_filtered_search.js b/app/assets/javascripts/pages/search/init_filtered_search.js index de8d4168d71..57f08701a4f 100644 --- a/app/assets/javascripts/pages/search/init_filtered_search.js +++ b/app/assets/javascripts/pages/search/init_filtered_search.js @@ -1,9 +1,21 @@ import FilteredSearchManager from '~/filtered_search/filtered_search_manager'; -export default ({ page }) => { +export default ({ + page, + filteredSearchTokenKeys, + isGroup, + isGroupAncestor, + stateFiltersSelector, +}) => { const filteredSearchEnabled = FilteredSearchManager && document.querySelector('.filtered-search'); if (filteredSearchEnabled) { - const filteredSearchManager = new FilteredSearchManager({ page }); + const filteredSearchManager = new FilteredSearchManager({ + page, + isGroup, + isGroupAncestor, + filteredSearchTokenKeys, + stateFiltersSelector, + }); filteredSearchManager.setup(); } }; From d40e05918ff330eec46e4c318c892db7dfc5c401 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 1 Mar 2018 13:04:24 +0530 Subject: [PATCH 51/53] Update class init config --- app/assets/javascripts/boards/filtered_search_boards.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index 0df1f7a6f82..57a7cc4ca30 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -4,7 +4,9 @@ import FilteredSearchManager from '../filtered_search/filtered_search_manager'; export default class FilteredSearchBoards extends FilteredSearchManager { constructor(store, updateUrl = false, cantEdit = []) { - super('boards'); + super({ + page: 'boards', + }); this.store = store; this.updateUrl = updateUrl; From add8a3081ac3415697f9f3006ddfe4557bf090cc Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 1 Mar 2018 13:05:49 +0530 Subject: [PATCH 52/53] Add support for custom container class --- .../vue_shared/components/sidebar/date_picker.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue index 1413dd69f24..3fcacd156c5 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue @@ -14,6 +14,11 @@ collapsedCalendarIcon, }, props: { + blockClass: { + type: String, + required: false, + default: '', + }, collapsed: { type: Boolean, required: false, @@ -91,7 +96,10 @@