From 84b355077f99b4cf3c14624e9b77c7c57054a36f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 14 May 2014 16:41:03 +0300 Subject: [PATCH 1/6] Add Jenkins CI service Signed-off-by: Dmitriy Zaporozhets --- app/models/project.rb | 8 +++ .../project_services/jenkins_service.rb | 52 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 app/models/project_services/jenkins_service.rb diff --git a/app/models/project.rb b/app/models/project.rb index 33b5ad0122..682db5d1fe 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -56,6 +56,8 @@ class Project < ActiveRecord::Base has_one :git_hook, dependent: :destroy has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id' + + # Project services has_one :gitlab_ci_service, dependent: :destroy has_one :campfire_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy @@ -66,6 +68,8 @@ class Project < ActiveRecord::Base has_one :gemnasium_service, dependent: :destroy has_one :slack_service, dependent: :destroy has_one :jira_service, dependent: :destroy + has_one :jenkins_service, dependent: :destroy + has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link # Merge Requests for target project should be removed with it @@ -318,7 +322,11 @@ class Project < ActiveRecord::Base end def available_services_names +<<<<<<< HEAD %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack jira) +======= + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack jenkins) +>>>>>>> Add Jenkins CI service end def gitlab_ci? diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb new file mode 100644 index 0000000000..9e1f94dff6 --- /dev/null +++ b/app/models/project_services/jenkins_service.rb @@ -0,0 +1,52 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# token :string(255) +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# project_url :string(255) +# subdomain :string(255) +# room :string(255) +# recipients :text +# api_key :string(255) +# + +class JenkinsService < Service + attr_accessible :project_url + + validates :project_url, presence: true, if: :activated? + + delegate :execute, to: :service_hook, prefix: nil + + after_save :compose_service_hook, if: :activated? + + def compose_service_hook + hook = service_hook || build_service_hook + hook.url = [project_url, "/gitlab/build_now"].join("") + hook.save + end + + def title + 'Jenkins CI' + end + + def description + 'An extendable open source continuous integration server' + end + + def to_param + 'jenkins' + end + + def fields + [ + { type: 'text', name: 'project_url', placeholder: 'Jenkins server URL like http://jenkins.example.com/' } + ] + end +end From e6d2c02e674ab1cfdeb2e0ba4fda542616ae8e0e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 15 May 2014 10:34:35 +0300 Subject: [PATCH 2/6] Dirty implementastion of MR build status for Jenkins Signed-off-by: Dmitriy Zaporozhets --- .../projects/merge_requests_controller.rb | 2 +- app/helpers/merge_requests_helper.rb | 2 +- app/models/project.rb | 22 ++++++++++++---- .../project_services/jenkins_service.rb | 26 +++++++++++++++++++ .../projects/merge_requests/_show.html.haml | 2 +- .../show/_state_widget.html.haml | 2 +- db/schema.rb | 22 ++++++++-------- 7 files changed, 58 insertions(+), 20 deletions(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 96633d7fa7..5d0c14bf21 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -161,7 +161,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def ci_status - status = @merge_request.source_project.gitlab_ci_service.commit_status(merge_request.last_commit.sha) + status = @merge_request.source_project.ci_service.commit_status(merge_request.last_commit.sha) response = {status: status} render json: response diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 00ec34ae54..f3c3d43771 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -32,7 +32,7 @@ module MergeRequestsHelper end def ci_build_details_path merge_request - merge_request.source_project.gitlab_ci_service.build_page(merge_request.last_commit.sha) + merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha) end def merge_path_description(merge_request, separator) diff --git a/app/models/project.rb b/app/models/project.rb index 682db5d1fe..74359d65d6 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -322,17 +322,29 @@ class Project < ActiveRecord::Base end def available_services_names -<<<<<<< HEAD - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack jira) -======= - %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack jenkins) ->>>>>>> Add Jenkins CI service + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack jira jenkins) end def gitlab_ci? gitlab_ci_service && gitlab_ci_service.active end + def jenkins_enabled? + jenkins_service && jenkins_service.active + end + + def enabled_ci? + gitlab_ci? || jenkins_enabled? + end + + def ci_service + if gitlab_ci? + gitlab_ci_service + elsif jenkins_enabled? + jenkins_service + end + end + def jira_tracker? self.issues_tracker == "jira" end diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb index 9e1f94dff6..efa7f2100d 100644 --- a/app/models/project_services/jenkins_service.rb +++ b/app/models/project_services/jenkins_service.rb @@ -49,4 +49,30 @@ class JenkinsService < Service { type: 'text', name: 'project_url', placeholder: 'Jenkins server URL like http://jenkins.example.com/' } ] end + + def build_page sha + project_url + "/job/test1/scm/bySHA1/#{sha}" + end + + def commit_status sha + response = HTTParty.get(commit_status_path(sha), verify: false) + + if response.code == 200 + if response.include?('alt="Success"') + 'success' + elsif response.include?('alt="Failed"') + 'failed' + elsif response.include?('alt="In progress"') + 'running' + else + 'pending' + end + else + :error + end + end + + def commit_status_path sha + project_url + "/job/test1/scm/bySHA1/#{sha}" + end end diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index e36a48f62c..cb4a98452c 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -37,7 +37,7 @@ url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}", check_enable: #{@merge_request.unchecked? ? "true" : "false"}, url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}", - ci_enable: #{@project.gitlab_ci? ? "true" : "false"}, + ci_enable: #{@project.ci_service ? "true" : "false"}, current_status: "#{@merge_request.merge_status_name}", action: "#{controller.action_name}" }); diff --git a/app/views/projects/merge_requests/show/_state_widget.html.haml b/app/views/projects/merge_requests/show/_state_widget.html.haml index 80fe540489..bff5b36821 100644 --- a/app/views/projects/merge_requests/show/_state_widget.html.haml +++ b/app/views/projects/merge_requests/show/_state_widget.html.haml @@ -1,5 +1,5 @@ .panel.mr-state-widget.panel-default - - if @merge_request.source_project.gitlab_ci? && @commits.any? + - if @merge_request.source_project.ci_service && @commits.any? .panel-heading = render "projects/merge_requests/show/mr_ci" .panel-body diff --git a/db/schema.rb b/db/schema.rb index e7c0cf3221..3ba95c633c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -189,9 +189,9 @@ ActiveRecord::Schema.define(version: 20140513095908) do t.datetime "updated_at" t.string "type" t.string "description", default: "", null: false - t.string "avatar" t.string "ldap_cn" t.integer "ldap_access" + t.string "avatar" end add_index "namespaces", ["name"], name: "index_namespaces_on_name", using: :btree @@ -238,20 +238,20 @@ ActiveRecord::Schema.define(version: 20140513095908) do t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" - t.boolean "issues_enabled", default: true, null: false - t.boolean "wall_enabled", default: true, null: false - t.boolean "merge_requests_enabled", default: true, null: false - t.boolean "wiki_enabled", default: true, null: false + t.boolean "issues_enabled", default: true, null: false + t.boolean "wall_enabled", default: true, null: false + t.boolean "merge_requests_enabled", default: true, null: false + t.boolean "wiki_enabled", default: true, null: false t.integer "namespace_id" - t.string "issues_tracker", default: "gitlab", null: false + t.string "issues_tracker", default: "gitlab", null: false t.string "issues_tracker_id" - t.boolean "snippets_enabled", default: true, null: false + t.boolean "snippets_enabled", default: true, null: false t.datetime "last_activity_at" t.string "import_url" - t.integer "visibility_level", default: 0, null: false - t.boolean "archived", default: false, null: false + t.integer "visibility_level", default: 0, null: false + t.boolean "archived", default: false, null: false t.string "import_status" - t.float "repository_size", default: 0.0 + t.float "repository_size", default: 0.0 t.text "merge_requests_template" end @@ -357,6 +357,7 @@ ActiveRecord::Schema.define(version: 20140513095908) do t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" + t.datetime "last_credential_check_at" t.string "avatar" t.string "confirmation_token" t.datetime "confirmed_at" @@ -364,7 +365,6 @@ ActiveRecord::Schema.define(version: 20140513095908) do t.string "unconfirmed_email" t.boolean "hide_no_ssh_key", default: false t.string "website_url", default: "", null: false - t.datetime "last_credential_check_at" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree From cce27a6a009cf93e0dcc8fa3f2f7a8e3fcd828dd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 15 May 2014 11:06:17 +0300 Subject: [PATCH 3/6] Add parent CiService class for easy add of new services Signed-off-by: Dmitriy Zaporozhets --- app/models/project_services/ci_service.rb | 34 +++++++++++++++++++ .../project_services/gitlab_ci_service.rb | 2 +- .../project_services/jenkins_service.rb | 8 ++--- app/models/service.rb | 4 +++ 4 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 app/models/project_services/ci_service.rb diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb new file mode 100644 index 0000000000..fd34a2a35e --- /dev/null +++ b/app/models/project_services/ci_service.rb @@ -0,0 +1,34 @@ +# Base class for CI services +# List methods you need to implement to get your CI service +# working with GitLab Merge Requests +class CiService < Service + def category + :ci + end + + # Return complete url to build page + # + # Ex. + # http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c + # + def build_page(sha) + # implement inside child + end + + # Return string with build status or :error symbol + # + # Allowed states: 'success', 'failed', 'running', 'pending' + # + # + # Ex. + # @service.commit_status('13be4ac') + # # => 'success' + # + # @service.commit_status('2abe4ac') + # # => 'running' + # + # + def commit_status(sha) + # implement inside child + end +end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index ed1944f337..ef395e0ec6 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -17,7 +17,7 @@ # api_key :string(255) # -class GitlabCiService < Service +class GitlabCiService < CiService attr_accessible :project_url validates :project_url, presence: true, if: :activated? diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb index efa7f2100d..c8cac62a01 100644 --- a/app/models/project_services/jenkins_service.rb +++ b/app/models/project_services/jenkins_service.rb @@ -17,7 +17,7 @@ # api_key :string(255) # -class JenkinsService < Service +class JenkinsService < CiService attr_accessible :project_url validates :project_url, presence: true, if: :activated? @@ -55,7 +55,7 @@ class JenkinsService < Service end def commit_status sha - response = HTTParty.get(commit_status_path(sha), verify: false) + response = HTTParty.get(build_page(sha), verify: false) if response.code == 200 if response.include?('alt="Success"') @@ -71,8 +71,4 @@ class JenkinsService < Service :error end end - - def commit_status_path sha - project_url + "/job/test1/scm/bySHA1/#{sha}" - end end diff --git a/app/models/service.rb b/app/models/service.rb index ea00039521..f0b65c7603 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -33,6 +33,10 @@ class Service < ActiveRecord::Base active end + def category + :common + end + def title # implement inside child end From b16d77c3e04cc34ea6258f4ba2f7f6d27bcfb418 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 15 May 2014 11:14:43 +0300 Subject: [PATCH 4/6] Make it easier to add CI services Signed-off-by: Dmitriy Zaporozhets --- app/models/project.rb | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 74359d65d6..813ae4fa6c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -58,6 +58,7 @@ class Project < ActiveRecord::Base has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id' # Project services + has_many :services has_one :gitlab_ci_service, dependent: :destroy has_one :campfire_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy @@ -329,20 +330,12 @@ class Project < ActiveRecord::Base gitlab_ci_service && gitlab_ci_service.active end - def jenkins_enabled? - jenkins_service && jenkins_service.active - end - - def enabled_ci? - gitlab_ci? || jenkins_enabled? + def ci_services + services.select { |service| service.category == :ci } end def ci_service - if gitlab_ci? - gitlab_ci_service - elsif jenkins_enabled? - jenkins_service - end + services.select(&:activated?).first end def jira_tracker? From afe1ff413dd4a6dd635afd26f7b57145abe4ad2b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 15 May 2014 11:20:25 +0300 Subject: [PATCH 5/6] Specify project URL for Jenkins service Signed-off-by: Dmitriy Zaporozhets --- app/models/project.rb | 2 +- app/models/project_services/jenkins_service.rb | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 813ae4fa6c..4389188811 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -335,7 +335,7 @@ class Project < ActiveRecord::Base end def ci_service - services.select(&:activated?).first + @ci_service ||= services.select(&:activated?).first end def jira_tracker? diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb index c8cac62a01..fd7a3ef0a9 100644 --- a/app/models/project_services/jenkins_service.rb +++ b/app/models/project_services/jenkins_service.rb @@ -28,7 +28,8 @@ class JenkinsService < CiService def compose_service_hook hook = service_hook || build_service_hook - hook.url = [project_url, "/gitlab/build_now"].join("") + jenkins_url = project_url.sub(/job\/.*/, '') + hook.url = jenkins_url + "/gitlab/build_now" hook.save end @@ -46,12 +47,12 @@ class JenkinsService < CiService def fields [ - { type: 'text', name: 'project_url', placeholder: 'Jenkins server URL like http://jenkins.example.com/' } + { type: 'text', name: 'project_url', placeholder: 'Jenkins project URL like http://jenkins.example.com/job/my-project/' } ] end def build_page sha - project_url + "/job/test1/scm/bySHA1/#{sha}" + project_url + "/scm/bySHA1/#{sha}" end def commit_status sha From 90342c9e9e01864cfd0cd30f8c1a8aae34391de2 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 15 May 2014 11:28:01 +0300 Subject: [PATCH 6/6] Add help section for services Signed-off-by: Dmitriy Zaporozhets --- app/models/project_services/jenkins_service.rb | 4 ++++ app/models/service.rb | 4 ++++ app/views/projects/services/_form.html.haml | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb index fd7a3ef0a9..f500dd4b3a 100644 --- a/app/models/project_services/jenkins_service.rb +++ b/app/models/project_services/jenkins_service.rb @@ -41,6 +41,10 @@ class JenkinsService < CiService 'An extendable open source continuous integration server' end + def help + 'You must have installed GitLab Hook plugin into Jenkins.' + end + def to_param 'jenkins' end diff --git a/app/models/service.rb b/app/models/service.rb index f0b65c7603..d655937079 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -45,6 +45,10 @@ class Service < ActiveRecord::Base # implement inside child end + def help + # implement inside child + end + def to_param # implement inside child end diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 46f6dd5257..ddd8bbe457 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -18,6 +18,10 @@ %li= msg + - if @service.help.present? + .bs-callout + = @service.help + .form-group = f.label :active, "Active", class: "control-label" .col-sm-10