Commit 683770f3 authored by James Lopez's avatar James Lopez

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into update-ruby-2.2.4

parents b8ed5789 a93f7099
......@@ -8,6 +8,27 @@ v 8.5.0 (unreleased)
- Fix diff comments loaded by AJAX to load comment with diff in discussion tab
- Whitelist raw "abbr" elements when parsing Markdown (Benedict Etzel)
- Don't vendor minified JS
- Track project import failure
- Fix visibility level text in admin area (Zeger-Jan van de Weg)
- Update the ExternalIssue regex pattern (Blake Hitchcock)
v 8.4.2 (unreleased)
- Bump required gitlab-workhorse version to bring in a fix for missing
artifacts in the build artifacts browser
- Get rid of those ugly borders on the file tree view
- Bump gitlab_git version to 7.2.24 in order to bring in a performance
improvement when checking if a repository was empty
- Add instrumentation for Gitlab::Git::Repository instance methods so we can
track them in Performance Monitoring.
v 8.4.2 (unreleased)
- Fix method undefined when using external commit status in builds
v 8.4.1
- Apply security updates for Rails (4.2.5.1), rails-html-sanitizer (1.0.3),
and Nokogiri (1.6.7.2)
- Fix redirect loop during import
- Fix diff highlighting for all syntax themes
v 8.4.0
- Allow LDAP users to change their email if it was not set by the LDAP server
......
source "https://rubygems.org"
gem 'rails', '4.2.4'
gem 'rails', '4.2.5.1'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
......@@ -103,7 +103,8 @@ gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.10.1'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
gem 'nokogiri', '1.6.7.1'
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
gem 'nokogiri', '1.6.7.2'
# Diffs
gem 'diffy', '~> 3.0.3'
......
......@@ -4,41 +4,41 @@ GEM
CFPropertyList (2.3.2)
RedCloth (4.2.9)
ace-rails-ap (2.0.1)
actionmailer (4.2.4)
actionpack (= 4.2.4)
actionview (= 4.2.4)
activejob (= 4.2.4)
actionmailer (4.2.5.1)
actionpack (= 4.2.5.1)
actionview (= 4.2.5.1)
activejob (= 4.2.5.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.4)
actionview (= 4.2.4)
activesupport (= 4.2.4)
actionpack (4.2.5.1)
actionview (= 4.2.5.1)
activesupport (= 4.2.5.1)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.4)
activesupport (= 4.2.4)
actionview (4.2.5.1)
activesupport (= 4.2.5.1)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.4)
activesupport (= 4.2.4)
activejob (4.2.5.1)
activesupport (= 4.2.5.1)
globalid (>= 0.3.0)
activemodel (4.2.4)
activesupport (= 4.2.4)
activemodel (4.2.5.1)
activesupport (= 4.2.5.1)
builder (~> 3.1)
activerecord (4.2.4)
activemodel (= 4.2.4)
activesupport (= 4.2.4)
activerecord (4.2.5.1)
activemodel (= 4.2.5.1)
activesupport (= 4.2.5.1)
arel (~> 6.0)
activerecord-deprecated_finders (1.0.4)
activerecord-session_store (0.1.2)
actionpack (>= 4.0.0, < 5)
activerecord (>= 4.0.0, < 5)
railties (>= 4.0.0, < 5)
activesupport (4.2.4)
activesupport (4.2.5.1)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
......@@ -356,7 +356,7 @@ GEM
posix-spawn (~> 0.3)
gitlab_emoji (0.2.0)
gemojione (~> 2.1)
gitlab_git (7.2.23)
gitlab_git (7.2.24)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
......@@ -482,7 +482,7 @@ GEM
grape
newrelic_rpm
newrelic_rpm (3.9.4.245)
nokogiri (1.6.7.1)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
nprogress-rails (0.1.6.7)
oauth (0.4.7)
......@@ -588,16 +588,16 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.4)
actionmailer (= 4.2.4)
actionpack (= 4.2.4)
actionview (= 4.2.4)
activejob (= 4.2.4)
activemodel (= 4.2.4)
activerecord (= 4.2.4)
activesupport (= 4.2.4)
rails (4.2.5.1)
actionmailer (= 4.2.5.1)
actionpack (= 4.2.5.1)
actionview (= 4.2.5.1)
activejob (= 4.2.5.1)
activemodel (= 4.2.5.1)
activerecord (= 4.2.5.1)
activesupport (= 4.2.5.1)
bundler (>= 1.3.0, < 2.0)
railties (= 4.2.4)
railties (= 4.2.5.1)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
......@@ -605,11 +605,11 @@ GEM
activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0)
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.2)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
railties (4.2.4)
actionpack (= 4.2.4)
activesupport (= 4.2.4)
railties (4.2.5.1)
actionpack (= 4.2.5.1)
activesupport (= 4.2.5.1)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.0.0)
......@@ -962,7 +962,7 @@ DEPENDENCIES
net-ssh (~> 3.0.1)
newrelic-grape
newrelic_rpm (~> 3.9.4.245)
nokogiri (= 1.6.7.1)
nokogiri (= 1.6.7.2)
nprogress-rails (~> 0.1.6.7)
oauth2 (~> 1.0.0)
octokit (~> 3.7.0)
......@@ -988,7 +988,7 @@ DEPENDENCIES
rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rails (= 4.2.4)
rails (= 4.2.5.1)
rails-deprecated_sanitizer (~> 1.0.3)
raphael-rails (~> 2.1.2)
rblineprof
......
......@@ -203,4 +203,13 @@ $ ->
form = btn.closest("form")
new ConfirmDangerModal(form, text)
$('input[type="search"]').each ->
$this = $(this)
$this.attr 'value', $this.val()
return
$(document).on 'keyup', 'input[type="search"]' , (e) ->
$this = $(this)
$this.attr 'value', $this.val()
new Aside()
......@@ -32,6 +32,7 @@ class @EditBlob
content: editor.getValue()
, (response) ->
currentPane.empty().append response
currentPane.syntaxHighlight()
return
else
......
......@@ -5,11 +5,11 @@ class @ShortcutsIssuable extends ShortcutsNavigation
constructor: (isMergeRequest) ->
super()
Mousetrap.bind('a', ->
$('.js-assignee').select2('open')
$('.block.assignee .edit-link').trigger('click')
return false
)
Mousetrap.bind('m', ->
$('.js-milestone').select2('open')
$('.block.milestone .edit-link').trigger('click')
return false
)
Mousetrap.bind('r', =>
......
......@@ -311,14 +311,6 @@ table {
}
}
.wiki .highlight, .note-body .highlight {
margin: 12px 0 12px 0;
}
.wiki .code {
overflow-x: auto;
}
.footer-links {
margin-bottom: 20px;
a {
......
......@@ -7,6 +7,7 @@
border: 1px solid $border-color;
&.readme-holder {
margin-top: 10px;
border-bottom: 0;
}
......
......@@ -2,11 +2,42 @@ textarea {
resize: vertical;
}
input[type='search'].search-text-input {
background-image: image-url("icon-search.png");
input {
border-radius: $border-radius-base;
}
input[type='search'] {
background-color: white;
padding-left: 10px;
}
input[type='search'].search-input {
background-repeat: no-repeat;
background-position: 10px;
padding-left: 25px;
background-size: 16px;
background-position-x: 30%;
padding-left: 10px;
background-color: $gray-light;
&.search-input[value=""] {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC');
}
&.search-input::-webkit-input-placeholder {
text-align: center;
}
&.search-input:-moz-placeholder { /* Firefox 18- */
text-align: center;
}
&.search-input::-moz-placeholder { /* Firefox 19+ */
text-align: center;
}
&.search-input:-ms-input-placeholder {
text-align: center;
}
}
input[type='text'].danger {
......@@ -74,6 +105,7 @@ label {
.form-control {
@include box-shadow(none);
border-radius: 3px;
}
.form-control-inline {
......
......@@ -108,16 +108,10 @@ header {
.search-input {
width: 220px;
background-image: image-url("icon-search.png");
background-repeat: no-repeat;
background-position: 195px;
@include input-big;
&:focus {
@include box-shadow(none);
outline: none;
border-color: #DDD;
background-color: #FFF;
}
}
}
......
......@@ -17,6 +17,7 @@
overflow-y: hidden;
white-space: pre;
word-wrap: normal;
border-left: 1px solid;
code {
font-family: $monospace_font;
......@@ -25,7 +26,7 @@
padding: 0;
.line {
display: inline;
display: inline-block;
}
}
}
......@@ -53,18 +54,3 @@
}
}
}
.note-text .code {
border: none;
box-shadow: none;
background: $background-color;
padding: 1em;
overflow-x: auto;
code {
font-family: $monospace_font;
white-space: pre;
word-wrap: normal;
padding: 0;
}
}
......@@ -33,12 +33,12 @@ table {
background-color: $background-color;
font-weight: normal;
font-size: 15px;
border-bottom: 1px solid $border-color !important;
border-bottom: 1px solid $border-color;
}
td {
border-color: $table-border-color !important;
border-bottom: 1px solid;
border-color: $table-border-color;
border-bottom: 1px solid $border-color;
}
}
}
......
......@@ -22,9 +22,9 @@ $brand-info: $gl-info;
$brand-warning: $gl-warning;
$brand-danger: $gl-danger;
$border-radius-base: 2px !default;
$border-radius-large: 2px !default;
$border-radius-small: 2px !default;
$border-radius-base: 3px !default;
$border-radius-large: 3px !default;
$border-radius-small: 3px !default;
//== Scaffolding
......
......@@ -87,8 +87,8 @@
}
p {
color:#5c5d5e;
margin:6px 0 0 0;
color: #5c5d5e;
margin: 6px 0 0 0;
}
table {
......@@ -102,11 +102,10 @@
}
pre {
margin: 12px 0 12px 0 !important;
background-color: #f8fafc;
font-size: 13px !important;
color: #5b6169;
line-height: 1.6em !important;
margin: 12px 0 12px 0;
font-size: 13px;
line-height: 1.6em;
overflow-x: auto;
@include border-radius(2px);
}
......@@ -204,11 +203,6 @@ h1, h2, h3, h4, h5, h6 {
pre {
font-family: $monospace_font;
&.dark {
background: #333;
color: $background-color;
}
&.plain-readme {
background: none;
border: none;
......
......@@ -10,8 +10,8 @@
}
// Code itself
pre.code {
border-left: 1px solid #666;
pre.code, .diff-line-num {
border-color: #666;
}
&, pre.code, .line_holder .line_content {
......
......@@ -10,8 +10,8 @@
}
// Code itself
pre.code {
border-left: 1px solid #555;
pre.code, .diff-line-num {
border-color: #555;
}
&, pre.code, .line_holder .line_content {
......
......@@ -10,8 +10,8 @@
}
// Code itself
pre.code {
border-left: 1px solid #113b46;
pre.code, .diff-line-num {
border-color: #113b46;
}
&, pre.code, .line_holder .line_content {
......@@ -22,11 +22,11 @@
// Diff line
.line_holder {
.diff-line-num.new, .line_content.new {
@include diff_background(rgba(133, 153, 0, 0.2), rgba(133, 153, 0, 0.3), #808080);
@include diff_background(rgba(133, 153, 0, 0.2), rgba(133, 153, 0, 0.3), #113b46);
}
.diff-line-num.old, .line_content.old {
@include diff_background(rgba(220, 50, 47, 0.3), rgba(220, 50, 47, 0.3), #808080);
@include diff_background(rgba(220, 50, 47, 0.3), rgba(220, 50, 47, 0.3), #113b46);
}
.line_content.match {
......
......@@ -10,8 +10,8 @@
}
// Code itself
pre.code {
border-left: 1px solid #c5d0d4;
pre.code, .diff-line-num {
border-color: #c5d0d4;
}
&, pre.code, .line_holder .line_content {
......@@ -22,11 +22,11 @@
// Diff line
.line_holder {
.diff-line-num.new, .line_content.new {
@include diff_background(rgba(133, 153, 0, 0.2), rgba(133, 153, 0, 0.3), #FAF3DD);
@include diff_background(rgba(133, 153, 0, 0.2), rgba(133, 153, 0, 0.3), #c5d0d4);
}
.diff-line-num.old, .line_content.old {
@include diff_background(rgba(220, 50, 47, 0.2), rgba(220, 50, 47, 0.3), #FAF3DD);
@include diff_background(rgba(220, 50, 47, 0.2), rgba(220, 50, 47, 0.3), #c5d0d4);
}
.line_content.match {
......
......@@ -10,8 +10,8 @@
}
// Code itself
pre.code {
border-left: 1px solid $border-color;
pre.code, .diff-line-num {
border-color: $border-color;
}
&, pre.code, .line_holder .line_content {
......
......@@ -79,10 +79,8 @@
margin: 0px;
padding: 0px;
border: none;
background: $background-color;
color: rgba(0, 0, 0, 0.3);
padding: 0px 5px;
border-right: 1px solid $border-color;
border-right: 1px solid;
text-align: right;
min-width: 35px;
max-width: 50px;
......@@ -92,7 +90,6 @@
float: left;
width: 35px;
font-weight: normal;
color: rgba(0, 0, 0, 0.3);
&:hover {
text-decoration: underline;
}
......
.member-search-form {
float: left;
input[type='search'] {
width: 225px;
vertical-align: bottom;
@media (max-width: $screen-xs-max) {
width: 100px;
vertical-align: bottom;
}
}
}
.milestone-row {
......
......@@ -49,11 +49,6 @@
.issue-search-form {
margin: 0;
height: 24px;
.issue_search {
border: 1px solid #DDD !important;
background-color: #f4f4f4;
}
}
form.edit-issue {
......
......@@ -154,6 +154,7 @@ ul.notes {
text-align: center;
padding: 10px 0;
background: #FFF;
color: $text-color;
}
&.notes_line2 {
text-align: center;
......
......@@ -22,8 +22,6 @@
&:hover {
td {
background: $hover;
border-top: 1px solid #ADF;
border-bottom: 1px solid #ADF;
}
cursor: pointer;
}
......
......@@ -2,17 +2,18 @@ class GroupsController < Groups::ApplicationController
include IssuesAction
include MergeRequestsAction
skip_before_action :authenticate_user!, only: [:show, :issues, :merge_requests]
respond_to :html
before_action :group, except: [:new, :create]
skip_before_action :authenticate_user!, only: [:index, :show, :issues, :merge_requests]
before_action :group, except: [:index, :new, :create]
# Authorize
before_action :authorize_read_group!, except: [:show, :new, :create, :autocomplete]
before_action :authorize_read_group!, except: [:index, :show, :new, :create, :autocomplete]
before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects]
before_action :authorize_create_group!, only: [:new, :create]
# Load group projects
before_action :load_projects, except: [:new, :create, :projects, :edit, :update, :autocomplete]
before_action :load_projects, except: [:index, :new, :create, :projects, :edit, :update, :autocomplete]
before_action :event_filter, only: :show
layout :determine_layout
......
class Projects::ImportsController < Projects::ApplicationController
# Authorize
before_action :authorize_admin_project!
before_action :require_no_repo, except: :show
before_action :redirect_if_progress, except: :show
before_action :require_no_repo, only: [:new, :create]
before_action :redirect_if_progress, only: [:new, :create]
def new
end
......@@ -24,11 +24,11 @@ class Projects::ImportsController < Projects::ApplicationController
end
def show
if @project.repository_exists? || @project.import_finished?
if @project.import_finished?
if continue_params
redirect_to continue_params[:to], notice: continue_params[:notice]
else
redirect_to project_path(@project), notice: "The project was successfully forked."
redirect_to namespace_project_path(@project.namespace, @project), notice: finished_notice
end
elsif @project.import_failed?
redirect_to new_namespace_project_import_path(@project.namespace, @project)
......@@ -36,6 +36,7 @@ class Projects::ImportsController < Projects::ApplicationController
if continue_params && continue_params[:notice_now]
flash.now[:notice] = continue_params[:notice_now]
end
# Render
end
end
......@@ -44,6 +45,7 @@ class Projects::ImportsController < Projects::ApplicationController
def continue_params
continue_params = params[:continue]
if continue_params
continue_params.permit(:to, :notice, :notice_now)
else
......@@ -51,8 +53,16 @@ class Projects::ImportsController < Projects::ApplicationController
end
end
def finished_notice
if @project.forked?
'The project was successfully forked.'
else
'The project was successfully imported.'
end
end
def require_no_repo
if @project.repository_exists? && !@project.import_in_progress?
if @project.repository_exists?
redirect_to(namespace_project_path(@project.namespace, @project))
end
end
......
......@@ -171,7 +171,7 @@ module ApplicationHelper
def search_placeholder
if @project && @project.persisted?
'Search in this project'
'Search'
elsif @snippet || @snippets || @show_snippets
'Search snippets'
elsif @group && @group.persisted?
......
......@@ -83,7 +83,11 @@ module LabelsHelper
end
def text_color_for_bg(bg_color)
r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex)
if bg_color.length == 4
r, g, b = bg_color[1, 4].scan(/./).map { |v| (v * 2).hex }
else
r, g, b = bg_color[1, 7].scan(/.{2}/).map(&:hex)
end
if (r + g + b) > 500
'#333333'
......
......@@ -31,7 +31,7 @@ class ExternalIssue
# Pattern used to extract `JIRA-123` issue references from text
def self.reference_pattern
%r{(?<issue>([A-Z\-]+-)\d+)}
%r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)}
end
def to_reference(_from_project = nil)
......
......@@ -57,7 +57,7 @@ class Repository
# This method return true if repository contains some content visible in project page.
#
def has_visible_content?
!raw_repository.branches.empty?
raw_repository.branch_count > 0
end
def commit(id = 'HEAD')
......
......@@ -17,12 +17,20 @@ class Tree
def readme
return @readme if defined?(@readme)
# Take the first previewable readme, or return nil if none is available or
# we can't preview any of them
readme_tree = blobs.find do |blob|
blob.readme? && (previewable?(blob.name) || plain?(blob.name))
available_readmes = blobs.select(&:readme?)
previewable_readmes = available_readmes.select do |blob|
previewable?(blob.name)
end
plain_readmes = available_readmes.select do |blob|
plain?(blob.name)
end
# Prioritize previewable over plain readmes
readme_tree = previewable_readmes.first || plain_readmes.first
# Return if we can't preview any of them
if readme_tree.nil?
return @readme = nil
end
......
module Projects
class ImportService < BaseService
include Gitlab::ShellAdapter
class Error < StandardError; end
ALLOWED_TYPES = [
'bitbucket',
'fogbugz',
'gitlab',
'github',
'google_code'
]
def execute
if unknown_url?
# In this case, we only want to import issues, not a repository.
create_repository
else
import_repository
end
import_data
success
rescue Error => e
error(e.message)
end
private
def create_repository
unless project.create_repository
raise Error, 'The repository could not be created.'
end
end
def import_repository
begin
gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
rescue Gitlab::Shell::Error => e
raise Error, e.message
end
end
def import_data
return unless has_importer?
unless importer.execute
raise Error, 'The remote data could not be imported.'
end
end
def has_importer?
ALLOWED_TYPES.include?(project.import_type)
end
def importer
class_name = "Gitlab::#{project.import_type.camelize}Import::Importer"
class_name.constantize.new(project)
end
def unknown_url?
project.import_url == Project::UNKNOWN_IMPORT_URL
end
end
end
......@@ -14,11 +14,11 @@
.form-group.project-visibility-level-holder
= f.label :default_project_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project)
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new)
.form-group.project-visibility-level-holder
= f.label :default_snippet_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: PersonalSnippet)
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
.form-group
= f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
.col-sm-10
......@@ -268,4 +268,4 @@
= f.text_field :sentry_dsn, class: 'form-control'
.form-actions
= f.submit 'Save', class: 'btn btn-primary'
= f.submit 'Save', class: 'btn btn-save'
......@@ -22,5 +22,5 @@
%code= Doorkeeper.configuration.native_redirect_uri
for local tests
.form-actions
= f.submit 'Submit', class: "btn btn-primary wide"
= f.submit 'Submit', class: "btn btn-save wide"
= link_to "Cancel", admin_applications_path, class: "btn btn-default"
......@@ -21,6 +21,5 @@
- else
.form-actions
= f.submit 'Save changes', class: "btn btn-primary"
= f.submit 'Save changes', class: "btn btn-save"
= link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel"
......@@ -2,8 +2,8 @@
.file-content.wiki
= render_markup(blob.name, blob.data)
- else
.file-content.code
- unless blob.empty?
= render 'shared/file_highlight', blob: blob
- else
.file-content.code
.nothing-here-block Empty file
......@@ -8,7 +8,7 @@
.file-content.wiki
= raw render_markup(@blob.name, @content)
- else
.file-content.code
.file-content.code.js-syntax-highlight
- unless @diff_lines.empty?
%table.text-file
- @diff_lines.each do |line|
......
......@@ -66,7 +66,7 @@
%td
.pull-right
- if current_user && can?(current_user, :read_build_artifacts, commit_status.project) && commit_status.artifacts?
- if current_user && can?(current_user, :read_build_artifacts, commit_status.project) && commit_status.artifacts_download_url
= link_to commit_status.artifacts_download_url, title: 'Download artifacts' do
%i.fa.fa-download
- if current_user && can?(current_user, :manage_builds, commit_status.project)
......
......@@ -9,7 +9,7 @@
= diff.new_path
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
.diff-content
.diff-content.code.js-syntax-highlight
%table
- note.truncated_diff_lines.each do |line|
- type = line.type
......
.gitlab-promo
= link_to 'Homepage', promo_url
= link_to "Blog", promo_url + '/blog/'
= link_to "@gitlab", "https://twitter.com/gitlab"
= link_to "Requests", "http://feedback.gitlab.com/"
= link_to 'Blog', promo_url + '/blog/'
= link_to '@gitlab', 'https://twitter.com/gitlab'
= link_to 'Requests', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#feature-proposals'
= form_tag(path, method: :get, id: "issue_search_form", class: 'pull-left issue-search-form') do
.append-right-10.hidden-xs.hidden-sm
= search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input', spellcheck: false }
= search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by name ...', class: 'form-control issue_search search-text-input', spellcheck: false }
= hidden_field_tag :state, params['state']
= hidden_field_tag :scope, params['scope']
= hidden_field_tag :assignee_id, params['assignee_id']
......
......@@ -3,7 +3,6 @@
.file-content.wiki
= render_markup(@snippet.file_name, @snippet.data)
- else
.file-content.code
= render 'shared/file_highlight', blob: @snippet
- else
.file-content.code
......
......@@ -4,52 +4,20 @@ class RepositoryImportWorker
sidekiq_options queue: :gitlab_shell
def perform(project_id)
project = Project.find(project_id)
attr_accessor :project, :current_user
if project.import_url == Project::UNKNOWN_IMPORT_URL
# In this case, we only want to import issues, not a repository.
unless project.create_repository
project.update(import_error: "The repository could not be created.")
project.import_fail
return
end
else
begin
gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
rescue Gitlab::Shell::Error => e
project.update(import_error: e.message)
project.import_fail
return
end
end
def perform(project_id)
@project = Project.find(project_id)
@current_user = @project.creator
data_import_result =
case project.import_type
when 'github'
Gitlab::GithubImport::Importer.new(project).execute
when 'gitlab'
Gitlab::GitlabImport::Importer.new(project).execute
when 'bitbucket'
Gitlab::BitbucketImport::Importer.new(project).execute
when 'google_code'
Gitlab::GoogleCodeImport::Importer.new(project).execute
when 'fogbugz'
Gitlab::FogbugzImport::Importer.new(project).execute
else
true
end
result = Projects::ImportService.new(project, current_user).execute
unless data_import_result
project.update(import_error: "The remote issue data could not be imported.")
if result[:status] == :error
project.update(import_error: result[:message])
project.import_fail
return
end
if project.import_type == 'bitbucket'
Gitlab::BitbucketImport::KeyDeleter.new(project).execute
end
project.import_finish
end
end
......@@ -176,7 +176,7 @@ Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].
Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_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]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z]*-\d*))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
......
## This patch is from rails 4.2-stable. Remove it when 4.2.6 is released
## https://github.com/rails/rails/issues/21108
module ActiveRecord
module ConnectionAdapters
class AbstractMysqlAdapter < AbstractAdapter
# SHOW VARIABLES LIKE 'name'
def show_variable(name)
variables = select_all("select @@#{name} as 'Value'", 'SCHEMA')
variables.first['Value'] unless variables.empty?
rescue ActiveRecord::StatementInvalid
nil
end
# MySQL is too stupid to create a temporary table for use subquery, so we have
# to give it some prompting in the form of a subsubquery. Ugh!
def subquery_for(key, select)
subsubselect = select.clone
subsubselect.projections = [key]
subselect = Arel::SelectManager.new(select.engine)
subselect.project Arel.sql(key.name)
# Materialized subquery by adding distinct
# to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
subselect.from subsubselect.distinct.as('__active_record_temp')
end
end
end
end
module ActiveRecord
module ConnectionAdapters
class MysqlAdapter < AbstractMysqlAdapter
ADAPTER_NAME = 'MySQL'.freeze
# Get the client encoding for this database
def client_encoding
return @client_encoding if @client_encoding
result = exec_query(
"select @@character_set_client",
'SCHEMA')
@client_encoding = ENCODINGS[result.rows.last.last]
end
end
end
end
......@@ -13,8 +13,8 @@ ability of downloading the files separately.
**Note:**
The artifacts browser will be available only for new artifacts that are sent
to GitLab using GitLab Runner version 1.0 and up. You will not be available to
see the browser for old artifacts already uploaded to GitLab.
to GitLab using GitLab Runner version 1.0 and up. It will not be possible to
browse old artifacts already uploaded to GitLab.
## Enabling build artifacts
......
......@@ -5,7 +5,7 @@ trackers and external authentication.
See the documentation below for details on how to configure these services.
- [Jira](jira.md) Integrate with the JIRA issue tracker
- [Jira](../project_services/jira.md) Integrate with the JIRA issue tracker
- [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc.
- [LDAP](ldap.md) Set up sign in via LDAP
- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, SAML, Crowd and Azure
......
......@@ -19,7 +19,7 @@ To enable an external issue tracker you must configure the appropriate **Service
Visit the links below for details:
- [Redmine](../project_services/redmine.md)
- [Jira](jira.md)
- [Jira](../project_services/jira.md)
### Service Template
......
# GitLab Jira integration
# GitLab JIRA integration
GitLab can be configured to interact with Jira. Configuration happens via
username and password. Connecting to a Jira server via CAS is not possible.
Each project can be configured to connect to a different Jira instance, see the
[configuration](#configuration) section. If you have one Jira instance you can
pre-fill the settings page with a default template. To configure the template
see the [Services Templates][services-templates] document.
Once the project is connected to Jira, you can reference and close the issues
in Jira directly from GitLab.
## Table of Contents
* [Referencing Jira Issues from GitLab](#referencing-jira-issues)
* [Closing Jira Issues from GitLab](#closing-jira-issues)
* [Configuration](#configuration)
### Referencing Jira Issues
When GitLab project has Jira issue tracker configured and enabled, mentioning
Jira issue in GitLab will automatically add a comment in Jira issue with the
link back to GitLab. This means that in comments in merge requests and commits
referencing an issue, eg. `PROJECT-7`, will add a comment in Jira issue in the
format:
```
USER mentioned this issue in LINK_TO_THE_MENTION
```
* `USER` A user that mentioned the issue. This is the link to the user profile in GitLab.
* `LINK_TO_THE_MENTION` Link to the origin of mention with a name of the entity where Jira issue was mentioned.
Can be commit or merge request.
![example of mentioning or closing the Jira issue](img/jira_issue_reference.png)
---
### Closing Jira Issues
Jira issues can be closed directly from GitLab by using trigger words, eg.
`Resolves PROJECT-1`, `Closes PROJECT-1` or `Fixes PROJECT-1`, in commits and
merge requests. When a commit which contains the trigger word in the commit
message is pushed, GitLab will add a comment in the mentioned Jira issue.
For example, for project named `PROJECT` in Jira, we implemented a new feature
and created a merge request in GitLab.
This feature was requested in Jira issue `PROJECT-7`. Merge request in GitLab
contains the improvement and in merge request description we say that this
merge request `Closes PROJECT-7` issue.
Once this merge request is merged, the Jira issue will be automatically closed
with a link to the commit that resolved the issue.
![A Git commit that causes the Jira issue to be closed](img/jira_merge_request_close.png)
---
![The GitLab integration user leaves a comment on Jira](img/jira_service_close_issue.png)
---
## Configuration
### 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 go to User Management and create a new user.
As an example, we'll create a user named `gitlab` and add it to `jira-developers`
group.
**It is important that the user `gitlab` has write-access to projects in JIRA**
### Configuring GitLab
JIRA configuration in GitLab is done via a project's **Services**.
#### GitLab 7.8 and up with JIRA v6.x
See next section.
#### GitLab 7.8 and up
_The currently supported JIRA versions are v6.x and v7.x._
To enable JIRA integration in a project, navigate to the project's
**Settings > Services > JIRA**.
Fill in the required details on the page as described in the table below.
| Field | Description |
| ----- | ----------- |
| `description` | A name for the issue tracker (to differentiate between instances, for instance). |
| `project url` | The URL to the JIRA project which is being linked to this GitLab project. |
| `issues url` | The URL to the JIRA project issues overview for the project that is linked to this GitLab project. |
| `new issue url` | This is the URL to create a new issue in JIRA for the project linked to this GitLab project. |
| `api url` | The base URL of the JIRA API. It may be omitted, in which case GitLab will automatically use API version `2` based on the `project url`, i.e. `https://jira.example.com/rest/api/2`. |
| `username` | The username of the user created in [configuring JIRA step](#configuring-jira). |
| `password` |The password of the user created in [configuring JIRA step](#configuring-jira). |
| `Jira issue transition` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). By default, this ID is `2` (in the example image, this is `2` as well) |
After saving the configuration, your GitLab project will be able to interact
with the linked JIRA project.
![Jira service page](img/jira_service_page.png)
---
#### GitLab 6.x-7.7 with JIRA v6.x
_**Note:** GitLab versions 7.8 and up contain various integration improvements.
We strongly recommend upgrading._
In `gitlab.yml` enable the JIRA issue tracker section by
[uncommenting these lines][jira-gitlab-yml]. This will make sure that all
issues within GitLab are pointing to the JIRA issue tracker.
After you set this, you will be able to close issues in JIRA by a commit in
GitLab.
Go to your project's **Settings** page and fill in the project name for the
JIRA project:
![Set the JIRA project name in GitLab to 'NEW'](img/jira_project_name.png)
---
You can also enable the JIRA service that will allow you to interact with JIRA
issues. Go to the **Settings > Services > JIRA** and:
1. Tick the active check box to enable the service
1. Supply the URL to JIRA server, for example http://jira.example.com
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. Optional: supply the JIRA issue transition ID (issue transition to closed).
This is dependent on JIRA settings, default is `2`
1. Hit save
![Jira services page](img/jira_service.png)
[services-templates]: ../project_services/services_templates.md
[jira-gitlab-yml]: https://gitlab.com/subscribers/gitlab-ee/blob/6-8-stable-ee/config/gitlab.yml.example#L111-115
This document was moved under [project_services/jira](../project_services/jira.md).
......@@ -6,15 +6,17 @@ To enable Slack integration you must create an Incoming WebHooks integration on
1. [Sign in to Slack](https://slack.com/signin)
1. Select **Configure Integrations** from the dropdown next to your team name.
1. Select **Apps & Custom Integrations** from the dropdown next to your team name.
1. Select the **All Services** tab
1. Click the **Configure** link (right-upper corner).
1. Click **Add** next to Incoming Webhooks
1. Select the **Custom integrations** tab.
1. Pick Incoming WebHooks
1. Click the **Incoming WebHooks** row.
1. Choose the channel name you want to send notifications to
1. Click the **Add configuration** button.
1. Choose the channel name you want to send notifications to.
1. Click **Add Incoming WebHooks Integration**
- Optional step; You can change bot's name and avatar by clicking modifying the bot name or avatar under **Integration Settings**.
......
......@@ -88,6 +88,9 @@ GFM will autolink almost any URL you copy and paste into your text.
## Code and Syntax Highlighting
_GitLab uses the [rouge ruby library][rouge] for syntax highlighting. For a
list of supported languages visit the rouge website._
Blocks of code are either fenced by lines with three back-ticks <code>```</code>, or are indented with four spaces. Only the fenced code blocks support syntax highlighting.
```no-highlight
......@@ -585,3 +588,5 @@ By including colons in the header row, you can align the text within that column
- This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
- The [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
- [Dillinger.io](http://dillinger.io) is a handy tool for testing standard markdown.
[rouge]: http://rouge.jneen.net/ "Rouge website"
......@@ -12,6 +12,9 @@ The default is **Charcoal**.
## Syntax highlighting theme
_GitLab uses the [rouge ruby library][rouge] for syntax highlighting. For a
list of supported languages visit the rouge website._
Changing this setting allows the user to customize the theme used when viewing
syntax highlighted code on the site.
......@@ -36,3 +39,5 @@ The default is **Your Projects**.
It allows user to choose what content he or she want to see on project page.
The default is **Readme**.
[rouge]: http://rouge.jneen.net/ "Rouge website"
# GitLab JIRA integration
GitLab can be configured to interact with [JIRA Core] either using an
on-premises instance or the SaaS solution that Atlassian offers. Configuration
happens via username and password on a per-project basis. Connecting to a JIRA
server via CAS is not possible.
Each project can be configured to connect to a different JIRA instance or, in
case you have a single JIRA instance, you can pre-fill the JIRA service
settings page in GitLab with a default template. To configure the JIRA template,
see the [Services Templates documentation][services-templates].
Once the GitLab project is connected to JIRA, you can reference and close the
issues in JIRA directly from GitLab's merge requests.
## Configuration
The configuration consists of two parts:
- [JIRA configuration](#configuring-jira)
- [GitLab configuration](#configuring-gitlab)
### Configuring JIRA
First things first, we need to create a user in JIRA which will have access to
all projects that need to integrate with GitLab.
We have split this stage in steps so it is easier to follow.
---
1. Login to your JIRA instance as an administrator and under **Administration**
go to **User Management** to create a new user.
![JIRA user management link](img/jira_user_management_link.png)
---
1. The next step is to create a new user (e.g., `gitlab`) who has write access
to projects in JIRA. Enter the user's name and a _valid_ e-mail address
since JIRA sends a verification e-mail to set-up the password.
_**Note:** JIRA creates the username automatically by using the e-mail
prefix. You can change it later if you want._
![JIRA create new user](img/jira_create_new_user.png)
---
1. Now, let's create a `gitlab-developers` group which will have write access
to projects in JIRA. Go to the **Groups** tab and select **Create group**.
![JIRA create new user](img/jira_create_new_group.png)
---
Give it an optional description and hit **Create group**.
![JIRA create new group](img/jira_create_new_group_name.png)
---
1. Give the newly-created group write access by going to
**Application access > View configuration** and adding the `gitlab-developers`
group to JIRA Core.
![JIRA group access](img/jira_group_access.png)
---
1. Add the `gitlab` user to the `gitlab-developers` group by going to
**Users > GitLab user > Add group** and selecting the `gitlab-developers`
group from the dropdown menu. Notice that the group says _Access_ which is
what we aim for.
![JIRA add user to group](img/jira_add_user_to_group.png)
---
The JIRA configuration is over. Write down the new JIRA username and its
password as they will be needed when configuring GitLab in the next section.
### Configuring GitLab
_**Note:** The currently supported JIRA versions are v6.x and v7.x. and GitLab
7.8 or higher is required._
---
Assuming you [have already configured JIRA](#configuring-jira), now it's time
to configure GitLab.
JIRA configuration in GitLab is done via a project's
[**Services**](../project_services/project_services.md).
To enable JIRA integration in a project, navigate to the project's
**Settings > Services > JIRA**.
Fill in the required details on the page, as described in the table below.
| Setting | Description |
| ------- | ----------- |
| `Description` | A name for the issue tracker (to differentiate between instances, for example). |
| `Project url` | The URL to the JIRA project which is being linked to this GitLab project. It is of the form: `https://<jira_host_url>/issues/?jql=project=<jira_project>`. |
| `Issues url` | The URL to the JIRA project issues overview for the project that is linked to this GitLab project. It is of the form: `https://<jira_host_url>/browse/:id`. Leave `:id` as-is, it gets replaced by GitLab at runtime. |
| `New issue url` | This is the URL to create a new issue in JIRA for the project linked to this GitLab project, and it is of the form: `https://<jira_host_url>/secure/CreateIssue.jspa` |
| `Api url` | The base URL of the JIRA API. It may be omitted, in which case GitLab will automatically use API version `2` based on the `project url`. It is of the form: `https://<jira_host_url>/rest/api/2`. |
| `Username` | The username of the user created in [configuring JIRA step](#configuring-jira). |
| `Password` |The password of the user created in [configuring JIRA step](#configuring-jira). |
| `JIRA issue transition` | This setting is very important to set up correctly. It is the ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot](img/jira_issues_workflow.png)). By default, this ID is set to `2` |
After saving the configuration, your GitLab project will be able to interact
with the linked JIRA project.
![JIRA service page](img/jira_service_page.png)
---
## JIRA issues
By now you should have [configured JIRA](#configuring-jira) and enabled the
[JIRA service in GitLab](#configuring-gitlab). If everything is set up correctly
you should be able to reference and close JIRA issues by just mentioning their
ID in GitLab commits and merge requests.
### Referencing JIRA Issues
If you reference a JIRA issue, e.g., `GITLAB-1`, in a commit comment, a link
which points back to JIRA is created.
The same works for comments in merge requests as well.
![JIRA add GitLab commit message](img/jira_add_gitlab_commit_message.png)
---
The mentioning action is two-fold, so a comment with a JIRA issue in GitLab
will automatically add a comment in that particular JIRA issue with the link
back to GitLab.
![JIRA reference commit message](img/jira_reference_commit_message_in_jira_issue.png)
---
The comment on the JIRA issue is of the form:
> USER mentioned this issue in LINK_TO_THE_MENTION
Where:
| Format | Description |
| ------ | ----------- |
| `USER` | A user that mentioned the issue. This is the link to the user profile in GitLab. |
| `LINK_TO_THE_MENTION` | Link to the origin of mention with a name of the entity where JIRA issue was mentioned. Can be commit or merge request. |
### Closing JIRA issues
JIRA issues can be closed directly from GitLab by using trigger words in
commits and merge requests. When a commit which contains the trigger word
followed by the JIRA issue ID in the commit message is pushed, GitLab will
add a comment in the mentioned JIRA issue and immediately close it (provided
the transition ID was set up correctly).
There are currently three trigger words, and you can use either one to achieve
the same goal:
- `Resolves GITLAB-1`
- `Closes GITLAB-1`
- `Fixes GITLAB-1`
where `GITLAB-1` the issue ID of the JIRA project.
### JIRA issue closing example
Let's say for example that we submitted a bug fix and created a merge request
in GitLab. The workflow would be something like this:
1. Create a new branch
1. Fix the bug
1. Commit the changes and push branch to GitLab
1. Open a new merge request and reference the JIRA issue including one of the
trigger words, e.g.: `Fixes GITLAB-1`, in the description
1. Submit the merge request
1. Ask someone to review
1. Merge the merge request
1. The JIRA issue is automatically closed
---
In the following screenshot you can see what the link references to the JIRA
issue look like.
![JIRA - submit a GitLab merge request](img/jira_submit_gitlab_merge_request.png)
---
Once this merge request is merged, the JIRA issue will be automatically closed
with a link to the commit that resolved the issue.
![The GitLab integration user leaves a comment on JIRA](img/jira_issue_closed.png)
---
You can see from the above image that there are four references to GitLab:
- The first is from a comment in a specific commit
- The second is from the JIRA issue reference in the merge request description
- The third is from the actual commit that solved the issue
- And the fourth is from the commit that the merge request created
[services-templates]: ../project_services/services_templates.md "Services templates documentation"
[JIRA Core]: https://www.atlassian.com/software/jira/core "The JIRA Core website"
......@@ -22,7 +22,7 @@ further configuration instructions and details. Contributions are welcome.
| Gemnasium | Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities |
| [HipChat](hipchat.md) | Private group chat and IM |
| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway |
| JIRA | Jira issue tracker |
| [JIRA](jira.md) | JIRA issue tracker |
| JetBrains TeamCity CI | A continuous integration and build server |
| PivotalTracker | Project Management Software (Source Commits Endpoint) |
| Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop |
......
# Project forking workflow
Forking a project to your own namespace is useful if you have no write access to the project you want to contribute
to. If you do have write access or can request it we recommend working together in the same repository since it is simpler.
See our **[GitLab Flow](https://about.gitlab.com/2014/09/29/gitlab-flow/)** article for more information about using
branches to work together.
Forking a project to your own namespace is useful if you have no write
access to the project you want to contribute to. If you do have write
access or can request it, we recommend working together in the same
repository since it is simpler. See our [GitLab Flow](gitlab_flow.md)
document more information about using branches to work together.
## Creating a fork
In order to create a fork of a project, all you need to do is click on the fork button located on the top right side
of the screen, close to the project's URL and right next to the stars button.
Forking a project is in most cases a two-step process.
![Fork button](forking/fork_button.png)
Once you do that you'll be presented with a screen where you can choose the namespace to fork to. Only namespaces
(groups and your own namespace) where you have write access to, will be shown. Click on the namespace to create your
fork there.
1. Click on the fork button located in the middle of the page or a project's
home page right next to the stars button.
![Groups view](forking/groups.png)
![Fork button](img/forking_workflow_fork_button.png)
After the forking is done, you can start working on the newly created repository. There you will have full
[Owner](../permissions/permissions.md) access, so you can set it up as you please.
---
1. Once you do that, you'll be presented with a screen where you can choose
the namespace to fork to. Only namespaces (groups and your own
namespace) where you have write access to, will be shown. Click on the
namespace to create your fork there.
![Choose namespace](img/forking_workflow_choose_namespace.png)
---
**Note:**
If the namespace you chose to fork the project to has another project with
the same path name, you will be presented with a warning that the forking
could not be completed. Try to resolve the error and repeat the forking
process.
![Path taken error](img/forking_workflow_path_taken_error.png)
---
After the forking is done, you can start working on the newly created
repository. There, you will have full [Owner](../permissions/permissions.md)
access, so you can set it up as you please.
## Merging upstream
Once you are ready to send your code back to the main project, you need to create a merge request. Choose your forked
project's main branch as the source and the original project's main branch as the destination and create the merge request.
Once you are ready to send your code back to the main project, you need
to create a merge request. Choose your forked project's main branch as
the source and the original project's main branch as the destination and
create the [merge request](merge_requests.md).
![Selecting branches](forking/branch_select.png)
You can then assign the merge request to someone to have them review your changes. Upon pressing the 'Accept Merge Request'
button, your changes will be added to the repository and branch you're merging into.
You can then assign the merge request to someone to have them review
your changes. Upon pressing the 'Accept Merge Request' button, your
changes will be added to the repository and branch you're merging into.
![New merge request](forking/merge_request.png)
[gitlab flow]: https://about.gitlab.com/2014/09/29/gitlab-flow/ "GitLab Flow blog post"
......@@ -51,3 +51,12 @@ Feature: Project Builds Artifacts
And I click artifacts browse button
And I click a link to file within build artifacts
Then download of a file extracted from build artifacts should start
@javascript
Scenario: I click on a row in an artifacts table
Given recent build has artifacts available
And recent build has artifacts metadata available
When I visit recent build details page
And I click artifacts browse button
And I click a first row within build artifacts table
Then page with a coresponding path is loading
......@@ -73,4 +73,14 @@ class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps
expect(response_json[:archive]).to end_with('build_artifacts.zip')
expect(response_json[:entry]).to eq Base64.encode64('ci_artifacts.txt')
end
step 'I click a first row within build artifacts table' do
row = first('tr[data-link]')
@row_path = row['data-link']
row.click
end
step 'page with a coresponding path is loading' do
expect(current_path).to eq @row_path
end
end
......@@ -163,7 +163,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I search for Wiki content' do
fill_in "Search in this project", with: "wiki_content"
fill_in "Search", with: "wiki_content"
click_button "Search"
end
......
......@@ -13,12 +13,36 @@ module Gitlab
end
def execute
project_identifier = project.import_source
import_issues if has_issues?
return true unless client.project(project_identifier)["has_issues"]
true
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error.new, e.message
ensure
Gitlab::BitbucketImport::KeyDeleter.new(project).execute
end
private
def gl_user_id(project, bitbucket_id)
if bitbucket_id
user = User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", bitbucket_id.to_s)
(user && user.id) || project.creator_id
else
project.creator_id
end
end
def identifier
project.import_source
end
#Issues && Comments
issues = client.issues(project_identifier)
def has_issues?
client.project(identifier)["has_issues"]
end
def import_issues
issues = client.issues(identifier)
issues.each do |issue|
body = ''
......@@ -33,7 +57,7 @@ module Gitlab
body = @formatter.author_line(author)
body += issue["content"]
comments = client.issue_comments(project_identifier, issue["local_id"])
comments = client.issue_comments(identifier, issue["local_id"])
if comments.any?
body += @formatter.comments_header
......@@ -56,19 +80,8 @@ module Gitlab
author_id: gl_user_id(project, reporter)
)
end
true
end
private
def gl_user_id(project, bitbucket_id)
if bitbucket_id
user = User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", bitbucket_id.to_s)
(user && user.id) || project.creator_id
else
project.creator_id
end
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
end
end
end
......
......@@ -35,8 +35,8 @@ module Gitlab
end
true
rescue ActiveRecord::RecordInvalid
false
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
end
def import_pull_requests
......@@ -53,8 +53,8 @@ module Gitlab
end
true
rescue ActiveRecord::RecordInvalid
false
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
end
def import_comments(issue_number, noteable)
......@@ -83,10 +83,13 @@ module Gitlab
true
rescue Gitlab::Shell::Error => e
if e.message =~ /repository not exported/
true
# GitHub error message when the wiki repo has not been created,
# this means that repo has wiki enabled, but have no pages. So,
# we can skip the import.
if e.message !~ /repository not exported/
raise Projects::ImportService::Error, e.message
else
false
true
end
end
end
......
......@@ -106,20 +106,36 @@ module Gitlab
if type == :instance
target = mod
label = "#{mod.name}##{name}"
method = mod.instance_method(name)
else
target = mod.singleton_class
label = "#{mod.name}.#{name}"
method = mod.method(name)
end
# Some code out there (e.g. the "state_machine" Gem) checks the arity of
# a method to make sure it only passes arguments when the method expects
# any. If we were to always overwrite a method to take an `*args`
# signature this would break things. As a result we'll make sure the
# generated method _only_ accepts regular arguments if the underlying
# method also accepts them.
if method.arity == 0
args_signature = '&block'
else
args_signature = '*args, &block'
end
send_signature = "__send__(#{alias_name.inspect}, #{args_signature})"
target.class_eval <<-EOF, __FILE__, __LINE__ + 1
alias_method #{alias_name.inspect}, #{name.inspect}
def #{name}(*args, &block)
def #{name}(#{args_signature})
trans = Gitlab::Metrics::Instrumentation.transaction
if trans
start = Time.now
retval = __send__(#{alias_name.inspect}, *args, &block)
retval = #{send_signature}
duration = (Time.now - start) * 1000.0
if duration >= Gitlab::Metrics.method_call_threshold
......@@ -132,7 +148,7 @@ module Gitlab
retval
else
__send__(#{alias_name.inspect}, *args, &block)
#{send_signature}
end
end
EOF
......
......@@ -10,7 +10,7 @@ if [ -f /.dockerinit ]; then
apt-get update -qq
apt-get -o dir::cache::archives="/cache/apt" install -y -qq --force-yes \
libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client
libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip
cp config/database.yml.mysql config/database.yml
sed -i 's/username:.*/username: root/g' config/database.yml
......
require 'rails_helper'
describe GroupsController do
describe 'GET index' do
context 'as a user' do
it 'redirects to Groups Dashboard' do
sign_in(create(:user))
get :index
expect(response).to redirect_to(dashboard_groups_path)
end
end
context 'as a guest' do
it 'redirects to Explore Groups' do
get :index
expect(response).to redirect_to(explore_groups_path)
end
end
end
end
require 'spec_helper'
describe Projects::ImportsController do
let(:user) { create(:user) }
describe 'GET #show' do
context 'when repository does not exists' do
let(:project) { create(:empty_project) }
before do
sign_in(user)
project.team << [user, :master]
end
it 'renders template' do
get :show, namespace_id: project.namespace.to_param, project_id: project.to_param
expect(response).to render_template :show
end
it 'sets flash.now if params is present' do
get :show, namespace_id: project.namespace.to_param, project_id: project.to_param, continue: { notice_now: 'Started' }
expect(flash.now[:notice]).to eq 'Started'
end
end
context 'when repository exists' do
let(:project) { create(:project_empty_repo, import_url: 'https://github.com/vim/vim.git') }
before do
sign_in(user)
project.team << [user, :master]
end
context 'when import is in progress' do
before do
project.update_attribute(:import_status, :started)
end
it 'renders template' do
get :show, namespace_id: project.namespace.to_param, project_id: project.to_param
expect(response).to render_template :show
end
it 'sets flash.now if params is present' do
get :show, namespace_id: project.namespace.to_param, project_id: project.to_param, continue: { notice_now: 'In progress' }
expect(flash.now[:notice]).to eq 'In progress'
end
end
context 'when import failed' do
before do
project.update_attribute(:import_status, :failed)
end
it 'redirects to new_namespace_project_import_path' do
get :show, namespace_id: project.namespace.to_param, project_id: project.to_param
expect(response).to redirect_to new_namespace_project_import_path(project.namespace, project)
end
end
context 'when import finished' do
before do
project.update_attribute(:import_status, :finished)
end
context 'when project is a fork' do
it 'redirects to namespace_project_path' do
allow_any_instance_of(Project).to receive(:forked?).and_return(true)
get :show, namespace_id: project.namespace.to_param, project_id: project.to_param
expect(flash[:notice]).to eq 'The project was successfully forked.'
expect(response).to redirect_to namespace_project_path(project.namespace, project)
end
end
context 'when project is external' do
it 'redirects to namespace_project_path' do
get :show, namespace_id: project.namespace.to_param, project_id: project.to_param
expect(flash[:notice]).to eq 'The project was successfully imported.'
expect(response).to redirect_to namespace_project_path(project.namespace, project)
end
end
context 'when continue params is present' do
let(:params) do
{
to: namespace_project_path(project.namespace, project),
notice: 'Finished'
}
end
it 'redirects to params[:to]' do
get :show, namespace_id: project.namespace.to_param, project_id: project.to_param, continue: params
expect(flash[:notice]).to eq params[:notice]
expect(response).to redirect_to params[:to]
end
end
end
end
end
end
FactoryGirl.define do
factory :commit_status, class: CommitStatus do
started_at 'Di 29. Okt 09:51:28 CET 2013'
finished_at 'Di 29. Okt 09:53:28 CET 2013'
name 'default'
status 'success'
description 'commit status'
commit factory: :ci_commit_with_one_job
started_at 'Tue, 26 Jan 2016 08:21:42 +0100'
finished_at 'Tue, 26 Jan 2016 08:23:42 +0100'
after(:build) do |build, evaluator|
build.project = build.commit.project
end
factory :generic_commit_status, class: GenericCommitStatus do
name 'generic'
......
......@@ -16,6 +16,26 @@ describe 'Commits' do
FactoryGirl.create :ci_commit, project: project, sha: project.commit.sha
end
context 'commit status is Generic Commit Status' do
let!(:status) { FactoryGirl.create :generic_commit_status, commit: commit }
describe 'Commit builds' do
before do
visit ci_status_path(commit)
end
it { expect(page).to have_content commit.sha[0..7] }
it 'contains generic commit status build' do
page.within('.table-holder') do
expect(page).to have_content "##{status.id}" # build id
expect(page).to have_content 'generic' # build name
end
end
end
end
context 'commit status is Ci Build' do
let!(:build) { FactoryGirl.create :ci_build, commit: commit }
describe 'Project commits' do
......@@ -97,4 +117,5 @@ describe 'Commits' do
end
end
end
end
end
......@@ -66,5 +66,10 @@ describe LabelsHelper do
it 'uses dark text on light backgrounds' do
expect(text_color_for_bg('#EEEEEE')).to eq('#333333')
end
it 'supports RGB triplets' do
expect(text_color_for_bg('#FFF')).to eq '#333333'
expect(text_color_for_bg('#000')).to eq '#FFFFFF'
end
end
end
......@@ -135,6 +135,17 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
message = "resolve #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
context 'with an external issue tracker reference' do
it 'extracts the referenced issue' do
jira_project = create(:jira_project, name: 'JIRA_EXT1')
jira_issue = ExternalIssue.new("#{jira_project.name}-1", project: jira_project)
closing_issue_extractor = described_class.new jira_project
message = "Resolve #{jira_issue.to_reference}"
expect(closing_issue_extractor.closed_by_message(message)).to eq([jira_issue])
end
end
end
context "with a cross-project reference" do
......
......@@ -66,6 +66,16 @@ describe Gitlab::Metrics::Instrumentation do
@dummy.foo
end
it 'generates a method with the correct arity when using methods without arguments' do
dummy = Class.new do
def self.test; end
end
described_class.instrument_method(dummy, :test)
expect(dummy.method(:test).arity).to eq(0)
end
end
describe 'with metrics disabled' do
......
......@@ -10,6 +10,21 @@ describe ExternalIssue, models: true do
it { is_expected.to include_module(Referable) }
end
describe '.reference_pattern' do
it 'allows underscores in the project name' do
expect(ExternalIssue.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
end
it 'allows numbers in the project name' do
expect(ExternalIssue.reference_pattern.match('EXT3_EXT-1234')[0]).to eq 'EXT3_EXT-1234'
end
it 'requires the project name to begin with A-Z' do
expect(ExternalIssue.reference_pattern.match('3EXT_EXT-1234')).to eq nil
expect(ExternalIssue.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
end
end
describe '#to_reference' do
it 'returns a String reference to the object' do
expect(issue.to_reference).to eq issue.id
......
......@@ -219,4 +219,24 @@ describe Repository, models: true do
end
end
end
describe '#has_visible_content?' do
subject { repository.has_visible_content? }
describe 'when there are no branches' do
before do
allow(repository.raw_repository).to receive(:branch_count).and_return(0)
end
it { is_expected.to eq(false) }
end
describe 'when there are branches' do
before do
allow(repository.raw_repository).to receive(:branch_count).and_return(3)
end
it { is_expected.to eq(true) }
end
end
end
require 'spec_helper'
describe Tree, models: true do
let(:repository) { create(:project).repository }
let(:sha) { repository.root_ref }
subject { described_class.new(repository, '54fcc214') }
describe '#readme' do
class FakeBlob
attr_reader :name
def initialize(name)
@name = name
end
def readme?
name =~ /^readme/i
end
end
it 'returns nil when repository does not contains a README file' do
files = [FakeBlob.new('file'), FakeBlob.new('license'), FakeBlob.new('copying')]
expect(subject).to receive(:blobs).and_return(files)
expect(subject.readme).to eq nil
end
it 'returns nil when repository does not contains a previewable README file' do
files = [FakeBlob.new('file'), FakeBlob.new('README.pages'), FakeBlob.new('README.png')]
expect(subject).to receive(:blobs).and_return(files)
expect(subject.readme).to eq nil
end
it 'returns README when repository contains a previewable README file' do
files = [FakeBlob.new('README.png'), FakeBlob.new('README'), FakeBlob.new('file')]
expect(subject).to receive(:blobs).and_return(files)
expect(subject.readme.name).to eq 'README'
end
it 'returns first previewable README when repository contains more than one' do
files = [FakeBlob.new('file'), FakeBlob.new('README.md'), FakeBlob.new('README.asciidoc')]
expect(subject).to receive(:blobs).and_return(files)
expect(subject.readme.name).to eq 'README.md'
end
it 'returns first plain text README when repository contains more than one' do
files = [FakeBlob.new('file'), FakeBlob.new('README'), FakeBlob.new('README.txt')]
expect(subject).to receive(:blobs).and_return(files)
expect(subject.readme.name).to eq 'README'
end
it 'prioritizes previewable README file over one in plain text' do
files = [FakeBlob.new('file'), FakeBlob.new('README'), FakeBlob.new('README.md')]
expect(subject).to receive(:blobs).and_return(files)
expect(subject.readme.name).to eq 'README.md'
end
end
end
require 'spec_helper'
describe Projects::ImportService, services: true do
let!(:project) { create(:empty_project) }
let(:user) { project.creator }
subject { described_class.new(project, user) }
describe '#execute' do
context 'with unknown url' do
before do
project.import_url = Project::UNKNOWN_IMPORT_URL
end
it 'succeeds if repository is created successfully' do
expect(project).to receive(:create_repository).and_return(true)
result = subject.execute
expect(result[:status]).to eq :success
end
it 'fails if repository creation fails' do
expect(project).to receive(:create_repository).and_return(false)
result = subject.execute
expect(result[:status]).to eq :error
expect(result[:message]).to eq 'The repository could not be created.'
end
end
context 'with known url' do
before do
project.import_url = 'https://github.com/vim/vim.git'
end
it 'succeeds if repository import is successfully' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
result = subject.execute
expect(result[:status]).to eq :success
end
it 'fails if repository import fails' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_raise(Gitlab::Shell::Error.new('Failed to import the repository'))
result = subject.execute
expect(result[:status]).to eq :error
expect(result[:message]).to eq 'Failed to import the repository'
end
end
context 'with valid importer' do
before do
stub_github_omniauth_provider
project.import_url = 'https://github.com/vim/vim.git'
project.import_type = 'github'
allow(project).to receive(:import_data).and_return(double.as_null_object)
end
it 'succeeds if importer succeeds' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(true)
result = subject.execute
expect(result[:status]).to eq :success
end
it 'fails if importer fails' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(false)
result = subject.execute
expect(result[:status]).to eq :error
expect(result[:message]).to eq 'The remote data could not be imported.'
end
it 'fails if importer raise an error' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_raise(Projects::ImportService::Error.new('Github: failed to connect API'))
result = subject.execute
expect(result[:status]).to eq :error
expect(result[:message]).to eq 'Github: failed to connect API'
end
end
def stub_github_omniauth_provider
provider = OpenStruct.new(
name: 'github',
app_id: 'asd123',
app_secret: 'asd123'
)
Gitlab.config.omniauth.providers << provider
end
end
end
......@@ -48,4 +48,10 @@ FactoryGirl::SyntaxRunner.class_eval do
include RSpec::Mocks::ExampleMethods
end
# Work around a Rails 4.2.5.1 issue
# See https://github.com/rspec/rspec-rails/issues/1532
RSpec::Rails::ViewRendering::EmptyTemplatePathSetDecorator.class_eval do
alias_method :find_all_anywhere, :find_all
end
ActiveRecord::Migration.maintain_test_schema!
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment