mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-15 07:36:41 +10:00
Merge branch 'jira_service' into 'master'
Close Jira issues with commit messages and merge requests
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
v 6.9.0
|
||||
- Support Jira ticket mentions in format JIRA-123
|
||||
- Add support for closing Jira tickets with commits and MR
|
||||
|
||||
v 6.8.0
|
||||
- Customise sign-in page with custom text and logo
|
||||
|
||||
@@ -64,6 +64,7 @@ class Project < ActiveRecord::Base
|
||||
has_one :assembla_service, dependent: :destroy
|
||||
has_one :gemnasium_service, dependent: :destroy
|
||||
has_one :slack_service, dependent: :destroy
|
||||
has_one :jira_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
|
||||
@@ -316,13 +317,17 @@ class Project < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def available_services_names
|
||||
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack)
|
||||
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack jira)
|
||||
end
|
||||
|
||||
def gitlab_ci?
|
||||
gitlab_ci_service && gitlab_ci_service.active
|
||||
end
|
||||
|
||||
def jira_tracker?
|
||||
self.issues_tracker == "jira"
|
||||
end
|
||||
|
||||
# For compatibility with old code
|
||||
def code
|
||||
path
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# == 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)
|
||||
# username :string(255)
|
||||
# password :string(255)
|
||||
# api_version :string(255)
|
||||
|
||||
class JiraService < Service
|
||||
|
||||
include HTTParty
|
||||
attr_accessible :project_url, :username, :password, :api_version
|
||||
|
||||
validates :username, :password, presence: true, if: :activated?
|
||||
before_validation :set_api_version
|
||||
|
||||
def title
|
||||
'JIRA'
|
||||
end
|
||||
|
||||
def description
|
||||
'Bug, issue tracking, and project management system'
|
||||
end
|
||||
|
||||
def to_param
|
||||
'jira'
|
||||
end
|
||||
|
||||
def fields
|
||||
[
|
||||
{ type: 'text', name: 'project_url', placeholder: 'Url to JIRA, http://jira.example' },
|
||||
{ type: 'text', name: 'username', placeholder: '' },
|
||||
{ type: 'password', name: 'password', placeholder: '' },
|
||||
{ type: 'text', name: 'api_version', placeholder: '2' }
|
||||
]
|
||||
end
|
||||
|
||||
def set_api_version
|
||||
self.api_version = "2"
|
||||
end
|
||||
|
||||
def execute(push, issue = nil)
|
||||
close_issue(push, issue) if issue
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def close_issue(push_data, issue_name)
|
||||
url = close_issue_url(issue_name)
|
||||
commit_url = push_data[:commits].first[:url]
|
||||
|
||||
message = {
|
||||
'update' => {
|
||||
'comment' => [{
|
||||
'add' => {
|
||||
'body' => "Issue solved with #{commit_url}"
|
||||
}
|
||||
}]
|
||||
},
|
||||
'transition' => {
|
||||
'id' => '2'
|
||||
}
|
||||
}
|
||||
|
||||
JiraService.post(
|
||||
url,
|
||||
body: message.to_json,
|
||||
headers: {
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => "Basic #{auth}"
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def close_issue_url(issue_name)
|
||||
"#{self.project_url.chomp("/")}/rest/api/#{self.api_version}/issue/#{issue_name}/transitions"
|
||||
end
|
||||
|
||||
def auth
|
||||
require 'base64'
|
||||
Base64.urlsafe_encode64("#{self.username}:#{self.password}")
|
||||
end
|
||||
end
|
||||
@@ -88,7 +88,11 @@ class GitPushService
|
||||
|
||||
if !issues_to_close.empty? && is_default_branch
|
||||
issues_to_close.each do |issue|
|
||||
Issues::CloseService.new(project, author, {}).execute(issue, commit)
|
||||
if project.jira_tracker? && project.jira_service.active
|
||||
project.jira_service.execute(push_data, issue)
|
||||
else
|
||||
Issues::CloseService.new(project, author, {}).execute(issue, commit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
= f.text_area name, rows: 5, class: "form-control", placeholder: placeholder
|
||||
- elsif type == 'checkbox'
|
||||
= f.check_box name
|
||||
- elsif type == 'password'
|
||||
= f.password_field name, class: "form-control"
|
||||
|
||||
.form-actions
|
||||
= f.submit 'Save', class: 'btn btn-save'
|
||||
|
||||
@@ -74,7 +74,7 @@ production: &base
|
||||
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
|
||||
# This happens when the commit is pushed or merged into the default branch of a project.
|
||||
# When not specified the default issue_closing_pattern as specified below will be used.
|
||||
# issue_closing_pattern: '([Cc]lose[sd]|[Ff]ixe[sd]) #(\d+)'
|
||||
# issue_closing_pattern: '([Cc]lose[sd]|[Ff]ixe[sd]) (#\d+|([A-Z\-]+-)\d+)'
|
||||
|
||||
## Default project features settings
|
||||
default_projects_features:
|
||||
|
||||
@@ -91,7 +91,7 @@ Settings.gitlab['signup_enabled'] ||= false
|
||||
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
|
||||
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
|
||||
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
|
||||
Settings.gitlab['issue_closing_pattern'] = '([Cc]lose[sd]|[Ff]ixe[sd]) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
|
||||
Settings.gitlab['issue_closing_pattern'] = '([Cc]lose[sd]|[Ff]ixe[sd]) (#\d+|([A-Z\-]+-)\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
|
||||
Settings.gitlab['default_projects_features'] ||= {}
|
||||
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
|
||||
Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class AddUsernamePasswordApiVersionToServices < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :services, :username, :string
|
||||
add_column :services, :password, :string
|
||||
add_column :services, :api_version, :string
|
||||
end
|
||||
end
|
||||
+4
-1
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20140502125220) do
|
||||
ActiveRecord::Schema.define(version: 20140513095908) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -280,6 +280,9 @@ ActiveRecord::Schema.define(version: 20140502125220) do
|
||||
t.string "room"
|
||||
t.text "recipients"
|
||||
t.string "api_key"
|
||||
t.string "username"
|
||||
t.string "password"
|
||||
t.string "api_version"
|
||||
end
|
||||
|
||||
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# GitLab JIRA integration
|
||||
|
||||
GitLab can be configured to interact with JIRA
|
||||
|
||||
*Note* Directions below are for JIRA v6.x and GitLab v6.x
|
||||
|
||||
## Configuring JIRA
|
||||
|
||||
We need to create a user in JIRA which will have access to all projects that need to integrate with GitLab.
|
||||
Login to your JIRA instance as admin and under Administration -> User Management create a new user.
|
||||
For example, let's create user `gitlab`. We've also added `gitlab` user to group `jira-developers` which grants it access to projects.
|
||||
|
||||
## Configuring GitLab
|
||||
|
||||
In `gitlab.yml` enable [JIRA issue tracker section by uncommenting the lines](https://gitlab.com/subscribers/gitlab-ee/blob/6-8-stable-ee/config/gitlab.yml.example#L111-115).
|
||||
This will make sure that all issues within GitLab are pointing to the JIRA issue tracker.
|
||||
|
||||
We can also enable JIRA service that will allow us to interact with JIRA issues.
|
||||
|
||||
For example, we can close issues in JIRA by a commit in GitLab.
|
||||
|
||||
Go to project settings page and fill in the project name for the JIRA project, see [screenshot](jira_project_name.png).
|
||||
Next, go to the services page and find JIRA.
|
||||
|
||||
1. Tick the active check box to enable the service.
|
||||
1. Supply the url to JIRA server, for example http://jira.sample
|
||||
1. Supply the username of a user we created under `Configuring JIRA` section, for example `gitlab`
|
||||
1. Supply the password of the user
|
||||
1. Optional: supply the JIRA api version, default is version 2
|
||||
1. Save
|
||||
|
||||
Now we should be able to interact with JIRA issues, for example we can close a JIRA issue by commiting to our GitLab repository and referencing the JIRA issue( in the format of JIRAPROJECT-123).
|
||||
|
||||
For example, for project named NEW we commit with a commit message `Add new file fixes NEW-1` [screenshot](jira_service_commit.png) and that will close an issue NEW-1 in JIRA and add a comment with a link to the commit that closed the issue as shown on the [screenshot](jira_service_close_issue.png)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -48,3 +48,9 @@ Feature: Project Services
|
||||
And I click email on push service link
|
||||
And I fill email on push settings
|
||||
Then I should see email on push service settings saved
|
||||
|
||||
Scenario: Activate JIRA service
|
||||
When I visit project "Shop" services page
|
||||
And I click jira service link
|
||||
And I fill jira settings
|
||||
Then I should see jira service settings saved
|
||||
|
||||
@@ -118,4 +118,23 @@ class ProjectServices < Spinach::FeatureSteps
|
||||
find_field('Room').value.should == '#gitlab'
|
||||
find_field('Token').value.should == 'verySecret'
|
||||
end
|
||||
|
||||
step 'I click jira service link' do
|
||||
click_link 'JIRA'
|
||||
end
|
||||
|
||||
step 'I fill jira settings' do
|
||||
fill_in 'Project url', with: 'http://jira.example'
|
||||
fill_in 'Username', with: 'gitlab'
|
||||
fill_in 'Password', with: 'gitlab'
|
||||
fill_in 'Api version', with: '2'
|
||||
click_button 'Save'
|
||||
end
|
||||
|
||||
step 'I should see jira service settings saved' do
|
||||
find_field('Project url').value.should == 'http://jira.example'
|
||||
find_field('Username').value.should == 'gitlab'
|
||||
find_field('Password').value.should_not == 'gitlab'
|
||||
find_field('Api version').value.should == '2'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -181,7 +181,7 @@ module Gitlab
|
||||
link_to("##{identifier}", url, html_options.merge(title: "Issue: #{title}", class: "gfm gfm-issue #{html_options[:class]}"))
|
||||
end
|
||||
else
|
||||
reference_jira_issue(identifier) if @project.issues_tracker == "jira"
|
||||
reference_jira_issue(identifier) if @project.jira_tracker?
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -23,9 +23,13 @@ module Gitlab
|
||||
end
|
||||
|
||||
def issues_for project
|
||||
issues.map do |identifier|
|
||||
project.issues.where(iid: identifier).first
|
||||
end.reject(&:nil?)
|
||||
if project.jira_tracker?
|
||||
issues.uniq
|
||||
else
|
||||
issues.map do |identifier|
|
||||
project.issues.where(iid: identifier).first
|
||||
end.reject(&:nil?)
|
||||
end
|
||||
end
|
||||
|
||||
def merge_requests_for project
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# == 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)
|
||||
# username :string(255)
|
||||
# password :string(255)
|
||||
# api_version :string(255)
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe JiraService, models: true do
|
||||
describe "Associations" do
|
||||
it { should belong_to :project }
|
||||
it { should have_one :service_hook }
|
||||
end
|
||||
|
||||
describe "Execute" do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
|
||||
before do
|
||||
@jira_service = JiraService.new
|
||||
@jira_service.stub(
|
||||
project_id: project.id,
|
||||
project: project,
|
||||
service_hook: true,
|
||||
project_url: 'http://jira.example.com',
|
||||
username: 'gitlab_jira_username',
|
||||
password: 'gitlab_jira_password',
|
||||
api_version: '2'
|
||||
)
|
||||
@sample_data = GitPushService.new.sample_data(project, user)
|
||||
# https://github.com/bblimke/webmock#request-with-basic-authentication
|
||||
@api_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/transitions'
|
||||
|
||||
WebMock.stub_request(:post, @api_url)
|
||||
end
|
||||
|
||||
it "should call JIRA API" do
|
||||
@jira_service.execute(@sample_data, "JIRA-123")
|
||||
WebMock.should have_requested(:post, @api_url).with(
|
||||
body: /Issue solved with/
|
||||
).once
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user