Commit 3b73db5a authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into ce-upstream

parents ac70bdcd a8b7ac91
image: "ruby:2.1"
image: "ruby:2.2"
services:
- mysql:latest
......@@ -134,3 +134,26 @@ bundler:audit:
- ruby
- mysql
allow_failure: true
# Ruby 2.1 jobs
spec:ruby21:
image: ruby:2.1
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec
tags:
- ruby
- mysql
only:
- master
spinach:ruby21:
image: ruby:2.1
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach
tags:
- ruby
- mysql
only:
- master
Please view this file on the master branch, on stable branches it's out of date.
v 8.5.0 (unreleased)
- Ensure rake tasks that don't need a DB connection can be run without one
- Add "visibility" flag to GET /projects api endpoint
- Ignore binary files in code search to prevent Error 500 (Stan Hu)
- Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push
- New UI for pagination
- Don't prevent sign out when 2FA enforcement is enabled and user hasn't yet
set it up
- 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
- Display 404 error on group not found
- Track project import failure
- Fix visibility level text in admin area (Zeger-Jan van de Weg)
- Update the ExternalIssue regex pattern (Blake Hitchcock)
- Revert "Add IP check against DNSBLs at account sign-up"
- Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
- Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
v 8.4.3
- Increase lfs_objects size column to 8-byte integer to allow files larger than 2.1GB
v 8.4.2
- 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
- Fix updating the runner information when asking for builds
- 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.
- Correctly highlight MR diff when MR has merge conflicts
- Increase contrast between highlighted code comments and inline diff marker
- Fix method undefined when using external commit status in builds
- Fix highlighting in blame view.
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 (unreleased)
- Hide issues settings when issues are disabled (Hannes Rosenögger)
......
source "https://rubygems.org"
gem 'rails', '4.2.5'
gem 'rails', '4.2.5.1'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
......@@ -110,7 +110,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'
......@@ -308,7 +309,7 @@ end
gem "newrelic_rpm", '~> 3.9.4.245'
gem 'newrelic-grape'
gem 'octokit', '~> 3.7.0'
gem 'octokit', '~> 3.8.0'
gem "mail_room", "~> 0.6.1"
......
......@@ -4,41 +4,41 @@ GEM
CFPropertyList (2.3.2)
RedCloth (4.2.9)
ace-rails-ap (2.0.1)
actionmailer (4.2.5)
actionpack (= 4.2.5)
actionview (= 4.2.5)
activejob (= 4.2.5)
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.5)
actionview (= 4.2.5)
activesupport (= 4.2.5)
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.5)
activesupport (= 4.2.5)
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.5)
activesupport (= 4.2.5)
activejob (4.2.5.1)
activesupport (= 4.2.5.1)
globalid (>= 0.3.0)
activemodel (4.2.5)
activesupport (= 4.2.5)
activemodel (4.2.5.1)
activesupport (= 4.2.5.1)
builder (~> 3.1)
activerecord (4.2.5)
activemodel (= 4.2.5)
activesupport (= 4.2.5)
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.5)
activesupport (4.2.5.1)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
......@@ -506,7 +506,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)
......@@ -516,7 +516,7 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (~> 1.2)
octokit (3.7.1)
octokit (3.8.0)
sawyer (~> 0.6.0, >= 0.5.3)
omniauth (1.2.2)
hashie (>= 1.2, < 4)
......@@ -612,16 +612,16 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.5)
actionmailer (= 4.2.5)
actionpack (= 4.2.5)
actionview (= 4.2.5)
activejob (= 4.2.5)
activemodel (= 4.2.5)
activerecord (= 4.2.5)
activesupport (= 4.2.5)
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.5)
railties (= 4.2.5.1)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
......@@ -629,11 +629,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.5)
actionpack (= 4.2.5)
activesupport (= 4.2.5)
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)
......@@ -749,7 +749,7 @@ GEM
activesupport (>= 3.1, < 4.3)
select2-rails (3.5.9.3)
thor (~> 0.14)
sentry-raven (0.15.3)
sentry-raven (0.15.4)
faraday (>= 0.7.6)
settingslogic (2.0.9)
sexp_processor (4.6.0)
......@@ -992,10 +992,10 @@ 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)
octokit (~> 3.8.0)
omniauth (~> 1.2.2)
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-bitbucket (~> 0.0.2)
......@@ -1018,7 +1018,7 @@ DEPENDENCIES
rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rails (= 4.2.5)
rails (= 4.2.5.1)
rails-deprecated_sanitizer (~> 1.0.3)
raphael-rails (~> 2.1.2)
rblineprof
......
......@@ -93,7 +93,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the
GitLab is a Ruby on Rails application that runs on the following software:
- Ubuntu/Debian/CentOS/RHEL
- Ruby (MRI) 2.1
- Ruby (MRI) 2.1 or 2.2
- Git 1.7.10+
- Redis 2.8+
- MySQL or PostgreSQL
......
......@@ -5,7 +5,10 @@
# the compiled file.
#
#= require jquery
#= require jquery-ui
#= require jquery-ui/autocomplete
#= require jquery-ui/datepicker
#= require jquery-ui/effect-highlight
#= require jquery-ui/sortable
#= require jquery_ujs
#= require jquery.cookie
#= require jquery.endless-scroll
......@@ -207,4 +210,13 @@ $ ->
form = btn.closest("form")
new ConfirmDangerModal(form, text, warningMessage: warningMessage)
$('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()
......@@ -4,6 +4,7 @@ class @AwardsHandler
event.stopPropagation()
event.preventDefault()
$(".emoji-menu").show()
$("#emoji_search").focus()
$("html").on 'click', (event) ->
if !$(event.target).closest(".emoji-menu").length
......
......@@ -32,6 +32,7 @@ class @EditBlob
content: editor.getValue()
, (response) ->
currentPane.empty().append response
currentPane.syntaxHighlight()
return
else
......
......@@ -50,6 +50,7 @@ class @Issue
new Flash(issueFailMessage, 'alert')
success: (data, textStatus, jqXHR) ->
if data.saved
$(document).trigger('issuable:change');
if isClose
$('a.btn-close').addClass('hidden')
$('a.btn-reopen').removeClass('hidden')
......
......@@ -64,6 +64,9 @@ class @Notes
# fetch notes when tab becomes visible
$(document).on "visibilitychange", @visibilityChange
# when issue status changes, we need to refresh data
$(document).on "issuable:change", @refresh
cleanBinding: ->
$(document).off "ajax:success", ".js-main-target-form"
$(document).off "ajax:success", ".js-discussion-note-form"
......
......@@ -9,11 +9,13 @@ class @ProjectsList
$(".projects-list-filter").keyup ->
terms = $(this).val()
uiBox = $('div.projects-list-holder')
filterSelector = $(this).data('filter-selector') || 'span.filter-title'
if terms == "" || terms == undefined
uiBox.find("ul.projects-list li").show()
else
uiBox.find("ul.projects-list li").each (index) ->
name = $(this).find("span.filter-title").text()
name = $(this).find(filterSelector).text()
if name.toLowerCase().search(terms.toLowerCase()) == -1
$(this).hide()
......
......@@ -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', =>
......
......@@ -320,14 +320,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,7 +33,7 @@ table {
background-color: $background-color;
font-weight: normal;
font-size: 15px;
border-bottom: 1px solid $border-color !important;
border-bottom: 1px solid $border-color;
&.sortable {
cursor: pointer;
......@@ -41,8 +41,8 @@ table {
}
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);
}
......@@ -116,7 +115,7 @@
ul, ol {
padding: 0;
margin: 6px 0 6px 18px !important;
margin: 6px 0 6px 28px !important;
}
li {
......@@ -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 {
......@@ -22,11 +22,11 @@
// Diff line
.line_holder {
.diff-line-num.new, .line_content.new {
@include diff_background(rgba(51, 255, 51, 0.1), rgba(51, 255, 51, 0.3), #808080);
@include diff_background(rgba(51, 255, 51, 0.1), rgba(51, 255, 51, 0.2), #808080);
}
.diff-line-num.old, .line_content.old {
@include diff_background(rgba(255, 51, 51, 0.2), rgba(255, 51, 51, 0.3), #808080);
@include diff_background(rgba(255, 51, 51, 0.2), rgba(255, 51, 51, 0.25), #808080);
}
.line_content.match {
......
......@@ -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 {
......@@ -22,11 +22,11 @@
// Diff line
.line_holder {
.diff-line-num.new, .line_content.new {
@include diff_background(rgba(166, 226, 46, 0.2), rgba(166, 226, 46, 0.3), #808080);
@include diff_background(rgba(166, 226, 46, 0.1), rgba(166, 226, 46, 0.15), #808080);
}
.diff-line-num.old, .line_content.old {
@include diff_background(rgba(254, 147, 140, 0.2), rgba(254, 147, 140, 0.3), #808080);
@include diff_background(rgba(254, 147, 140, 0.15), rgba(254, 147, 140, 0.2), #808080);
}
.line_content.match {
......
......@@ -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.15), rgba(133, 153, 0, 0.25), #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.25), #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.25), #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.25), #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;
......
......@@ -564,3 +564,53 @@ pre.light-well {
color: #E62958;
margin-top: 2px;
}
/*
* Forks list rendered on Project's forks page
*/
.forks-top-block {
padding: 16px 0;
}
.projects-search-form {
.dropdown-toggle.btn {
margin-top: -3px;
}
&.fork-search-form {
margin: 0;
margin-top: -$gl-padding;
padding-bottom: 0;
input {
/* Small devices (tablets, 768px and up) */
@media (min-width: $screen-sm-min) { width: 180px; }
/* Medium devices (desktops, 992px and up) */
@media (min-width: $screen-md-min) { width: 350px; }
/* Large devices (large desktops, 1200px and up) */
@media (min-width: $screen-lg-min) { width: 400px; }
}
.sort-forks {
width: 160px;
}
.fork-link {
float: right;
margin-left: $gl-padding;
}
}
}
.private-forks-notice .private-fork-icon {
i:nth-child(1) {
color: #2AA056;
}
i:nth-child(2) {
color: #FFFFFF;
}
}
......@@ -22,8 +22,6 @@
&:hover {
td {
background: $hover;
border-top: 1px solid #ADF;
border-bottom: 1px solid #ADF;
}
cursor: pointer;
}
......
......@@ -76,8 +76,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:metrics_timeout,
:metrics_method_call_threshold,
:metrics_sample_interval,
:ip_blocking_enabled,
:dnsbl_servers_list,
:recaptcha_enabled,
:recaptcha_site_key,
:recaptcha_private_key,
......
......@@ -308,7 +308,8 @@ class ApplicationController < ActionController::Base
end
def set_filters_params
params[:sort] ||= 'id_desc'
set_default_sort
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
......@@ -415,4 +416,24 @@ class ApplicationController < ActionController::Base
current_user.nil? && root_path == request.path
end
private
def set_default_sort
key = if is_a_listing_page_for?('issues') || is_a_listing_page_for?('merge_requests')
'issuable_sort'
end
cookies[key] = params[:sort] if key && params[:sort].present?
params[:sort] = cookies[key] if key
params[:sort] ||= 'id_desc'
end
def is_a_listing_page_for?(page_type)
controller_name, action_name = params.values_at(:controller, :action)
(controller_name == "projects/#{page_type}" && action_name == 'index') ||
(controller_name == 'groups' && action_name == page_type) ||
(controller_name == 'dashboard' && action_name == page_type)
end
end
......@@ -36,7 +36,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
private
def load_events
@events = Event.in_projects(@projects.pluck(:id))
@events = Event.in_projects(@projects)
@events = @event_filter.apply_filter(@events).with_associations
@events = @events.limit(20).offset(params[:offset] || 0)
end
......
......@@ -23,14 +23,14 @@ class DashboardController < Dashboard::ApplicationController
protected
def load_events
project_ids =
projects =
if params[:filter] == "starred"
current_user.starred_projects
else
current_user.authorized_projects
end.pluck(:id)
end
@events = Event.in_projects(project_ids)
@events = Event.in_projects(projects)
@events = @event_filter.apply_filter(@events).with_associations
@events = @events.limit(20).offset(params[:offset] || 0)
end
......
......@@ -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
......@@ -89,16 +90,13 @@ class GroupsController < Groups::ApplicationController
def group
@group ||= Group.find_by(path: params[:id])
@group || render_404
end
def load_projects
@projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity.non_archived
end
def project_ids
@projects.pluck(:id)
end
# Dont allow unauthorized access to group
def authorize_read_group!
unless @group and (@projects.present? or can?(current_user, :read_group, @group))
......@@ -131,7 +129,7 @@ class GroupsController < Groups::ApplicationController
end
def load_events
@events = Event.in_projects(project_ids)
@events = Event.in_projects(@projects)
@events = event_filter.apply_filter(@events).with_associations
@events = @events.limit(20).offset(params[:offset] || 0)
end
......
......@@ -13,10 +13,10 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
current_user.save! if current_user.changed?
if two_factor_grace_period_expired?
flash.now[:alert] = 'You must configure Two-Factor Authentication in your account.'
flash.now[:alert] = 'You must enable Two-factor Authentication for your account.'
else
grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
flash.now[:alert] = "You must configure Two-Factor Authentication in your account until #{l(grace_period_deadline)}."
flash.now[:alert] = "You must enable Two-factor Authentication for your account before #{l(grace_period_deadline)}."
end
@qr_code = build_qr_code
......
......@@ -8,28 +8,6 @@ class Projects::BlameController < Projects::ApplicationController
def show
@blob = @repository.blob_at(@commit.id, @path)
@blame = group_blame_lines
end
def group_blame_lines
blame = Gitlab::Git::Blame.new(@repository, @commit.id, @path)
prev_sha = nil
groups = []
current_group = nil
blame.each do |commit, line|
if prev_sha && prev_sha == commit.sha
current_group[:lines] << line
else
groups << current_group if current_group.present?
current_group = { commit: commit, lines: [line] }
end
prev_sha = commit.sha
end
groups << current_group if current_group.present?
groups
@blame_groups = Gitlab::Blame.new(@blob, @commit).groups
end
end
......@@ -21,7 +21,7 @@ class Projects::CompareController < Projects::ApplicationController
@commits = Commit.decorate(compare_result.commits, @project)
@diffs = compare_result.diffs
@commit = @project.commit(head_ref)
@base_commit = @project.commit(base_ref)
@base_commit = @project.merge_base_commit(base_ref, head_ref)
@diff_refs = [@base_commit, @commit]
@line_notes = []
end
......
......@@ -3,6 +3,15 @@ class Projects::ForksController < Projects::ApplicationController
before_action :require_non_empty_project
before_action :authorize_download_code!
def index
@sort = params[:sort] || 'id_desc'
@all_forks = project.forks.includes(:creator).order_by(@sort)
@public_forks, @protected_forks = @all_forks.partition do |project|
can?(current_user, :read_project, project)
end
end
def new
@namespaces = current_user.manageable_namespaces
@namespaces.delete(@project.namespace)
......@@ -10,7 +19,7 @@ class Projects::ForksController < Projects::ApplicationController
def create
namespace = Namespace.find(params[:namespace_key])
@forked_project = namespace.projects.find_by(path: project.path)
@forked_project = nil unless @forked_project && @forked_project.forked_from_project == project
......
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
......@@ -22,11 +22,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)
......@@ -34,6 +34,7 @@ class Projects::ImportsController < Projects::ApplicationController
if continue_params && continue_params[:notice_now]
flash.now[:notice] = continue_params[:notice_now]
end
# Render
end
end
......@@ -42,6 +43,7 @@ class Projects::ImportsController < Projects::ApplicationController
def continue_params
continue_params = params[:continue]
if continue_params
continue_params.permit(:to, :notice, :notice_now)
else
......@@ -49,8 +51,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
......
......@@ -106,7 +106,7 @@ class Projects::NotesController < Projects::ApplicationController
{ notes_left: [note], notes_right: [] }
else
{ notes_left: [], notes_right: [note] }
end
end
else
template = "projects/notes/_diff_notes_with_reply"
locals = { notes: [note] }
......
......@@ -8,11 +8,6 @@ class RegistrationsController < Devise::RegistrationsController
def create
if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
if Gitlab::IpCheck.new(request.remote_ip).spam?
flash[:alert] = 'Could not create an account. This IP is listed for spam.'
return render action: 'new'
end
super
else
flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code."
......
......@@ -2,6 +2,8 @@ class SessionsController < Devise::SessionsController
include AuthenticatesWithTwoFactor
include Recaptcha::ClientHelper
skip_before_action :check_2fa_requirement, only: [:destroy]
prepend_before_action :authenticate_with_two_factor, only: [:create]
prepend_before_action :store_redirect_path, only: [:new]
before_action :gitlab_geo_login, only: [:new]
......
......@@ -174,7 +174,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?
......
......@@ -36,8 +36,7 @@ module BlobHelper
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now
}
fork_path = namespace_project_fork_path(project.namespace, project, namespace_key: current_user.namespace.id,
continue: continue_params)
fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
link_to "Edit", fork_path, class: 'btn', method: :post
end
......@@ -62,8 +61,7 @@ module BlobHelper
notice: edit_in_new_fork_notice + " Try to #{action} this file again.",
notice_now: edit_in_new_fork_notice_now
}
fork_path = namespace_project_fork_path(project.namespace, project, namespace_key: current_user.namespace.id,
continue: continue_params)
fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
link_to label, fork_path, class: "btn btn-#{btn_class}", method: :post
end
......
......@@ -152,7 +152,7 @@ module CommitsHelper
options = {
class: "commit-#{options[:source]}-link has_tooltip",
data: { :'original-title' => sanitize(source_email) }
data: { 'original-title'.to_sym => sanitize(source_email) }
}
if user.nil?
......
......@@ -7,7 +7,7 @@ module IconsHelper
# font-awesome-rails gem, but should we ever use a different icon pack in the
# future we won't have to change hundreds of method calls.
def icon(names, options = {})
fa_icon(names, options)
options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
end
def spinner(text = nil, visible = false)
......
......@@ -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'
......
......@@ -40,7 +40,7 @@ module ProjectsHelper
link_to(author_html, user_path(author), class: "author_link").html_safe
else
title = opts[:title].sub(":name", sanitize(author.name))
link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => title, container: 'body' } ).html_safe
link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
end
end
......@@ -116,7 +116,7 @@ module ProjectsHelper
private
def get_project_nav_tabs(project, current_user)
nav_tabs = [:home]
nav_tabs = [:home, :forks]
if !project.empty_repo? && can?(current_user, :download_code, project)
nav_tabs << [:files, :commits, :network, :graphs]
......
......@@ -44,8 +44,6 @@
# metrics_port :integer default(8089)
# sentry_enabled :boolean default(FALSE)
# sentry_dsn :string
# ip_blocking_enabled :boolean default(FALSE)
# dns_blacklist_threshold :float default(0.33)
#
class ApplicationSetting < ActiveRecord::Base
......
......@@ -47,7 +47,11 @@ class Event < ActiveRecord::Base
# Scopes
scope :recent, -> { reorder(id: :desc) }
scope :code_push, -> { where(action: PUSHED) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
scope :in_projects, ->(projects) do
where(project_id: projects.select(:id).reorder(nil)).recent
end
scope :with_associations, -> { includes(project: :namespace) }
scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
scope :issues, -> { where(target_type: 'Issue') }
......@@ -70,12 +74,6 @@ class Event < ActiveRecord::Base
[Event::CREATED, Event::CLOSED, Event::MERGED])
end
def latest_update_time
row = select(:updated_at, :project_id).reorder(id: :desc).take
row ? row.updated_at : nil
end
def limit_recent(limit = 20, offset = nil)
recent.limit(limit).offset(offset)
end
......
......@@ -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)
......
......@@ -19,7 +19,7 @@ require 'file_size_validator'
class Group < Namespace
include Gitlab::ConfigHelper
include Referable
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
alias_method :members, :group_members
has_many :users, through: :group_members
......
......@@ -187,8 +187,8 @@ class MergeRequest < ActiveRecord::Base
def diff_base_commit
if merge_request_diff
merge_request_diff.base_commit
else
self.target_project.commit(self.target_branch)
elsif source_sha
self.target_project.merge_base_commit(self.source_sha, self.target_branch)
end
end
......@@ -545,7 +545,7 @@ class MergeRequest < ActiveRecord::Base
end
def source_sha
commits.first.sha
last_commit.try(:sha)
end
def fetch_ref
......
......@@ -48,14 +48,11 @@ class MergeRequestDiff < ActiveRecord::Base
end
def diffs_no_whitespace
# Get latest sha of branch from source project
source_sha = merge_request.source_project.commit(source_branch).sha
compare_result = Gitlab::CompareResult.new(
Gitlab::Git::Compare.new(
merge_request.target_project.repository.raw_repository,
merge_request.target_branch,
source_sha,
self.repository.raw_repository,
self.target_branch,
self.source_sha,
), { ignore_whitespace_change: true }
)
@diffs_no_whitespace ||= load_diffs(dump_commits(compare_result.diffs))
......@@ -83,8 +80,6 @@ class MergeRequestDiff < ActiveRecord::Base
@last_commit_short_sha ||= last_commit.short_id
end
private
def dump_commits(commits)
commits.map(&:to_hash)
end
......@@ -163,7 +158,7 @@ class MergeRequestDiff < ActiveRecord::Base
self.st_diffs = new_diffs
self.base_commit_sha = merge_request.target_project.commit(target_branch).try(:sha)
self.base_commit_sha = self.repository.merge_base(self.source_sha, self.target_branch)
self.save
end
......@@ -181,7 +176,10 @@ class MergeRequestDiff < ActiveRecord::Base
merge_request.target_project.repository
end
private
def source_sha
source_commit = merge_request.source_project.commit(source_branch)
source_commit.try(:sha)
end
def compare_result
@compare_result ||=
......@@ -189,15 +187,11 @@ class MergeRequestDiff < ActiveRecord::Base
# Update ref for merge request
merge_request.fetch_ref
# Get latest sha of branch from source project
source_commit = merge_request.source_project.commit(source_branch)
source_sha = source_commit.try(:sha)
Gitlab::CompareResult.new(
Gitlab::Git::Compare.new(
merge_request.target_project.repository.raw_repository,
merge_request.target_branch,
source_sha,
self.repository.raw_repository,
self.target_branch,
self.source_sha
)
)
end
......
......@@ -383,6 +383,11 @@ class Project < ActiveRecord::Base
repository.commit(id)
end
def merge_base_commit(first_commit_id, second_commit_id)
sha = repository.merge_base(first_commit_id, second_commit_id)
repository.commit(sha) if sha
end
def saved?
id && persisted?
end
......
......@@ -664,6 +664,8 @@ class Repository
def merge_base(first_commit_id, second_commit_id)
rugged.merge_base(first_commit_id, second_commit_id)
rescue Rugged::ReferenceError
nil
end
def is_ancestor?(ancestor_id, descendant_id)
......
......@@ -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
......
......@@ -110,7 +110,7 @@ class WikiPage
# Returns boolean True or False if this instance
# is an old version of the page.
def historical?
@page.historical?
@page.historical? && versions.first.sha != version.sha
end
# Returns boolean True or False if this instance
......
......@@ -6,27 +6,12 @@ module Notes
note.system = false
if note.save
notification_service.new_note(note)
# Skip system notes, like status changes and cross-references and awards
unless note.system || note.is_award
event_service.leave_note(note, note.author)
note.create_cross_references!
execute_hooks(note)
end
# Finish the harder work in the background
NewNoteWorker.perform_in(2.seconds, note.id, params)
end
note
end
def hook_data(note)
Gitlab::NoteDataBuilder.build(note, current_user)
end
def execute_hooks(note)
note_data = hook_data(note)
note.project.execute_hooks(note_data, :note_hooks)
note.project.execute_services(note_data, :note_hooks)
end
end
end
module Notes
class PostProcessService
attr_accessor :note
def initialize(note)
@note = note
end
def execute
# Skip system notes, like status changes and cross-references and awards
unless @note.system || @note.is_award
EventCreateService.new.leave_note(@note, @note.author)
@note.create_cross_references!
execute_note_hooks
end
end
def hook_data
Gitlab::NoteDataBuilder.build(@note, @note.author)
end
def execute_note_hooks
note_data = hook_data
@note.project.execute_hooks(note_data, :note_hooks)
@note.project.execute_services(note_data, :note_hooks)
end
end
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
......@@ -225,22 +225,6 @@
%fieldset
%legend Spam and Anti-bot Protection
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :ip_blocking_enabled do
= f.check_box :ip_blocking_enabled
Enable IP check against blacklist at sign-up
.help-block Helps preventing accounts creation from 'known spam sources'
.form-group
= f.label :dnsbl_servers_list, class: 'control-label col-sm-2' do
DNSBL servers list
.col-sm-10
= f.text_field :dnsbl_servers_list, class: 'form-control'
.help-block
Please enter DNSBL servers separated with comma
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
......@@ -281,4 +265,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,7 +21,7 @@
- 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"
- if @group.persisted?
......
......@@ -17,7 +17,7 @@
.pull-right
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
......
......@@ -37,8 +37,7 @@
- @hooks.each do |hook|
%li
.list-item-name
= link_to admin_hook_path(hook) do
%strong= hook.url
%strong= hook.url
%p SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
.pull-right
......
......@@ -50,7 +50,7 @@
.controls
.dropdown.inline
%button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
......
......@@ -32,7 +32,7 @@
.pull-right
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
......
......@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link href: dashboard_projects_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html"
xml.id dashboard_projects_url
xml.updated @events.latest_update_time.xmlschema if @events.any?
xml.updated @events[0].updated_at.xmlschema if @events[0]
@events.each do |event|
event_to_atom(xml, event)
......
......@@ -18,7 +18,7 @@
.pull-right
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
......
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- elsif current_page?(trending_explore_projects_path) || current_page?(explore_root_path)
......@@ -24,4 +24,3 @@
= sort_title_recently_updated
= link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated
......@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link href: group_url(@group, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
xml.link href: group_url(@group), rel: "alternate", type: "text/html"
xml.id group_url(@group)
xml.updated @events.latest_update_time.xmlschema if @events.any?
xml.updated @events[0].updated_at.xmlschema if @events[0]
@events.each do |event|
event_to_atom(xml, event)
......
......@@ -98,6 +98,13 @@
%span
Wiki
- if project_nav_tab? :forks
= nav_link(controller: :forks, action: :index) do
= link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks' do
= icon('code-fork fw')
%span
Forks
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
= link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
......
......@@ -15,12 +15,11 @@
.file-content.blame.code.js-syntax-highlight
%table
- current_line = 1
- blame_highlighter = highlighter(@blob.name, @blob.data, nowrap: true)
- @blame.each do |blame_group|
- @blame_groups.each do |blame_group|
%tr
%td.blame-commit
.commit
- commit = Commit.new(blame_group[:commit], @project)
- commit = blame_group[:commit]
.commit-row-title
%strong
= link_to_gfm truncate(commit.title, length: 35), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "cdark"
......@@ -38,8 +37,7 @@
\
- current_line += line_count
%td.lines
%pre{class: 'code highlight'}
%pre.code.highlight
%code
- blame_group[:lines].each do |line|
:preserve
#{blame_highlighter.highlight(line)}
#{line}
......@@ -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
- 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|
......
......@@ -10,7 +10,7 @@
&nbsp;
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
%span.light
- if @sort.present?
= @sort.humanize
- else
......
......@@ -46,7 +46,7 @@
- continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now }
- fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
- fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('file fw')
......
......@@ -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)
......
.gray-content-block.top-block.clearfix.white.forks-top-block
.pull-left
- public_count = @public_forks.size
- protected_count = @protected_forks.size
- full_count_title = "#{public_count} public and #{protected_count} private"
== #{pluralize(@all_forks.size, 'fork')}: #{full_count_title}
.pull-right
.projects-search-form.fork-search-form
= search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter form-control',
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown.inline.prepend-left-10
%button.dropdown-toggle.btn.sort-forks{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
%b.caret
%ul.dropdown-menu.dropdown-menu-align-right
%li
- excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id]
= link_to page_filter_path(sort: sort_value_recently_created, without: excluded_filters) do
= sort_title_recently_created
= link_to page_filter_path(sort: sort_value_oldest_created, without: excluded_filters) do
= sort_title_oldest_created
= link_to page_filter_path(sort: sort_value_recently_updated, without: excluded_filters) do
= sort_title_recently_updated
= link_to page_filter_path(sort: sort_value_oldest_updated, without: excluded_filters) do
= sort_title_oldest_updated
.fork-link.inline
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'pull-right btn btn-new' do
= icon('code-fork fw')
Fork
- else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'pull-right btn btn-new' do
= icon('code-fork fw')
Fork
.projects-list-holder
- if @public_forks.blank?
%ul.content-list
%li
.nothing-here-block No forks to show
- else
= render 'shared/projects/list', projects: @public_forks, use_creator_avatar: true,
forks: true, show_last_commit_as_description: true
- if protected_count > 0
%ul.projects-list.private-forks-notice
%li.project-row
= icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
%strong= pluralize(protected_count, 'private fork')
%span you have no access to.
......@@ -22,7 +22,7 @@
- else
.fork-thumbnail
= link_to namespace_project_fork_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do
= link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do
= image_tag namespace_icon(namespace, 100)
.caption
%strong
......
......@@ -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
......
......@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
xml.link href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html"
xml.id namespace_project_url(@project.namespace, @project)
xml.updated @events.latest_update_time.xmlschema if @events.any?
xml.updated @events[0].updated_at.xmlschema if @events[0?
@events.each do |event|
event_to_atom(xml, event)
......
......@@ -40,7 +40,7 @@
- continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now }
- fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
- fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('pencil fw')
......@@ -49,7 +49,7 @@
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to upload a file again.",
notice_now: edit_in_new_fork_notice_now }
- fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
- fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('file fw')
......@@ -58,7 +58,7 @@
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to create a new directory again.",
notice_now: edit_in_new_fork_notice_now }
- fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
- fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
continue: continue_params)
= link_to fork_path, method: :post do
= icon('folder fw')
......
.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'
.dropdown.inline.prepend-left-10
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
......
= 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']
......
- projects_limit = 20 unless local_assigns[:projects_limit]
- avatar = true unless local_assigns[:avatar] == false
- use_creator_avatar = false unless local_assigns[:use_creator_avatar] == true
- stars = true unless local_assigns[:stars] == false
- forks = false unless local_assigns[:forks] == true
- ci = false unless local_assigns[:ci] == true
- skip_namespace = false unless local_assigns[:skip_namespace] == true
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true
%ul.projects-list
- projects.each_with_index do |project, i|
- css_class = (i >= projects_limit) ? 'hide' : nil
= render "shared/projects/project", project: project, skip_namespace: skip_namespace,
avatar: avatar, stars: stars, css_class: css_class, ci: ci
avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar,
forks: forks, show_last_commit_as_description: show_last_commit_as_description
- if projects.size > projects_limit
%li.bottom.center
......
- avatar = true unless local_assigns[:avatar] == false
- stars = true unless local_assigns[:stars] == false
- forks = false unless local_assigns[:forks] == true
- ci = false unless local_assigns[:ci] == true
- skip_namespace = false unless local_assigns[:skip_namespace] == true
- css_class = '' unless local_assigns[:css_class]
- css_class += " no-description" unless project.description.present?
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true
- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
- ci_commit = project.ci_commit(project.commit.sha) if ci && !project.empty_repo? && project.commit
- cache_key = [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.2']
- cache_key.push(ci_commit.status) if ci_commit
......@@ -13,7 +15,10 @@
= link_to project_path(project), class: dom_class(project) do
- if avatar
.dash-project-avatar
= project_icon(project, alt: '', class: 'avatar project-avatar s46')
- if use_creator_avatar
= image_tag avatar_icon(project.creator.email, 46), class: "avatar s46", alt:''
- else
= project_icon(project, alt: '', class: 'avatar project-avatar s46')
%span.project-full-name
%span.namespace-name
- if project.namespace && !skip_namespace
......@@ -26,10 +31,18 @@
- if ci_commit
= render_ci_status(ci_commit)
&nbsp;
- if forks
%span
= icon('code-fork')
= project.forks_count
- if stars
%span
%i.fa.fa-star
= icon('star')
= project.star_count
- if project.description.present?
- if show_last_commit_as_description
.project-description
= link_to_gfm project.commit.title, namespace_project_commit_path(project.namespace, project, project.commit),
class: "commit-row-message"
- elsif project.description.present?
.project-description
= markdown(project.description, pipeline: :description)
......@@ -3,8 +3,7 @@
.file-content.wiki
= render_markup(@snippet.file_name, @snippet.data)
- else
.file-content.code
= render 'shared/file_highlight', blob: @snippet
= render 'shared/file_highlight', blob: @snippet
- else
.file-content.code
.nothing-here-block Empty file
......@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml"
xml.link href: user_url(@user), rel: "alternate", type: "text/html"
xml.id user_url(@user)
xml.updated @events.latest_update_time.xmlschema if @events.any?
xml.updated @events[0].updated_at.xmlschema if @events[0]
@events.each do |event|
event_to_atom(xml, event)
......
......@@ -7,7 +7,7 @@
- if current_user
.awards-controls
%a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"}
%a.add-award{"href" => "#"}
= icon('smile-o')
.emoji-menu
.emoji-menu-content
......
class NewNoteWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform(note_id, note_params)
note = Note.find(note_id)
NotificationService.new.new_note(note)
Notes::PostProcessService.new(note).execute
end
end
......@@ -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
# Explicitly update mirror so that upstream remote is created and fetched
......
......@@ -236,7 +236,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
......
Haml::Template.options[:ugly] = true
# Remove the `:coffee` and `:coffeescript` filters
#
# See https://git.io/vztMu and http://stackoverflow.com/a/17571242/223897
Haml::Filters.remove_filter('coffee')
Haml::Filters.remove_filter('coffeescript')
......@@ -596,7 +596,7 @@ Rails.application.routes.draw do
end
end
resource :fork, only: [:new, :create]
resources :forks, only: [:index, :new, :create]
resource :import, only: [:new, :create, :show]
resources :refs, only: [] do
......
class RemoveIpBlockingSettingsFromApplicationSettings < ActiveRecord::Migration
def change
remove_column :application_settings, :ip_blocking_enabled, :boolean, default: false
remove_column :application_settings, :dnsbl_servers_list, :text
end
end
class ChangeLfsObjectsSizeColumn < ActiveRecord::Migration
def change
change_column :lfs_objects, :size, :integer, limit: 8
end
end
......@@ -47,6 +47,7 @@ GITLAB_DATABASE_PORT | 5432
## Adding more variables
We welcome merge requests to make more settings configurable via variables.
Please make changes in the file config/initializers/1_settings.rb
Please stick to the naming scheme "GITLAB_#{name 1_settings.rb in upper case}".
## Omnibus configuration
......
......@@ -60,7 +60,7 @@ Parameters:
Shows information about a single merge request.
```
GET /projects/:id/merge_request/:merge_request_id
GET /projects/:id/merge_requests/:merge_request_id
```
Parameters:
......@@ -105,7 +105,7 @@ Parameters:
Get a list of merge request commits.
```
GET /projects/:id/merge_request/:merge_request_id/commits
GET /projects/:id/merge_requests/:merge_request_id/commits
```
Parameters:
......@@ -142,7 +142,7 @@ Parameters:
Shows information about the merge request including its files and changes.
```
GET /projects/:id/merge_request/:merge_request_id/changes
GET /projects/:id/merge_requests/:merge_request_id/changes
```
Parameters:
......@@ -264,7 +264,7 @@ If an error occurs, an error number and a message explaining the reason is retur
Updates an existing merge request. You can change the target branch, title, or even close the MR.
```
PUT /projects/:id/merge_request/:merge_request_id
PUT /projects/:id/merge_requests/:merge_request_id
```
Parameters:
......@@ -323,7 +323,7 @@ If merge request is already merged or closed - you get 405 and error message 'Me
If you don't have permissions to accept this merge request - you'll get a 401
```
PUT /projects/:id/merge_request/:merge_request_id/merge
PUT /projects/:id/merge_requests/:merge_request_id/merge
```
Parameters:
......@@ -373,7 +373,7 @@ If the merge request is already merged or closed - you get 405 and error message
In case the merge request is not set to be merged when the build succeeds, you'll also get a 406 error.
```
PUT /projects/:id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds
PUT /projects/:id/merge_requests/:merge_request_id/cancel_merge_when_build_succeeds
```
Parameters:
......@@ -409,66 +409,6 @@ Parameters:
}
```
## Post comment to MR
Adds a comment to a merge request.
```
POST /projects/:id/merge_request/:merge_request_id/comments
```
Parameters:
- `id` (required) - The ID of a project
- `merge_request_id` (required) - ID of merge request
- `note` (required) - Text of comment
```json
{
"note": "text1"
}
```
## Get the comments on a MR
Gets all the comments associated with a merge request.
```
GET /projects/:id/merge_request/:merge_request_id/comments
```
Parameters:
- `id` (required) - The ID of a project
- `merge_request_id` (required) - ID of merge request
```json
[
{
"note": "this is the 1st comment on the 2merge merge request",
"author": {
"id": 11,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2014-03-06T08:17:35.000Z"
}
},
{
"note": "Status changed to closed",
"author": {
"id": 11,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2014-03-06T08:17:35.000Z"
}
}
]
```
## Comments on merge requets
Comments are done via the notes resource.
Comments are done via the [notes](notes.md) resource.
......@@ -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
......
......@@ -56,12 +56,12 @@ gitlab-ci-multi-runner register \
--non-interactive \
--url "https://gitlab.com/ci/" \
--registration-token "PROJECT_REGISTRATION_TOKEN" \
--description "ruby-2.1" \
--description "ruby-2.2" \
--executor "docker" \
--docker-image ruby:2.1 \
--docker-image ruby:2.2 \
--docker-postgres latest
```
With the command above, you create a runner that uses [ruby:2.1](https://registry.hub.docker.com/u/library/ruby/) image and uses [postgres](https://registry.hub.docker.com/u/library/postgres/) database.
With the command above, you create a runner that uses [ruby:2.2](https://registry.hub.docker.com/u/library/ruby/) image and uses [postgres](https://registry.hub.docker.com/u/library/postgres/) database.
To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password.
......@@ -33,7 +33,7 @@ The YAML syntax allows for using more complex job specifications than in the
above example:
```yaml
image: ruby:2.1
image: ruby:2.2
services:
- postgres
......
......@@ -26,7 +26,7 @@ We use [these build scripts](https://gitlab.com/gitlab-org/gitlab-ci/blob/master
# Build configuration on [Semaphore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for testing the [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
- Language: Ruby
- Ruby version: 2.1.2
- Ruby version: 2.2.4
- database.yml: pg
Build commands
......
......@@ -107,7 +107,7 @@ Then select 'Internet Site' and press enter to confirm the hostname.
## 2. Ruby
_**Note:** The current supported Ruby versions are 2.1.x. Ruby 2.2 and 2.3 are
_**Note:** The current supported Ruby versions are 2.1.x and 2.2.x. Ruby 2.3 is
currently not supported._
The use of Ruby version managers such as [RVM], [rbenv] or [chruby] with GitLab
......@@ -123,9 +123,9 @@ Remove the old Ruby 1.8 if present:
Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby
curl -O --progress https://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.7.tar.gz
echo 'e2e195a4a58133e3ad33b955c829bb536fa3c075 ruby-2.1.7.tar.gz' | shasum -c - && tar xzf ruby-2.1.7.tar.gz
cd ruby-2.1.7
curl -O --progress https://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.4.tar.gz
echo 'e2e195a4a58133e3ad33b955c829bb536fa3c075 ruby-2.2.4.tar.gz' | shasum -c - && tar xzf ruby-2.2.4.tar.gz
cd ruby-2.2.4
./configure --disable-install-rdoc
make
sudo make install
......@@ -358,7 +358,7 @@ GitLab Shell is an SSH access and repository management software developed speci
cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse
sudo -u git -H git checkout 0.5.4
sudo -u git -H git checkout 0.6.2
sudo -u git -H make
### Initialize Database and Activate Advanced Features
......
......@@ -32,8 +32,7 @@ Please consider using a virtual machine to run GitLab.
## Ruby versions
GitLab requires Ruby (MRI) 2.1.x and currently does not work with versions 2.2
and 2.3.
GitLab requires Ruby (MRI) 2.1.x or 2.2.x and currently does not work with version 2.3.
You will have to use the standard MRI implementation of Ruby.
We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab
......
......@@ -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
- [Jenkins](jenkins.md) Integrate with the Jenkins CI
......
......@@ -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 |
......
......@@ -48,7 +48,7 @@ which should already be on your system from GitLab 8.1.
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all
sudo -u git -H git checkout 0.6.1
sudo -u git -H git checkout 0.6.2
sudo -u git -H make
```
......
# 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"
......@@ -41,3 +41,33 @@ Feature: Dashboard
And user with name "John Doe" left project "Shop"
When I visit dashboard activity page
Then I should see "John Doe left project Shop" event
@javascript
Scenario: Sorting Issues
Given I visit dashboard issues page
And I sort the list by "Oldest updated"
And I visit dashboard activity page
And I visit dashboard issues page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Visiting Project's issues after sorting
Given I visit dashboard issues page
And I sort the list by "Oldest updated"
And I visit project "Shop" issues page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Sorting Merge Requests
Given I visit dashboard merge requests page
And I sort the list by "Oldest updated"
And I visit dashboard activity page
And I visit dashboard merge requests page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Visiting Project's merge requests after sorting
Given I visit dashboard merge requests page
And I sort the list by "Oldest updated"
And I visit project "Shop" merge requests page
Then The list should be sorted by "Oldest updated"
......@@ -3,6 +3,10 @@ Feature: Groups
Given I sign in as "John Doe"
And "John Doe" is owner of group "Owned"
Scenario: I should not see a group if it does not exist
When I visit group "NonExistentGroup" page
Then page status code should be 404
Scenario: I should have back to group button
When I visit group "Owned" page
Then I should see back to dashboard button
......
......@@ -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
......@@ -25,3 +25,18 @@ Feature: Project Fork
Then I should see "New merge request"
And I click link "New merge request"
Then I should see the new merge request page for my namespace
Scenario: Viewing forks of a Project
Given I click link "Fork"
When I fork to my namespace
And I visit the forks page of the "Shop" project
Then I should see my fork on the list
Scenario: Viewing private forks of a Project
Given There is an existent fork of the "Shop" project
And I click link "Fork"
When I fork to my namespace
And I visit the forks page of the "Shop" project
Then I should see my fork on the list
And I should not see the other fork listed
And I should see a private fork notice
......@@ -9,6 +9,7 @@ Feature: Award Emoji
@javascript
Scenario: I add and remove award in the issue
Given I click to emoji-picker
Then The search field is focused
And I click to emoji in the picker
Then I have award added
And I can remove it by clicking to icon
......@@ -16,11 +17,13 @@ Feature: Award Emoji
@javascript
Scenario: I can see the list of emoji categories
Given I click to emoji-picker
Then The search field is focused
Then I can see the activity and food categories
@javascript
Scenario: I can search emoji
Given I click to emoji-picker
Then The search field is focused
And I search "hand"
Then I see search result for "hand"
......
......@@ -59,6 +59,28 @@ Feature: Project Issues
And I sort the list by "Last updated"
Then I should see "Release 0.4" at the top
@javascript
Scenario: Visiting Issues after being sorted the list
Given I visit project "Shop" issues page
And I sort the list by "Oldest updated"
And I visit my project's home page
And I visit project "Shop" issues page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Visiting Merge Requests after being sorted the list
Given I visit project "Shop" issues page
And I sort the list by "Oldest updated"
And I visit project "Shop" merge requests page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Visiting Merge Requests from a differente Project after sorting
Given I visit project "Shop" merge requests page
And I sort the list by "Oldest updated"
And I visit dashboard merge requests page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: I search issue
Given I fill in issue search with "Re"
......
......@@ -84,6 +84,28 @@ Feature: Project Merge Requests
And I sort the list by "Last updated"
Then I should see "Bug NS-04" at the top
@javascript
Scenario: Visiting Merge Requests after being sorted the list
Given I visit project "Shop" merge requests page
And I sort the list by "Oldest updated"
And I visit my project's home page
And I visit project "Shop" merge requests page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Visiting Issues after being sorted the list
Given I visit project "Shop" merge requests page
And I sort the list by "Oldest updated"
And I visit project "Shop" issues page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Visiting Merge Requests from a differente Project after sorting
Given I visit project "Shop" merge requests page
And I sort the list by "Oldest updated"
And I visit dashboard merge requests page
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Visiting Merge Requests after commenting on diffs
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
......
......@@ -2,6 +2,7 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedProject
include SharedIssuable
step 'I should see "New Project" link' do
expect(page).to have_link "New project"
......
......@@ -188,6 +188,10 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
expect(page).to have_content('As Developer on ldap server')
end
step 'I visit group "NonExistentGroup" page' do
visit group_path(-1)
end
private
def assigned_to_me(key)
......
......@@ -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
......@@ -49,4 +49,29 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
step 'I should see the new merge request page for my namespace' do
current_path.should have_content(/#{current_user.namespace.name}/i)
end
step 'I visit the forks page of the "Shop" project' do
@project = Project.where(name: 'Shop').last
visit namespace_project_forks_path(@project.namespace, @project)
end
step 'I should see my fork on the list' do
page.within('.projects-list-holder') do
project = @user.fork_of(@project)
expect(page).to have_content("#{project.namespace.human_name} / #{project.name}")
end
end
step 'There is an existent fork of the "Shop" project' do
user = create(:user, name: 'Mike')
@forked_project = Projects::ForkService.new(@project, user).execute
end
step 'I should not see the other fork listed' do
expect(page).not_to have_content("#{@forked_project.namespace.human_name} / #{@forked_project.name}")
end
step 'I should see a private fork notice' do
expect(page).to have_content("1 private fork")
end
end
......@@ -43,7 +43,9 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
expect(page).to have_css("h3.page-title", text: "New Merge Request")
fill_in "merge_request_title", with: "Merge Request On Forked Project"
page.within 'form#new_merge_request' do
fill_in "merge_request_title", with: "Merge Request On Forked Project"
end
end
step 'I submit the merge request' do
......
......@@ -66,4 +66,8 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
expect(page).to have_selector '[data-emoji="raised_hand"]'
end
end
step 'The search field is focused' do
page.evaluate_script("document.activeElement.id").should eq "emoji_search"
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
......
......@@ -106,6 +106,19 @@ module SharedIssuable
edit_issuable
end
step 'I sort the list by "Oldest updated"' do
find('button.dropdown-toggle.btn').click
page.within('ul.dropdown-menu.dropdown-menu-align-right li') do
click_link "Oldest updated"
end
end
step 'The list should be sorted by "Oldest updated"' do
page.within('div.dropdown.inline.prepend-left-10') do
expect(page.find('button.dropdown-toggle.btn')).to have_content('Oldest updated')
end
end
def create_issuable_for_project(project_name:, title:, type: :issue)
project = Project.find_by(name: project_name)
......
......@@ -9,10 +9,6 @@ Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, js_errors: true, timeout: timeout)
end
Spinach.hooks.on_tag("javascript") do
Capybara.current_driver = Capybara.javascript_driver
end
Capybara.default_wait_time = timeout
Capybara.ignore_hidden_elements = false
......@@ -22,3 +18,7 @@ unless ENV['CI'] || ENV['CI_SERVER']
# Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run
end
Spinach.hooks.before_run do
TestEnv.warm_asset_cache
end
......@@ -153,10 +153,11 @@ module API
end
def attributes_for_keys(keys, custom_params = nil)
params_hash = custom_params || params
attrs = {}
keys.each do |key|
if params[key].present? or (params.has_key?(key) and params[key] == false)
attrs[key] = params[key]
if params_hash[key].present? or (params_hash.has_key?(key) and params_hash[key] == false)
attrs[key] = params_hash[key]
end
end
ActionController::Parameters.new(attrs).permit!
......
......@@ -59,55 +59,6 @@ module API
present paginate(merge_requests), with: Entities::MergeRequest
end
# Show MR
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
# GET /projects/:id/merge_request/:merge_request_id
#
get ":id/merge_request/:merge_request_id" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request, with: Entities::MergeRequest
end
# Show MR commits
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
# GET /projects/:id/merge_request/:merge_request_id/commits
#
get ':id/merge_request/:merge_request_id/commits' do
merge_request = user_project.merge_requests.
find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request.commits, with: Entities::RepoCommit
end
# Show MR changes
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
# GET /projects/:id/merge_request/:merge_request_id/changes
#
get ':id/merge_request/:merge_request_id/changes' do
merge_request = user_project.merge_requests.
find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request, with: Entities::MergeRequestChanges
end
# Create MR
#
# Parameters:
......@@ -148,146 +99,206 @@ module API
end
end
# Update MR
# Routing "merge_request/:merge_request_id/..." is DEPRECATED and WILL BE REMOVED in version 9.0
# Use "merge_requests/:merge_request_id/..." instead.
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# target_branch - The target branch
# assignee_id - Assignee user ID
# title - Title of MR
# state_event - Status of MR. (close|reopen|merge)
# description - Description of MR
# labels (optional) - Labels for a MR as a comma-separated list
# Example:
# PUT /projects/:id/merge_request/:merge_request_id
#
put ":id/merge_request/:merge_request_id" do
attrs = attributes_for_keys [:target_branch, :assignee_id, :title, :state_event, :description]
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :update_merge_request, merge_request
# Ensure source_branch is not specified
if params[:source_branch].present?
render_api_error!('Source branch cannot be changed', 400)
end
# Validate label names in advance
if (errors = validate_label_params(params)).any?
render_api_error!({ labels: errors }, 400)
end
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request)
if merge_request.valid?
# Find or create labels and attach to issue
unless params[:labels].nil?
merge_request.remove_labels
merge_request.add_labels_by_names(params[:labels].split(","))
end
[":id/merge_request/:merge_request_id", ":id/merge_requests/:merge_request_id"].each do |path|
# Show MR
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
# GET /projects/:id/merge_requests/:merge_request_id
#
get path do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request, with: Entities::MergeRequest
else
handle_merge_request_errors! merge_request.errors
end
end
# Merge MR
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# merge_commit_message (optional) - Custom merge commit message
# should_remove_source_branch (optional) - When true, the source branch will be deleted if possible
# merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds
# Example:
# PUT /projects/:id/merge_request/:merge_request_id/merge
#
put ":id/merge_request/:merge_request_id/merge" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
# Merge request can not be merged
# because user dont have permissions to push into target branch
unauthorized! unless merge_request.can_be_merged_by?(current_user)
not_allowed! if !merge_request.open? || merge_request.work_in_progress?
merge_request.check_if_can_be_merged
render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged?
merge_params = {
commit_message: params[:merge_commit_message],
should_remove_source_branch: params[:should_remove_source_branch]
}
if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.ci_commit && merge_request.ci_commit.active?
::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
execute(merge_request)
else
::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params).
execute(merge_request)
# Show MR commits
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
# GET /projects/:id/merge_requests/:merge_request_id/commits
#
get "#{path}/commits" do
merge_request = user_project.merge_requests.
find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request.commits, with: Entities::RepoCommit
end
present merge_request, with: Entities::MergeRequest
end
# Show MR changes
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - The ID of MR
#
# Example:
# GET /projects/:id/merge_requests/:merge_request_id/changes
#
get "#{path}/changes" do
merge_request = user_project.merge_requests.
find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present merge_request, with: Entities::MergeRequestChanges
end
# Cancel Merge if Merge When build succeeds is enabled
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
#
post ":id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
# Update MR
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# target_branch - The target branch
# assignee_id - Assignee user ID
# title - Title of MR
# state_event - Status of MR. (close|reopen|merge)
# description - Description of MR
# labels (optional) - Labels for a MR as a comma-separated list
# Example:
# PUT /projects/:id/merge_requests/:merge_request_id
#
put path do
attrs = attributes_for_keys [:target_branch, :assignee_id, :title, :state_event, :description]
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :update_merge_request, merge_request
# Ensure source_branch is not specified
if params[:source_branch].present?
render_api_error!('Source branch cannot be changed', 400)
end
unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
# Validate label names in advance
if (errors = validate_label_params(params)).any?
render_api_error!({ labels: errors }, 400)
end
::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request)
end
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request)
# Get a merge request's comments
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# Examples:
# GET /projects/:id/merge_request/:merge_request_id/comments
#
get ":id/merge_request/:merge_request_id/comments" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
if merge_request.valid?
# Find or create labels and attach to issue
unless params[:labels].nil?
merge_request.remove_labels
merge_request.add_labels_by_names(params[:labels].split(","))
end
authorize! :read_merge_request, merge_request
present merge_request, with: Entities::MergeRequest
else
handle_merge_request_errors! merge_request.errors
end
end
present paginate(merge_request.notes.fresh), with: Entities::MRNote
end
# Merge MR
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# merge_commit_message (optional) - Custom merge commit message
# should_remove_source_branch (optional) - When true, the source branch will be deleted if possible
# merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds
# Example:
# PUT /projects/:id/merge_requests/:merge_request_id/merge
#
put "#{path}/merge" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
# Merge request can not be merged
# because user dont have permissions to push into target branch
unauthorized! unless merge_request.can_be_merged_by?(current_user)
not_allowed! if !merge_request.open? || merge_request.work_in_progress?
merge_request.check_if_can_be_merged
render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged?
merge_params = {
commit_message: params[:merge_commit_message],
should_remove_source_branch: params[:should_remove_source_branch]
}
if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.ci_commit && merge_request.ci_commit.active?
::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
execute(merge_request)
else
::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params).
execute(merge_request)
end
# Post comment to merge request
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# note (required) - Text of comment
# Examples:
# POST /projects/:id/merge_request/:merge_request_id/comments
#
post ":id/merge_request/:merge_request_id/comments" do
required_attributes! [:note]
present merge_request, with: Entities::MergeRequest
end
merge_request = user_project.merge_requests.find(params[:merge_request_id])
# Cancel Merge if Merge When build succeeds is enabled
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
#
post "#{path}/cancel_merge_when_build_succeeds" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :create_note, merge_request
unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
opts = {
note: params[:note],
noteable_type: 'MergeRequest',
noteable_id: merge_request.id
}
::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request)
end
note = ::Notes::CreateService.new(user_project, current_user, opts).execute
# Duplicate. DEPRECATED and WILL BE REMOVED in 9.0.
# Use GET "/projects/:id/merge_requests/:merge_request_id/notes" instead
#
# Get a merge request's comments
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# Examples:
# GET /projects/:id/merge_requests/:merge_request_id/comments
#
get "#{path}/comments" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :read_merge_request, merge_request
present paginate(merge_request.notes.fresh), with: Entities::MRNote
end
if note.save
present note, with: Entities::MRNote
else
render_api_error!("Failed to save note #{note.errors.messages}", 400)
# Duplicate. DEPRECATED and WILL BE REMOVED in 9.0.
# Use POST "/projects/:id/merge_requests/:merge_request_id/notes" instead
#
# Post comment to merge request
#
# Parameters:
# id (required) - The ID of a project
# merge_request_id (required) - ID of MR
# note (required) - Text of comment
# Examples:
# POST /projects/:id/merge_requests/:merge_request_id/comments
#
post "#{path}/comments" do
required_attributes! [:note]
merge_request = user_project.merge_requests.find(params[:merge_request_id])
authorize! :create_note, merge_request
opts = {
note: params[:note],
noteable_type: 'MergeRequest',
noteable_id: merge_request.id
}
note = ::Notes::CreateService.new(user_project, current_user, opts).execute
if note.save
present note, with: Entities::MRNote
else
render_api_error!("Failed to save note #{note.errors.messages}", 400)
end
end
end
end
......
......@@ -25,7 +25,7 @@ module Ci
format :json
helpers Helpers
helpers ::Ci::API::Helpers
helpers ::API::Helpers
helpers Gitlab::CurrentSettings
......
......@@ -13,13 +13,13 @@ module Ci
post "register" do
authenticate_runner!
update_runner_last_contact
update_runner_info
required_attributes! [:token]
not_found! unless current_runner.active?
build = Ci::RegisterBuildService.new.execute(current_runner)
if build
update_runner_info
present build, with: Entities::BuildDetails
else
not_found!
......
......@@ -34,10 +34,14 @@ module Ci
@runner ||= Runner.find_by_token(params[:token].to_s)
end
def update_runner_info
def get_runner_version_from_params
return unless params["info"].present?
info = attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"])
current_runner.update(info)
attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"])
end
def update_runner_info
current_runner.assign_attributes(get_runner_version_from_params)
current_runner.save if current_runner.changed?
end
def max_artifacts_size
......
......@@ -47,6 +47,7 @@ module Ci
return forbidden! unless runner
if runner.id
runner.update(get_runner_version_from_params)
present runner, with: Entities::Runner
else
not_found!
......
require 'resolv'
class DNSXLCheck
class Resolver
def self.search(query)
begin
Resolv.getaddress(query)
true
rescue Resolv::ResolvError
false
end
end
end
IP_REGEXP = /\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z/
DEFAULT_THRESHOLD = 0.33
def self.create_from_list(list)
dnsxl_check = DNSXLCheck.new
list.each do |entry|
dnsxl_check.add_list(entry.domain, entry.weight)
end
dnsxl_check
end
def test(ip)
if use_threshold?
test_with_threshold(ip)
else
test_strict(ip)
end
end
def test_with_threshold(ip)
return false if lists.empty?
search(ip)
final_score >= threshold
end
def test_strict(ip)
return false if lists.empty?
search(ip)
@score > 0
end
def use_threshold=(value)
@use_threshold = value == true
end
def use_threshold?
@use_threshold &&= true
end
def threshold=(threshold)
raise ArgumentError, "'threshold' value must be grather than 0 and less than or equal to 1" unless threshold > 0 && threshold <= 1
@threshold = threshold
end
def threshold
@threshold ||= DEFAULT_THRESHOLD
end
def add_list(domain, weight)
@lists ||= []
@lists << { domain: domain, weight: weight }
end
def lists
@lists ||= []
end
private
def search(ip)
raise ArgumentError, "'ip' value must be in #{IP_REGEXP} format" unless ip.match(IP_REGEXP)
@score = 0
reversed = reverse_ip(ip)
search_in_rbls(reversed)
end
def reverse_ip(ip)
ip.split('.').reverse.join('.')
end
def search_in_rbls(reversed_ip)
lists.each do |rbl|
query = "#{reversed_ip}.#{rbl[:domain]}"
@score += rbl[:weight] if Resolver.search(query)
end
end
def final_score
weights = lists.map{ |rbl| rbl[:weight] }.reduce(:+).to_i
return 0 if weights == 0
(@score.to_f / weights.to_f).round(2)
end
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
#Issues && Comments
issues = client.issues(project_identifier)
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
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,20 +80,9 @@ module Gitlab
author_id: gl_user_id(project, reporter)
)
end
true
rescue ActiveRecord::RecordInvalid => e
raise Projects::ImportService::Error, e.message
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
end
end
end
module Gitlab
class Blame
attr_accessor :blob, :commit
def initialize(blob, commit)
@blob = blob
@commit = commit
end
def groups(highlight: true)
prev_sha = nil
groups = []
current_group = nil
i = 0
blame.each do |commit, line|
commit = Commit.new(commit, project)
sha = commit.sha
if prev_sha != sha
groups << current_group if current_group
current_group = { commit: commit, lines: [] }
end
line = highlighted_lines[i].html_safe if highlight
current_group[:lines] << line
prev_sha = sha
i += 1
end
groups << current_group if current_group
groups
end
private
def blame
@blame ||= Gitlab::Git::Blame.new(repository, @commit.id, @blob.path)
end
def highlighted_lines
@highlighted_lines ||= Gitlab::Highlight.highlight(@blob.name, @blob.data).lines
end
def project
commit.project
end
def repository
project.repository
end
end
end
......@@ -4,11 +4,14 @@ module Gitlab
key = :current_application_settings
RequestStore.store[key] ||= begin
settings = nil
if connect_to_db?
ApplicationSetting.current || ApplicationSetting.create_from_defaults
else
fake_application_settings
settings = ApplicationSetting.current
settings ||= ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
end
settings || fake_application_settings
end
end
......@@ -18,28 +21,32 @@ module Gitlab
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
twitter_sharing_enabled: Settings.gitlab['twitter_sharing_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
import_sources: Settings.gitlab['import_sources'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,
two_factor_grace_period: 48
)
end
private
def connect_to_db?
use_db = if ENV['USE_DB'] == "false"
false
else
true
end
use_db && ActiveRecord::Base.connection.active? &&
ActiveRecord::Base.connection.table_exists?('application_settings')
# When the DBMS is not available, an exception (e.g. PG::ConnectionBad) is raised
active_db_connection = ActiveRecord::Base.connection.active? rescue false
ENV['USE_DB'] != 'false' &&
active_db_connection &&
ActiveRecord::Base.connection.table_exists?('application_settings')
rescue ActiveRecord::NoDatabaseError
false
......
......@@ -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
......
module Gitlab
class IpCheck
def initialize(ip)
@ip = ip
application_settings = ApplicationSetting.current
@ip_blocking_enabled = application_settings.ip_blocking_enabled
@dnsbl_servers_list = application_settings.dnsbl_servers_list
end
def spam?
@ip_blocking_enabled && blacklisted?
end
private
def blacklisted?
on_dns_blacklist?
end
def on_dns_blacklist?
dnsbl_check = DNSXLCheck.new
prepare_dnsbl_list(dnsbl_check)
dnsbl_check.test(@ip)
end
def prepare_dnsbl_list(dnsbl_check)
@dnsbl_servers_list.split(',').map(&:strip).reject(&:empty?).each do |domain|
dnsbl_check.add_list(domain, 1)
end
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
......
......@@ -24,20 +24,6 @@ describe Projects::BlameController do
context "valid file" do
let(:id) { 'master/files/ruby/popen.rb' }
it { is_expected.to respond_with(:success) }
it 'groups blames properly' do
blame = assigns(:blame)
# Sanity check a few items
expect(blame.count).to eq(18)
expect(blame[0][:commit].sha).to eq('913c66a37b4a45b9769037c55c2d238bd0942d2e')
expect(blame[0][:lines]).to eq(["require 'fileutils'", "require 'open3'", ""])
expect(blame[1][:commit].sha).to eq('874797c3a73b60d2187ed6e2fcabd289ff75171e')
expect(blame[1][:lines]).to eq(["module Popen", " extend self"])
expect(blame[-1][:commit].sha).to eq('913c66a37b4a45b9769037c55c2d238bd0942d2e')
expect(blame[-1][:lines]).to eq([" end", "end"])
end
end
end
end
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,83 +16,104 @@ describe 'Commits' do
FactoryGirl.create :ci_commit, project: project, sha: project.commit.sha
end
let!(:build) { FactoryGirl.create :ci_build, commit: commit }
context 'commit status is Generic Commit Status' do
let!(:status) { FactoryGirl.create :generic_commit_status, commit: commit }
describe 'Project commits' do
before do
visit namespace_project_commits_path(project.namespace, project, :master)
end
describe 'Commit builds' do
before do
visit ci_status_path(commit)
end
it 'should show build status' do
page.within("//li[@id='commit-#{commit.short_sha}']") do
expect(page).to have_css(".ci-status-link")
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
describe 'Commit builds' do
before do
visit ci_status_path(commit)
end
it { expect(page).to have_content commit.sha[0..7] }
it { expect(page).to have_content commit.git_commit_message }
it { expect(page).to have_content commit.git_author_name }
end
context 'commit status is Ci Build' do
let!(:build) { FactoryGirl.create :ci_build, commit: commit }
context 'Download artifacts' do
let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
describe 'Project commits' do
before do
visit namespace_project_commits_path(project.namespace, project, :master)
end
before do
build.update_attributes(artifacts_file: artifacts_file)
it 'should show build status' do
page.within("//li[@id='commit-#{commit.short_sha}']") do
expect(page).to have_css(".ci-status-link")
end
end
end
it do
visit ci_status_path(commit)
click_on 'Download artifacts'
expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type)
end
end
describe 'Commit builds' do
before do
visit ci_status_path(commit)
end
describe 'Cancel all builds' do
it 'cancels commit' do
visit ci_status_path(commit)
click_on 'Cancel running'
expect(page).to have_content 'canceled'
it { expect(page).to have_content commit.sha[0..7] }
it { expect(page).to have_content commit.git_commit_message }
it { expect(page).to have_content commit.git_author_name }
end
end
describe 'Cancel build' do
it 'cancels build' do
visit ci_status_path(commit)
click_on 'Cancel'
expect(page).to have_content 'canceled'
end
end
context 'Download artifacts' do
let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
before do
build.update_attributes(artifacts_file: artifacts_file)
end
describe '.gitlab-ci.yml not found warning' do
context 'ci builds enabled' do
it "does not show warning" do
it do
visit ci_status_path(commit)
expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
click_on 'Download artifacts'
expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type)
end
end
it 'shows warning' do
stub_ci_commit_yaml_file(nil)
describe 'Cancel all builds' do
it 'cancels commit' do
visit ci_status_path(commit)
expect(page).to have_content '.gitlab-ci.yml not found in this commit'
click_on 'Cancel running'
expect(page).to have_content 'canceled'
end
end
context 'ci builds disabled' do
before do
stub_ci_builds_disabled
stub_ci_commit_yaml_file(nil)
describe 'Cancel build' do
it 'cancels build' do
visit ci_status_path(commit)
click_on 'Cancel'
expect(page).to have_content 'canceled'
end
end
describe '.gitlab-ci.yml not found warning' do
context 'ci builds enabled' do
it "does not show warning" do
visit ci_status_path(commit)
expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
end
it 'shows warning' do
stub_ci_commit_yaml_file(nil)
visit ci_status_path(commit)
expect(page).to have_content '.gitlab-ci.yml not found in this commit'
end
end
context 'ci builds disabled' do
before do
stub_ci_builds_disabled
stub_ci_commit_yaml_file(nil)
visit ci_status_path(commit)
end
it 'does not show warning' do
expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
it 'does not show warning' do
expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
end
end
end
end
......
......@@ -112,10 +112,10 @@ feature 'Login', feature: true do
context 'within the grace period' do
it 'redirects to two-factor configuration page' do
expect(current_path).to eq new_profile_two_factor_auth_path
expect(page).to have_content('You must configure Two-Factor Authentication in your account until')
expect(page).to have_content('You must enable Two-factor Authentication for your account before')
end
it 'two-factor configuration is skippable' do
it 'disallows skipping two-factor configuration' do
expect(current_path).to eq new_profile_two_factor_auth_path
click_link 'Configure it later'
......@@ -128,10 +128,10 @@ feature 'Login', feature: true do
it 'redirects to two-factor configuration page' do
expect(current_path).to eq new_profile_two_factor_auth_path
expect(page).to have_content('You must configure Two-Factor Authentication in your account.')
expect(page).to have_content('You must enable Two-factor Authentication for your account.')
end
it 'two-factor configuration is not skippable' do
it 'disallows skipping two-factor configuration' do
expect(current_path).to eq new_profile_two_factor_auth_path
expect(page).not_to have_link('Configure it later')
end
......@@ -146,7 +146,7 @@ feature 'Login', feature: true do
it 'redirects to two-factor configuration page' do
expect(current_path).to eq new_profile_two_factor_auth_path
expect(page).to have_content('You must configure Two-Factor Authentication in your account.')
expect(page).to have_content('You must enable Two-factor Authentication for your account.')
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
require 'spec_helper'
require 'ostruct'
describe 'DNSXLCheck', lib: true, no_db: true do
let(:spam_ip) { '127.0.0.2' }
let(:no_spam_ip) { '127.0.0.3' }
let(:invalid_ip) { 'a.b.c.d' }
let!(:dnsxl_check) { DNSXLCheck.create_from_list([OpenStruct.new({ domain: 'test', weight: 1 })]) }
before(:context) do
class DNSXLCheck::Resolver
class << self
alias_method :old_search, :search
def search(query)
return false if query.match(/always\.failing\.domain\z/)
return true if query.match(/\A2\.0\.0\.127\./)
return false if query.match(/\A3\.0\.0\.127\./)
end
end
end
end
describe '#test' do
before do
dnsxl_check.threshold = 0.75
dnsxl_check.add_list('always.failing.domain', 1)
end
context 'when threshold is used' do
before { dnsxl_check.use_threshold= true }
it { expect(dnsxl_check.test(spam_ip)).to be_falsey }
end
context 'when threshold is not used' do
before { dnsxl_check.use_threshold= false }
it { expect(dnsxl_check.test(spam_ip)).to be_truthy }
end
end
describe '#test_with_threshold' do
it { expect{ dnsxl_check.test_with_threshold(invalid_ip) }.to raise_error(ArgumentError) }
it { expect(dnsxl_check.test_with_threshold(spam_ip)).to be_truthy }
it { expect(dnsxl_check.test_with_threshold(no_spam_ip)).to be_falsey }
end
describe '#test_strict' do
before do
dnsxl_check.threshold = 1
dnsxl_check.add_list('always.failing.domain', 1)
end
it { expect{ dnsxl_check.test_strict(invalid_ip) }.to raise_error(ArgumentError) }
it { expect(dnsxl_check.test_with_threshold(spam_ip)).to be_falsey }
it { expect(dnsxl_check.test_with_threshold(no_spam_ip)).to be_falsey }
it { expect(dnsxl_check.test_strict(spam_ip)).to be_truthy }
it { expect(dnsxl_check.test_strict(no_spam_ip)).to be_falsey }
end
describe '#threshold=' do
it { expect{ dnsxl_check.threshold = 0 }.to raise_error(ArgumentError) }
it { expect{ dnsxl_check.threshold = 1.1 }.to raise_error(ArgumentError) }
it { expect{ dnsxl_check.threshold = 0.5 }.not_to raise_error }
end
end
require 'spec_helper'
describe Gitlab::Blame, lib: true do
let(:project) { create(:project) }
let(:path) { 'files/ruby/popen.rb' }
let(:commit) { project.commit('master') }
let(:blob) { project.repository.blob_at(commit.id, path) }
describe "#groups" do
let(:subject) { described_class.new(blob, commit).groups(highlight: false) }
it 'groups lines properly' do
expect(subject.count).to eq(18)
expect(subject[0][:commit].sha).to eq('913c66a37b4a45b9769037c55c2d238bd0942d2e')
expect(subject[0][:lines]).to eq(["require 'fileutils'", "require 'open3'", ""])
expect(subject[1][:commit].sha).to eq('874797c3a73b60d2187ed6e2fcabd289ff75171e')
expect(subject[1][:lines]).to eq(["module Popen", " extend self"])
expect(subject[-1][:commit].sha).to eq('913c66a37b4a45b9769037c55c2d238bd0942d2e')
expect(subject[-1][:lines]).to eq([" end", "end"])
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
......
require 'spec_helper'
describe Ci::Build, models: true do
let(:project) { FactoryGirl.create :empty_project }
let(:project) { FactoryGirl.create :project }
let(:commit) { FactoryGirl.create :ci_commit, project: project }
let(:build) { FactoryGirl.create :ci_build, commit: commit }
......
......@@ -37,7 +37,7 @@ describe CaseSensitivity, models: true do
with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar').
and_return(criteria)
expect(model.iwhere(:'foo.bar' => 'bar')).to eq(criteria)
expect(model.iwhere('foo.bar'.to_sym => 'bar')).to eq(criteria)
end
end
......@@ -87,8 +87,8 @@ describe CaseSensitivity, models: true do
with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz').
and_return(final)
got = model.iwhere(:'foo.bar' => 'bar',
:'foo.baz' => 'baz')
got = model.iwhere('foo.bar'.to_sym => 'bar',
'foo.baz'.to_sym => 'baz')
expect(got).to eq(final)
end
......@@ -127,7 +127,7 @@ describe CaseSensitivity, models: true do
with(%q{`foo`.`bar` = :value}, value: 'bar').
and_return(criteria)
expect(model.iwhere(:'foo.bar' => 'bar')).
expect(model.iwhere('foo.bar'.to_sym => 'bar')).
to eq(criteria)
end
end
......@@ -178,8 +178,8 @@ describe CaseSensitivity, models: true do
with(%q{`foo`.`baz` = :value}, value: 'baz').
and_return(final)
got = model.iwhere(:'foo.bar' => 'bar',
:'foo.baz' => 'baz')
got = model.iwhere('foo.bar'.to_sym => 'bar',
'foo.baz'.to_sym => 'baz')
expect(got).to eq(final)
end
......
......@@ -65,27 +65,6 @@ describe Event, models: true do
it { expect(@event.author).to eq(@user) }
end
describe '.latest_update_time' do
describe 'when events are present' do
let(:time) { Time.utc(2015, 1, 1) }
before do
create(:closed_issue_event, updated_at: time)
create(:closed_issue_event, updated_at: time + 5)
end
it 'returns the latest update time' do
expect(Event.latest_update_time).to eq(time + 5)
end
end
describe 'when no events exist' do
it 'returns nil' do
expect(Event.latest_update_time).to be_nil
end
end
end
describe '.limit_recent' do
let!(:event1) { create(:closed_issue_event) }
let!(:event2) { create(:closed_issue_event) }
......
......@@ -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
......
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
......@@ -189,6 +189,38 @@ describe WikiPage, models: true do
end
end
describe '#historical?' do
before do
create_page('Update', 'content')
@page = wiki.find_page('Update')
3.times { |i| @page.update("content #{i}") }
end
after do
destroy_page('Update')
end
it 'returns true when requesting an old version' do
old_version = @page.versions.last.to_s
old_page = wiki.find_page('Update', old_version)
expect(old_page.historical?).to eq true
end
it 'returns false when requesting latest version' do
latest_version = @page.versions.first.to_s
latest_page = wiki.find_page('Update', latest_version)
expect(latest_page.historical?).to eq false
end
it 'returns false when version is nil' do
latest_page = wiki.find_page('Update', nil)
expect(latest_page.historical?).to eq false
end
end
private
def remove_temp_repo(path)
......
......@@ -109,9 +109,9 @@ describe API::API, api: true do
end
end
describe "GET /projects/:id/merge_request/:merge_request_id" do
describe "GET /projects/:id/merge_requests/:merge_request_id" do
it "should return merge_request" do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}", user)
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
expect(response.status).to eq(200)
expect(json_response['title']).to eq(merge_request.title)
expect(json_response['iid']).to eq(merge_request.iid)
......@@ -126,14 +126,14 @@ describe API::API, api: true do
end
it "should return a 404 error if merge_request_id not found" do
get api("/projects/#{project.id}/merge_request/999", user)
get api("/projects/#{project.id}/merge_requests/999", user)
expect(response.status).to eq(404)
end
end
describe 'GET /projects/:id/merge_request/:merge_request_id/commits' do
describe 'GET /projects/:id/merge_requests/:merge_request_id/commits' do
context 'valid merge request' do
before { get api("/projects/#{project.id}/merge_request/#{merge_request.id}/commits", user) }
before { get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/commits", user) }
let(:commit) { merge_request.commits.first }
it { expect(response.status).to eq 200 }
......@@ -143,20 +143,20 @@ describe API::API, api: true do
end
it 'returns a 404 when merge_request_id not found' do
get api("/projects/#{project.id}/merge_request/999/commits", user)
get api("/projects/#{project.id}/merge_requests/999/commits", user)
expect(response.status).to eq(404)
end
end
describe 'GET /projects/:id/merge_request/:merge_request_id/changes' do
describe 'GET /projects/:id/merge_requests/:merge_request_id/changes' do
it 'should return the change information of the merge_request' do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}/changes", user)
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/changes", user)
expect(response.status).to eq 200
expect(json_response['changes'].size).to eq(merge_request.diffs.size)
end
it 'returns a 404 when merge_request_id not found' do
get api("/projects/#{project.id}/merge_request/999/changes", user)
get api("/projects/#{project.id}/merge_requests/999/changes", user)
expect(response.status).to eq(404)
end
end
......@@ -311,19 +311,19 @@ describe API::API, api: true do
end
end
describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do
describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
it "should return merge_request" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), state_event: "close"
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
expect(response.status).to eq(200)
expect(json_response['state']).to eq('closed')
end
end
describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do
describe "PUT /projects/:id/merge_requests/:merge_request_id/merge" do
let(:ci_commit) { create(:ci_commit_without_jobs) }
it "should return merge_request in case of success" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
expect(response.status).to eq(200)
end
......@@ -332,7 +332,7 @@ describe API::API, api: true do
allow_any_instance_of(MergeRequest).
to receive(:can_be_merged?).and_return(false)
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
expect(response.status).to eq(406)
expect(json_response['message']).to eq('Branch cannot be merged')
......@@ -340,14 +340,14 @@ describe API::API, api: true do
it "should return 405 if merge_request is not open" do
merge_request.close
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
expect(response.status).to eq(405)
expect(json_response['message']).to eq('405 Method Not Allowed')
end
it "should return 405 if merge_request is a work in progress" do
merge_request.update_attribute(:title, "WIP: #{merge_request.title}")
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
expect(response.status).to eq(405)
expect(json_response['message']).to eq('405 Method Not Allowed')
end
......@@ -355,7 +355,7 @@ describe API::API, api: true do
it "should return 401 if user has no permissions to merge" do
user2 = create(:user)
project.team << [user2, :reporter]
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user2)
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user2)
expect(response.status).to eq(401)
expect(json_response['message']).to eq('401 Unauthorized')
end
......@@ -364,7 +364,7 @@ describe API::API, api: true do
allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
allow(ci_commit).to receive(:active?).and_return(true)
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
expect(response.status).to eq(200)
expect(json_response['title']).to eq('Test')
......@@ -372,33 +372,33 @@ describe API::API, api: true do
end
end
describe "PUT /projects/:id/merge_request/:merge_request_id" do
describe "PUT /projects/:id/merge_requests/:merge_request_id" do
it "should return merge_request" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title"
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: "New title"
expect(response.status).to eq(200)
expect(json_response['title']).to eq('New title')
end
it "should return merge_request" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), description: "New description"
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), description: "New description"
expect(response.status).to eq(200)
expect(json_response['description']).to eq('New description')
end
it "should return 400 when source_branch is specified" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user),
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user),
source_branch: "master", target_branch: "master"
expect(response.status).to eq(400)
end
it "should return merge_request with renamed target_branch" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "wiki"
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), target_branch: "wiki"
expect(response.status).to eq(200)
expect(json_response['target_branch']).to eq('wiki')
end
it 'should return 400 on invalid label names' do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}",
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}",
user),
title: 'new issue',
labels: 'label, ?'
......@@ -407,11 +407,11 @@ describe API::API, api: true do
end
end
describe "POST /projects/:id/merge_request/:merge_request_id/comments" do
describe "POST /projects/:id/merge_requests/:merge_request_id/comments" do
it "should return comment" do
original_count = merge_request.notes.size
post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user), note: "My comment"
post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user), note: "My comment"
expect(response.status).to eq(201)
expect(json_response['note']).to eq('My comment')
expect(json_response['author']['name']).to eq(user.name)
......@@ -420,20 +420,20 @@ describe API::API, api: true do
end
it "should return 400 if note is missing" do
post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user)
expect(response.status).to eq(400)
end
it "should return 404 if note is attached to non existent merge request" do
post api("/projects/#{project.id}/merge_request/404/comments", user),
post api("/projects/#{project.id}/merge_requests/404/comments", user),
note: 'My comment'
expect(response.status).to eq(404)
end
end
describe "GET :id/merge_request/:merge_request_id/comments" do
describe "GET :id/merge_requests/:merge_request_id/comments" do
it "should return merge_request comments ordered by created_at" do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
......@@ -443,7 +443,7 @@ describe API::API, api: true do
end
it "should return a 404 error if merge_request_id not found" do
get api("/projects/#{project.id}/merge_request/999/comments", user)
get api("/projects/#{project.id}/merge_requests/999/comments", user)
expect(response.status).to eq(404)
end
end
......
......@@ -113,6 +113,21 @@ describe Ci::API::API do
expect(json_response["depends_on_builds"].count).to eq(2)
expect(json_response["depends_on_builds"][0]["name"]).to eq("rspec")
end
%w(name version revision platform architecture).each do |param|
context "updates runner #{param}" do
let(:value) { "#{param}_value" }
subject { runner.read_attribute(param.to_sym) }
it do
post ci_api("/builds/register"), token: runner.token, info: { param => value }
expect(response.status).to eq(404)
runner.reload
is_expected.to eq(value)
end
end
end
end
describe "PUT /builds/:id" do
......
......@@ -51,6 +51,20 @@ describe Ci::API::API do
expect(response.status).to eq(400)
end
%w(name version revision platform architecture).each do |param|
context "creates runner with #{param} saved" do
let(:value) { "#{param}_value" }
subject { Ci::Runner.first.read_attribute(param.to_sym) }
it do
post ci_api("/runners/register"), token: registration_token, info: { param => value }
expect(response.status).to eq(201)
is_expected.to eq(value)
end
end
end
end
describe "DELETE /runners/delete" do
......
......@@ -496,11 +496,11 @@ end
describe Projects::ForksController, 'routing' do
it 'to #new' do
expect(get('/gitlab/gitlabhq/fork/new')).to route_to('projects/forks#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
expect(get('/gitlab/gitlabhq/forks/new')).to route_to('projects/forks#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
it 'to #create' do
expect(post('/gitlab/gitlabhq/fork')).to route_to('projects/forks#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
expect(post('/gitlab/gitlabhq/forks')).to route_to('projects/forks#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end
......
......@@ -14,9 +14,7 @@ describe Notes::CreateService, services: true do
noteable_type: 'Issue',
noteable_id: issue.id
}
expect(project).to receive(:execute_hooks)
expect(project).to receive(:execute_services)
@note = Notes::CreateService.new(project, user, opts).execute
end
......
require 'spec_helper'
describe Notes::PostProcessService, services: true do
let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
describe :execute do
before do
project.team << [user, :master]
note_opts = {
note: 'Awesome comment',
noteable_type: 'Issue',
noteable_id: issue.id
}
@note = Notes::CreateService.new(project, user, note_opts).execute
end
it do
expect(project).to receive(:execute_hooks)
expect(project).to receive(:execute_services)
Notes::PostProcessService.new(@note).execute
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
......@@ -52,4 +52,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!
......@@ -19,3 +19,9 @@ unless ENV['CI'] || ENV['CI_SERVER']
# Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run
end
RSpec.configure do |config|
config.before(:suite) do
TestEnv.warm_asset_cache
end
end
......@@ -146,6 +146,22 @@ module TestEnv
FileUtils.chmod_R 0755, target_repo_path
end
# When no cached assets exist, manually hit the root path to create them
#
# Otherwise they'd be created by the first test, often timing out and
# causing a transient test failure
def warm_asset_cache
return if warm_asset_cache?
return unless defined?(Capybara)
Capybara.current_session.driver.visit '/'
end
def warm_asset_cache?
cache = Rails.root.join(*%w(tmp cache assets test))
Dir.exist?(cache) && Dir.entries(cache).length > 2
end
private
def factory_repo_path
......@@ -172,7 +188,6 @@ module TestEnv
'gitlab-test-fork'
end
# Prevent developer git configurations from being persisted to test
# repositories
def git_env
......
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