├── .gitignore ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── jenkins-gitlab-hook.pluginspec ├── lib ├── gitlab.rb └── gitlab │ └── client.rb ├── models ├── api.rb ├── crumb_exclusion.rb ├── exceptions │ ├── bad_request_exception.rb │ ├── configuration_exception.rb │ └── not_found_exception.rb ├── gitlab_notifier.rb ├── root_action.rb ├── root_action_descriptor.rb ├── services │ ├── build_scm.rb │ ├── check_git_details.rb │ ├── flat_keys_hash.rb │ ├── get_build_actions.rb │ ├── get_build_cause.rb │ ├── get_jenkins_projects.rb │ ├── get_parameters_values.rb │ ├── parse_request.rb │ └── security.rb ├── unprotected_root_action.rb ├── use_cases │ ├── build_now.rb │ ├── create_project_for_branch.rb │ ├── notify_commit.rb │ ├── process_commit.rb │ ├── process_delete_commit.rb │ └── process_merge_request.rb ├── util │ └── settings.rb └── values │ ├── abstract_details.rb │ ├── commit.rb │ ├── merge_request_details.rb │ ├── parameters_request_details.rb │ ├── payload_request_details.rb │ ├── project.rb │ ├── repository_uri.rb │ ├── request_details.rb │ └── scm_data.rb ├── spec ├── acceptance_spec.rb ├── fixtures │ ├── 8x │ │ ├── merge_request.json │ │ ├── push.json │ │ └── tag.json │ ├── default_params.json │ ├── default_payload.json │ ├── default_tag.json │ ├── descriptor.xml │ ├── merge_request_payload.json │ ├── new_merge_request_payload.json │ ├── payloads │ │ ├── 8x │ │ │ ├── accept_merge_request.json │ │ │ ├── branch_creation.json │ │ │ ├── branch_deletion.json │ │ │ ├── branch_push.json │ │ │ ├── first_push.json │ │ │ ├── master_push.json │ │ │ ├── merge_request.json │ │ │ └── tag.json │ │ ├── accept_merge_request.json │ │ ├── branch_creation.json │ │ ├── branch_deletion.json │ │ ├── branch_push.json │ │ ├── first_push.json │ │ ├── legacy │ │ │ ├── accept_merge_request.json │ │ │ └── merge_request.json │ │ ├── master_push.json │ │ ├── merge_request.json │ │ └── tag.json │ └── testrepo.git │ │ ├── HEAD │ │ ├── config │ │ ├── objects │ │ ├── 32 │ │ │ └── e499748b811a2c9044bf9b6894072281120932 │ │ ├── 41 │ │ │ └── a6bb30b745172af19f158850a1633c4d259cff │ │ ├── 47 │ │ │ └── 1c1119345bef3ee97841cbca12ee9905ba8bed │ │ ├── 48 │ │ │ └── 9d14020729682229b17e5bb4a571632b1c4c99 │ │ ├── 51 │ │ │ └── c9a7db79925f9bc9e66669e1ed16158541fa1b │ │ ├── 69 │ │ │ └── 57dc21ae95f0c70931517841a9eb461f94548c │ │ ├── 80 │ │ │ └── a89e1156d5d7e9471c245ccaeafb7bcb49c0a5 │ │ ├── 86 │ │ │ └── 8b6adf6b5910cef199da9905c6026f3ee044c1 │ │ ├── 05 │ │ │ └── 627d3548e3a64b465c99fb1b8bc8765c506759 │ │ ├── 4b │ │ │ └── cd06209455a89ae3dd2f48e894c5d993ee3914 │ │ ├── 5c │ │ │ └── 87f8a2e42643936c5a274cb348b50445065e52 │ │ ├── 7b │ │ │ └── 16a5cba8b4bf090a28c99ef1cbe55ca3f8c7fb │ │ ├── 8e │ │ │ └── 8384098ff589ee941e142134bec284a329c061 │ │ ├── a0 │ │ │ └── 41e443aedc4df03d49f2db62bb278da1d0874c │ │ ├── ba │ │ │ └── 46b858929aec55a84a9cb044e988d5d347b8de │ │ ├── c1 │ │ │ └── a1e6918fdbf9fe49ad70060508abcc88b876d4 │ │ ├── d4 │ │ │ └── 36ccdac1e7e0dd31486a5355dd439eaa4b5cf8 │ │ ├── d9 │ │ │ └── 0c6ad729f14401dfdaebfa95eee2b105ae5bac │ │ └── e3 │ │ │ └── 719eaab95642a63e90da0b9b23de0c9d384785 │ │ └── refs │ │ ├── heads │ │ ├── feature │ │ │ └── branch │ │ └── master │ │ └── tags │ │ └── tag1 ├── models │ └── root_action_descriptor_spec.rb ├── services │ ├── build_scm_spec.rb │ ├── check_git_details_spec.rb │ ├── flat_keys_hash_spec.rb │ ├── get_build_actions_spec.rb │ ├── get_build_cause_spec.rb │ ├── get_jenkins_projects_spec.rb │ ├── get_parameters_values_spec.rb │ └── parse_request_spec.rb ├── spec_helper.rb ├── support │ ├── common.rb │ ├── env.rb │ ├── gitlab.rb │ ├── jenkins.rb │ ├── jenkins │ │ └── default_descriptor.rb │ ├── phantom.rb │ ├── shared │ │ ├── details.rb │ │ ├── mr_details.rb │ │ ├── projects.rb │ │ ├── settings.rb │ │ └── tag_details.rb │ └── travisci.rb ├── use_cases │ ├── build_now_spec.rb │ ├── create_project_for_branch_spec.rb │ ├── notify_commit_spec.rb │ ├── process_commit_spec.rb │ ├── process_delete_commit_spec.rb │ └── process_merge_request_spec.rb └── values │ ├── abstract_details_spec.rb │ ├── commit_spec.rb │ ├── matches_branch_spec.rb │ ├── merge_request_details_spec.rb │ ├── parameters_request_details_spec.rb │ ├── payload_request_details_spec.rb │ ├── project_spec.rb │ ├── repository_uri_spec.rb │ ├── request_details_spec.rb │ └── scm_data_spec.rb ├── views ├── gitlab_notifier │ ├── global.erb │ ├── help-commit_status.erb │ ├── help-gitlab_url.erb │ ├── help-mr_status_only.erb │ └── help-token.erb └── gitlab_web_hook_root_action │ ├── global.erb │ ├── help-automatic_project_creation.erb │ ├── help-description.erb │ ├── help-ignore_users.erb │ ├── help-master_branch.erb │ ├── help-merge_request_processing.erb │ ├── help-merged_branch_triggering.erb │ └── help-use_master_project_name.erb └── work ├── config.xml ├── gitlab-hook-GitlabNotifier.xml ├── gitlab-hook-GitlabWebHookRootAction.xml ├── hudson.plugins.git.GitSCM.xml ├── jenkins.model.JenkinsLocationConfiguration.xml └── jobs ├── multiscm └── config.xml.erb ├── simplejob └── config.xml ├── specificjob └── config.xml.erb ├── subdirjob └── config.xml.erb └── tagbuilder └── config.xml.erb /.gitignore: -------------------------------------------------------------------------------- 1 | # exclude Jenkins work files 2 | pkg 3 | work 4 | 5 | # exclude local bundle files 6 | .bundle 7 | 8 | # exclude repository for acceptance tests 9 | spec/fixtures/testrepo.git 10 | 11 | # exclude Idea files 12 | .idea 13 | *.iml 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | env: 3 | - JENKINS_VERSION=1.651.3 4 | - JENKINS_VERSION=2.89.2 5 | - JENKINS_VERSION=latest 6 | jdk: 7 | - oraclejdk8 8 | - openjdk7 9 | rvm: 10 | - 1.9.3 11 | - jruby-1.7-19mode # JRuby in 1.9 mode 12 | matrix: 13 | exclude: 14 | - env: JENKINS_VERSION=1.651.3 15 | jdk: openjdk7 16 | rvm: 1.9.3 17 | - env: JENKINS_VERSION=2.89.2 18 | jdk: openjdk7 19 | - env: JENKINS_VERSION=latest 20 | jdk: openjdk7 21 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | # Use this line instead if you want to bundle from a local copy. 4 | #gem 'jenkins-plugin-runtime', :path => "#{File.dirname(__FILE__)}/../jenkins-plugin-runtime" 5 | gem 'jenkins-plugin-runtime', '~> 0.2.3' 6 | 7 | # using sinatra to run the web hook 8 | gem 'sinatra' 9 | 10 | group :development do 11 | gem 'sinatra-json' 12 | gem 'jpi', '~> 0.4.0' 13 | gem 'rake' 14 | gem 'rspec', '>= 3.0.0' 15 | gem 'poltergeist' 16 | end 17 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | capybara (2.4.4) 5 | mime-types (>= 1.16) 6 | nokogiri (>= 1.3.3) 7 | rack (>= 1.0.0) 8 | rack-test (>= 0.5.4) 9 | xpath (~> 2.0) 10 | cliver (0.3.2) 11 | diff-lcs (1.2.5) 12 | jenkins-plugin-runtime (0.2.3) 13 | json 14 | slop (~> 3.0.2) 15 | jenkins-war (1.514) 16 | jpi (0.4.1) 17 | bundler 18 | jenkins-plugin-runtime (~> 0.2.3) 19 | jenkins-war (> 1.427) 20 | rubyzip (~> 1.1.0) 21 | thor 22 | json (1.7.5) 23 | json (1.7.5-java) 24 | mime-types (2.4.3) 25 | mini_portile (0.6.2) 26 | multi_json (1.11.0) 27 | nokogiri (1.6.6.2) 28 | mini_portile (~> 0.6.0) 29 | poltergeist (1.6.0) 30 | capybara (~> 2.1) 31 | cliver (~> 0.3.1) 32 | multi_json (~> 1.0) 33 | websocket-driver (>= 0.2.0) 34 | rack (1.4.1) 35 | rack-protection (1.2.0) 36 | rack 37 | rack-test (0.6.3) 38 | rack (>= 1.0) 39 | rake (10.3.2) 40 | rspec (3.0.0) 41 | rspec-core (~> 3.0.0) 42 | rspec-expectations (~> 3.0.0) 43 | rspec-mocks (~> 3.0.0) 44 | rspec-core (3.0.2) 45 | rspec-support (~> 3.0.0) 46 | rspec-expectations (3.0.2) 47 | diff-lcs (>= 1.2.0, < 2.0) 48 | rspec-support (~> 3.0.0) 49 | rspec-mocks (3.0.2) 50 | rspec-support (~> 3.0.0) 51 | rspec-support (3.0.2) 52 | rubyzip (1.1.6) 53 | sinatra (1.3.3) 54 | rack (~> 1.3, >= 1.3.6) 55 | rack-protection (~> 1.2) 56 | tilt (~> 1.3, >= 1.3.3) 57 | sinatra-json (0.1.0) 58 | multi_json (~> 1.0) 59 | sinatra (~> 1.0) 60 | slop (3.0.4) 61 | thor (0.19.1) 62 | tilt (1.3.3) 63 | websocket-driver (0.5.3) 64 | websocket-extensions (>= 0.1.0) 65 | websocket-extensions (0.1.2) 66 | xpath (2.0.0) 67 | nokogiri (~> 1.3) 68 | 69 | PLATFORMS 70 | java 71 | ruby 72 | 73 | DEPENDENCIES 74 | jenkins-plugin-runtime (~> 0.2.3) 75 | jpi (~> 0.4.0) 76 | poltergeist 77 | rake 78 | rspec (>= 3.0.0) 79 | sinatra 80 | sinatra-json 81 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rspec/core/rake_task' 3 | 4 | if RUBY_PLATFORM == 'java' 5 | task :default => :rspec 6 | else 7 | task :default => :acceptance 8 | end 9 | 10 | desc "Run all RSpec code examples" 11 | RSpec::Core::RakeTask.new(:rspec) do |t| 12 | t.pattern = "spec/*/**/*_spec.rb" 13 | end 14 | 15 | SPEC_SUITES = (Dir.entries('spec') - ['.', '..','fixtures']).select {|e| File.directory? "spec/#{e}" } 16 | namespace :rspec do 17 | SPEC_SUITES.each do |suite| 18 | desc "Run #{suite} RSpec code examples" 19 | RSpec::Core::RakeTask.new(suite) do |t| 20 | t.pattern = "spec/#{suite}/**/*_spec.rb" 21 | end 22 | end 23 | end 24 | 25 | desc "Run acceptance tests" 26 | RSpec::Core::RakeTask.new(:acceptance) do |t| 27 | t.pattern = "spec/acceptance_spec.rb" 28 | t.rspec_opts = "--order default --format doc" 29 | end 30 | 31 | -------------------------------------------------------------------------------- /jenkins-gitlab-hook.pluginspec: -------------------------------------------------------------------------------- 1 | Jenkins::Plugin::Specification.new do |plugin| 2 | plugin.name = "gitlab-hook" 3 | plugin.display_name = "Gitlab Hook Plugin" 4 | plugin.version = '1.4.2' 5 | plugin.description = 'Enables Gitlab web hooks to be used to trigger SMC polling on Gitlab projects' 6 | 7 | plugin.url = 'https://wiki.jenkins-ci.org/display/JENKINS/Gitlab+Hook+Plugin' 8 | plugin.developed_by "javiplx", "Javier Palacios " 9 | plugin.developed_by "elvanja", "Vanja Radovanovic " 10 | plugin.developed_by "hughepaul", "Paul Winkler " 11 | plugin.uses_repository :github => "jenkinsci/gitlab-hook-plugin" 12 | 13 | plugin.depends_on 'ruby-runtime', '0.12' 14 | plugin.depends_on 'git', '2.0' 15 | end 16 | -------------------------------------------------------------------------------- /lib/gitlab.rb: -------------------------------------------------------------------------------- 1 | require 'gitlab/client' 2 | -------------------------------------------------------------------------------- /lib/gitlab/client.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'json' 3 | 4 | module Gitlab 5 | class Client 6 | 7 | attr_reader :gitlab_url, :id, :namespace, :name, :code, :msg 8 | 9 | def initialize(descriptor, repo_name=nil) 10 | @gitlab_url = descriptor.gitlab_url 11 | @token = descriptor.token 12 | self.name = repo_name if repo_name 13 | end 14 | 15 | def name=(repo_namespace) 16 | @namespace = repo_namespace.split('/')[-2] 17 | if @name = repo_namespace.split('/').last 18 | @name.slice!(/.git$/) 19 | @id = repo_id 20 | end 21 | end 22 | 23 | def merge_request(project) 24 | 25 | src = project.matching_scms.first.branches.first.name.split '/' 26 | [ '*' , 'origin'].include?(src[0]) ? src.delete_at(0) : nil 27 | source = src.join '/' 28 | 29 | if target = project.merge_target 30 | do_request("projects/#{id}/merge_requests?state=opened").each do |mr| 31 | return mr['id'] if mr['source_branch'] == source && mr['target_branch'] == target 32 | end 33 | end 34 | return -1 35 | end 36 | 37 | def details(project_id) 38 | do_request("projects/#{project_id}") 39 | end 40 | 41 | def post_status(commit, status, ci_url, mr_id=nil) 42 | if mr_id.nil? 43 | status = case status.to_s 44 | when 'UNSTABLE', 'FAILURE', 'NOT_BUILT' 45 | 'failed' 46 | when 'ABORTED' 47 | 'canceled' 48 | else 49 | status.to_s.downcase 50 | end 51 | post_commit_status(commit, status, ci_url) 52 | elsif mr_id == -1 53 | post_commit_note(commit, status, ci_url) 54 | else 55 | post_mr_note(mr_id, status, ci_url) 56 | end 57 | end 58 | 59 | private 60 | 61 | attr_accessor :token 62 | 63 | def post_commit_status(commit, status, ci_url) 64 | url = "projects/#{id}/statuses/#{commit}" 65 | do_request url, :state => status, :target_url => ci_url 66 | end 67 | 68 | def post_commit_note(commit, status, ci_url) 69 | url = "projects/#{id}/repository/commits/#{commit}/comments" 70 | do_request url, :note => comment(status, ci_url) 71 | end 72 | 73 | def post_mr_note(mr_id, status, ci_url) 74 | url = "projects/#{id}/merge_request/#{mr_id}/comments" 75 | do_request url, :note => comment(status, ci_url) 76 | end 77 | 78 | def comment(status, ci_url) 79 | "[Jenkins CI result #{status}](#{ci_url})" 80 | end 81 | 82 | def me 83 | do_request("user")['id'] 84 | end 85 | 86 | def repo_id 87 | matches = do_request("projects/search/#{name}") 88 | if matches.is_a? Array 89 | matches.each do |repo| 90 | return repo['id'] if "#{namespace}/#{name}" == repo['path_with_namespace'] 91 | end 92 | @msg = "No gitlab project matches #{name}" 93 | else 94 | @msg = matches['message'] 95 | logger.severe("Cannot contact GitLab server : #{msg}") 96 | end 97 | return 98 | end 99 | 100 | def do_request(url, data=nil) 101 | 102 | uri = URI "#{gitlab_url}/api/v3/#{url}" 103 | 104 | if data 105 | req = Net::HTTP::Post.new uri.request_uri 106 | req.set_form_data data 107 | else 108 | req = Net::HTTP::Get.new uri.request_uri 109 | end 110 | 111 | req['PRIVATE-TOKEN'] = token 112 | 113 | http = Net::HTTP.new uri.host, uri.port 114 | if uri.scheme == 'https' 115 | http.use_ssl = true 116 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 117 | end 118 | 119 | res = http.request req 120 | 121 | begin 122 | JSON.parse(res.body).tap do |out| 123 | unless [ "200" , "201" ].include? res.code 124 | logger.severe "Bad Request '#{url}' : #{res.code} - #{res.msg}" 125 | logger.severe "Server response : #{JSON.pretty_generate(out)}" 126 | end 127 | @code = res.code 128 | end 129 | rescue JSON::ParserError 130 | logger.severe( "GitLab response is not JSON" ) 131 | @msg = "GitLab response is not JSON" 132 | end 133 | end 134 | 135 | def logger 136 | @logger ||= Java.java.util.logging.Logger.getLogger(Gitlab::Client.class.name) 137 | end 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /models/api.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra/base' 2 | require 'json' 3 | 4 | require_relative 'exceptions/bad_request_exception' 5 | require_relative 'exceptions/configuration_exception' 6 | require_relative 'exceptions/not_found_exception' 7 | require_relative 'use_cases/process_commit' 8 | require_relative 'use_cases/process_delete_commit' 9 | require_relative 'use_cases/process_merge_request' 10 | require_relative 'services/get_jenkins_projects' 11 | require_relative 'services/parse_request' 12 | require_relative 'services/check_git_details' 13 | 14 | java_import Java.org.jruby.exceptions.RaiseException 15 | 16 | module GitlabWebHook 17 | class Api < Sinatra::Base 18 | 19 | java_import Java.java.util.logging.Level 20 | 21 | get '/ping' do 22 | 'Gitlab Web Hook is up and running :-)' 23 | end 24 | 25 | notify_commit = lambda do 26 | process_projects Proc.new { |project| NotifyCommit.new(project).call } 27 | end 28 | get '/notify_commit', ¬ify_commit 29 | post '/notify_commit', ¬ify_commit 30 | 31 | build_now = lambda do 32 | process_projects Proc.new { |project, details| BuildNow.new(project).with(details) } 33 | end 34 | get '/build_now', &build_now 35 | post '/build_now', &build_now 36 | 37 | post '/build_now/:project_name' do 38 | get_jenkins_projects = GetJenkinsProjects.new 39 | if projects = get_jenkins_projects.named(params[:project_name]) 40 | project = projects.first 41 | cause_builder = GetBuildCause.new 42 | actions_builder = GetBuildActions.new 43 | details = parse_request 44 | project.scheduleBuild2(project.getQuietPeriod(), cause_builder.with(details), actions_builder.with(project, details)) 45 | end 46 | end 47 | 48 | error = lambda do 49 | unknown_route 50 | end 51 | get '/*', &error 52 | post '/*', &error 53 | 54 | private 55 | 56 | def process_projects(action) 57 | details = parse_request 58 | 59 | if errors = CheckGitDetails.new.with(details) 60 | return errors 61 | end 62 | 63 | projects = GetJenkinsProjects.new.matching_uri details 64 | if details.kind == 'merge_request' 65 | messages = ProcessMergeRequest.new.with(details, projects) 66 | else 67 | messages = details.delete_branch_commit? ? ProcessDeleteCommit.new.with(details, projects) : ProcessCommit.new.with(details, projects, action) 68 | end 69 | logger.info(messages.join("\n")) 70 | messages.collect { |message| message.gsub("\n", '
') }.join("
") 71 | rescue BadRequestException => e 72 | logger.warning(e.message) 73 | status 400 74 | e.message.gsub("\n", '
') 75 | rescue NotFoundException => e 76 | logger.warning(e.message) 77 | status 404 78 | e.message.gsub("\n", '
') 79 | rescue => e 80 | logger.severe("#{e.message}\nStack trace:\n#{e.backtrace.join("\n")}") 81 | status 500 82 | [e.message, '', 'Stack trace:', e.backtrace].flatten.join('
') 83 | end 84 | 85 | def parse_request 86 | ParseRequest.new.from(params, request).tap do |details| 87 | msgs = [ 88 | 'gitlab web hook triggered for', 89 | ' - with payload:', 90 | JSON.pretty_generate(details.payload) 91 | ] 92 | msgs.insert( 1, 93 | " - repo url: #{details.repository_url}", 94 | " - branch: #{details.branch}", 95 | ) unless details.kind == 'merge_request' 96 | logger.info(msgs.join("\n")) 97 | end 98 | end 99 | 100 | def unknown_route 101 | message = [ 102 | "Don't know how to process '/#{params[:splat].first}'", 103 | 'Use one of the following:', 104 | ' - /ping', 105 | ' - /notify_commit', 106 | ' - /build_now', 107 | ' - /build_now/:project_name', 108 | 'See https://github.com/elvanja/jenkins-gitlab-hook-plugin for details' 109 | ].join("\n") 110 | logger.warning(message) 111 | status 400 112 | message.gsub("\n", '
') 113 | end 114 | 115 | def logger 116 | @logger ||= Java.java.util.logging.Logger.getLogger(Api.class.name) 117 | end 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /models/crumb_exclusion.rb: -------------------------------------------------------------------------------- 1 | include Java 2 | 3 | java_import Java.hudson.security.csrf.CrumbExclusion 4 | 5 | require_relative 'root_action' 6 | 7 | class GitlabWebHookCrumbExclusion < CrumbExclusion 8 | def process(request, response, chain) 9 | return false unless request.getPathInfo().to_s.start_with?(exclusion_path()) 10 | chain.doFilter(request, response) 11 | true 12 | end 13 | 14 | private 15 | 16 | def exclusion_path 17 | "/#{GitlabWebHookRootAction::WEB_HOOK_ROOT_URL}/" 18 | end 19 | end 20 | 21 | Jenkins::Plugin.instance.register_extension(GitlabWebHookCrumbExclusion.new) 22 | -------------------------------------------------------------------------------- /models/exceptions/bad_request_exception.rb: -------------------------------------------------------------------------------- 1 | module GitlabWebHook 2 | class BadRequestException < StandardError 3 | end 4 | end -------------------------------------------------------------------------------- /models/exceptions/configuration_exception.rb: -------------------------------------------------------------------------------- 1 | module GitlabWebHook 2 | class ConfigurationException < StandardError 3 | end 4 | end -------------------------------------------------------------------------------- /models/exceptions/not_found_exception.rb: -------------------------------------------------------------------------------- 1 | module GitlabWebHook 2 | class NotFoundException < StandardError 3 | end 4 | end -------------------------------------------------------------------------------- /models/gitlab_notifier.rb: -------------------------------------------------------------------------------- 1 | require 'gitlab' 2 | 3 | class GitlabNotifier < Jenkins::Tasks::Publisher 4 | 5 | display_name 'GitLab commit status publisher' 6 | 7 | transient :descriptor, :client 8 | 9 | attr_reader :descriptor, :client 10 | attr_reader :project 11 | 12 | def initialize(attrs) 13 | create_client 14 | end 15 | 16 | def read_completed 17 | create_client 18 | end 19 | 20 | def prebuild(build, listener) 21 | env = build.native.environment listener 22 | @project = GitlabWebHook::Project.new( build.native.project , env ) 23 | unless client.gitlab_url 24 | listener.warn("No GitLab url configured, skipping reporting") 25 | return 26 | end 27 | client.name = repo_namespace(build, env) 28 | unless client.id 29 | listener.error("GitLab error : #{client.msg}") 30 | return 31 | end 32 | return unless descriptor.commit_status? 33 | if project.pre_build_merge? 34 | sha = post_commit build, listener 35 | else 36 | sha = env['GIT_COMMIT'] 37 | end 38 | # client.post_status( sha , 'running' , env['BUILD_URL'] ).tap do |msg| 39 | # unless [ "200" , "201" ].include? client.code 40 | # listener.warn("Failed gitlab notification : #{msg['message']}") 41 | # else 42 | # listener.info("GitLab notified about building of #{sha}") 43 | # end 44 | # end 45 | end 46 | 47 | def perform(build, launcher, listener) 48 | return unless client.id 49 | mr_id = client.merge_request(project) 50 | return if mr_id == -1 && descriptor.mr_status_only? 51 | env = build.native.environment listener 52 | if project.pre_build_merge? 53 | sha = post_commit build, listener 54 | else 55 | sha = env['GIT_COMMIT'] 56 | end 57 | client.post_status( sha , build.native.result , env['BUILD_URL'] , descriptor.commit_status? ? nil : mr_id ).tap do |msg| 58 | unless [ "200" , "201" ].include? client.code 59 | listener.warn("Failed gitlab notification : #{msg['message']}") 60 | else 61 | listener.info("GitLab notified about #{sha} result") 62 | end 63 | end 64 | end 65 | 66 | class GitlabNotifierDescriptor < Jenkins::Model::DefaultDescriptor 67 | 68 | java_import Java.hudson.util.Secret 69 | java_import Java.hudson.BulkChange 70 | java_import Java.hudson.model.listeners.SaveableListener 71 | 72 | attr_reader :gitlab_url 73 | 74 | def token 75 | @token.get_plain_text 76 | end 77 | 78 | def commit_status? 79 | @commit_status == 'true' 80 | end 81 | 82 | def mr_status_only? 83 | @mr_status_only == 'true' 84 | end 85 | 86 | def initialize(describable, object, describable_type) 87 | super 88 | load 89 | end 90 | 91 | def load 92 | return unless configFile.file.exists() 93 | xmlfile = File.new(configFile.file.canonicalPath) 94 | xmldoc = REXML::Document.new(xmlfile) 95 | if xmldoc.root 96 | @gitlab_url = xmldoc.root.elements['gitlab_url'].text 97 | @token = Secret.fromString xmldoc.root.elements['token'].text 98 | @commit_status = xmldoc.root.elements['commit_status'].nil? ? 'false' : xmldoc.root.elements['commit_status'].text 99 | @mr_status_only = xmldoc.root.elements['mr_status_only'].nil? ? 'true' : xmldoc.root.elements['mr_status_only'].text 100 | end 101 | end 102 | 103 | def configure(req, form) 104 | parse(form) 105 | save 106 | end 107 | 108 | def save 109 | return if BulkChange.contains(self) 110 | 111 | doc = REXML::Document.new 112 | doc.add_element( 'hudson.model.Descriptor' , { "plugin" => "gitlab-notifier" } ) 113 | 114 | doc.root.add_element( 'gitlab_url' ).add_text( gitlab_url ) 115 | doc.root.add_element( 'token' ).add_text( @token.get_encrypted_value ) 116 | doc.root.add_element( 'commit_status' ).add_text( @commit_status ) 117 | doc.root.add_element( 'mr_status_only' ).add_text( @mr_status_only ) 118 | 119 | f = File.open(configFile.file.canonicalPath, 'wb') 120 | f.puts("") 121 | 122 | formatter = REXML::Formatters::Pretty.new 123 | formatter.compact = true 124 | formatter.write doc, f 125 | 126 | f.close 127 | 128 | SaveableListener.fireOnChange(self, configFile) 129 | f.closed? 130 | end 131 | 132 | private 133 | 134 | def parse(form) 135 | @gitlab_url = form["gitlab_url"] 136 | @token = Secret.fromString form['token'] 137 | @commit_status = form['commit_status'] ? 'true' : 'false' 138 | @mr_status_only = form['mr_status_only'] ? 'true' : 'false' 139 | end 140 | 141 | end 142 | 143 | describe_as Java.hudson.tasks.Publisher, :with => GitlabNotifierDescriptor 144 | 145 | private 146 | 147 | def clone_dir( build ) 148 | if local_branch = project.local_clone 149 | build.workspace + local_branch 150 | else 151 | build.workspace 152 | end 153 | end 154 | 155 | def post_commit(build, listener) 156 | gitlog = StringIO.new 157 | launcher = build.workspace.create_launcher(listener) 158 | if launcher.execute('git', 'log', '-1', '--oneline' ,'--format=%P', {:out => gitlog, :chdir => clone_dir(build)} ) == 0 159 | parents = gitlog.string.split 160 | else 161 | listener.warn( "git-log failed : '#{parents.join(' ')}'" ) 162 | end 163 | parents.last 164 | end 165 | 166 | def create_client 167 | @descriptor = Jenkins::Plugin.instance.descriptors[GitlabNotifier] 168 | @client = Gitlab::Client.new @descriptor 169 | end 170 | 171 | def repo_namespace(build, env) 172 | env['GIT_URL'].split(':').last.split('/')[-2..-1].join('/') 173 | end 174 | 175 | end 176 | -------------------------------------------------------------------------------- /models/root_action.rb: -------------------------------------------------------------------------------- 1 | require 'jenkins/rack' 2 | 3 | require_relative 'unprotected_root_action' 4 | require_relative 'root_action_descriptor' 5 | require_relative 'api' 6 | 7 | class GitlabWebHookRootAction < Jenkins::Model::UnprotectedRootAction 8 | include Jenkins::Model::DescribableNative 9 | include Jenkins::RackSupport 10 | 11 | WEB_HOOK_ROOT_URL = "gitlab" 12 | 13 | display_name "Gitlab Web Hook" 14 | icon nil # we don't need the link in the main navigation 15 | url_path WEB_HOOK_ROOT_URL 16 | 17 | def call(env) 18 | GitlabWebHook::Api.new.call(env) 19 | end 20 | 21 | describe_as Java.hudson.model.Descriptor, with: GitlabWebHookRootActionDescriptor 22 | end 23 | 24 | Jenkins::Plugin.instance.register_extension(GitlabWebHookRootAction.new) 25 | -------------------------------------------------------------------------------- /models/root_action_descriptor.rb: -------------------------------------------------------------------------------- 1 | require 'rexml/document' 2 | 3 | java_import Java.hudson.BulkChange 4 | java_import Java.hudson.model.listeners.SaveableListener 5 | 6 | class GitlabWebHookRootActionDescriptor < Jenkins::Model::DefaultDescriptor 7 | # TODO a hook to delete artifacts from the feature branches would be nice 8 | 9 | MERGE_REQUEST_PROCESSING = 'merge_request_processing' 10 | MERGED_BRANCH_PROCESSING = 'merged_branch_triggering' 11 | AUTOMATIC_PROJECT_CREATION_PROPERTY = 'automatic_project_creation' 12 | MASTER_BRANCH_PROPERTY = 'master_branch' 13 | USE_MASTER_PROJECT_NAME_PROPERTY = 'use_master_project_name' 14 | DESCRIPTION_PROPERTY = 'description' 15 | IGNORE_USERS = 'ignore_users' 16 | DEFAULT_IGNORE_USERS = '' 17 | 18 | def initialize(*args) 19 | super 20 | begin 21 | load 22 | rescue Exception => ex 23 | logger.severe "Cannot load globan plugin configuration : #{ex}" 24 | end 25 | end 26 | 27 | def merge_request_processing? 28 | !!@merge_request_processing 29 | end 30 | 31 | def merged_branch_triggering? 32 | !!@merged_branch_triggering 33 | end 34 | 35 | def automatic_project_creation? 36 | !!@automatic_project_creation 37 | end 38 | 39 | def master_branch 40 | @master_branch || "master" 41 | end 42 | 43 | def use_master_project_name? 44 | !!@use_master_project_name 45 | end 46 | 47 | def description 48 | @description || "Automatically created by Gitlab Web Hook plugin" 49 | end 50 | 51 | def load 52 | return unless configFile.file.exists() 53 | 54 | doc = REXML::Document.new(File.new(configFile.file.canonicalPath)) 55 | if doc.root 56 | @ignore_usernames = read_property(doc, IGNORE_USERS, DEFAULT_IGNORE_USERS) 57 | @merge_request_processing = read_property(doc, MERGE_REQUEST_PROCESSING, "true") == "true" 58 | @merged_branch_triggering = read_property(doc, MERGED_BRANCH_PROCESSING, "false") == "true" 59 | @automatic_project_creation = read_property(doc, AUTOMATIC_PROJECT_CREATION_PROPERTY) == "true" 60 | @use_master_project_name = read_property(doc, USE_MASTER_PROJECT_NAME_PROPERTY) == "true" 61 | @master_branch = read_property(doc, MASTER_BRANCH_PROPERTY) 62 | @description = read_property(doc, DESCRIPTION_PROPERTY) 63 | @templates = get_templates doc.root.elements['templates'] 64 | @group_templates = get_templates doc.root.elements['group_templates'] 65 | @template = doc.root.elements['template'] && doc.root.elements['template'].text 66 | end 67 | end 68 | 69 | def configure(req, form) 70 | parse(form) 71 | save 72 | end 73 | 74 | def save 75 | return if BulkChange.contains(self) 76 | 77 | doc = REXML::Document.new 78 | doc.add_element('hudson.model.Descriptor', {"plugin" => "gitlab-hook"}) 79 | 80 | write_property(doc, MERGE_REQUEST_PROCESSING, merge_request_processing?) 81 | write_property(doc, MERGED_BRANCH_PROCESSING, merged_branch_triggering?) 82 | write_property(doc, AUTOMATIC_PROJECT_CREATION_PROPERTY, automatic_project_creation?) 83 | write_property(doc, MASTER_BRANCH_PROPERTY, master_branch) 84 | write_property(doc, USE_MASTER_PROJECT_NAME_PROPERTY, use_master_project_name?) 85 | write_property(doc, DESCRIPTION_PROPERTY, description) 86 | write_property(doc, IGNORE_USERS, ignore_users) 87 | 88 | doc.root.add_element( 'template' ).add_text( template_fallback ) 89 | 90 | tpls = doc.root.add_element( 'templates' ) 91 | templated_jobs.each do |k,v| 92 | new = tpls.add_element('template') 93 | new.add_element('string').add_text(k) 94 | new.add_element('project').add_text(v) 95 | end 96 | 97 | tpls = doc.root.add_element( 'group_templates' ) 98 | templated_groups.each do |k,v| 99 | new = tpls.add_element('template') 100 | new.add_element('string').add_text(k) 101 | new.add_element('project').add_text(v) 102 | end 103 | 104 | f = File.open(configFile.file.canonicalPath, 'wb') 105 | f.puts("") 106 | 107 | formatter = REXML::Formatters::Pretty.new 108 | formatter.compact = true 109 | formatter.write(doc, f) 110 | 111 | f.close 112 | 113 | SaveableListener.fireOnChange(self, configFile) 114 | f.closed? 115 | end 116 | 117 | def templated_jobs 118 | @templates || {} 119 | end 120 | 121 | def templated_groups 122 | @group_templates || {} 123 | end 124 | 125 | def template_fallback 126 | @template 127 | end 128 | 129 | def ignore_users 130 | @ignore_usernames || DEFAULT_IGNORE_USERS 131 | end 132 | 133 | private 134 | 135 | def parse(form) 136 | @ignore_usernames = form[IGNORE_USERS] 137 | @merge_request_processing = form[MERGE_REQUEST_PROCESSING] ? true : false 138 | @merged_branch_triggering = form[MERGED_BRANCH_PROCESSING] ? true : false 139 | @automatic_project_creation = form[AUTOMATIC_PROJECT_CREATION_PROPERTY] ? true : false 140 | if automatic_project_creation? 141 | @master_branch = form[AUTOMATIC_PROJECT_CREATION_PROPERTY][MASTER_BRANCH_PROPERTY] 142 | @use_master_project_name = form[AUTOMATIC_PROJECT_CREATION_PROPERTY][USE_MASTER_PROJECT_NAME_PROPERTY] 143 | @description = form[AUTOMATIC_PROJECT_CREATION_PROPERTY][DESCRIPTION_PROPERTY] 144 | end 145 | @template = form['template'] 146 | @templates = form['templates'] && form2list( form['templates'] ).inject({}) do |hash, item| 147 | hash.update( item['string'] => item['project'] ) 148 | end 149 | @group_templates = form['group_templates'] && form2list( form['group_templates'] ).inject({}) do |hash, item| 150 | hash.update( item['string'] => item['project'] ) 151 | end 152 | end 153 | 154 | def form2list(form_item) 155 | form_item.is_a?(Java::NetSfJson::JSONArray) ? form_item : [].push( form_item ) 156 | end 157 | 158 | def get_templates(templates) 159 | return unless templates 160 | templates.elements.select{ |tpl| tpl.name == 'template' }.inject({}) do |hash, tpl| 161 | hash[tpl.elements['string'].text] = tpl.elements['project'].text 162 | hash 163 | end 164 | end 165 | 166 | def read_property(doc, property, default=nil) 167 | if item = doc.root.elements[property] 168 | return item.text 169 | end 170 | default 171 | end 172 | 173 | def write_property(doc, property, value) 174 | doc.root.add_element(property).add_text(value.to_s) 175 | end 176 | 177 | def logger 178 | @logger ||= Java.java.util.logging.Logger.getLogger(GitlabWebHookRootActionDescriptor.class.name) 179 | end 180 | end 181 | -------------------------------------------------------------------------------- /models/services/build_scm.rb: -------------------------------------------------------------------------------- 1 | require_relative '../values/scm_data' 2 | 3 | include Java 4 | 5 | java_import Java.hudson.plugins.git.GitSCM 6 | java_import Java.hudson.plugins.git.BranchSpec 7 | java_import Java.hudson.plugins.git.UserRemoteConfig 8 | java_import Java.hudson.plugins.git.browser.GitLab 9 | java_import Java.hudson.plugins.git.util.DefaultBuildChooser 10 | java_import Java.hudson.util.VersionNumber 11 | 12 | 13 | module GitlabWebHook 14 | class BuildScm 15 | 16 | def with(source_scm, details, is_template=false) 17 | # refspec is skipped, we will build specific commit branch 18 | scm_data = ScmData.new(source_scm, details, is_template) 19 | 20 | build_scm(scm_data) 21 | end 22 | 23 | private 24 | 25 | def build_scm(scm_data) 26 | GitSCM.new( 27 | java.util.ArrayList.new([UserRemoteConfig.new(scm_data.url, scm_data.name, scm_data.refspec, scm_data.credentials).java_object]), 28 | scm_data.branchlist, 29 | scm_data.source_scm.isDoGenerateSubmoduleConfigurations(), 30 | scm_data.source_scm.getSubmoduleCfg(), 31 | scm_data.source_scm.getBrowser(), 32 | scm_data.source_scm.getGitTool(), 33 | scm_data.source_scm.getExtensions() 34 | ) 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /models/services/check_git_details.rb: -------------------------------------------------------------------------------- 1 | 2 | require_relative '../util/settings' 3 | 4 | module GitlabWebHook 5 | class CheckGitDetails 6 | include Settings 7 | 8 | def with(details) 9 | raise ArgumentError.new('details are required') unless details 10 | 11 | ignore_users = settings.ignore_users.split(',').collect(&:strip).reject(&:empty?) 12 | ignore_users.each do|ignore_user| 13 | return "Not processing request for a git action by #{ignore_user}" if details.user_name == ignore_user 14 | end 15 | 16 | return nil 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /models/services/flat_keys_hash.rb: -------------------------------------------------------------------------------- 1 | module GitlabWebHook 2 | module FlatKeysHash 3 | FLATTENED_KEYS_DELIMITER = "." 4 | 5 | def to_flat_keys 6 | flatten_keys(self) 7 | end 8 | 9 | private 10 | 11 | def flatten_keys(data) 12 | return data unless data.is_a?(Enumerable) 13 | 14 | prepare_for_flattening(data).inject({}) do |flattened, (key, value)| 15 | flattened.merge!(expand_keys(key, value)) 16 | end 17 | end 18 | 19 | def prepare_for_flattening(data) 20 | return data unless data.is_a?(Array) 21 | 22 | Hash[data.map.with_index { |value, index| [index.to_s, value] }] 23 | end 24 | 25 | def expand_keys(key, value) 26 | flattened = flatten_keys(value) 27 | (value.is_a?(Enumerable) ? build_expanded(flattened, key) : {}).tap do |expanded| 28 | expanded["#{key}"] = value.is_a?(Enumerable) ? value : flattened 29 | end 30 | end 31 | 32 | def build_expanded(flattened, key) 33 | flattened.inject({}) do |expanded, (nested_key, nested_value)| 34 | expanded["#{key}#{FLATTENED_KEYS_DELIMITER}#{nested_key}"] = nested_value 35 | expanded 36 | end 37 | end 38 | end 39 | end -------------------------------------------------------------------------------- /models/services/get_build_actions.rb: -------------------------------------------------------------------------------- 1 | require_relative 'get_parameters_values' 2 | 3 | include Java 4 | 5 | java_import Java.hudson.model.ParametersAction 6 | 7 | module GitlabWebHook 8 | class GetBuildActions 9 | def with(project, details) 10 | raise ArgumentError.new('project is required') unless project 11 | raise ArgumentError.new('details are required') unless details 12 | 13 | return [] unless project.parametrized? # no need to process if not parameterized 14 | 15 | if details.kind == 'merge_request' 16 | ParametersAction.new( java.util.ArrayList.new(GetParametersValues.new.with_mr(project, details)) ) 17 | else 18 | ParametersAction.new( java.util.ArrayList.new(GetParametersValues.new.with(project, details)) ) 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /models/services/get_build_cause.rb: -------------------------------------------------------------------------------- 1 | include Java 2 | 3 | java_import Java.hudson.model.Cause 4 | 5 | module GitlabWebHook 6 | class GetBuildCause 7 | def with(details) 8 | raise ArgumentError.new('details are required') unless details 9 | 10 | notes = details.payload ? from_payload(details) : 'no payload available' 11 | Cause::RemoteCause.new(details.repository_uri.host, notes) 12 | end 13 | 14 | def from_payload(details) 15 | notes = "triggered by " 16 | if details.kind == 'merge_request' 17 | notes += "merge request #{details.branch} -> #{details.target_branch}" 18 | elsif details.tagname.nil? 19 | notes += "push on branch #{details.branch} with " 20 | if details.commits_count == '1' 21 | notes += "one commit" 22 | else 23 | notes += "#{details.commits_count} commits" 24 | end 25 | else 26 | notes += "tag #{details.tagname}" 27 | end 28 | notes 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /models/services/get_jenkins_projects.rb: -------------------------------------------------------------------------------- 1 | require_relative '../exceptions/not_found_exception' 2 | require_relative '../values/project' 3 | require_relative '../util/settings' 4 | require_relative '../services/security' 5 | 6 | include Java 7 | 8 | java_import Java.hudson.model.AbstractProject 9 | java_import Java.hudson.matrix.MatrixConfiguration 10 | 11 | java_import Java.hudson.plugins.git.GitSCM 12 | java_import Java.hudson.plugins.git.BranchSpec 13 | java_import Java.hudson.plugins.git.UserRemoteConfig 14 | java_import Java.hudson.plugins.git.browser.GitLab 15 | java_import Java.hudson.plugins.git.util.DefaultBuildChooser 16 | 17 | module GitlabWebHook 18 | class GetJenkinsProjects 19 | include Settings 20 | 21 | attr_reader :logger 22 | 23 | def initialize(logger = Java.java.util.logging.Logger.getLogger(GetJenkinsProjects.class.name)) 24 | @logger = logger 25 | end 26 | 27 | def matching_uri(details) 28 | all.select do |project| 29 | project.matches_uri?(details.repository_uri) 30 | end.tap { |projects| log_matched(projects) } 31 | end 32 | 33 | def named(name) 34 | all.select do |project| 35 | project.name == name 36 | end 37 | end 38 | 39 | def master(details) 40 | projects = all.select do |project| 41 | project.matches_uri?(details.repository_uri) && !project.tag? 42 | end 43 | 44 | # find project for the repo and master branch 45 | # use any other branch matching the repo 46 | projects.find { |project| project.matches?(details, settings.master_branch, true) } || projects.first 47 | end 48 | 49 | private 50 | 51 | def all 52 | projects = nil 53 | Security.impersonate(ACL::SYSTEM) do 54 | projects = Java.jenkins.model.Jenkins.instance.getAllItems(AbstractProject.java_class).map do |jenkins_project| 55 | Project.new(jenkins_project) unless jenkins_project.java_kind_of?(MatrixConfiguration) 56 | end - [nil] 57 | end 58 | projects 59 | end 60 | 61 | def log_matched(projects) 62 | logger.info(['matching projects:'].concat(projects.map { |project| " - #{project}" }).join("\n")) 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /models/services/get_parameters_values.rb: -------------------------------------------------------------------------------- 1 | include Java 2 | 3 | java_import Java.hudson.model.StringParameterDefinition 4 | java_import Java.hudson.model.StringParameterValue 5 | 6 | module GitlabWebHook 7 | class GetParametersValues 8 | def with(project, details) 9 | raise ArgumentError.new('project is required') unless project 10 | raise ArgumentError.new('details are required') unless details 11 | 12 | values = build_from_payload_or_default(details, project) 13 | remove_empty(values) 14 | apply_branch(project, details, values) 15 | 16 | values 17 | end 18 | 19 | def with_mr(project, details) 20 | raise ArgumentError.new('project is required') unless project 21 | raise ArgumentError.new('details are required') unless details 22 | 23 | project.get_default_parameters.collect do |parameter| 24 | from_payload(parameter, details.payload) || parameter.getDefaultParameterValue() 25 | end.reject { |value| value.nil? } 26 | end 27 | 28 | private 29 | 30 | def build_from_payload_or_default(details, project) 31 | project.get_default_parameters.collect do |parameter| 32 | from_payload(parameter, details.flat_payload) || parameter.getDefaultParameterValue() 33 | end 34 | end 35 | 36 | def remove_empty(values) 37 | values.reject! { |value| value.nil? } 38 | end 39 | 40 | def apply_branch(project, details, values) 41 | branch_parameter = project.get_branch_name_parameter 42 | if branch_parameter 43 | tagname = branch_parameter.name.downcase == 'tagname' 44 | if ( tagname && details.tagname ) || ( !tagname && details.tagname.nil? ) 45 | values.reject! { |value| value.name.downcase == branch_parameter.name.downcase } 46 | values << StringParameterValue.new(branch_parameter.name, tagname ? details.tagname : details.branch) 47 | end 48 | end 49 | end 50 | 51 | def from_payload(parameter, payload) 52 | # TODO possibly support other types as well? 53 | return nil unless parameter.java_kind_of?(StringParameterDefinition) 54 | 55 | value = payload.find { |key, _| key.downcase == parameter.name.downcase }.to_a[1] 56 | value.nil? ? value : StringParameterValue.new(parameter.name, value.to_s.strip) 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /models/services/parse_request.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | require_relative '../exceptions/bad_request_exception' 4 | require_relative '../values/parameters_request_details' 5 | require_relative '../values/payload_request_details' 6 | require_relative '../values/merge_request_details' 7 | 8 | module GitlabWebHook 9 | class ParseRequest 10 | EMPTY_BODY = '{}' 11 | 12 | def from(parameters, request) 13 | details = ParametersRequestDetails.new(parameters) 14 | return details if details.valid? 15 | 16 | body = read_request_body(request) 17 | details = PayloadRequestDetails.new(parse_request_body(body)) 18 | return details if details.valid? 19 | 20 | details = MergeRequestDetails.new(parse_request_body(body)) 21 | throw_bad_request_exception(body, parameters) unless details.valid? 22 | details 23 | end 24 | 25 | private 26 | 27 | def read_request_body(request) 28 | request.body.rewind 29 | body = request.body.read 30 | return body.empty? ? EMPTY_BODY : body 31 | rescue 32 | EMPTY_BODY 33 | end 34 | 35 | def parse_request_body(body) 36 | JSON.parse(body) 37 | rescue 38 | {} 39 | end 40 | 41 | def throw_bad_request_exception(body, parameters) 42 | raise BadRequestException.new([ 43 | 'repo url could not be found in Gitlab payload or the HTTP parameters:', 44 | " - body: #{body}", 45 | " - parameters: #{JSON.pretty_generate(parameters)}" 46 | ].join("\n")) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /models/services/security.rb: -------------------------------------------------------------------------------- 1 | include Java 2 | 3 | java_import Java.hudson.security.ACL 4 | java_import Java.org.acegisecurity.context.SecurityContextHolder 5 | 6 | module GitlabWebHook 7 | class Security 8 | def self.impersonate(acl = ACL::ANONYMOUS) 9 | securityContext = ACL.impersonate(acl) 10 | begin 11 | yield 12 | ensure 13 | SecurityContextHolder.setContext(securityContext) 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /models/unprotected_root_action.rb: -------------------------------------------------------------------------------- 1 | module Jenkins 2 | module Model 3 | class UnprotectedRootAction 4 | include Jenkins::Model::Action 5 | end 6 | 7 | class UnprotectedRootActionProxy 8 | include ActionProxy 9 | include Java.hudson.model.UnprotectedRootAction 10 | proxy_for Jenkins::Model::UnprotectedRootAction 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /models/use_cases/build_now.rb: -------------------------------------------------------------------------------- 1 | require_relative '../services/get_jenkins_projects' 2 | require_relative '../services/get_build_cause' 3 | require_relative '../services/get_build_actions' 4 | 5 | module GitlabWebHook 6 | class BuildNow 7 | attr_reader :project, :logger 8 | 9 | java_import Java.java.util.logging.Logger 10 | java_import Java.java.util.logging.Level 11 | 12 | def initialize(project, logger = Logger.getLogger(self.class.name)) 13 | raise ArgumentError.new('project is required') unless project 14 | @project = project 15 | @logger = logger 16 | end 17 | 18 | def with(details, cause_builder = GetBuildCause.new, actions_builder = GetBuildActions.new) 19 | return "#{project} is configured to ignore notify commit, skipping the build" if project.ignore_notify_commit? 20 | return "#{project} is not buildable (it is disabled or not saved), skipping the build" unless project.buildable? 21 | raise ArgumentError.new('details are required') unless details 22 | 23 | begin 24 | return "#{project} scheduled for build" if project.scheduleBuild2(project.getQuietPeriod(), cause_builder.with(details), actions_builder.with(project, details)) 25 | rescue java.lang.Exception => e 26 | # avoid method signature warnings 27 | severe = logger.java_method(:log, [Level, java.lang.String, java.lang.Throwable]) 28 | severe.call(Level::SEVERE, e.message, e) 29 | end 30 | 31 | "#{project} could not be scheduled for build" 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /models/use_cases/create_project_for_branch.rb: -------------------------------------------------------------------------------- 1 | require_relative '../exceptions/not_found_exception' 2 | require_relative '../exceptions/configuration_exception' 3 | require_relative '../values/project' 4 | require_relative '../services/get_jenkins_projects' 5 | require_relative '../services/build_scm' 6 | require_relative '../util/settings' 7 | require_relative '../services/security' 8 | 9 | java_import Java.hudson.plugins.git.GitSCM 10 | java_import Java.hudson.plugins.git.BranchSpec 11 | java_import Java.hudson.plugins.git.UserRemoteConfig 12 | java_import Java.hudson.plugins.git.UserMergeOptions 13 | java_import Java.hudson.plugins.git.extensions.impl.PreBuildMerge 14 | 15 | module GitlabWebHook 16 | class CreateProjectForBranch 17 | include Settings 18 | 19 | def initialize(get_jenkins_projects = GetJenkinsProjects.new, build_scm = BuildScm.new) 20 | @get_jenkins_projects = get_jenkins_projects 21 | @build_scm = build_scm 22 | @logger = Java.java.util.logging.Logger.getLogger(self.class.name) 23 | end 24 | 25 | def with(details) 26 | return if details.branch.empty? 27 | copy_from = get_project_to_copy_from(details) 28 | new_project_name = get_new_project_name(copy_from, details) 29 | @logger.warning( "Multiple-SCMs project #{copy_from} incompletelly copied onto #{new_project_name}" ) if copy_from.multiscm? 30 | new_project_scm = @build_scm.with(copy_from.matched_scm || copy_from.matching_scms.find.first, details) 31 | branch_project = nil 32 | 33 | Security.impersonate(ACL::SYSTEM) do 34 | branch_project = Java.jenkins.model.Jenkins.instance.copy(copy_from.jenkins_project, new_project_name) 35 | branch_project.scm = new_project_scm 36 | branch_project.makeDisabled(false) 37 | branch_project.description = settings.description 38 | branch_project.save 39 | end 40 | 41 | Project.new(branch_project) 42 | end 43 | 44 | def from_template(template, details) 45 | return if details.branch.empty? 46 | copy_from = get_template_project(template) 47 | raise ConfigurationException.new("Templates with multiples-scms plugin not supported") if copy_from.multiscm? 48 | new_project_name = details.repository_name 49 | raise ConfigurationException.new("project #{new_project_name} already created from #{template}") unless @get_jenkins_projects.named(new_project_name).empty? 50 | modified_scm = @build_scm.with(copy_from.jenkins_project.scm, details, true) 51 | branch_project = nil 52 | 53 | Security.impersonate(ACL::SYSTEM) do 54 | branch_project = Java.jenkins.model.Jenkins.instance.copy(copy_from.jenkins_project, new_project_name) 55 | branch_project.scm = modified_scm 56 | branch_project.makeDisabled(false) 57 | branch_project.save 58 | end 59 | 60 | Project.new(branch_project) 61 | end 62 | 63 | def for_merge(details) 64 | get_candidate_projects(details).collect do |copy_from| 65 | new_project_name = "#{copy_from.name}-mr-#{details.safe_branch}" 66 | @logger.warning( "Multiple-SCMs project #{copy_from} incompletelly copied onto #{new_project_name}" ) if copy_from.multiscm? 67 | cloned_scm = @build_scm.with(copy_from.matched_scm || copy_from.matching_scms.find.first, details) 68 | # What about candidates with pre-build merge enabled? 69 | user_merge_options = UserMergeOptions.new('origin', details.target_branch, 'default') 70 | cloned_scm.extensions.add PreBuildMerge.new(user_merge_options) 71 | new_project = nil 72 | 73 | Security.impersonate(ACL::SYSTEM) do 74 | new_project = Java.jenkins.model.Jenkins.instance.copy(copy_from.jenkins_project, new_project_name) 75 | new_project.scm = cloned_scm 76 | new_project.makeDisabled(false) 77 | new_project.description = settings.description 78 | new_project.save 79 | end 80 | 81 | Project.new(new_project) 82 | end 83 | end 84 | 85 | private 86 | 87 | def get_project_to_copy_from(details) 88 | master_not_found_message = 'could not determine master project, please create a project for the repo (usually for the master branch)' 89 | @get_jenkins_projects.master(details) || raise(NotFoundException.new(master_not_found_message)) 90 | end 91 | 92 | def get_template_project(template) 93 | candidates = @get_jenkins_projects.named(template) 94 | raise NotFoundException.new("could not found template '#{template}'") if candidates.empty? 95 | candidates.first 96 | end 97 | 98 | def get_candidate_projects(details) 99 | not_found_message = "could not find candidate for #{details.repository_name}::#{details.branch}" 100 | @get_jenkins_projects.matching_uri(details).select do |project| 101 | project.matches?(details, details.target_branch, true) 102 | end || raise(NotFoundException.new(not_found_message)) 103 | end 104 | 105 | def get_new_project_name(copy_from, details) 106 | new_project_name = "#{settings.use_master_project_name? ? copy_from.name : details.repository_name}_#{details.safe_branch}" 107 | raise ConfigurationException.new("project #{new_project_name} already exists") unless @get_jenkins_projects.named(new_project_name).empty? 108 | new_project_name 109 | end 110 | 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /models/use_cases/notify_commit.rb: -------------------------------------------------------------------------------- 1 | require_relative '../services/get_jenkins_projects' 2 | 3 | module GitlabWebHook 4 | class NotifyCommit 5 | attr_reader :project, :logger 6 | 7 | java_import Java.java.util.logging.Logger 8 | java_import Java.java.util.logging.Level 9 | 10 | def initialize(project, logger = Logger.getLogger(NotifyCommit.class.name)) 11 | raise ArgumentError.new('project is required') unless project 12 | @project = project 13 | @logger = logger 14 | end 15 | 16 | def call 17 | return "#{project} is configured to ignore notify commit, skipping scheduling for polling" if project.ignore_notify_commit? 18 | return "#{project} is not buildable (it is disabled or not saved), skipping polling" unless project.buildable? 19 | 20 | begin 21 | return "#{project} scheduled for polling" if project.schedulePolling 22 | rescue java.lang.Exception => e 23 | # avoid method signature warnings 24 | severe = logger.java_method(:log, [Level, java.lang.String, java.lang.Throwable]) 25 | severe.call(Level::SEVERE, e.message, e) 26 | end 27 | 28 | "#{project} could not be scheduled for polling, it is disabled or has no SCM trigger" 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /models/use_cases/process_commit.rb: -------------------------------------------------------------------------------- 1 | require_relative 'create_project_for_branch' 2 | require_relative '../util/settings' 3 | 4 | module GitlabWebHook 5 | class ProcessCommit 6 | include Settings 7 | 8 | def initialize 9 | @create_project_for_branch = CreateProjectForBranch.new 10 | end 11 | 12 | def with(details, projects, action) 13 | 14 | if projects.any? 15 | if settings.automatic_project_creation? 16 | projects.select! do |project| 17 | project.matches?(details, details.branch, true) 18 | end 19 | projects << @create_project_for_branch.with(details) if projects.empty? 20 | else 21 | projects.select! do |project| 22 | project.matches?(details) 23 | end 24 | end 25 | else 26 | search_templates(details, projects) 27 | end 28 | 29 | raise NotFoundException.new('no project references the given repo url and commit branch') unless projects.any? 30 | 31 | messages = [] 32 | projects.each do |project| 33 | messages << action.call(project, details) 34 | end 35 | messages 36 | end 37 | 38 | private 39 | 40 | def search_templates(details, projects) 41 | settings.templated_jobs.each do |matchstr,template| 42 | if details.repository_name.start_with? matchstr 43 | projects << @create_project_for_branch.from_template(template, details) 44 | end 45 | end 46 | return if projects.any? 47 | settings.templated_groups.each do |matchstr,template| 48 | if details.repository_group == matchstr 49 | projects << @create_project_for_branch.from_template(template, details) 50 | end 51 | end 52 | return if projects.any? 53 | if settings.template_fallback 54 | projects << @create_project_for_branch.from_template(settings.template_fallback, details) 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /models/use_cases/process_delete_commit.rb: -------------------------------------------------------------------------------- 1 | require_relative '../util/settings' 2 | require_relative '../services/security' 3 | 4 | module GitlabWebHook 5 | class ProcessDeleteCommit 6 | include Settings 7 | 8 | def with(details, projects) 9 | 10 | return ["branch #{details.branch} is deleted, but automatic branch projects creation is not active, skipping processing"] unless settings.automatic_project_creation? 11 | return ["branch #{details.branch} is deleted, but relates to master project so will not delete, skipping processing"] if details.branch == settings.master_branch 12 | 13 | messages = [] 14 | projects.select do |project| 15 | project.matches?(details, details.branch, true) 16 | end.each do |project| 17 | messages << "project #{project} matches deleted branch but is not automatically created by the plugin, skipping" and next unless project.description.match /#{settings.description}/ 18 | Security.impersonate(ACL::SYSTEM) do 19 | project.delete 20 | end 21 | messages << "deleted #{project} project" 22 | end 23 | messages << "no project matches the #{details.branch} branch" if messages.empty? 24 | 25 | messages 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /models/use_cases/process_merge_request.rb: -------------------------------------------------------------------------------- 1 | require_relative 'create_project_for_branch.rb' 2 | require_relative '../util/settings' 3 | require_relative '../services/security' 4 | 5 | module GitlabWebHook 6 | class ProcessMergeRequest 7 | include Settings 8 | 9 | def initialize 10 | @create_project_for_branch = CreateProjectForBranch.new 11 | end 12 | 13 | def with(details, candidates) 14 | messages = [] 15 | return messages unless settings.merge_request_processing? 16 | if details.merge_status == 'cannot_be_merged' && details.state != 'closed' 17 | messages << "Skipping not ready merge request for #{details.repository_name} with #{details.merge_status} status" 18 | else 19 | return [ 'No merge-request project candidates'] unless candidates 20 | 21 | candidates.select! do |project| 22 | project.matches?(details, details.branch, true) && project.merge_to?(details.target_branch, true) 23 | end 24 | 25 | case details.state 26 | when 'opened', 'reopened' 27 | if candidates.any? 28 | candidates.each do |project| 29 | messages << "Already created #{project.name} for #{details.branch} -> #{details.target_branch}" 30 | messages << BuildNow.new(project).with(details) 31 | end 32 | else 33 | projects = @create_project_for_branch.for_merge(details) 34 | if projects.any? 35 | projects.each do |project| 36 | messages << "Created #{project.name} for #{details.branch} from #{details.repository_name}" 37 | messages << BuildNow.new(project).with(details) 38 | end 39 | else 40 | messages << "No project candidate for merging #{details.safe_branch}" 41 | end 42 | end 43 | when 'closed', 'merged' 44 | candidates.each do |project| 45 | Security.impersonate(ACL::SYSTEM) { project.delete } 46 | messages << "Deleting merge-request project #{project.name}" 47 | end 48 | else 49 | messages << "Skipping request : merge request status is '#{details.state}'" 50 | end 51 | end 52 | messages 53 | end 54 | 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /models/util/settings.rb: -------------------------------------------------------------------------------- 1 | module GitlabWebHook 2 | module Settings 3 | def settings 4 | Java.jenkins.model.Jenkins.instance.descriptor(GitlabWebHookRootActionDescriptor.java_class) 5 | end 6 | end 7 | end -------------------------------------------------------------------------------- /models/values/abstract_details.rb: -------------------------------------------------------------------------------- 1 | 2 | module GitlabWebHook 3 | class AbstractDetails 4 | 5 | attr_accessor :kind 6 | 7 | def valid? 8 | raise NameError.new("should be implemented in concrete implementation") 9 | end 10 | 11 | def repository_uri 12 | RepositoryUri.new(repository_url) 13 | end 14 | 15 | def repository_url 16 | raise NameError.new("should be implemented in concrete implementation") 17 | end 18 | 19 | def repository_name 20 | raise NameError.new("should be implemented in concrete implementation") 21 | end 22 | 23 | def repository_homepage 24 | raise NameError.new("should be implemented in concrete implementation") 25 | end 26 | 27 | def branch 28 | raise NameError.new("should be implemented in concrete implementation") 29 | end 30 | 31 | def tagname 32 | return nil 33 | end 34 | 35 | def full_branch_reference 36 | raise NameError.new("should be implemented in concrete implementation") 37 | end 38 | 39 | def safe_branch 40 | branch.gsub("/", "_") 41 | end 42 | 43 | def payload 44 | payload = get_payload || {} 45 | raise ArgumentError.new("payload must be a hash") unless payload.is_a?(Hash) 46 | payload 47 | end 48 | 49 | def user_name 50 | payload["user_name"] || "" 51 | end 52 | 53 | private 54 | 55 | def get_payload 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /models/values/commit.rb: -------------------------------------------------------------------------------- 1 | module GitlabWebHook 2 | class Commit 3 | attr_reader :url, :message 4 | 5 | def initialize(url, message) 6 | @url = url 7 | @message = message 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /models/values/merge_request_details.rb: -------------------------------------------------------------------------------- 1 | require_relative 'abstract_details' 2 | require_relative '../exceptions/bad_request_exception' 3 | 4 | require 'gitlab' 5 | 6 | module GitlabWebHook 7 | class MergeRequestDetails < AbstractDetails 8 | 9 | def initialize(payload) 10 | raise(ArgumentError.new("request payload is required")) unless payload 11 | @kind = payload['object_kind'] 12 | @payload = payload['object_attributes'] 13 | throw_cross_repo_exception unless project_id == target_project_id 14 | end 15 | 16 | def valid? 17 | kind == 'merge_request' 18 | end 19 | 20 | def project_id 21 | return "" unless payload['source_project_id'] 22 | payload['source_project_id'].to_s 23 | end 24 | 25 | def branch 26 | return "" unless payload['source_branch'] 27 | payload['source_branch'] 28 | end 29 | 30 | def full_branch_reference 31 | "refs/heads/#{branch}" 32 | end 33 | 34 | def target_project_id 35 | return "" unless payload['target_project_id'] 36 | payload['target_project_id'].to_s 37 | end 38 | 39 | def target_branch 40 | return "" unless payload['target_branch'] 41 | payload['target_branch'] 42 | end 43 | 44 | def state 45 | return "" unless payload['state'] 46 | payload['state'] 47 | end 48 | 49 | def merge_status 50 | return "" unless payload['merge_status'] 51 | payload['merge_status'] 52 | end 53 | 54 | def repository_url 55 | payload["source"] ? payload["source"]["ssh_url"] : extended["ssh_url_to_repo"] 56 | end 57 | 58 | def repository_name 59 | payload["source"] ? payload["source"]["name"] : extended["name"] 60 | end 61 | 62 | def repository_homepage 63 | payload["source"] ? payload["source"]["homepage"] : extended["web_url"] 64 | end 65 | 66 | private 67 | 68 | def extended 69 | @extended ||= get_project_details 70 | end 71 | 72 | def get_project_details 73 | descriptor = Jenkins::Plugin.instance.descriptors[GitlabNotifier] 74 | Gitlab::Client.new(descriptor).details(project_id) 75 | end 76 | 77 | def throw_cross_repo_exception 78 | message = "Cross-repo merge requests not supported" 79 | raise BadRequestException.new(message) 80 | end 81 | 82 | def get_payload 83 | @payload 84 | end 85 | 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /models/values/parameters_request_details.rb: -------------------------------------------------------------------------------- 1 | require_relative 'request_details' 2 | 3 | module GitlabWebHook 4 | class ParametersRequestDetails < RequestDetails 5 | attr_reader :parameters 6 | 7 | def initialize(parameters) 8 | @kind = 'parameters' 9 | @parameters = parameters || raise(ArgumentError.new("request parameters are required")) 10 | end 11 | 12 | def repository_url 13 | url = nil 14 | [:repo_url, :url, :repository_url].each do |key| 15 | url ||= parameters[key] 16 | url ||= parameters[key.to_s] 17 | end 18 | url ? url.strip : "" 19 | end 20 | 21 | def repository_name 22 | name = nil 23 | [:repo_name, :name, :repository_name].each do |key| 24 | name ||= parameters[key] 25 | name ||= parameters[key.to_s] 26 | end 27 | name ? name.strip : "" 28 | end 29 | 30 | def repository_homepage 31 | homepage = nil 32 | [:repo_homepage, :homepage, :repository_homepage].each do |key| 33 | homepage ||= parameters[key] 34 | homepage ||= parameters[key.to_s] 35 | end 36 | homepage ? homepage.strip : "" 37 | end 38 | 39 | def full_branch_reference 40 | ref = nil 41 | [:ref, :branch, :branch_reference].each do |key| 42 | ref ||= parameters[key] 43 | ref ||= parameters[key.to_s] 44 | end 45 | ref ? ref.strip : "" 46 | end 47 | 48 | def delete_branch_commit? 49 | delete = nil 50 | [:delete_branch_commit, :delete].each do |key| 51 | delete ||= parameters[key] 52 | delete ||= parameters[key.to_s] 53 | end 54 | return false unless delete 55 | delete.to_s != "0" || delete 56 | end 57 | 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /models/values/payload_request_details.rb: -------------------------------------------------------------------------------- 1 | require_relative 'request_details' 2 | 3 | module GitlabWebHook 4 | class PayloadRequestDetails < RequestDetails 5 | def initialize(payload) 6 | @payload = payload || raise(ArgumentError.new("request payload is required")) 7 | @kind = payload['object_kind'].nil? ? 'webhook' : payload['object_kind'] 8 | end 9 | 10 | def valid? 11 | [ 'push' , 'tag_push' ].include?(kind) or super 12 | end 13 | 14 | def repository_url 15 | return "" unless payload["repository"] 16 | return "" unless payload["repository"]["url"] 17 | payload["repository"]["url"].strip 18 | end 19 | 20 | def repository_group 21 | return "" unless repository_homepage 22 | repository_homepage.split('/')[-2] 23 | end 24 | 25 | def repository_name 26 | return "" unless payload["repository"] 27 | return "" unless payload["repository"]["name"] 28 | payload["repository"]["name"].strip 29 | end 30 | 31 | def repository_homepage 32 | return "" unless payload["repository"] 33 | return "" unless payload["repository"]["homepage"] 34 | payload["repository"]["homepage"].strip 35 | end 36 | 37 | def full_branch_reference 38 | payload["ref"].to_s.strip 39 | end 40 | 41 | def delete_branch_commit? 42 | after = payload["after"] 43 | after ? (after.strip.squeeze == "0") : false 44 | end 45 | 46 | private 47 | 48 | def get_commits 49 | @commits ||= payload["commits"].to_a.map do |commit| 50 | Commit.new(commit["url"], commit["message"]) 51 | end 52 | end 53 | 54 | def get_payload 55 | @payload 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /models/values/repository_uri.rb: -------------------------------------------------------------------------------- 1 | include Java 2 | 3 | java_import Java.org.eclipse.jgit.transport.URIish 4 | 5 | module GitlabWebHook 6 | class RepositoryUri 7 | attr_reader :url 8 | 9 | def initialize(url) 10 | @url = url 11 | @uri = parse_url(url) 12 | end 13 | 14 | def matches?(other_uri) 15 | parse_uri(other_uri) == parse_uri(@uri) 16 | end 17 | 18 | def host 19 | @uri ? @uri.host : "" 20 | end 21 | 22 | private 23 | 24 | def parse_url(url) 25 | begin 26 | # explicitly using the correct constructor to remove annoying warning 27 | return (URIish.java_class.constructor(java.lang.String).new_instance(url)).to_java(URIish) 28 | rescue 29 | end 30 | end 31 | 32 | def parse_uri(uri) 33 | return nil, nil unless uri 34 | return normalize_host(uri.host), normalize_path(uri.path) 35 | end 36 | 37 | def normalize_host(host) 38 | return unless host 39 | host.downcase 40 | end 41 | 42 | def normalize_path(path) 43 | return unless path 44 | 45 | path.slice!(0) if path.start_with?('/') 46 | path.slice!(-1) if path.end_with?('/') 47 | path.slice!(-4..-1) if path.end_with?('.git') 48 | path.downcase 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /models/values/request_details.rb: -------------------------------------------------------------------------------- 1 | require_relative 'abstract_details' 2 | require_relative '../services/flat_keys_hash' 3 | 4 | module GitlabWebHook 5 | class RequestDetails < AbstractDetails 6 | 7 | def valid? 8 | ['webhook', 'parameters'].include?(kind) && !repository_url.to_s.strip.empty? 9 | end 10 | 11 | def branch 12 | ref = full_branch_reference 13 | return "" unless ref && tagname.nil? 14 | 15 | refs = ref.split("/") 16 | refs.reject { |ref| ref =~ /\A(ref|head)s?\z/ }.join("/") 17 | end 18 | 19 | def tagname 20 | return nil unless full_branch_reference.start_with?('refs/tags/') 21 | full_branch_reference.sub('refs/tags/', '') 22 | end 23 | 24 | def delete_branch_commit? 25 | raise NameError.new("should be implemented in concrete implementation") 26 | end 27 | 28 | def commits 29 | commits = get_commits || [] 30 | raise ArgumentError.new("payload must be an array") unless commits.is_a?(Array) 31 | commits 32 | end 33 | 34 | def commits_count 35 | commits ? commits.size : 0 36 | end 37 | 38 | def flat_payload 39 | @flat_payload ||= payload.extend(FlatKeysHash).to_flat_keys.tap do |flattened| 40 | [ 41 | :repository_url, 42 | :repository_name, 43 | :repository_homepage, 44 | :full_branch_reference, 45 | :branch 46 | ].each { |detail| flattened[detail.to_s] = self.send(detail) } 47 | flattened['tagname'] = tagname unless tagname.nil? 48 | end 49 | end 50 | 51 | private 52 | 53 | def get_commits 54 | end 55 | 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /models/values/scm_data.rb: -------------------------------------------------------------------------------- 1 | require_relative '../exceptions/configuration_exception' 2 | 3 | module GitlabWebHook 4 | class ScmData 5 | attr_reader :source_scm, :details, :url, :name, :branchlist, :credentials, :refspec 6 | 7 | def initialize(source_scm, details, is_template=false) 8 | @source_scm = source_scm 9 | @details = details 10 | from_config(@source_scm.getUserRemoteConfigs().first, is_template) 11 | raise ConfigurationException.new('remote repo clone url not found') unless valid? 12 | end 13 | 14 | private 15 | 16 | def from_config(config, is_template=false) 17 | raise ConfigurationException.new('No git configuration found') unless config 18 | @url = is_template ? details.repository_url : config.getUrl() 19 | @name = config.getName() || 'origin' 20 | @credentials = config.getCredentialsId() 21 | if is_template 22 | @branchlist = source_scm.getBranches() 23 | else 24 | branch = "#{@name}/#{@details.branch}" 25 | @branchlist = java.util.ArrayList.new([BranchSpec.new(branch).java_object]) 26 | end 27 | @refspec = config.getRefspec() 28 | end 29 | 30 | def valid? 31 | !@url.to_s.empty? 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/fixtures/8x/merge_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "merge_request", 3 | "user": { 4 | "name": "Administrator", 5 | "username": "root", 6 | "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" 7 | }, 8 | "object_attributes": { 9 | "id": 99, 10 | "target_branch": "master", 11 | "source_branch": "ms-viewport", 12 | "source_project_id": 14, 13 | "author_id": 51, 14 | "assignee_id": 6, 15 | "title": "MS-Viewport", 16 | "created_at": "2013-12-03T17:23:34Z", 17 | "updated_at": "2013-12-03T17:23:34Z", 18 | "st_commits": null, 19 | "st_diffs": null, 20 | "milestone_id": null, 21 | "state": "opened", 22 | "merge_status": "unchecked", 23 | "target_project_id": 14, 24 | "iid": 1, 25 | "description": "", 26 | "source":{ 27 | "name":"Awesome Project", 28 | "description":"Aut reprehenderit ut est.", 29 | "web_url":"http://example.com/awesome_space/awesome_project", 30 | "avatar_url":null, 31 | "git_ssh_url":"git@example.com:awesome_space/awesome_project.git", 32 | "git_http_url":"http://example.com/awesome_space/awesome_project.git", 33 | "namespace":"Awesome Space", 34 | "visibility_level":20, 35 | "path_with_namespace":"awesome_space/awesome_project", 36 | "default_branch":"master", 37 | "homepage":"http://example.com/awesome_space/awesome_project", 38 | "url":"http://example.com/awesome_space/awesome_project.git", 39 | "ssh_url":"git@example.com:awesome_space/awesome_project.git", 40 | "http_url":"http://example.com/awesome_space/awesome_project.git" 41 | }, 42 | "target": { 43 | "name":"Awesome Project", 44 | "description":"Aut reprehenderit ut est.", 45 | "web_url":"http://example.com/awesome_space/awesome_project", 46 | "avatar_url":null, 47 | "git_ssh_url":"git@example.com:awesome_space/awesome_project.git", 48 | "git_http_url":"http://example.com/awesome_space/awesome_project.git", 49 | "namespace":"Awesome Space", 50 | "visibility_level":20, 51 | "path_with_namespace":"awesome_space/awesome_project", 52 | "default_branch":"master", 53 | "homepage":"http://example.com/awesome_space/awesome_project", 54 | "url":"http://example.com/awesome_space/awesome_project.git", 55 | "ssh_url":"git@example.com:awesome_space/awesome_project.git", 56 | "http_url":"http://example.com/awesome_space/awesome_project.git" 57 | }, 58 | "last_commit": { 59 | "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 60 | "message": "fixed readme", 61 | "timestamp": "2012-01-03T23:36:29+02:00", 62 | "url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 63 | "author": { 64 | "name": "GitLab dev user", 65 | "email": "gitlabdev@dv6700.(none)" 66 | } 67 | }, 68 | "work_in_progress": false, 69 | "url": "http://example.com/diaspora/merge_requests/1", 70 | "action": "open", 71 | "assignee": { 72 | "name": "User1", 73 | "username": "user1", 74 | "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /spec/fixtures/8x/push.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "push", 3 | "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", 4 | "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 5 | "ref": "refs/heads/master", 6 | "user_id": 4, 7 | "user_name": "John Smith", 8 | "user_email": "john@example.com", 9 | "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", 10 | "project_id": 15, 11 | "project":{ 12 | "name":"Diaspora", 13 | "description":"", 14 | "web_url":"http://example.com/mike/diaspora", 15 | "avatar_url":null, 16 | "git_ssh_url":"git@example.com:mike/diaspora.git", 17 | "git_http_url":"http://example.com/mike/diaspora.git", 18 | "namespace":"Mike", 19 | "visibility_level":0, 20 | "path_with_namespace":"mike/diaspora", 21 | "default_branch":"master", 22 | "homepage":"http://example.com/mike/diaspora", 23 | "url":"git@example.com:mike/diaspora.git", 24 | "ssh_url":"git@example.com:mike/diaspora.git", 25 | "http_url":"http://example.com/mike/diaspora.git" 26 | }, 27 | "repository":{ 28 | "name": "Diaspora", 29 | "url": "git@example.com:mike/diaspora.git", 30 | "description": "", 31 | "homepage": "http://example.com/mike/diaspora", 32 | "git_http_url":"http://example.com/mike/diaspora.git", 33 | "git_ssh_url":"git@example.com:mike/diaspora.git", 34 | "visibility_level":0 35 | }, 36 | "commits": [ 37 | { 38 | "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", 39 | "message": "Update Catalan translation to e38cb41.", 40 | "timestamp": "2011-12-12T14:27:31+02:00", 41 | "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", 42 | "author": { 43 | "name": "Jordi Mallach", 44 | "email": "jordi@softcatala.org" 45 | }, 46 | "added": ["CHANGELOG"], 47 | "modified": ["app/controller/application.rb"], 48 | "removed": [] 49 | }, 50 | { 51 | "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 52 | "message": "fixed readme", 53 | "timestamp": "2012-01-03T23:36:29+02:00", 54 | "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 55 | "author": { 56 | "name": "GitLab dev user", 57 | "email": "gitlabdev@dv6700.(none)" 58 | }, 59 | "added": ["CHANGELOG"], 60 | "modified": ["app/controller/application.rb"], 61 | "removed": [] 62 | } 63 | ], 64 | "total_commits_count": 4 65 | } 66 | -------------------------------------------------------------------------------- /spec/fixtures/8x/tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "tag_push", 3 | "ref": "refs/tags/v1.0.0", 4 | "before": "0000000000000000000000000000000000000000", 5 | "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", 6 | "user_id": 1, 7 | "user_name": "John Smith", 8 | "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", 9 | "project_id": 1, 10 | "project":{ 11 | "name":"Example", 12 | "description":"", 13 | "web_url":"http://example.com/jsmith/example", 14 | "avatar_url":null, 15 | "git_ssh_url":"git@example.com:jsmith/example.git", 16 | "git_http_url":"http://example.com/jsmith/example.git", 17 | "namespace":"Jsmith", 18 | "visibility_level":0, 19 | "path_with_namespace":"jsmith/example", 20 | "default_branch":"master", 21 | "homepage":"http://example.com/jsmith/example", 22 | "url":"git@example.com:jsmith/example.git", 23 | "ssh_url":"git@example.com:jsmith/example.git", 24 | "http_url":"http://example.com/jsmith/example.git" 25 | }, 26 | "repository":{ 27 | "name": "example", 28 | "url": "ssh://git@example.com/jsmith/example.git", 29 | "description": "", 30 | "homepage": "http://example.com/jsmith/example", 31 | "git_http_url":"http://example.com/jsmith/example.git", 32 | "git_ssh_url":"git@example.com:jsmith/example.git", 33 | "visibility_level":0 34 | }, 35 | "commits": [], 36 | "total_commits_count": 0 37 | } 38 | -------------------------------------------------------------------------------- /spec/fixtures/default_params.json: -------------------------------------------------------------------------------- 1 | { 2 | "ref": "refs/heads/master", 3 | "repo_url": "http://localhost/mike/peronospora", 4 | "repo_name": "Peronospora", 5 | "repo_homepage": "http://localhost/mike/peronospora", 6 | "delete_branch_commit": false 7 | } 8 | -------------------------------------------------------------------------------- /spec/fixtures/default_payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", 3 | "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 4 | "ref": "refs/heads/master", 5 | "user_id": 4, 6 | "user_name": "John Smith", 7 | "project_id": 15, 8 | "repository": { 9 | "name": "Diaspora", 10 | "url": "git@example.com:diaspora/diaspora.git", 11 | "description": "", 12 | "homepage": "http://example.com/diaspora/diaspora", 13 | "private": true 14 | }, 15 | "commits": [ 16 | { 17 | "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", 18 | "message": "Update Catalan translation to e38cb41.", 19 | "timestamp": "2011-12-12T14:27:31+02:00", 20 | "url": "http://example.com/diaspora/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", 21 | "author": { 22 | "name": "John Smith", 23 | "email": "jsmith@example.com" 24 | } 25 | }, 26 | { 27 | "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 28 | "message": "fixed readme", 29 | "timestamp": "2012-01-03T23:36:29+02:00", 30 | "url": "http://example.com/diaspora/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 31 | "author": { 32 | "name": "John Smith the Second", 33 | "email": "jsmith2@example.com" 34 | } 35 | } 36 | ], 37 | "total_commits_count": 2 38 | } 39 | -------------------------------------------------------------------------------- /spec/fixtures/default_tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "ref": "refs/tags/v1.0.0", 3 | "before": "0000000000000000000000000000000000000000", 4 | "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", 5 | "user_id": 1, 6 | "user_name": "John Smith", 7 | "project_id": 1, 8 | "repository": { 9 | "name": "jsmith", 10 | "url": "ssh://git@example.com/jsmith/example.git", 11 | "description": "", 12 | "homepage": "http://example.com/jsmith/example", 13 | "git_http_url":"http://example.com/jsmith/example.git", 14 | "git_ssh_url":"git@example.com:jsmith/example.git", 15 | "visibility_level":0 16 | }, 17 | "commits": [], 18 | "total_commits_count": 0 19 | } 20 | -------------------------------------------------------------------------------- /spec/fixtures/descriptor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | false 5 | true 6 | primary 7 | true 8 | Alternate description 9 | user1, user2, user3 10 | 11 | 12 | 16 | 20 | 21 | 22 | 26 | 27 | -------------------------------------------------------------------------------- /spec/fixtures/merge_request_payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "merge_request", 3 | "object_attributes": { 4 | "id": 99, 5 | "target_branch": "master", 6 | "source_branch": "ms-viewport", 7 | "source_project_id": 14, 8 | "author_id": 51, 9 | "assignee_id": 6, 10 | "title": "MS-Viewport", 11 | "created_at": "2013-12-03T17:23:34Z", 12 | "updated_at": "2013-12-03T17:23:34Z", 13 | "st_commits": null, 14 | "st_diffs": null, 15 | "milestone_id": null, 16 | "state": "opened", 17 | "merge_status": "unchecked", 18 | "target_project_id": 14, 19 | "iid": 1, 20 | "description": "" 21 | } 22 | } -------------------------------------------------------------------------------- /spec/fixtures/new_merge_request_payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "merge_request", 3 | "user": { 4 | "name": "Administrator", 5 | "username": "root", 6 | "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" 7 | }, 8 | "object_attributes": { 9 | "id": 99, 10 | "target_branch": "master", 11 | "source_branch": "ms-viewport", 12 | "source_project_id": 14, 13 | "author_id": 51, 14 | "assignee_id": 6, 15 | "title": "MS-Viewport", 16 | "created_at": "2013-12-03T17:23:34Z", 17 | "updated_at": "2013-12-03T17:23:34Z", 18 | "st_commits": null, 19 | "st_diffs": null, 20 | "milestone_id": null, 21 | "state": "opened", 22 | "merge_status": "unchecked", 23 | "target_project_id": 14, 24 | "iid": 1, 25 | "description": "", 26 | "source": { 27 | "name": "awesome_project", 28 | "ssh_url": "git@example.com:awesome_space/awesome_project.git", 29 | "http_url": "http://example.com/awesome_space/awesome_project.git", 30 | "visibility_level": 20, 31 | "namespace": "awesome_space" 32 | }, 33 | "target": { 34 | "name": "awesome_project", 35 | "ssh_url": "git@example.com:awesome_space/awesome_project.git", 36 | "http_url": "http://example.com/awesome_space/awesome_project.git", 37 | "visibility_level": 20, 38 | "namespace": "awesome_space" 39 | }, 40 | "last_commit": { 41 | "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 42 | "message": "fixed readme", 43 | "timestamp": "2012-01-03T23:36:29+02:00", 44 | "url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", 45 | "author": { 46 | "name": "GitLab dev user", 47 | "email": "gitlabdev@dv6700.(none)" 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/8x/accept_merge_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "merge_request", 3 | "object_attributes": { 4 | "id": 0, 5 | "target_branch": "master", 6 | "source_branch": "feature/branch", 7 | "source_project_id": 0, 8 | "author_id": 0, 9 | "title": "Feature/branch", 10 | "created_at": "2015-03-14 22:31:45 UTC", 11 | "updated_at": "2015-03-14 22:31:52 UTC", 12 | "milestone_id": null, 13 | "state": "merged", 14 | "merge_status": "can_be_merged", 15 | "target_project_id": 0, 16 | "iid": 0, 17 | "source": { 18 | "name": "%{reponame}", 19 | "web_url": "http://localhost/tmp/%{reponame}", 20 | "git_ssh_url": "localhost:%{repodir}", 21 | "git_http_url": "http://localhost/tmp/%{reponame}.git", 22 | "namespace": "tmp", 23 | "visibility_level": 20, 24 | "path_with_namespace": "tmp/%{reponame}", 25 | "default_branch": "master", 26 | "homepage": "http://localhost/tmp/%{reponame}", 27 | "url": "http://localhost/tmp/%{reponame}.git", 28 | "ssh_url": "localhost:%{repodir}", 29 | "http_url": "http://localhost/tmp/%{reponame}.git" 30 | }, 31 | "target": { 32 | "name": "%{reponame}", 33 | "web_url": "http://localhost/tmp/%{reponame}", 34 | "git_ssh_url": "localhost:%{repodir}", 35 | "git_http_url": "http://localhost/tmp/%{reponame}.git", 36 | "namespace": "tmp", 37 | "visibility_level": 20, 38 | "path_with_namespace": "tmp/%{reponame}", 39 | "default_branch": "master", 40 | "homepage": "http://localhost/tmp/%{reponame}", 41 | "url": "http://localhost/tmp/%{reponame}.git", 42 | "ssh_url": "localhost:%{repodir}", 43 | "http_url": "http://localhost/tmp/%{reponame}.git" 44 | }, 45 | "last_commit": { 46 | "id": "ba46b858929aec55a84a9cb044e988d5d347b8de", 47 | "message": "Commit on branch\n", 48 | "timestamp": "2015-03-14T23:27:09+01:00", 49 | "url": "https://localhost/tmp/%{reponame}/commit/ba46b858929aec55a84a9cb044e988d5d347b8de", 50 | "author": { 51 | "name": "root", 52 | "email": "root@localhost" 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/8x/branch_creation.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "push", 3 | "before": "0000000000000000000000000000000000000000", 4 | "after": "80a89e1156d5d7e9471c245ccaeafb7bcb49c0a5", 5 | "ref": "refs/heads/feature/branch", 6 | "user_id": 0, 7 | "user_name": "root", 8 | "project_id": 0, 9 | "project":{ 10 | "name":"%{reponame}", 11 | "description":"", 12 | "web_url":"http://localhost/tmp/%{reponame}", 13 | "avatar_url":null, 14 | "git_ssh_url":"localhost:%{repodir}", 15 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 16 | "namespace":"Mike", 17 | "visibility_level":0, 18 | "path_with_namespace":"tmp/%{reponame}", 19 | "default_branch":"master", 20 | "homepage":"http://localhost/tmp/%{reponame}", 21 | "url":"localhost:%{repodir}", 22 | "ssh_url":"localhost:%{repodir}", 23 | "http_url":"http://localhost/tmp/%{reponame}.git" 24 | }, 25 | "repository": { 26 | "name": "%{reponame}", 27 | "url": "localhost:%{repodir}", 28 | "description": "", 29 | "homepage": "http://localhost/tmp/%{reponame}", 30 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 31 | "git_ssh_url":"localhost:%{repodir}", 32 | "visibility_level":0 33 | }, 34 | "commits": [ 35 | 36 | ], 37 | "total_commits_count": 0 38 | } 39 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/8x/branch_deletion.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "push", 3 | "before": "ba46b858929aec55a84a9cb044e988d5d347b8de", 4 | "after": "0000000000000000000000000000000000000000", 5 | "ref": "refs/heads/feature/branch", 6 | "user_id": 0, 7 | "user_name": "root", 8 | "project_id": 0, 9 | "project":{ 10 | "name":"%{reponame}", 11 | "description":"", 12 | "web_url":"http://localhost/tmp/%{reponame}", 13 | "avatar_url":null, 14 | "git_ssh_url":"localhost:%{repodir}", 15 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 16 | "namespace":"Mike", 17 | "visibility_level":0, 18 | "path_with_namespace":"tmp/%{reponame}", 19 | "default_branch":"master", 20 | "homepage":"http://localhost/tmp/%{reponame}", 21 | "url":"localhost:%{repodir}", 22 | "ssh_url":"localhost:%{repodir}", 23 | "http_url":"http://localhost/tmp/%{reponame}.git" 24 | }, 25 | "repository": { 26 | "name": "%{reponame}", 27 | "url": "localhost:%{repodir}", 28 | "description": "", 29 | "homepage": "http://localhost/tmp/%{reponame}", 30 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 31 | "git_ssh_url":"localhost:%{repodir}", 32 | "visibility_level":0 33 | }, 34 | "commits": [ 35 | 36 | ], 37 | "total_commits_count": 0 38 | } 39 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/8x/branch_push.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "push", 3 | "before": "80a89e1156d5d7e9471c245ccaeafb7bcb49c0a5", 4 | "after": "ba46b858929aec55a84a9cb044e988d5d347b8de", 5 | "ref": "refs/heads/feature/branch", 6 | "user_id": 0, 7 | "user_name": "root", 8 | "project_id": 0, 9 | "project":{ 10 | "name":"%{reponame}", 11 | "description":"", 12 | "web_url":"http://localhost/tmp/%{reponame}", 13 | "avatar_url":null, 14 | "git_ssh_url":"localhost:%{repodir}", 15 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 16 | "namespace":"Mike", 17 | "visibility_level":0, 18 | "path_with_namespace":"tmp/%{reponame}", 19 | "default_branch":"master", 20 | "homepage":"http://localhost/tmp/%{reponame}", 21 | "url":"localhost:%{repodir}", 22 | "ssh_url":"localhost:%{repodir}", 23 | "http_url":"http://localhost/tmp/%{reponame}.git" 24 | }, 25 | "repository": { 26 | "name": "%{reponame}", 27 | "url": "localhost:%{repodir}", 28 | "description": "", 29 | "homepage": "http://localhost/tmp/%{reponame}", 30 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 31 | "git_ssh_url":"localhost:%{repodir}", 32 | "visibility_level":0 33 | }, 34 | "commits": [ 35 | { 36 | "id": "ba46b858929aec55a84a9cb044e988d5d347b8de", 37 | "message": "Commit on branch\n", 38 | "timestamp": "2015-03-14T23:27:09+01:00", 39 | "url": "http://localhost/tmp/%{reponame}/commit/ba46b858929aec55a84a9cb044e988d5d347b8de", 40 | "author": { 41 | "name": "root", 42 | "email": "root@localhost" 43 | } 44 | } 45 | ], 46 | "total_commits_count": 1 47 | } 48 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/8x/first_push.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "push", 3 | "before": "5c87f8a2e42643936c5a274cb348b50445065e52", 4 | "after": "e3719eaab95642a63e90da0b9b23de0c9d384785", 5 | "ref": "refs/heads/master", 6 | "user_id": 0, 7 | "user_name": "root", 8 | "project_id": 0, 9 | "project":{ 10 | "name":"%{reponame}", 11 | "description":"", 12 | "web_url":"http://localhost/tmp/%{reponame}", 13 | "avatar_url":null, 14 | "git_ssh_url":"localhost:%{repodir}", 15 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 16 | "namespace":"Mike", 17 | "visibility_level":0, 18 | "path_with_namespace":"tmp/%{reponame}", 19 | "default_branch":"master", 20 | "homepage":"http://localhost/tmp/%{reponame}", 21 | "url":"localhost:%{repodir}", 22 | "ssh_url":"localhost:%{repodir}", 23 | "http_url":"http://localhost/tmp/%{reponame}.git" 24 | }, 25 | "repository": { 26 | "name": "%{reponame}", 27 | "url": "localhost:%{repodir}", 28 | "description": "", 29 | "homepage": "https://localhost/tmp/%{reponame}" 30 | }, 31 | "commits": [ 32 | { 33 | "id": "c1a1e6918fdbf9fe49ad70060508abcc88b876d4", 34 | "message": "Second commit\n", 35 | "timestamp": "2015-03-14T23:21:18+01:00", 36 | "url": "https://localhost/tmp/%{reponame}/commit/c1a1e6918fdbf9fe49ad70060508abcc88b876d4", 37 | "author": { 38 | "name": "root", 39 | "email": "root@localhost" 40 | } 41 | }, 42 | { 43 | "id": "e3719eaab95642a63e90da0b9b23de0c9d384785", 44 | "message": "Third commit\n", 45 | "timestamp": "2015-03-14T23:23:15+01:00", 46 | "url": "https://localhost/tmp/%{reponame}/commit/e3719eaab95642a63e90da0b9b23de0c9d384785", 47 | "author": { 48 | "name": "root", 49 | "email": "root@localhost" 50 | } 51 | } 52 | ], 53 | "total_commits_count": 2 54 | } 55 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/8x/master_push.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "push", 3 | "before": "e3719eaab95642a63e90da0b9b23de0c9d384785", 4 | "after": "6957dc21ae95f0c70931517841a9eb461f94548c", 5 | "ref": "refs/heads/master", 6 | "user_id": 0, 7 | "user_name": "root", 8 | "project_id": 0, 9 | "project":{ 10 | "name":"%{reponame}", 11 | "description":"", 12 | "web_url":"http://localhost/tmp/%{reponame}", 13 | "avatar_url":null, 14 | "git_ssh_url":"localhost:%{repodir}", 15 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 16 | "namespace":"Tmp", 17 | "visibility_level":0, 18 | "path_with_namespace":"tmp/%{reponame}", 19 | "default_branch":"master", 20 | "homepage":"http://localhost/tmp/%{reponame}", 21 | "url":"localhost:%{repodir}", 22 | "ssh_url":"localhost:%{repodir}", 23 | "http_url":"http://localhost/tmp/%{reponame}.git" 24 | }, 25 | "repository": { 26 | "name": "%{reponame}", 27 | "url": "localhost:%{repodir}", 28 | "description": "", 29 | "homepage": "https://localhost/tmp/%{reponame}" 30 | }, 31 | "commits": [ 32 | { 33 | "id": "6957dc21ae95f0c70931517841a9eb461f94548c", 34 | "message": "Fourth commit\n", 35 | "timestamp": "2015-03-14T23:29:47+01:00", 36 | "url": "https://localhost/tmp/%{reponame}/commit/6957dc21ae95f0c70931517841a9eb461f94548c", 37 | "author": { 38 | "name": "root", 39 | "email": "root@localhost" 40 | } 41 | } 42 | ], 43 | "total_commits_count": 1 44 | } 45 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/8x/merge_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "merge_request", 3 | "object_attributes": { 4 | "id": 0, 5 | "target_branch": "master", 6 | "source_branch": "feature/branch", 7 | "source_project_id": 0, 8 | "author_id": 0, 9 | "assignee_id": null, 10 | "title": "Feature/branch", 11 | "created_at": "2015-03-14 22:31:45 UTC", 12 | "updated_at": "2015-03-14 22:31:45 UTC", 13 | "milestone_id": null, 14 | "state": "opened", 15 | "merge_status": "unchecked", 16 | "target_project_id": 0, 17 | "iid": 0, 18 | "source": { 19 | "name": "%{reponame}", 20 | "web_url": "http://localhost/tmp/%{reponame}", 21 | "avatar_url": null, 22 | "git_ssh_url": "localhost:%{repodir}", 23 | "git_http_url": "http://localhost/tmp/%{reponame}.git", 24 | "namespace": "tmp", 25 | "visibility_level": 20, 26 | "path_with_namespace": "tmp/%{reponame}", 27 | "default_branch": "master", 28 | "homepage": "http://localhost/tmp/%{reponame}", 29 | "url": "http://localhost/tmp/%{reponame}.git", 30 | "ssh_url": "localhost:%{repodir}", 31 | "http_url": "http://localhost/tmp/%{reponame}.git" 32 | }, 33 | "target": { 34 | "name": "%{reponame}", 35 | "web_url": "http://localhost/tmp/%{reponame}", 36 | "avatar_url": null, 37 | "git_ssh_url": "localhost:%{repodir}", 38 | "git_http_url": "http://localhost/tmp/%{reponame}.git", 39 | "namespace": "tmp", 40 | "visibility_level": 20, 41 | "path_with_namespace": "tmp/%{reponame}", 42 | "default_branch": "master", 43 | "homepage": "http://localhost/tmp/%{reponame}", 44 | "url": "http://localhost/tmp/%{reponame}.git", 45 | "ssh_url": "localhost:%{repodir}", 46 | "http_url": "http://localhost/tmp/%{reponame}.git" 47 | }, 48 | "last_commit": { 49 | "id": "ba46b858929aec55a84a9cb044e988d5d347b8de", 50 | "message": "Commit on branch\n", 51 | "timestamp": "2015-03-14T23:27:09+01:00", 52 | "url": "https://localhost/tmp/%{reponame}/commit/ba46b858929aec55a84a9cb044e988d5d347b8de", 53 | "author": { 54 | "name": "root", 55 | "email": "root@localhost" 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/8x/tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "tag_push", 3 | "ref": "refs/tags/tag1", 4 | "before": "0000000000000000000000000000000000000000", 5 | "after": "c1a1e6918fdbf9fe49ad70060508abcc88b876d4", 6 | "user_id": 0, 7 | "user_name": "root", 8 | "project_id": 0, 9 | "project":{ 10 | "name":"%{reponame}", 11 | "description":"", 12 | "web_url":"http://localhost/tmp/%{reponame}", 13 | "avatar_url":null, 14 | "git_ssh_url":"localhost:%{repodir}", 15 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 16 | "namespace":"Tmp", 17 | "visibility_level":0, 18 | "path_with_namespace":"tmp/%{reponame}", 19 | "default_branch":"master", 20 | "homepage":"http://localhost/tmp/%{reponame}", 21 | "url":"localhost:%{repodir}", 22 | "ssh_url":"localhost:%{repodir}", 23 | "http_url":"http://localhost/tmp/%{reponame}.git" 24 | }, 25 | "repository": { 26 | "name": "%{reponame}", 27 | "url": "localhost:%{repodir}", 28 | "description": "", 29 | "homepage": "https://localhost/tmp/%{reponame}", 30 | "git_http_url":"http://localhost/tmp/%{reponame}.git", 31 | "git_ssh_url":"localhost:%{repodir}", 32 | "visibility_level":0 33 | }, 34 | "commits": [], 35 | "total_commits_count": 0 36 | } 37 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/accept_merge_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "merge_request", 3 | "object_attributes": { 4 | "id": 0, 5 | "target_branch": "master", 6 | "source_branch": "feature/branch", 7 | "source_project_id": 0, 8 | "author_id": 0, 9 | "assignee_id": null, 10 | "title": "Feature/branch", 11 | "created_at": "2015-03-14 22:31:45 UTC", 12 | "updated_at": "2015-03-14 22:31:52 UTC", 13 | "milestone_id": null, 14 | "state": "merged", 15 | "merge_status": "can_be_merged", 16 | "target_project_id": 0, 17 | "iid": 0, 18 | "description": "", 19 | "position": 0, 20 | "source": { 21 | "name": "%{reponame}", 22 | "ssh_url": "localhost:%{repodir}", 23 | "http_url": "https://localhost/tmp/%{reponame}.git", 24 | "namespace": "tmp", 25 | "visibility_level": 20 26 | }, 27 | "target": { 28 | "name": "%{reponame}", 29 | "ssh_url": "localhost:%{repodir}", 30 | "http_url": "https://localhost/tmp/%{reponame}.git", 31 | "namespace": "tmp", 32 | "visibility_level": 20 33 | }, 34 | "last_commit": { 35 | "id": "ba46b858929aec55a84a9cb044e988d5d347b8de", 36 | "message": "Commit on branch\n", 37 | "timestamp": "2015-03-14T23:27:09+01:00", 38 | "url": "https://localhost/tmp/%{reponame}/commit/ba46b858929aec55a84a9cb044e988d5d347b8de", 39 | "author": { 40 | "name": "root", 41 | "email": "root@localhost" 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/branch_creation.json: -------------------------------------------------------------------------------- 1 | { 2 | "before": "0000000000000000000000000000000000000000", 3 | "after": "80a89e1156d5d7e9471c245ccaeafb7bcb49c0a5", 4 | "ref": "refs/heads/feature/branch", 5 | "user_id": 0, 6 | "user_name": "root", 7 | "project_id": 0, 8 | "repository": { 9 | "name": "%{reponame}", 10 | "url": "localhost:%{repodir}", 11 | "description": "", 12 | "homepage": "https://localhost/tmp/%{reponame}" 13 | }, 14 | "commits": [ 15 | 16 | ], 17 | "total_commits_count": 0 18 | } 19 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/branch_deletion.json: -------------------------------------------------------------------------------- 1 | { 2 | "before": "ba46b858929aec55a84a9cb044e988d5d347b8de", 3 | "after": "0000000000000000000000000000000000000000", 4 | "ref": "refs/heads/feature/branch", 5 | "user_id": 0, 6 | "user_name": "root", 7 | "project_id": 0, 8 | "repository": { 9 | "name": "%{reponame}", 10 | "url": "localhost:%{repodir}", 11 | "description": "", 12 | "homepage": "https://localhost/tmp/%{reponame}" 13 | }, 14 | "commits": [ 15 | 16 | ], 17 | "total_commits_count": 0 18 | } 19 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/branch_push.json: -------------------------------------------------------------------------------- 1 | { 2 | "before": "80a89e1156d5d7e9471c245ccaeafb7bcb49c0a5", 3 | "after": "ba46b858929aec55a84a9cb044e988d5d347b8de", 4 | "ref": "refs/heads/feature/branch", 5 | "user_id": 0, 6 | "user_name": "root", 7 | "project_id": 0, 8 | "repository": { 9 | "name": "%{reponame}", 10 | "url": "localhost:%{repodir}", 11 | "description": "", 12 | "homepage": "https://localhost/tmp/%{reponame}" 13 | }, 14 | "commits": [ 15 | { 16 | "id": "ba46b858929aec55a84a9cb044e988d5d347b8de", 17 | "message": "Commit on branch\n", 18 | "timestamp": "2015-03-14T23:27:09+01:00", 19 | "url": "https://localhost/tmp/%{reponame}/commit/ba46b858929aec55a84a9cb044e988d5d347b8de", 20 | "author": { 21 | "name": "root", 22 | "email": "root@localhost" 23 | } 24 | } 25 | ], 26 | "total_commits_count": 1 27 | } 28 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/first_push.json: -------------------------------------------------------------------------------- 1 | { 2 | "before": "5c87f8a2e42643936c5a274cb348b50445065e52", 3 | "after": "e3719eaab95642a63e90da0b9b23de0c9d384785", 4 | "ref": "refs/heads/master", 5 | "user_id": 0, 6 | "user_name": "root", 7 | "project_id": 0, 8 | "repository": { 9 | "name": "%{reponame}", 10 | "url": "localhost:%{repodir}", 11 | "description": "", 12 | "homepage": "https://localhost/tmp/%{reponame}" 13 | }, 14 | "commits": [ 15 | { 16 | "id": "c1a1e6918fdbf9fe49ad70060508abcc88b876d4", 17 | "message": "Second commit\n", 18 | "timestamp": "2015-03-14T23:21:18+01:00", 19 | "url": "https://localhost/tmp/%{reponame}/commit/c1a1e6918fdbf9fe49ad70060508abcc88b876d4", 20 | "author": { 21 | "name": "root", 22 | "email": "root@localhost" 23 | } 24 | }, 25 | { 26 | "id": "e3719eaab95642a63e90da0b9b23de0c9d384785", 27 | "message": "Third commit\n", 28 | "timestamp": "2015-03-14T23:23:15+01:00", 29 | "url": "https://localhost/tmp/%{reponame}/commit/e3719eaab95642a63e90da0b9b23de0c9d384785", 30 | "author": { 31 | "name": "root", 32 | "email": "root@localhost" 33 | } 34 | } 35 | ], 36 | "total_commits_count": 2 37 | } 38 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/legacy/accept_merge_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "merge_request", 3 | "object_attributes": { 4 | "id": 0, 5 | "target_branch": "master", 6 | "source_branch": "feature/branch", 7 | "source_project_id": 0, 8 | "author_id": 0, 9 | "assignee_id": null, 10 | "title": "Feature/branch", 11 | "created_at": "2015-03-14 22:31:45 UTC", 12 | "updated_at": "2015-03-14 22:31:52 UTC", 13 | "milestone_id": null, 14 | "state": "merged", 15 | "merge_status": "can_be_merged", 16 | "target_project_id": 0, 17 | "iid": 0, 18 | "description": "", 19 | "position": 0 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/legacy/merge_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "merge_request", 3 | "object_attributes": { 4 | "id": 0, 5 | "target_branch": "master", 6 | "source_branch": "feature/branch", 7 | "source_project_id": 0, 8 | "author_id": 0, 9 | "assignee_id": null, 10 | "title": "Feature/branch", 11 | "created_at": "2015-03-14 22:31:45 UTC", 12 | "updated_at": "2015-03-14 22:31:45 UTC", 13 | "milestone_id": null, 14 | "state": "opened", 15 | "merge_status": "unchecked", 16 | "target_project_id": 0, 17 | "iid": 0, 18 | "description": "", 19 | "position": 0 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/master_push.json: -------------------------------------------------------------------------------- 1 | { 2 | "before": "e3719eaab95642a63e90da0b9b23de0c9d384785", 3 | "after": "6957dc21ae95f0c70931517841a9eb461f94548c", 4 | "ref": "refs/heads/master", 5 | "user_id": 0, 6 | "user_name": "root", 7 | "project_id": 0, 8 | "repository": { 9 | "name": "%{reponame}", 10 | "url": "localhost:%{repodir}", 11 | "description": "", 12 | "homepage": "https://localhost/tmp/%{reponame}" 13 | }, 14 | "commits": [ 15 | { 16 | "id": "6957dc21ae95f0c70931517841a9eb461f94548c", 17 | "message": "Fourth commit\n", 18 | "timestamp": "2015-03-14T23:29:47+01:00", 19 | "url": "https://localhost/tmp/%{reponame}/commit/6957dc21ae95f0c70931517841a9eb461f94548c", 20 | "author": { 21 | "name": "root", 22 | "email": "root@localhost" 23 | } 24 | } 25 | ], 26 | "total_commits_count": 1 27 | } 28 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/merge_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "object_kind": "merge_request", 3 | "object_attributes": { 4 | "id": 0, 5 | "target_branch": "master", 6 | "source_branch": "feature/branch", 7 | "source_project_id": 0, 8 | "author_id": 0, 9 | "assignee_id": null, 10 | "title": "Feature/branch", 11 | "created_at": "2015-03-14 22:31:45 UTC", 12 | "updated_at": "2015-03-14 22:31:45 UTC", 13 | "milestone_id": null, 14 | "state": "opened", 15 | "merge_status": "unchecked", 16 | "target_project_id": 0, 17 | "iid": 0, 18 | "description": "", 19 | "position": 0, 20 | "source": { 21 | "name": "%{reponame}", 22 | "ssh_url": "localhost:%{repodir}", 23 | "http_url": "https://localhost/tmp/%{reponame}.git", 24 | "namespace": "tmp", 25 | "visibility_level": 20 26 | }, 27 | "target": { 28 | "name": "%{reponame}", 29 | "ssh_url": "localhost:%{repodir}", 30 | "http_url": "https://localhost/tmp/%{reponame}.git", 31 | "namespace": "tmp", 32 | "visibility_level": 20 33 | }, 34 | "last_commit": { 35 | "id": "ba46b858929aec55a84a9cb044e988d5d347b8de", 36 | "message": "Commit on branch\n", 37 | "timestamp": "2015-03-14T23:27:09+01:00", 38 | "url": "https://localhost/tmp/%{reponame}/commit/ba46b858929aec55a84a9cb044e988d5d347b8de", 39 | "author": { 40 | "name": "root", 41 | "email": "root@localhost" 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /spec/fixtures/payloads/tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "ref": "refs/tags/tag1", 3 | "before": "0000000000000000000000000000000000000000", 4 | "after": "c1a1e6918fdbf9fe49ad70060508abcc88b876d4", 5 | "user_id": 0, 6 | "user_name": "root", 7 | "project_id": 0, 8 | "repository": { 9 | "name": "%{reponame}", 10 | "url": "localhost:%{repodir}", 11 | "description": "", 12 | "homepage": "https://localhost/tmp/%{reponame}" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/05/627d3548e3a64b465c99fb1b8bc8765c506759: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/05/627d3548e3a64b465c99fb1b8bc8765c506759 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/32/e499748b811a2c9044bf9b6894072281120932: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/32/e499748b811a2c9044bf9b6894072281120932 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/41/a6bb30b745172af19f158850a1633c4d259cff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/41/a6bb30b745172af19f158850a1633c4d259cff -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/47/1c1119345bef3ee97841cbca12ee9905ba8bed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/47/1c1119345bef3ee97841cbca12ee9905ba8bed -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/48/9d14020729682229b17e5bb4a571632b1c4c99: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/48/9d14020729682229b17e5bb4a571632b1c4c99 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/4b/cd06209455a89ae3dd2f48e894c5d993ee3914: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/4b/cd06209455a89ae3dd2f48e894c5d993ee3914 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/51/c9a7db79925f9bc9e66669e1ed16158541fa1b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/51/c9a7db79925f9bc9e66669e1ed16158541fa1b -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/5c/87f8a2e42643936c5a274cb348b50445065e52: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/5c/87f8a2e42643936c5a274cb348b50445065e52 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/69/57dc21ae95f0c70931517841a9eb461f94548c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/69/57dc21ae95f0c70931517841a9eb461f94548c -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/7b/16a5cba8b4bf090a28c99ef1cbe55ca3f8c7fb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/7b/16a5cba8b4bf090a28c99ef1cbe55ca3f8c7fb -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/80/a89e1156d5d7e9471c245ccaeafb7bcb49c0a5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/80/a89e1156d5d7e9471c245ccaeafb7bcb49c0a5 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/86/8b6adf6b5910cef199da9905c6026f3ee044c1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/86/8b6adf6b5910cef199da9905c6026f3ee044c1 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/8e/8384098ff589ee941e142134bec284a329c061: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/8e/8384098ff589ee941e142134bec284a329c061 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/a0/41e443aedc4df03d49f2db62bb278da1d0874c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/a0/41e443aedc4df03d49f2db62bb278da1d0874c -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/ba/46b858929aec55a84a9cb044e988d5d347b8de: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/ba/46b858929aec55a84a9cb044e988d5d347b8de -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/c1/a1e6918fdbf9fe49ad70060508abcc88b876d4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/c1/a1e6918fdbf9fe49ad70060508abcc88b876d4 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/d4/36ccdac1e7e0dd31486a5355dd439eaa4b5cf8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/d4/36ccdac1e7e0dd31486a5355dd439eaa4b5cf8 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/d9/0c6ad729f14401dfdaebfa95eee2b105ae5bac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/d9/0c6ad729f14401dfdaebfa95eee2b105ae5bac -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/objects/e3/719eaab95642a63e90da0b9b23de0c9d384785: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/gitlab-hook-plugin/d52d8b4c970aeb4215cc0ca537cbdfb2a5e98f35/spec/fixtures/testrepo.git/objects/e3/719eaab95642a63e90da0b9b23de0c9d384785 -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/refs/heads/feature/branch: -------------------------------------------------------------------------------- 1 | 80a89e1156d5d7e9471c245ccaeafb7bcb49c0a5 2 | -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/refs/heads/master: -------------------------------------------------------------------------------- 1 | e3719eaab95642a63e90da0b9b23de0c9d384785 2 | -------------------------------------------------------------------------------- /spec/fixtures/testrepo.git/refs/tags/tag1: -------------------------------------------------------------------------------- 1 | 7b16a5cba8b4bf090a28c99ef1cbe55ca3f8c7fb 2 | -------------------------------------------------------------------------------- /spec/models/root_action_descriptor_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe GitlabWebHookRootActionDescriptor do 4 | context 'with ignore users' do 5 | it 'defines it' do 6 | expect(subject).to respond_to(:ignore_users) 7 | end 8 | 9 | it 'has default' do 10 | expect(subject.ignore_users).to eq('') 11 | end 12 | end 13 | 14 | context 'whether automatic project creation is enabled' do 15 | it 'defines it' do 16 | expect(subject).to respond_to(:automatic_project_creation?) 17 | end 18 | 19 | it 'has default' do 20 | expect(subject.automatic_project_creation?).to be(false) 21 | end 22 | end 23 | 24 | context 'with master branch identificator' do 25 | it 'defines it' do 26 | expect(subject).to respond_to(:master_branch) 27 | end 28 | 29 | it 'has default' do 30 | expect(subject.master_branch).to eq('master') 31 | end 32 | end 33 | 34 | context 'whether to use master project name' do 35 | it 'defines it' do 36 | expect(subject).to respond_to(:use_master_project_name?) 37 | end 38 | 39 | it 'has default' do 40 | expect(subject.use_master_project_name?).to eq(false) 41 | end 42 | end 43 | 44 | context 'with automatically created project description' do 45 | it 'defines it' do 46 | expect(subject).to respond_to(:description) 47 | end 48 | 49 | it 'has default' do 50 | expect(subject.description).to eq('Automatically created by Gitlab Web Hook plugin') 51 | end 52 | end 53 | 54 | context '#template_fallback' do 55 | it 'is defined' do 56 | expect(subject).to respond_to(:template_fallback) 57 | end 58 | 59 | it 'evaluates to false by default' do 60 | expect(subject.template_fallback).to be nil 61 | end 62 | end 63 | 64 | context '#templated_groups' do 65 | it 'is defined' do 66 | expect(subject).to respond_to(:templated_groups) 67 | end 68 | 69 | it 'has empty default' do 70 | expect(subject.templated_groups).to eq({}) 71 | end 72 | end 73 | 74 | context '#templated_jobs' do 75 | it 'is defined' do 76 | expect(subject).to respond_to(:templated_jobs) 77 | end 78 | 79 | it 'has empty default' do 80 | expect(subject.templated_jobs).to eq({}) 81 | end 82 | end 83 | 84 | context 'disk descriptor' do 85 | 86 | let(:xml_file) { double(exists: true, canonicalPath: 'spec/fixtures/descriptor.xml' ) } 87 | let(:config_file) { double('configFile', file: xml_file) } 88 | let(:subject) { GitlabWebHookRootActionDescriptor.new } 89 | 90 | context 'read' do 91 | 92 | before(:each) do 93 | expect(subject).to receive(:configFile).twice { config_file } 94 | subject.load 95 | end 96 | 97 | it '#automatic_project_creation?' do 98 | expect(subject.automatic_project_creation?).to be true 99 | end 100 | 101 | it '#master_branch' do 102 | expect(subject.master_branch).to eq 'primary' 103 | end 104 | 105 | it '#use_master_project_name?' do 106 | expect(subject.use_master_project_name?).to be true 107 | end 108 | 109 | it '#description' do 110 | expect(subject.description).to eq 'Alternate description' 111 | end 112 | 113 | it '#template_fallback' do 114 | expect(subject.template_fallback).to eq 'default_project' 115 | end 116 | 117 | it '#templated_groups' do 118 | expect(subject.templated_groups).to eq( { 'android' => 'gradle_project' } ) 119 | end 120 | 121 | it '#templated_jobs' do 122 | expect(subject.templated_jobs).to eq( { 'webapp-' => 'maven_project' , 'java-lib-' => 'artifactory_project' } ) 123 | end 124 | 125 | end 126 | 127 | context 'write' do 128 | 129 | let (:content) { File.read('spec/fixtures/descriptor.xml') } 130 | let (:outfile) { StringIO.new } 131 | 132 | it 'recovers disk content' do 133 | expect(subject).to receive(:configFile).and_return( config_file ).exactly(4).times 134 | subject.load 135 | expect(BulkChange).to receive(:contains) { false } 136 | expect(File).to receive(:open) { outfile } 137 | expect(SaveableListener).to receive(:fireOnChange) 138 | subject.save 139 | expect(outfile.string).to eq content 140 | end 141 | 142 | end 143 | 144 | end 145 | 146 | end 147 | -------------------------------------------------------------------------------- /spec/services/build_scm_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe BuildScm do 5 | include_context 'details' 6 | let(:remote_config) { double(UserRemoteConfig, getCredentialsId: 'id').as_null_object } 7 | let(:source_scm) { double(GitSCM, getScmName: 'git', getUserRemoteConfigs: [remote_config]) } 8 | 9 | context 'with up to date git plugin' do 10 | it 'builds up to date git scm' do 11 | expect(subject).to receive(:build_scm) 12 | subject.with(source_scm, details, false) 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/services/check_git_details_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe CheckGitDetails do 5 | include_context 'settings' 6 | 7 | let(:details) { double(RequestDetails, user_name: '') } 8 | 9 | context 'when validating' do 10 | it 'requires details' do 11 | expect { subject.with(nil) }.to raise_exception(ArgumentError) 12 | end 13 | end 14 | 15 | context 'with no username' do 16 | include_context 'details' 17 | it 'returns nil' do 18 | error = subject.with(details) 19 | expect(error).to be_nil 20 | end 21 | end 22 | 23 | context 'with allowed username' do 24 | include_context 'details' 25 | it 'returns nil' do 26 | allow(details).to receive(:user_name) { 'user 7' } 27 | error = subject.with(details) 28 | expect(error).to be_nil 29 | end 30 | end 31 | 32 | context 'with disallowed username' do 33 | include_context 'details' 34 | it 'returns an error message' do 35 | allow(details).to receive(:user_name) { 'user 2' } 36 | error = subject.with(details) 37 | expect(error).to match('Not processing request for a git action by') 38 | end 39 | end 40 | 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/services/flat_keys_hash_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe FlatKeysHash do 5 | include_context 'details' 6 | let (:subject) { payload.extend(FlatKeysHash) } 7 | 8 | it 'keeps reference to first level keys' do 9 | expect(subject.to_flat_keys['after']).to eq('da1560886d4f094c3e6c9ef40349f7d38b5d27d7') 10 | end 11 | 12 | it 'preserves plain value type' do 13 | expect(subject.to_flat_keys['user_id']).to eq(4) 14 | end 15 | 16 | it 'exposes nested keys' do 17 | expect(subject.to_flat_keys[%w(repository name).join(FlatKeysHash::FLATTENED_KEYS_DELIMITER)]).to eq('Diaspora') 18 | end 19 | 20 | it 'exposes root' do 21 | repository = subject.to_flat_keys['repository'] 22 | expect(repository.keys.count).to eq(7) 23 | expect(repository['url']).to eq('git@example.com:mike/diaspora.git') 24 | end 25 | 26 | it 'indexes arrays' do 27 | expect(subject.to_flat_keys[%w(commits 1 id).join(FlatKeysHash::FLATTENED_KEYS_DELIMITER)]).to eq('da1560886d4f094c3e6c9ef40349f7d38b5d27d7') 28 | end 29 | 30 | it 'exposes nested array element' do 31 | commit = subject.to_flat_keys[%w(commits 0).join(FlatKeysHash::FLATTENED_KEYS_DELIMITER)] 32 | expect(commit.keys.count).to eq(8) 33 | expect(commit['message']).to eq('Update Catalan translation to e38cb41.') 34 | end 35 | 36 | it 'supports indefinite levels' do 37 | expect(subject.to_flat_keys[%w(commits 0 author email).join(FlatKeysHash::FLATTENED_KEYS_DELIMITER)]).to eq('jordi@softcatala.org') 38 | end 39 | 40 | it 'exposes nested root' do 41 | author = subject.to_flat_keys[%w(commits 1 author).join(FlatKeysHash::FLATTENED_KEYS_DELIMITER)] 42 | expect(author.keys.count).to eq(2) 43 | expect(author['email']).to eq('gitlabdev@dv6700.(none)') 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/services/get_build_actions_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe GetBuildActions do 5 | let(:project) { double(Project, parametrized?: true) } 6 | let(:details) { double(RequestDetails) } 7 | 8 | context 'when project not parametrized' do 9 | it 'returns empty array' do 10 | allow(project).to receive(:parametrized?) { false } 11 | expect(subject.with(project, details)).to eq([]) 12 | end 13 | end 14 | 15 | context 'when building 7.x actions' do 16 | let(:parameters_values) { [] } 17 | before :each do 18 | allow_any_instance_of(GetParametersValues).to receive(:with).with(project, details) { parameters_values } 19 | expect(details).to receive(:kind) { 'webhook' } 20 | end 21 | 22 | it 'delegates parameter values build' do 23 | subject.with(project, details) 24 | end 25 | 26 | it 'returns parameters action' do 27 | expect(subject.with(project, details).java_kind_of?(ParametersAction)).to be 28 | end 29 | 30 | it 'parameters action contain parameters values' do 31 | expect(subject.with(project, details).getParameters()).to eq(parameters_values) 32 | end 33 | end 34 | 35 | context 'when building 8.x actions' do 36 | let(:parameters_values) { [] } 37 | before :each do 38 | allow_any_instance_of(GetParametersValues).to receive(:with).with(project, details) { parameters_values } 39 | expect(details).to receive(:kind) { 'push' } 40 | end 41 | 42 | it 'delegates parameter values build' do 43 | subject.with(project, details) 44 | end 45 | 46 | it 'returns parameters action' do 47 | expect(subject.with(project, details).java_kind_of?(ParametersAction)).to be 48 | end 49 | 50 | it 'parameters action contain parameters values' do 51 | expect(subject.with(project, details).getParameters()).to eq(parameters_values) 52 | end 53 | end 54 | 55 | context 'when building due to a merge request' do 56 | let(:parameters_values) { [] } 57 | before :each do 58 | allow_any_instance_of(GetParametersValues).to receive(:with_mr).with(project, details) { parameters_values } 59 | expect(details).to receive(:kind) { 'merge_request' } 60 | end 61 | 62 | it 'returns parameters action old' do 63 | expect(subject.with(project, details).java_kind_of?(ParametersAction)).to be 64 | end 65 | 66 | it 'parameters action contain parameters values' do 67 | expect(subject.with(project, details).getParameters()).to eq(parameters_values) 68 | end 69 | end 70 | 71 | context 'when validating' do 72 | it 'requires project' do 73 | expect { subject.with(nil, details) }.to raise_exception(ArgumentError) 74 | end 75 | 76 | it 'requires details' do 77 | expect { subject.with(project, nil) }.to raise_exception(ArgumentError) 78 | end 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /spec/services/get_build_cause_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # NOTE : Since 1.565, implementation of RemoteCause.getShortDescription 4 | # calls Jenkins.getInstance().getMarkupFormatter(), with no simple mockup, so 5 | # we test for addr and note accessors 6 | 7 | module GitlabWebHook 8 | describe GetBuildCause do 9 | let(:repository_uri) { RepositoryUri.new('git@example.com:diaspora/diaspora.git') } 10 | let(:details) { double(RequestDetails, payload: nil, repository_uri: repository_uri) } 11 | let(:cause) { subject.with(details) } 12 | 13 | context 'with repository details' do 14 | it 'contains repository host' do 15 | expect(cause.addr).to match('example.com') 16 | end 17 | end 18 | 19 | context 'with no payload' do 20 | it 'contains default message' do 21 | expect(cause.note).to match('no payload available') 22 | end 23 | end 24 | 25 | context 'with payload' do 26 | include_context 'details' 27 | it 'contains payload details' do 28 | cause = subject.with(details) 29 | expect(cause.note).not_to match('no payload available') 30 | expect(cause.note).to match('branch master with 2 commits') 31 | end 32 | include_context 'mr_details' 33 | it 'contains merge request payload details' do 34 | cause = subject.with(mr_details) 35 | expect(cause.note).not_to match('no payload available') 36 | expect(cause.note).to match('triggered by merge request') 37 | end 38 | end 39 | 40 | context 'when validating' do 41 | it 'requires details' do 42 | expect { subject.with(nil) }.to raise_exception(ArgumentError) 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/services/get_jenkins_projects_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | java_import Java.hudson.matrix.MatrixProject 4 | 5 | module GitlabWebHook 6 | describe GetJenkinsProjects do 7 | include_context 'settings' 8 | 9 | before(:each) { allow(subject).to receive(:log_matched) {} } 10 | 11 | context 'when fetching projects by request details' do 12 | include_context 'projects' 13 | include_context 'details' 14 | 15 | before(:each) { allow(subject).to receive(:all) { all_projects } } 16 | 17 | it 'finds projects matching details' do 18 | projects = subject.matching_uri(details) 19 | expect(projects.size).to eq(2) 20 | expect(projects[0]).to eq(not_matching_project) 21 | expect(projects[1]).to eq(matching_project) 22 | end 23 | 24 | it 'finds projects matching details exactly' do 25 | projects = subject.matching_uri(details).select do |project| 26 | project.matches?(details, details.branch, true) 27 | end 28 | expect(projects.size).to eq(1) 29 | expect(projects[0]).to eq(matching_project) 30 | end 31 | end 32 | 33 | context 'when fetching master project matching request details' do 34 | include_context 'projects' 35 | include_context 'details' 36 | 37 | before(:each) { allow(subject).to receive(:all) { all_projects } } 38 | 39 | it 'finds project matching details and master branch' do 40 | expect(subject.master(details)).to eq(matching_project) 41 | end 42 | 43 | it 'finds first projects matching details and any non master branch' do 44 | expect(matching_project).to receive(:matches?).with(anything, anything, true).and_return(false) 45 | expect(subject.master(details)).to eq(not_matching_project) 46 | end 47 | end 48 | 49 | context 'when fetching projects by name' do 50 | include_context 'projects' 51 | 52 | before(:each) { allow(subject).to receive(:all) { all_projects } } 53 | 54 | it 'finds project by name' do 55 | projects = subject.named('matching project') 56 | expect(projects.size).to eq(1) 57 | expect(projects[0].name).to eq('matching project') 58 | end 59 | 60 | it 'does not find project by name' do 61 | projects = subject.named('undefined project') 62 | expect(projects.size).to eq(0) 63 | end 64 | end 65 | 66 | context 'when fetching all projects from jenkins instance' do 67 | let(:scm) { double(GitSCM) } 68 | let(:jenkins_instance) { double(Java.jenkins.model.Jenkins, getAllItems: []) } 69 | 70 | before(:each) do 71 | allow(scm).to receive(:java_kind_of?).with(GitSCM) { true } 72 | allow(scm).to receive(:java_kind_of?).with(MultiSCM) { false } 73 | allow(Java.jenkins.model.Jenkins).to receive(:instance) { jenkins_instance } 74 | end 75 | 76 | it 'returns custom projects' do 77 | allow(jenkins_instance).to receive(:getAllItems) {[ 78 | double(Java.hudson.model.AbstractProject, scm: scm), 79 | double(Java.hudson.model.AbstractProject, scm: scm) 80 | ]} 81 | 82 | projects = subject.send(:all) 83 | expect(projects.size).to eq(2) 84 | projects.each { |project| expect(project).to be_kind_of(Project) } 85 | end 86 | 87 | it 'skips matrix configuration project axis' do 88 | maven_project = double(Java.hudson.model.AbstractProject, scm: scm) 89 | allow(maven_project).to receive(:java_kind_of?).with(MatrixConfiguration) { false } 90 | 91 | matrix_project = double(Java.hudson.matrix.MatrixProject, scm: scm) 92 | allow(matrix_project).to receive(:java_kind_of?).with(MatrixConfiguration) { false } 93 | 94 | matrix_configuration = double(Java.hudson.matrix.MatrixConfiguration, scm: scm) 95 | allow(matrix_configuration).to receive(:java_kind_of?).with(MatrixConfiguration) { true } 96 | 97 | allow(jenkins_instance).to receive(:getAllItems) {[ 98 | maven_project, 99 | matrix_project, 100 | matrix_configuration, 101 | matrix_configuration 102 | ]} 103 | 104 | projects = subject.send(:all) 105 | expect(projects.size).to eq(2) 106 | projects.each { |project| expect(project.jenkins_project.java_kind_of?(Java.hudson.matrix.MatrixConfiguration)).not_to be } 107 | end 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /spec/services/get_parameters_values_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe GetParametersValues do 5 | 6 | def build_parameter(name, default = nil) 7 | double(name: name, getDefaultParameterValue: default ? StringParameterValue.new(name, default) : nil).tap do |parameter| 8 | allow(parameter).to receive(:java_kind_of?).with(StringParameterDefinition) { true } 9 | end 10 | end 11 | 12 | let(:project) { double(Project, get_branch_name_parameter: nil) } 13 | 14 | context 'with push data in payload' do 15 | include_context 'details' 16 | 17 | context 'with parameters present in payload data' do 18 | it 'recognizes root keys' do 19 | allow(project).to receive(:get_default_parameters) { [build_parameter('before')] } 20 | expect(subject.with(project, details)[0].value).to eq('95790bf891e76fee5e1747ab589903a6a1f80f22') 21 | end 22 | 23 | context 'with branch parameter' do 24 | 25 | include_context 'tag_details' 26 | 27 | before :each do 28 | allow(project).to receive(:get_branch_name_parameter) { branch_parameter } 29 | allow(project).to receive(:get_default_parameters) { [branch_parameter] } 30 | end 31 | 32 | context 'with paramter for branch' do 33 | let(:branch_parameter) { build_parameter('commit_branch_parameter', 'default_branch') } 34 | it 'replaces it under push event' do 35 | expect(subject.with(project, details)[0].value).to eq('master') 36 | end 37 | it 'uses default under tag event' do 38 | expect(subject.with(project, tag_details)[0].value).to eq('default_branch') 39 | end 40 | end 41 | 42 | context 'with parameter for tag' do 43 | let(:branch_parameter) { build_parameter('tagname', '-') } 44 | it 'uses default under push event' do 45 | expect(subject.with(project, details)[0].value).to eq('-') 46 | end 47 | it 'replaces it under tag event' do 48 | expect(subject.with(project, tag_details)[0].value).to eq('v1.0.0') 49 | end 50 | end 51 | end 52 | 53 | it 'recognizes nested keys' do 54 | allow(project).to receive(:get_default_parameters) { [build_parameter('repository.url')] } 55 | expect(subject.with(project, details)[0].value).to eq('git@example.com:mike/diaspora.git') 56 | end 57 | 58 | it 'recognizes nested array elements' do 59 | allow(project).to receive(:get_default_parameters) { [build_parameter('commits.1.id')] } 60 | expect(subject.with(project, details)[0].value).to eq('da1560886d4f094c3e6c9ef40349f7d38b5d27d7') 61 | end 62 | 63 | it 'recognizes deep nested elements' do 64 | allow(project).to receive(:get_default_parameters) { [build_parameter('commits.0.author.email')] } 65 | expect(subject.with(project, details)[0].value).to eq('jordi@softcatala.org') 66 | end 67 | end 68 | 69 | context 'with branch parameter' do 70 | it 'replaces it with data from details' do 71 | branch_parameter = build_parameter('commit_branch_parameter', 'default_branch') 72 | allow(project).to receive(:get_branch_name_parameter) { branch_parameter } 73 | allow(project).to receive(:get_default_parameters) { [branch_parameter] } 74 | allow(details).to receive(:branch) { 'commit_branch' } 75 | expect(subject.with(project, details)[0].value).to eq('commit_branch') 76 | end 77 | end 78 | 79 | context 'with parameters not in payload' do 80 | before(:each) { allow(project).to receive(:get_default_parameters) { [build_parameter('not_in_payload', 'default value')] } } 81 | 82 | it 'keeps them' do 83 | expect(subject.with(project, details)[0].name).to eq('not_in_payload') 84 | end 85 | 86 | it 'applies default value' do 87 | expect(subject.with(project, details)[0].value).to eq('default value') 88 | end 89 | end 90 | 91 | context 'with empty values' do 92 | it 'removes them' do 93 | allow(project).to receive(:get_default_parameters) { [build_parameter('not_in_payload')] } 94 | expect(subject.with(project, details).size).to eq(0) 95 | end 96 | end 97 | 98 | context 'with parameters in general' do 99 | it 'is case insensitive' do 100 | allow(project).to receive(:get_branch_name_parameter) { build_parameter('commit_branch_parameter') } 101 | allow(project).to receive(:get_default_parameters) { [ 102 | build_parameter('not_IN_payload', 'default value'), 103 | build_parameter('BEFORE'), 104 | build_parameter('TAGNAME', '*'), 105 | build_parameter('commit_BRANCH_parameter') 106 | ] } 107 | allow(details).to receive(:branch) { 'commit_branch' } 108 | expect(subject.with(project, details)[0].value).to eq('default value') 109 | expect(subject.with(project, details)[1].value).to eq('95790bf891e76fee5e1747ab589903a6a1f80f22') 110 | expect(subject.with(project, details)[2].value).to eq('*') 111 | expect(subject.with(project, details)[3].value).to eq('commit_branch') 112 | end 113 | 114 | it 'leaves non string parameters as is' do 115 | boolean_parameter = double(name: 'boolean', getDefaultParameterValue: Java.hudson.model.BooleanParameterValue.new('boolean', true)).tap do |parameter| 116 | allow(parameter).to receive(:java_kind_of?).with(StringParameterDefinition) { false } 117 | end 118 | allow(project).to receive(:get_default_parameters) { [boolean_parameter] } 119 | expect(subject.with(project, details)[0].value).to eq(true) 120 | end 121 | end 122 | 123 | context 'with a TAGNAME parameter' do 124 | it 'is case insensitive' do 125 | allow(project).to receive(:get_default_parameters) { [ 126 | build_parameter('TAGNAME', '*') 127 | ] } 128 | allow(details).to receive(:full_branch_reference) { 'refs/tags/v1.0.0' } 129 | expect(subject.with(project, details)[0].value).to eq('v1.0.0') 130 | end 131 | end 132 | 133 | context 'when validating' do 134 | it 'requires project' do 135 | expect { subject.with(nil, details) }.to raise_exception(ArgumentError) 136 | end 137 | 138 | it 'requires details' do 139 | expect { subject.with(project, nil) }.to raise_exception(ArgumentError) 140 | end 141 | end 142 | end 143 | 144 | context 'with merge request data in payload' do 145 | include_context 'mr_details' 146 | 147 | context 'when validating' do 148 | it 'requires project' do 149 | expect { subject.with_mr(nil, mr_details) }.to raise_exception(ArgumentError) 150 | end 151 | 152 | it 'requires details' do 153 | expect { subject.with_mr(project, nil) }.to raise_exception(ArgumentError) 154 | end 155 | end 156 | 157 | context 'with parameters present in payload data' do 158 | it 'recognizes root keys' do 159 | allow(project).to receive(:get_default_parameters) { [build_parameter('source_branch')] } 160 | expect(subject.with_mr(project, mr_details)[0].value).to eq('ms-viewport') 161 | end 162 | 163 | it 'do not recognizes nested keys' do 164 | allow(project).to receive(:get_default_parameters) { [build_parameter('target.name')] } 165 | expect(subject.with_mr(project, mr_details).size).to eq(0) 166 | end 167 | end 168 | 169 | context 'with parameters not in payload' do 170 | before(:each) { allow(project).to receive(:get_default_parameters) { [build_parameter('not_in_payload', 'default value')] } } 171 | 172 | it 'keeps them' do 173 | expect(subject.with_mr(project, mr_details)[0].name).to eq('not_in_payload') 174 | end 175 | 176 | it 'applies default value' do 177 | expect(subject.with_mr(project, mr_details)[0].value).to eq('default value') 178 | end 179 | end 180 | 181 | context 'with empty values' do 182 | it 'removes them' do 183 | allow(project).to receive(:get_default_parameters) { [build_parameter('not_in_payload')] } 184 | expect(subject.with_mr(project, mr_details).size).to eq(0) 185 | end 186 | end 187 | end 188 | end 189 | end 190 | -------------------------------------------------------------------------------- /spec/services/parse_request_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe ParseRequest do 5 | let(:parameters) { JSON.parse(File.read('spec/fixtures/default_params.json')) } 6 | let(:body) { File.new('spec/fixtures/8x/push.json') } 7 | let(:request) { OpenStruct.new(body: body) } 8 | 9 | context 'with data from params' do 10 | it 'builds parameters influenced details' do 11 | details = subject.from(parameters, nil) 12 | expect(details.repository_url).to eq('http://localhost/mike/peronospora') 13 | end 14 | end 15 | 16 | context 'with data from request' do 17 | it 'builds payload influenced details' do 18 | details = subject.from({}, request) 19 | expect(details.repository_url).to eq('git@example.com:mike/diaspora.git') 20 | end 21 | end 22 | 23 | context 'with parameters' do 24 | context 'when empty' do 25 | let(:parameters) { {} } 26 | 27 | it 'raises exception' do 28 | expect { subject.from(parameters, nil) }.to raise_exception(BadRequestException) 29 | end 30 | end 31 | 32 | context 'with invalid details' do 33 | let(:parameters) { {key: "value"} } 34 | 35 | it 'raises exception' do 36 | expect { subject.from(parameters, nil) }.to raise_exception(BadRequestException) 37 | end 38 | end 39 | end 40 | 41 | context 'with body' do 42 | context 'when unreadable' do 43 | it 'raises exception' do 44 | expect(body).to receive(:read).and_raise() 45 | expect { subject.from({}, OpenStruct.new(body: body)) }.to raise_exception(BadRequestException) 46 | end 47 | end 48 | 49 | context 'when non rewindable' do 50 | it 'raises exception' do 51 | expect(body).to receive(:read).and_raise() 52 | expect { subject.from({}, OpenStruct.new(body: body)) }.to raise_exception(BadRequestException) 53 | end 54 | end 55 | 56 | context 'when empty' do 57 | it 'raises exception' do 58 | expect(body).to receive(:read).and_return('') 59 | expect { subject.from({}, OpenStruct.new(body: body)) }.to raise_exception(BadRequestException) 60 | end 61 | end 62 | 63 | context 'when invalid json' do 64 | it 'raises exception' do 65 | expect(body).to receive(:read).and_return('{"key": "value", 456456}') 66 | expect { subject.from({}, OpenStruct.new(body: body)) }.to raise_exception(BadRequestException) 67 | end 68 | end 69 | 70 | context 'with invalid details' do 71 | it 'raises exception' do 72 | expect(body).to receive(:read).and_return('{"key": "value"}') 73 | expect { subject.from({}, OpenStruct.new(body: body)) }.to raise_exception(BadRequestException) 74 | end 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # test dependencies 2 | require 'json' 3 | require 'support/common' 4 | 5 | REQUIRED_CORE = '1.651.3' 6 | 7 | # java dependencies 8 | java_libs = Dir["spec/lib/**/*.jar"] 9 | 10 | # the rest of the application 11 | model_files = %W(models/exceptions models/values models/services models/use_cases) 12 | 13 | if RUBY_PLATFORM == 'java' 14 | 15 | # jenkins core libraries 16 | download_war( ENV['JENKINS_VERSION'] || REQUIRED_CORE ) 17 | extract_jar 'jenkins.war', 'spec/war' 18 | java_libs = java_libs + Dir["spec/war/winstone.jar", "spec/war/WEB-INF/lib/*.jar"] 19 | 20 | # required plugins 21 | download_plugin 'git', '2.0' 22 | download_plugin 'git-client', '1.4.4' 23 | download_plugin 'multiple-scms', '0.4' 24 | download_plugin 'matrix-project', '1.2' 25 | download_plugin 'credentials', '1.18' 26 | ['git', 'git-client', 'multiple-scms', 'matrix-project', 'credentials'].each{ |plugin| extract_jar "#{plugin}.hpi" } 27 | java_libs = java_libs + Dir["spec/plugins/WEB-INF/lib/*.jar"] 28 | # Some old versions do not have a jarfile with their own classes 29 | ['git', 'git-client', 'credentials'].each{ |plugin| extract_classes plugin } 30 | $CLASSPATH << 'spec/plugins/WEB-INF/classes' 31 | 32 | java_libs.each do |jar| 33 | require jar 34 | end 35 | 36 | model_files.each do |autoload_path| 37 | Dir[File.expand_path("../../#{autoload_path}/**/*.rb", __FILE__)].each { |f| require f } 38 | end 39 | 40 | end 41 | 42 | # supporting ruby files with custom matchers and macros, etc, 43 | # in spec/support/ and its subdirectories. 44 | Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f } 45 | 46 | RSpec.configure do |config| 47 | # disable should syntax, it wil become obsolete in future RSpec releases 48 | # http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 49 | config.expect_with :rspec do |c| 50 | c.syntax = [:expect] 51 | end 52 | 53 | # Run specs in random order to surface order dependencies. If you find an 54 | # order dependency and want to debug it, you can fix the order by providing 55 | # the seed, which is printed after each run. 56 | # --seed 1234 57 | config.order = "random" 58 | 59 | config.raise_errors_for_deprecations! 60 | end 61 | -------------------------------------------------------------------------------- /spec/support/common.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'open-uri' 3 | require 'zip' 4 | 5 | require 'fileutils' 6 | 7 | def incoming_payload(filename, tempdir, project_name=nil) 8 | reponame = File.basename(tempdir, '.git').split('-').first[0..-9] 9 | uri = URI "http://localhost:8080/gitlab/build_now#{"/#{project_name}" if project_name}" 10 | req = Net::HTTP::Post.new(uri.request_uri, initheader = {'Content-Type' =>'application/json'}) 11 | req.body = File.read("spec/fixtures/payloads/#{filename}.json") % { reponame: reponame, repodir: tempdir } 12 | http = Net::HTTP.new uri.host, uri.port 13 | response = Net::HTTP.start(uri.hostname, uri.port).request req 14 | sleep 10 15 | end 16 | 17 | def wait_for(url, xmlpath, waittime=30) 18 | count = waittime / 5 19 | begin 20 | visit url 21 | break if page.has_xpath? xmlpath 22 | sleep 5 23 | end until (count-=1).zero? 24 | end 25 | 26 | def wait_idle(waittime=30) 27 | sleep 5 28 | begin 29 | info = JSON.parse Net::HTTP.get URI "http://localhost:8080/computer/api/json" 30 | queue = JSON.parse Net::HTTP.get URI "http://localhost:8080/queue/api/json" 31 | break if info['busyExecutors'] == 0 and queue['items'].length == 0 32 | sleep 1 33 | end until (waittime-=1).zero? 34 | end 35 | 36 | def download_war(version, warname='jenkins.war') 37 | return if File.exists? warname 38 | puts "Downloading jenkins #{version} ..." if ENV['DEBUG']=='YES' 39 | if version == "latest" 40 | file = open "http://updates.jenkins-ci.org/download/war/#{version}/jenkins.war" 41 | else 42 | file = open "http://repo.jenkins-ci.org/releases/org/jenkins-ci/main/jenkins-war/#{version}/jenkins-war-#{version}.war" 43 | end 44 | FileUtils.cp file.path, warname 45 | end 46 | 47 | def download_plugin(name, version, destdir='.') 48 | plugin = "#{destdir}/#{name}.hpi" 49 | return if File.exists? plugin 50 | puts "Downloading #{name}-#{version} ..." if ENV['DEBUG']=='YES' 51 | file = open "http://mirrors.jenkins-ci.org/plugins/#{name}/#{version}/#{name}.hpi?for=ruby-plugin" 52 | FileUtils.cp file.path, plugin 53 | end 54 | 55 | def extract_jar(zipfile, destdir='spec/plugins') 56 | FileUtils.mkdir_p "#{destdir}/WEB-INF/lib" 57 | Zip::File.open(zipfile) do |zipfile| 58 | zipfile.each do |entry| 59 | if entry.name.end_with? '.jar' 60 | outfile = "#{destdir}/#{entry.name}" 61 | entry.extract(outfile) unless File.exist? outfile 62 | end 63 | end 64 | end 65 | end 66 | 67 | def extract_classes(plugin, destdir='spec/plugins') 68 | FileUtils.mkdir_p "#{destdir}/WEB-INF/classes" 69 | Zip::File.open("#{plugin}.hpi") do |zipfile| 70 | zipfile.each do |entry| 71 | if entry.name.start_with? 'WEB-INF/classes' 72 | outfile = "#{destdir}/#{entry.name}" 73 | entry.extract(outfile) unless File.exist? outfile 74 | end 75 | end 76 | end 77 | end 78 | 79 | -------------------------------------------------------------------------------- /spec/support/env.rb: -------------------------------------------------------------------------------- 1 | require 'support/phantom' 2 | 3 | require 'capybara/rspec' 4 | require 'capybara/poltergeist' 5 | 6 | Capybara.default_driver = :poltergeist 7 | Capybara.javascript_driver = :poltergeist 8 | Capybara.register_driver :poltergeist do |app| 9 | opts = { :phantomjs => phantom } 10 | Capybara::Poltergeist::Driver.new(app, opts) 11 | end 12 | 13 | Capybara.configure do |c| 14 | c.app = proc{} 15 | c.app_host = "http://localhost:8080" 16 | c.run_server = false 17 | end 18 | 19 | -------------------------------------------------------------------------------- /spec/support/gitlab.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra/base' 2 | require 'sinatra/json' 3 | 4 | class GitLabMockup 5 | 6 | def initialize(repodirs, port=4567) 7 | reponames = repodirs.collect{ |dir| File.basename(dir, '.git').split('-').first[0..-9] } 8 | # We actully hide whole stderr, not only sinatra, but 9 | # that's better than keep the noise added by request tracing 10 | @log, err = IO.pipe 11 | @server = Thread.fork do 12 | $stderr.reopen err 13 | MyServer.start reponames, port 14 | end 15 | end 16 | 17 | def last reponame 18 | MyServer.last reponame 19 | end 20 | 21 | def kill 22 | @server.kill 23 | @server.join 24 | $stderr.puts "END MOCKUP" 25 | dump @log, ' ## ' if ENV['DEBUG']=='YES' 26 | end 27 | 28 | def dump(instream, prefix='', outstream=$stdout) 29 | begin 30 | line = instream.readline 31 | outstream.puts "#{prefix}#{line}" 32 | end until line.start_with?("END MOCKUP") 33 | end 34 | 35 | class MyServer < Sinatra::Base 36 | 37 | class << self 38 | 39 | def last reponame 40 | @@lasts[reponame] 41 | end 42 | 43 | def start(reponames, port=4567) 44 | @@repos = reponames 45 | @@lasts = {} 46 | @@urls = {} 47 | set :port, port 48 | run! 49 | end 50 | 51 | end 52 | 53 | helpers do 54 | 55 | def author 56 | { 57 | "id" => 0, 58 | "name" => "root", 59 | "username" => "root" 60 | } 61 | end 62 | 63 | def project_info(id) 64 | name = @@repos[id] 65 | dirname = @@urls[name] or name 66 | { 67 | 'id' => id, 68 | 'name' => name, 69 | 'default_branch' => 'master', 70 | 'http_url_to_repo' => "http://localhost/tmp/#{dirname}.git", 71 | 'ssh_url_to_repo' => "localhost:/tmp/#{dirname}.git", 72 | 'path' => dirname, 73 | 'path_with_namespace' => "tmp/#{dirname}", 74 | 'web_url' => "http://localhost/tmp/#{name}" 75 | } 76 | end 77 | 78 | def mr_response(id) 79 | { 80 | 'id' => id, 'iid' => id, 81 | 'target_branch' => 'master', 82 | 'source_branch' => 'feature/branch' 83 | } 84 | end 85 | end 86 | 87 | get "/api/v3/projects/:project_id" do 88 | json project_info(params['project_id'].to_i) 89 | end 90 | 91 | get "/api/v3/projects/search/:query" do 92 | reponame = params['query'].split('-').first[0..-9] 93 | @@urls[reponame] = params['query'] unless @@urls.has_key?(reponame) 94 | project_id = @@repos.rindex(reponame) 95 | json [ project_info(project_id) ] 96 | end 97 | 98 | get "/api/v3/projects/:project_id/merge_requests" do 99 | json [ mr_response(params['project_id']) ] 100 | end 101 | 102 | post "/api/v3/projects/:project_id/merge_request/:mr_id/comments" do 103 | reponame = @@repos[params['project_id'].to_i] 104 | @@lasts[reponame] = "/mr_comment/#{params[:mr_id]} - #{params[:note]}" 105 | json author: author , note: request.body.string , created_at: Time.new.utc.strftime("%FT%TZ") 106 | end 107 | 108 | post "/api/v3/projects/:project_id/repository/commits/:sha/comments" do 109 | reponame = @@repos[params['project_id'].to_i] 110 | @@lasts[reponame] = "/comment/#{params[:sha]} - #{params[:note]}" 111 | json author: author , note: request.body.string , created_at: Time.new.utc.strftime("%FT%TZ") 112 | end 113 | 114 | post "/api/v3/projects/:project_id/statuses/:sha" do 115 | reponame = @@repos[params['project_id'].to_i] 116 | @@lasts[reponame] = "/status/#{params[:sha]} - #{params[:state]} - #{params[:target_url]}" 117 | json state: params[:state] , target_url: params[:target_url] , created_at: Time.new.utc.strftime("%FT%TZ") 118 | end 119 | 120 | end 121 | 122 | end 123 | 124 | -------------------------------------------------------------------------------- /spec/support/jenkins.rb: -------------------------------------------------------------------------------- 1 | require 'jenkins/plugin/specification' 2 | require 'jenkins/plugin/tools/server' 3 | 4 | require 'net/http' 5 | require 'rexml/document' 6 | 7 | require 'tmpdir' 8 | require 'fileutils' 9 | 10 | class Jenkins::Server 11 | 12 | attr_reader :workdir 13 | attr_reader :job, :std, :log 14 | 15 | REQUIRED_CORE = '1.651.3' 16 | 17 | def initialize 18 | 19 | version = ENV['JENKINS_VERSION'] || REQUIRED_CORE 20 | 21 | FileUtils.mkdir_p 'vendor/bundle' 22 | warname = "vendor/bundle/jenkins-#{version}.war" 23 | 24 | download_war( version , warname ) 25 | 26 | @workdir = Dir.mktmpdir 'work' 27 | 28 | spec = Jenkins::Plugin::Specification.load('jenkins-gitlab-hook.pluginspec') 29 | server = Jenkins::Plugin::Tools::Server.new(spec, workdir, warname, '8080') 30 | 31 | # Dependencies for git 2.0 32 | FileUtils.mkdir_p "#{workdir}/plugins" 33 | unless version.split('.').first == '1' 34 | download_plugin 'credentials', '1.16.1', "#{workdir}/plugins" 35 | download_plugin 'ssh-credentials', '1.7.1', "#{workdir}/plugins" 36 | download_plugin 'matrix-project', '1.4.1', "#{workdir}/plugins" 37 | download_plugin 'script-security', '1.13', "#{workdir}/plugins" 38 | download_plugin 'junit', '1.2', "#{workdir}/plugins" 39 | download_plugin 'mailer', '1.11', "#{workdir}/plugins" 40 | download_plugin 'matrix-auth', '1.1', "#{workdir}/plugins" 41 | end 42 | 43 | download_plugin 'scm-api', '0.1', "#{workdir}/plugins" 44 | download_plugin 'git-client', '1.4.4', "#{workdir}/plugins" 45 | download_plugin 'ssh-agent', '1.3', "#{workdir}/plugins" 46 | download_plugin 'multiple-scms', '0.4', "#{workdir}/plugins" 47 | 48 | FileUtils.cp_r Dir.glob('work/*'), workdir 49 | 50 | @std, out = IO.pipe 51 | @log, err = IO.pipe 52 | @job = fork do 53 | $stdout.reopen out 54 | $stderr.reopen err 55 | ENV['JAVA_OPTS'] = "-XX:MaxPermSize=512m -Xms512m -Xmx1024m -Djruby.compile.mode=OFF -Djruby.objectspace.enabled=false -Dhudson.DNSMultiCast.disabled=true" 56 | server.run! 57 | end 58 | Process.detach job 59 | 60 | begin 61 | line = log.readline 62 | puts " -> #{line}" if ENV['DEBUG']=='YES' 63 | end until line.include?('Jenkins is fully up and running') 64 | 65 | 66 | end 67 | 68 | def kill 69 | Process.kill 'TERM', job 70 | dump log, ' -> ' if ENV['DEBUG']=='YES' 71 | Process.waitpid job, Process::WNOHANG 72 | rescue Errno::ECHILD => e 73 | ensure 74 | if ENV['DEBUG']=='YES' 75 | Dir["#{workdir}/jobs/*/builds/?/log"].each do |file| 76 | puts 77 | puts "## #{file} ##" 78 | puts File.read(file) 79 | end 80 | Dir["#{workdir}/jobs/*/builds/?/build.xml"].each do |file| 81 | puts 82 | puts "## #{file} ##" 83 | puts File.read(file) 84 | end 85 | end 86 | FileUtils.rm_rf workdir 87 | end 88 | 89 | def result(name, seq) 90 | waittime = 30 91 | begin 92 | sleep 5 93 | uri = URI "http://localhost:8080/job/#{name}/#{seq}/api/json" 94 | response = JSON.parse Net::HTTP.get uri 95 | break if response['building'].is_a? FalseClass 96 | end until (waittime-=5).zero? 97 | response['result'] 98 | end 99 | 100 | private 101 | 102 | def dump(instream, prefix='', outstream=$stdout) 103 | begin 104 | line = instream.readline 105 | outstream.puts "#{prefix}#{line}" 106 | end until instream.eof? 107 | end 108 | 109 | end 110 | -------------------------------------------------------------------------------- /spec/support/jenkins/default_descriptor.rb: -------------------------------------------------------------------------------- 1 | # mocks extended stuff from Jenkins 2 | module Jenkins 3 | module Model 4 | class DefaultDescriptor 5 | def configFile 6 | self 7 | end 8 | def file 9 | self 10 | end 11 | def exists 12 | false 13 | end 14 | def self.java_class 15 | self 16 | end 17 | end 18 | end 19 | end 20 | 21 | if RUBY_PLATFORM == 'java' 22 | # explicitly require stuff from models root folder, due to above mock(s) 23 | require 'models/root_action_descriptor' 24 | end 25 | -------------------------------------------------------------------------------- /spec/support/phantom.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | def phantom(version='1.9.8') 4 | 5 | exe = `which phantomjs 2>/dev/null`.chomp 6 | return exe unless exe.empty? 7 | 8 | arch , os = RUBY_PLATFORM.split '-' 9 | name = "phantomjs-#{version}-#{os}-#{arch}" 10 | url = "https://bitbucket.org/ariya/phantomjs/downloads/#{name}.tar.bz2" 11 | 12 | File.join(Bundler.user_bundle_path, name, 'bin/phantomjs').tap do |exe| 13 | unless File.exists? exe 14 | FileUtils.mkdir_p Bundler.user_bundle_path 15 | Dir.chdir(Bundler.user_bundle_path) do 16 | puts "Downloading phantomjs #{version} ..." 17 | system("curl -s #{url} | tar -jx") 18 | end 19 | end 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /spec/support/shared/details.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_context 'details' do 2 | let (:payload) { JSON.parse(File.read('spec/fixtures/8x/push.json')) } 3 | let (:details) { GitlabWebHook::PayloadRequestDetails.new(payload) } 4 | end 5 | -------------------------------------------------------------------------------- /spec/support/shared/mr_details.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_context 'mr_details' do 2 | let (:mr_payload) { JSON.parse(File.read('spec/fixtures/8x/merge_request.json')) } 3 | let (:mr_details) { GitlabWebHook::MergeRequestDetails.new(mr_payload) } 4 | end 5 | -------------------------------------------------------------------------------- /spec/support/shared/projects.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_context 'projects' do 2 | let(:repo_url) { 'http://example.com/mike/diaspora.git' } 3 | let(:refspec) { double('RefSpec', matchSource: true) } 4 | let(:repository) { double('RemoteConfig', name: 'origin', getURIs: [URIish.new(repo_url)], getFetchRefSpecs: [refspec]) } 5 | let(:build_chooser) { double('BuildChooser') } 6 | let(:scm1) { double(GitSCM, repositories: [repository], branches: [BranchSpec.new('origin/master')], buildChooser: build_chooser, extensions: double('ExtensionsList', get: nil)) } 7 | let(:java_project1) { double(AbstractProject, fullName: 'matching project', scm: scm1, isBuildable: true, isParameterized: false) } 8 | let(:scm2) { double(GitSCM, repositories: [repository], branches: [BranchSpec.new('origin/otherbranch')], buildChooser: build_chooser, extensions: double('ExtensionsList', get: nil)) } 9 | let(:java_project2) { double(AbstractProject, fullName: 'not matching project', scm: scm2, isBuildable: true, isParameterized: false) } 10 | let(:matching_project) { GitlabWebHook::Project.new(java_project1) } 11 | let(:not_matching_project) { GitlabWebHook::Project.new(java_project2) } 12 | 13 | let(:repo_url3) { 'git@example.com:discourse/discourse.git' } 14 | let(:repository3) { double('RemoteConfig', name: 'origin', getURIs: [URIish.new(repo_url3)], getFetchRefSpecs: [refspec]) } 15 | let(:scm3) { double(GitSCM, repositories: [repository3], branches: [BranchSpec.new('origin/master')], buildChooser: build_chooser, extensions: double('ExtensionsList', get: nil)) } 16 | let(:java_project3) { double(AbstractProject, fullName: 'autocreate matching project', scm: scm3, isBuildable: true, isParameterized: false) } 17 | let(:autocreate_match_project) { GitlabWebHook::Project.new(java_project3) } 18 | 19 | let(:all_projects) { [ not_matching_project , matching_project , autocreate_match_project ] } 20 | 21 | before(:each) do 22 | allow(build_chooser).to receive(:java_kind_of?).with(InverseBuildChooser) { false } 23 | [ scm1 , scm2 , scm3 ].each do |scm| 24 | allow(scm).to receive(:java_kind_of?).with(GitSCM) { true } 25 | allow(scm).to receive(:java_kind_of?).with(MultiSCM) { false } 26 | end 27 | all_projects.each do |project| 28 | allow(project).to receive(:tag?) { nil } 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/support/shared/settings.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_context 'settings' do 2 | let(:settings) { GitlabWebHookRootActionDescriptor.new } 3 | let(:jenkins_instance) { double(Java.jenkins.model.Jenkins) } 4 | 5 | before(:each) do 6 | allow(Java.jenkins.model.Jenkins).to receive(:instance) { jenkins_instance } 7 | allow(jenkins_instance).to receive(:descriptor) { settings } 8 | allow(settings).to receive(:merge_request_processing?) { true } 9 | allow(settings).to receive(:merged_branch_triggering?) { true } 10 | allow(settings).to receive(:ignore_users) { "user 1, user 2, user 3" } 11 | end 12 | end -------------------------------------------------------------------------------- /spec/support/shared/tag_details.rb: -------------------------------------------------------------------------------- 1 | RSpec.shared_context 'tag_details' do 2 | let (:tag_payload) { JSON.parse(File.read('spec/fixtures/8x/tag.json')) } 3 | let (:tag_details) { GitlabWebHook::PayloadRequestDetails.new(tag_payload) } 4 | end 5 | -------------------------------------------------------------------------------- /spec/support/travisci.rb: -------------------------------------------------------------------------------- 1 | 2 | module Autologin 3 | 4 | module_function 5 | 6 | def enable 7 | set_known_hosts 8 | create_key 9 | end 10 | 11 | private 12 | 13 | # Adds localhost to known_hosts file 14 | def self.set_known_hosts 15 | fd = File.open "#{ENV['HOME']}/.ssh/known_hosts", 'a+' 16 | begin 17 | begin 18 | line = fd.readline 19 | end until line.start_with? 'localhost' 20 | rescue EOFError => e 21 | ecdsa = File.read '/etc/ssh/ssh_host_ecdsa_key.pub' 22 | fd.puts "localhost #{ecdsa}" 23 | end 24 | fd.close 25 | end 26 | 27 | # Create key and enable autologin 28 | def self.create_key 29 | ssh_dir = File.join ENV['HOME'], '.ssh' 30 | id_rsa = File.join ssh_dir, 'id_rsa' 31 | unless File.exists? id_rsa 32 | `ssh-keygen -f "#{id_rsa}" -P ""` 33 | end 34 | File.open(File.join(ssh_dir, 'authorized_keys'), 'w') do |outfd| 35 | pubkey = File.read File.join(ssh_dir, 'id_rsa.pub') 36 | outfd.write pubkey 37 | end 38 | end 39 | 40 | end 41 | -------------------------------------------------------------------------------- /spec/use_cases/build_now_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe BuildNow do 5 | let(:details) { double(RequestDetails, payload: double) } 6 | let(:project) { double(Project, ignore_notify_commit?: false, buildable?: true, getQuietPeriod: double) } 7 | let(:logger) { double } 8 | let(:subject) { BuildNow.new(project, logger) } 9 | 10 | context 'when project configured to ignore notify commit' do 11 | it 'skips the build' do 12 | allow(project).to receive(:ignore_notify_commit?) { true } 13 | expect(project).not_to receive(:scheduleBuild2) 14 | expect(subject.with(details, GetBuildActions.new, GetBuildCause.new)).to match('configured to ignore notify commit') 15 | end 16 | end 17 | 18 | context 'when project not buildable' do 19 | it 'skips the build' do 20 | allow(project).to receive(:buildable?) { false } 21 | expect(project).not_to receive(:scheduleBuild2) 22 | expect(subject.with(details, GetBuildActions.new, GetBuildCause.new)).to match('not buildable') 23 | end 24 | end 25 | 26 | context 'when build triggered' do 27 | let(:cause_builder) { double(with: true) } 28 | let(:actions_builder) { double(with: true) } 29 | 30 | before(:each) do 31 | expect(cause_builder).to receive(:with).with(details) 32 | expect(actions_builder).to receive(:with).with(project, details) 33 | end 34 | 35 | context 'successfully' do 36 | it 'schedules the build' do 37 | expect(project).to receive(:scheduleBuild2).and_return(true) 38 | expect(subject.with(details, cause_builder, actions_builder)).to match('scheduled for build') 39 | end 40 | end 41 | 42 | context 'unsuccessfully' do 43 | before(:each) do 44 | exception = java.lang.Exception.new('message') 45 | expect(project).to receive(:scheduleBuild2).and_raise(exception) 46 | 47 | severe = Proc.new {} 48 | expect(severe).to receive(:call).with(Java.java.util.logging.Level::SEVERE, 'message', exception) 49 | 50 | expect(logger).to receive(:java_method).with(:log, [Java.java.util.logging.Level, java.lang.String, java.lang.Throwable]).and_return(severe) 51 | end 52 | 53 | it 'logs error' do 54 | subject.with(details, cause_builder, actions_builder) 55 | end 56 | 57 | it 'returns appropriate message' do 58 | expect(subject.with(details, cause_builder, actions_builder)).to match('could not be scheduled for build') 59 | end 60 | end 61 | end 62 | 63 | context 'when validating' do 64 | it 'requires project' do 65 | expect { BuildNow.new(nil) }.to raise_exception(ArgumentError) 66 | end 67 | 68 | it 'requires details' do 69 | expect { subject.with(nil, GetBuildActions.new, GetBuildCause.new) }.to raise_exception(ArgumentError) 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/use_cases/create_project_for_branch_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe CreateProjectForBranch do 5 | include_context 'settings' 6 | include_context 'projects' 7 | 8 | let(:repository_uri) { RepositoryUri.new('http://example.com/discourse/discourse.git') } 9 | let(:details) { double(PayloadRequestDetails, repository_uri: repository_uri, repository_name: 'discourse', branch: 'features/meta', safe_branch: 'features_meta', full_branch_reference: 'refs/heads/features/meta') } 10 | let(:master) { double(Project, name: 'discourse', jenkins_project: java_project1, multiscm?: false) } 11 | let(:get_jenkins_projects) { GetJenkinsProjects.new } 12 | let(:build_scm) { double(BuildScm, with: double(GitSCM)) } 13 | let(:subject) { CreateProjectForBranch.new(get_jenkins_projects, build_scm) } 14 | 15 | before(:each) { allow(get_jenkins_projects).to receive(:all) { all_projects } } 16 | 17 | context 'when not able to find a master project to copy from' do 18 | it 'raises appropriate exception' do 19 | all_projects.delete(autocreate_match_project) 20 | expect { subject.with(details) }.to raise_exception(NotFoundException) 21 | end 22 | end 23 | 24 | context 'when master project is configured to build tags' do 25 | it 'raises appropriate exception' do 26 | expect(autocreate_match_project).to receive(:tag?) { 'tag' } 27 | expect { subject.with(details) }.to raise_exception(NotFoundException) 28 | end 29 | end 30 | 31 | context 'when branch project already exists' do 32 | it 'raises appropriate exception' do 33 | expect(get_jenkins_projects).to receive(:named).and_return([not_matching_project]) 34 | expect { subject.with(details) }.to raise_exception(ConfigurationException) 35 | end 36 | end 37 | 38 | context 'when naming the branch project' do 39 | 40 | it 'uses master project name with appropriate settings' do 41 | expect(subject.send(:get_new_project_name, master, details)).to match(master.name) 42 | end 43 | 44 | it 'uses repository name with appropriate settings' do 45 | expect(subject.send(:get_new_project_name, master, details)).to match(details.repository_name) 46 | end 47 | end 48 | 49 | context 'when creating the branch project' do 50 | let(:scm) { double(GitSCM) } 51 | let(:jenkins_instance) { double(Java.jenkins.model.Jenkins) } 52 | let(:new_jenkins_project) { double(Java.hudson.model.AbstractProject, scm: scm).as_null_object } 53 | 54 | before(:each) do 55 | allow(master).to receive(:scm) { scm } 56 | allow(scm).to receive(:java_kind_of?).with(GitSCM) { true } 57 | allow(scm).to receive(:java_kind_of?).with(MultiSCM) { false } 58 | allow(Java.jenkins.model.Jenkins).to receive(:instance) { jenkins_instance } 59 | expect(get_jenkins_projects).to receive(:master).and_return( master ) 60 | expect(jenkins_instance).to receive(:copy).with(java_project1, anything).and_return(new_jenkins_project) 61 | expect(master).to receive(:matched_scm) { [ scm ] } 62 | end 63 | 64 | it 'returns a new project' do 65 | branch_project = subject.with(details) 66 | expect(branch_project).to be_kind_of(Project) 67 | expect(branch_project.jenkins_project).to eq(new_jenkins_project) 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/use_cases/notify_commit_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe NotifyCommit do 5 | let(:project) { double(Project, ignore_notify_commit?: false, buildable?: true) } 6 | let(:logger) { double } 7 | let(:subject) { NotifyCommit.new(project, logger) } 8 | 9 | context 'when project configured to ignore notify commit' do 10 | it 'skips the build' do 11 | allow(project).to receive(:ignore_notify_commit?) { true } 12 | expect(project).not_to receive(:schedulePolling) 13 | expect(subject.call).to match('configured to ignore notify commit') 14 | end 15 | end 16 | 17 | context 'when project not buildable' do 18 | it 'skips the build' do 19 | allow(project).to receive(:buildable?) { false } 20 | expect(project).not_to receive(:schedulePolling) 21 | expect(subject.call).to match('not buildable') 22 | end 23 | end 24 | 25 | context 'when notify commit triggered' do 26 | context 'successfully' do 27 | it 'schedules polling' do 28 | expect(project).to receive(:schedulePolling).and_return(true) 29 | expect(subject.call).to match('scheduled for polling') 30 | end 31 | end 32 | 33 | context 'unsuccessfully' do 34 | before(:each) do 35 | exception = java.lang.Exception.new('message') 36 | expect(project).to receive(:schedulePolling).and_raise(exception) 37 | 38 | severe = Proc.new {} 39 | expect(severe).to receive(:call).with(Java.java.util.logging.Level::SEVERE, 'message', exception) 40 | 41 | expect(logger).to receive(:java_method).with(:log, [Java.java.util.logging.Level, java.lang.String, java.lang.Throwable]).and_return(severe) 42 | end 43 | 44 | it 'logs error' do 45 | subject.call 46 | end 47 | 48 | it 'returns appropriate message' do 49 | expect(subject.call).to match('could not be scheduled for polling') 50 | end 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/use_cases/process_commit_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe ProcessCommit do 5 | include_context 'settings' 6 | include_context 'projects' 7 | include_context 'details' 8 | 9 | let(:action) { double(Proc) } 10 | let(:create_project_for_branch) { double(CreateProjectForBranch) } 11 | 12 | before :each do 13 | expect(CreateProjectForBranch).to receive(:new).and_return( create_project_for_branch ) 14 | end 15 | 16 | context 'with related projects' do 17 | 18 | let(:projects_matching_uri) { [ matching_project , not_matching_project ] } 19 | 20 | before :each do 21 | expect(matching_project.matches_uri?(details.repository_uri)).to be(true) 22 | expect(not_matching_project.matches_uri?(details.repository_uri)).to be(true) 23 | expect(autocreate_match_project.matches_uri?(details.repository_uri)).to be(false) 24 | end 25 | 26 | it 'calls action with found project and related details' do 27 | expect(action).to receive(:call).once.with(matching_project, details) 28 | subject.with(details, projects_matching_uri, action) 29 | end 30 | 31 | it 'returns messages collected by calls to action' do 32 | expect(action).to receive(:call).once.with(matching_project, details).and_return('executed') 33 | messages = subject.with(details, projects_matching_uri, action) 34 | expect(messages.size).to eq(1) 35 | expect(messages).to eq(%w(executed)) 36 | end 37 | 38 | context 'when automatic project creation is on' do 39 | let(:new_project) { double(Project) } 40 | before(:each) { allow(settings).to receive(:automatic_project_creation?) { true } } 41 | 42 | it 'searches exactly matching projects' do 43 | expect(create_project_for_branch).not_to receive(:with) 44 | expect(action).to receive(:call).once 45 | subject.with(details, projects_matching_uri, action) 46 | end 47 | 48 | it 'creates a new project when no matching projects found' do 49 | expect(create_project_for_branch).to receive(:with).with(details).and_return(new_project) 50 | expect(action).to receive(:call).once.with(new_project, details).once 51 | subject.with(details, [ not_matching_project ] , action) 52 | end 53 | end 54 | end 55 | 56 | context 'with push from unknown repository' do 57 | let(:new_project) { double(Project) } 58 | let(:templated_jobs) { { 'matchstr' => 'job-template'} } 59 | let(:templated_groups) { { 'matchstr' => 'group-template'} } 60 | 61 | before(:each) do 62 | allow(settings).to receive(:templated_jobs).and_return( templated_jobs ) 63 | allow(settings).to receive(:templated_groups).and_return( templated_groups ) 64 | end 65 | 66 | context 'and a template matches repository name' do 67 | let(:templated_jobs) { { 'Dias' => 'reponame-template'} } 68 | 69 | it 'returns the jobname template' do 70 | expect(settings).not_to receive(:templated_groups) 71 | expect(settings).not_to receive(:template_fallback) 72 | expect(create_project_for_branch).to receive(:from_template).with('reponame-template', details).and_return(new_project) 73 | expect(action).to receive(:call) 74 | subject.with(details, [], action) 75 | end 76 | end 77 | 78 | context 'and repo namespace matches some template' do 79 | let(:templated_groups) { { 'mike' => 'repogroup-template' } } 80 | 81 | it 'returns the groupname template' do 82 | expect(settings).not_to receive(:template_fallback) 83 | expect(create_project_for_branch).to receive(:from_template).with('repogroup-template', details).and_return(new_project) 84 | expect(action).to receive(:call) 85 | subject.with(details, [], action) 86 | end 87 | end 88 | 89 | context 'and fallback template exists' do 90 | it 'returns the groupname template' do 91 | expect(settings).to receive(:template_fallback).twice.and_return( 'fallback-template' ) 92 | expect(create_project_for_branch).to receive(:from_template).with('fallback-template', details).and_return(new_project) 93 | expect(action).to receive(:call) 94 | subject.with(details, [], action) 95 | end 96 | end 97 | 98 | it 'raises exception when no matching projects found' do 99 | expect(action).not_to receive(:call) 100 | expect { subject.with(details, [], action) }.to raise_exception(NotFoundException) 101 | end 102 | 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /spec/use_cases/process_delete_commit_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe ProcessDeleteCommit do 5 | include_context 'settings' 6 | 7 | let(:details) { double(RequestDetails, branch: 'features/meta', repository_uri: 'git@gitlab.com/group/discourse') } 8 | 9 | context 'when automatic project creation is offline' do 10 | let(:project) { double(Project) } 11 | before(:each) { allow(settings).to receive(:automatic_project_creation?) { false } } 12 | 13 | it 'skips processing' do 14 | expect(subject.with(details, [project]).first).to match('automatic branch projects creation is not active') 15 | end 16 | end 17 | 18 | context 'when automatic project creation is online' do 19 | before(:each) { allow(settings).to receive(:automatic_project_creation?) { true } } 20 | 21 | context 'with master branch in commit' do 22 | let(:project) { double(Project) } 23 | 24 | it 'skips processing' do 25 | allow(details).to receive(:branch) { settings.master_branch } 26 | expect(subject.with(details, [project]).first).to match('relates to master project') 27 | end 28 | end 29 | 30 | context 'with non master branch in commit' do 31 | let(:project) { double(Project, to_s: 'non_master') } 32 | 33 | before(:each) do 34 | allow(project).to receive(:matches?) { true } 35 | end 36 | 37 | it 'deletes automatically created project' do 38 | allow(project).to receive(:description) { settings.description } 39 | expect(project).to receive(:delete) 40 | expect(subject.with(details, [project]).first).to match("deleted #{project} project") 41 | end 42 | 43 | it 'skips project not automatically created' do 44 | allow(project).to receive(:description) { 'manually created' } 45 | expect(project).not_to receive(:delete) 46 | expect(subject.with(details, [project]).first).to match('not automatically created') 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/use_cases/process_merge_request_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'models/root_action_descriptor' 4 | 5 | module GitlabWebHook 6 | describe ProcessMergeRequest do 7 | include_context 'settings' 8 | 9 | include_context 'mr_details' 10 | let (:jenkins_project) { double(AbstractProject, name: 'diaspora', matches?: false, merge_to?: false) } 11 | let (:create_project_for_branch) { double(for_merge: []) } 12 | 13 | before :each do 14 | expect(CreateProjectForBranch).to receive(:new).and_return( create_project_for_branch ) 15 | end 16 | 17 | context 'when merge request is unmergeable' do 18 | 19 | [ 'opened', 'reopened' ].each do |state| 20 | it "skips processing for state #{state}" do 21 | expect(mr_details).to receive(:merge_status).twice.and_return( 'cannot_be_merged' ) 22 | expect(mr_details).to receive(:state).and_return( state ) 23 | messages = subject.with(mr_details, [jenkins_project]) 24 | expect(messages[0]).to match('Skipping not ready merge request') 25 | end 26 | end 27 | 28 | it "keeps processing when closing" do 29 | expect(mr_details).to receive(:merge_status).and_return( 'cannot_be_merged' ) 30 | expect(mr_details).to receive(:state).twice.and_return( 'closed' ) 31 | expect(jenkins_project).to receive(:matches?).and_return(true) 32 | expect(jenkins_project).to receive(:merge_to?).and_return(true) 33 | expect(jenkins_project).to receive(:delete) 34 | subject.with(mr_details, [jenkins_project]) 35 | end 36 | 37 | end 38 | 39 | context 'when merge request is mergeable' do 40 | 41 | before :each do 42 | expect(mr_details).to receive(:merge_status).and_return( 'can_be_merged' ) 43 | end 44 | 45 | [ 'opened', 'reopened' ].each do |status| 46 | context "and status is #{status}" do 47 | before :each do 48 | expect(mr_details).to receive(:state).and_return( status ) 49 | end 50 | it 'and project already exists' do 51 | expect(jenkins_project).to receive(:matches?).and_return(true) 52 | expect(jenkins_project).to receive(:merge_to?).and_return(true) 53 | expect(BuildNow).to receive(:new).and_return(double(with:'')) 54 | expect(create_project_for_branch).not_to receive(:for_merge) 55 | subject.with(mr_details, [jenkins_project]) 56 | end 57 | context 'and project does not exists' do 58 | let (:merge_project) { double(AbstractProject, name: 'diaspora-mr-branchname') } 59 | before :each do 60 | expect(jenkins_project).to receive(:matches?).and_return(true) 61 | end 62 | it 'and no candidate target project exists' do 63 | expect(create_project_for_branch).to receive(:for_merge).and_return([]) 64 | expect(BuildNow).not_to receive(:new) 65 | subject.with(mr_details, [jenkins_project]) 66 | end 67 | it 'and target branch candidate exists' do 68 | expect(create_project_for_branch).to receive(:for_merge).and_return([merge_project]) 69 | expect(BuildNow).to receive(:new).with(merge_project).and_return(double(with:'')) 70 | subject.with(mr_details, [jenkins_project]) 71 | end 72 | end 73 | end 74 | end 75 | 76 | [ 'closed', 'merged' ].each do |status| 77 | context "and status is #{status}" do 78 | before :each do 79 | expect(mr_details).to receive(:state).and_return( status ) 80 | expect(create_project_for_branch).not_to receive(:for_merge) 81 | end 82 | it 'and project already exists' do 83 | expect(jenkins_project).to receive(:matches?).and_return(true) 84 | expect(jenkins_project).to receive(:merge_to?).and_return(true) 85 | expect(jenkins_project).to receive(:delete) 86 | subject.with(mr_details, [jenkins_project]) 87 | end 88 | it 'and only target_branch matches' do 89 | allow(jenkins_project).to receive(:merge_to?).and_return(true) 90 | expect(jenkins_project).not_to receive(:delete) 91 | subject.with(mr_details, [jenkins_project]) 92 | end 93 | it 'and only source_branch matches' do 94 | expect(jenkins_project).to receive(:matches?).and_return(true) 95 | expect(jenkins_project).not_to receive(:delete) 96 | subject.with(mr_details, [jenkins_project]) 97 | end 98 | it 'and project does not exists' do 99 | expect(jenkins_project).not_to receive(:delete) 100 | subject.with(mr_details, [jenkins_project]) 101 | end 102 | end 103 | end 104 | 105 | end 106 | 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /spec/values/abstract_details_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe AbstractDetails do 5 | 6 | it '#repository_url is abstract' do 7 | expect { subject.repository_url }.to raise_exception(NameError) 8 | end 9 | 10 | it '#repository_name is abstract' do 11 | expect { subject.repository_name }.to raise_exception(NameError) 12 | end 13 | 14 | it '#repository_homepage is abstract' do 15 | expect { subject.repository_homepage }.to raise_exception(NameError) 16 | end 17 | 18 | it '#branch is abstract' do 19 | expect { subject.branch }.to raise_exception(NameError) 20 | end 21 | 22 | it 'makes branch safe' do 23 | allow(subject).to receive(:branch) { 'feature/cool' } 24 | expect(subject.safe_branch).to eq('feature_cool') 25 | end 26 | 27 | it '#payload returns empty hash' do 28 | expect(subject.payload).to eq({}) 29 | end 30 | 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/values/commit_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe Commit do 5 | let(:subject) { Commit.new('http://localhost/diaspora/diaspora/commits/450d0de7532f', 'Update Catalan translation') } 6 | 7 | it 'has commit url' do 8 | expect(subject).to respond_to(:url) 9 | expect(subject.url).to eq('http://localhost/diaspora/diaspora/commits/450d0de7532f') 10 | end 11 | 12 | it 'has commit message' do 13 | expect(subject).to respond_to(:message) 14 | expect(subject.message).to eq('Update Catalan translation') 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/values/merge_request_details_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe MergeRequestDetails do 5 | 6 | include_context 'mr_details' 7 | let (:subject) { MergeRequestDetails.new(mr_payload) } 8 | 9 | context 'when initializing' do 10 | it 'requires payload data' do 11 | expect { MergeRequestDetails.new(nil) }.to raise_exception(ArgumentError) 12 | end 13 | it 'raise exception for cross-repo merge requests' do 14 | mr_payload['object_attributes']['target_project_id'] = '15' 15 | expect { MergeRequestDetails.new(mr_payload) }.to raise_exception(BadRequestException) 16 | end 17 | end 18 | 19 | it '#kind is merge request' do 20 | expect(subject.kind).to eq('merge_request') 21 | end 22 | 23 | context '#project_id' do 24 | it 'parsed from payload' do 25 | expect(subject.project_id).to eq('14') 26 | end 27 | 28 | it 'returns empty when no source project found' do 29 | mr_payload['object_attributes'].delete('source_project_id') 30 | mr_payload['object_attributes'].delete('target_project_id') 31 | expect(subject.project_id).to eq('') 32 | end 33 | end 34 | 35 | context do 36 | before :each do 37 | allow(subject).to receive(:get_project_details).once.and_return( { 38 | 'name' => 'diaspora' , 39 | 'web_url' => 'http://localhost/peronospora', 40 | 'ssh_url_to_repo' => 'git@localhost:peronospora.git' } ) 41 | end 42 | 43 | it '#repository_url returns ssh url for repository' do 44 | expect(subject.repository_url).to eq('git@example.com:awesome_space/awesome_project.git') 45 | end 46 | 47 | it '#repository_name returns repository name' do 48 | expect(subject.repository_name).to eq('Awesome Project') 49 | end 50 | 51 | it '#repository_homepage returns homepage for repository' do 52 | expect(subject.repository_homepage).to eq('http://example.com/awesome_space/awesome_project') 53 | end 54 | 55 | end 56 | 57 | context '#branch' do 58 | it 'returns source branch' do 59 | expect(subject.branch).to eq('ms-viewport') 60 | end 61 | 62 | it 'returns empty when no source branch found' do 63 | mr_payload['object_attributes'].delete('source_branch') 64 | expect(subject.branch).to eq('') 65 | end 66 | end 67 | 68 | context '#target_project_id' do 69 | it 'parsed from payload' do 70 | expect(subject.target_project_id).to eq('14') 71 | end 72 | 73 | it 'returns empty when no target project found' do 74 | mr_payload['object_attributes'].delete('source_project_id') 75 | mr_payload['object_attributes'].delete('target_project_id') 76 | expect(subject.target_project_id).to eq('') 77 | end 78 | end 79 | 80 | context '#target_branch' do 81 | it 'parsed from payload' do 82 | expect(subject.target_branch).to eq('master') 83 | end 84 | 85 | it 'returns empty when no target branch found' do 86 | mr_payload['object_attributes'].delete('target_branch') 87 | expect(subject.target_branch).to eq('') 88 | end 89 | end 90 | 91 | context '#state' do 92 | it 'parsed from payload' do 93 | expect(subject.state).to eq('opened') 94 | end 95 | 96 | it 'returns empty when no state data found' do 97 | mr_payload['object_attributes'].delete('state') 98 | expect(subject.state).to eq('') 99 | end 100 | end 101 | 102 | context '#merge_status' do 103 | it 'parsed from payload' do 104 | expect(subject.merge_status).to eq('unchecked') 105 | end 106 | 107 | it 'returns empty when no merge status data found' do 108 | mr_payload['object_attributes'].delete('merge_status') 109 | expect(subject.merge_status).to eq('') 110 | end 111 | end 112 | 113 | context '#repository_url' do 114 | it 'returns ssh url for repository' do 115 | expect(subject.repository_url).to eq('git@example.com:awesome_space/awesome_project.git') 116 | end 117 | end 118 | 119 | context '#repository_name' do 120 | it 'returns repository name' do 121 | expect(subject.repository_name).to eq('Awesome Project') 122 | end 123 | end 124 | 125 | context '#repository_homepage' do 126 | it 'returns homepage for repository' do 127 | expect(subject.repository_homepage).to eq('http://example.com/awesome_space/awesome_project') 128 | end 129 | end 130 | 131 | end 132 | end 133 | -------------------------------------------------------------------------------- /spec/values/parameters_request_details_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe ParametersRequestDetails do 5 | let (:parameters) { JSON.parse(File.read('spec/fixtures/default_params.json')) } 6 | let (:subject) { ParametersRequestDetails.new(parameters) } 7 | 8 | context 'when initializing' do 9 | it 'requires parameters' do 10 | expect { ParametersRequestDetails.new(nil) }.to raise_exception(ArgumentError) 11 | end 12 | end 13 | 14 | it '#kind is parameter request' do 15 | expect(subject.kind).to eq('parameters') 16 | end 17 | 18 | context 'with repository url' do 19 | it 'extracts from parameters' do 20 | expect(subject.repository_url).to eq('http://localhost/mike/peronospora') 21 | end 22 | 23 | it 'returns empty when no repository details found' do 24 | parameters.delete('repo_url') 25 | expect(subject.repository_url).to eq('') 26 | end 27 | end 28 | 29 | context 'with repository name' do 30 | it 'extracts from parameters' do 31 | expect(subject.repository_name).to eq('Peronospora') 32 | end 33 | 34 | it 'returns empty when no repository details found' do 35 | parameters.delete('repo_name') 36 | expect(subject.repository_name).to eq('') 37 | end 38 | end 39 | 40 | context 'with repository homepage' do 41 | it 'extracts from parameters' do 42 | expect(subject.repository_homepage).to eq('http://localhost/mike/peronospora') 43 | end 44 | 45 | it 'returns empty when no repository details found' do 46 | parameters.delete('repo_homepage') 47 | expect(subject.repository_homepage).to eq('') 48 | end 49 | end 50 | 51 | context 'with branch' do 52 | it 'extracts full branch name from payeload' do 53 | expect(subject.full_branch_reference).to eq('refs/heads/master') 54 | end 55 | 56 | it 'returns empty full branch name when no branch reference data found' do 57 | parameters.delete('ref') 58 | expect(subject.full_branch_reference).to eq('') 59 | end 60 | end 61 | 62 | context 'with delete branch commit' do 63 | it 'defaults to false' do 64 | expect(subject.delete_branch_commit?).not_to be 65 | end 66 | 67 | it 'detects delete branch commit' do 68 | parameters['delete_branch_commit'] = true 69 | expect(subject.delete_branch_commit?).to be 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/values/payload_request_details_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe PayloadRequestDetails do 5 | include_context 'details' 6 | let (:subject) { PayloadRequestDetails.new(payload) } 7 | 8 | context 'when initializing' do 9 | it 'requires payload data' do 10 | expect { PayloadRequestDetails.new(nil) }.to raise_exception(ArgumentError) 11 | end 12 | end 13 | 14 | it '#kind is web hook' do 15 | expect(subject.kind).to eq('push') 16 | end 17 | 18 | context 'with repository url' do 19 | it 'extracts from payload' do 20 | expect(subject.repository_url).to eq('git@example.com:mike/diaspora.git') 21 | end 22 | 23 | it 'returns empty when no repository details found' do 24 | payload.delete('repository') 25 | expect(subject.repository_url).to eq('') 26 | end 27 | end 28 | 29 | context 'with repository name' do 30 | it 'extracts from payload' do 31 | expect(subject.repository_name).to eq('Diaspora') 32 | end 33 | 34 | it 'returns empty when no repository details found' do 35 | payload.delete('repository') 36 | expect(subject.repository_name).to eq('') 37 | end 38 | end 39 | 40 | context 'with repository homepage' do 41 | it 'extracts from payload' do 42 | expect(subject.repository_homepage).to eq('http://example.com/mike/diaspora') 43 | end 44 | 45 | it 'returns empty when no repository details found' do 46 | payload.delete('repository') 47 | expect(subject.repository_homepage).to eq('') 48 | end 49 | end 50 | 51 | context 'with full branch reference' do 52 | it 'extracts from payload' do 53 | expect(subject.full_branch_reference).to eq('refs/heads/master') 54 | end 55 | 56 | it 'returns empty when no branch reference data found' do 57 | payload.delete('ref') 58 | expect(subject.full_branch_reference).to eq('') 59 | end 60 | end 61 | 62 | context 'with delete branch commit' do 63 | it 'defaults to false' do 64 | expect(subject.delete_branch_commit?).not_to be 65 | end 66 | 67 | it 'detects delete branch commit' do 68 | payload['after'] = '00000000000000000' 69 | expect(subject.delete_branch_commit?).to be 70 | end 71 | end 72 | 73 | context 'with commits' do 74 | it 'extracts from payload' do 75 | expect(subject.commits.size).to eq(2) 76 | 77 | expect(subject.commits[0].url).to eq('http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327') 78 | expect(subject.commits[0].message).to eq('Update Catalan translation to e38cb41.') 79 | 80 | expect(subject.commits[1].url).to eq('http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7') 81 | expect(subject.commits[1].message).to eq('fixed readme') 82 | end 83 | 84 | it 'memoizes the result' do 85 | expect(payload).to receive(:[]).with('commits').once.and_return(payload['commits']) 86 | expect(payload).to receive(:[]).with('object_kind').twice.and_return('webhook') 87 | 10.times { subject.commits } 88 | end 89 | 90 | it 'returns empty array when no commits details found' do 91 | payload.delete('commits') 92 | expect(subject.commits).to eq([]) 93 | end 94 | end 95 | 96 | context 'with payload' do 97 | it 'returns it' do 98 | expect(subject.payload).to eq(payload) 99 | end 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /spec/values/repository_uri_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe RepositoryUri do 5 | let(:subject) { RepositoryUri.new('http://localhost/diaspora/diaspora') } 6 | 7 | context 'with attributes' do 8 | it 'has url' do 9 | expect(subject.url).to eq('http://localhost/diaspora/diaspora') 10 | end 11 | 12 | it 'has host' do 13 | expect(subject.host).to eq('localhost') 14 | end 15 | end 16 | 17 | context 'when matching against other uri' do 18 | context 'it is matching' do 19 | it 'with no uris' do 20 | expect(RepositoryUri.new(nil).matches?(nil)).to be 21 | end 22 | 23 | it 'with local file system paths' do 24 | other = URIish.new('/git/foo.git') 25 | expect(RepositoryUri.new('/git/foo.git').matches?(other)).to be 26 | end 27 | 28 | it 'with regular uris' do 29 | other = URIish.new('http://localhost/diaspora/diaspora') 30 | expect(subject.matches?(other)).to be 31 | end 32 | end 33 | 34 | context 'it is not matching' do 35 | it 'when repo uris do not match' do 36 | other = URIish.new('http://localhost/diaspora/other') 37 | expect(subject.matches?(other)).not_to be 38 | end 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/values/request_details_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe RequestDetails do 5 | context 'with validation' do 6 | before :each do 7 | allow(subject).to receive(:kind) { 'webhook' } 8 | end 9 | 10 | it 'is valid when repository url is present' do 11 | allow(subject).to receive(:repository_url) { 'http://repo.url' } 12 | expect(subject.valid?).to be 13 | end 14 | 15 | it 'is not valid when repository url is missing' do 16 | [nil, '', " \n "].each do |repository_url| 17 | allow(subject).to receive(:repository_url) { repository_url } 18 | expect(subject.valid?).not_to be 19 | end 20 | end 21 | end 22 | 23 | context 'with repository uri' do 24 | it 'returns uri regardless of repository url' do 25 | ['http://repo.url', nil, '', ' \n '].each do |repository_url| 26 | allow(subject).to receive(:repository_url) { repository_url } 27 | expect(subject.repository_uri).to be_kind_of(RepositoryUri) 28 | end 29 | end 30 | end 31 | 32 | context 'with full branch name' do 33 | it 'expects to implemented in concrete implementation' do 34 | expect { subject.full_branch_reference }.to raise_exception(NameError) 35 | end 36 | end 37 | 38 | context 'with branch' do 39 | it 'extracts branch name from payload' do 40 | allow(subject).to receive(:full_branch_reference) { 'refs/heads/master' } 41 | expect(subject.branch).to eq('master') 42 | end 43 | 44 | it 'returns empty branch name when no branch reference data found' do 45 | allow(subject).to receive(:full_branch_reference) { nil } 46 | expect(subject.branch).to eq('') 47 | end 48 | 49 | it 'makes branch safe' do 50 | allow(subject).to receive(:full_branch_reference) { 'refs/heads/feature/cool' } 51 | expect(subject.safe_branch).to eq('feature_cool') 52 | end 53 | 54 | it 'returns nil when no payload present' do 55 | allow(subject).to receive(:full_branch_reference) { nil } 56 | expect(subject.branch).to eq('') 57 | end 58 | 59 | it 'removes refs, heads and tags from result' do 60 | refs = ['ref', 'refs'] 61 | heads = ['head', 'heads'] 62 | refs.product(heads).each do |combination| 63 | allow(subject).to receive(:full_branch_reference) { "#{combination.join('/')}/master" } 64 | expect(subject.branch).to eq('master') 65 | end 66 | end 67 | 68 | it 'detects non refs and non heads' do 69 | ['refref', 'headhead', 'tagtag'].each do |ref| 70 | allow(subject).to receive(:full_branch_reference) { ref } 71 | expect(subject.branch).to eq(ref) 72 | end 73 | end 74 | 75 | it 'returns branch name' do 76 | allow(subject).to receive(:full_branch_reference) { 'refs/heads/master' } 77 | expect(subject.branch).to eq('master') 78 | end 79 | 80 | it 'respects nested branches' do 81 | allow(subject).to receive(:full_branch_reference) { 'refs/heads/feature/new_hot_feature' } 82 | expect(subject.branch).to eq('feature/new_hot_feature') 83 | end 84 | 85 | it 'returns no tagname' do 86 | allow(subject).to receive(:full_branch_reference) { 'refs/heads/master' } 87 | expect(subject.tagname).to eq(nil) 88 | end 89 | end 90 | 91 | context 'with tag' do 92 | it 'extracts tag name from payload' do 93 | allow(subject).to receive(:full_branch_reference) { 'refs/tags/v1.0.0' } 94 | expect(subject.tagname).to eq('v1.0.0') 95 | end 96 | end 97 | 98 | context 'with delete branch commit' do 99 | it 'expects to implemented in concrete implementation' do 100 | expect { subject.repository_delete_branch_commit? }.to raise_exception(NameError) 101 | end 102 | end 103 | 104 | context 'with commits' do 105 | it 'defaults to empty array' do 106 | expect(subject.commits).to eq([]) 107 | end 108 | 109 | it 'requires it to be an array' do 110 | allow(subject).to receive(:get_commits) { 'invalid commits' } 111 | expect { subject.commits }.to raise_exception(ArgumentError) 112 | end 113 | end 114 | 115 | context 'with commits count' do 116 | it 'returns commits size' do 117 | allow(subject).to receive(:commits) { [double, double, double] } 118 | expect(subject.commits_count).to eq(3) 119 | end 120 | 121 | it 'defaults to 0 when no commits present' do 122 | allow(subject).to receive(:commits) { nil } 123 | expect(subject.commits_count).to eq(0) 124 | end 125 | end 126 | 127 | context 'with payload' do 128 | it 'defaults to empty hash' do 129 | expect(subject.payload).to eq({}) 130 | end 131 | 132 | it 'requires it to be a hash' do 133 | allow(subject).to receive(:get_payload) { 'invalid payload' } 134 | expect { subject.payload }.to raise_exception(ArgumentError) 135 | end 136 | end 137 | 138 | context 'with flat payload' do 139 | param_details = { 140 | repository_url: 'git@example.com:diaspora/diaspora.git', 141 | repository_name: 'Diaspora', 142 | repository_homepage: 'http://example.com/diaspora/diaspora', 143 | full_branch_reference: 'refs/heads/master', 144 | branch: 'master' 145 | } 146 | 147 | include_context 'details' 148 | 149 | before(:each) do 150 | param_details.each { |detail, value| allow(subject).to receive(detail) { value } } 151 | allow(subject).to receive(:get_payload) { payload } 152 | end 153 | 154 | it 'returns flattened payload' do 155 | expect(subject.flat_payload[%w(repository name).join(FlatKeysHash::FLATTENED_KEYS_DELIMITER)]).to eq('Diaspora') 156 | end 157 | 158 | param_details.each do |detail, value| 159 | it "appends :#{detail} from details" do 160 | expect(subject.flat_payload[detail.to_s]).to eq(value) 161 | end 162 | end 163 | 164 | it 'tagname absent' do 165 | expect(subject.flat_payload.keys).not_to include( 'tagname' ) 166 | end 167 | 168 | it 'memoizes flattened payload' do 169 | expect(payload).to receive(:to_flat_keys).once.and_return({}) 170 | 10.times { subject.flat_payload } 171 | end 172 | 173 | it 'returns tagname if present' do 174 | allow(subject).to receive('full_branch_reference') { 'refs/tags/v1.0.0' } 175 | expect(subject.flat_payload['tagname']).to eq('v1.0.0') 176 | end 177 | end 178 | end 179 | end 180 | -------------------------------------------------------------------------------- /spec/values/scm_data_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module GitlabWebHook 4 | describe ScmData do 5 | let(:details) { double(RequestDetails, repository_name: 'discourse', branch: 'features_meta', safe_branch: 'features_meta') } 6 | let(:remote_config) { double(UserRemoteConfig, getUrl: 'http://localhost/diaspora/diaspora', getName: 'Diaspora', getCredentialsId: 'id', getRefspec: ['+refs/heads/*:refs/remotes/origin/*']) } 7 | let(:source_scm) { double(GitSCM, getScmName: 'git', getUserRemoteConfigs: [remote_config, double(UserRemoteConfig)]).as_null_object } 8 | let(:subject) { described_class.new(source_scm, details) } 9 | 10 | context 'when building' do 11 | it 'it uses source scm first configuration' do 12 | expect(subject.url).to eq('http://localhost/diaspora/diaspora') 13 | end 14 | 15 | it 'takes url from source scm configuration' do 16 | expect(subject.url).to eq('http://localhost/diaspora/diaspora') 17 | end 18 | 19 | it 'takes name from source scm configuration' do 20 | expect(subject.name).to eq('Diaspora') 21 | end 22 | 23 | it 'takes credentials from source scm configuration' do 24 | expect(subject.credentials).to eq('id') 25 | end 26 | 27 | it 'takes refspec from source scm configuration' do 28 | expect(subject.refspec).to eq(['+refs/heads/*:refs/remotes/origin/*']) 29 | end 30 | 31 | context 'when determining branch' do 32 | let(:branchspec) { double(BranchSpec) } 33 | let(:java_branchspec) { double(BranchSpec) } 34 | context 'without source scm name' do 35 | before(:each) do 36 | expect(branchspec).to receive(:java_object).and_return(java_branchspec) 37 | expect(BranchSpec).to receive(:new).with('origin/features_meta').and_return(branchspec) 38 | end 39 | it 'uses prefixed details branch' do 40 | expect(remote_config).to receive(:getName).and_return(nil) 41 | expect(subject.branchlist).to eq([java_branchspec]) 42 | end 43 | end 44 | 45 | context 'with source scm name present' do 46 | before(:each) do 47 | expect(branchspec).to receive(:java_object).and_return(java_branchspec) 48 | expect(BranchSpec).to receive(:new).with('Diaspora/features_meta').and_return(branchspec) 49 | end 50 | it 'prefixes details branch' do 51 | expect(subject.branchlist).to eq([java_branchspec]) 52 | end 53 | end 54 | end 55 | 56 | context 'when validating' do 57 | context 'with url present' do 58 | it 'does not raise exception' do 59 | expect { described_class.new(source_scm, details) }.not_to raise_exception 60 | end 61 | end 62 | 63 | context 'without url' do 64 | it 'raises appropriate exception' do 65 | expect(remote_config).to receive(:getUrl).and_return(nil) 66 | expect { described_class.new(source_scm, details) }.to raise_exception(ConfigurationException) 67 | end 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /views/gitlab_notifier/global.erb: -------------------------------------------------------------------------------- 1 | <% 2 | f = taglib("/lib/form") 3 | 4 | f.section(:title => "Gitlab Notifier Configuration") do 5 | 6 | f.entry :title => 'Gitlab URL', :field => 'gitlab_url' do 7 | f.textbox :default => "#{descriptor.gitlab_url}" 8 | end 9 | 10 | f.entry :title => 'Gitlab token', :field => 'token' do 11 | f.password :value => "#{descriptor.token}" 12 | end 13 | 14 | f.entry :title => 'Send notification as commit statuses', :field => 'commit_status' do 15 | f.checkbox :checked => "#{descriptor.commit_status?}" 16 | end 17 | 18 | f.entry :title => 'Only submit status for merge projects', :field => 'mr_status_only' do 19 | f.checkbox :checked => "#{descriptor.mr_status_only?}" 20 | end 21 | 22 | end 23 | %> 24 | -------------------------------------------------------------------------------- /views/gitlab_notifier/help-commit_status.erb: -------------------------------------------------------------------------------- 1 | NOTE: This functionality required a patched gitlab installation.
2 | Submit gitlab notification as commit statuses instead of standard gitlab notes. When enabled, running status is also notified at build start. 3 | -------------------------------------------------------------------------------- /views/gitlab_notifier/help-gitlab_url.erb: -------------------------------------------------------------------------------- 1 | URL to your gitlab instance (e.g. https://gitlab.example.com). 2 | -------------------------------------------------------------------------------- /views/gitlab_notifier/help-mr_status_only.erb: -------------------------------------------------------------------------------- 1 | Submit status only when handling merge requests. If unmarked, notes will also be sent for individual commits built by jenkins. 2 | -------------------------------------------------------------------------------- /views/gitlab_notifier/help-token.erb: -------------------------------------------------------------------------------- 1 | Private token for unauthenticated API use. It can be obtained from account settings or posting to /session path on api url. 2 | -------------------------------------------------------------------------------- /views/gitlab_web_hook_root_action/global.erb: -------------------------------------------------------------------------------- 1 | <% 2 | f = taglib("/lib/form") 3 | f.section(:title => "Gitlab Web Hook") do 4 | 5 | f.entry :title => 'Ignore actions by git usernames', :field => 'ignore_users' do 6 | f.textbox :default => "#{descriptor.ignore_users}" 7 | end 8 | 9 | f.entry :title => "Create new projects for merge requests", :field => 'merge_request_processing' do 10 | f.checkbox :checked => "#{descriptor.merge_request_processing?}" 11 | end 12 | 13 | f.entry :title => "Trigger build also when pushing to merged branches", :field => 'merged_branch_triggering' do 14 | f.checkbox :checked => "#{descriptor.merged_branch_triggering?}" 15 | end 16 | 17 | f.optionalBlock :title => "Automatic project creation", :name => 'automatic_project_creation', :checked => "#{descriptor.automatic_project_creation?}" do 18 | f.entry :title => 'Project master branch', :field => 'master_branch' do 19 | f.textbox :default => "#{descriptor.master_branch}" 20 | end 21 | 22 | f.entry :title => 'Use master project name', :field => 'use_master_project_name' do 23 | f.checkbox :checked => "#{descriptor.use_master_project_name?}" 24 | end 25 | 26 | f.entry :title => 'Description', :field => 'description' do 27 | f.textbox :default => "#{descriptor.description}" 28 | end 29 | 30 | end 31 | 32 | f.advanced do 33 | 34 | f.block do 35 | %>

36 | Template based job creation allows to automatically create projects for repositories not already built by jenkins. 37 | An already existing jenkins project will be used as base, and different kind of string matching is performed against the repository name. 38 |

<% 39 | end 40 | 41 | f.entry :title => 'Template based job creation', :description => 'The incoming repository name leading text is compared with the match string' do 42 | 43 | f.repeatable :items => descriptor.templated_jobs.to_a, :var => 'templates', :add => 'Add new template' do 44 | %><% 45 | f.entry :title => 'Match string', :field => 'string' do 46 | f.textbox :default => "#{templates.nil? ? '' : templates.first }" 47 | end 48 | f.entry :title => 'Base project', :field => 'project' do 49 | f.textbox :default => "#{templates.nil? ? '' : templates.last }" 50 | end 51 | %>
<%f.repeatableDeleteButton%>
<% 52 | end 53 | 54 | end 55 | 56 | f.entry :title => 'Templates for gitlab groups', :description => 'Full string comparison is performed with the incoming repository' do 57 | f.repeatable :items => descriptor.templated_groups.to_a, :var => 'group_templates', :add => 'Add new group template' do 58 | %><% 59 | f.entry :title => 'GitLab group/namespace', :field => 'string' do 60 | f.textbox :default => "#{group_templates.nil? ? '' : group_templates.first }" 61 | end 62 | f.entry :title => 'Base project', :field => 'project' do 63 | f.textbox :default => "#{group_templates.nil? ? '' : group_templates.last }" 64 | end 65 | %>
<%f.repeatableDeleteButton%>
<% 66 | end 67 | end 68 | 69 | f.entry :title => 'Last resort template', :field => 'template' do 70 | f.textbox :default => "#{descriptor.template_fallback}" 71 | end 72 | 73 | end 74 | 75 | end 76 | %> 77 | -------------------------------------------------------------------------------- /views/gitlab_web_hook_root_action/help-automatic_project_creation.erb: -------------------------------------------------------------------------------- 1 | Automatically create projects when payload doesn't match an existing one. 2 | -------------------------------------------------------------------------------- /views/gitlab_web_hook_root_action/help-description.erb: -------------------------------------------------------------------------------- 1 | Description string to use in automatically created jobs. 2 | -------------------------------------------------------------------------------- /views/gitlab_web_hook_root_action/help-ignore_users.erb: -------------------------------------------------------------------------------- 1 | Git usernames to ignore, separated by commas. -------------------------------------------------------------------------------- /views/gitlab_web_hook_root_action/help-master_branch.erb: -------------------------------------------------------------------------------- 1 | Exact branch name to select the project to copy new projects from. 2 | -------------------------------------------------------------------------------- /views/gitlab_web_hook_root_action/help-merge_request_processing.erb: -------------------------------------------------------------------------------- 1 | Automatically create new projects when merge request payload is received. 2 | -------------------------------------------------------------------------------- /views/gitlab_web_hook_root_action/help-merged_branch_triggering.erb: -------------------------------------------------------------------------------- 1 | Projects where branches are merged can be triggered only when the tracked 2 | branch is pushed, but also when the merged branch gets pushed. 3 | -------------------------------------------------------------------------------- /views/gitlab_web_hook_root_action/help-use_master_project_name.erb: -------------------------------------------------------------------------------- 1 | Prefix the created job with the name of the one it was copied from. If unchecked, the name of the repository is used as prefix. 2 | -------------------------------------------------------------------------------- /work/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 4 | NORMAL 5 | true 6 | 7 | hudson.model.Hudson.Administer:anonymous 8 | hudson.model.Item.Read:anonymous 9 | hudson.model.View.Read:anonymous 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /work/gitlab-hook-GitlabNotifier.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | http://localhost:4567 4 | 2GJJSdNJdxFnMX9vmMHh 5 | false 6 | false 7 | 8 | -------------------------------------------------------------------------------- /work/gitlab-hook-GitlabWebHookRootAction.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | true 6 | master 7 | false 8 | Automatically created by Gitlab Web Hook plugin 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /work/hudson.plugins.git.GitSCM.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 4 | jenkins 5 | jenkins@localhost 6 | false 7 | 8 | -------------------------------------------------------------------------------- /work/jenkins.model.JenkinsLocationConfiguration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | root@localhost 4 | http://localhost:8080/ 5 | 6 | -------------------------------------------------------------------------------- /work/jobs/multiscm/config.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | 8 | 9 | 10 | 2 11 | 12 | 13 | localhost:%{multiscmdir1} 14 | 15 | 16 | 17 | 18 | */master 19 | 20 | 21 | false 22 | 23 | 24 | 25 | 26 | 2 27 | 28 | 29 | localhost:%{multiscmdir2} 30 | 31 | 32 | 33 | 34 | */master 35 | 36 | 37 | false 38 | 39 | 40 | 41 | 42 | 43 | false 44 | false 45 | false 46 | false 47 | 48 | false 49 | 50 | 51 | 52 | 53 | gitlab-hook 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /work/jobs/simplejob/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | 8 | 2 9 | 10 | 11 | 12 | 13 | 14 | */master 15 | 16 | 17 | false 18 | 19 | 20 | 21 | true 22 | true 23 | false 24 | false 25 | 26 | false 27 | 28 | 29 | 30 | 31 | gitlab-hook 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /work/jobs/specificjob/config.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | 8 | 2 9 | 10 | 11 | localhost:%{specificdir} 12 | 13 | 14 | 15 | 16 | ** 17 | 18 | 19 | false 20 | 21 | 22 | 23 | true 24 | false 25 | false 26 | 27 | false 28 | 29 | 30 | set | grep -i git 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /work/jobs/subdirjob/config.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | 8 | 2 9 | 10 | 11 | localhost:%{xtrarepodir} 12 | 13 | 14 | 15 | 16 | */master 17 | 18 | 19 | false 20 | 21 | 22 | 23 | clone_subdir 24 | 25 | 26 | 27 | true 28 | false 29 | false 30 | false 31 | 32 | false 33 | 34 | 35 | 36 | 37 | gitlab-hook 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /work/jobs/tagbuilder/config.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | 8 | 9 | 10 | TAGNAME 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 2 19 | 20 | 21 | localhost:%{tagsrepodir} 22 | 23 | 24 | 25 | 26 | refs/tags/${TAGNAME} 27 | 28 | 29 | false 30 | 31 | 32 | 33 | true 34 | false 35 | false 36 | false 37 | 38 | false 39 | 40 | 41 | 42 | 43 | gitlab-hook 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | --------------------------------------------------------------------------------