Commit 4d1945028ebee62ff04896a8f1b9aec08f60df86

Authored by Timm Drevensek
2 parents cb659e4c 7611ce72

Merge branch 'master' into request/relative_submodules

Showing 79 changed files with 831 additions and 857 deletions   Show diff stats
@@ -6,6 +6,13 @@ v 6.8.0 @@ -6,6 +6,13 @@ v 6.8.0
6 - Drop all tables before restoring a Postgres backup 6 - Drop all tables before restoring a Postgres backup
7 - Make the repository downloads path configurable 7 - Make the repository downloads path configurable
8 - Create branches via API (sponsored by O'Reilly Media) 8 - Create branches via API (sponsored by O'Reilly Media)
  9 + - Changed permission of gitlab-satellites directory not to be world accessible
  10 + - Protected branch does not allow force push
  11 +
  12 +v 6.7.3
  13 + - Fix the merge notification email not being sent (Pierre de La Morinerie)
  14 + - Drop all tables before restoring a Postgres backup
  15 + - Remove yanked modernizr gem
9 16
10 v 6.7.2 17 v 6.7.2
11 - Fix upgrader script 18 - Fix upgrader script
CONTRIBUTING.md
@@ -29,7 +29,9 @@ If something is wrong but it is not a regression compared to older versions of G @@ -29,7 +29,9 @@ If something is wrong but it is not a regression compared to older versions of G
29 When submitting an issue please conform to the issue submission guidelines listed below. 29 When submitting an issue please conform to the issue submission guidelines listed below.
30 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. 30 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.
31 31
32 -Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose. 32 +Do not use the issue tracker for feature requests.
  33 +We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose.
  34 +Please keep feature requests as small and simple as possible, complex ones might be edited to make them small and simple.
33 35
34 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. 36 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.
35 37
@@ -12,8 +12,6 @@ gem "rails", "~> 4.0.0" @@ -12,8 +12,6 @@ gem "rails", "~> 4.0.0"
12 12
13 gem "protected_attributes" 13 gem "protected_attributes"
14 gem 'rails-observers' 14 gem 'rails-observers'
15 -gem 'actionpack-page_caching'  
16 -gem 'actionpack-action_caching'  
17 15
18 # Default values for AR models 16 # Default values for AR models
19 gem "default_value_for", "~> 3.0.0" 17 gem "default_value_for", "~> 3.0.0"
@@ -102,7 +100,7 @@ gem "acts-as-taggable-on" @@ -102,7 +100,7 @@ gem "acts-as-taggable-on"
102 # Background jobs 100 # Background jobs
103 gem 'slim' 101 gem 'slim'
104 gem 'sinatra', require: nil 102 gem 'sinatra', require: nil
105 -gem 'sidekiq' 103 +gem 'sidekiq', '2.17.0'
106 104
107 # HTTP requests 105 # HTTP requests
108 gem "httparty" 106 gem "httparty"
@@ -161,7 +159,6 @@ gem 'select2-rails' @@ -161,7 +159,6 @@ gem 'select2-rails'
161 gem 'jquery-atwho-rails', "~> 0.3.3" 159 gem 'jquery-atwho-rails', "~> 0.3.3"
162 gem "jquery-rails", "2.1.3" 160 gem "jquery-rails", "2.1.3"
163 gem "jquery-ui-rails", "2.0.2" 161 gem "jquery-ui-rails", "2.0.2"
164 -gem "modernizr", "2.6.2"  
165 gem "raphael-rails", "~> 2.1.2" 162 gem "raphael-rails", "~> 2.1.2"
166 gem 'bootstrap-sass', '~> 3.0' 163 gem 'bootstrap-sass', '~> 3.0'
167 gem "font-awesome-rails", '~> 3.2' 164 gem "font-awesome-rails", '~> 3.2'
@@ -27,10 +27,6 @@ GEM @@ -27,10 +27,6 @@ GEM
27 erubis (~> 2.7.0) 27 erubis (~> 2.7.0)
28 rack (~> 1.5.2) 28 rack (~> 1.5.2)
29 rack-test (~> 0.6.2) 29 rack-test (~> 0.6.2)
30 - actionpack-action_caching (1.1.0)  
31 - actionpack (>= 4.0.0, < 5.0)  
32 - actionpack-page_caching (1.0.2)  
33 - actionpack (>= 4.0.0, < 5)  
34 activemodel (4.0.3) 30 activemodel (4.0.3)
35 activesupport (= 4.0.3) 31 activesupport (= 4.0.3)
36 builder (~> 3.1.0) 32 builder (~> 3.1.0)
@@ -289,8 +285,6 @@ GEM @@ -289,8 +285,6 @@ GEM
289 method_source (0.8.2) 285 method_source (0.8.2)
290 mime-types (1.25.1) 286 mime-types (1.25.1)
291 minitest (4.7.5) 287 minitest (4.7.5)
292 - modernizr (2.6.2)  
293 - sprockets (~> 2.0)  
294 multi_json (1.8.4) 288 multi_json (1.8.4)
295 multi_xml (0.5.5) 289 multi_xml (0.5.5)
296 multipart-post (1.2.0) 290 multipart-post (1.2.0)
@@ -567,8 +561,6 @@ PLATFORMS @@ -567,8 +561,6 @@ PLATFORMS
567 561
568 DEPENDENCIES 562 DEPENDENCIES
569 ace-rails-ap 563 ace-rails-ap
570 - actionpack-action_caching  
571 - actionpack-page_caching  
572 acts-as-taggable-on 564 acts-as-taggable-on
573 annotate (~> 2.6.0.beta2) 565 annotate (~> 2.6.0.beta2)
574 asciidoctor 566 asciidoctor
@@ -622,7 +614,6 @@ DEPENDENCIES @@ -622,7 +614,6 @@ DEPENDENCIES
622 launchy 614 launchy
623 letter_opener 615 letter_opener
624 minitest (~> 4.7.0) 616 minitest (~> 4.7.0)
625 - modernizr (= 2.6.2)  
626 mysql2 617 mysql2
627 nprogress-rails 618 nprogress-rails
628 omniauth (~> 1.1.3) 619 omniauth (~> 1.1.3)
@@ -653,7 +644,7 @@ DEPENDENCIES @@ -653,7 +644,7 @@ DEPENDENCIES
653 select2-rails 644 select2-rails
654 settingslogic 645 settingslogic
655 shoulda-matchers (~> 2.1.0) 646 shoulda-matchers (~> 2.1.0)
656 - sidekiq 647 + sidekiq (= 2.17.0)
657 simplecov 648 simplecov
658 sinatra 649 sinatra
659 six 650 six
app/assets/javascripts/application.js.coffee
@@ -18,7 +18,6 @@ @@ -18,7 +18,6 @@
18 #= require turbolinks 18 #= require turbolinks
19 #= require jquery.turbolinks 19 #= require jquery.turbolinks
20 #= require bootstrap 20 #= require bootstrap
21 -#= require modernizr  
22 #= require select2 21 #= require select2
23 #= require raphael 22 #= require raphael
24 #= require g.raphael-min 23 #= require g.raphael-min
app/assets/javascripts/commit/file.js.coffee
1 class CommitFile 1 class CommitFile
2 - 2 +
3 constructor: (file) -> 3 constructor: (file) ->
4 if $('.image', file).length 4 if $('.image', file).length
5 new ImageFile(file) 5 new ImageFile(file)
6 -  
7 -this.CommitFile = CommitFile  
8 \ No newline at end of file 6 \ No newline at end of file
  7 +
  8 +@CommitFile = CommitFile
app/assets/javascripts/commit/image-file.js.coffee
@@ -125,4 +125,4 @@ class ImageFile @@ -125,4 +125,4 @@ class ImageFile
125 img.on 'load', => 125 img.on 'load', =>
126 callback.call(this, domImg.naturalWidth, domImg.naturalHeight) 126 callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
127 127
128 -this.ImageFile = ImageFile  
129 \ No newline at end of file 128 \ No newline at end of file
  129 +@ImageFile = ImageFile
app/assets/stylesheets/application.scss
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 * This is a manifest file that'll automatically include all the stylesheets available in this directory 2 * This is a manifest file that'll automatically include all the stylesheets available in this directory
3 * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at 3 * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4 * the top of the compiled file, but it's generally better to create a new file per style scope. 4 * the top of the compiled file, but it's generally better to create a new file per style scope.
5 - *= require jquery.ui.gitlab 5 + *= require jquery.ui.datepicker
6 *= require jquery.atwho 6 *= require jquery.atwho
7 *= require select2 7 *= require select2
8 *= require highlightjs.min 8 *= require highlightjs.min
@@ -43,6 +43,7 @@ @@ -43,6 +43,7 @@
43 @import "generic/forms.scss"; 43 @import "generic/forms.scss";
44 @import "generic/selects.scss"; 44 @import "generic/selects.scss";
45 @import "generic/highlight.scss"; 45 @import "generic/highlight.scss";
  46 +@import "generic/jquery.scss";
46 47
47 /** 48 /**
48 * Page specific styles (issues, projects etc): 49 * Page specific styles (issues, projects etc):
app/assets/stylesheets/generic/jquery.scss 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +.ui-widget {
  2 + font-family: $regular_font;
  3 + font-size: $font-size-base;
  4 +
  5 + &.ui-datepicker-inline {
  6 + border: 1px solid #DDD;
  7 + padding: 10px;
  8 + width: 270px;
  9 +
  10 + .ui-datepicker-header {
  11 + background: #EEE;
  12 + border-color: #DDD;
  13 + }
  14 +
  15 + .ui-datepicker-calendar td a {
  16 + padding: 5px;
  17 + text-align: center;
  18 + }
  19 + }
  20 +}
app/assets/stylesheets/jquery.ui.gitlab.css
@@ -1,257 +0,0 @@ @@ -1,257 +0,0 @@
1 -/* Interaction Cues  
2 -----------------------------------*/  
3 -.ui-state-disabled { cursor: default !important; }  
4 -  
5 -  
6 -/* Icons  
7 -----------------------------------*/  
8 -  
9 -/* states and images */  
10 -.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }  
11 -  
12 -  
13 -/* Misc visuals  
14 -----------------------------------*/  
15 -  
16 -/* Overlays */  
17 -.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }  
18 -  
19 -  
20 -/*  
21 - * jQuery UI CSS Framework 1.8.7  
22 - *  
23 - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)  
24 - * Dual licensed under the MIT or GPL Version 2 licenses.  
25 - * http://jquery.org/license  
26 - *  
27 - * http://docs.jquery.com/UI/Theming/API  
28 - *  
29 - * To view and modify this theme, visit http://jqueryui.com/themeroller/?ctl=themeroller  
30 - */  
31 -  
32 -  
33 -/* Component containers  
34 -----------------------------------*/  
35 -.ui-widget { font-family: Arial,sans-serif; font-size: 1.1em; }  
36 -.ui-widget .ui-widget { font-size: 1em; }  
37 -.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Arial,sans-serif; font-size: 1em; }  
38 -.ui-widget-content { border: 1px solid #CCC; background: #ffffff; color: #4F4F4F; }  
39 -.ui-widget-content a { color: #4F4F4F; }  
40 -.ui-widget-header { border: 1px solid #B6B6B6; color: #4F4F4F; font-weight: bold; }  
41 -.ui-widget-header {  
42 - background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */  
43 - background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */  
44 - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */  
45 - background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */  
46 - background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */  
47 - background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */  
48 - background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */  
49 -}  
50 -.ui-widget-header a { color: #4F4F4F; }  
51 -  
52 -/* Interaction states  
53 -----------------------------------*/  
54 -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #B6B6B6; font-weight: normal; color: #4F4F4F; }  
55 -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {  
56 - background: #ededed url(bg_fallback.png) 0 0 repeat-x; /* Old browsers */  
57 - background: -moz-linear-gradient(top, #ededed 0%, #c4c4c4 100%); /* FF3.6+ */  
58 - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ededed), color-stop(100%,#c4c4c4)); /* Chrome,Safari4+ */  
59 - background: -webkit-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Chrome10+,Safari5.1+ */  
60 - background: -o-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* Opera11.10+ */  
61 - background: -ms-linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* IE10+ */  
62 - background: linear-gradient(top, #ededed 0%,#c4c4c4 100%); /* W3C */  
63 - -webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;  
64 - -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;  
65 - box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;  
66 -}  
67 -.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #4F4F4F; text-decoration: none; }  
68 -.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; }  
69 -.ui-state-hover a, .ui-state-hover a:hover { color: #313131; text-decoration: none; }  
70 -.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {  
71 - outline: none;  
72 - color: #1c4257; border: 1px solid #7096ab;  
73 - background: #ededed url(bg_fallback.png) 0 -50px repeat-x; /* Old browsers */  
74 - background: -moz-linear-gradient(top, #b9e0f5 0%, #92bdd6 100%); /* FF3.6+ */  
75 - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b9e0f5), color-stop(100%,#92bdd6)); /* Chrome,Safari4+ */  
76 - background: -webkit-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Chrome10+,Safari5.1+ */  
77 - background: -o-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* Opera11.10+ */  
78 - background: -ms-linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* IE10+ */  
79 - background: linear-gradient(top, #b9e0f5 0%,#92bdd6 100%); /* W3C */  
80 - -webkit-box-shadow: none;  
81 - -moz-box-shadow: none;  
82 - box-shadow: none;  
83 -}  
84 -.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #313131; text-decoration: none; }  
85 -.ui-widget :active { outline: none; }  
86 -  
87 -/* Interaction Cues  
88 -----------------------------------*/  
89 -.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; }  
90 -.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }  
91 -.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; }  
92 -.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }  
93 -.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }  
94 -.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }  
95 -.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }  
96 -.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }  
97 -  
98 -/* Icons  
99 -----------------------------------*/  
100 -  
101 -/* states and images */  
102 -.ui-icon { width: 16px; height: 16px; background-image: url(ui-icons_222222_256x240.png); }  
103 -.ui-widget-content .ui-icon {background-image: url(ui-icons_222222_256x240.png); }  
104 -.ui-widget-header .ui-icon {background-image: url(ui-icons_222222_256x240.png); }  
105 -.ui-state-default .ui-icon { background-image: url(ui-icons_454545_256x240.png); }  
106 -.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(ui-icons_454545_256x240.png); }  
107 -.ui-state-active .ui-icon {background-image: url(ui-icons_454545_256x240.png); }  
108 -.ui-state-highlight .ui-icon {background-image: url(ui-icons_454545_256x240.png); }  
109 -.ui-state-error .ui-icon, .ui-state-error-text .ui-icon { background: url(icon_sprite.png) -16px 0 no-repeat !important; }  
110 -.ui-state-highlight .ui-icon, .ui-state-error .ui-icon { margin-top: -1px; }  
111 -  
112 -  
113 -/* Misc visuals  
114 -----------------------------------*/  
115 -  
116 -/* Overlays */  
117 -.ui-widget-overlay { background: #262b33; opacity: .70;filter:Alpha(Opacity=70); }  
118 -.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; }  
119 -/*  
120 - * jQuery UI Selectable 1.8.7  
121 - *  
122 - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)  
123 - * Dual licensed under the MIT or GPL Version 2 licenses.  
124 - * http://jquery.org/license  
125 - *  
126 - * http://docs.jquery.com/UI/Selectable#theming  
127 - */  
128 -.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }  
129 -/*  
130 - * jQuery UI Autocomplete 1.8.7  
131 - *  
132 - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)  
133 - * Dual licensed under the MIT or GPL Version 2 licenses.  
134 - * http://jquery.org/license  
135 - *  
136 - * http://docs.jquery.com/UI/Autocomplete#theming  
137 - */  
138 -.ui-autocomplete {  
139 - position: absolute; cursor: default; z-index: 3;  
140 - -moz-border-radius: 0;  
141 - -webkit-border-radius: 0;  
142 - border-radius: 0;  
143 - -moz-box-shadow: 0 1px 5px rgba(0,0,0,0.3);  
144 - -webkit-box-shadow: 0 1px 5px rgba(0,0,0,0.3);  
145 - box-shadow: 0 1px 5px rgba(0,0,0,0.3);  
146 -}  
147 -  
148 -/* workarounds */  
149 -* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */  
150 -  
151 -/*  
152 - * jQuery UI Menu 1.8.7  
153 - *  
154 - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)  
155 - * Dual licensed under the MIT or GPL Version 2 licenses.  
156 - * http://jquery.org/license  
157 - *  
158 - * http://docs.jquery.com/UI/Menu#theming  
159 - */  
160 -.ui-menu {  
161 - list-style:none;  
162 - padding: 1px;  
163 - margin: 0;  
164 - display:block;  
165 - float: left;  
166 -}  
167 -.ui-menu .ui-menu {  
168 - margin-top: -3px;  
169 -}  
170 -.ui-menu .ui-menu-item {  
171 - margin:0;  
172 - padding: 0;  
173 - zoom: 1;  
174 - float: left;  
175 - clear: left;  
176 - width: 100%;  
177 -}  
178 -.ui-menu .ui-menu-item a {  
179 - text-decoration:none;  
180 - display:block;  
181 - padding:.2em .4em;  
182 - line-height:1.5;  
183 - zoom:1;  
184 - color: #666;  
185 - font-size: 13px;  
186 -}  
187 -.ui-menu .ui-menu-item a.ui-state-hover,  
188 -.ui-menu .ui-menu-item a.ui-state-active {  
189 - font-weight: normal;  
190 - margin: -1px;  
191 - background: #D9EDF7;  
192 - color: #3A89A3;  
193 - text-shadow: 0px 1px 1px #fff;  
194 - border: none;  
195 - border: 1px solid #ADE;  
196 - cursor: pointer;  
197 - font-weight: bold;  
198 -}  
199 -  
200 -/*  
201 - * jQuery UI Datepicker 1.8.7  
202 - *  
203 - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)  
204 - * Dual licensed under the MIT or GPL Version 2 licenses.  
205 - * http://jquery.org/license  
206 - *  
207 - * http://docs.jquery.com/UI/Datepicker#theming  
208 - */  
209 -.ui-datepicker {  
210 - width: 17em;  
211 - padding: 0;  
212 - display: none;  
213 - border-color: #DDDDDD;  
214 - border: none;  
215 - box-shadow: none;  
216 -}  
217 -.ui-datepicker .ui-datepicker-header {  
218 - position:relative;  
219 - padding:.35em 0;  
220 - border: none;  
221 - border-bottom: 1px solid #B6B6B6;  
222 - -moz-border-radius: 0;  
223 - -webkit-border-radius: 0;  
224 - border-radius: 0;  
225 - margin-bottom: 10px;  
226 - border: 1px solid #bbb;  
227 - -webkit-box-shadow: 0 0 0 3px #F1F1F1;  
228 - -moz-box-shadow: 0 0 0 3px #f1f1f1;  
229 - -ms-box-shadow: 0 0 0 3px #f1f1f1;  
230 - -o-box-shadow: 0 0 0 3px #f1f1f1;  
231 - box-shadow: 0 0 0 3px #F1F1F1;  
232 -}  
233 -  
234 -.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 6px; width: 1.8em; height: 1.8em; }  
235 -.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { border: 1px none; }  
236 -.ui-datepicker .ui-datepicker-prev { left:2px; }  
237 -.ui-datepicker .ui-datepicker-next { right:2px; }  
238 -.ui-datepicker .ui-datepicker-prev span { background-position: 0px -32px !important; }  
239 -.ui-datepicker .ui-datepicker-next span { background-position: -16px -32px !important; }  
240 -.ui-datepicker .ui-datepicker-prev-hover span { background-position: 0px -48px !important; }  
241 -.ui-datepicker .ui-datepicker-next-hover span { background-position: -16px -48px !important; }  
242 -.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; }  
243 -.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); }  
244 -.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }  
245 -.ui-datepicker select.ui-datepicker-month-year {width: 100%;}  
246 -.ui-datepicker select.ui-datepicker-month,  
247 -.ui-datepicker select.ui-datepicker-year { width: 49%;}  
248 -.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }  
249 -.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }  
250 -.ui-datepicker td { border: 0; padding: 1px; line-height: 24px; background-color: #FFF!important; }  
251 -.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }  
252 -.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; }  
253 -.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }  
254 -.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }  
255 -.ui-datepicker table .ui-state-highlight { border-color: #ADE; }  
256 -.ui-datepicker-calendar .ui-state-default { background: transparent; border-color: #FFF; }  
257 -.ui-datepicker-calendar .ui-state-active { background: #D9EDF7; border-color: #ADE; color: #3A89A3; font-weight: bold; text-shadow: 0 1px 1px #fff; }  
app/assets/stylesheets/main/fonts.scss
1 /** Typo **/ 1 /** Typo **/
2 $monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; 2 $monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;
  3 +$regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif;
app/controllers/projects/issues_controller.rb
@@ -59,9 +59,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController @@ -59,9 +59,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController
59 end 59 end
60 60
61 def create 61 def create
62 - @issue = @project.issues.new(params[:issue])  
63 - @issue.author = current_user  
64 - @issue.save 62 + @issue = Issues::CreateService.new(project, current_user, params[:issue]).execute
65 63
66 respond_to do |format| 64 respond_to do |format|
67 format.html do 65 format.html do
@@ -76,8 +74,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController @@ -76,8 +74,7 @@ class Projects::IssuesController &lt; Projects::ApplicationController
76 end 74 end
77 75
78 def update 76 def update
79 - @issue.update_attributes(params[:issue])  
80 - @issue.reset_events_cache 77 + @issue = Issues::UpdateService.new(project, current_user, params[:issue]).execute(issue)
81 78
82 respond_to do |format| 79 respond_to do |format|
83 format.js 80 format.js
app/controllers/projects/merge_requests_controller.rb
@@ -76,10 +76,10 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController @@ -76,10 +76,10 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController
76 end 76 end
77 77
78 def create 78 def create
79 - @merge_request = MergeRequest.new(params[:merge_request])  
80 - @merge_request.author = current_user  
81 @target_branches ||= [] 79 @target_branches ||= []
82 - if @merge_request.save 80 + @merge_request = MergeRequests::CreateService.new(project, current_user, params[:merge_request]).execute
  81 +
  82 + if @merge_request.valid?
83 redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully created.' 83 redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully created.'
84 else 84 else
85 @source_project = @merge_request.source_project 85 @source_project = @merge_request.source_project
@@ -89,29 +89,9 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController @@ -89,29 +89,9 @@ class Projects::MergeRequestsController &lt; Projects::ApplicationController
89 end 89 end
90 90
91 def update 91 def update
92 - # If we close MergeRequest we want to ignore validation  
93 - # so we can close broken one (Ex. fork project removed)  
94 - if params[:merge_request] == {"state_event"=>"close"}  
95 - @merge_request.allow_broken = true  
96 -  
97 - if @merge_request.close  
98 - opts = { notice: 'Merge request was successfully closed.' }  
99 - else  
100 - opts = { alert: 'Failed to close merge request.' }  
101 - end  
102 -  
103 - redirect_to [@merge_request.target_project, @merge_request], opts  
104 - return  
105 - end  
106 -  
107 - # We dont allow change of source/target projects  
108 - # after merge request was created  
109 - params[:merge_request].delete(:source_project_id)  
110 - params[:merge_request].delete(:target_project_id)  
111 -  
112 - if @merge_request.update_attributes(params[:merge_request])  
113 - @merge_request.reset_events_cache 92 + @merge_request = MergeRequests::UpdateService.new(project, current_user, params[:merge_request]).execute(@merge_request)
114 93
  94 + if @merge_request.valid?
115 respond_to do |format| 95 respond_to do |format|
116 format.js 96 format.js
117 format.html do 97 format.html do
app/helpers/merge_requests_helper.rb
@@ -20,7 +20,7 @@ module MergeRequestsHelper @@ -20,7 +20,7 @@ module MergeRequestsHelper
20 target_project_id: target_project.id, 20 target_project_id: target_project.id,
21 source_branch: event.branch_name, 21 source_branch: event.branch_name,
22 target_branch: target_project.repository.root_ref, 22 target_branch: target_project.repository.root_ref,
23 - title: event.branch_name.humanize 23 + title: event.branch_name.titleize.humanize
24 } 24 }
25 end 25 end
26 26
app/models/email.rb
@@ -13,14 +13,15 @@ class Email &lt; ActiveRecord::Base @@ -13,14 +13,15 @@ class Email &lt; ActiveRecord::Base
13 # Relations 13 # Relations
14 # 14 #
15 belongs_to :user 15 belongs_to :user
16 - 16 +
17 # 17 #
18 # Validations 18 # Validations
19 # 19 #
20 validates :user_id, presence: true 20 validates :user_id, presence: true
21 validates :email, presence: true, email: { strict_mode: true }, uniqueness: true 21 validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
22 validate :unique_email, if: ->(email) { email.email_changed? } 22 validate :unique_email, if: ->(email) { email.email_changed? }
23 - 23 +
  24 + after_create :notify
24 before_validation :cleanup_email 25 before_validation :cleanup_email
25 26
26 def cleanup_email 27 def cleanup_email
@@ -30,4 +31,8 @@ class Email &lt; ActiveRecord::Base @@ -30,4 +31,8 @@ class Email &lt; ActiveRecord::Base
30 def unique_email 31 def unique_email
31 self.errors.add(:email, 'has already been taken') if User.exists?(email: self.email) 32 self.errors.add(:email, 'has already been taken') if User.exists?(email: self.email)
32 end 33 end
33 -end  
34 \ No newline at end of file 34 \ No newline at end of file
  35 +
  36 + def notify
  37 + NotificationService.new.new_email(self)
  38 + end
  39 +end
app/models/key.rb
@@ -29,6 +29,10 @@ class Key &lt; ActiveRecord::Base @@ -29,6 +29,10 @@ class Key &lt; ActiveRecord::Base
29 29
30 delegate :name, :email, to: :user, prefix: true 30 delegate :name, :email, to: :user, prefix: true
31 31
  32 + after_create :add_to_shell
  33 + after_create :notify_user
  34 + after_destroy :remove_from_shell
  35 +
32 def strip_white_space 36 def strip_white_space
33 self.key = key.strip unless key.blank? 37 self.key = key.strip unless key.blank?
34 end 38 end
@@ -42,6 +46,26 @@ class Key &lt; ActiveRecord::Base @@ -42,6 +46,26 @@ class Key &lt; ActiveRecord::Base
42 "key-#{id}" 46 "key-#{id}"
43 end 47 end
44 48
  49 + def add_to_shell
  50 + GitlabShellWorker.perform_async(
  51 + :add_key,
  52 + shell_id,
  53 + key
  54 + )
  55 + end
  56 +
  57 + def notify_user
  58 + NotificationService.new.new_key(self)
  59 + end
  60 +
  61 + def remove_from_shell
  62 + GitlabShellWorker.perform_async(
  63 + :remove_key,
  64 + shell_id,
  65 + key,
  66 + )
  67 + end
  68 +
45 private 69 private
46 70
47 def generate_fingerpint 71 def generate_fingerpint
app/models/merge_request.rb
@@ -97,6 +97,7 @@ class MergeRequest &lt; ActiveRecord::Base @@ -97,6 +97,7 @@ class MergeRequest &lt; ActiveRecord::Base
97 validates :target_project, presence: true 97 validates :target_project, presence: true
98 validates :target_branch, presence: true 98 validates :target_branch, presence: true
99 validate :validate_branches 99 validate :validate_branches
  100 + validate :validate_fork
100 101
101 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) } 102 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) }
102 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) } 103 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 &lt; ActiveRecord::Base @@ -125,6 +126,22 @@ class MergeRequest &lt; ActiveRecord::Base
125 end 126 end
126 end 127 end
127 128
  129 + def validate_fork
  130 + return true unless target_project && source_project
  131 +
  132 + if target_project == source_project
  133 + true
  134 + else
  135 + # If source and target projects are different
  136 + # we should check if source project is actually a fork of target project
  137 + if source_project.forked_from?(target_project)
  138 + true
  139 + else
  140 + errors.add :base, "Source project is not a fork of target project"
  141 + end
  142 + end
  143 + end
  144 +
128 def update_merge_request_diff 145 def update_merge_request_diff
129 if source_branch_changed? || target_branch_changed? 146 if source_branch_changed? || target_branch_changed?
130 reload_code 147 reload_code
app/models/project.rb
@@ -552,4 +552,8 @@ class Project &lt; ActiveRecord::Base @@ -552,4 +552,8 @@ class Project &lt; ActiveRecord::Base
552 gitlab_shell.update_repository_head(self.path_with_namespace, branch) 552 gitlab_shell.update_repository_head(self.path_with_namespace, branch)
553 reload_default_branch 553 reload_default_branch
554 end 554 end
  555 +
  556 + def forked_from?(project)
  557 + forked? && project == forked_from_project
  558 + end
555 end 559 end
app/observers/email_observer.rb
@@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
1 -class EmailObserver < BaseObserver  
2 - def after_create(email)  
3 - notification.new_email(email)  
4 - end  
5 -end  
app/observers/issue_observer.rb
@@ -1,46 +0,0 @@ @@ -1,46 +0,0 @@
1 -class IssueObserver < BaseObserver  
2 - def after_create(issue)  
3 - notification.new_issue(issue, current_user)  
4 - event_service.open_issue(issue, current_user)  
5 - issue.create_cross_references!(issue.project, current_user)  
6 - execute_hooks(issue)  
7 - end  
8 -  
9 - def after_close(issue, transition)  
10 - notification.close_issue(issue, current_user)  
11 - event_service.close_issue(issue, current_user)  
12 - create_note(issue)  
13 - execute_hooks(issue)  
14 - end  
15 -  
16 - def after_reopen(issue, transition)  
17 - event_service.reopen_issue(issue, current_user)  
18 - create_note(issue)  
19 - execute_hooks(issue)  
20 - end  
21 -  
22 - def after_update(issue)  
23 - if issue.is_being_reassigned?  
24 - notification.reassigned_issue(issue, current_user)  
25 - create_assignee_note(issue)  
26 - end  
27 -  
28 - issue.notice_added_references(issue.project, current_user)  
29 - execute_hooks(issue)  
30 - end  
31 -  
32 - protected  
33 -  
34 - # Create issue note with service comment like 'Status changed to closed'  
35 - def create_note(issue)  
36 - Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit)  
37 - end  
38 -  
39 - def create_assignee_note(issue)  
40 - Note.create_assignee_change_note(issue, issue.project, current_user, issue.assignee)  
41 - end  
42 -  
43 - def execute_hooks(issue)  
44 - issue.project.execute_hooks(issue.to_hook_data, :issue_hooks)  
45 - end  
46 -end  
app/observers/key_observer.rb
@@ -1,19 +0,0 @@ @@ -1,19 +0,0 @@
1 -class KeyObserver < BaseObserver  
2 - def after_create(key)  
3 - GitlabShellWorker.perform_async(  
4 - :add_key,  
5 - key.shell_id,  
6 - key.key  
7 - )  
8 -  
9 - notification.new_key(key)  
10 - end  
11 -  
12 - def after_destroy(key)  
13 - GitlabShellWorker.perform_async(  
14 - :remove_key,  
15 - key.shell_id,  
16 - key.key,  
17 - )  
18 - end  
19 -end  
app/observers/merge_request_observer.rb
@@ -1,43 +0,0 @@ @@ -1,43 +0,0 @@
1 -class MergeRequestObserver < BaseObserver  
2 - def after_create(merge_request)  
3 - event_service.open_mr(merge_request, current_user)  
4 - notification.new_merge_request(merge_request, current_user)  
5 - merge_request.create_cross_references!(merge_request.project, current_user)  
6 - execute_hooks(merge_request)  
7 - end  
8 -  
9 - def after_close(merge_request, transition)  
10 - event_service.close_mr(merge_request, current_user)  
11 - notification.close_mr(merge_request, current_user)  
12 - create_note(merge_request)  
13 - execute_hooks(merge_request)  
14 - end  
15 -  
16 - def after_reopen(merge_request, transition)  
17 - event_service.reopen_mr(merge_request, current_user)  
18 - create_note(merge_request)  
19 - execute_hooks(merge_request)  
20 - merge_request.reload_code  
21 - merge_request.mark_as_unchecked  
22 - end  
23 -  
24 - def after_update(merge_request)  
25 - notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned?  
26 -  
27 - merge_request.notice_added_references(merge_request.project, current_user)  
28 - execute_hooks(merge_request)  
29 - end  
30 -  
31 - private  
32 -  
33 - # Create merge request note with service comment like 'Status changed to closed'  
34 - def create_note(merge_request)  
35 - Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)  
36 - end  
37 -  
38 - def execute_hooks(merge_request)  
39 - if merge_request.project  
40 - merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks)  
41 - end  
42 - end  
43 -end  
app/observers/users_project_observer.rb
@@ -10,7 +10,7 @@ class UsersProjectObserver &lt; BaseObserver @@ -10,7 +10,7 @@ class UsersProjectObserver &lt; BaseObserver
10 end 10 end
11 11
12 def after_update(users_project) 12 def after_update(users_project)
13 - notification.update_team_member(users_project) 13 + notification.update_team_member(users_project) if users_project.project_access_changed?
14 end 14 end
15 15
16 def after_destroy(users_project) 16 def after_destroy(users_project)
app/services/base_service.rb
@@ -16,4 +16,16 @@ class BaseService @@ -16,4 +16,16 @@ class BaseService
16 def can?(object, action, subject) 16 def can?(object, action, subject)
17 abilities.allowed?(object, action, subject) 17 abilities.allowed?(object, action, subject)
18 end 18 end
  19 +
  20 + def notification_service
  21 + NotificationService.new
  22 + end
  23 +
  24 + def event_service
  25 + EventCreateService.new
  26 + end
  27 +
  28 + def log_info message
  29 + Gitlab::AppLogger.info message
  30 + end
19 end 31 end
app/services/git_push_service.rb
@@ -86,10 +86,9 @@ class GitPushService @@ -86,10 +86,9 @@ class GitPushService
86 author = commit_user(commit) 86 author = commit_user(commit)
87 87
88 if !issues_to_close.empty? && is_default_branch 88 if !issues_to_close.empty? && is_default_branch
89 - Thread.current[:current_user] = author  
90 - Thread.current[:current_commit] = commit  
91 -  
92 - issues_to_close.each { |i| i.close && i.save } 89 + issues_to_close.each do |issue|
  90 + Issues::CloseService.new(project, author, {}).execute(issue, commit)
  91 + end
93 end 92 end
94 93
95 # Create cross-reference notes for any other references. Omit any issues that were referenced in an 94 # Create cross-reference notes for any other references. Omit any issues that were referenced in an
app/services/issues/base_service.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +module Issues
  2 + class BaseService < ::BaseService
  3 +
  4 + private
  5 +
  6 + def create_assignee_note(issue)
  7 + Note.create_assignee_change_note(issue, issue.project, current_user, issue.assignee)
  8 + end
  9 +
  10 + def execute_hooks(issue)
  11 + issue.project.execute_hooks(issue.to_hook_data, :issue_hooks)
  12 + end
  13 + end
  14 +end
app/services/issues/close_service.rb 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +module Issues
  2 + class CloseService < Issues::BaseService
  3 + def execute(issue, commit = nil)
  4 + if issue.close
  5 + notification_service.close_issue(issue, current_user)
  6 + event_service.close_issue(issue, current_user)
  7 + create_note(issue, commit)
  8 + execute_hooks(issue)
  9 + end
  10 +
  11 + issue
  12 + end
  13 +
  14 + private
  15 +
  16 + def create_note(issue, current_commit)
  17 + Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit)
  18 + end
  19 + end
  20 +end
app/services/issues/create_service.rb 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +module Issues
  2 + class CreateService < Issues::BaseService
  3 + def execute
  4 + issue = project.issues.new(params)
  5 + issue.author = current_user
  6 +
  7 + if issue.save
  8 + notification_service.new_issue(issue, current_user)
  9 + event_service.open_issue(issue, current_user)
  10 + issue.create_cross_references!(issue.project, current_user)
  11 + execute_hooks(issue)
  12 + end
  13 +
  14 + issue
  15 + end
  16 + end
  17 +end
app/services/issues/reopen_service.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +module Issues
  2 + class ReopenService < Issues::BaseService
  3 + def execute(issue)
  4 + if issue.reopen
  5 + event_service.reopen_issue(issue, current_user)
  6 + create_note(issue)
  7 + execute_hooks(issue)
  8 + end
  9 +
  10 + issue
  11 + end
  12 +
  13 + private
  14 +
  15 + def create_note(issue)
  16 + Note.create_status_change_note(issue, issue.project, current_user, issue.state, nil)
  17 + end
  18 + end
  19 +end
app/services/issues/update_service.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +module Issues
  2 + class UpdateService < Issues::BaseService
  3 + def execute(issue)
  4 + state = params.delete('state_event')
  5 +
  6 + case state
  7 + when 'reopen'
  8 + Issues::ReopenService.new(project, current_user, {}).execute(issue)
  9 + when 'close'
  10 + Issues::CloseService.new(project, current_user, {}).execute(issue)
  11 + end
  12 +
  13 + if params.present? && issue.update_attributes(params)
  14 + issue.reset_events_cache
  15 +
  16 + if issue.previous_changes.include?('assignee_id')
  17 + notification_service.reassigned_issue(issue, current_user)
  18 + create_assignee_note(issue)
  19 + end
  20 +
  21 + issue.notice_added_references(issue.project, current_user)
  22 + execute_hooks(issue)
  23 + end
  24 +
  25 + issue
  26 + end
  27 + end
  28 +end
app/services/merge_requests/base_service.rb 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +module MergeRequests
  2 + class BaseService < ::BaseService
  3 +
  4 + private
  5 +
  6 + def create_assignee_note(merge_request)
  7 + Note.create_assignee_change_note(merge_request, merge_request.project, current_user, merge_request.assignee)
  8 + end
  9 +
  10 + def create_note(merge_request)
  11 + Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
  12 + end
  13 +
  14 + def execute_hooks(merge_request)
  15 + if merge_request.project
  16 + merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks)
  17 + end
  18 + end
  19 + end
  20 +end
app/services/merge_requests/close_service.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +module MergeRequests
  2 + class CloseService < MergeRequests::BaseService
  3 + def execute(merge_request, commit = nil)
  4 + # If we close MergeRequest we want to ignore validation
  5 + # so we can close broken one (Ex. fork project removed)
  6 + merge_request.allow_broken = true
  7 +
  8 + if merge_request.close
  9 + event_service.close_mr(merge_request, current_user)
  10 + notification_service.close_mr(merge_request, current_user)
  11 + create_note(merge_request)
  12 + execute_hooks(merge_request)
  13 + end
  14 +
  15 + merge_request
  16 + end
  17 + end
  18 +end
app/services/merge_requests/create_service.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +module MergeRequests
  2 + class CreateService < MergeRequests::BaseService
  3 + def execute
  4 + merge_request = MergeRequest.new(params)
  5 + merge_request.source_project = project
  6 + merge_request.target_project ||= project
  7 + merge_request.author = current_user
  8 +
  9 + if merge_request.save
  10 + event_service.open_mr(merge_request, current_user)
  11 + notification_service.new_merge_request(merge_request, current_user)
  12 + merge_request.create_cross_references!(merge_request.project, current_user)
  13 + execute_hooks(merge_request)
  14 + end
  15 +
  16 + merge_request
  17 + end
  18 + end
  19 +end
app/services/merge_requests/reopen_service.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +module MergeRequests
  2 + class ReopenService < MergeRequests::BaseService
  3 + def execute(merge_request)
  4 + if merge_request.reopen
  5 + event_service.reopen_mr(merge_request, current_user)
  6 + create_note(merge_request)
  7 + execute_hooks(merge_request)
  8 + merge_request.reload_code
  9 + merge_request.mark_as_unchecked
  10 + end
  11 +
  12 + merge_request
  13 + end
  14 + end
  15 +end
app/services/merge_requests/update_service.rb 0 → 100644
@@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
  1 +require_relative 'base_service'
  2 +require_relative 'reopen_service'
  3 +require_relative 'close_service'
  4 +
  5 +module MergeRequests
  6 + class UpdateService < MergeRequests::BaseService
  7 + def execute(merge_request)
  8 + # We dont allow change of source/target projects
  9 + # after merge request was created
  10 + params.delete(:source_project_id)
  11 + params.delete(:target_project_id)
  12 +
  13 + state = params.delete('state_event')
  14 +
  15 + case state
  16 + when 'reopen'
  17 + MergeRequests::ReopenService.new(project, current_user, {}).execute(merge_request)
  18 + when 'close'
  19 + MergeRequests::CloseService.new(project, current_user, {}).execute(merge_request)
  20 + end
  21 +
  22 + if params.present? && merge_request.update_attributes(params)
  23 + merge_request.reset_events_cache
  24 +
  25 + if merge_request.previous_changes.include?('assignee_id')
  26 + notification_service.reassigned_merge_request(merge_request, current_user)
  27 + create_assignee_note(merge_request)
  28 + end
  29 +
  30 + merge_request.notice_added_references(merge_request.project, current_user)
  31 + execute_hooks(merge_request)
  32 + end
  33 +
  34 + merge_request
  35 + end
  36 + end
  37 +end
app/services/notification_service.rb
@@ -178,29 +178,29 @@ class NotificationService @@ -178,29 +178,29 @@ class NotificationService
178 178
179 # Get project users with WATCH notification level 179 # Get project users with WATCH notification level
180 def project_watchers(project) 180 def project_watchers(project)
181 - # Gather all user ids that have WATCH notification setting for project  
182 - project_notification_uids = project_notification_list(project, Notification::N_WATCH) 181 + project_members = users_project_notification(project)
183 182
184 - # Gather all user ids that have WATCH notification setting for group  
185 - group_notification_uids = group_notification_list(project, Notification::N_WATCH) 183 + users_with_project_level_global = users_project_notification(project, Notification::N_GLOBAL)
  184 + users_with_group_level_global = users_group_notification(project, Notification::N_GLOBAL)
  185 + users = users_with_global_level_watch([users_with_project_level_global, users_with_group_level_global].flatten.uniq)
186 186
187 - # Gather all user ids that have GLOBAL setting  
188 - global_notification_uids = global_notification_list(project) 187 + users_with_project_setting = select_users_project_setting(project, users_with_project_level_global, users)
  188 + users_with_group_setting = select_users_group_setting(project, project_members, users_with_group_level_global, users)
189 189
190 - project_and_group_uids = [project_notification_uids, group_notification_uids].flatten.uniq  
191 - group_and_project_watchers = User.where(id: project_and_group_uids)  
192 -  
193 - # Find all users that have WATCH as their GLOBAL setting  
194 - global_watchers = User.where(id: global_notification_uids, notification_level: Notification::N_WATCH)  
195 -  
196 - [group_and_project_watchers, global_watchers].flatten.uniq 190 + User.where(id: users_with_project_setting.concat(users_with_group_setting).uniq).to_a
197 end 191 end
198 192
199 - def project_notification_list(project, notification_level)  
200 - project.users_projects.where(notification_level: notification_level).pluck(:user_id) 193 + def users_project_notification(project, notification_level=nil)
  194 + project_members = project.users_projects
  195 +
  196 + if notification_level
  197 + project_members.where(notification_level: notification_level).pluck(:user_id)
  198 + else
  199 + project_members.pluck(:user_id)
  200 + end
201 end 201 end
202 202
203 - def group_notification_list(project, notification_level) 203 + def users_group_notification(project, notification_level)
204 if project.group 204 if project.group
205 project.group.users_groups.where(notification_level: notification_level).pluck(:user_id) 205 project.group.users_groups.where(notification_level: notification_level).pluck(:user_id)
206 else 206 else
@@ -208,11 +208,47 @@ class NotificationService @@ -208,11 +208,47 @@ class NotificationService
208 end 208 end
209 end 209 end
210 210
211 - def global_notification_list(project)  
212 - [  
213 - project_notification_list(project, Notification::N_GLOBAL),  
214 - group_notification_list(project, Notification::N_GLOBAL)  
215 - ].flatten 211 + def users_with_global_level_watch(ids)
  212 + User.where(
  213 + id: ids,
  214 + notification_level: Notification::N_WATCH
  215 + ).pluck(:id)
  216 + end
  217 +
  218 + # Build a list of users based on project notifcation settings
  219 + def select_users_project_setting(project, global_setting, users_global_level_watch)
  220 + users = users_project_notification(project, Notification::N_WATCH)
  221 +
  222 + # If project setting is global, add to watch list if global setting is watch
  223 + global_setting.each do |user_id|
  224 + if users_global_level_watch.include?(user_id)
  225 + users << user_id
  226 + end
  227 + end
  228 +
  229 + users
  230 + end
  231 +
  232 + # Build a list of users based on group notifcation settings
  233 + def select_users_group_setting(project, project_members, global_setting, users_global_level_watch)
  234 + uids = users_group_notification(project, Notification::N_WATCH)
  235 +
  236 + # Group setting is watch, add to users list if user is not project member
  237 + users = []
  238 + uids.each do |user_id|
  239 + if project_members.exclude?(user_id)
  240 + users << user_id
  241 + end
  242 + end
  243 +
  244 + # Group setting is global, add to users list if global setting is watch
  245 + global_setting.each do |user_id|
  246 + if project_members.exclude?(user_id) && users_global_level_watch.include?(user_id)
  247 + users << user_id
  248 + end
  249 + end
  250 +
  251 + users
216 end 252 end
217 253
218 # Remove users with disabled notifications from array 254 # Remove users with disabled notifications from array
app/views/projects/issues/_head.html.haml
@@ -20,6 +20,11 @@ @@ -20,6 +20,11 @@
20 = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do 20 = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
21 .append-right-10.hidden-xs.hidden-sm 21 .append-right-10.hidden-xs.hidden-sm
22 = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } 22 = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
  23 + = hidden_field_tag :state, params['state']
  24 + = hidden_field_tag :scope, params['scope']
  25 + = hidden_field_tag :assignee_id, params['assignee_id']
  26 + = hidden_field_tag :milestone_id, params['milestone_id']
  27 + = hidden_field_tag :label_id, params['label_id']
23 - if can? current_user, :write_issue, @project 28 - if can? current_user, :write_issue, @project
24 = 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 29 = 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
25 %i.icon-plus 30 %i.icon-plus
app/views/projects/merge_requests/_form.html.haml
@@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
33 .col-sm-10 33 .col-sm-10
34 .clearfix 34 .clearfix
35 .pull-left 35 .pull-left
36 - - projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project] 36 + - projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
37 = 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? }) 37 = 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? })
38 .pull-left 38 .pull-left
39 &nbsp; 39 &nbsp;
app/views/projects/merge_requests/branch_from.js.haml
@@ -3,5 +3,5 @@ @@ -3,5 +3,5 @@
3 var mrTitle = $('#merge_request_title'); 3 var mrTitle = $('#merge_request_title');
4 4
5 if(mrTitle.val().length == 0) { 5 if(mrTitle.val().length == 0) {
6 - mrTitle.val("#{params[:ref].humanize}"); 6 + mrTitle.val("#{params[:ref].titleize.humanize}");
7 } 7 }
app/views/projects/protected_branches/index.html.haml
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 %ul 9 %ul
10 %li keep stable branches secured 10 %li keep stable branches secured
11 %li forced code review before merge to protected branches 11 %li forced code review before merge to protected branches
  12 + %li prevents branch from force push
12 %p Read more about project permissions #{link_to "here", help_permissions_path, class: "underlined-link"} 13 %p Read more about project permissions #{link_to "here", help_permissions_path, class: "underlined-link"}
13 14
14 - if can? current_user, :admin_project, @project 15 - if can? current_user, :admin_project, @project
config/application.rb
@@ -21,9 +21,6 @@ module Gitlab @@ -21,9 +21,6 @@ module Gitlab
21 # Activate observers that should always be running. 21 # Activate observers that should always be running.
22 config.active_record.observers = :milestone_observer, 22 config.active_record.observers = :milestone_observer,
23 :project_activity_cache_observer, 23 :project_activity_cache_observer,
24 - :issue_observer,  
25 - :key_observer,  
26 - :merge_request_observer,  
27 :note_observer, 24 :note_observer,
28 :project_observer, 25 :project_observer,
29 :system_hook_observer, 26 :system_hook_observer,
@@ -64,6 +61,7 @@ module Gitlab @@ -64,6 +61,7 @@ module Gitlab
64 config.assets.enabled = true 61 config.assets.enabled = true
65 config.assets.paths << Emoji.images_path 62 config.assets.paths << Emoji.images_path
66 config.assets.precompile << "emoji/*.png" 63 config.assets.precompile << "emoji/*.png"
  64 + config.assets.precompile << "print.css"
67 65
68 # Version of your assets, change this if you want to expire all your assets 66 # Version of your assets, change this if you want to expire all your assets
69 config.assets.version = '1.0' 67 config.assets.version = '1.0'
1 -## The GitLab Documentation covers the following subjects 1 +**User documentation**
2 2
3 -+ [API](api/README.md)  
4 -+ [Development](development/README.md)  
5 -+ [Install](install/README.md)  
6 -+ [Integration](integration/external-issue-tracker.md)  
7 -+ [Legal](legal/README.md)  
8 -+ [Markdown](markdown/markdown.md)  
9 -+ [Permissions](permissions/permissions.md)  
10 -+ [Public access](public_access/public_access.md)  
11 -+ [Raketasks](raketasks/README.md)  
12 -+ [Release](release/README.md)  
13 -+ [Security](security/README.md)  
14 -+ [SSH](ssh/README.md)  
15 -+ [System hooks](system_hooks/system_hooks.md)  
16 -+ [Update](update/README.md)  
17 -+ [Web hooks](web_hooks/web_hooks.md)  
18 -+ [Workflow](workflow/workflow.md) 3 ++ [API](api/README.md) Explore how you can access GitLab via a simple and powerful API.
  4 ++ [Markdown](markdown/markdown.md) Learn what you can do with GitLab's advanced formatting system.
  5 ++ [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
  6 ++ [Public access](public_access/public_access.md) Learn how you can allow public and internal access to a project.
  7 ++ [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
  8 ++ [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
  9 ++ [Workflow](workflow/workflow.md) Learn how to use Git and GitLab together.
  10 +
  11 +**Administrator documentation**
  12 +
  13 ++ [Install](install/README.md) Requirements, directory structures and manual installation.
  14 ++ [Integration](integration/external-issue-tracker.md) How to integrate JIRA and Redmine.
  15 ++ [Raketasks](raketasks/README.md) Explore what GitLab has in store for you to make administration easier.
  16 ++ [System hooks](system_hooks/system_hooks.md) Let GitLab notify you when certain management tasks need to be carried out.
  17 ++ [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
  18 ++ [Update](update/README.md) Update guides to upgrade your installation.
  19 +
  20 +**Contributor documentation**
  21 +
  22 ++ [Development](development/README.md) Explains the architecture and the guidelines for shell commands.
  23 ++ [Legal](legal/README.md) Contributor license agreements.
  24 ++ [Release](release/README.md) How to make the monthly and security releases.
doc/install/installation.md
@@ -93,7 +93,7 @@ Then select &#39;Internet Site&#39; and press enter to confirm the hostname. @@ -93,7 +93,7 @@ Then select &#39;Internet Site&#39; and press enter to confirm the hostname.
93 93
94 # 2. Ruby 94 # 2. Ruby
95 95
96 -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. 96 +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.
97 97
98 Remove the old Ruby 1.8 if present 98 Remove the old Ruby 1.8 if present
99 99
@@ -202,6 +202,7 @@ You can change `6-6-stable` to `master` if you want the *bleeding edge* version, @@ -202,6 +202,7 @@ You can change `6-6-stable` to `master` if you want the *bleeding edge* version,
202 202
203 # Create directory for satellites 203 # Create directory for satellites
204 sudo -u git -H mkdir /home/git/gitlab-satellites 204 sudo -u git -H mkdir /home/git/gitlab-satellites
  205 + sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
205 206
206 # Create directories for sockets/pids and make sure GitLab can write to them 207 # Create directories for sockets/pids and make sure GitLab can write to them
207 sudo -u git -H mkdir tmp/pids/ 208 sudo -u git -H mkdir tmp/pids/
doc/integration/external-issue-tracker.md
@@ -2,7 +2,7 @@ GitLab has a great issue tracker but you can also use an external issue tracker @@ -2,7 +2,7 @@ GitLab has a great issue tracker but you can also use an external issue tracker
2 2
3 - the 'Issues' link on the GitLab project pages takes you to the appropriate JIRA issue index; 3 - the 'Issues' link on the GitLab project pages takes you to the appropriate JIRA issue index;
4 - clicking 'New issue' on the project dashboard creates a new JIRA issue; 4 - clicking 'New issue' on the project dashboard creates a new JIRA issue;
5 -- textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue. 5 +- To reference JIRA issue PROJECT-1234 in comments, use syntax #PROJECT-1234. Commit messages get turned into HTML links to the corresponding JIRA issue.
6 6
7 ![jira screenshot](jira-intergration-points.png) 7 ![jira screenshot](jira-intergration-points.png)
8 8
doc/integration/ldap.md 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +# GitLab LDAP integration
  2 +
  3 +GitLab can be configured to allow your users to sign with their LDAP credentials to integrate with e.g. Active Directory.
  4 +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.
  5 +GitLab user attributes such as nickname and email will be copied from the LDAP user entry.
  6 +
  7 +## Enabling LDAP sign-in for existing GitLab users
  8 +
  9 +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.
  10 +If the LDAP email attribute is not found in GitLab's database, a new user is created.
  11 +
  12 +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.
  13 +GitLab recognizes the following LDAP attributes as email addresses: `mail`, `email` and `userPrincipalName`.
  14 +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`.
doc/release/monthly.md
@@ -61,9 +61,9 @@ After making the release branch new commits are cherry-picked from master. When @@ -61,9 +61,9 @@ After making the release branch new commits are cherry-picked from master. When
61 * 1-7th: official merge window (see contributing guide) 61 * 1-7th: official merge window (see contributing guide)
62 * 8-14th: work on bugfixes, sponsored features and GitLab EE 62 * 8-14th: work on bugfixes, sponsored features and GitLab EE
63 * 15th: code freeze (stop merging into master except essential bugfixes) 63 * 15th: code freeze (stop merging into master except essential bugfixes)
64 -* 18th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1, release on GitLab Cloud) 64 +* 18th: release candidate 1 (VERSION x.x.0.rc1, annotated tag and tweet about x.x.0.rc1, release on GitLab Cloud)
65 * 20st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems) 65 * 20st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems)
66 -* 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet) 66 +* 22nd: release (VERSION x.x.0, create x-x-stable branch, annotated tag tag, blog and tweet)
67 * 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems) 67 * 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems)
68 * 24-end of month: release GitLab EE and GitLab CI 68 * 24-end of month: release GitLab EE and GitLab CI
69 69
doc/release/security.md
@@ -18,7 +18,7 @@ Please report suspected security vulnerabilities in private to support@gitlab.co @@ -18,7 +18,7 @@ Please report suspected security vulnerabilities in private to support@gitlab.co
18 1. Create feature branches for the blog post on GitLab.com and link them from the code branch 18 1. Create feature branches for the blog post on GitLab.com and link them from the code branch
19 1. Merge the code feature branch into master 19 1. Merge the code feature branch into master
20 1. Cherry-pick the code into the latest stable branch 20 1. Cherry-pick the code into the latest stable branch
21 -1. Create a git tag vX.X.X for CE and another patch release for EE 21 +1. Create an annotated tag vX.X.X for CE and another patch release for EE
22 1. Push the code and the tags to all the CE and EE repositories 22 1. Push the code and the tags to all the CE and EE repositories
23 1. Apply the patch to GitLab Cloud and the private GitLab development server 23 1. Apply the patch to GitLab Cloud and the private GitLab development server
24 1. Merge and publish the blog posts 24 1. Merge and publish the blog posts
doc/update/6.0-to-6.7.md
@@ -80,6 +80,9 @@ sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production @@ -80,6 +80,9 @@ sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
80 80
81 # Clean up assets and cache 81 # Clean up assets and cache
82 sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production 82 sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
  83 +
  84 +# Close access to gitlab-satellites for others
  85 +sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
83 ``` 86 ```
84 87
85 ### 6. Update config files 88 ### 6. Update config files
doc/update/6.6-to-6.7.md
@@ -63,6 +63,9 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab @@ -63,6 +63,9 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
63 63
64 # Update the logrotate configuration (keep logs for 90 days instead of 52 weeks) 64 # Update the logrotate configuration (keep logs for 90 days instead of 52 weeks)
65 sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab 65 sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
  66 +
  67 +# Close access to gitlab-satellites for others
  68 +sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
66 ``` 69 ```
67 70
68 71
doc/update/upgrader.md
@@ -40,3 +40,10 @@ To make sure you didn&#39;t miss anything run a more thorough check with: @@ -40,3 +40,10 @@ To make sure you didn&#39;t miss anything run a more thorough check with:
40 sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production 40 sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
41 41
42 If all items are green, then congratulations upgrade is complete! 42 If all items are green, then congratulations upgrade is complete!
  43 +
  44 +
  45 +### One line upgrade command
  46 +
  47 +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:
  48 +
  49 + 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
doc/workflow/README.md 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 ++ [Workflow](workflow/workflow.md)
  2 ++ [Project Features](workflow/project_features.md)
doc/workflow/project_features.md 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +When in a Project -> Settings, you will find Features on the bottom of the page that you can toggle.
  2 +Below you will find a more elaborate explanation of each of these.
  3 +
  4 +
  5 +## Issues
  6 +
  7 +Issues is a really powerful, but lightweight issue tracking system.
  8 +You can make tickets, assign them to people, file them under milestones, order them with labels and have discussion in them.
  9 +They integrate deeply into GitLab and are easily referenced from anywhere by using # and the issuenumber.
  10 +At GitLab.com, we use this for all our project management needs.
  11 +
  12 +## Merge Requests
  13 +
  14 +Using a merge request, you can review and discuss code before it is merged in the branch of your code.
  15 +As with issues, it can be assigned; people, issues, etc. can be refereced; milestones attached.
  16 +We see it as an integral part of working together on code and couldn't work without it.
  17 +
  18 +
  19 +## Wiki
  20 +
  21 +This is a separate system for documentation, built right into GitLab.
  22 +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.
  23 +
  24 +
  25 +## Wall
  26 +
  27 +For simple, project specific conversations, the wall can be used.
  28 +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.
  29 +
  30 +
  31 +## Snippets
  32 +
  33 +Snippets are little bits of code or text.
  34 +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.
  35 +For example, a specific config file that is used by > the team that is only valid for the people that work on the code.
features/steps/dashboard/merge_requests.rb
@@ -53,15 +53,15 @@ class DashboardMergeRequests &lt; Spinach::FeatureSteps @@ -53,15 +53,15 @@ class DashboardMergeRequests &lt; Spinach::FeatureSteps
53 end 53 end
54 54
55 def assigned_merge_request 55 def assigned_merge_request
56 - @assigned_merge_request ||= create :merge_request, assignee: current_user, target_project: project 56 + @assigned_merge_request ||= create :merge_request, assignee: current_user, target_project: project, source_project: project
57 end 57 end
58 58
59 def authored_merge_request 59 def authored_merge_request
60 - @authored_merge_request ||= create :merge_request, author: current_user, target_project: project 60 + @authored_merge_request ||= create :merge_request, source_branch: 'simple_merge_request', author: current_user, target_project: project, source_project: project
61 end 61 end
62 62
63 def other_merge_request 63 def other_merge_request
64 - @other_merge_request ||= create :merge_request, target_project: project 64 + @other_merge_request ||= create :merge_request, source_branch: '2_3_notes_fix', target_project: project, source_project: project
65 end 65 end
66 66
67 def project 67 def project
features/support/env.rb
@@ -52,6 +52,4 @@ Spinach.hooks.before_run do @@ -52,6 +52,4 @@ Spinach.hooks.before_run do
52 RSpec::Mocks::setup self 52 RSpec::Mocks::setup self
53 53
54 include FactoryGirl::Syntax::Methods 54 include FactoryGirl::Syntax::Methods
55 - MergeRequestObserver.any_instance.stub(current_user: create(:user))  
56 end 55 end
57 -  
lib/api/internal.rb
@@ -10,6 +10,7 @@ module API @@ -10,6 +10,7 @@ module API
10 # project - project path with namespace 10 # project - project path with namespace
11 # action - git action (git-upload-pack or git-receive-pack) 11 # action - git action (git-upload-pack or git-receive-pack)
12 # ref - branch name 12 # ref - branch name
  13 + # forced_push - forced_push
13 # 14 #
14 get "/allowed" do 15 get "/allowed" do
15 # Check for *.wiki repositories. 16 # Check for *.wiki repositories.
@@ -35,7 +36,8 @@ module API @@ -35,7 +36,8 @@ module API
35 project, 36 project,
36 params[:ref], 37 params[:ref],
37 params[:oldrev], 38 params[:oldrev],
38 - params[:newrev] 39 + params[:newrev],
  40 + params[:forced_push]
39 ) 41 )
40 end 42 end
41 43
lib/api/issues.rb
@@ -48,17 +48,15 @@ module API @@ -48,17 +48,15 @@ module API
48 # Example Request: 48 # Example Request:
49 # POST /projects/:id/issues 49 # POST /projects/:id/issues
50 post ":id/issues" do 50 post ":id/issues" do
51 - set_current_user_for_thread do  
52 - required_attributes! [:title]  
53 - attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]  
54 - attrs[:label_list] = params[:labels] if params[:labels].present?  
55 - @issue = user_project.issues.new attrs  
56 - @issue.author = current_user  
57 - if @issue.save  
58 - present @issue, with: Entities::Issue  
59 - else  
60 - not_found!  
61 - end 51 + required_attributes! [:title]
  52 + attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
  53 + attrs[:label_list] = params[:labels] if params[:labels].present?
  54 + issue = ::Issues::CreateService.new(user_project, current_user, attrs).execute
  55 +
  56 + if issue.valid?
  57 + present issue, with: Entities::Issue
  58 + else
  59 + not_found!
62 end 60 end
63 end 61 end
64 62
@@ -76,18 +74,18 @@ module API @@ -76,18 +74,18 @@ module API
76 # Example Request: 74 # Example Request:
77 # PUT /projects/:id/issues/:issue_id 75 # PUT /projects/:id/issues/:issue_id
78 put ":id/issues/:issue_id" do 76 put ":id/issues/:issue_id" do
79 - set_current_user_for_thread do  
80 - @issue = user_project.issues.find(params[:issue_id])  
81 - authorize! :modify_issue, @issue 77 + issue = user_project.issues.find(params[:issue_id])
  78 + authorize! :modify_issue, issue
  79 +
  80 + attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
  81 + attrs[:label_list] = params[:labels] if params[:labels].present?
82 82
83 - attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]  
84 - attrs[:label_list] = params[:labels] if params[:labels].present? 83 + issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
85 84
86 - if @issue.update_attributes attrs  
87 - present @issue, with: Entities::Issue  
88 - else  
89 - not_found!  
90 - end 85 + if issue.valid?
  86 + present issue, with: Entities::Issue
  87 + else
  88 + not_found!
91 end 89 end
92 end 90 end
93 91
lib/api/merge_requests.rb
@@ -13,14 +13,6 @@ module API @@ -13,14 +13,6 @@ module API
13 end 13 end
14 not_found! 14 not_found!
15 end 15 end
16 -  
17 - def not_fork?(target_project_id, user_project)  
18 - target_project_id.nil? || target_project_id == user_project.id.to_s  
19 - end  
20 -  
21 - def target_matches_fork(target_project_id,user_project)  
22 - user_project.forked? && user_project.forked_from_project.id.to_s == target_project_id  
23 - end  
24 end 16 end
25 17
26 # List merge requests 18 # List merge requests
@@ -70,29 +62,15 @@ module API @@ -70,29 +62,15 @@ module API
70 # POST /projects/:id/merge_requests 62 # POST /projects/:id/merge_requests
71 # 63 #
72 post ":id/merge_requests" do 64 post ":id/merge_requests" do
73 - set_current_user_for_thread do  
74 - authorize! :write_merge_request, user_project  
75 - required_attributes! [:source_branch, :target_branch, :title]  
76 - attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description]  
77 - merge_request = user_project.merge_requests.new(attrs)  
78 - merge_request.author = current_user  
79 - merge_request.source_project = user_project  
80 - target_project_id = attrs[:target_project_id]  
81 - if not_fork?(target_project_id, user_project)  
82 - merge_request.target_project = user_project  
83 - else  
84 - if target_matches_fork(target_project_id,user_project)  
85 - merge_request.target_project = Project.find_by(id: attrs[:target_project_id])  
86 - else  
87 - render_api_error!('(Bad Request) Specified target project that is not the source project, or the source fork of the project.', 400)  
88 - end  
89 - end  
90 -  
91 - if merge_request.save  
92 - present merge_request, with: Entities::MergeRequest  
93 - else  
94 - handle_merge_request_errors! merge_request.errors  
95 - end 65 + authorize! :write_merge_request, user_project
  66 + required_attributes! [:source_branch, :target_branch, :title]
  67 + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description]
  68 + merge_request = ::MergeRequests::CreateService.new(user_project, current_user, attrs).execute
  69 +
  70 + if merge_request.valid?
  71 + present merge_request, with: Entities::MergeRequest
  72 + else
  73 + handle_merge_request_errors! merge_request.errors
96 end 74 end
97 end 75 end
98 76
@@ -111,17 +89,15 @@ module API @@ -111,17 +89,15 @@ module API
111 # PUT /projects/:id/merge_request/:merge_request_id 89 # PUT /projects/:id/merge_request/:merge_request_id
112 # 90 #
113 put ":id/merge_request/:merge_request_id" do 91 put ":id/merge_request/:merge_request_id" do
114 - set_current_user_for_thread do  
115 - attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description]  
116 - merge_request = user_project.merge_requests.find(params[:merge_request_id])  
117 -  
118 - authorize! :modify_merge_request, merge_request 92 + attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :state_event, :description]
  93 + merge_request = user_project.merge_requests.find(params[:merge_request_id])
  94 + authorize! :modify_merge_request, merge_request
  95 + merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request)
119 96
120 - if merge_request.update_attributes attrs  
121 - present merge_request, with: Entities::MergeRequest  
122 - else  
123 - handle_merge_request_errors! merge_request.errors  
124 - end 97 + if merge_request.valid?
  98 + present merge_request, with: Entities::MergeRequest
  99 + else
  100 + handle_merge_request_errors! merge_request.errors
125 end 101 end
126 end 102 end
127 103
lib/gitlab/git_access.rb
@@ -5,7 +5,7 @@ module Gitlab @@ -5,7 +5,7 @@ module Gitlab
5 5
6 attr_reader :params, :project, :git_cmd, :user 6 attr_reader :params, :project, :git_cmd, :user
7 7
8 - def allowed?(actor, cmd, project, ref = nil, oldrev = nil, newrev = nil) 8 + def allowed?(actor, cmd, project, ref = nil, oldrev = nil, newrev = nil, forced_push = false)
9 case cmd 9 case cmd
10 when *DOWNLOAD_COMMANDS 10 when *DOWNLOAD_COMMANDS
11 if actor.is_a? User 11 if actor.is_a? User
@@ -19,12 +19,12 @@ module Gitlab @@ -19,12 +19,12 @@ module Gitlab
19 end 19 end
20 when *PUSH_COMMANDS 20 when *PUSH_COMMANDS
21 if actor.is_a? User 21 if actor.is_a? User
22 - push_allowed?(actor, project, ref, oldrev, newrev) 22 + push_allowed?(actor, project, ref, oldrev, newrev, forced_push)
23 elsif actor.is_a? DeployKey 23 elsif actor.is_a? DeployKey
24 # Deploy key not allowed to push 24 # Deploy key not allowed to push
25 return false 25 return false
26 elsif actor.is_a? Key 26 elsif actor.is_a? Key
27 - push_allowed?(actor.user, project, ref, oldrev, newrev) 27 + push_allowed?(actor.user, project, ref, oldrev, newrev, forced_push)
28 else 28 else
29 raise 'Wrong actor' 29 raise 'Wrong actor'
30 end 30 end
@@ -41,13 +41,17 @@ module Gitlab @@ -41,13 +41,17 @@ module Gitlab
41 end 41 end
42 end 42 end
43 43
44 - def push_allowed?(user, project, ref, oldrev, newrev) 44 + def push_allowed?(user, project, ref, oldrev, newrev, forced_push)
45 if user && user_allowed?(user) 45 if user && user_allowed?(user)
46 action = if project.protected_branch?(ref) 46 action = if project.protected_branch?(ref)
47 - :push_code_to_protected_branches  
48 - else  
49 - :push_code  
50 - end 47 + if forced_push.to_s == 'true'
  48 + :force_push_code_to_protected_branches
  49 + else
  50 + :push_code_to_protected_branches
  51 + end
  52 + else
  53 + :push_code
  54 + end
51 user.can?(action, project) 55 user.can?(action, project)
52 else 56 else
53 false 57 false
lib/tasks/gitlab/check.rake
@@ -342,6 +342,7 @@ namespace :gitlab do @@ -342,6 +342,7 @@ namespace :gitlab do
342 check_repo_base_is_not_symlink 342 check_repo_base_is_not_symlink
343 check_repo_base_user_and_group 343 check_repo_base_user_and_group
344 check_repo_base_permissions 344 check_repo_base_permissions
  345 + check_satellites_permissions
345 check_update_hook_is_up_to_date 346 check_update_hook_is_up_to_date
346 check_repos_update_hooks_is_link 347 check_repos_update_hooks_is_link
347 check_gitlab_shell_self_test 348 check_gitlab_shell_self_test
@@ -443,6 +444,29 @@ namespace :gitlab do @@ -443,6 +444,29 @@ namespace :gitlab do
443 end 444 end
444 end 445 end
445 446
  447 + def check_satellites_permissions
  448 + print "Satellites access is drwxr-x---? ... "
  449 +
  450 + satellites_path = Gitlab.config.satellites.path
  451 + unless File.exists?(satellites_path)
  452 + puts "can't check because of previous errors".magenta
  453 + return
  454 + end
  455 +
  456 + if File.stat(satellites_path).mode.to_s(8).ends_with?("0750")
  457 + puts "yes".green
  458 + else
  459 + puts "no".red
  460 + try_fixing_it(
  461 + "sudo chmod u+rwx,g+rx,o-rwx #{satellites_path}",
  462 + )
  463 + for_more_information(
  464 + see_installation_guide_section "GitLab"
  465 + )
  466 + fix_and_rerun
  467 + end
  468 + end
  469 +
446 def check_repo_base_user_and_group 470 def check_repo_base_user_and_group
447 gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user 471 gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user
448 gitlab_shell_owner_group = Gitlab.config.gitlab_shell.owner_group 472 gitlab_shell_owner_group = Gitlab.config.gitlab_shell.owner_group
lib/tasks/gitlab/info.rake
@@ -24,6 +24,7 @@ namespace :gitlab do @@ -24,6 +24,7 @@ namespace :gitlab do
24 puts "Gem Version:\t#{gem_version || "unknown".red}" 24 puts "Gem Version:\t#{gem_version || "unknown".red}"
25 puts "Bundler Version:#{bunder_version || "unknown".red}" 25 puts "Bundler Version:#{bunder_version || "unknown".red}"
26 puts "Rake Version:\t#{rake_version || "unknown".red}" 26 puts "Rake Version:\t#{rake_version || "unknown".red}"
  27 + puts "Sidekiq Version:#{Sidekiq::VERSION}"
27 28
28 29
29 # check database adapter 30 # check database adapter
script/background_jobs
@@ -37,7 +37,7 @@ function start_no_deamonize @@ -37,7 +37,7 @@ function start_no_deamonize
37 37
38 function start_sidekiq 38 function start_sidekiq
39 { 39 {
40 - 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 40 + 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
41 } 41 }
42 42
43 function load_ok 43 function load_ok
spec/finders/merge_requests_finder_spec.rb
@@ -5,10 +5,10 @@ describe MergeRequestsFinder do @@ -5,10 +5,10 @@ describe MergeRequestsFinder do
5 let(:user2) { create :user } 5 let(:user2) { create :user }
6 6
7 let(:project1) { create(:project) } 7 let(:project1) { create(:project) }
8 - let(:project2) { create(:project) } 8 + let(:project2) { create(:project, forked_from_project: project1) }
9 9
10 - let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2) }  
11 - let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) } 10 + let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
  11 + let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1, state: 'closed') }
12 let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) } 12 let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) }
13 13
14 before do 14 before do
@@ -21,7 +21,7 @@ describe MergeRequestsFinder do @@ -21,7 +21,7 @@ describe MergeRequestsFinder do
21 it 'should filter by scope' do 21 it 'should filter by scope' do
22 params = { scope: 'authored', state: 'opened' } 22 params = { scope: 'authored', state: 'opened' }
23 merge_requests = MergeRequestsFinder.new.execute(user, params) 23 merge_requests = MergeRequestsFinder.new.execute(user, params)
24 - merge_requests.size.should == 3 24 + merge_requests.size.should == 2
25 end 25 end
26 26
27 it 'should filter by project' do 27 it 'should filter by project' do
spec/lib/gitlab/satellite/merge_action_spec.rb
@@ -13,7 +13,7 @@ describe &#39;Gitlab::Satellite::MergeAction&#39; do @@ -13,7 +13,7 @@ describe &#39;Gitlab::Satellite::MergeAction&#39; do
13 end 13 end
14 14
15 let(:project) { create(:project, namespace: create(:group)) } 15 let(:project) { create(:project, namespace: create(:group)) }
16 - let(:fork_project) { create(:project, namespace: create(:group)) } 16 + let(:fork_project) { create(:project, namespace: create(:group), forked_from_project: project) }
17 let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } 17 let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
18 let(:merge_request_fork) { create(:merge_request, source_project: fork_project, target_project: project) } 18 let(:merge_request_fork) { create(:merge_request, source_project: fork_project, target_project: project) }
19 19
spec/models/key_spec.rb
@@ -68,4 +68,18 @@ describe Key do @@ -68,4 +68,18 @@ describe Key do
68 build(:invalid_key).should_not be_valid 68 build(:invalid_key).should_not be_valid
69 end 69 end
70 end 70 end
  71 +
  72 + context 'callbacks' do
  73 + it 'should add new key to authorized_file' do
  74 + @key = build(:personal_key, id: 7)
  75 + GitlabShellWorker.should_receive(:perform_async).with(:add_key, @key.shell_id, @key.key)
  76 + @key.save
  77 + end
  78 +
  79 + it 'should remove key from authorized_file' do
  80 + @key = create(:personal_key)
  81 + GitlabShellWorker.should_receive(:perform_async).with(:remove_key, @key.shell_id, @key.key)
  82 + @key.destroy
  83 + end
  84 + end
71 end 85 end
spec/models/note_spec.rb
@@ -209,7 +209,7 @@ describe Note do @@ -209,7 +209,7 @@ describe Note do
209 let(:project) { create(:project) } 209 let(:project) { create(:project) }
210 let(:author) { create(:user) } 210 let(:author) { create(:user) }
211 let(:issue) { create(:issue, project: project) } 211 let(:issue) { create(:issue, project: project) }
212 - let(:mergereq) { create(:merge_request, target_project: project) } 212 + let(:mergereq) { create(:merge_request, :simple, target_project: project, source_project: project) }
213 let(:commit) { project.repository.commit } 213 let(:commit) { project.repository.commit }
214 214
215 # Test all of {issue, merge request, commit} in both the referenced and referencing 215 # Test all of {issue, merge request, commit} in both the referenced and referencing
spec/observers/email_observer_spec.rb
@@ -1,17 +0,0 @@ @@ -1,17 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe EmailObserver do  
4 - let(:email) { create(:email) }  
5 -  
6 - before { subject.stub(notification: double('NotificationService').as_null_object) }  
7 -  
8 - subject { EmailObserver.instance }  
9 -  
10 - describe '#after_create' do  
11 - it 'trigger notification to send emails' do  
12 - subject.should_receive(:notification)  
13 -  
14 - subject.after_create(email)  
15 - end  
16 - end  
17 -end  
spec/observers/issue_observer_spec.rb
@@ -1,99 +0,0 @@ @@ -1,99 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe IssueObserver do  
4 - let(:some_user) { create :user }  
5 - let(:assignee) { create :user }  
6 - let(:author) { create :user }  
7 - let(:mock_issue) { create(:issue, assignee: assignee, author: author) }  
8 -  
9 -  
10 - before { subject.stub(:current_user).and_return(some_user) }  
11 - before { subject.stub(:current_commit).and_return(nil) }  
12 - before { subject.stub(notification: double('NotificationService').as_null_object) }  
13 - before { mock_issue.project.stub_chain(:repository, :commit).and_return(nil) }  
14 -  
15 - subject { IssueObserver.instance }  
16 -  
17 - describe '#after_create' do  
18 - it 'trigger notification to send emails' do  
19 - subject.should_receive(:notification)  
20 -  
21 - subject.after_create(mock_issue)  
22 - end  
23 -  
24 - it 'should create cross-reference notes' do  
25 - other_issue = create(:issue)  
26 - mock_issue.stub(references: [other_issue])  
27 -  
28 - Note.should_receive(:create_cross_reference_note).with(other_issue, mock_issue,  
29 - some_user, mock_issue.project)  
30 - subject.after_create(mock_issue)  
31 - end  
32 - end  
33 -  
34 - context '#after_close' do  
35 - context 'a status "closed"' do  
36 - before { mock_issue.stub(state: 'closed') }  
37 -  
38 - it 'note is created if the issue is being closed' do  
39 - Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'closed', nil)  
40 -  
41 - subject.after_close(mock_issue, nil)  
42 - end  
43 -  
44 - it 'trigger notification to send emails' do  
45 - subject.notification.should_receive(:close_issue).with(mock_issue, some_user)  
46 - subject.after_close(mock_issue, nil)  
47 - end  
48 -  
49 - it 'appends a mention to the closing commit if one is present' do  
50 - commit = double('commit', gfm_reference: 'commit 123456')  
51 - subject.stub(current_commit: commit)  
52 -  
53 - Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'closed', commit)  
54 -  
55 - subject.after_close(mock_issue, nil)  
56 - end  
57 - end  
58 -  
59 - context 'a status "reopened"' do  
60 - before { mock_issue.stub(state: 'reopened') }  
61 -  
62 - it 'note is created if the issue is being reopened' do  
63 - Note.should_receive(:create_status_change_note).with(mock_issue, mock_issue.project, some_user, 'reopened', nil)  
64 -  
65 - subject.after_reopen(mock_issue, nil)  
66 - end  
67 - end  
68 - end  
69 -  
70 - context '#after_update' do  
71 - before(:each) do  
72 - mock_issue.stub(:is_being_reassigned?).and_return(false)  
73 - end  
74 -  
75 - context 'notification' do  
76 - it 'triggered if the issue is being reassigned' do  
77 - mock_issue.should_receive(:is_being_reassigned?).and_return(true)  
78 - subject.should_receive(:notification)  
79 -  
80 - subject.after_update(mock_issue)  
81 - end  
82 -  
83 - it 'is not triggered if the issue is not being reassigned' do  
84 - mock_issue.should_receive(:is_being_reassigned?).and_return(false)  
85 - subject.should_not_receive(:notification)  
86 -  
87 - subject.after_update(mock_issue)  
88 - end  
89 - end  
90 -  
91 - context 'cross-references' do  
92 - it 'notices added references' do  
93 - mock_issue.should_receive(:notice_added_references)  
94 -  
95 - subject.after_update(mock_issue)  
96 - end  
97 - end  
98 - end  
99 -end  
spec/observers/key_observer_spec.rb
@@ -1,23 +0,0 @@ @@ -1,23 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe KeyObserver do  
4 - before do  
5 - @key = create(:personal_key)  
6 -  
7 - @observer = KeyObserver.instance  
8 - end  
9 -  
10 - context :after_create do  
11 - it do  
12 - GitlabShellWorker.should_receive(:perform_async).with(:add_key, @key.shell_id, @key.key)  
13 - @observer.after_create(@key)  
14 - end  
15 - end  
16 -  
17 - context :after_destroy do  
18 - it do  
19 - GitlabShellWorker.should_receive(:perform_async).with(:remove_key, @key.shell_id, @key.key)  
20 - @observer.after_destroy(@key)  
21 - end  
22 - end  
23 -end  
spec/observers/merge_request_observer_spec.rb
@@ -1,131 +0,0 @@ @@ -1,131 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe MergeRequestObserver do  
4 - let(:some_user) { create :user }  
5 - let(:assignee) { create :user }  
6 - let(:author) { create :user }  
7 - let(:project) { create :project }  
8 - let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author).as_null_object }  
9 - let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author, source_project: project) }  
10 - let(:unassigned_mr) { create(:merge_request, author: author, source_project: project) }  
11 - let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author, source_project: project) }  
12 - let(:closed_unassigned_mr) { create(:closed_merge_request, author: author, source_project: project) }  
13 -  
14 - before { subject.stub(:current_user).and_return(some_user) }  
15 - before { subject.stub(notification: double('NotificationService').as_null_object) }  
16 - before { mr_mock.stub(:author_id) }  
17 - before { mr_mock.stub(:source_project) }  
18 - before { mr_mock.stub(:source_project) }  
19 - before { mr_mock.stub(:project) }  
20 - before { mr_mock.stub(:create_cross_references!).and_return(true) }  
21 - before { Repository.any_instance.stub(commit: nil) }  
22 -  
23 - before(:each) { enable_observers }  
24 - after(:each) { disable_observers }  
25 -  
26 - subject { MergeRequestObserver.instance }  
27 -  
28 - describe '#after_create' do  
29 - it 'trigger notification service' do  
30 - subject.should_receive(:notification)  
31 - subject.after_create(mr_mock)  
32 - end  
33 -  
34 - it 'creates cross-reference notes' do  
35 - project = create :project  
36 - mr_mock.stub(title: "this mr references !#{assigned_mr.id}", project: project)  
37 - mr_mock.should_receive(:create_cross_references!).with(project, some_user)  
38 -  
39 - subject.after_create(mr_mock)  
40 - end  
41 - end  
42 -  
43 - context '#after_update' do  
44 - before(:each) do  
45 - mr_mock.stub(:is_being_reassigned?).and_return(false)  
46 - mr_mock.stub(:notice_added_references)  
47 - end  
48 -  
49 - it 'is called when a merge request is changed' do  
50 - changed = create(:merge_request, source_project: project)  
51 - subject.should_receive(:after_update)  
52 -  
53 - MergeRequest.observers.enable :merge_request_observer do  
54 - changed.title = 'I changed'  
55 - changed.save  
56 - end  
57 - end  
58 -  
59 - it 'checks for new references' do  
60 - mr_mock.should_receive(:notice_added_references)  
61 -  
62 - subject.after_update(mr_mock)  
63 - end  
64 -  
65 - context 'a notification' do  
66 - it 'is sent if the merge request is being reassigned' do  
67 - mr_mock.should_receive(:is_being_reassigned?).and_return(true)  
68 - subject.should_receive(:notification)  
69 -  
70 - subject.after_update(mr_mock)  
71 - end  
72 -  
73 - it 'is not sent if the merge request is not being reassigned' do  
74 - mr_mock.should_receive(:is_being_reassigned?).and_return(false)  
75 - subject.should_not_receive(:notification)  
76 -  
77 - subject.after_update(mr_mock)  
78 - end  
79 - end  
80 - end  
81 -  
82 - context '#after_close' do  
83 - context 'a status "closed"' do  
84 - it 'note is created if the merge request is being closed' do  
85 - Note.should_receive(:create_status_change_note).with(assigned_mr, assigned_mr.source_project, some_user, 'closed', nil)  
86 -  
87 - assigned_mr.close  
88 - end  
89 -  
90 - it 'notification is delivered only to author if the merge request is being closed' do  
91 - Note.should_receive(:create_status_change_note).with(unassigned_mr, unassigned_mr.source_project, some_user, 'closed', nil)  
92 -  
93 - unassigned_mr.close  
94 - end  
95 - end  
96 - end  
97 -  
98 - context '#after_reopen' do  
99 - context 'a status "reopened"' do  
100 - it 'note is created if the merge request is being reopened' do  
101 - Note.should_receive(:create_status_change_note).with(closed_assigned_mr, closed_assigned_mr.source_project, some_user, 'reopened', nil)  
102 -  
103 - closed_assigned_mr.reopen  
104 - end  
105 -  
106 - it 'notification is delivered only to author if the merge request is being reopened' do  
107 - Note.should_receive(:create_status_change_note).with(closed_unassigned_mr, closed_unassigned_mr.source_project, some_user, 'reopened', nil)  
108 -  
109 - closed_unassigned_mr.reopen  
110 - end  
111 - end  
112 - end  
113 -  
114 - describe "Merge Request created" do  
115 - def self.it_should_be_valid_event  
116 - it { @event.should_not be_nil }  
117 - it { @event.should_not be_nil }  
118 - it { @event.project.should == project }  
119 - it { @event.project.should == project }  
120 - end  
121 -  
122 - before do  
123 - @merge_request = create(:merge_request, source_project: project, target_project: project)  
124 - @event = Event.last  
125 - end  
126 -  
127 - it_should_be_valid_event  
128 - it { @event.action.should == Event::CREATED }  
129 - it { @event.target.should == @merge_request }  
130 - end  
131 -end  
spec/observers/users_project_observer_spec.rb
@@ -21,7 +21,7 @@ describe UsersProjectObserver do @@ -21,7 +21,7 @@ describe UsersProjectObserver do
21 21
22 it "should send email to user" do 22 it "should send email to user" do
23 subject.should_receive(:notification) 23 subject.should_receive(:notification)
24 - @users_project.update_attribute(:project_access, UsersProject::MASTER) 24 + @users_project.update_attribute(:project_access, UsersProject::OWNER)
25 end 25 end
26 26
27 it "should not called after UsersProject destroyed" do 27 it "should not called after UsersProject destroyed" do
spec/requests/api/merge_requests_spec.rb
@@ -6,7 +6,7 @@ describe API::API do @@ -6,7 +6,7 @@ describe API::API do
6 after(:each) { ActiveRecord::Base.observers.disable(:user_observer) } 6 after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
7 let(:user) { create(:user) } 7 let(:user) { create(:user) }
8 let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) } 8 let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
9 - let!(:merge_request) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "Test") } 9 + let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
10 let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") } 10 let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
11 before { 11 before {
12 project.team << [user, :reporters] 12 project.team << [user, :reporters]
@@ -79,16 +79,12 @@ describe API::API do @@ -79,16 +79,12 @@ describe API::API do
79 end 79 end
80 80
81 context 'forked projects' do 81 context 'forked projects' do
82 - let!(:user2) {create(:user)}  
83 - let!(:forked_project_link) { build(:forked_project_link) }  
84 - let!(:fork_project) { create(:project, forked_project_link: forked_project_link, namespace: user2.namespace, creator_id: user2.id) }  
85 - let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) } 82 + let!(:user2) { create(:user) }
  83 + let!(:fork_project) { create(:project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
  84 + let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) }
86 85
87 before :each do |each| 86 before :each do |each|
88 fork_project.team << [user2, :reporters] 87 fork_project.team << [user2, :reporters]
89 - forked_project_link.forked_from_project = project  
90 - forked_project_link.forked_to_project = fork_project  
91 - forked_project_link.save!  
92 end 88 end
93 89
94 it "should return merge_request" do 90 it "should return merge_request" do
@@ -127,16 +123,16 @@ describe API::API do @@ -127,16 +123,16 @@ describe API::API do
127 response.status.should == 400 123 response.status.should == 400
128 end 124 end
129 125
130 - it "should return 400 when target_branch is specified and not a forked project" do 126 + it "should return 404 when target_branch is specified and not a forked project" do
131 post api("/projects/#{project.id}/merge_requests", user), 127 post api("/projects/#{project.id}/merge_requests", user),
132 title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user, target_project_id: fork_project.id 128 title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user, target_project_id: fork_project.id
133 - response.status.should == 400 129 + response.status.should == 404
134 end 130 end
135 131
136 - it "should return 400 when target_branch is specified and for a different fork" do 132 + it "should return 404 when target_branch is specified and for a different fork" do
137 post api("/projects/#{fork_project.id}/merge_requests", user2), 133 post api("/projects/#{fork_project.id}/merge_requests", user2),
138 title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: unrelated_project.id 134 title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: unrelated_project.id
139 - response.status.should == 400 135 + response.status.should == 404
140 end 136 end
141 137
142 it "should return 201 when target_branch is specified and for the same project" do 138 it "should return 201 when target_branch is specified and for the same project" do
spec/services/git_push_service_spec.rb
@@ -170,16 +170,10 @@ describe GitPushService do @@ -170,16 +170,10 @@ describe GitPushService do
170 Issue.find(issue.id).should be_closed 170 Issue.find(issue.id).should be_closed
171 end 171 end
172 172
173 - it "passes the closing commit as a thread-local" do  
174 - service.execute(project, user, @oldrev, @newrev, @ref)  
175 -  
176 - Thread.current[:current_commit].should == closing_commit  
177 - end  
178 -  
179 it "doesn't create cross-reference notes for a closing reference" do 173 it "doesn't create cross-reference notes for a closing reference" do
180 expect { 174 expect {
181 service.execute(project, user, @oldrev, @newrev, @ref) 175 service.execute(project, user, @oldrev, @newrev, @ref)
182 - }.not_to change { Note.where(project_id: project.id, system: true).count } 176 + }.not_to change { Note.where(project_id: project.id, system: true, commit_id: closing_commit.id).count }
183 end 177 end
184 178
185 it "doesn't close issues when pushed to non-default branches" do 179 it "doesn't close issues when pushed to non-default branches" do
spec/services/issues/close_service_spec.rb 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Issues::CloseService do
  4 + let(:project) { create(:empty_project) }
  5 + let(:user) { create(:user) }
  6 + let(:user2) { create(:user) }
  7 + let(:issue) { create(:issue, assignee: user2) }
  8 +
  9 + before do
  10 + project.team << [user, :master]
  11 + project.team << [user2, :developer]
  12 + end
  13 +
  14 + describe :execute do
  15 + context "valid params" do
  16 + before do
  17 + @issue = Issues::CloseService.new(project, user, {}).execute(issue)
  18 + end
  19 +
  20 + it { @issue.should be_valid }
  21 + it { @issue.should be_closed }
  22 +
  23 + it 'should send email to user2 about assign of new issue' do
  24 + email = ActionMailer::Base.deliveries.last
  25 + email.to.first.should == user2.email
  26 + email.subject.should include(issue.title)
  27 + end
  28 +
  29 + it 'should create system note about issue reassign' do
  30 + note = @issue.notes.last
  31 + note.note.should include "Status changed to closed"
  32 + end
  33 + end
  34 + end
  35 +end
spec/services/issues/create_service_spec.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Issues::CreateService do
  4 + let(:project) { create(:empty_project) }
  5 + let(:user) { create(:user) }
  6 +
  7 + describe :execute do
  8 + context "valid params" do
  9 + before do
  10 + project.team << [user, :master]
  11 + opts = {
  12 + title: 'Awesome issue',
  13 + description: 'please fix'
  14 + }
  15 +
  16 + @issue = Issues::CreateService.new(project, user, opts).execute
  17 + end
  18 +
  19 + it { @issue.should be_valid }
  20 + it { @issue.title.should == 'Awesome issue' }
  21 + end
  22 + end
  23 +end
spec/services/issues/update_service_spec.rb 0 → 100644
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Issues::UpdateService do
  4 + let(:project) { create(:empty_project) }
  5 + let(:user) { create(:user) }
  6 + let(:user2) { create(:user) }
  7 + let(:issue) { create(:issue) }
  8 +
  9 + before do
  10 + project.team << [user, :master]
  11 + project.team << [user2, :developer]
  12 + end
  13 +
  14 + describe :execute do
  15 + context "valid params" do
  16 + before do
  17 + opts = {
  18 + title: 'New title',
  19 + description: 'Also please fix',
  20 + assignee_id: user2.id,
  21 + state_event: 'close'
  22 + }
  23 +
  24 + @issue = Issues::UpdateService.new(project, user, opts).execute(issue)
  25 + end
  26 +
  27 + it { @issue.should be_valid }
  28 + it { @issue.title.should == 'New title' }
  29 + it { @issue.assignee.should == user2 }
  30 + it { @issue.should be_closed }
  31 +
  32 + it 'should send email to user2 about assign of new issue' do
  33 + email = ActionMailer::Base.deliveries.last
  34 + email.to.first.should == user2.email
  35 + email.subject.should include(issue.title)
  36 + end
  37 +
  38 + it 'should create system note about issue reassign' do
  39 + note = @issue.notes.last
  40 + note.note.should include "Reassigned to \@#{user2.username}"
  41 + end
  42 + end
  43 + end
  44 +end
spec/services/merge_requests/close_service_spec.rb 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +require 'spec_helper'
  2 +
  3 +describe MergeRequests::CloseService do
  4 + let(:user) { create(:user) }
  5 + let(:user2) { create(:user) }
  6 + let(:merge_request) { create(:merge_request, assignee: user2) }
  7 + let(:project) { merge_request.project }
  8 +
  9 + before do
  10 + project.team << [user, :master]
  11 + project.team << [user2, :developer]
  12 + end
  13 +
  14 + describe :execute do
  15 + context "valid params" do
  16 + before do
  17 + @merge_request = MergeRequests::CloseService.new(project, user, {}).execute(merge_request)
  18 + end
  19 +
  20 + it { @merge_request.should be_valid }
  21 + it { @merge_request.should be_closed }
  22 +
  23 + it 'should send email to user2 about assign of new merge_request' do
  24 + email = ActionMailer::Base.deliveries.last
  25 + email.to.first.should == user2.email
  26 + email.subject.should include(merge_request.title)
  27 + end
  28 +
  29 + it 'should create system note about merge_request reassign' do
  30 + note = @merge_request.notes.last
  31 + note.note.should include "Status changed to closed"
  32 + end
  33 + end
  34 + end
  35 +end
spec/services/merge_requests/create_service_spec.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +require 'spec_helper'
  2 +
  3 +describe MergeRequests::CreateService do
  4 + let(:project) { create(:project) }
  5 + let(:user) { create(:user) }
  6 +
  7 + describe :execute do
  8 + context "valid params" do
  9 + before do
  10 + project.team << [user, :master]
  11 + opts = {
  12 + title: 'Awesome merge_request',
  13 + description: 'please fix',
  14 + source_branch: 'stable',
  15 + target_branch: 'master'
  16 + }
  17 +
  18 + @merge_request = MergeRequests::CreateService.new(project, user, opts).execute
  19 + end
  20 +
  21 + it { @merge_request.should be_valid }
  22 + it { @merge_request.title.should == 'Awesome merge_request' }
  23 + end
  24 + end
  25 +end
spec/services/merge_requests/update_service_spec.rb 0 → 100644
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
  1 +require 'spec_helper'
  2 +
  3 +describe MergeRequests::UpdateService do
  4 + let(:user) { create(:user) }
  5 + let(:user2) { create(:user) }
  6 + let(:merge_request) { create(:merge_request, :simple) }
  7 + let(:project) { merge_request.project }
  8 +
  9 + before do
  10 + project.team << [user, :master]
  11 + project.team << [user2, :developer]
  12 + end
  13 +
  14 + describe :execute do
  15 + context "valid params" do
  16 + before do
  17 + opts = {
  18 + title: 'New title',
  19 + description: 'Also please fix',
  20 + assignee_id: user2.id,
  21 + state_event: 'close'
  22 + }
  23 +
  24 + @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
  25 + end
  26 +
  27 + it { @merge_request.should be_valid }
  28 + it { @merge_request.title.should == 'New title' }
  29 + it { @merge_request.assignee.should == user2 }
  30 + it { @merge_request.should be_closed }
  31 +
  32 + it 'should send email to user2 about assign of new merge_request' do
  33 + email = ActionMailer::Base.deliveries.last
  34 + email.to.first.should == user2.email
  35 + email.subject.should include(merge_request.title)
  36 + end
  37 +
  38 + it 'should create system note about merge_request reassign' do
  39 + note = @merge_request.notes.last
  40 + note.note.should include "Reassigned to \@#{user2.username}"
  41 + end
  42 + end
  43 + end
  44 +end
spec/services/notification_service_spec.rb
@@ -5,7 +5,7 @@ describe NotificationService do @@ -5,7 +5,7 @@ describe NotificationService do
5 5
6 describe 'Keys' do 6 describe 'Keys' do
7 describe :new_key do 7 describe :new_key do
8 - let(:key) { create(:personal_key) } 8 + let!(:key) { create(:personal_key) }
9 9
10 it { notification.new_key(key).should be_true } 10 it { notification.new_key(key).should be_true }
11 11
@@ -18,7 +18,7 @@ describe NotificationService do @@ -18,7 +18,7 @@ describe NotificationService do
18 18
19 describe 'Email' do 19 describe 'Email' do
20 describe :new_email do 20 describe :new_email do
21 - let(:email) { create(:email) } 21 + let!(:email) { create(:email) }
22 22
23 it { notification.new_email(email).should be_true } 23 it { notification.new_email(email).should be_true }
24 24
@@ -57,15 +57,42 @@ describe NotificationService do @@ -57,15 +57,42 @@ describe NotificationService do
57 Notify.should_not_receive(:note_issue_email) 57 Notify.should_not_receive(:note_issue_email)
58 notification.new_note(mentioned_note) 58 notification.new_note(mentioned_note)
59 end 59 end
  60 + end
60 61
61 - def should_email(user_id)  
62 - Notify.should_receive(:note_issue_email).with(user_id, note.id) 62 + describe 'new note on issue in project that belongs to a group' do
  63 + let(:group) { create(:group) }
  64 +
  65 + before do
  66 + note.project.namespace_id = group.id
  67 + note.project.group.add_user(@u_watcher, UsersGroup::MASTER)
  68 + note.project.save
  69 + user_project = note.project.users_projects.find_by_user_id(@u_watcher.id)
  70 + user_project.notification_level = Notification::N_PARTICIPATING
  71 + user_project.save
  72 + user_group = note.project.group.users_groups.find_by_user_id(@u_watcher.id)
  73 + user_group.notification_level = Notification::N_GLOBAL
  74 + user_group.save
63 end 75 end
64 76
65 - def should_not_email(user_id)  
66 - Notify.should_not_receive(:note_issue_email).with(user_id, note.id) 77 + it do
  78 + should_email(note.noteable.author_id)
  79 + should_email(note.noteable.assignee_id)
  80 + should_email(@u_mentioned.id)
  81 + should_not_email(@u_watcher.id)
  82 + should_not_email(note.author_id)
  83 + should_not_email(@u_participating.id)
  84 + should_not_email(@u_disabled.id)
  85 + notification.new_note(note)
67 end 86 end
68 end 87 end
  88 +
  89 + def should_email(user_id)
  90 + Notify.should_receive(:note_issue_email).with(user_id, note.id)
  91 + end
  92 +
  93 + def should_not_email(user_id)
  94 + Notify.should_not_receive(:note_issue_email).with(user_id, note.id)
  95 + end
69 end 96 end
70 97
71 context 'commit note' do 98 context 'commit note' do