Merge branch 'master' into request/relative_submodules
This commit is contained in:
commit
4d1945028e
|
@ -6,6 +6,13 @@ v 6.8.0
|
|||
- Drop all tables before restoring a Postgres backup
|
||||
- Make the repository downloads path configurable
|
||||
- Create branches via API (sponsored by O'Reilly Media)
|
||||
- Changed permission of gitlab-satellites directory not to be world accessible
|
||||
- Protected branch does not allow force push
|
||||
|
||||
v 6.7.3
|
||||
- Fix the merge notification email not being sent (Pierre de La Morinerie)
|
||||
- Drop all tables before restoring a Postgres backup
|
||||
- Remove yanked modernizr gem
|
||||
|
||||
v 6.7.2
|
||||
- Fix upgrader script
|
||||
|
|
|
@ -29,7 +29,9 @@ If something is wrong but it is not a regression compared to older versions of G
|
|||
When submitting an issue please conform to the issue submission guidelines listed below.
|
||||
Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue.
|
||||
|
||||
Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose.
|
||||
Do not use the issue tracker for feature requests.
|
||||
We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose.
|
||||
Please keep feature requests as small and simple as possible, complex ones might be edited to make them small and simple.
|
||||
|
||||
Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
|
||||
|
||||
|
|
5
Gemfile
5
Gemfile
|
@ -12,8 +12,6 @@ gem "rails", "~> 4.0.0"
|
|||
|
||||
gem "protected_attributes"
|
||||
gem 'rails-observers'
|
||||
gem 'actionpack-page_caching'
|
||||
gem 'actionpack-action_caching'
|
||||
|
||||
# Default values for AR models
|
||||
gem "default_value_for", "~> 3.0.0"
|
||||
|
@ -102,7 +100,7 @@ gem "acts-as-taggable-on"
|
|||
# Background jobs
|
||||
gem 'slim'
|
||||
gem 'sinatra', require: nil
|
||||
gem 'sidekiq'
|
||||
gem 'sidekiq', '2.17.0'
|
||||
|
||||
# HTTP requests
|
||||
gem "httparty"
|
||||
|
@ -161,7 +159,6 @@ gem 'select2-rails'
|
|||
gem 'jquery-atwho-rails', "~> 0.3.3"
|
||||
gem "jquery-rails", "2.1.3"
|
||||
gem "jquery-ui-rails", "2.0.2"
|
||||
gem "modernizr", "2.6.2"
|
||||
gem "raphael-rails", "~> 2.1.2"
|
||||
gem 'bootstrap-sass', '~> 3.0'
|
||||
gem "font-awesome-rails", '~> 3.2'
|
||||
|
|
11
Gemfile.lock
11
Gemfile.lock
|
@ -27,10 +27,6 @@ GEM
|
|||
erubis (~> 2.7.0)
|
||||
rack (~> 1.5.2)
|
||||
rack-test (~> 0.6.2)
|
||||
actionpack-action_caching (1.1.0)
|
||||
actionpack (>= 4.0.0, < 5.0)
|
||||
actionpack-page_caching (1.0.2)
|
||||
actionpack (>= 4.0.0, < 5)
|
||||
activemodel (4.0.3)
|
||||
activesupport (= 4.0.3)
|
||||
builder (~> 3.1.0)
|
||||
|
@ -289,8 +285,6 @@ GEM
|
|||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
minitest (4.7.5)
|
||||
modernizr (2.6.2)
|
||||
sprockets (~> 2.0)
|
||||
multi_json (1.8.4)
|
||||
multi_xml (0.5.5)
|
||||
multipart-post (1.2.0)
|
||||
|
@ -567,8 +561,6 @@ PLATFORMS
|
|||
|
||||
DEPENDENCIES
|
||||
ace-rails-ap
|
||||
actionpack-action_caching
|
||||
actionpack-page_caching
|
||||
acts-as-taggable-on
|
||||
annotate (~> 2.6.0.beta2)
|
||||
asciidoctor
|
||||
|
@ -622,7 +614,6 @@ DEPENDENCIES
|
|||
launchy
|
||||
letter_opener
|
||||
minitest (~> 4.7.0)
|
||||
modernizr (= 2.6.2)
|
||||
mysql2
|
||||
nprogress-rails
|
||||
omniauth (~> 1.1.3)
|
||||
|
@ -653,7 +644,7 @@ DEPENDENCIES
|
|||
select2-rails
|
||||
settingslogic
|
||||
shoulda-matchers (~> 2.1.0)
|
||||
sidekiq
|
||||
sidekiq (= 2.17.0)
|
||||
simplecov
|
||||
sinatra
|
||||
six
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#= require turbolinks
|
||||
#= require jquery.turbolinks
|
||||
#= require bootstrap
|
||||
#= require modernizr
|
||||
#= require select2
|
||||
#= require raphael
|
||||
#= require g.raphael-min
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class CommitFile
|
||||
|
||||
|
||||
constructor: (file) ->
|
||||
if $('.image', file).length
|
||||
new ImageFile(file)
|
||||
|
||||
this.CommitFile = CommitFile
|
||||
|
||||
@CommitFile = CommitFile
|
||||
|
|
|
@ -125,4 +125,4 @@ class ImageFile
|
|||
img.on 'load', =>
|
||||
callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
|
||||
|
||||
this.ImageFile = ImageFile
|
||||
@ImageFile = ImageFile
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
||||
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||
*= require jquery.ui.gitlab
|
||||
*= require jquery.ui.datepicker
|
||||
*= require jquery.atwho
|
||||
*= require select2
|
||||
*= require highlightjs.min
|
||||
|
@ -43,6 +43,7 @@
|
|||
@import "generic/forms.scss";
|
||||
@import "generic/selects.scss";
|
||||
@import "generic/highlight.scss";
|
||||
@import "generic/jquery.scss";
|
||||
|
||||
/**
|
||||
* Page specific styles (issues, projects etc):
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
.ui-widget {
|
||||
font-family: $regular_font;
|
||||
font-size: $font-size-base;
|
||||
|
||||
&.ui-datepicker-inline {
|
||||
border: 1px solid #DDD;
|
||||
padding: 10px;
|
||||
width: 270px;
|
||||
|
||||
.ui-datepicker-header {
|
||||
background: #EEE;
|
||||
border-color: #DDD;
|
||||
}
|
||||
|
||||
.ui-datepicker-calendar td a {
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,257 +0,0 @@
|
|||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-disabled { cursor: default !important; }
|
||||
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||
|
||||
|
||||
/*
|
||||
* jQuery UI CSS Framework 1.8.7
|
||||
*
|
||||
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Theming/API
|
||||
*
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller
|
||||
*/
|
||||
|
||||
|
||||
/* Component containers
|
||||
----------------------------------*/
|
||||
.ui-widget { font-family: Arial,sans-serif; font-size: 1.1em; }
|
||||
.ui-widget .ui-widget { font-size: 1em; }
|
||||
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Arial,sans-serif; font-size: 1em; }
|
||||
.ui-widget-content { border: 1px solid #CCC; background: #ffffff; color: #4F4F4F; }
|
||||
.ui-widget-content a { color: #4F4F4F; }
|
||||
.ui-widget-header { border: 1px solid #B6B6B6; color: #4F4F4F; font-weight: bold; }
|
||||
.ui-widget-header {
|
||||
background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */
|
||||
background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */
|
||||
}
|
||||
.ui-widget-header a { color: #4F4F4F; }
|
||||
|
||||
/* Interaction states
|
||||
----------------------------------*/
|
||||
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #B6B6B6; font-weight: normal; color: #4F4F4F; }
|
||||
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
|
||||
background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */
|
||||
background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */
|
||||
-webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;
|
||||
-moz-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;
|
||||
box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;
|
||||
}
|
||||
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #4F4F4F; text-decoration: none; }
|
||||
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #9D9D9D; font-weight: normal; color: #313131; }
|
||||
.ui-state-hover a, .ui-state-hover a:hover { color: #313131; text-decoration: none; }
|
||||
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {
|
||||
outline: none;
|
||||
color: #1c4257; border: 1px solid #7096ab;
|
||||
background: #ededed url(bg_fallback.png) 0 -50px repeat-x; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* IE10+ */
|
||||
background: linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* W3C */
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #313131; text-decoration: none; }
|
||||
.ui-widget :active { outline: none; }
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight { border: 1px solid #d2dbf4; background: #f4f8fd; color: #0d2054; -moz-border-radius: 0 !important; -webkit-border-radius: 0 !important; border-radius: 0 !important; }
|
||||
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
|
||||
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error { border: 1px solid #e2d0d0; background: #fcf0f0; color: #280b0b; -moz-border-radius: 0 !important; -webkit-border-radius: 0 !important; border-radius: 0 !important; }
|
||||
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
|
||||
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
|
||||
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
|
||||
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
|
||||
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { width: 16px; height: 16px; background-image: url(ui-icons_222222_256x240.png); }
|
||||
.ui-widget-content .ui-icon {background-image: url(ui-icons_222222_256x240.png); }
|
||||
.ui-widget-header .ui-icon {background-image: url(ui-icons_222222_256x240.png); }
|
||||
.ui-state-default .ui-icon { background-image: url(ui-icons_454545_256x240.png); }
|
||||
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(ui-icons_454545_256x240.png); }
|
||||
.ui-state-active .ui-icon {background-image: url(ui-icons_454545_256x240.png); }
|
||||
.ui-state-highlight .ui-icon {background-image: url(ui-icons_454545_256x240.png); }
|
||||
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background: url(icon_sprite.png) -16px 0 no-repeat !important; }
|
||||
.ui-state-highlight .ui-icon, .ui-state-error .ui-icon { margin-top: -1px; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { background: #262b33; opacity: .70;filter:Alpha(Opacity=70); }
|
||||
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #000000; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }
|
||||
/*
|
||||
* jQuery UI Selectable 1.8.7
|
||||
*
|
||||
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Selectable#theming
|
||||
*/
|
||||
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
|
||||
/*
|
||||
* jQuery UI Autocomplete 1.8.7
|
||||
*
|
||||
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Autocomplete#theming
|
||||
*/
|
||||
.ui-autocomplete {
|
||||
position: absolute; cursor: default; z-index: 3;
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
border-radius: 0;
|
||||
-moz-box-shadow: 0 1px 5px rgba(0,0,0,0.3);
|
||||
-webkit-box-shadow: 0 1px 5px rgba(0,0,0,0.3);
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* workarounds */
|
||||
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
|
||||
|
||||
/*
|
||||
* jQuery UI Menu 1.8.7
|
||||
*
|
||||
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Menu#theming
|
||||
*/
|
||||
.ui-menu {
|
||||
list-style:none;
|
||||
padding: 1px;
|
||||
margin: 0;
|
||||
display:block;
|
||||
float: left;
|
||||
}
|
||||
.ui-menu .ui-menu {
|
||||
margin-top: -3px;
|
||||
}
|
||||
.ui-menu .ui-menu-item {
|
||||
margin:0;
|
||||
padding: 0;
|
||||
zoom: 1;
|
||||
float: left;
|
||||
clear: left;
|
||||
width: 100%;
|
||||
}
|
||||
.ui-menu .ui-menu-item a {
|
||||
text-decoration:none;
|
||||
display:block;
|
||||
padding:.2em .4em;
|
||||
line-height:1.5;
|
||||
zoom:1;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
.ui-menu .ui-menu-item a.ui-state-hover,
|
||||
.ui-menu .ui-menu-item a.ui-state-active {
|
||||
font-weight: normal;
|
||||
margin: -1px;
|
||||
background: #D9EDF7;
|
||||
color: #3A89A3;
|
||||
text-shadow: 0px 1px 1px #fff;
|
||||
border: none;
|
||||
border: 1px solid #ADE;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*
|
||||
* jQuery UI Datepicker 1.8.7
|
||||
*
|
||||
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://docs.jquery.com/UI/Datepicker#theming
|
||||
*/
|
||||
.ui-datepicker {
|
||||
width: 17em;
|
||||
padding: 0;
|
||||
display: none;
|
||||
border-color: #DDDDDD;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-header {
|
||||
position:relative;
|
||||
padding:.35em 0;
|
||||
border: none;
|
||||
border-bottom: 1px solid #B6B6B6;
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
border-radius: 0;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #bbb;
|
||||
-webkit-box-shadow: 0 0 0 3px #F1F1F1;
|
||||
-moz-box-shadow: 0 0 0 3px #f1f1f1;
|
||||
-ms-box-shadow: 0 0 0 3px #f1f1f1;
|
||||
-o-box-shadow: 0 0 0 3px #f1f1f1;
|
||||
box-shadow: 0 0 0 3px #F1F1F1;
|
||||
}
|
||||
|
||||
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 6px; width: 1.8em; height: 1.8em; }
|
||||
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { border: 1px none; }
|
||||
.ui-datepicker .ui-datepicker-prev { left:2px; }
|
||||
.ui-datepicker .ui-datepicker-next { right:2px; }
|
||||
.ui-datepicker .ui-datepicker-prev span { background-position: 0px -32px !important; }
|
||||
.ui-datepicker .ui-datepicker-next span { background-position: -16px -32px !important; }
|
||||
.ui-datepicker .ui-datepicker-prev-hover span { background-position: 0px -48px !important; }
|
||||
.ui-datepicker .ui-datepicker-next-hover span { background-position: -16px -48px !important; }
|
||||
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; background: url(icon_sprite.png) no-repeat; }
|
||||
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; font-size: 12px; text-shadow: 0 1px 0 rgba(255,255,255,0.6); }
|
||||
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
|
||||
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
|
||||
.ui-datepicker select.ui-datepicker-month,
|
||||
.ui-datepicker select.ui-datepicker-year { width: 49%;}
|
||||
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
|
||||
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
|
||||
.ui-datepicker td { border: 0; padding: 1px; line-height: 24px; background-color: #FFF!important; }
|
||||
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
|
||||
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
|
||||
.ui-datepicker table .ui-state-highlight { border-color: #ADE; }
|
||||
.ui-datepicker-calendar .ui-state-default { background: transparent; border-color: #FFF; }
|
||||
.ui-datepicker-calendar .ui-state-active { background: #D9EDF7; border-color: #ADE; color: #3A89A3; font-weight: bold; text-shadow: 0 1px 1px #fff; }
|
|
@ -1,2 +1,3 @@
|
|||
/** Typo **/
|
||||
$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;
|
||||
$regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
|
|
|
@ -59,9 +59,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@issue = @project.issues.new(params[:issue])
|
||||
@issue.author = current_user
|
||||
@issue.save
|
||||
@issue = Issues::CreateService.new(project, current_user, params[:issue]).execute
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
|
@ -76,8 +74,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
@issue.update_attributes(params[:issue])
|
||||
@issue.reset_events_cache
|
||||
@issue = Issues::UpdateService.new(project, current_user, params[:issue]).execute(issue)
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
|
|
|
@ -76,10 +76,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@merge_request = MergeRequest.new(params[:merge_request])
|
||||
@merge_request.author = current_user
|
||||
@target_branches ||= []
|
||||
if @merge_request.save
|
||||
@merge_request = MergeRequests::CreateService.new(project, current_user, params[:merge_request]).execute
|
||||
|
||||
if @merge_request.valid?
|
||||
redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully created.'
|
||||
else
|
||||
@source_project = @merge_request.source_project
|
||||
|
@ -89,29 +89,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
# If we close MergeRequest we want to ignore validation
|
||||
# so we can close broken one (Ex. fork project removed)
|
||||
if params[:merge_request] == {"state_event"=>"close"}
|
||||
@merge_request.allow_broken = true
|
||||
|
||||
if @merge_request.close
|
||||
opts = { notice: 'Merge request was successfully closed.' }
|
||||
else
|
||||
opts = { alert: 'Failed to close merge request.' }
|
||||
end
|
||||
|
||||
redirect_to [@merge_request.target_project, @merge_request], opts
|
||||
return
|
||||
end
|
||||
|
||||
# We dont allow change of source/target projects
|
||||
# after merge request was created
|
||||
params[:merge_request].delete(:source_project_id)
|
||||
params[:merge_request].delete(:target_project_id)
|
||||
|
||||
if @merge_request.update_attributes(params[:merge_request])
|
||||
@merge_request.reset_events_cache
|
||||
@merge_request = MergeRequests::UpdateService.new(project, current_user, params[:merge_request]).execute(@merge_request)
|
||||
|
||||
if @merge_request.valid?
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.html do
|
||||
|
|
|
@ -20,7 +20,7 @@ module MergeRequestsHelper
|
|||
target_project_id: target_project.id,
|
||||
source_branch: event.branch_name,
|
||||
target_branch: target_project.repository.root_ref,
|
||||
title: event.branch_name.humanize
|
||||
title: event.branch_name.titleize.humanize
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -13,14 +13,15 @@ class Email < ActiveRecord::Base
|
|||
# Relations
|
||||
#
|
||||
belongs_to :user
|
||||
|
||||
|
||||
#
|
||||
# Validations
|
||||
#
|
||||
validates :user_id, presence: true
|
||||
validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
|
||||
validate :unique_email, if: ->(email) { email.email_changed? }
|
||||
|
||||
|
||||
after_create :notify
|
||||
before_validation :cleanup_email
|
||||
|
||||
def cleanup_email
|
||||
|
@ -30,4 +31,8 @@ class Email < ActiveRecord::Base
|
|||
def unique_email
|
||||
self.errors.add(:email, 'has already been taken') if User.exists?(email: self.email)
|
||||
end
|
||||
end
|
||||
|
||||
def notify
|
||||
NotificationService.new.new_email(self)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,6 +29,10 @@ class Key < ActiveRecord::Base
|
|||
|
||||
delegate :name, :email, to: :user, prefix: true
|
||||
|
||||
after_create :add_to_shell
|
||||
after_create :notify_user
|
||||
after_destroy :remove_from_shell
|
||||
|
||||
def strip_white_space
|
||||
self.key = key.strip unless key.blank?
|
||||
end
|
||||
|
@ -42,6 +46,26 @@ class Key < ActiveRecord::Base
|
|||
"key-#{id}"
|
||||
end
|
||||
|
||||
def add_to_shell
|
||||
GitlabShellWorker.perform_async(
|
||||
:add_key,
|
||||
shell_id,
|
||||
key
|
||||
)
|
||||
end
|
||||
|
||||
def notify_user
|
||||
NotificationService.new.new_key(self)
|
||||
end
|
||||
|
||||
def remove_from_shell
|
||||
GitlabShellWorker.perform_async(
|
||||
:remove_key,
|
||||
shell_id,
|
||||
key,
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_fingerpint
|
||||
|
|
|
@ -97,6 +97,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
validates :target_project, presence: true
|
||||
validates :target_branch, presence: true
|
||||
validate :validate_branches
|
||||
validate :validate_fork
|
||||
|
||||
scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.project_ids) }
|
||||
scope :of_user_team, ->(team) { where("(source_project_id in (:team_project_ids) OR target_project_id in (:team_project_ids) AND assignee_id in (:team_member_ids))", team_project_ids: team.project_ids, team_member_ids: team.member_ids) }
|
||||
|
@ -125,6 +126,22 @@ class MergeRequest < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def validate_fork
|
||||
return true unless target_project && source_project
|
||||
|
||||
if target_project == source_project
|
||||
true
|
||||
else
|
||||
# If source and target projects are different
|
||||
# we should check if source project is actually a fork of target project
|
||||
if source_project.forked_from?(target_project)
|
||||
true
|
||||
else
|
||||
errors.add :base, "Source project is not a fork of target project"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_merge_request_diff
|
||||
if source_branch_changed? || target_branch_changed?
|
||||
reload_code
|
||||
|
|
|
@ -552,4 +552,8 @@ class Project < ActiveRecord::Base
|
|||
gitlab_shell.update_repository_head(self.path_with_namespace, branch)
|
||||
reload_default_branch
|
||||
end
|
||||
|
||||
def forked_from?(project)
|
||||
forked? && project == forked_from_project
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
class EmailObserver < BaseObserver
|
||||
def after_create(email)
|
||||
notification.new_email(email)
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
class IssueObserver < BaseObserver
|
||||
def after_create(issue)
|
||||
notification.new_issue(issue, current_user)
|
||||
event_service.open_issue(issue, current_user)
|
||||
issue.create_cross_references!(issue.project, current_user)
|
||||
execute_hooks(issue)
|
||||
end
|
||||
|
||||
def after_close(issue, transition)
|
||||
notification.close_issue(issue, current_user)
|
||||
event_service.close_issue(issue, current_user)
|
||||
create_note(issue)
|
||||
execute_hooks(issue)
|
||||
end
|
||||
|
||||
def after_reopen(issue, transition)
|
||||
event_service.reopen_issue(issue, current_user)
|
||||
create_note(issue)
|
||||
execute_hooks(issue)
|
||||
end
|
||||
|
||||
def after_update(issue)
|
||||
if issue.is_being_reassigned?
|
||||
notification.reassigned_issue(issue, current_user)
|
||||
create_assignee_note(issue)
|
||||
end
|
||||
|
||||
issue.notice_added_references(issue.project, current_user)
|
||||
execute_hooks(issue)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Create issue note with service comment like 'Status changed to closed'
|
||||
def create_note(issue)
|
||||
Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit)
|
||||
end
|
||||
|
||||
def create_assignee_note(issue)
|
||||
Note.create_assignee_change_note(issue, issue.project, current_user, issue.assignee)
|
||||
end
|
||||
|
||||
def execute_hooks(issue)
|
||||
issue.project.execute_hooks(issue.to_hook_data, :issue_hooks)
|
||||
end
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
class KeyObserver < BaseObserver
|
||||
def after_create(key)
|
||||
GitlabShellWorker.perform_async(
|
||||
:add_key,
|
||||
key.shell_id,
|
||||
key.key
|
||||
)
|
||||
|
||||
notification.new_key(key)
|
||||
end
|
||||
|
||||
def after_destroy(key)
|
||||
GitlabShellWorker.perform_async(
|
||||
:remove_key,
|
||||
key.shell_id,
|
||||
key.key,
|
||||
)
|
||||
end
|
||||
end
|
|
@ -1,43 +0,0 @@
|
|||
class MergeRequestObserver < BaseObserver
|
||||
def after_create(merge_request)
|
||||
event_service.open_mr(merge_request, current_user)
|
||||
notification.new_merge_request(merge_request, current_user)
|
||||
merge_request.create_cross_references!(merge_request.project, current_user)
|
||||
execute_hooks(merge_request)
|
||||
end
|
||||
|
||||
def after_close(merge_request, transition)
|
||||
event_service.close_mr(merge_request, current_user)
|
||||
notification.close_mr(merge_request, current_user)
|
||||
create_note(merge_request)
|
||||
execute_hooks(merge_request)
|
||||
end
|
||||
|
||||
def after_reopen(merge_request, transition)
|
||||
event_service.reopen_mr(merge_request, current_user)
|
||||
create_note(merge_request)
|
||||
execute_hooks(merge_request)
|
||||
merge_request.reload_code
|
||||
merge_request.mark_as_unchecked
|
||||
end
|
||||
|
||||
def after_update(merge_request)
|
||||
notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned?
|
||||
|
||||
merge_request.notice_added_references(merge_request.project, current_user)
|
||||
execute_hooks(merge_request)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Create merge request note with service comment like 'Status changed to closed'
|
||||
def create_note(merge_request)
|
||||
Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
|
||||
end
|
||||
|
||||
def execute_hooks(merge_request)
|
||||
if merge_request.project
|
||||
merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@ class UsersProjectObserver < BaseObserver
|
|||
end
|
||||
|
||||
def after_update(users_project)
|
||||
notification.update_team_member(users_project)
|
||||
notification.update_team_member(users_project) if users_project.project_access_changed?
|
||||
end
|
||||
|
||||
def after_destroy(users_project)
|
||||
|
|
|
@ -16,4 +16,16 @@ class BaseService
|
|||
def can?(object, action, subject)
|
||||
abilities.allowed?(object, action, subject)
|
||||
end
|
||||
|
||||
def notification_service
|
||||
NotificationService.new
|
||||
end
|
||||
|
||||
def event_service
|
||||
EventCreateService.new
|
||||
end
|
||||
|
||||
def log_info message
|
||||
Gitlab::AppLogger.info message
|
||||
end
|
||||
end
|
||||
|
|
|
@ -86,10 +86,9 @@ class GitPushService
|
|||
author = commit_user(commit)
|
||||
|
||||
if !issues_to_close.empty? && is_default_branch
|
||||
Thread.current[:current_user] = author
|
||||
Thread.current[:current_commit] = commit
|
||||
|
||||
issues_to_close.each { |i| i.close && i.save }
|
||||
issues_to_close.each do |issue|
|
||||
Issues::CloseService.new(project, author, {}).execute(issue, commit)
|
||||
end
|
||||
end
|
||||
|
||||
# Create cross-reference notes for any other references. Omit any issues that were referenced in an
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
module Issues
|
||||
class BaseService < ::BaseService
|
||||
|
||||
private
|
||||
|
||||
def create_assignee_note(issue)
|
||||
Note.create_assignee_change_note(issue, issue.project, current_user, issue.assignee)
|
||||
end
|
||||
|
||||
def execute_hooks(issue)
|
||||
issue.project.execute_hooks(issue.to_hook_data, :issue_hooks)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
module Issues
|
||||
class CloseService < Issues::BaseService
|
||||
def execute(issue, commit = nil)
|
||||
if issue.close
|
||||
notification_service.close_issue(issue, current_user)
|
||||
event_service.close_issue(issue, current_user)
|
||||
create_note(issue, commit)
|
||||
execute_hooks(issue)
|
||||
end
|
||||
|
||||
issue
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_note(issue, current_commit)
|
||||
Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
module Issues
|
||||
class CreateService < Issues::BaseService
|
||||
def execute
|
||||
issue = project.issues.new(params)
|
||||
issue.author = current_user
|
||||
|
||||
if issue.save
|
||||
notification_service.new_issue(issue, current_user)
|
||||
event_service.open_issue(issue, current_user)
|
||||
issue.create_cross_references!(issue.project, current_user)
|
||||
execute_hooks(issue)
|
||||
end
|
||||
|
||||
issue
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
module Issues
|
||||
class ReopenService < Issues::BaseService
|
||||
def execute(issue)
|
||||
if issue.reopen
|
||||
event_service.reopen_issue(issue, current_user)
|
||||
create_note(issue)
|
||||
execute_hooks(issue)
|
||||
end
|
||||
|
||||
issue
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_note(issue)
|
||||
Note.create_status_change_note(issue, issue.project, current_user, issue.state, nil)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
module Issues
|
||||
class UpdateService < Issues::BaseService
|
||||
def execute(issue)
|
||||
state = params.delete('state_event')
|
||||
|
||||
case state
|
||||
when 'reopen'
|
||||
Issues::ReopenService.new(project, current_user, {}).execute(issue)
|
||||
when 'close'
|
||||
Issues::CloseService.new(project, current_user, {}).execute(issue)
|
||||
end
|
||||
|
||||
if params.present? && issue.update_attributes(params)
|
||||
issue.reset_events_cache
|
||||
|
||||
if issue.previous_changes.include?('assignee_id')
|
||||
notification_service.reassigned_issue(issue, current_user)
|
||||
create_assignee_note(issue)
|
||||
end
|
||||
|
||||
issue.notice_added_references(issue.project, current_user)
|
||||
execute_hooks(issue)
|
||||
end
|
||||
|
||||
issue
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
module MergeRequests
|
||||
class BaseService < ::BaseService
|
||||
|
||||
private
|
||||
|
||||
def create_assignee_note(merge_request)
|
||||
Note.create_assignee_change_note(merge_request, merge_request.project, current_user, merge_request.assignee)
|
||||
end
|
||||
|
||||
def create_note(merge_request)
|
||||
Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
|
||||
end
|
||||
|
||||
def execute_hooks(merge_request)
|
||||
if merge_request.project
|
||||
merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
module MergeRequests
|
||||
class CloseService < MergeRequests::BaseService
|
||||
def execute(merge_request, commit = nil)
|
||||
# If we close MergeRequest we want to ignore validation
|
||||
# so we can close broken one (Ex. fork project removed)
|
||||
merge_request.allow_broken = true
|
||||
|
||||
if merge_request.close
|
||||
event_service.close_mr(merge_request, current_user)
|
||||
notification_service.close_mr(merge_request, current_user)
|
||||
create_note(merge_request)
|
||||
execute_hooks(merge_request)
|
||||
end
|
||||
|
||||
merge_request
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
module MergeRequests
|
||||
class CreateService < MergeRequests::BaseService
|
||||
def execute
|
||||
merge_request = MergeRequest.new(params)
|
||||
merge_request.source_project = project
|
||||
merge_request.target_project ||= project
|
||||
merge_request.author = current_user
|
||||
|
||||
if merge_request.save
|
||||
event_service.open_mr(merge_request, current_user)
|
||||
notification_service.new_merge_request(merge_request, current_user)
|
||||
merge_request.create_cross_references!(merge_request.project, current_user)
|
||||
execute_hooks(merge_request)
|
||||
end
|
||||
|
||||
merge_request
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
module MergeRequests
|
||||
class ReopenService < MergeRequests::BaseService
|
||||
def execute(merge_request)
|
||||
if merge_request.reopen
|
||||
event_service.reopen_mr(merge_request, current_user)
|
||||
create_note(merge_request)
|
||||
execute_hooks(merge_request)
|
||||
merge_request.reload_code
|
||||
merge_request.mark_as_unchecked
|
||||
end
|
||||
|
||||
merge_request
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
require_relative 'base_service'
|
||||
require_relative 'reopen_service'
|
||||
require_relative 'close_service'
|
||||
|
||||
module MergeRequests
|
||||
class UpdateService < MergeRequests::BaseService
|
||||
def execute(merge_request)
|
||||
# We dont allow change of source/target projects
|
||||
# after merge request was created
|
||||
params.delete(:source_project_id)
|
||||
params.delete(:target_project_id)
|
||||
|
||||
state = params.delete('state_event')
|
||||
|
||||
case state
|
||||
when 'reopen'
|
||||
MergeRequests::ReopenService.new(project, current_user, {}).execute(merge_request)
|
||||
when 'close'
|
||||
MergeRequests::CloseService.new(project, current_user, {}).execute(merge_request)
|
||||
end
|
||||
|
||||
if params.present? && merge_request.update_attributes(params)
|
||||
merge_request.reset_events_cache
|
||||
|
||||
if merge_request.previous_changes.include?('assignee_id')
|
||||
notification_service.reassigned_merge_request(merge_request, current_user)
|
||||
create_assignee_note(merge_request)
|
||||
end
|
||||
|
||||
merge_request.notice_added_references(merge_request.project, current_user)
|
||||
execute_hooks(merge_request)
|
||||
end
|
||||
|
||||
merge_request
|
||||
end
|
||||
end
|
||||
end
|
|
@ -178,29 +178,29 @@ class NotificationService
|
|||
|
||||
# Get project users with WATCH notification level
|
||||
def project_watchers(project)
|
||||
# Gather all user ids that have WATCH notification setting for project
|
||||
project_notification_uids = project_notification_list(project, Notification::N_WATCH)
|
||||
project_members = users_project_notification(project)
|
||||
|
||||
# Gather all user ids that have WATCH notification setting for group
|
||||
group_notification_uids = group_notification_list(project, Notification::N_WATCH)
|
||||
users_with_project_level_global = users_project_notification(project, Notification::N_GLOBAL)
|
||||
users_with_group_level_global = users_group_notification(project, Notification::N_GLOBAL)
|
||||
users = users_with_global_level_watch([users_with_project_level_global, users_with_group_level_global].flatten.uniq)
|
||||
|
||||
# Gather all user ids that have GLOBAL setting
|
||||
global_notification_uids = global_notification_list(project)
|
||||
users_with_project_setting = select_users_project_setting(project, users_with_project_level_global, users)
|
||||
users_with_group_setting = select_users_group_setting(project, project_members, users_with_group_level_global, users)
|
||||
|
||||
project_and_group_uids = [project_notification_uids, group_notification_uids].flatten.uniq
|
||||
group_and_project_watchers = User.where(id: project_and_group_uids)
|
||||
|
||||
# Find all users that have WATCH as their GLOBAL setting
|
||||
global_watchers = User.where(id: global_notification_uids, notification_level: Notification::N_WATCH)
|
||||
|
||||
[group_and_project_watchers, global_watchers].flatten.uniq
|
||||
User.where(id: users_with_project_setting.concat(users_with_group_setting).uniq).to_a
|
||||
end
|
||||
|
||||
def project_notification_list(project, notification_level)
|
||||
project.users_projects.where(notification_level: notification_level).pluck(:user_id)
|
||||
def users_project_notification(project, notification_level=nil)
|
||||
project_members = project.users_projects
|
||||
|
||||
if notification_level
|
||||
project_members.where(notification_level: notification_level).pluck(:user_id)
|
||||
else
|
||||
project_members.pluck(:user_id)
|
||||
end
|
||||
end
|
||||
|
||||
def group_notification_list(project, notification_level)
|
||||
def users_group_notification(project, notification_level)
|
||||
if project.group
|
||||
project.group.users_groups.where(notification_level: notification_level).pluck(:user_id)
|
||||
else
|
||||
|
@ -208,11 +208,47 @@ class NotificationService
|
|||
end
|
||||
end
|
||||
|
||||
def global_notification_list(project)
|
||||
[
|
||||
project_notification_list(project, Notification::N_GLOBAL),
|
||||
group_notification_list(project, Notification::N_GLOBAL)
|
||||
].flatten
|
||||
def users_with_global_level_watch(ids)
|
||||
User.where(
|
||||
id: ids,
|
||||
notification_level: Notification::N_WATCH
|
||||
).pluck(:id)
|
||||
end
|
||||
|
||||
# Build a list of users based on project notifcation settings
|
||||
def select_users_project_setting(project, global_setting, users_global_level_watch)
|
||||
users = users_project_notification(project, Notification::N_WATCH)
|
||||
|
||||
# If project setting is global, add to watch list if global setting is watch
|
||||
global_setting.each do |user_id|
|
||||
if users_global_level_watch.include?(user_id)
|
||||
users << user_id
|
||||
end
|
||||
end
|
||||
|
||||
users
|
||||
end
|
||||
|
||||
# Build a list of users based on group notifcation settings
|
||||
def select_users_group_setting(project, project_members, global_setting, users_global_level_watch)
|
||||
uids = users_group_notification(project, Notification::N_WATCH)
|
||||
|
||||
# Group setting is watch, add to users list if user is not project member
|
||||
users = []
|
||||
uids.each do |user_id|
|
||||
if project_members.exclude?(user_id)
|
||||
users << user_id
|
||||
end
|
||||
end
|
||||
|
||||
# Group setting is global, add to users list if global setting is watch
|
||||
global_setting.each do |user_id|
|
||||
if project_members.exclude?(user_id) && users_global_level_watch.include?(user_id)
|
||||
users << user_id
|
||||
end
|
||||
end
|
||||
|
||||
users
|
||||
end
|
||||
|
||||
# Remove users with disabled notifications from array
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
= form_tag project_issues_path(@project), 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, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
|
||||
= hidden_field_tag :state, params['state']
|
||||
= hidden_field_tag :scope, params['scope']
|
||||
= hidden_field_tag :assignee_id, params['assignee_id']
|
||||
= hidden_field_tag :milestone_id, params['milestone_id']
|
||||
= hidden_field_tag :label_id, params['label_id']
|
||||
- if can? current_user, :write_issue, @project
|
||||
= link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
|
||||
%i.icon-plus
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
.col-sm-10
|
||||
.clearfix
|
||||
.pull-left
|
||||
- projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project]
|
||||
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
|
||||
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? })
|
||||
.pull-left
|
||||
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
var mrTitle = $('#merge_request_title');
|
||||
|
||||
if(mrTitle.val().length == 0) {
|
||||
mrTitle.val("#{params[:ref].humanize}");
|
||||
mrTitle.val("#{params[:ref].titleize.humanize}");
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
%ul
|
||||
%li keep stable branches secured
|
||||
%li forced code review before merge to protected branches
|
||||
%li prevents branch from force push
|
||||
%p Read more about project permissions #{link_to "here", help_permissions_path, class: "underlined-link"}
|
||||
|
||||
- if can? current_user, :admin_project, @project
|
||||
|
|
|
@ -21,9 +21,6 @@ module Gitlab
|
|||
# Activate observers that should always be running.
|
||||
config.active_record.observers = :milestone_observer,
|
||||
:project_activity_cache_observer,
|
||||
:issue_observer,
|
||||
:key_observer,
|
||||
:merge_request_observer,
|
||||
:note_observer,
|
||||
:project_observer,
|
||||
:system_hook_observer,
|
||||
|
@ -64,6 +61,7 @@ module Gitlab
|
|||
config.assets.enabled = true
|
||||
config.assets.paths << Emoji.images_path
|
||||
config.assets.precompile << "emoji/*.png"
|
||||
config.assets.precompile << "print.css"
|
||||
|
||||
# Version of your assets, change this if you want to expire all your assets
|
||||
config.assets.version = '1.0'
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
## The GitLab Documentation covers the following subjects
|
||||
**User documentation**
|
||||
|
||||
+ [API](api/README.md)
|
||||
+ [Development](development/README.md)
|
||||
+ [Install](install/README.md)
|
||||
+ [Integration](integration/external-issue-tracker.md)
|
||||
+ [Legal](legal/README.md)
|
||||
+ [Markdown](markdown/markdown.md)
|
||||
+ [Permissions](permissions/permissions.md)
|
||||
+ [Public access](public_access/public_access.md)
|
||||
+ [Raketasks](raketasks/README.md)
|
||||
+ [Release](release/README.md)
|
||||
+ [Security](security/README.md)
|
||||
+ [SSH](ssh/README.md)
|
||||
+ [System hooks](system_hooks/system_hooks.md)
|
||||
+ [Update](update/README.md)
|
||||
+ [Web hooks](web_hooks/web_hooks.md)
|
||||
+ [Workflow](workflow/workflow.md)
|
||||
+ [API](api/README.md) Explore how you can access GitLab via a simple and powerful API.
|
||||
+ [Markdown](markdown/markdown.md) Learn what you can do with GitLab's advanced formatting system.
|
||||
+ [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
|
||||
+ [Public access](public_access/public_access.md) Learn how you can allow public and internal access to a project.
|
||||
+ [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
|
||||
+ [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
|
||||
+ [Workflow](workflow/workflow.md) Learn how to use Git and GitLab together.
|
||||
|
||||
**Administrator documentation**
|
||||
|
||||
+ [Install](install/README.md) Requirements, directory structures and manual installation.
|
||||
+ [Integration](integration/external-issue-tracker.md) How to integrate JIRA and Redmine.
|
||||
+ [Raketasks](raketasks/README.md) Explore what GitLab has in store for you to make administration easier.
|
||||
+ [System hooks](system_hooks/system_hooks.md) Let GitLab notify you when certain management tasks need to be carried out.
|
||||
+ [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
|
||||
+ [Update](update/README.md) Update guides to upgrade your installation.
|
||||
|
||||
**Contributor documentation**
|
||||
|
||||
+ [Development](development/README.md) Explains the architecture and the guidelines for shell commands.
|
||||
+ [Legal](legal/README.md) Contributor license agreements.
|
||||
+ [Release](release/README.md) How to make the monthly and security releases.
|
||||
|
|
|
@ -93,7 +93,7 @@ Then select 'Internet Site' and press enter to confirm the hostname.
|
|||
|
||||
# 2. Ruby
|
||||
|
||||
The use of ruby version managers such as [RVM](http://rvm.io/), [rbenv](https://github.com/sstephenson/rbenv) or [chruby](https://github.com/postmodern/chruby) with GitLab in production frequently leads to hard to diagnose problems. Version managers are not supported and we stronly advise everyone to follow the instructions below to use a system ruby.
|
||||
The use of ruby version managers such as [RVM](http://rvm.io/), [rbenv](https://github.com/sstephenson/rbenv) or [chruby](https://github.com/postmodern/chruby) with GitLab in production frequently leads to hard to diagnose problems. For example, GitLab Shell is called from OpenSSH and having a version manager can prevent pushing and pulling over SSH. Version managers are not supported and we stronly advise everyone to follow the instructions below to use a system ruby.
|
||||
|
||||
Remove the old Ruby 1.8 if present
|
||||
|
||||
|
@ -202,6 +202,7 @@ You can change `6-6-stable` to `master` if you want the *bleeding edge* version,
|
|||
|
||||
# Create directory for satellites
|
||||
sudo -u git -H mkdir /home/git/gitlab-satellites
|
||||
sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
|
||||
|
||||
# Create directories for sockets/pids and make sure GitLab can write to them
|
||||
sudo -u git -H mkdir tmp/pids/
|
||||
|
|
|
@ -2,7 +2,7 @@ GitLab has a great issue tracker but you can also use an external issue tracker
|
|||
|
||||
- the 'Issues' link on the GitLab project pages takes you to the appropriate JIRA issue index;
|
||||
- clicking 'New issue' on the project dashboard creates a new JIRA issue;
|
||||
- textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue.
|
||||
- To reference JIRA issue PROJECT-1234 in comments, use syntax #PROJECT-1234. Commit messages get turned into HTML links to the corresponding JIRA issue.
|
||||
|
||||
![jira screenshot](jira-intergration-points.png)
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# GitLab LDAP integration
|
||||
|
||||
GitLab can be configured to allow your users to sign with their LDAP credentials to integrate with e.g. Active Directory.
|
||||
The first time a user signs in with LDAP credentials, GitLab will create a new GitLab user associated with the LDAP Distinguished Name (DN) of the LDAP user.
|
||||
GitLab user attributes such as nickname and email will be copied from the LDAP user entry.
|
||||
|
||||
## Enabling LDAP sign-in for existing GitLab users
|
||||
|
||||
When a user signs in to GitLab with LDAP for the first time, and their LDAP email address is the primary email address of an existing GitLab user, then the LDAP DN will be associated with the existing user.
|
||||
If the LDAP email attribute is not found in GitLab's database, a new user is created.
|
||||
|
||||
In other words, if an existing GitLab user wants to enable LDAP sign-in for themselves, they should check that their GitLab email address matches their LDAP email address, and then sign into GitLab via their LDAP credentials.
|
||||
GitLab recognizes the following LDAP attributes as email addresses: `mail`, `email` and `userPrincipalName`.
|
||||
If multiple LDAP email attributes are present, e.g. `mail: foo@bar.com` and `email: foo@example.com`, then the first attribute found wins -- in this case `foo@bar.com`.
|
|
@ -61,9 +61,9 @@ After making the release branch new commits are cherry-picked from master. When
|
|||
* 1-7th: official merge window (see contributing guide)
|
||||
* 8-14th: work on bugfixes, sponsored features and GitLab EE
|
||||
* 15th: code freeze (stop merging into master except essential bugfixes)
|
||||
* 18th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1, release on GitLab Cloud)
|
||||
* 18th: release candidate 1 (VERSION x.x.0.rc1, annotated tag and tweet about x.x.0.rc1, release on GitLab Cloud)
|
||||
* 20st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems)
|
||||
* 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet)
|
||||
* 22nd: release (VERSION x.x.0, create x-x-stable branch, annotated tag tag, blog and tweet)
|
||||
* 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems)
|
||||
* 24-end of month: release GitLab EE and GitLab CI
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ Please report suspected security vulnerabilities in private to support@gitlab.co
|
|||
1. Create feature branches for the blog post on GitLab.com and link them from the code branch
|
||||
1. Merge the code feature branch into master
|
||||
1. Cherry-pick the code into the latest stable branch
|
||||
1. Create a git tag vX.X.X for CE and another patch release for EE
|
||||
1. Create an annotated tag vX.X.X for CE and another patch release for EE
|
||||
1. Push the code and the tags to all the CE and EE repositories
|
||||
1. Apply the patch to GitLab Cloud and the private GitLab development server
|
||||
1. Merge and publish the blog posts
|
||||
|
|
|
@ -80,6 +80,9 @@ sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
|
|||
|
||||
# Clean up assets and cache
|
||||
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
|
||||
|
||||
# Close access to gitlab-satellites for others
|
||||
sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
|
||||
```
|
||||
|
||||
### 6. Update config files
|
||||
|
|
|
@ -63,6 +63,9 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
|
|||
|
||||
# Update the logrotate configuration (keep logs for 90 days instead of 52 weeks)
|
||||
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
|
||||
|
||||
# Close access to gitlab-satellites for others
|
||||
sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -40,3 +40,10 @@ To make sure you didn't miss anything run a more thorough check with:
|
|||
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
|
||||
|
||||
If all items are green, then congratulations upgrade is complete!
|
||||
|
||||
|
||||
### One line upgrade command
|
||||
|
||||
You've read through the entire guide, and probably did all the steps manually. Here is a one liner for convenience, the next time you upgrade:
|
||||
|
||||
cd /home/git/gitlab; sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; sudo service gitlab stop; sudo -u git -H ruby script/upgrade.rb -y; sudo service gitlab start; sudo service nginx restart; sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
+ [Workflow](workflow/workflow.md)
|
||||
+ [Project Features](workflow/project_features.md)
|
|
@ -0,0 +1,35 @@
|
|||
When in a Project -> Settings, you will find Features on the bottom of the page that you can toggle.
|
||||
Below you will find a more elaborate explanation of each of these.
|
||||
|
||||
|
||||
## Issues
|
||||
|
||||
Issues is a really powerful, but lightweight issue tracking system.
|
||||
You can make tickets, assign them to people, file them under milestones, order them with labels and have discussion in them.
|
||||
They integrate deeply into GitLab and are easily referenced from anywhere by using # and the issuenumber.
|
||||
At GitLab.com, we use this for all our project management needs.
|
||||
|
||||
## Merge Requests
|
||||
|
||||
Using a merge request, you can review and discuss code before it is merged in the branch of your code.
|
||||
As with issues, it can be assigned; people, issues, etc. can be refereced; milestones attached.
|
||||
We see it as an integral part of working together on code and couldn't work without it.
|
||||
|
||||
|
||||
## Wiki
|
||||
|
||||
This is a separate system for documentation, built right into GitLab.
|
||||
It is source controlled and is very convenient if you don't want to keep you documentation in your source code, but you do want to keep it in your GitLab project.
|
||||
|
||||
|
||||
## Wall
|
||||
|
||||
For simple, project specific conversations, the wall can be used.
|
||||
It's very lightweight and simple and works well if you're not interested in using issues, but still want to occasionally communicate within a project.
|
||||
|
||||
|
||||
## Snippets
|
||||
|
||||
Snippets are little bits of code or text.
|
||||
This is a nice place to put code or text that is used semi-regularly within the project, but does not belong in source control.
|
||||
For example, a specific config file that is used by > the team that is only valid for the people that work on the code.
|
|
@ -53,15 +53,15 @@ class DashboardMergeRequests < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
def assigned_merge_request
|
||||
@assigned_merge_request ||= create :merge_request, assignee: current_user, target_project: project
|
||||
@assigned_merge_request ||= create :merge_request, assignee: current_user, target_project: project, source_project: project
|
||||
end
|
||||
|
||||
def authored_merge_request
|
||||
@authored_merge_request ||= create :merge_request, author: current_user, target_project: project
|
||||
@authored_merge_request ||= create :merge_request, source_branch: 'simple_merge_request', author: current_user, target_project: project, source_project: project
|
||||
end
|
||||
|
||||
def other_merge_request
|
||||
@other_merge_request ||= create :merge_request, target_project: project
|
||||
@other_merge_request ||= create :merge_request, source_branch: '2_3_notes_fix', target_project: project, source_project: project
|
||||
end
|
||||
|
||||
def project
|
||||
|
|
|
@ -52,6 +52,4 @@ Spinach.hooks.before_run do
|
|||
RSpec::Mocks::setup self
|
||||
|
||||
include FactoryGirl::Syntax::Methods
|
||||
MergeRequestObserver.any_instance.stub(current_user: create(:user))
|
||||
end
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ module API
|
|||
# project - project path with namespace
|
||||
# action - git action (git-upload-pack or git-receive-pack)
|
||||
# ref - branch name
|
||||
# forced_push - forced_push
|
||||
#
|
||||
get "/allowed" do
|
||||
# Check for *.wiki repositories.
|
||||
|
@ -35,7 +36,8 @@ module API
|
|||
project,
|
||||
params[:ref],
|
||||
params[:oldrev],
|
||||
params[:newrev]
|
||||
params[:newrev],
|
||||
params[:forced_push]
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -48,17 +48,15 @@ module API
|
|||
# Example Request:
|
||||
# POST /projects/:id/issues
|
||||
post ":id/issues" do
|
||||
set_current_user_for_thread do
|
||||
required_attributes! [:title]
|
||||
attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
|
||||
attrs[:label_list] = params[:labels] if params[:labels].present?
|
||||
@issue = user_project.issues.new attrs
|
||||
@issue.author = current_user
|
||||
if @issue.save
|
||||
present @issue, with: Entities::Issue
|
||||
else
|
||||
not_found!
|
||||
end
|
||||
required_attributes! [:title]
|
||||
attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
|
||||
attrs[:label_list] = params[:labels] if params[:labels].present?
|
||||
issue = ::Issues::CreateService.new(user_project, current_user, attrs).execute
|
||||
|
||||
if issue.valid?
|
||||
present issue, with: Entities::Issue
|
||||
else
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -76,18 +74,18 @@ module API
|
|||
# Example Request:
|
||||
# PUT /projects/:id/issues/:issue_id
|
||||
put ":id/issues/:issue_id" do
|
||||
set_current_user_for_thread do
|
||||
@issue = user_project.issues.find(params[:issue_id])
|
||||
authorize! :modify_issue, @issue
|
||||
issue = user_project.issues.find(params[:issue_id])
|
||||
authorize! :modify_issue, issue
|
||||
|
||||
attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
|
||||
attrs[:label_list] = params[:labels] if params[:labels].present?
|
||||
attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
|
||||
attrs[:label_list] = params[:labels] if params[:labels].present?
|
||||
|
||||
if @issue.update_attributes attrs
|
||||
present @issue, with: Entities::Issue
|
||||
else
|
||||
not_found!
|
||||
end
|
||||
issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
|
||||
|
||||
if issue.valid?
|
||||
present issue, with: Entities::Issue
|
||||
else
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -13,14 +13,6 @@ module API
|
|||
end
|
||||
not_found!
|
||||
end
|
||||
|
||||
def not_fork?(target_project_id, user_project)
|
||||
target_project_id.nil? || target_project_id == user_project.id.to_s
|
||||
end
|
||||
|
||||
def target_matches_fork(target_project_id,user_project)
|
||||
user_project.forked? && user_project.forked_from_project.id.to_s == target_project_id
|
||||
end
|
||||
end
|
||||
|
||||
# List merge requests
|
||||
|
@ -70,29 +62,15 @@ module API
|
|||
# POST /projects/:id/merge_requests
|
||||
#
|
||||
post ":id/merge_requests" do
|
||||
set_current_user_for_thread do
|
||||
authorize! :write_merge_request, user_project
|
||||
required_attributes! [:source_branch, :target_branch, :title]
|
||||
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description]
|
||||
merge_request = user_project.merge_requests.new(attrs)
|
||||
merge_request.author = current_user
|
||||
merge_request.source_project = user_project
|
||||
target_project_id = attrs[:target_project_id]
|
||||
if not_fork?(target_project_id, user_project)
|
||||
merge_request.target_project = user_project
|
||||
else
|
||||
if target_matches_fork(target_project_id,user_project)
|
||||
merge_request.target_project = Project.find_by(id: attrs[:target_project_id])
|
||||
else
|
||||
render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400)
|
||||
end
|
||||
end
|
||||
authorize! :write_merge_request, user_project
|
||||
required_attributes! [:source_branch, :target_branch, :title]
|
||||
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description]
|
||||
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, attrs).execute
|
||||
|
||||
if merge_request.save
|
||||
present merge_request, with: Entities::MergeRequest
|
||||
else
|
||||
handle_merge_request_errors! merge_request.errors
|
||||
end
|
||||
if merge_request.valid?
|
||||
present merge_request, with: Entities::MergeRequest
|
||||
else
|
||||
handle_merge_request_errors! merge_request.errors
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -111,17 +89,15 @@ module API
|
|||
# PUT /projects/:id/merge_request/:merge_request_id
|
||||
#
|
||||
put ":id/merge_request/:merge_request_id" do
|
||||
set_current_user_for_thread do
|
||||
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description]
|
||||
merge_request = user_project.merge_requests.find(params[:merge_request_id])
|
||||
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description]
|
||||
merge_request = user_project.merge_requests.find(params[:merge_request_id])
|
||||
authorize! :modify_merge_request, merge_request
|
||||
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request)
|
||||
|
||||
authorize! :modify_merge_request, merge_request
|
||||
|
||||
if merge_request.update_attributes attrs
|
||||
present merge_request, with: Entities::MergeRequest
|
||||
else
|
||||
handle_merge_request_errors! merge_request.errors
|
||||
end
|
||||
if merge_request.valid?
|
||||
present merge_request, with: Entities::MergeRequest
|
||||
else
|
||||
handle_merge_request_errors! merge_request.errors
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ module Gitlab
|
|||
|
||||
attr_reader :params, :project, :git_cmd, :user
|
||||
|
||||
def allowed?(actor, cmd, project, ref = nil, oldrev = nil, newrev = nil)
|
||||
def allowed?(actor, cmd, project, ref = nil, oldrev = nil, newrev = nil, forced_push = false)
|
||||
case cmd
|
||||
when *DOWNLOAD_COMMANDS
|
||||
if actor.is_a? User
|
||||
|
@ -19,12 +19,12 @@ module Gitlab
|
|||
end
|
||||
when *PUSH_COMMANDS
|
||||
if actor.is_a? User
|
||||
push_allowed?(actor, project, ref, oldrev, newrev)
|
||||
push_allowed?(actor, project, ref, oldrev, newrev, forced_push)
|
||||
elsif actor.is_a? DeployKey
|
||||
# Deploy key not allowed to push
|
||||
return false
|
||||
elsif actor.is_a? Key
|
||||
push_allowed?(actor.user, project, ref, oldrev, newrev)
|
||||
push_allowed?(actor.user, project, ref, oldrev, newrev, forced_push)
|
||||
else
|
||||
raise 'Wrong actor'
|
||||
end
|
||||
|
@ -41,13 +41,17 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def push_allowed?(user, project, ref, oldrev, newrev)
|
||||
def push_allowed?(user, project, ref, oldrev, newrev, forced_push)
|
||||
if user && user_allowed?(user)
|
||||
action = if project.protected_branch?(ref)
|
||||
:push_code_to_protected_branches
|
||||
else
|
||||
:push_code
|
||||
end
|
||||
if forced_push.to_s == 'true'
|
||||
:force_push_code_to_protected_branches
|
||||
else
|
||||
:push_code_to_protected_branches
|
||||
end
|
||||
else
|
||||
:push_code
|
||||
end
|
||||
user.can?(action, project)
|
||||
else
|
||||
false
|
||||
|
|
|
@ -342,6 +342,7 @@ namespace :gitlab do
|
|||
check_repo_base_is_not_symlink
|
||||
check_repo_base_user_and_group
|
||||
check_repo_base_permissions
|
||||
check_satellites_permissions
|
||||
check_update_hook_is_up_to_date
|
||||
check_repos_update_hooks_is_link
|
||||
check_gitlab_shell_self_test
|
||||
|
@ -443,6 +444,29 @@ namespace :gitlab do
|
|||
end
|
||||
end
|
||||
|
||||
def check_satellites_permissions
|
||||
print "Satellites access is drwxr-x---? ... "
|
||||
|
||||
satellites_path = Gitlab.config.satellites.path
|
||||
unless File.exists?(satellites_path)
|
||||
puts "can't check because of previous errors".magenta
|
||||
return
|
||||
end
|
||||
|
||||
if File.stat(satellites_path).mode.to_s(8).ends_with?("0750")
|
||||
puts "yes".green
|
||||
else
|
||||
puts "no".red
|
||||
try_fixing_it(
|
||||
"sudo chmod u+rwx,g+rx,o-rwx #{satellites_path}",
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "GitLab"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_repo_base_user_and_group
|
||||
gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user
|
||||
gitlab_shell_owner_group = Gitlab.config.gitlab_shell.owner_group
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace :gitlab do
|
|||
puts "Gem Version:\t#{gem_version || "unknown".red}"
|
||||
puts "Bundler Version:#{bunder_version || "unknown".red}"
|
||||
puts "Rake Version:\t#{rake_version || "unknown".red}"
|
||||
puts "Sidekiq Version:#{Sidekiq::VERSION}"
|
||||
|
||||
|
||||
# check database adapter
|
||||
|
|
|
@ -37,7 +37,7 @@ function start_no_deamonize
|
|||
|
||||
function start_sidekiq
|
||||
{
|
||||
bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
|
||||
bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
|
||||
}
|
||||
|
||||
function load_ok
|
||||
|
|
|
@ -5,10 +5,10 @@ describe MergeRequestsFinder do
|
|||
let(:user2) { create :user }
|
||||
|
||||
let(:project1) { create(:project) }
|
||||
let(:project2) { create(:project) }
|
||||
let(:project2) { create(:project, forked_from_project: project1) }
|
||||
|
||||
let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2) }
|
||||
let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
|
||||
let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
|
||||
let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1, state: 'closed') }
|
||||
let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) }
|
||||
|
||||
before do
|
||||
|
@ -21,7 +21,7 @@ describe MergeRequestsFinder do
|
|||
it 'should filter by scope' do
|
||||
params = { scope: 'authored', state: 'opened' }
|
||||
merge_requests = MergeRequestsFinder.new.execute(user, params)
|
||||
merge_requests.size.should == 3
|
||||
merge_requests.size.should == 2
|
||||
end
|
||||
|
||||
it 'should filter by project' do
|
||||
|
|
|
@ -13,7 +13,7 @@ describe 'Gitlab::Satellite::MergeAction' do
|
|||
end
|
||||
|
||||
let(:project) { create(:project, namespace: create(:group)) }
|
||||
let(:fork_project) { create(:project, namespace: create(:group)) }
|
||||
let(:fork_project) { create(:project, namespace: create(:group), forked_from_project: project) }
|
||||
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let(:merge_request_fork) { create(:merge_request, source_project: fork_project, target_project: project) }
|
||||
|
||||
|
|
|
@ -68,4 +68,18 @@ describe Key do
|
|||
build(:invalid_key).should_not be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'callbacks' do
|
||||
it 'should add new key to authorized_file' do
|
||||
@key = build(:personal_key, id: 7)
|
||||
GitlabShellWorker.should_receive(:perform_async).with(:add_key, @key.shell_id, @key.key)
|
||||
@key.save
|
||||
end
|
||||
|
||||
it 'should remove key from authorized_file' do
|
||||
@key = create(:personal_key)
|
||||
GitlabShellWorker.should_receive(:perform_async).with(:remove_key, @key.shell_id, @key.key)
|
||||
@key.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -209,7 +209,7 @@ describe Note do
|
|||
let(:project) { create(:project) }
|
||||
let(:author) { create(:user) }
|
||||
let(:issue) { create(:issue, project: project) }
|
||||
let(:mergereq) { create(:merge_request, target_project: project) }
|
||||
let(:mergereq) { create(:merge_request, :simple, target_project: project, source_project: project) }
|
||||
let(:commit) { project.repository.commit }
|
||||
|
||||
# Test all of {issue, merge request, commit} in both the referenced and referencing
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe EmailObserver do
|
||||
let(:email) { create(:email) }
|
||||
|
||||
before { subject.stub(notification: double('NotificationService').as_null_object) }
|
||||
|
||||
subject { EmailObserver.instance }
|
||||
|
||||
describe '#after_create' do
|
||||
it 'trigger notification to send emails' do
|
||||
subject.should_receive(:notification)
|
||||
|
||||
subject.after_create(email)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,99 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe IssueObserver do
|
||||
let(:some_user) { create :user }
|
||||
let(:assignee) { create :user }
|
||||
let(:author) { create :user }
|
||||
let(:mock_issue) { create(:issue, assignee: assignee, author: author) }
|
||||
|
||||
|
||||
before { subject.stub(:current_user).and_return(some_user) }
|
||||
before { subject.stub(:current_commit).and_return(nil) }
|
||||
before { subject.stub(notification: double('NotificationService').as_null_object) }
|
||||
before { mock_issue.project.stub_chain(:repository, :commit).and_return(nil) }
|
||||
|
||||
subject { IssueObserver.instance }
|
||||
|
||||
describe '#after_create' do
|
||||
it 'trigger notification to send emails' do
|
||||
subject.should_receive(:notification)
|
||||
|
||||
subject.after_create(mock_issue)
|
||||
end
|
||||
|
||||
it 'should create cross-reference notes' do
|
||||
other_issue = create(:issue)
|
||||
mock_issue.stub(references: [other_issue])
|
||||
|
||||
Note.should_receive(:create_cross_reference_note).with(other_issue, mock_issue,
|
||||
some_user, mock_issue.project)
|
||||
subject.after_create(mock_issue)
|
||||
end
|
||||
end
|
||||
|
||||
context '#after_close' do
|
||||
context 'a status "closed"' do
|
||||
before { mock_issue.stub(state: 'closed') }
|
||||
|
||||
it 'note is created if the issue is being closed' do
|
||||
Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'closed', nil)
|
||||
|
||||
subject.after_close(mock_issue, nil)
|
||||
end
|
||||
|
||||
it 'trigger notification to send emails' do
|
||||
subject.notification.should_receive(:close_issue).with(mock_issue, some_user)
|
||||
subject.after_close(mock_issue, nil)
|
||||
end
|
||||
|
||||
it 'appends a mention to the closing commit if one is present' do
|
||||
commit = double('commit', gfm_reference: 'commit 123456')
|
||||
subject.stub(current_commit: commit)
|
||||
|
||||
Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'closed', commit)
|
||||
|
||||
subject.after_close(mock_issue, nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'a status "reopened"' do
|
||||
before { mock_issue.stub(state: 'reopened') }
|
||||
|
||||
it 'note is created if the issue is being reopened' do
|
||||
Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'reopened', nil)
|
||||
|
||||
subject.after_reopen(mock_issue, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '#after_update' do
|
||||
before(:each) do
|
||||
mock_issue.stub(:is_being_reassigned?).and_return(false)
|
||||
end
|
||||
|
||||
context 'notification' do
|
||||
it 'triggered if the issue is being reassigned' do
|
||||
mock_issue.should_receive(:is_being_reassigned?).and_return(true)
|
||||
subject.should_receive(:notification)
|
||||
|
||||
subject.after_update(mock_issue)
|
||||
end
|
||||
|
||||
it 'is not triggered if the issue is not being reassigned' do
|
||||
mock_issue.should_receive(:is_being_reassigned?).and_return(false)
|
||||
subject.should_not_receive(:notification)
|
||||
|
||||
subject.after_update(mock_issue)
|
||||
end
|
||||
end
|
||||
|
||||
context 'cross-references' do
|
||||
it 'notices added references' do
|
||||
mock_issue.should_receive(:notice_added_references)
|
||||
|
||||
subject.after_update(mock_issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe KeyObserver do
|
||||
before do
|
||||
@key = create(:personal_key)
|
||||
|
||||
@observer = KeyObserver.instance
|
||||
end
|
||||
|
||||
context :after_create do
|
||||
it do
|
||||
GitlabShellWorker.should_receive(:perform_async).with(:add_key, @key.shell_id, @key.key)
|
||||
@observer.after_create(@key)
|
||||
end
|
||||
end
|
||||
|
||||
context :after_destroy do
|
||||
it do
|
||||
GitlabShellWorker.should_receive(:perform_async).with(:remove_key, @key.shell_id, @key.key)
|
||||
@observer.after_destroy(@key)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,131 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe MergeRequestObserver do
|
||||
let(:some_user) { create :user }
|
||||
let(:assignee) { create :user }
|
||||
let(:author) { create :user }
|
||||
let(:project) { create :project }
|
||||
let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author).as_null_object }
|
||||
let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author, source_project: project) }
|
||||
let(:unassigned_mr) { create(:merge_request, author: author, source_project: project) }
|
||||
let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author, source_project: project) }
|
||||
let(:closed_unassigned_mr) { create(:closed_merge_request, author: author, source_project: project) }
|
||||
|
||||
before { subject.stub(:current_user).and_return(some_user) }
|
||||
before { subject.stub(notification: double('NotificationService').as_null_object) }
|
||||
before { mr_mock.stub(:author_id) }
|
||||
before { mr_mock.stub(:source_project) }
|
||||
before { mr_mock.stub(:source_project) }
|
||||
before { mr_mock.stub(:project) }
|
||||
before { mr_mock.stub(:create_cross_references!).and_return(true) }
|
||||
before { Repository.any_instance.stub(commit: nil) }
|
||||
|
||||
before(:each) { enable_observers }
|
||||
after(:each) { disable_observers }
|
||||
|
||||
subject { MergeRequestObserver.instance }
|
||||
|
||||
describe '#after_create' do
|
||||
it 'trigger notification service' do
|
||||
subject.should_receive(:notification)
|
||||
subject.after_create(mr_mock)
|
||||
end
|
||||
|
||||
it 'creates cross-reference notes' do
|
||||
project = create :project
|
||||
mr_mock.stub(title: "this mr references !#{assigned_mr.id}", project: project)
|
||||
mr_mock.should_receive(:create_cross_references!).with(project, some_user)
|
||||
|
||||
subject.after_create(mr_mock)
|
||||
end
|
||||
end
|
||||
|
||||
context '#after_update' do
|
||||
before(:each) do
|
||||
mr_mock.stub(:is_being_reassigned?).and_return(false)
|
||||
mr_mock.stub(:notice_added_references)
|
||||
end
|
||||
|
||||
it 'is called when a merge request is changed' do
|
||||
changed = create(:merge_request, source_project: project)
|
||||
subject.should_receive(:after_update)
|
||||
|
||||
MergeRequest.observers.enable :merge_request_observer do
|
||||
changed.title = 'I changed'
|
||||
changed.save
|
||||
end
|
||||
end
|
||||
|
||||
it 'checks for new references' do
|
||||
mr_mock.should_receive(:notice_added_references)
|
||||
|
||||
subject.after_update(mr_mock)
|
||||
end
|
||||
|
||||
context 'a notification' do
|
||||
it 'is sent if the merge request is being reassigned' do
|
||||
mr_mock.should_receive(:is_being_reassigned?).and_return(true)
|
||||
subject.should_receive(:notification)
|
||||
|
||||
subject.after_update(mr_mock)
|
||||
end
|
||||
|
||||
it 'is not sent if the merge request is not being reassigned' do
|
||||
mr_mock.should_receive(:is_being_reassigned?).and_return(false)
|
||||
subject.should_not_receive(:notification)
|
||||
|
||||
subject.after_update(mr_mock)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '#after_close' do
|
||||
context 'a status "closed"' do
|
||||
it 'note is created if the merge request is being closed' do
|
||||
Note.should_receive(:create_status_change_note).with(assigned_mr, assigned_mr.source_project, some_user, 'closed', nil)
|
||||
|
||||
assigned_mr.close
|
||||
end
|
||||
|
||||
it 'notification is delivered only to author if the merge request is being closed' do
|
||||
Note.should_receive(:create_status_change_note).with(unassigned_mr, unassigned_mr.source_project, some_user, 'closed', nil)
|
||||
|
||||
unassigned_mr.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '#after_reopen' do
|
||||
context 'a status "reopened"' do
|
||||
it 'note is created if the merge request is being reopened' do
|
||||
Note.should_receive(:create_status_change_note).with(closed_assigned_mr, closed_assigned_mr.source_project, some_user, 'reopened', nil)
|
||||
|
||||
closed_assigned_mr.reopen
|
||||
end
|
||||
|
||||
it 'notification is delivered only to author if the merge request is being reopened' do
|
||||
Note.should_receive(:create_status_change_note).with(closed_unassigned_mr, closed_unassigned_mr.source_project, some_user, 'reopened', nil)
|
||||
|
||||
closed_unassigned_mr.reopen
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Merge Request created" do
|
||||
def self.it_should_be_valid_event
|
||||
it { @event.should_not be_nil }
|
||||
it { @event.should_not be_nil }
|
||||
it { @event.project.should == project }
|
||||
it { @event.project.should == project }
|
||||
end
|
||||
|
||||
before do
|
||||
@merge_request = create(:merge_request, source_project: project, target_project: project)
|
||||
@event = Event.last
|
||||
end
|
||||
|
||||
it_should_be_valid_event
|
||||
it { @event.action.should == Event::CREATED }
|
||||
it { @event.target.should == @merge_request }
|
||||
end
|
||||
end
|
|
@ -21,7 +21,7 @@ describe UsersProjectObserver do
|
|||
|
||||
it "should send email to user" do
|
||||
subject.should_receive(:notification)
|
||||
@users_project.update_attribute(:project_access, UsersProject::MASTER)
|
||||
@users_project.update_attribute(:project_access, UsersProject::OWNER)
|
||||
end
|
||||
|
||||
it "should not called after UsersProject destroyed" do
|
||||
|
|
|
@ -6,7 +6,7 @@ describe API::API do
|
|||
after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
|
||||
let(:user) { create(:user) }
|
||||
let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
|
||||
let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
|
||||
let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
|
||||
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
|
||||
before {
|
||||
project.team << [user, :reporters]
|
||||
|
@ -79,16 +79,12 @@ describe API::API do
|
|||
end
|
||||
|
||||
context 'forked projects' do
|
||||
let!(:user2) {create(:user)}
|
||||
let!(:forked_project_link) { build(:forked_project_link) }
|
||||
let!(:fork_project) { create(:project, forked_project_link: forked_project_link, namespace: user2.namespace, creator_id: user2.id) }
|
||||
let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) }
|
||||
let!(:user2) { create(:user) }
|
||||
let!(:fork_project) { create(:project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
|
||||
let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) }
|
||||
|
||||
before :each do |each|
|
||||
fork_project.team << [user2, :reporters]
|
||||
forked_project_link.forked_from_project = project
|
||||
forked_project_link.forked_to_project = fork_project
|
||||
forked_project_link.save!
|
||||
end
|
||||
|
||||
it "should return merge_request" do
|
||||
|
@ -127,16 +123,16 @@ describe API::API do
|
|||
response.status.should == 400
|
||||
end
|
||||
|
||||
it "should return 400 when target_branch is specified and not a forked project" do
|
||||
it "should return 404 when target_branch is specified and not a forked project" do
|
||||
post api("/projects/#{project.id}/merge_requests", user),
|
||||
title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user, target_project_id: fork_project.id
|
||||
response.status.should == 400
|
||||
response.status.should == 404
|
||||
end
|
||||
|
||||
it "should return 400 when target_branch is specified and for a different fork" do
|
||||
it "should return 404 when target_branch is specified and for a different fork" do
|
||||
post api("/projects/#{fork_project.id}/merge_requests", user2),
|
||||
title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: unrelated_project.id
|
||||
response.status.should == 400
|
||||
response.status.should == 404
|
||||
end
|
||||
|
||||
it "should return 201 when target_branch is specified and for the same project" do
|
||||
|
|
|
@ -170,16 +170,10 @@ describe GitPushService do
|
|||
Issue.find(issue.id).should be_closed
|
||||
end
|
||||
|
||||
it "passes the closing commit as a thread-local" do
|
||||
service.execute(project, user, @oldrev, @newrev, @ref)
|
||||
|
||||
Thread.current[:current_commit].should == closing_commit
|
||||
end
|
||||
|
||||
it "doesn't create cross-reference notes for a closing reference" do
|
||||
expect {
|
||||
service.execute(project, user, @oldrev, @newrev, @ref)
|
||||
}.not_to change { Note.where(project_id: project.id, system: true).count }
|
||||
}.not_to change { Note.where(project_id: project.id, system: true, commit_id: closing_commit.id).count }
|
||||
end
|
||||
|
||||
it "doesn't close issues when pushed to non-default branches" do
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Issues::CloseService do
|
||||
let(:project) { create(:empty_project) }
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:issue) { create(:issue, assignee: user2) }
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
project.team << [user2, :developer]
|
||||
end
|
||||
|
||||
describe :execute do
|
||||
context "valid params" do
|
||||
before do
|
||||
@issue = Issues::CloseService.new(project, user, {}).execute(issue)
|
||||
end
|
||||
|
||||
it { @issue.should be_valid }
|
||||
it { @issue.should be_closed }
|
||||
|
||||
it 'should send email to user2 about assign of new issue' do
|
||||
email = ActionMailer::Base.deliveries.last
|
||||
email.to.first.should == user2.email
|
||||
email.subject.should include(issue.title)
|
||||
end
|
||||
|
||||
it 'should create system note about issue reassign' do
|
||||
note = @issue.notes.last
|
||||
note.note.should include "Status changed to closed"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Issues::CreateService do
|
||||
let(:project) { create(:empty_project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
describe :execute do
|
||||
context "valid params" do
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
opts = {
|
||||
title: 'Awesome issue',
|
||||
description: 'please fix'
|
||||
}
|
||||
|
||||
@issue = Issues::CreateService.new(project, user, opts).execute
|
||||
end
|
||||
|
||||
it { @issue.should be_valid }
|
||||
it { @issue.title.should == 'Awesome issue' }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Issues::UpdateService do
|
||||
let(:project) { create(:empty_project) }
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:issue) { create(:issue) }
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
project.team << [user2, :developer]
|
||||
end
|
||||
|
||||
describe :execute do
|
||||
context "valid params" do
|
||||
before do
|
||||
opts = {
|
||||
title: 'New title',
|
||||
description: 'Also please fix',
|
||||
assignee_id: user2.id,
|
||||
state_event: 'close'
|
||||
}
|
||||
|
||||
@issue = Issues::UpdateService.new(project, user, opts).execute(issue)
|
||||
end
|
||||
|
||||
it { @issue.should be_valid }
|
||||
it { @issue.title.should == 'New title' }
|
||||
it { @issue.assignee.should == user2 }
|
||||
it { @issue.should be_closed }
|
||||
|
||||
it 'should send email to user2 about assign of new issue' do
|
||||
email = ActionMailer::Base.deliveries.last
|
||||
email.to.first.should == user2.email
|
||||
email.subject.should include(issue.title)
|
||||
end
|
||||
|
||||
it 'should create system note about issue reassign' do
|
||||
note = @issue.notes.last
|
||||
note.note.should include "Reassigned to \@#{user2.username}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe MergeRequests::CloseService do
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:merge_request) { create(:merge_request, assignee: user2) }
|
||||
let(:project) { merge_request.project }
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
project.team << [user2, :developer]
|
||||
end
|
||||
|
||||
describe :execute do
|
||||
context "valid params" do
|
||||
before do
|
||||
@merge_request = MergeRequests::CloseService.new(project, user, {}).execute(merge_request)
|
||||
end
|
||||
|
||||
it { @merge_request.should be_valid }
|
||||
it { @merge_request.should be_closed }
|
||||
|
||||
it 'should send email to user2 about assign of new merge_request' do
|
||||
email = ActionMailer::Base.deliveries.last
|
||||
email.to.first.should == user2.email
|
||||
email.subject.should include(merge_request.title)
|
||||
end
|
||||
|
||||
it 'should create system note about merge_request reassign' do
|
||||
note = @merge_request.notes.last
|
||||
note.note.should include "Status changed to closed"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe MergeRequests::CreateService do
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
describe :execute do
|
||||
context "valid params" do
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
opts = {
|
||||
title: 'Awesome merge_request',
|
||||
description: 'please fix',
|
||||
source_branch: 'stable',
|
||||
target_branch: 'master'
|
||||
}
|
||||
|
||||
@merge_request = MergeRequests::CreateService.new(project, user, opts).execute
|
||||
end
|
||||
|
||||
it { @merge_request.should be_valid }
|
||||
it { @merge_request.title.should == 'Awesome merge_request' }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe MergeRequests::UpdateService do
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:merge_request) { create(:merge_request, :simple) }
|
||||
let(:project) { merge_request.project }
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
project.team << [user2, :developer]
|
||||
end
|
||||
|
||||
describe :execute do
|
||||
context "valid params" do
|
||||
before do
|
||||
opts = {
|
||||
title: 'New title',
|
||||
description: 'Also please fix',
|
||||
assignee_id: user2.id,
|
||||
state_event: 'close'
|
||||
}
|
||||
|
||||
@merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
|
||||
end
|
||||
|
||||
it { @merge_request.should be_valid }
|
||||
it { @merge_request.title.should == 'New title' }
|
||||
it { @merge_request.assignee.should == user2 }
|
||||
it { @merge_request.should be_closed }
|
||||
|
||||
it 'should send email to user2 about assign of new merge_request' do
|
||||
email = ActionMailer::Base.deliveries.last
|
||||
email.to.first.should == user2.email
|
||||
email.subject.should include(merge_request.title)
|
||||
end
|
||||
|
||||
it 'should create system note about merge_request reassign' do
|
||||
note = @merge_request.notes.last
|
||||
note.note.should include "Reassigned to \@#{user2.username}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,7 +5,7 @@ describe NotificationService do
|
|||
|
||||
describe 'Keys' do
|
||||
describe :new_key do
|
||||
let(:key) { create(:personal_key) }
|
||||
let!(:key) { create(:personal_key) }
|
||||
|
||||
it { notification.new_key(key).should be_true }
|
||||
|
||||
|
@ -18,7 +18,7 @@ describe NotificationService do
|
|||
|
||||
describe 'Email' do
|
||||
describe :new_email do
|
||||
let(:email) { create(:email) }
|
||||
let!(:email) { create(:email) }
|
||||
|
||||
it { notification.new_email(email).should be_true }
|
||||
|
||||
|
@ -57,15 +57,42 @@ describe NotificationService do
|
|||
Notify.should_not_receive(:note_issue_email)
|
||||
notification.new_note(mentioned_note)
|
||||
end
|
||||
end
|
||||
|
||||
def should_email(user_id)
|
||||
Notify.should_receive(:note_issue_email).with(user_id, note.id)
|
||||
describe 'new note on issue in project that belongs to a group' do
|
||||
let(:group) { create(:group) }
|
||||
|
||||
before do
|
||||
note.project.namespace_id = group.id
|
||||
note.project.group.add_user(@u_watcher, UsersGroup::MASTER)
|
||||
note.project.save
|
||||
user_project = note.project.users_projects.find_by_user_id(@u_watcher.id)
|
||||
user_project.notification_level = Notification::N_PARTICIPATING
|
||||
user_project.save
|
||||
user_group = note.project.group.users_groups.find_by_user_id(@u_watcher.id)
|
||||
user_group.notification_level = Notification::N_GLOBAL
|
||||
user_group.save
|
||||
end
|
||||
|
||||
def should_not_email(user_id)
|
||||
Notify.should_not_receive(:note_issue_email).with(user_id, note.id)
|
||||
it do
|
||||
should_email(note.noteable.author_id)
|
||||
should_email(note.noteable.assignee_id)
|
||||
should_email(@u_mentioned.id)
|
||||
should_not_email(@u_watcher.id)
|
||||
should_not_email(note.author_id)
|
||||
should_not_email(@u_participating.id)
|
||||
should_not_email(@u_disabled.id)
|
||||
notification.new_note(note)
|
||||
end
|
||||
end
|
||||
|
||||
def should_email(user_id)
|
||||
Notify.should_receive(:note_issue_email).with(user_id, note.id)
|
||||
end
|
||||
|
||||
def should_not_email(user_id)
|
||||
Notify.should_not_receive(:note_issue_email).with(user_id, note.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'commit note' do
|
||||
|
|
Loading…
Reference in New Issue