Commit eefb27f5ae0edf0c005eb8ce6da56cbd17c9aa8a

Authored by Sebastian Ziebell
2 parents 1b97a2ee b7ac654b

Merge branch 'master' into fixes/api

Conflicts:
	spec/requests/api/projects_spec.rb
Showing 135 changed files with 1141 additions and 884 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 135 files displayed.

@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 .rbx/ 2 .rbx/
3 db/*.sqlite3 3 db/*.sqlite3
4 db/*.sqlite3-journal 4 db/*.sqlite3-journal
5 -log/*.log 5 +log/*.log*
6 tmp/ 6 tmp/
7 .sass-cache/ 7 .sass-cache/
8 coverage/* 8 coverage/*
@@ -20,6 +20,7 @@ config/database.yml @@ -20,6 +20,7 @@ config/database.yml
20 config/initializers/omniauth.rb 20 config/initializers/omniauth.rb
21 config/unicorn.rb 21 config/unicorn.rb
22 config/resque.yml 22 config/resque.yml
  23 +config/aws.yml
23 db/data.yml 24 db/data.yml
24 .idea 25 .idea
25 .DS_Store 26 .DS_Store
@@ -70,6 +70,9 @@ gem "github-markup", "~> 0.7.4", require: 'github/markup' @@ -70,6 +70,9 @@ gem "github-markup", "~> 0.7.4", require: 'github/markup'
70 # Servers 70 # Servers
71 gem "unicorn", "~> 4.4.0" 71 gem "unicorn", "~> 4.4.0"
72 72
  73 +# State machine
  74 +gem "state_machine"
  75 +
73 # Issue tags 76 # Issue tags
74 gem "acts-as-taggable-on", "2.3.3" 77 gem "acts-as-taggable-on", "2.3.3"
75 78
@@ -425,6 +425,7 @@ GEM @@ -425,6 +425,7 @@ GEM
425 rack (~> 1.0) 425 rack (~> 1.0)
426 tilt (~> 1.1, != 1.3.0) 426 tilt (~> 1.1, != 1.3.0)
427 stamp (0.3.0) 427 stamp (0.3.0)
  428 + state_machine (1.1.2)
428 temple (0.5.5) 429 temple (0.5.5)
429 test_after_commit (0.0.1) 430 test_after_commit (0.0.1)
430 therubyracer (0.10.2) 431 therubyracer (0.10.2)
@@ -536,6 +537,7 @@ DEPENDENCIES @@ -536,6 +537,7 @@ DEPENDENCIES
536 slim 537 slim
537 spinach-rails 538 spinach-rails
538 stamp 539 stamp
  540 + state_machine
539 test_after_commit 541 test_after_commit
540 therubyracer 542 therubyracer
541 thin 543 thin
app/assets/javascripts/main.js.coffee
@@ -49,6 +49,10 @@ $ -> @@ -49,6 +49,10 @@ $ ->
49 # Bottom tooltip 49 # Bottom tooltip
50 $('.has_bottom_tooltip').tooltip(placement: 'bottom') 50 $('.has_bottom_tooltip').tooltip(placement: 'bottom')
51 51
  52 + # Form submitter
  53 + $('.trigger-submit').on 'change', ->
  54 + $(@).parents('form').submit()
  55 +
52 # Flash 56 # Flash
53 if (flash = $("#flash-container")).length > 0 57 if (flash = $("#flash-container")).length > 0
54 flash.click -> $(@).slideUp("slow") 58 flash.click -> $(@).slideUp("slow")
app/assets/javascripts/merge_requests.js.coffee
@@ -27,7 +27,7 @@ class MergeRequest @@ -27,7 +27,7 @@ class MergeRequest
27 this.$el.find(selector) 27 this.$el.find(selector)
28 28
29 initMergeWidget: -> 29 initMergeWidget: ->
30 - this.showState( @opts.current_state ) 30 + this.showState( @opts.current_status )
31 31
32 if this.$('.automerge_widget').length and @opts.check_enable 32 if this.$('.automerge_widget').length and @opts.check_enable
33 $.get @opts.url_to_automerge_check, (data) => 33 $.get @opts.url_to_automerge_check, (data) =>
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
@@ -63,7 +63,7 @@ @@ -63,7 +63,7 @@
63 color: $style_color; 63 color: $style_color;
64 text-shadow: 0 1px 1px #FFF; 64 text-shadow: 0 1px 1px #FFF;
65 font-family: 'Yanone', sans-serif; 65 font-family: 'Yanone', sans-serif;
66 - font-size: 26px;  
67 - line-height: 42px; 66 + font-size: 24px;
  67 + line-height: 36px;
68 font-weight: normal; 68 font-weight: normal;
69 } 69 }
app/assets/stylesheets/sections/commits.scss
@@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
29 a{ 29 a{
30 color: $style_color; 30 color: $style_color;
31 } 31 }
32 - 32 +
33 > span { 33 > span {
34 font-family: $monospace_font; 34 font-family: $monospace_font;
35 font-size: 14px; 35 font-size: 14px;
@@ -124,7 +124,7 @@ @@ -124,7 +124,7 @@
124 .wrap{ 124 .wrap{
125 display: inline-block; 125 display: inline-block;
126 } 126 }
127 - 127 +
128 .frame { 128 .frame {
129 display: inline-block; 129 display: inline-block;
130 background-color: #fff; 130 background-color: #fff;
@@ -149,7 +149,7 @@ @@ -149,7 +149,7 @@
149 149
150 .view.swipe{ 150 .view.swipe{
151 position: relative; 151 position: relative;
152 - 152 +
153 .swipe-frame{ 153 .swipe-frame{
154 display: block; 154 display: block;
155 margin: auto; 155 margin: auto;
@@ -228,7 +228,7 @@ @@ -228,7 +228,7 @@
228 bottom: 0px; 228 bottom: 0px;
229 left: 50%; 229 left: 50%;
230 margin-left: -150px; 230 margin-left: -150px;
231 - 231 +
232 .drag-track{ 232 .drag-track{
233 display: block; 233 display: block;
234 position: absolute; 234 position: absolute;
@@ -237,7 +237,7 @@ @@ -237,7 +237,7 @@
237 width: 276px; 237 width: 276px;
238 background: url('onion_skin_sprites.gif') -4px -20px repeat-x; 238 background: url('onion_skin_sprites.gif') -4px -20px repeat-x;
239 } 239 }
240 - 240 +
241 .dragger { 241 .dragger {
242 display: block; 242 display: block;
243 position: absolute; 243 position: absolute;
@@ -248,7 +248,7 @@ @@ -248,7 +248,7 @@
248 background: url('onion_skin_sprites.gif') 0px -34px repeat-x; 248 background: url('onion_skin_sprites.gif') 0px -34px repeat-x;
249 cursor: pointer; 249 cursor: pointer;
250 } 250 }
251 - 251 +
252 .transparent { 252 .transparent {
253 display: block; 253 display: block;
254 position: absolute; 254 position: absolute;
@@ -258,7 +258,7 @@ @@ -258,7 +258,7 @@
258 width: 10px; 258 width: 10px;
259 background: url('onion_skin_sprites.gif') -2px 0px no-repeat; 259 background: url('onion_skin_sprites.gif') -2px 0px no-repeat;
260 } 260 }
261 - 261 +
262 .opaque { 262 .opaque {
263 display: block; 263 display: block;
264 position: absolute; 264 position: absolute;
@@ -275,19 +275,19 @@ @@ -275,19 +275,19 @@
275 275
276 padding: 10px; 276 padding: 10px;
277 text-align: center; 277 text-align: center;
278 - 278 +
279 background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); 279 background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
280 background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); 280 background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
281 background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); 281 background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
282 background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); 282 background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
283 - 283 +
284 ul, li{ 284 ul, li{
285 list-style: none; 285 list-style: none;
286 margin: 0; 286 margin: 0;
287 padding: 0; 287 padding: 0;
288 display: inline-block; 288 display: inline-block;
289 } 289 }
290 - 290 +
291 li{ 291 li{
292 color: grey; 292 color: grey;
293 border-left: 1px solid #c1c1c1; 293 border-left: 1px solid #c1c1c1;
@@ -322,12 +322,12 @@ @@ -322,12 +322,12 @@
322 } 322 }
323 .commit-author, .commit-committer{ 323 .commit-author, .commit-committer{
324 display: block; 324 display: block;
325 - color: #999;  
326 - font-weight: normal; 325 + color: #999;
  326 + font-weight: normal;
327 font-style: italic; 327 font-style: italic;
328 } 328 }
329 .commit-author strong, .commit-committer strong{ 329 .commit-author strong, .commit-committer strong{
330 - font-weight: bold; 330 + font-weight: bold;
331 font-style: normal; 331 font-style: normal;
332 } 332 }
333 333
@@ -337,7 +337,6 @@ @@ -337,7 +337,6 @@
337 */ 337 */
338 .commit { 338 .commit {
339 .browse_code_link_holder { 339 .browse_code_link_holder {
340 - @extend .span2;  
341 float: right; 340 float: right;
342 } 341 }
343 342
app/assets/stylesheets/sections/header.scss
@@ -5,15 +5,16 @@ @@ -5,15 +5,16 @@
5 header { 5 header {
6 &.navbar-gitlab { 6 &.navbar-gitlab {
7 .navbar-inner { 7 .navbar-inner {
8 - height: 45px;  
9 - padding: 5px; 8 + height: 40px;
  9 + padding: 3px;
10 background: #F1F1F1; 10 background: #F1F1F1;
  11 + filter: none;
11 12
12 .nav > li > a { 13 .nav > li > a {
13 color: $style_color; 14 color: $style_color;
14 text-shadow: 0 1px 0 #fff; 15 text-shadow: 0 1px 0 #fff;
15 - font-size: 18px;  
16 - padding: 12px; 16 + font-size: 16px;
  17 + padding: 10px;
17 } 18 }
18 19
19 /** NAV block with links and profile **/ 20 /** NAV block with links and profile **/
@@ -25,7 +26,6 @@ header { @@ -25,7 +26,6 @@ header {
25 } 26 }
26 27
27 z-index: 10; 28 z-index: 10;
28 - /*height: 60px;*/  
29 29
30 /** 30 /**
31 * 31 *
@@ -34,7 +34,7 @@ header { @@ -34,7 +34,7 @@ header {
34 */ 34 */
35 .app_logo { 35 .app_logo {
36 float: left; 36 float: left;
37 - margin-right: 15px; 37 + margin-right: 9px;
38 position: relative; 38 position: relative;
39 top: -5px; 39 top: -5px;
40 padding-top: 5px; 40 padding-top: 5px;
@@ -42,10 +42,10 @@ header { @@ -42,10 +42,10 @@ header {
42 a { 42 a {
43 float: left; 43 float: left;
44 padding: 0px; 44 padding: 0px;
45 - margin: 0 10px; 45 + margin: 0 6px;
46 46
47 h1 { 47 h1 {
48 - background: url('logo_dark.png') no-repeat 0px 2px; 48 + background: url('logo_dark.png') no-repeat center 1px;
49 float: left; 49 float: left;
50 height: 40px; 50 height: 40px;
51 width: 40px; 51 width: 40px;
@@ -79,7 +79,6 @@ header { @@ -79,7 +79,6 @@ header {
79 .search { 79 .search {
80 margin-right: 45px; 80 margin-right: 45px;
81 margin-left: 10px; 81 margin-left: 10px;
82 - margin-top: 2px;  
83 82
84 .search-input { 83 .search-input {
85 @extend .span2; 84 @extend .span2;
@@ -105,7 +104,7 @@ header { @@ -105,7 +104,7 @@ header {
105 .account-box { 104 .account-box {
106 position: absolute; 105 position: absolute;
107 right: 0; 106 right: 0;
108 - top: 6px; 107 + top: 4px;
109 z-index: 10000; 108 z-index: 10000;
110 width: 128px; 109 width: 128px;
111 font-size: 11px; 110 font-size: 11px;
@@ -228,6 +227,7 @@ header { @@ -228,6 +227,7 @@ header {
228 .search-input { 227 .search-input {
229 background-color: #D2D5DA; 228 background-color: #D2D5DA;
230 background-color: rgba(255, 255, 255, 0.5); 229 background-color: rgba(255, 255, 255, 0.5);
  230 + border: 1px solid #AAA;
231 231
232 &:focus { 232 &:focus {
233 background-color: white; 233 background-color: white;
@@ -240,13 +240,16 @@ header { @@ -240,13 +240,16 @@ header {
240 .app_logo { 240 .app_logo {
241 a { 241 a {
242 h1 { 242 h1 {
243 - background: url('logo_white.png') no-repeat center center; 243 + background: url('logo_white.png') no-repeat center 1px;
244 color: #fff; 244 color: #fff;
245 text-shadow: 0 1px 1px #111; 245 text-shadow: 0 1px 1px #111;
246 } 246 }
247 } 247 }
248 } 248 }
249 .project_name { 249 .project_name {
  250 + a {
  251 + color: #FFF;
  252 + }
250 color: #fff; 253 color: #fff;
251 text-shadow: 0 1px 1px #111; 254 text-shadow: 0 1px 1px #111;
252 } 255 }
@@ -261,11 +264,11 @@ header { @@ -261,11 +264,11 @@ header {
261 264
262 .separator { 265 .separator {
263 float: left; 266 float: left;
264 - height: 60px; 267 + height: 46px;
265 width: 1px; 268 width: 1px;
266 background: white; 269 background: white;
267 border-left: 1px solid #DDD; 270 border-left: 1px solid #DDD;
268 - margin-top: -10px; 271 + margin-top: -3px;
269 margin-left: 10px; 272 margin-left: 10px;
270 margin-right: 10px; 273 margin-right: 10px;
271 } 274 }
app/assets/stylesheets/sections/projects.scss
@@ -115,3 +115,7 @@ ul.nav.nav-projects-tabs { @@ -115,3 +115,7 @@ ul.nav.nav-projects-tabs {
115 } 115 }
116 } 116 }
117 } 117 }
  118 +
  119 +.team_member_row form {
  120 + margin: 0px;
  121 +}
app/assets/stylesheets/themes/ui_mars.scss
@@ -8,66 +8,27 @@ @@ -8,66 +8,27 @@
8 * 8 *
9 */ 9 */
10 .ui_mars { 10 .ui_mars {
11 -  
12 /* 11 /*
13 * Application Header 12 * Application Header
14 * 13 *
15 */ 14 */
16 header { 15 header {
17 - 16 + @extend .header-dark;
18 &.navbar-gitlab { 17 &.navbar-gitlab {
19 .navbar-inner { 18 .navbar-inner {
20 - background: #474D57 url('bg-header.png') repeat-x bottom;  
21 - border-bottom: 1px solid #444;  
22 -  
23 - .nav > li > a {  
24 - color: #eee;  
25 - text-shadow: 0 1px 0 #444; 19 + background: #474D57;
  20 + border-bottom: 1px solid #373D47;
  21 + .app_logo {
  22 + &:hover {
  23 + background-color: #373D47;
  24 + }
26 } 25 }
27 } 26 }
28 } 27 }
29 28
30 - .search {  
31 - float: right;  
32 - margin-right: 45px;  
33 - .search-input {  
34 - border: 1px solid rgba(0, 0, 0, 0.7);  
35 - background-color: #D2D5DA;  
36 - background-color: rgba(255, 255, 255, 0.5);  
37 -  
38 - &:focus {  
39 - background-color: white;  
40 - }  
41 - }  
42 - }  
43 - .search-input::-webkit-input-placeholder {  
44 - color: #666;  
45 - }  
46 - .app_logo {  
47 - a {  
48 - h1 {  
49 - background: url('logo_white.png') no-repeat center center;  
50 - color: #eee;  
51 - text-shadow: 0 1px 1px #111;  
52 - }  
53 - }  
54 - &:hover {  
55 - background-color: #41464e;  
56 - }  
57 - }  
58 - .project_name {  
59 - color: #eee;  
60 - text-shadow: 0 1px 1px #111; 29 + .separator {
  30 + background: #31363E;
  31 + border-left: 1px solid #666;
61 } 32 }
62 } 33 }
63 -  
64 - .separator {  
65 - background: #31363E;  
66 - border-left: 1px solid #666;  
67 - }  
68 -  
69 - /*  
70 - * End of Application Header  
71 - *  
72 - */  
73 } 34 }
app/contexts/merge_requests_load_context.rb
@@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext @@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext
14 end 14 end
15 15
16 merge_requests = merge_requests.page(params[:page]).per(20) 16 merge_requests = merge_requests.page(params[:page]).per(20)
17 - merge_requests = merge_requests.includes(:author, :project).order("closed, created_at desc") 17 + merge_requests = merge_requests.includes(:author, :project).order("state, created_at desc")
18 18
19 # Filter by specific assignee_id (or lack thereof)? 19 # Filter by specific assignee_id (or lack thereof)?
20 if params[:assignee_id].present? 20 if params[:assignee_id].present?
app/contexts/projects/create_context.rb
@@ -38,6 +38,8 @@ module Projects @@ -38,6 +38,8 @@ module Projects
38 if @project.valid? && @project.import_url.present? 38 if @project.valid? && @project.import_url.present?
39 shell = Gitlab::Shell.new 39 shell = Gitlab::Shell.new
40 if shell.import_repository(@project.path_with_namespace, @project.import_url) 40 if shell.import_repository(@project.path_with_namespace, @project.import_url)
  41 + # We should create satellite for imported repo
  42 + @project.satellite.create unless @project.satellite.exists?
41 true 43 true
42 else 44 else
43 @project.errors.add(:import_url, 'cannot clone repo') 45 @project.errors.add(:import_url, 'cannot clone repo')
app/controllers/dashboard_controller.rb
@@ -5,7 +5,7 @@ class DashboardController < ApplicationController @@ -5,7 +5,7 @@ class DashboardController < ApplicationController
5 before_filter :event_filter, only: :show 5 before_filter :event_filter, only: :show
6 6
7 def show 7 def show
8 - @groups = current_user.authorized_groups 8 + @groups = current_user.authorized_groups.sort_by(&:human_name)
9 @has_authorized_projects = @projects.count > 0 9 @has_authorized_projects = @projects.count > 0
10 @teams = current_user.authorized_teams 10 @teams = current_user.authorized_teams
11 @projects_count = @projects.count 11 @projects_count = @projects.count
app/controllers/files_controller.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class FilesController < ApplicationController
  2 + def download
  3 + note = Note.find(params[:id])
  4 +
  5 + if can?(current_user, :read_project, note.project)
  6 + uploader = note.attachment
  7 + send_file uploader.file.path, disposition: 'attachment'
  8 + else
  9 + not_found!
  10 + end
  11 + end
  12 +end
  13 +
app/controllers/merge_requests_controller.rb
@@ -73,14 +73,14 @@ class MergeRequestsController &lt; ProjectResourceController @@ -73,14 +73,14 @@ class MergeRequestsController &lt; ProjectResourceController
73 if @merge_request.unchecked? 73 if @merge_request.unchecked?
74 @merge_request.check_if_can_be_merged 74 @merge_request.check_if_can_be_merged
75 end 75 end
76 - render json: {state: @merge_request.human_state} 76 + render json: {merge_status: @merge_request.human_merge_status}
77 rescue Gitlab::SatelliteNotExistError 77 rescue Gitlab::SatelliteNotExistError
78 - render json: {state: :no_satellite} 78 + render json: {merge_status: :no_satellite}
79 end 79 end
80 80
81 def automerge 81 def automerge
82 return access_denied! unless can?(current_user, :accept_mr, @project) 82 return access_denied! unless can?(current_user, :accept_mr, @project)
83 - if @merge_request.open? && @merge_request.can_be_merged? 83 + if @merge_request.opened? && @merge_request.can_be_merged?
84 @merge_request.should_remove_source_branch = params[:should_remove_source_branch] 84 @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
85 @merge_request.automerge!(current_user) 85 @merge_request.automerge!(current_user)
86 @status = true 86 @status = true
app/controllers/milestones_controller.rb
@@ -12,7 +12,7 @@ class MilestonesController &lt; ProjectResourceController @@ -12,7 +12,7 @@ class MilestonesController &lt; ProjectResourceController
12 12
13 def index 13 def index
14 @milestones = case params[:f] 14 @milestones = case params[:f]
15 - when 'all'; @project.milestones.order("closed, due_date DESC") 15 + when 'all'; @project.milestones.order("state, due_date DESC")
16 when 'closed'; @project.milestones.closed.order("due_date DESC") 16 when 'closed'; @project.milestones.closed.order("due_date DESC")
17 else @project.milestones.active.order("due_date ASC") 17 else @project.milestones.active.order("due_date ASC")
18 end 18 end
app/controllers/profiles_controller.rb
@@ -51,7 +51,9 @@ class ProfilesController &lt; ApplicationController @@ -51,7 +51,9 @@ class ProfilesController &lt; ApplicationController
51 end 51 end
52 52
53 def update_username 53 def update_username
54 - @user.update_attributes(username: params[:user][:username]) 54 + if @user.can_change_username?
  55 + @user.update_attributes(username: params[:user][:username])
  56 + end
55 57
56 respond_to do |format| 58 respond_to do |format|
57 format.js 59 format.js
app/controllers/team_members_controller.rb
@@ -4,7 +4,11 @@ class TeamMembersController &lt; ProjectResourceController @@ -4,7 +4,11 @@ class TeamMembersController &lt; ProjectResourceController
4 before_filter :authorize_admin_project!, except: [:index, :show] 4 before_filter :authorize_admin_project!, except: [:index, :show]
5 5
6 def index 6 def index
7 - @teams = UserTeam.scoped 7 + @team = @project.users_projects.scoped
  8 + @team = @team.send(params[:type]) if %w(masters developers reporters guests).include?(params[:type])
  9 + @team = @team.sort_by(&:project_access).reverse.group_by(&:project_access)
  10 +
  11 + @assigned_teams = @project.user_team_project_relationships
8 end 12 end
9 13
10 def show 14 def show
app/controllers/teams/members_controller.rb
@@ -27,7 +27,13 @@ class Teams::MembersController &lt; Teams::ApplicationController @@ -27,7 +27,13 @@ class Teams::MembersController &lt; Teams::ApplicationController
27 end 27 end
28 28
29 def update 29 def update
30 - options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]} 30 + member_params = params[:team_member]
  31 +
  32 + options = {
  33 + default_projects_access: member_params[:permission],
  34 + group_admin: member_params[:group_admin]
  35 + }
  36 +
31 if user_team.update_membership(team_member, options) 37 if user_team.update_membership(team_member, options)
32 redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users." 38 redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
33 else 39 else
@@ -45,5 +51,4 @@ class Teams::MembersController &lt; Teams::ApplicationController @@ -45,5 +51,4 @@ class Teams::MembersController &lt; Teams::ApplicationController
45 def team_member 51 def team_member
46 @member ||= user_team.members.find_by_username(params[:id]) 52 @member ||= user_team.members.find_by_username(params[:id])
47 end 53 end
48 -  
49 end 54 end
app/controllers/teams_controller.rb
@@ -9,13 +9,11 @@ class TeamsController &lt; ApplicationController @@ -9,13 +9,11 @@ class TeamsController &lt; ApplicationController
9 layout 'user_team', except: [:new, :create] 9 layout 'user_team', except: [:new, :create]
10 10
11 def show 11 def show
12 - user_team  
13 projects 12 projects
14 @events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0) 13 @events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0)
15 end 14 end
16 15
17 def edit 16 def edit
18 - user_team  
19 end 17 end
20 18
21 def update 19 def update
@@ -41,6 +39,9 @@ class TeamsController &lt; ApplicationController @@ -41,6 +39,9 @@ class TeamsController &lt; ApplicationController
41 @team.path = @team.name.dup.parameterize if @team.name 39 @team.path = @team.name.dup.parameterize if @team.name
42 40
43 if @team.save 41 if @team.save
  42 + # Add current user as Master to the team
  43 + @team.add_members([current_user.id], UsersProject::MASTER, true)
  44 +
44 redirect_to team_path(@team) 45 redirect_to team_path(@team)
45 else 46 else
46 render action: :new 47 render action: :new
app/helpers/application_helper.rb
@@ -73,8 +73,8 @@ module ApplicationHelper @@ -73,8 +73,8 @@ module ApplicationHelper
73 73
74 def search_autocomplete_source 74 def search_autocomplete_source
75 projects = current_user.authorized_projects.map { |p| { label: "project: #{p.name_with_namespace}", url: project_path(p) } } 75 projects = current_user.authorized_projects.map { |p| { label: "project: #{p.name_with_namespace}", url: project_path(p) } }
76 - groups = current_user.authorized_groups.map { |group| { label: "group: #{group.name}", url: group_path(group) } }  
77 - teams = current_user.authorized_teams.map { |team| { label: "team: #{team.name}", url: team_path(team) } } 76 + groups = current_user.authorized_groups.map { |group| { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } }
  77 + teams = current_user.authorized_teams.map { |team| { label: "team: #{simple_sanitize(team.name)}", url: team_path(team) } }
78 78
79 default_nav = [ 79 default_nav = [
80 { label: "My Profile", url: profile_path }, 80 { label: "My Profile", url: profile_path },
@@ -159,8 +159,13 @@ module ApplicationHelper @@ -159,8 +159,13 @@ module ApplicationHelper
159 alt: "Sign in with #{provider.to_s.titleize}") 159 alt: "Sign in with #{provider.to_s.titleize}")
160 end 160 end
161 161
  162 + def simple_sanitize str
  163 + sanitize(str, tags: %w(a span))
  164 + end
  165 +
162 def image_url(source) 166 def image_url(source)
163 root_url + path_to_image(source) 167 root_url + path_to_image(source)
164 end 168 end
  169 +
165 alias_method :url_to_image, :image_url 170 alias_method :url_to_image, :image_url
166 end 171 end
app/helpers/dashboard_helper.rb
@@ -27,6 +27,6 @@ module DashboardHelper @@ -27,6 +27,6 @@ module DashboardHelper
27 items.opened 27 items.opened
28 end 28 end
29 29
30 - items.where(assignee_id: current_user.id).count 30 + items.cared(current_user).count
31 end 31 end
32 end 32 end
app/helpers/issues_helper.rb
@@ -6,7 +6,7 @@ module IssuesHelper @@ -6,7 +6,7 @@ module IssuesHelper
6 6
7 def issue_css_classes issue 7 def issue_css_classes issue
8 classes = "issue" 8 classes = "issue"
9 - classes << " closed" if issue.closed 9 + classes << " closed" if issue.closed?
10 classes << " today" if issue.today? 10 classes << " today" if issue.today?
11 classes 11 classes
12 end 12 end
app/helpers/merge_requests_helper.rb
@@ -12,7 +12,7 @@ module MergeRequestsHelper @@ -12,7 +12,7 @@ module MergeRequestsHelper
12 12
13 def mr_css_classes mr 13 def mr_css_classes mr
14 classes = "merge_request" 14 classes = "merge_request"
15 - classes << " closed" if mr.closed 15 + classes << " closed" if mr.closed?
16 classes << " merged" if mr.merged? 16 classes << " merged" if mr.merged?
17 classes 17 classes
18 end 18 end
app/helpers/namespaces_helper.rb
@@ -10,8 +10,8 @@ module NamespacesHelper @@ -10,8 +10,8 @@ module NamespacesHelper
10 10
11 11
12 global_opts = ["Global", [['/', Namespace.global_id]] ] 12 global_opts = ["Global", [['/', Namespace.global_id]] ]
13 - group_opts = ["Groups", groups.map {|g| [g.human_name, g.id]} ]  
14 - users_opts = [ "Users", users.map {|u| [u.human_name, u.id]} ] 13 + group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
  14 + users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
15 15
16 options = [] 16 options = []
17 options << global_opts if current_user.admin 17 options << global_opts if current_user.admin
app/helpers/projects_helper.rb
1 module ProjectsHelper 1 module ProjectsHelper
2 - def grouper_project_members(project)  
3 - @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)  
4 - end  
5 -  
6 - def grouper_project_teams(project)  
7 - @project.user_team_project_relationships.sort_by(&:greatest_access).reverse.group_by(&:greatest_access)  
8 - end  
9 -  
10 def remove_from_project_team_message(project, user) 2 def remove_from_project_team_message(project, user)
11 "You are going to remove #{user.name} from #{project.name} project team. Are you sure?" 3 "You are going to remove #{user.name} from #{project.name} project team. Are you sure?"
12 end 4 end
@@ -56,7 +48,7 @@ module ProjectsHelper @@ -56,7 +48,7 @@ module ProjectsHelper
56 def project_title project 48 def project_title project
57 if project.group 49 if project.group
58 content_tag :span do 50 content_tag :span do
59 - link_to(project.group.name, group_path(project.group)) + " / " + project.name 51 + link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name
60 end 52 end
61 else 53 else
62 project.name 54 project.name
app/models/ability.rb
@@ -123,7 +123,7 @@ class Ability @@ -123,7 +123,7 @@ class Ability
123 def user_team_abilities user, team 123 def user_team_abilities user, team
124 rules = [] 124 rules = []
125 125
126 - # Only group owner and administrators can manage group 126 + # Only group owner and administrators can manage team
127 if team.owner == user || team.admin?(user) || user.admin? 127 if team.owner == user || team.admin?(user) || user.admin?
128 rules << [ :manage_user_team ] 128 rules << [ :manage_user_team ]
129 end 129 end
app/models/concerns/issuable.rb
@@ -17,10 +17,9 @@ module Issuable @@ -17,10 +17,9 @@ module Issuable
17 validates :project, presence: true 17 validates :project, presence: true
18 validates :author, presence: true 18 validates :author, presence: true
19 validates :title, presence: true, length: { within: 0..255 } 19 validates :title, presence: true, length: { within: 0..255 }
20 - validates :closed, inclusion: { in: [true, false] }  
21 20
22 - scope :opened, -> { where(closed: false) }  
23 - scope :closed, -> { where(closed: true) } 21 + scope :opened, -> { with_state(:opened) }
  22 + scope :closed, -> { with_state(:closed) }
24 scope :of_group, ->(group) { where(project_id: group.project_ids) } 23 scope :of_group, ->(group) { where(project_id: group.project_ids) }
25 scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } 24 scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
26 scope :assigned, ->(u) { where(assignee_id: u.id)} 25 scope :assigned, ->(u) { where(assignee_id: u.id)}
@@ -62,14 +61,6 @@ module Issuable @@ -62,14 +61,6 @@ module Issuable
62 assignee_id_changed? 61 assignee_id_changed?
63 end 62 end
64 63
65 - def is_being_closed?  
66 - closed_changed? && closed  
67 - end  
68 -  
69 - def is_being_reopened?  
70 - closed_changed? && !closed  
71 - end  
72 -  
73 # 64 #
74 # Votes 65 # Votes
75 # 66 #
app/models/event.rb
@@ -130,10 +130,6 @@ class Event &lt; ActiveRecord::Base @@ -130,10 +130,6 @@ class Event &lt; ActiveRecord::Base
130 target if target_type == "MergeRequest" 130 target if target_type == "MergeRequest"
131 end 131 end
132 132
133 - def author  
134 - @author ||= User.find(author_id)  
135 - end  
136 -  
137 def action_name 133 def action_name
138 if closed? 134 if closed?
139 "closed" 135 "closed"
app/models/issue.rb
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 # project_id :integer 9 # project_id :integer
10 # created_at :datetime not null 10 # created_at :datetime not null
11 # updated_at :datetime not null 11 # updated_at :datetime not null
12 -# closed :boolean default(FALSE), not null 12 +# state :string default(FALSE), not null
13 # position :integer default(0) 13 # position :integer default(0)
14 # branch_name :string(255) 14 # branch_name :string(255)
15 # description :text 15 # description :text
@@ -19,12 +19,35 @@ @@ -19,12 +19,35 @@
19 class Issue < ActiveRecord::Base 19 class Issue < ActiveRecord::Base
20 include Issuable 20 include Issuable
21 21
22 - attr_accessible :title, :assignee_id, :closed, :position, :description,  
23 - :milestone_id, :label_list, :author_id_of_changes 22 + attr_accessible :title, :assignee_id, :position, :description,
  23 + :milestone_id, :label_list, :author_id_of_changes,
  24 + :state_event
24 25
25 acts_as_taggable_on :labels 26 acts_as_taggable_on :labels
26 27
27 - def self.open_for(user)  
28 - opened.assigned(user) 28 + class << self
  29 + def cared(user)
  30 + where('assignee_id = :user', user: user.id)
  31 + end
  32 +
  33 + def open_for(user)
  34 + opened.assigned(user)
  35 + end
  36 + end
  37 +
  38 + state_machine :state, initial: :opened do
  39 + event :close do
  40 + transition [:reopened, :opened] => :closed
  41 + end
  42 +
  43 + event :reopen do
  44 + transition closed: :reopened
  45 + end
  46 +
  47 + state :opened
  48 +
  49 + state :reopened
  50 +
  51 + state :closed
29 end 52 end
30 end 53 end
app/models/key.rb
@@ -35,7 +35,7 @@ class Key &lt; ActiveRecord::Base @@ -35,7 +35,7 @@ class Key &lt; ActiveRecord::Base
35 35
36 def fingerprintable_key 36 def fingerprintable_key
37 return true unless key # Don't test if there is no key. 37 return true unless key # Don't test if there is no key.
38 - # `ssh-keygen -lf /dev/stdin <<< "#{key}"` errors with: redirection unexpected 38 +
39 file = Tempfile.new('key_file') 39 file = Tempfile.new('key_file')
40 begin 40 begin
41 file.puts key 41 file.puts key
@@ -45,7 +45,7 @@ class Key &lt; ActiveRecord::Base @@ -45,7 +45,7 @@ class Key &lt; ActiveRecord::Base
45 file.close 45 file.close
46 file.unlink # deletes the temp file 46 file.unlink # deletes the temp file
47 end 47 end
48 - errors.add(:key, "can't be fingerprinted") if fingerprint_output.match("failed") 48 + errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0
49 end 49 end
50 50
51 def set_identifier 51 def set_identifier
app/models/merge_request.rb
@@ -9,15 +9,14 @@ @@ -9,15 +9,14 @@
9 # author_id :integer 9 # author_id :integer
10 # assignee_id :integer 10 # assignee_id :integer
11 # title :string(255) 11 # title :string(255)
12 -# closed :boolean default(FALSE), not null 12 +# state :string(255) not null
13 # created_at :datetime not null 13 # created_at :datetime not null
14 # updated_at :datetime not null 14 # updated_at :datetime not null
15 # st_commits :text(2147483647) 15 # st_commits :text(2147483647)
16 # st_diffs :text(2147483647) 16 # st_diffs :text(2147483647)
17 -# merged :boolean default(FALSE), not null  
18 -# state :integer default(1), not null  
19 -# milestone_id :integer 17 +# merge_status :integer default(1), not null
20 # 18 #
  19 +# milestone_id :integer
21 20
22 require Rails.root.join("app/models/commit") 21 require Rails.root.join("app/models/commit")
23 require Rails.root.join("lib/static_model") 22 require Rails.root.join("lib/static_model")
@@ -25,11 +24,33 @@ require Rails.root.join(&quot;lib/static_model&quot;) @@ -25,11 +24,33 @@ require Rails.root.join(&quot;lib/static_model&quot;)
25 class MergeRequest < ActiveRecord::Base 24 class MergeRequest < ActiveRecord::Base
26 include Issuable 25 include Issuable
27 26
28 - attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id,  
29 - :author_id_of_changes 27 + attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id,
  28 + :author_id_of_changes, :state_event
30 29
31 attr_accessor :should_remove_source_branch 30 attr_accessor :should_remove_source_branch
32 31
  32 + state_machine :state, initial: :opened do
  33 + event :close do
  34 + transition [:reopened, :opened] => :closed
  35 + end
  36 +
  37 + event :merge do
  38 + transition [:reopened, :opened] => :merged
  39 + end
  40 +
  41 + event :reopen do
  42 + transition closed: :reopened
  43 + end
  44 +
  45 + state :opened
  46 +
  47 + state :reopened
  48 +
  49 + state :closed
  50 +
  51 + state :merged
  52 + end
  53 +
33 BROKEN_DIFF = "--broken-diff" 54 BROKEN_DIFF = "--broken-diff"
34 55
35 UNCHECKED = 1 56 UNCHECKED = 1
@@ -43,21 +64,33 @@ class MergeRequest &lt; ActiveRecord::Base @@ -43,21 +64,33 @@ class MergeRequest &lt; ActiveRecord::Base
43 validates :target_branch, presence: true 64 validates :target_branch, presence: true
44 validate :validate_branches 65 validate :validate_branches
45 66
46 - def self.find_all_by_branch(branch_name)  
47 - where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)  
48 - end 67 + scope :merged, -> { with_state(:merged) }
49 68
50 - def self.find_all_by_milestone(milestone)  
51 - where("milestone_id = :milestone_id", milestone_id: milestone) 69 + class << self
  70 + def find_all_by_branch(branch_name)
  71 + where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
  72 + end
  73 +
  74 + def cared(user)
  75 + where('assignee_id = :user OR author_id = :user', user: user.id)
  76 + end
  77 +
  78 + def find_all_by_branch(branch_name)
  79 + where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
  80 + end
  81 +
  82 + def find_all_by_milestone(milestone)
  83 + where("milestone_id = :milestone_id", milestone_id: milestone)
  84 + end
52 end 85 end
53 86
54 - def human_state  
55 - states = { 87 + def human_merge_status
  88 + merge_statuses = {
56 CAN_BE_MERGED => "can_be_merged", 89 CAN_BE_MERGED => "can_be_merged",
57 CANNOT_BE_MERGED => "cannot_be_merged", 90 CANNOT_BE_MERGED => "cannot_be_merged",
58 UNCHECKED => "unchecked" 91 UNCHECKED => "unchecked"
59 } 92 }
60 - states[self.state] 93 + merge_statuses[self.merge_status]
61 end 94 end
62 95
63 def validate_branches 96 def validate_branches
@@ -72,20 +105,20 @@ class MergeRequest &lt; ActiveRecord::Base @@ -72,20 +105,20 @@ class MergeRequest &lt; ActiveRecord::Base
72 end 105 end
73 106
74 def unchecked? 107 def unchecked?
75 - state == UNCHECKED 108 + merge_status == UNCHECKED
76 end 109 end
77 110
78 def mark_as_unchecked 111 def mark_as_unchecked
79 - self.state = UNCHECKED 112 + self.merge_status = UNCHECKED
80 self.save 113 self.save
81 end 114 end
82 115
83 def can_be_merged? 116 def can_be_merged?
84 - state == CAN_BE_MERGED 117 + merge_status == CAN_BE_MERGED
85 end 118 end
86 119
87 def check_if_can_be_merged 120 def check_if_can_be_merged
88 - self.state = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged? 121 + self.merge_status = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
89 CAN_BE_MERGED 122 CAN_BE_MERGED
90 else 123 else
91 CANNOT_BE_MERGED 124 CANNOT_BE_MERGED
@@ -98,7 +131,7 @@ class MergeRequest &lt; ActiveRecord::Base @@ -98,7 +131,7 @@ class MergeRequest &lt; ActiveRecord::Base
98 end 131 end
99 132
100 def reloaded_diffs 133 def reloaded_diffs
101 - if open? && unmerged_diffs.any? 134 + if opened? && unmerged_diffs.any?
102 self.st_diffs = unmerged_diffs 135 self.st_diffs = unmerged_diffs
103 self.save 136 self.save
104 end 137 end
@@ -128,10 +161,6 @@ class MergeRequest &lt; ActiveRecord::Base @@ -128,10 +161,6 @@ class MergeRequest &lt; ActiveRecord::Base
128 commits.first 161 commits.first
129 end 162 end
130 163
131 - def merged?  
132 - merged && merge_event  
133 - end  
134 -  
135 def merge_event 164 def merge_event
136 self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last 165 self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last
137 end 166 end
@@ -146,26 +175,16 @@ class MergeRequest &lt; ActiveRecord::Base @@ -146,26 +175,16 @@ class MergeRequest &lt; ActiveRecord::Base
146 175
147 def probably_merged? 176 def probably_merged?
148 unmerged_commits.empty? && 177 unmerged_commits.empty? &&
149 - commits.any? && open?  
150 - end  
151 -  
152 - def open?  
153 - !closed  
154 - end  
155 -  
156 - def mark_as_merged!  
157 - self.merged = true  
158 - self.closed = true  
159 - save 178 + commits.any? && opened?
160 end 179 end
161 180
162 def mark_as_unmergable 181 def mark_as_unmergable
163 - self.state = CANNOT_BE_MERGED 182 + self.merge_status = CANNOT_BE_MERGED
164 self.save 183 self.save
165 end 184 end
166 185
167 def reloaded_commits 186 def reloaded_commits
168 - if open? && unmerged_commits.any? 187 + if opened? && unmerged_commits.any?
169 self.st_commits = unmerged_commits 188 self.st_commits = unmerged_commits
170 save 189 save
171 end 190 end
@@ -181,7 +200,8 @@ class MergeRequest &lt; ActiveRecord::Base @@ -181,7 +200,8 @@ class MergeRequest &lt; ActiveRecord::Base
181 end 200 end
182 201
183 def merge!(user_id) 202 def merge!(user_id)
184 - self.mark_as_merged! 203 + self.merge
  204 +
185 Event.create( 205 Event.create(
186 project: self.project, 206 project: self.project,
187 action: Event::MERGED, 207 action: Event::MERGED,
app/models/milestone.rb
@@ -13,19 +13,32 @@ @@ -13,19 +13,32 @@
13 # 13 #
14 14
15 class Milestone < ActiveRecord::Base 15 class Milestone < ActiveRecord::Base
16 - attr_accessible :title, :description, :due_date, :closed, :author_id_of_changes 16 + attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes
17 attr_accessor :author_id_of_changes 17 attr_accessor :author_id_of_changes
18 18
19 belongs_to :project 19 belongs_to :project
20 has_many :issues 20 has_many :issues
21 has_many :merge_requests 21 has_many :merge_requests
22 22
23 - scope :active, -> { where(closed: false) }  
24 - scope :closed, -> { where(closed: true) } 23 + scope :active, -> { with_state(:active) }
  24 + scope :closed, -> { with_state(:closed) }
25 25
26 validates :title, presence: true 26 validates :title, presence: true
27 validates :project, presence: true 27 validates :project, presence: true
28 - validates :closed, inclusion: { in: [true, false] } 28 +
  29 + state_machine :state, initial: :active do
  30 + event :close do
  31 + transition active: :closed
  32 + end
  33 +
  34 + event :activate do
  35 + transition closed: :active
  36 + end
  37 +
  38 + state :closed
  39 +
  40 + state :active
  41 + end
29 42
30 def expired? 43 def expired?
31 if due_date 44 if due_date
@@ -68,17 +81,13 @@ class Milestone &lt; ActiveRecord::Base @@ -68,17 +81,13 @@ class Milestone &lt; ActiveRecord::Base
68 end 81 end
69 82
70 def can_be_closed? 83 def can_be_closed?
71 - open? && issues.opened.count.zero? 84 + active? && issues.opened.count.zero?
72 end 85 end
73 86
74 def is_empty? 87 def is_empty?
75 total_items_count.zero? 88 total_items_count.zero?
76 end 89 end
77 90
78 - def open?  
79 - !closed  
80 - end  
81 -  
82 def author_id 91 def author_id
83 author_id_of_changes 92 author_id_of_changes
84 end 93 end
app/models/namespace.rb
@@ -17,11 +17,15 @@ class Namespace &lt; ActiveRecord::Base @@ -17,11 +17,15 @@ class Namespace &lt; ActiveRecord::Base
17 has_many :projects, dependent: :destroy 17 has_many :projects, dependent: :destroy
18 belongs_to :owner, class_name: "User" 18 belongs_to :owner, class_name: "User"
19 19
20 - validates :name, presence: true, uniqueness: true 20 + validates :owner, presence: true
  21 + validates :name, presence: true, uniqueness: true,
  22 + length: { within: 0..255 },
  23 + format: { with: Gitlab::Regex.name_regex,
  24 + message: "only letters, digits, spaces & '_' '-' '.' allowed." }
  25 +
21 validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, 26 validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
22 format: { with: Gitlab::Regex.path_regex, 27 format: { with: Gitlab::Regex.path_regex,
23 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } 28 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
24 - validates :owner, presence: true  
25 29
26 delegate :name, to: :owner, allow_nil: true, prefix: true 30 delegate :name, to: :owner, allow_nil: true, prefix: true
27 31
app/models/project.rb
@@ -43,7 +43,7 @@ class Project &lt; ActiveRecord::Base @@ -43,7 +43,7 @@ class Project &lt; ActiveRecord::Base
43 43
44 has_many :events, dependent: :destroy 44 has_many :events, dependent: :destroy
45 has_many :merge_requests, dependent: :destroy 45 has_many :merge_requests, dependent: :destroy
46 - has_many :issues, dependent: :destroy, order: "closed, created_at DESC" 46 + has_many :issues, dependent: :destroy, order: "state, created_at DESC"
47 has_many :milestones, dependent: :destroy 47 has_many :milestones, dependent: :destroy
48 has_many :users_projects, dependent: :destroy 48 has_many :users_projects, dependent: :destroy
49 has_many :notes, dependent: :destroy 49 has_many :notes, dependent: :destroy
@@ -146,7 +146,7 @@ class Project &lt; ActiveRecord::Base @@ -146,7 +146,7 @@ class Project &lt; ActiveRecord::Base
146 end 146 end
147 147
148 def saved? 148 def saved?
149 - id && valid? 149 + id && persisted?
150 end 150 end
151 151
152 def import? 152 def import?
app/models/repository.rb
@@ -132,16 +132,16 @@ class Repository @@ -132,16 +132,16 @@ class Repository
132 return nil unless commit 132 return nil unless commit
133 133
134 # Build file path 134 # Build file path
135 - file_name = self.path_with_namespace + "-" + commit.id.to_s + ".tar.gz" 135 + file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz"
136 storage_path = Rails.root.join("tmp", "repositories") 136 storage_path = Rails.root.join("tmp", "repositories")
137 - file_path = File.join(storage_path, file_name) 137 + file_path = File.join(storage_path, self.path_with_namespace, file_name)
138 138
139 # Put files into a directory before archiving 139 # Put files into a directory before archiving
140 prefix = self.path_with_namespace + "/" 140 prefix = self.path_with_namespace + "/"
141 141
142 # Create file if not exists 142 # Create file if not exists
143 unless File.exists?(file_path) 143 unless File.exists?(file_path)
144 - FileUtils.mkdir_p storage_path 144 + FileUtils.mkdir_p File.dirname(file_path)
145 file = self.repo.archive_to_file(ref, prefix, file_path) 145 file = self.repo.archive_to_file(ref, prefix, file_path)
146 end 146 end
147 147
app/models/user.rb
@@ -234,8 +234,12 @@ class User &lt; ActiveRecord::Base @@ -234,8 +234,12 @@ class User &lt; ActiveRecord::Base
234 keys.count == 0 234 keys.count == 0
235 end 235 end
236 236
  237 + def can_change_username?
  238 + Gitlab.config.gitlab.username_changing_enabled
  239 + end
  240 +
237 def can_create_project? 241 def can_create_project?
238 - projects_limit > personal_projects.count 242 + projects_limit > owned_projects.count
239 end 243 end
240 244
241 def can_create_group? 245 def can_create_group?
@@ -263,7 +267,7 @@ class User &lt; ActiveRecord::Base @@ -263,7 +267,7 @@ class User &lt; ActiveRecord::Base
263 end 267 end
264 268
265 def cared_merge_requests 269 def cared_merge_requests
266 - MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id) 270 + MergeRequest.cared(self)
267 end 271 end
268 272
269 # Remove user from all projects and 273 # Remove user from all projects and
app/models/user_team.rb
@@ -21,8 +21,11 @@ class UserTeam &lt; ActiveRecord::Base @@ -21,8 +21,11 @@ class UserTeam &lt; ActiveRecord::Base
21 has_many :projects, through: :user_team_project_relationships 21 has_many :projects, through: :user_team_project_relationships
22 has_many :members, through: :user_team_user_relationships, source: :user 22 has_many :members, through: :user_team_user_relationships, source: :user
23 23
24 - validates :name, presence: true, uniqueness: true  
25 validates :owner, presence: true 24 validates :owner, presence: true
  25 + validates :name, presence: true, uniqueness: true,
  26 + length: { within: 0..255 },
  27 + format: { with: Gitlab::Regex.name_regex,
  28 + message: "only letters, digits, spaces & '_' '-' '.' allowed." }
26 validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, 29 validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
27 format: { with: Gitlab::Regex.path_regex, 30 format: { with: Gitlab::Regex.path_regex,
28 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } 31 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
app/models/user_team_project_relationship.rb
@@ -26,6 +26,10 @@ class UserTeamProjectRelationship &lt; ActiveRecord::Base @@ -26,6 +26,10 @@ class UserTeamProjectRelationship &lt; ActiveRecord::Base
26 user_team.name 26 user_team.name
27 end 27 end
28 28
  29 + def human_max_access
  30 + UserTeam.access_roles.key(greatest_access)
  31 + end
  32 +
29 private 33 private
30 34
31 def check_greatest_access 35 def check_greatest_access
app/observers/activity_observer.rb
@@ -20,15 +20,23 @@ class ActivityObserver &lt; ActiveRecord::Observer @@ -20,15 +20,23 @@ class ActivityObserver &lt; ActiveRecord::Observer
20 end 20 end
21 end 21 end
22 22
23 - def after_save(record)  
24 - if record.changed.include?("closed") && record.author_id_of_changes 23 + def after_close(record, transition)
25 Event.create( 24 Event.create(
26 project: record.project, 25 project: record.project,
27 target_id: record.id, 26 target_id: record.id,
28 target_type: record.class.name, 27 target_type: record.class.name,
29 - action: (record.closed ? Event::CLOSED : Event::REOPENED), 28 + action: Event::CLOSED,
  29 + author_id: record.author_id_of_changes
  30 + )
  31 + end
  32 +
  33 + def after_reopen(record, transition)
  34 + Event.create(
  35 + project: record.project,
  36 + target_id: record.id,
  37 + target_type: record.class.name,
  38 + action: Event::REOPENED,
30 author_id: record.author_id_of_changes 39 author_id: record.author_id_of_changes
31 ) 40 )
32 - end  
33 end 41 end
34 end 42 end
app/observers/issue_observer.rb
@@ -7,22 +7,31 @@ class IssueObserver &lt; ActiveRecord::Observer @@ -7,22 +7,31 @@ class IssueObserver &lt; ActiveRecord::Observer
7 end 7 end
8 end 8 end
9 9
10 - def after_update(issue) 10 + def after_close(issue, transition)
11 send_reassigned_email(issue) if issue.is_being_reassigned? 11 send_reassigned_email(issue) if issue.is_being_reassigned?
12 12
13 - status = nil  
14 - status = 'closed' if issue.is_being_closed?  
15 - status = 'reopened' if issue.is_being_reopened?  
16 - if status  
17 - Note.create_status_change_note(issue, current_user, status)  
18 - [issue.author, issue.assignee].compact.each do |recipient|  
19 - Notify.delay.issue_status_changed_email(recipient.id, issue.id, status, current_user.id)  
20 - end  
21 - end 13 + create_note(issue)
  14 + end
  15 +
  16 + def after_reopen(issue, transition)
  17 + send_reassigned_email(issue) if issue.is_being_reassigned?
  18 +
  19 + create_note(issue)
  20 + end
  21 +
  22 + def after_update(issue)
  23 + send_reassigned_email(issue) if issue.is_being_reassigned?
22 end 24 end
23 25
24 protected 26 protected
25 27
  28 + def create_note(issue)
  29 + Note.create_status_change_note(issue, current_user, issue.state)
  30 + [issue.author, issue.assignee].compact.each do |recipient|
  31 + Notify.delay.issue_status_changed_email(recipient.id, issue.id, issue.state, current_user.id)
  32 + end
  33 + end
  34 +
26 def send_reassigned_email(issue) 35 def send_reassigned_email(issue)
27 recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id } 36 recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id }
28 37
app/observers/merge_request_observer.rb
@@ -7,15 +7,20 @@ class MergeRequestObserver &lt; ActiveRecord::Observer @@ -7,15 +7,20 @@ class MergeRequestObserver &lt; ActiveRecord::Observer
7 end 7 end
8 end 8 end
9 9
10 - def after_update(merge_request) 10 + def after_close(merge_request, transition)
11 send_reassigned_email(merge_request) if merge_request.is_being_reassigned? 11 send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
12 12
13 - status = nil  
14 - status = 'closed' if merge_request.is_being_closed?  
15 - status = 'reopened' if merge_request.is_being_reopened?  
16 - if status  
17 - Note.create_status_change_note(merge_request, current_user, status)  
18 - end 13 + Note.create_status_change_note(merge_request, current_user, merge_request.state)
  14 + end
  15 +
  16 + def after_reopen(merge_request, transition)
  17 + send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
  18 +
  19 + Note.create_status_change_note(merge_request, current_user, merge_request.state)
  20 + end
  21 +
  22 + def after_update(merge_request)
  23 + send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
19 end 24 end
20 25
21 protected 26 protected
app/uploaders/attachment_uploader.rb
@@ -19,4 +19,12 @@ class AttachmentUploader &lt; CarrierWave::Uploader::Base @@ -19,4 +19,12 @@ class AttachmentUploader &lt; CarrierWave::Uploader::Base
19 rescue 19 rescue
20 false 20 false
21 end 21 end
  22 +
  23 + def secure_url
  24 + if self.class.storage == CarrierWave::Storage::File
  25 + "/files/#{model.class.to_s.underscore}/#{model.id}/#{file.filename}"
  26 + else
  27 + url
  28 + end
  29 + end
22 end 30 end
app/views/admin/groups/index.html.haml
@@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
28 %td= group.path 28 %td= group.path
29 %td= group.projects.count 29 %td= group.projects.count
30 %td 30 %td
31 - = link_to group.owner_name, admin_user_path(group.owner_id) 31 + = link_to group.owner_name, admin_user_path(group.owner)
32 %td.bgred 32 %td.bgred
33 = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small" 33 = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
34 = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" 34 = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
app/views/admin/teams/index.html.haml
@@ -30,7 +30,7 @@ @@ -30,7 +30,7 @@
30 %td= team.projects.count 30 %td= team.projects.count
31 %td= team.members.count 31 %td= team.members.count
32 %td 32 %td
33 - = link_to team.owner.name, admin_user_path(team.owner_id) 33 + = link_to team.owner.name, admin_user_path(team.owner)
34 %td.bgred 34 %td.bgred
35 = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small" 35 = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
36 = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" 36 = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
app/views/commits/_commit.html.haml
@@ -6,9 +6,9 @@ @@ -6,9 +6,9 @@
6 = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" 6 = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
7 = commit.author_link avatar: true, size: 24 7 = commit.author_link avatar: true, size: 24
8 &nbsp; 8 &nbsp;
9 - = link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, commit.id), class: "row_title" 9 + = link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title"
10 10
11 - %span.committed_ago 11 + %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") }
12 = time_ago_in_words(commit.committed_date) 12 = time_ago_in_words(commit.committed_date)
13 ago 13 ago
14 &nbsp; 14 &nbsp;
app/views/events/event/_note.html.haml
@@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
26 = markdown truncate(event.target.note, length: 70) 26 = markdown truncate(event.target.note, length: 70)
27 - note = event.target 27 - note = event.target
28 - if note.attachment.url 28 - if note.attachment.url
29 - = link_to note.attachment.url, target: "_blank", class: 'note-file-attach' do 29 + = link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do
30 - if note.attachment.image? 30 - if note.attachment.image?
31 = image_tag note.attachment.url, class: 'note-image-attach' 31 = image_tag note.attachment.url, class: 'note-image-attach'
32 - else 32 - else
app/views/groups/_filter.html.haml
@@ -26,6 +26,8 @@ @@ -26,6 +26,8 @@
26 = link_to group_filter_path(entity, project_id: project.id) do 26 = link_to group_filter_path(entity, project_id: project.id) do
27 = project.name_with_namespace 27 = project.name_with_namespace
28 %small.pull-right= entities_per_project(project, entity) 28 %small.pull-right= entities_per_project(project, entity)
  29 + - if @projects.blank?
  30 + %p.nothing_here_message This group has no projects yet
29 31
30 %fieldset 32 %fieldset
31 %hr 33 %hr
app/views/groups/_people_filter.html.haml
@@ -7,6 +7,8 @@ @@ -7,6 +7,8 @@
7 = link_to people_group_path(@group, project_id: project.id) do 7 = link_to people_group_path(@group, project_id: project.id) do
8 = project.name_with_namespace 8 = project.name_with_namespace
9 %small.pull-right= project.users.count 9 %small.pull-right= project.users.count
  10 + - if @projects.blank?
  11 + %p.nothing_here_message This group has no projects yet
10 12
11 %fieldset 13 %fieldset
12 %hr 14 %hr
app/views/groups/edit.html.haml
@@ -30,6 +30,8 @@ @@ -30,6 +30,8 @@
30 = link_to 'Team', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" 30 = link_to 'Team', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
31 = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" 31 = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
32 = link_to 'Remove', project, confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" 32 = link_to 'Remove', project, confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
  33 + - if @group.projects.blank?
  34 + %p.nothing_here_message This group has no projects yet
33 35
34 .span5 36 .span5
35 .ui-box 37 .ui-box
app/views/help/index.html.haml
1 %h3.page_title 1 %h3.page_title
2 GITLAB 2 GITLAB
3 .pull-right 3 .pull-right
4 - %span= Gitlab::Version  
5 - %small= Gitlab::Revision 4 + %span= Gitlab::VERSION
  5 + %small= Gitlab::REVISION
6 %hr 6 %hr
7 %p.lead 7 %p.lead
8 Self Hosted Git Management 8 Self Hosted Git Management
app/views/issues/_show.html.haml
@@ -8,10 +8,10 @@ @@ -8,10 +8,10 @@
8 %i.icon-comment 8 %i.icon-comment
9 = issue.notes.count 9 = issue.notes.count
10 - if can? current_user, :modify_issue, issue 10 - if can? current_user, :modify_issue, issue
11 - - if issue.closed  
12 - = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {closed: false }, status_only: true), method: :put, class: "btn btn-small grouped reopen_issue", remote: true 11 + - if issue.closed?
  12 + = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-small grouped reopen_issue", remote: true
13 - else 13 - else
14 - = link_to 'Close', project_issue_path(issue.project, issue, issue: {closed: true }, status_only: true), method: :put, class: "btn btn-small grouped close_issue", remote: true 14 + = link_to 'Close', project_issue_path(issue.project, issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-small grouped close_issue", remote: true
15 = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do 15 = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do
16 %i.icon-edit 16 %i.icon-edit
17 Edit 17 Edit
app/views/issues/show.html.haml
@@ -7,10 +7,10 @@ @@ -7,10 +7,10 @@
7 7
8 %span.pull-right 8 %span.pull-right
9 - if can?(current_user, :admin_project, @project) || @issue.author == current_user 9 - if can?(current_user, :admin_project, @project) || @issue.author == current_user
10 - - if @issue.closed  
11 - = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped reopen_issue" 10 + - if @issue.closed?
  11 + = link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue"
12 - else 12 - else
13 - = link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue" 13 + = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue"
14 - if can?(current_user, :admin_project, @project) || @issue.author == current_user 14 - if can?(current_user, :admin_project, @project) || @issue.author == current_user
15 = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do 15 = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
16 %i.icon-edit 16 %i.icon-edit
@@ -27,7 +27,7 @@ @@ -27,7 +27,7 @@
27 .ui-box.ui-box-show 27 .ui-box.ui-box-show
28 .ui-box-head 28 .ui-box-head
29 %h4.box-title 29 %h4.box-title
30 - - if @issue.closed 30 + - if @issue.closed?
31 .error.status_info Closed 31 .error.status_info Closed
32 = gfm escape_once(@issue.title) 32 = gfm escape_once(@issue.title)
33 33
app/views/merge_requests/_show.html.haml
@@ -29,10 +29,10 @@ @@ -29,10 +29,10 @@
29 $(function(){ 29 $(function(){
30 merge_request = new MergeRequest({ 30 merge_request = new MergeRequest({
31 url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}", 31 url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}",
32 - check_enable: #{@merge_request.state == MergeRequest::UNCHECKED ? "true" : "false"}, 32 + check_enable: #{@merge_request.merge_status == MergeRequest::UNCHECKED ? "true" : "false"},
33 url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}", 33 url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}",
34 ci_enable: #{@project.gitlab_ci? ? "true" : "false"}, 34 ci_enable: #{@project.gitlab_ci? ? "true" : "false"},
35 - current_state: "#{@merge_request.human_state}", 35 + current_status: "#{@merge_request.human_merge_status}",
36 action: "#{controller.action_name}" 36 action: "#{controller.action_name}"
37 }); 37 });
38 }); 38 });
app/views/merge_requests/show/_mr_accept.html.haml
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 %strong Only masters can accept MR 3 %strong Only masters can accept MR
4 4
5 5
6 -- if @merge_request.open? && @commits.any? && can?(current_user, :accept_mr, @project) 6 +- if @merge_request.opened? && @commits.any? && can?(current_user, :accept_mr, @project)
7 .automerge_widget.can_be_merged{style: "display:none"} 7 .automerge_widget.can_be_merged{style: "display:none"}
8 .alert.alert-success 8 .alert.alert-success
9 %span 9 %span
app/views/merge_requests/show/_mr_box.html.haml
1 .ui-box.ui-box-show 1 .ui-box.ui-box-show
2 .ui-box-head 2 .ui-box-head
3 %h4.box-title 3 %h4.box-title
4 - - if @merge_request.merged 4 + - if @merge_request.merged?
5 .error.status_info 5 .error.status_info
6 %i.icon-ok 6 %i.icon-ok
7 Merged 7 Merged
8 - - elsif @merge_request.closed 8 + - elsif @merge_request.closed?
9 .error.status_info Closed 9 .error.status_info Closed
10 = gfm escape_once(@merge_request.title) 10 = gfm escape_once(@merge_request.title)
11 11
@@ -21,14 +21,14 @@ @@ -21,14 +21,14 @@
21 %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone) 21 %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
22 22
23 23
24 - - if @merge_request.closed 24 + - if @merge_request.closed?
25 .ui-box-bottom 25 .ui-box-bottom
26 - - if @merge_request.merged?  
27 - %span  
28 - Merged by #{link_to_member(@project, @merge_request.merge_event.author)}  
29 - %small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago.  
30 - - elsif @merge_request.closed_event  
31 - %span  
32 - Closed by #{link_to_member(@project, @merge_request.closed_event.author)}  
33 - %small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago. 26 + %span
  27 + Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
  28 + %small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago.
  29 + - if @merge_request.merged?
  30 + .ui-box-bottom
  31 + %span
  32 + Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
  33 + %small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago.
34 34
app/views/merge_requests/show/_mr_ci.html.haml
1 -- if @merge_request.open? && @commits.any? 1 +- if @merge_request.opened? && @commits.any?
2 .ci_widget.ci-success{style: "display:none"} 2 .ci_widget.ci-success{style: "display:none"}
3 .alert.alert-success 3 .alert.alert-success
4 %i.icon-ok 4 %i.icon-ok
app/views/merge_requests/show/_mr_title.html.haml
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 7
8 %span.pull-right 8 %span.pull-right
9 - if can?(current_user, :modify_merge_request, @merge_request) 9 - if can?(current_user, :modify_merge_request, @merge_request)
10 - - if @merge_request.open? 10 + - if @merge_request.opened?
11 .left.btn-group 11 .left.btn-group
12 %a.btn.grouped.dropdown-toggle{ data: {toggle: :dropdown} } 12 %a.btn.grouped.dropdown-toggle{ data: {toggle: :dropdown} }
13 %i.icon-download-alt 13 %i.icon-download-alt
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch) 17 %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch)
18 %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff) 18 %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff)
19 19
20 - = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {closed: true }, status_only: true), method: :put, class: "btn grouped btn-close", title: "Close merge request" 20 + = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request"
21 21
22 = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do 22 = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do
23 %i.icon-edit 23 %i.icon-edit
app/views/milestones/_milestone.html.haml
1 -%li{class: "milestone milestone-#{milestone.closed ? 'closed' : 'open'}", id: dom_id(milestone) } 1 +%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone) }
2 .pull-right 2 .pull-right
3 - - if can?(current_user, :admin_milestone, milestone.project) and milestone.open? 3 + - if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
4 = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link grouped" do 4 = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link grouped" do
5 %i.icon-edit 5 %i.icon-edit
6 Edit 6 Edit
7 %h4 7 %h4
8 = link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone) 8 = link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone)
9 - - if milestone.expired? and not milestone.closed 9 + - if milestone.expired? and not milestone.closed?
10 %span.cred (Expired) 10 %span.cred (Expired)
11 %small 11 %small
12 = milestone.expires_at 12 = milestone.expires_at
app/views/milestones/show.html.haml
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 &larr; To milestones list 9 &larr; To milestones list
10 .span6 10 .span6
11 .pull-right 11 .pull-right
12 - - unless @milestone.closed 12 + - unless @milestone.closed?
13 = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small grouped", title: "New Issue" do 13 = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small grouped", title: "New Issue" do
14 %i.icon-plus 14 %i.icon-plus
15 New Issue 15 New Issue
@@ -25,12 +25,12 @@ @@ -25,12 +25,12 @@
25 %hr 25 %hr
26 %p 26 %p
27 %span All issues for this milestone are closed. You may close milestone now. 27 %span All issues for this milestone are closed. You may close milestone now.
28 - = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {closed: true }), method: :put, class: "btn btn-small btn-remove" 28 + = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-small btn-remove"
29 29
30 .ui-box.ui-box-show 30 .ui-box.ui-box-show
31 .ui-box-head 31 .ui-box-head
32 %h4.box-title 32 %h4.box-title
33 - - if @milestone.closed 33 + - if @milestone.closed?
34 .error.status_info Closed 34 .error.status_info Closed
35 - elsif @milestone.expired? 35 - elsif @milestone.expired?
36 .error.status_info Expired 36 .error.status_info Expired
@@ -63,7 +63,7 @@ @@ -63,7 +63,7 @@
63 %li=link_to('All Issues', '#') 63 %li=link_to('All Issues', '#')
64 %ul.well-list 64 %ul.well-list
65 - @issues.each do |issue| 65 - @issues.each do |issue|
66 - %li{data: {closed: issue.closed}} 66 + %li{data: {closed: issue.closed?}}
67 = link_to [@project, issue] do 67 = link_to [@project, issue] do
68 %span.badge.badge-info ##{issue.id} 68 %span.badge.badge-info ##{issue.id}
69 &ndash; 69 &ndash;
@@ -77,7 +77,7 @@ @@ -77,7 +77,7 @@
77 %li=link_to('All Merge Requests', '#') 77 %li=link_to('All Merge Requests', '#')
78 %ul.well-list 78 %ul.well-list
79 - @merge_requests.each do |merge_request| 79 - @merge_requests.each do |merge_request|
80 - %li{data: {closed: merge_request.closed}} 80 + %li{data: {closed: merge_request.closed?}}
81 = link_to [@project, merge_request] do 81 = link_to [@project, merge_request] do
82 %span.badge.badge-info ##{merge_request.id} 82 %span.badge.badge-info ##{merge_request.id}
83 &ndash; 83 &ndash;
app/views/notes/_note.html.haml
@@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
31 - if note.attachment.image? 31 - if note.attachment.image?
32 = image_tag note.attachment.url, class: 'note-image-attach' 32 = image_tag note.attachment.url, class: 'note-image-attach'
33 .attachment.pull-right 33 .attachment.pull-right
34 - = link_to note.attachment.url, target: "_blank" do 34 + = link_to note.attachment.secure_url, target: "_blank" do
35 %i.icon-paper-clip 35 %i.icon-paper-clip
36 = note.attachment_identifier 36 = note.attachment_identifier
37 .clear 37 .clear
app/views/profiles/account.html.haml
@@ -53,29 +53,30 @@ @@ -53,29 +53,30 @@
53 53
54 54
55 55
56 -%fieldset.update-username  
57 - %legend  
58 - Username  
59 - %small.cred.pull-right  
60 - Changing your username can have unintended side effects!  
61 - = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|  
62 - .padded  
63 - = f.label :username  
64 - .input  
65 - = f.text_field :username, required: true  
66 - &nbsp;  
67 - %span.loading-gif.hide= image_tag "ajax_loader.gif"  
68 - %span.update-success.cgreen.hide  
69 - %i.icon-ok  
70 - Saved  
71 - %span.update-failed.cred.hide  
72 - %i.icon-remove  
73 - Failed  
74 - %ul.cred  
75 - %li It will change web url for personal projects.  
76 - %li It will change the git path to repositories for personal projects.  
77 - .input  
78 - = f.submit 'Save username', class: "btn btn-save" 56 +- if current_user.can_change_username?
  57 + %fieldset.update-username
  58 + %legend
  59 + Username
  60 + %small.cred.pull-right
  61 + Changing your username can have unintended side effects!
  62 + = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
  63 + .padded
  64 + = f.label :username
  65 + .input
  66 + = f.text_field :username, required: true
  67 + &nbsp;
  68 + %span.loading-gif.hide= image_tag "ajax_loader.gif"
  69 + %span.update-success.cgreen.hide
  70 + %i.icon-ok
  71 + Saved
  72 + %span.update-failed.cred.hide
  73 + %i.icon-remove
  74 + Failed
  75 + %ul.cred
  76 + %li It will change web url for personal projects.
  77 + %li It will change the git path to repositories for personal projects.
  78 + .input
  79 + = f.submit 'Save username', class: "btn btn-save"
79 80
80 - if Gitlab.config.gitlab.signup_enabled 81 - if Gitlab.config.gitlab.signup_enabled
81 %fieldset.remove-account 82 %fieldset.remove-account
@@ -83,4 +84,4 @@ @@ -83,4 +84,4 @@
83 Remove account 84 Remove account
84 %small.cred.pull-right 85 %small.cred.pull-right
85 Before removing the account you must remove all projects! 86 Before removing the account you must remove all projects!
86 - = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove delete-key btn-small pull-right"  
87 \ No newline at end of file 87 \ No newline at end of file
  88 + = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove delete-key btn-small pull-right"
app/views/profiles/show.html.haml
@@ -77,7 +77,7 @@ @@ -77,7 +77,7 @@
77 %legend 77 %legend
78 Personal projects: 78 Personal projects:
79 %small.pull-right 79 %small.pull-right
80 - %span= current_user.personal_projects.count 80 + %span= current_user.owned_projects.count
81 of 81 of
82 %span= current_user.projects_limit 82 %span= current_user.projects_limit
83 .padded 83 .padded
app/views/projects/_new_form.html.haml
@@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
28 .input 28 .input
29 = f.text_field :import_url, class: 'xlarge', placeholder: 'https://github.com/randx/six.git' 29 = f.text_field :import_url, class: 'xlarge', placeholder: 'https://github.com/randx/six.git'
30 .light 30 .light
31 - URL should be clonable 31 + URL must be clonable
32 32
33 %p.padded 33 %p.padded
34 New projects are private by default. You choose who can see the project and commit to repository. 34 New projects are private by default. You choose who can see the project and commit to repository.
app/views/projects/files.html.haml
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 - @notes.each do |note| 9 - @notes.each do |note|
10 %tr 10 %tr
11 %td 11 %td
12 - %a{href: note.attachment.url} 12 + = link_to note.attachment.secure_url, target: "_blank" do
13 = image_tag gravatar_icon(note.author_email), class: "avatar s24" 13 = image_tag gravatar_icon(note.author_email), class: "avatar s24"
14 = note.attachment_identifier 14 = note.attachment_identifier
15 %td 15 %td
app/views/team_members/_assigned_team.html.haml 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +%li{id: dom_id(team), class: "user_team_row team_#{team.id}"}
  2 + .pull-right
  3 + - if can?(current_user, :admin_team_member, @project)
  4 + = link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you shure?", class: "btn btn-remove btn-tiny" do
  5 + %i.icon-minus.icon-white
  6 +
  7 + %strong= link_to team.name, team_path(team), title: team.name, class: "dark"
  8 + %br
  9 + %small.cgray Members: #{team.members.count}
  10 + %small.cgray Max access: #{team_relation.human_max_access}
app/views/team_members/_assigned_teams.html.haml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +.ui-box
  2 + %ul.well-list
  3 + - assigned_teams.sort_by(&:team_name).each do |team_relation|
  4 + = render "team_members/assigned_team", team_relation: team_relation, team: team_relation.user_team
app/views/team_members/_show.html.haml
@@ -1,28 +0,0 @@ @@ -1,28 +0,0 @@
1 -- user = member.user  
2 -- allow_admin = can? current_user, :admin_project, @project  
3 -%li{id: dom_id(user), class: "team_member_row user_#{user.id}"}  
4 - .row  
5 - .span6  
6 - = link_to project_team_member_path(@project, user), title: user.name, class: "dark" do  
7 - = image_tag gravatar_icon(user.email, 40), class: "avatar s32"  
8 - = link_to project_team_member_path(@project, user), title: user.name, class: "dark" do  
9 - %strong= truncate(user.name, lenght: 40)  
10 - %br  
11 - %small.cgray= user.email  
12 -  
13 - .span5.pull-right  
14 - - if allow_admin  
15 - .left  
16 - = form_for(member, as: :team_member, url: project_team_member_path(@project, member.user)) do |f|  
17 - = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2"  
18 - .pull-right  
19 - - if current_user == user  
20 - %span.btn.disabled This is you!  
21 - - if @project.namespace_owner == user  
22 - %span.btn.disabled Owner  
23 - - elsif user.blocked  
24 - %span.btn.disabled.blocked Blocked  
25 - - elsif allow_admin  
26 - = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do  
27 - %i.icon-minus.icon-white  
28 -  
app/views/team_members/_show_team.html.haml
@@ -1,15 +0,0 @@ @@ -1,15 +0,0 @@
1 -- team = team_rel.user_team  
2 -- allow_admin = can? current_user, :admin_team_member, @project  
3 -%li{id: dom_id(team), class: "user_team_row team_#{team.id}"}  
4 - .row  
5 - .span6  
6 - %strong= link_to team.name, team_path(team), title: team.name, class: "dark"  
7 - %br  
8 - %small.cgray Members: #{team.members.count}  
9 -  
10 - .span5.pull-right  
11 - .pull-right  
12 - - if allow_admin  
13 - .left  
14 - = link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you shure?", class: "btn btn-remove small" do  
15 - %i.icon-minus.icon-white  
app/views/team_members/_team.html.haml
1 -- grouper_project_members(@project).each do |access, members| 1 +- team.each do |access, members|
2 .ui-box 2 .ui-box
3 %h5.title 3 %h5.title
4 = Project.access_options.key(access).pluralize 4 = Project.access_options.key(access).pluralize
5 %small= members.size 5 %small= members.size
6 %ul.well-list 6 %ul.well-list
7 - - members.sort_by(&:user_name).each do |up|  
8 - = render(partial: 'team_members/show', locals: {member: up})  
9 -  
10 -  
11 -:javascript  
12 - $(function(){  
13 - $('.repo-access-select, .project-access-select').live("change", function() {  
14 - $(this.form).submit();  
15 - });  
16 - }) 7 + - members.sort_by(&:user_name).each do |team_member|
  8 + = render 'team_members/team_member', member: team_member
app/views/team_members/_team_member.html.haml 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +- user = member.user
  2 +- allow_admin = can? current_user, :admin_project, @project
  3 +%li{id: dom_id(user), class: "team_member_row user_#{user.id}"}
  4 + .row
  5 + .span4
  6 + = link_to project_team_member_path(@project, user), title: user.name, class: "dark" do
  7 + = image_tag gravatar_icon(user.email, 40), class: "avatar s32"
  8 + = link_to project_team_member_path(@project, user), title: user.name, class: "dark" do
  9 + %strong= truncate(user.name, lenght: 40)
  10 + %br
  11 + %small.cgray= user.email
  12 +
  13 + .span4.pull-right
  14 + - if allow_admin
  15 + .left
  16 + = form_for(member, as: :team_member, url: project_team_member_path(@project, member.user)) do |f|
  17 + = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2 trigger-submit"
  18 + .pull-right
  19 + - if current_user == user
  20 + %span.label This is you!
  21 + - if @project.namespace_owner == user
  22 + %span.label Owner
  23 + - elsif user.blocked
  24 + %span.label Blocked
  25 + - elsif allow_admin
  26 + = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do
  27 + %i.icon-minus.icon-white
  28 +
app/views/team_members/_teams.html.haml
@@ -1,16 +0,0 @@ @@ -1,16 +0,0 @@
1 -- grouper_project_teams(@project).each do |access, teams|  
2 - .ui-box  
3 - %h5.title  
4 - = UserTeam.access_roles.key(access).pluralize  
5 - %small= teams.size  
6 - %ul.well-list  
7 - - teams.sort_by(&:team_name).each do |tofr|  
8 - = render(partial: 'team_members/show_team', locals: {team_rel: tofr})  
9 -  
10 -  
11 -:javascript  
12 - $(function(){  
13 - $('.repo-access-select, .project-access-select').live("change", function() {  
14 - $(this.form).submit();  
15 - });  
16 - })  
app/views/team_members/index.html.haml
@@ -18,16 +18,39 @@ @@ -18,16 +18,39 @@
18 %hr 18 %hr
19 19
20 .clearfix 20 .clearfix
21 -%div.team-table  
22 - = render partial: "team_members/team", locals: {project: @project} 21 +.row
  22 + .span3
  23 + %ul.nav.nav-pills.nav-stacked
  24 + %li{class: ("active" if !params[:type])}
  25 + = link_to project_team_members_path(type: nil) do
  26 + All
  27 + %li{class: ("active" if params[:type] == 'masters')}
  28 + = link_to project_team_members_path(type: 'masters') do
  29 + Masters
  30 + %span.pull-right= @project.users_projects.masters.count
  31 + %li{class: ("active" if params[:type] == 'developers')}
  32 + = link_to project_team_members_path(type: 'developers') do
  33 + Developers
  34 + %span.pull-right= @project.users_projects.developers.count
  35 + %li{class: ("active" if params[:type] == 'reporters')}
  36 + = link_to project_team_members_path(type: 'reporters') do
  37 + Reporters
  38 + %span.pull-right= @project.users_projects.reporters.count
  39 + %li{class: ("active" if params[:type] == 'guests')}
  40 + = link_to project_team_members_path(type: 'guests') do
  41 + Guests
  42 + %span.pull-right= @project.users_projects.guests.count
23 43
  44 + - if @assigned_teams.present?
  45 + %h5
  46 + Assigned teams
  47 + (#{@project.user_teams.count})
  48 + %div
  49 + = render "team_members/assigned_teams", assigned_teams: @assigned_teams
  50 +
  51 + .span9
  52 + %div.team-table
  53 + = render "team_members/team", team: @team
24 54
25 -%h3.page_title  
26 - Assigned teams  
27 - (#{@project.user_teams.count})  
28 55
29 -%hr  
30 56
31 -.clearfix  
32 -%div.team-table  
33 - = render partial: "team_members/teams", locals: {project: @project}  
app/views/teams/edit.html.haml
1 %h3.page_title= "Edit Team #{@team.name}" 1 %h3.page_title= "Edit Team #{@team.name}"
2 %hr 2 %hr
3 -= form_for @team, url: team_path(@team) do |f|  
4 - - if @team.errors.any?  
5 - .alert.alert-error  
6 - %span= @team.errors.full_messages.first  
7 - .clearfix  
8 - = f.label :name do  
9 - Team name is  
10 - .input  
11 - = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" 3 +.row
  4 + .span7
  5 + = form_for @team, url: team_path(@team) do |f|
  6 + - if @team.errors.any?
  7 + .alert.alert-error
  8 + %span= @team.errors.full_messages.first
  9 + .clearfix
  10 + = f.label :name do
  11 + Team name is
  12 + .input
  13 + = f.text_field :name, placeholder: "Ex. OpenSource", class: "xlarge left"
  14 +
  15 + .clearfix
  16 + = f.label :path do
  17 + Team path is
  18 + .input
  19 + = f.text_field :path, placeholder: "opensource", class: "xlarge left"
  20 + .form-actions
  21 + = f.submit 'Save team changes', class: "btn btn-save"
  22 + .span5
  23 + .ui-box
  24 + %h5.title Remove team
  25 + .padded.bgred
  26 + %p
  27 + Removed team can not be restored!
  28 + = link_to 'Remove team', team_path(@team), method: :delete, confirm: "You are sure?", class: "btn btn-remove btn-small"
12 29
13 - .clearfix  
14 - = f.label :path do  
15 - Team path is  
16 - .input  
17 - = f.text_field :path, placeholder: "opensource", class: "xxlarge left"  
18 - .form-actions  
19 - = f.submit 'Save team changes', class: "btn btn-primary"  
20 - = link_to 'Delete team', team_path(@team), method: :delete, confirm: "You are shure?", class: "btn btn-remove pull-right"  
app/views/teams/members/_show.html.haml
@@ -10,22 +10,21 @@ @@ -10,22 +10,21 @@
10 %br 10 %br
11 %small.cgray= user.email 11 %small.cgray= user.email
12 12
13 - .span6.pull-right 13 + .span4
14 - if allow_admin 14 - if allow_admin
15 - .left.span2  
16 - = form_for(member, as: :team_member, url: team_member_path(@team, user)) do |f|  
17 - = f.select :permission, options_for_select(UsersProject.access_roles, @team.default_projects_access(user)), {}, class: "medium project-access-select span2"  
18 - .left.span2  
19 - %span  
20 - = check_box_tag :group_admin, true, @team.admin?(user)  
21 - Admin access  
22 - .pull-right  
23 - - if current_user == user  
24 - %span.btn.disabled This is you!  
25 - - if @team.owner == user  
26 - %span.btn.disabled.btn-success Owner  
27 - - elsif user.blocked  
28 - %span.btn.disabled.blocked Blocked  
29 - - elsif allow_admin  
30 - = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove" do  
31 - %i.icon-minus.icon-white 15 + = form_for(member, as: :team_member, url: team_member_path(@team, user)) do |f|
  16 + = f.select :permission, options_for_select(UsersProject.access_roles, @team.default_projects_access(user)), {}, class: "medium trigger-submit"
  17 + %br
  18 + = label_tag do
  19 + = f.check_box :group_admin, class: 'trigger-submit'
  20 + %span Admin access
  21 + .pull-right
  22 + - if current_user == user
  23 + %span.btn.disabled This is you!
  24 + - if @team.owner == user
  25 + %span.btn.disabled Owner
  26 + - elsif user.blocked
  27 + %span.btn.disabled.blocked Blocked
  28 + - elsif allow_admin
  29 + = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove" do
  30 + %i.icon-minus.icon-white
app/views/teams/new.html.haml
@@ -17,3 +17,17 @@ @@ -17,3 +17,17 @@
17 %li All created teams are public (users can view who enter into team and which project are assigned for this team) 17 %li All created teams are public (users can view who enter into team and which project are assigned for this team)
18 %li People within a team see only projects they have access to 18 %li People within a team see only projects they have access to
19 %li You will be able to assign existing projects for team 19 %li You will be able to assign existing projects for team
  20 + %hr
  21 +
  22 + - if current_user.can_create_group?
  23 + .clearfix
  24 + .input.light
  25 + Need a group for several dependent projects?
  26 + = link_to new_group_path, class: "btn btn-tiny" do
  27 + Create a group
  28 + - if current_user.can_create_project?
  29 + .clearfix
  30 + .input.light
  31 + Want to create a project?
  32 + = link_to new_project_path, class: "btn btn-tiny" do
  33 + Create a project
app/workers/post_receive.rb
@@ -21,14 +21,18 @@ class PostReceive @@ -21,14 +21,18 @@ class PostReceive
21 return false 21 return false
22 end 22 end
23 23
24 - # Ignore push from non-gitlab users  
25 - user = if identifier.nil?  
26 - raise identifier.inspect 24 + user = if identifier.blank?
  25 + # Local push from gitlab
27 email = project.repository.commit(newrev).author.email rescue nil 26 email = project.repository.commit(newrev).author.email rescue nil
28 User.find_by_email(email) if email 27 User.find_by_email(email) if email
29 - elsif /^[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}$/.match(identifier)  
30 - User.find_by_email(identifier)  
31 - elsif identifier =~ /key/ 28 +
  29 + elsif identifier =~ /\Auser-\d+\Z/
  30 + # git push over http
  31 + user_id = identifier.gsub("user-", "")
  32 + User.find_by_id(user_id)
  33 +
  34 + elsif identifier =~ /\Akey-\d+\Z/
  35 + # git push over ssh
32 key_id = identifier.gsub("key-", "") 36 key_id = identifier.gsub("key-", "")
33 Key.find_by_id(key_id).try(:user) 37 Key.find_by_id(key_id).try(:user)
34 end 38 end
config/gitlab.yml.example
@@ -7,121 +7,132 @@ @@ -7,121 +7,132 @@
7 # 2. Replace gitlab -> host with your domain 7 # 2. Replace gitlab -> host with your domain
8 # 3. Replace gitlab -> email_from 8 # 3. Replace gitlab -> email_from
9 9
10 -#  
11 -# 1. GitLab app settings  
12 -# ==========================  
13 -  
14 -## GitLab settings  
15 -gitlab:  
16 - ## Web server settings  
17 - host: localhost  
18 - port: 80  
19 - https: false  
20 - # Uncomment and customize to run in non-root path  
21 - # Note that ENV['RAILS_RELATIVE_URL_ROOT'] in config/unicorn.rb may need to be changed  
22 - # relative_url_root: /gitlab  
23 -  
24 - # Uncomment and customize if you can't use the default user to run GitLab (default: 'git')  
25 - # user: git  
26 -  
27 - ## Email settings  
28 - # Email address used in the "From" field in mails sent by GitLab  
29 - email_from: gitlab@localhost  
30 -  
31 - # Email address of your support contact (default: same as email_from)  
32 - support_email: support@localhost  
33 -  
34 - ## Project settings  
35 - default_projects_limit: 10  
36 - # signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled.  
37 -  
38 -## Gravatar  
39 -gravatar:  
40 - enabled: true # Use user avatar images from Gravatar.com (default: true)  
41 - # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm  
42 - # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm  
43 -  
44 -  
45 -  
46 -#  
47 -# 2. Auth settings  
48 -# ==========================  
49 -  
50 -## LDAP settings  
51 -ldap:  
52 - enabled: false  
53 - host: '_your_ldap_server'  
54 - base: '_the_base_where_you_search_for_users'  
55 - port: 636  
56 - uid: 'sAMAccountName'  
57 - method: 'ssl' # "ssl" or "plain"  
58 - bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'  
59 - password: '_the_password_of_the_bind_user'  
60 -  
61 -## Omniauth settings  
62 -omniauth:  
63 - # Enable ability for users  
64 - # Allow logging in via Twitter, Google, etc. using Omniauth providers  
65 - enabled: false  
66 - 10 +production: &base
  11 + #
  12 + # 1. GitLab app settings
  13 + # ==========================
  14 +
  15 + ## GitLab settings
  16 + gitlab:
  17 + ## Web server settings
  18 + host: localhost
  19 + port: 80
  20 + https: false
  21 + # Uncomment and customize to run in non-root path
  22 + # Note that ENV['RAILS_RELATIVE_URL_ROOT'] in config/unicorn.rb may need to be changed
  23 + # relative_url_root: /gitlab
  24 +
  25 + # Uncomment and customize if you can't use the default user to run GitLab (default: 'git')
  26 + # user: git
  27 +
  28 + ## Email settings
  29 + # Email address used in the "From" field in mails sent by GitLab
  30 + email_from: gitlab@localhost
  31 +
  32 + # Email address of your support contact (default: same as email_from)
  33 + support_email: support@localhost
  34 +
  35 + ## Project settings
  36 + default_projects_limit: 10
  37 + # signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled.
  38 + # username_changing_enabled: false # default: true - User can change her username/namespace
  39 +
  40 + ## Gravatar
  41 + gravatar:
  42 + enabled: true # Use user avatar images from Gravatar.com (default: true)
  43 + # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
  44 + # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
  45 +
  46 +
  47 +
  48 + #
  49 + # 2. Auth settings
  50 + # ==========================
  51 +
  52 + ## LDAP settings
  53 + ldap:
  54 + enabled: false
  55 + host: '_your_ldap_server'
  56 + base: '_the_base_where_you_search_for_users'
  57 + port: 636
  58 + uid: 'sAMAccountName'
  59 + method: 'ssl' # "ssl" or "plain"
  60 + bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
  61 + password: '_the_password_of_the_bind_user'
  62 +
  63 + ## Omniauth settings
  64 + omniauth:
  65 + # Enable ability for users
  66 + # Allow logging in via Twitter, Google, etc. using Omniauth providers
  67 + enabled: false
  68 +
  69 + # CAUTION!
  70 + # This allows users to login without having a user account first (default: false)
  71 + # User accounts will be created automatically when authentication was successful.
  72 + allow_single_sign_on: false
  73 + # Locks down those users until they have been cleared by the admin (default: true)
  74 + block_auto_created_users: true
  75 +
  76 + ## Auth providers
  77 + # Uncomment the lines and fill in the data of the auth provider you want to use
  78 + # If your favorite auth provider is not listed you can user others:
  79 + # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers
  80 + # The 'app_id' and 'app_secret' parameters are always passed as the first two
  81 + # arguments, followed by optional 'args' which can be either a hash or an array.
  82 + providers:
  83 + # - { name: 'google_oauth2', app_id: 'YOUR APP ID',
  84 + # app_secret: 'YOUR APP SECRET',
  85 + # args: { access_type: 'offline', approval_prompt: '' } }
  86 + # - { name: 'twitter', app_id: 'YOUR APP ID',
  87 + # app_secret: 'YOUR APP SECRET'}
  88 + # - { name: 'github', app_id: 'YOUR APP ID',
  89 + # app_secret: 'YOUR APP SECRET' }
  90 +
  91 +
  92 +
  93 + #
  94 + # 3. Advanced settings
  95 + # ==========================
  96 +
  97 + # GitLab Satellites
  98 + satellites:
  99 + # Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
  100 + path: /home/git/gitlab-satellites/
  101 +
  102 + ## Backup settings
  103 + backup:
  104 + path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
  105 + # keep_time: 604800 # default: 0 (forever) (in seconds)
  106 +
  107 + ## GitLab Shell settings
  108 + gitlab_shell:
  109 + # REPOS_PATH MUST NOT BE A SYMLINK!!!
  110 + repos_path: /home/git/repositories/
  111 + hooks_path: /home/git/gitlab-shell/hooks/
  112 +
  113 + # Git over HTTP
  114 + upload_pack: true
  115 + receive_pack: true
  116 +
  117 + # If you use non-standart ssh port you need to specify it
  118 + # ssh_port: 22
  119 +
  120 + ## Git settings
67 # CAUTION! 121 # CAUTION!
68 - # This allows users to login without having a user account first (default: false)  
69 - # User accounts will be created automatically when authentication was successful.  
70 - allow_single_sign_on: false  
71 - # Locks down those users until they have been cleared by the admin (default: true)  
72 - block_auto_created_users: true  
73 -  
74 - ## Auth providers  
75 - # Uncomment the lines and fill in the data of the auth provider you want to use  
76 - # If your favorite auth provider is not listed you can user others:  
77 - # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers  
78 - # The 'app_id' and 'app_secret' parameters are always passed as the first two  
79 - # arguments, followed by optional 'args' which can be either a hash or an array.  
80 - providers:  
81 - # - { name: 'google_oauth2', app_id: 'YOUR APP ID',  
82 - # app_secret: 'YOUR APP SECRET',  
83 - # args: { access_type: 'offline', approval_prompt: '' } }  
84 - # - { name: 'twitter', app_id: 'YOUR APP ID',  
85 - # app_secret: 'YOUR APP SECRET'}  
86 - # - { name: 'github', app_id: 'YOUR APP ID',  
87 - # app_secret: 'YOUR APP SECRET' }  
88 -  
89 -  
90 -  
91 -#  
92 -# 3. Advanced settings  
93 -# ==========================  
94 -  
95 -# GitLab Satellites  
96 -satellites:  
97 - # Relative paths are relative to Rails.root (default: tmp/repo_satellites/)  
98 - path: /home/git/gitlab-satellites/  
99 -  
100 -## Backup settings  
101 -backup:  
102 - path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)  
103 - # keep_time: 604800 # default: 0 (forever) (in seconds)  
104 -  
105 -## GitLab Shell settings  
106 -gitlab_shell:  
107 - # REPOS_PATH MUST NOT BE A SYMLINK!!!  
108 - repos_path: /home/git/repositories/  
109 - hooks_path: /home/git/gitlab-shell/hooks/  
110 -  
111 - # Git over HTTP  
112 - upload_pack: true  
113 - receive_pack: true  
114 -  
115 - # If you use non-standart ssh port you need to specify it  
116 - # ssh_port: 22  
117 -  
118 -## Git settings  
119 -# CAUTION!  
120 -# Use the default values unless you really know what you are doing  
121 -git:  
122 - bin_path: /usr/bin/git  
123 - # Max size of git object like commit, in bytes  
124 - # This value can be increased if you have a very large commits  
125 - max_size: 5242880 # 5.megabytes  
126 - # Git timeout to read commit, in seconds  
127 - timeout: 10 122 + # Use the default values unless you really know what you are doing
  123 + git:
  124 + bin_path: /usr/bin/git
  125 + # Max size of git object like commit, in bytes
  126 + # This value can be increased if you have a very large commits
  127 + max_size: 5242880 # 5.megabytes
  128 + # Git timeout to read commit, in seconds
  129 + timeout: 10
  130 +
  131 +development:
  132 + <<: *base
  133 +
  134 +test:
  135 + <<: *base
  136 +
  137 +staging:
  138 + <<: *base
config/initializers/1_settings.rb
1 class Settings < Settingslogic 1 class Settings < Settingslogic
2 source "#{Rails.root}/config/gitlab.yml" 2 source "#{Rails.root}/config/gitlab.yml"
  3 + namespace Rails.env
3 4
4 class << self 5 class << self
5 def gitlab_on_non_standard_port? 6 def gitlab_on_non_standard_port?
@@ -56,6 +57,7 @@ Settings.gitlab[&#39;support_email&#39;] ||= Settings.gitlab.email_from @@ -56,6 +57,7 @@ Settings.gitlab[&#39;support_email&#39;] ||= Settings.gitlab.email_from
56 Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) 57 Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url)
57 Settings.gitlab['user'] ||= 'git' 58 Settings.gitlab['user'] ||= 'git'
58 Settings.gitlab['signup_enabled'] ||= false 59 Settings.gitlab['signup_enabled'] ||= false
  60 +Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
59 61
60 # 62 #
61 # Gravatar 63 # Gravatar
config/initializers/2_app.rb
1 module Gitlab 1 module Gitlab
2 - Version = File.read(Rails.root.join("VERSION"))  
3 - Revision = `git log --pretty=format:'%h' -n 1` 2 + VERSION = File.read(Rails.root.join("VERSION")).strip
  3 + REVISION = `git log --pretty=format:'%h' -n 1`
4 4
5 def self.config 5 def self.config
6 Settings 6 Settings
config/routes.rb
@@ -47,6 +47,11 @@ Gitlab::Application.routes.draw do @@ -47,6 +47,11 @@ Gitlab::Application.routes.draw do
47 end 47 end
48 48
49 # 49 #
  50 + # Attachments serving
  51 + #
  52 + get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /[a-zA-Z.0-9_\-\+]+/ }
  53 +
  54 + #
50 # Admin Area 55 # Admin Area
51 # 56 #
52 namespace :admin do 57 namespace :admin do
db/fixtures/development/02_source_code.rb
1 -root = Gitlab.config.gitolite.repos_path 1 +root = Gitlab.config.gitlab_shell.repos_path
2 2
3 projects = [ 3 projects = [
4 { path: 'underscore.git', git: 'https://github.com/documentcloud/underscore.git' }, 4 { path: 'underscore.git', git: 'https://github.com/documentcloud/underscore.git' },
db/fixtures/development/09_issues.rb
@@ -16,7 +16,7 @@ Gitlab::Seeder.quiet do @@ -16,7 +16,7 @@ Gitlab::Seeder.quiet do
16 project_id: project.id, 16 project_id: project.id,
17 author_id: user_id, 17 author_id: user_id,
18 assignee_id: user_id, 18 assignee_id: user_id,
19 - closed: [true, false].sample, 19 + state: ['opened', 'closed'].sample,
20 milestone: project.milestones.sample, 20 milestone: project.milestones.sample,
21 title: Faker::Lorem.sentence(6) 21 title: Faker::Lorem.sentence(6)
22 }]) 22 }])
db/fixtures/development/10_merge_requests.rb
@@ -17,7 +17,7 @@ Gitlab::Seeder.quiet do @@ -17,7 +17,7 @@ Gitlab::Seeder.quiet do
17 project_id: project.id, 17 project_id: project.id,
18 author_id: user_id, 18 author_id: user_id,
19 assignee_id: user_id, 19 assignee_id: user_id,
20 - closed: [true, false].sample, 20 + state: ['opened', 'closed'].sample,
21 milestone: project.milestones.sample, 21 milestone: project.milestones.sample,
22 title: Faker::Lorem.sentence(6) 22 title: Faker::Lorem.sentence(6)
23 }]) 23 }])
db/migrate/20130214154045_rename_state_to_merge_status_in_milestone.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class RenameStateToMergeStatusInMilestone < ActiveRecord::Migration
  2 + def change
  3 + rename_column :merge_requests, :state, :merge_status
  4 + end
  5 +end
db/migrate/20130218140952_add_state_to_issue.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddStateToIssue < ActiveRecord::Migration
  2 + def change
  3 + add_column :issues, :state, :string
  4 + end
  5 +end
db/migrate/20130218141038_add_state_to_merge_request.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddStateToMergeRequest < ActiveRecord::Migration
  2 + def change
  3 + add_column :merge_requests, :state, :string
  4 + end
  5 +end
db/migrate/20130218141117_add_state_to_milestone.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddStateToMilestone < ActiveRecord::Migration
  2 + def change
  3 + add_column :milestones, :state, :string
  4 + end
  5 +end
db/migrate/20130218141258_convert_closed_to_state_in_issue.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class ConvertClosedToStateInIssue < ActiveRecord::Migration
  2 + def up
  3 + Issue.transaction do
  4 + Issue.where(closed: true).update_all("state = 'closed'")
  5 + Issue.where(closed: false).update_all("state = 'opened'")
  6 + end
  7 + end
  8 +
  9 + def down
  10 + Issue.transaction do
  11 + Issue.where(state: :closed).update_all("closed = 1")
  12 + end
  13 + end
  14 +end
db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
  2 + def up
  3 + MergeRequest.transaction do
  4 + MergeRequest.where("closed = 1 AND merged = 1").update_all("state = 'merged'")
  5 + MergeRequest.where("closed = 1 AND merged = 0").update_all("state = 'closed'")
  6 + MergeRequest.where("closed = 0").update_all("state = 'opened'")
  7 + end
  8 + end
  9 +
  10 + def down
  11 + MergeRequest.transaction do
  12 + MergeRequest.where(state: :closed).update_all("closed = 1")
  13 + MergeRequest.where(state: :merged).update_all("closed = 1, merged = 1")
  14 + end
  15 + end
  16 +end
db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class ConvertClosedToStateInMilestone < ActiveRecord::Migration
  2 + def up
  3 + Milestone.transaction do
  4 + Milestone.where(closed: false).update_all("state = 'opened'")
  5 + Milestone.where(closed: false).update_all("state = 'active'")
  6 + end
  7 + end
  8 +
  9 + def down
  10 + Milestone.transaction do
  11 + Milestone.where(state: :closed).update_all("closed = 1")
  12 + end
  13 + end
  14 +end
db/migrate/20130218141444_remove_merged_from_merge_request.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class RemoveMergedFromMergeRequest < ActiveRecord::Migration
  2 + def up
  3 + remove_column :merge_requests, :merged
  4 + end
  5 +
  6 + def down
  7 + add_column :merge_requests, :merged, :boolean, default: true, null: false
  8 + end
  9 +end
db/migrate/20130218141507_remove_closed_from_issue.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class RemoveClosedFromIssue < ActiveRecord::Migration
  2 + def up
  3 + remove_column :issues, :closed
  4 + end
  5 +
  6 + def down
  7 + add_column :issues, :closed, :boolean
  8 + end
  9 +end
db/migrate/20130218141536_remove_closed_from_merge_request.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class RemoveClosedFromMergeRequest < ActiveRecord::Migration
  2 + def up
  3 + remove_column :merge_requests, :closed
  4 + end
  5 +
  6 + def down
  7 + add_column :merge_requests, :closed, :boolean
  8 + end
  9 +end
db/migrate/20130218141554_remove_closed_from_milestone.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class RemoveClosedFromMilestone < ActiveRecord::Migration
  2 + def up
  3 + remove_column :milestones, :closed
  4 + end
  5 +
  6 + def down
  7 + add_column :milestones, :closed, :boolean
  8 + end
  9 +end
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 # 11 #
12 # It's strongly recommended to check this file into your version control system. 12 # It's strongly recommended to check this file into your version control system.
13 13
14 -ActiveRecord::Schema.define(:version => 20130131070232) do 14 +ActiveRecord::Schema.define(:version => 20130218141554) do
15 15
16 create_table "events", :force => true do |t| 16 create_table "events", :force => true do |t|
17 t.string "target_type" 17 t.string "target_type"
@@ -37,18 +37,17 @@ ActiveRecord::Schema.define(:version =&gt; 20130131070232) do @@ -37,18 +37,17 @@ ActiveRecord::Schema.define(:version =&gt; 20130131070232) do
37 t.integer "assignee_id" 37 t.integer "assignee_id"
38 t.integer "author_id" 38 t.integer "author_id"
39 t.integer "project_id" 39 t.integer "project_id"
40 - t.datetime "created_at", :null => false  
41 - t.datetime "updated_at", :null => false  
42 - t.boolean "closed", :default => false, :null => false 40 + t.datetime "created_at", :null => false
  41 + t.datetime "updated_at", :null => false
43 t.integer "position", :default => 0 42 t.integer "position", :default => 0
44 t.string "branch_name" 43 t.string "branch_name"
45 t.text "description" 44 t.text "description"
46 t.integer "milestone_id" 45 t.integer "milestone_id"
  46 + t.string "state"
47 end 47 end
48 48
49 add_index "issues", ["assignee_id"], :name => "index_issues_on_assignee_id" 49 add_index "issues", ["assignee_id"], :name => "index_issues_on_assignee_id"
50 add_index "issues", ["author_id"], :name => "index_issues_on_author_id" 50 add_index "issues", ["author_id"], :name => "index_issues_on_author_id"
51 - add_index "issues", ["closed"], :name => "index_issues_on_closed"  
52 add_index "issues", ["created_at"], :name => "index_issues_on_created_at" 51 add_index "issues", ["created_at"], :name => "index_issues_on_created_at"
53 add_index "issues", ["milestone_id"], :name => "index_issues_on_milestone_id" 52 add_index "issues", ["milestone_id"], :name => "index_issues_on_milestone_id"
54 add_index "issues", ["project_id"], :name => "index_issues_on_project_id" 53 add_index "issues", ["project_id"], :name => "index_issues_on_project_id"
@@ -69,25 +68,23 @@ ActiveRecord::Schema.define(:version =&gt; 20130131070232) do @@ -69,25 +68,23 @@ ActiveRecord::Schema.define(:version =&gt; 20130131070232) do
69 add_index "keys", ["user_id"], :name => "index_keys_on_user_id" 68 add_index "keys", ["user_id"], :name => "index_keys_on_user_id"
70 69
71 create_table "merge_requests", :force => true do |t| 70 create_table "merge_requests", :force => true do |t|
72 - t.string "target_branch", :null => false  
73 - t.string "source_branch", :null => false  
74 - t.integer "project_id", :null => false 71 + t.string "target_branch", :null => false
  72 + t.string "source_branch", :null => false
  73 + t.integer "project_id", :null => false
75 t.integer "author_id" 74 t.integer "author_id"
76 t.integer "assignee_id" 75 t.integer "assignee_id"
77 t.string "title" 76 t.string "title"
78 - t.boolean "closed", :default => false, :null => false  
79 - t.datetime "created_at", :null => false  
80 - t.datetime "updated_at", :null => false 77 + t.datetime "created_at", :null => false
  78 + t.datetime "updated_at", :null => false
81 t.text "st_commits", :limit => 2147483647 79 t.text "st_commits", :limit => 2147483647
82 t.text "st_diffs", :limit => 2147483647 80 t.text "st_diffs", :limit => 2147483647
83 - t.boolean "merged", :default => false, :null => false  
84 - t.integer "state", :default => 1, :null => false 81 + t.integer "merge_status", :default => 1, :null => false
85 t.integer "milestone_id" 82 t.integer "milestone_id"
  83 + t.string "state"
86 end 84 end
87 85
88 add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id" 86 add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id"
89 add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id" 87 add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id"
90 - add_index "merge_requests", ["closed"], :name => "index_merge_requests_on_closed"  
91 add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at" 88 add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at"
92 add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id" 89 add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id"
93 add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" 90 add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id"
@@ -96,13 +93,13 @@ ActiveRecord::Schema.define(:version =&gt; 20130131070232) do @@ -96,13 +93,13 @@ ActiveRecord::Schema.define(:version =&gt; 20130131070232) do
96 add_index "merge_requests", ["title"], :name => "index_merge_requests_on_title" 93 add_index "merge_requests", ["title"], :name => "index_merge_requests_on_title"
97 94
98 create_table "milestones", :force => true do |t| 95 create_table "milestones", :force => true do |t|
99 - t.string "title", :null => false  
100 - t.integer "project_id", :null => false 96 + t.string "title", :null => false
  97 + t.integer "project_id", :null => false
101 t.text "description" 98 t.text "description"
102 t.date "due_date" 99 t.date "due_date"
103 - t.boolean "closed", :default => false, :null => false  
104 - t.datetime "created_at", :null => false  
105 - t.datetime "updated_at", :null => false 100 + t.datetime "created_at", :null => false
  101 + t.datetime "updated_at", :null => false
  102 + t.string "state"
106 end 103 end
107 104
108 add_index "milestones", ["due_date"], :name => "index_milestones_on_due_date" 105 add_index "milestones", ["due_date"], :name => "index_milestones_on_due_date"
doc/api/milestones.md
@@ -34,7 +34,6 @@ POST /projects/:id/milestones @@ -34,7 +34,6 @@ POST /projects/:id/milestones
34 Parameters: 34 Parameters:
35 35
36 + `id` (required) - The ID of a project 36 + `id` (required) - The ID of a project
37 -+ `milestone_id` (required) - The ID of a project milestone  
38 + `title` (required) - The title of an milestone 37 + `title` (required) - The title of an milestone
39 + `description` (optional) - The description of the milestone 38 + `description` (optional) - The description of the milestone
40 + `due_date` (optional) - The due date of the milestone 39 + `due_date` (optional) - The due date of the milestone
doc/api/projects.md
@@ -23,7 +23,7 @@ GET /projects @@ -23,7 +23,7 @@ GET /projects
23 "blocked": false, 23 "blocked": false,
24 "created_at": "2012-05-23T08:00:58Z" 24 "created_at": "2012-05-23T08:00:58Z"
25 }, 25 },
26 - "private": true, 26 + "public": true,
27 "path": "rails", 27 "path": "rails",
28 "path_with_namespace": "rails/rails", 28 "path_with_namespace": "rails/rails",
29 "issues_enabled": false, 29 "issues_enabled": false,
@@ -45,7 +45,7 @@ GET /projects @@ -45,7 +45,7 @@ GET /projects
45 "blocked": false, 45 "blocked": false,
46 "created_at": "2012-05-23T08:00:58Z" 46 "created_at": "2012-05-23T08:00:58Z"
47 }, 47 },
48 - "private": true, 48 + "public": true,
49 "path": "gitlab", 49 "path": "gitlab",
50 "path_with_namespace": "randx/gitlab", 50 "path_with_namespace": "randx/gitlab",
51 "issues_enabled": true, 51 "issues_enabled": true,
@@ -89,7 +89,7 @@ Parameters: @@ -89,7 +89,7 @@ Parameters:
89 "blocked": false, 89 "blocked": false,
90 "created_at": "2012-05-23T08:00:58Z" 90 "created_at": "2012-05-23T08:00:58Z"
91 }, 91 },
92 - "private": true, 92 + "public": true,
93 "path": "gitlab", 93 "path": "gitlab",
94 "path_with_namespace": "randx/gitlab", 94 "path_with_namespace": "randx/gitlab",
95 "issues_enabled": true, 95 "issues_enabled": true,
doc/install/databases.md
@@ -27,7 +27,7 @@ GitLab supports the following databases: @@ -27,7 +27,7 @@ GitLab supports the following databases:
27 mysql> \q 27 mysql> \q
28 28
29 # Try connecting to the new database with the new user 29 # Try connecting to the new database with the new user
30 - sudo -u gitlab -H mysql -u gitlab -p -D gitlabhq_production 30 + sudo -u git -H mysql -u gitlab -p -D gitlabhq_production
31 31
32 ## PostgreSQL 32 ## PostgreSQL
33 33
@@ -47,5 +47,5 @@ GitLab supports the following databases: @@ -47,5 +47,5 @@ GitLab supports the following databases:
47 template1=# \q 47 template1=# \q
48 48
49 # Try connecting to the new database with the new user 49 # Try connecting to the new database with the new user
50 - sudo -u gitlab -H psql -d gitlabhq_production 50 + sudo -u git -H psql -d gitlabhq_production
51 51
doc/install/installation.md
1 This installation guide was created for Debian/Ubuntu and tested on it. 1 This installation guide was created for Debian/Ubuntu and tested on it.
2 2
3 -Please read `doc/install/requirements.md` for hardware and platform requirements. 3 +Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements.
4 4
5 5
6 **Important Note:** 6 **Important Note:**
@@ -8,12 +8,13 @@ The following steps have been known to work. @@ -8,12 +8,13 @@ The following steps have been known to work.
8 If you deviate from this guide, do it with caution and make sure you don't 8 If you deviate from this guide, do it with caution and make sure you don't
9 violate any assumptions GitLab makes about its environment. 9 violate any assumptions GitLab makes about its environment.
10 For things like AWS installation scripts, init scripts or config files for 10 For things like AWS installation scripts, init scripts or config files for
11 -alternative web server have a look at the "Advanced Setup Tips" section. 11 +alternative web server have a look at the [`Advanced Setup
  12 +Tips`](./installation.md#advanced-setup-tips) section.
12 13
13 14
14 **Important Note:** 15 **Important Note:**
15 If you find a bug/error in this guide please submit an issue or pull request 16 If you find a bug/error in this guide please submit an issue or pull request
16 -following the contribution guide (see `CONTRIBUTING.md`). 17 +following the [`contribution guide`](../../CONTRIBUTING.md).
17 18
18 - - - 19 - - -
19 20
@@ -24,7 +25,7 @@ The GitLab installation consists of setting up the following components: @@ -24,7 +25,7 @@ The GitLab installation consists of setting up the following components:
24 1. Packages / Dependencies 25 1. Packages / Dependencies
25 2. Ruby 26 2. Ruby
26 3. System Users 27 3. System Users
27 -4. Gitolite 28 +4. GitLab shell
28 5. Database 29 5. Database
29 6. GitLab 30 6. GitLab
30 7. Nginx 31 7. Nginx
@@ -32,16 +33,13 @@ The GitLab installation consists of setting up the following components: @@ -32,16 +33,13 @@ The GitLab installation consists of setting up the following components:
32 33
33 # 1. Packages / Dependencies 34 # 1. Packages / Dependencies
34 35
35 -`sudo` is not installed on Debian by default. If you don't have it you'll need  
36 -to install it first. 36 +`sudo` is not installed on Debian by default. Make sure your system is
  37 +up-to-date and install it.
37 38
38 # run as root 39 # run as root
39 - apt-get update && apt-get upgrade && apt-get install sudo  
40 -  
41 -Make sure your system is up-to-date:  
42 -  
43 - sudo apt-get update  
44 - sudo apt-get upgrade 40 + apt-get update
  41 + apt-get upgrade
  42 + apt-get install sudo
45 43
46 **Note:** 44 **Note:**
47 Vim is an editor that is used here whenever there are files that need to be 45 Vim is an editor that is used here whenever there are files that need to be
@@ -96,25 +94,24 @@ Create a `git` user for Gitlab: @@ -96,25 +94,24 @@ Create a `git` user for Gitlab:
96 94
97 # 4. GitLab shell 95 # 4. GitLab shell
98 96
99 - # login as git 97 + # Login as git
100 sudo su git 98 sudo su git
101 99
102 - # go to home directory 100 + # Go to home directory
103 cd /home/git 101 cd /home/git
104 102
105 - # clone gitlab shell 103 + # Clone gitlab shell
106 git clone https://github.com/gitlabhq/gitlab-shell.git 104 git clone https://github.com/gitlabhq/gitlab-shell.git
107 105
108 - # setup 106 + # Setup
109 cd gitlab-shell 107 cd gitlab-shell
110 cp config.yml.example config.yml 108 cp config.yml.example config.yml
111 ./bin/install 109 ./bin/install
112 110
113 111
114 -  
115 # 5. Database 112 # 5. Database
116 113
117 -To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install/databases.md`](./databases.md) . 114 +To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install/databases.md`](./databases.md).
118 115
119 116
120 # 6. GitLab 117 # 6. GitLab
@@ -154,9 +151,13 @@ do so with caution! @@ -154,9 +151,13 @@ do so with caution!
154 sudo chmod -R u+rwX log/ 151 sudo chmod -R u+rwX log/
155 sudo chmod -R u+rwX tmp/ 152 sudo chmod -R u+rwX tmp/
156 153
157 - # Make directory for satellites 154 + # Create directory for satellites
158 sudo -u git -H mkdir /home/git/gitlab-satellites 155 sudo -u git -H mkdir /home/git/gitlab-satellites
159 156
  157 + # Create directory for pids and make sure GitLab can write to it
  158 + sudo -u git -H mkdir tmp/pids/
  159 + sudo chmod -R u+rwX tmp/pids/
  160 +
160 # Copy the example Unicorn config 161 # Copy the example Unicorn config
161 sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb 162 sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
162 163
@@ -187,7 +188,9 @@ Make sure to update username/password in config/database.yml. @@ -187,7 +188,9 @@ Make sure to update username/password in config/database.yml.
187 188
188 189
189 ## Initialise Database and Activate Advanced Features 190 ## Initialise Database and Activate Advanced Features
190 - 191 +
  192 + sudo -u git -H bundle exec rake db:setup RAILS_ENV=production
  193 + sudo -u git -H bundle exec rake db:seed_fu RAILS_ENV=production
191 sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production 194 sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
192 195
193 196
@@ -205,7 +208,7 @@ Make GitLab start on boot: @@ -205,7 +208,7 @@ Make GitLab start on boot:
205 208
206 ## Check Application Status 209 ## Check Application Status
207 210
208 -Check if GitLab and its environment is configured correctly: 211 +Check if GitLab and its environment are configured correctly:
209 212
210 sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production 213 sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
211 214
@@ -227,7 +230,7 @@ However there are still a few steps left. @@ -227,7 +230,7 @@ However there are still a few steps left.
227 230
228 **Note:** 231 **Note:**
229 If you can't or don't want to use Nginx as your web server, have a look at the 232 If you can't or don't want to use Nginx as your web server, have a look at the
230 -"Advanced Setup Tips" section. 233 +[`Advanced Setup Tips`](./installation.md#advanced-setup-tips) section.
231 234
232 ## Installation 235 ## Installation
233 sudo apt-get install nginx 236 sudo apt-get install nginx
@@ -244,11 +247,11 @@ Make sure to edit the config file to match your setup: @@ -244,11 +247,11 @@ Make sure to edit the config file to match your setup:
244 # Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN** 247 # Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN**
245 # to the IP address and fully-qualified domain name 248 # to the IP address and fully-qualified domain name
246 # of your host serving GitLab 249 # of your host serving GitLab
247 - sudo vim /etc/nginx/sites-enabled/gitlab 250 + sudo vim /etc/nginx/sites-available/gitlab
248 251
249 ## Restart 252 ## Restart
250 253
251 - sudo /etc/init.d/nginx restart 254 + sudo service nginx restart
252 255
253 256
254 # Done! 257 # Done!
@@ -282,7 +285,7 @@ a different host, you can configure its connection string via the @@ -282,7 +285,7 @@ a different host, you can configure its connection string via the
282 285
283 ## Custom SSH Connection 286 ## Custom SSH Connection
284 287
285 -If you are running SSH on a non-standard port, you must change the gitlab user'S SSH config. 288 +If you are running SSH on a non-standard port, you must change the gitlab user's SSH config.
286 289
287 # Add to /home/git/.ssh/config 290 # Add to /home/git/.ssh/config
288 host localhost # Give your setup a name (here: override localhost) 291 host localhost # Give your setup a name (here: override localhost)