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.

.gitignore
... ... @@ -2,7 +2,7 @@
2 2 .rbx/
3 3 db/*.sqlite3
4 4 db/*.sqlite3-journal
5   -log/*.log
  5 +log/*.log*
6 6 tmp/
7 7 .sass-cache/
8 8 coverage/*
... ... @@ -20,6 +20,7 @@ config/database.yml
20 20 config/initializers/omniauth.rb
21 21 config/unicorn.rb
22 22 config/resque.yml
  23 +config/aws.yml
23 24 db/data.yml
24 25 .idea
25 26 .DS_Store
... ...
Gemfile
... ... @@ -70,6 +70,9 @@ gem "github-markup", "~> 0.7.4", require: 'github/markup'
70 70 # Servers
71 71 gem "unicorn", "~> 4.4.0"
72 72  
  73 +# State machine
  74 +gem "state_machine"
  75 +
73 76 # Issue tags
74 77 gem "acts-as-taggable-on", "2.3.3"
75 78  
... ...
Gemfile.lock
... ... @@ -425,6 +425,7 @@ GEM
425 425 rack (~> 1.0)
426 426 tilt (~> 1.1, != 1.3.0)
427 427 stamp (0.3.0)
  428 + state_machine (1.1.2)
428 429 temple (0.5.5)
429 430 test_after_commit (0.0.1)
430 431 therubyracer (0.10.2)
... ... @@ -536,6 +537,7 @@ DEPENDENCIES
536 537 slim
537 538 spinach-rails
538 539 stamp
  540 + state_machine
539 541 test_after_commit
540 542 therubyracer
541 543 thin
... ...
app/assets/javascripts/main.js.coffee
... ... @@ -49,6 +49,10 @@ $ ->
49 49 # Bottom tooltip
50 50 $('.has_bottom_tooltip').tooltip(placement: 'bottom')
51 51  
  52 + # Form submitter
  53 + $('.trigger-submit').on 'change', ->
  54 + $(@).parents('form').submit()
  55 +
52 56 # Flash
53 57 if (flash = $("#flash-container")).length > 0
54 58 flash.click -> $(@).slideUp("slow")
... ...
app/assets/javascripts/merge_requests.js.coffee
... ... @@ -27,7 +27,7 @@ class MergeRequest
27 27 this.$el.find(selector)
28 28  
29 29 initMergeWidget: ->
30   - this.showState( @opts.current_state )
  30 + this.showState( @opts.current_status )
31 31  
32 32 if this.$('.automerge_widget').length and @opts.check_enable
33 33 $.get @opts.url_to_automerge_check, (data) =>
... ...
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
... ... @@ -63,7 +63,7 @@
63 63 color: $style_color;
64 64 text-shadow: 0 1px 1px #FFF;
65 65 font-family: 'Yanone', sans-serif;
66   - font-size: 26px;
67   - line-height: 42px;
  66 + font-size: 24px;
  67 + line-height: 36px;
68 68 font-weight: normal;
69 69 }
... ...
app/assets/stylesheets/sections/commits.scss
... ... @@ -29,7 +29,7 @@
29 29 a{
30 30 color: $style_color;
31 31 }
32   -
  32 +
33 33 > span {
34 34 font-family: $monospace_font;
35 35 font-size: 14px;
... ... @@ -124,7 +124,7 @@
124 124 .wrap{
125 125 display: inline-block;
126 126 }
127   -
  127 +
128 128 .frame {
129 129 display: inline-block;
130 130 background-color: #fff;
... ... @@ -149,7 +149,7 @@
149 149  
150 150 .view.swipe{
151 151 position: relative;
152   -
  152 +
153 153 .swipe-frame{
154 154 display: block;
155 155 margin: auto;
... ... @@ -228,7 +228,7 @@
228 228 bottom: 0px;
229 229 left: 50%;
230 230 margin-left: -150px;
231   -
  231 +
232 232 .drag-track{
233 233 display: block;
234 234 position: absolute;
... ... @@ -237,7 +237,7 @@
237 237 width: 276px;
238 238 background: url('onion_skin_sprites.gif') -4px -20px repeat-x;
239 239 }
240   -
  240 +
241 241 .dragger {
242 242 display: block;
243 243 position: absolute;
... ... @@ -248,7 +248,7 @@
248 248 background: url('onion_skin_sprites.gif') 0px -34px repeat-x;
249 249 cursor: pointer;
250 250 }
251   -
  251 +
252 252 .transparent {
253 253 display: block;
254 254 position: absolute;
... ... @@ -258,7 +258,7 @@
258 258 width: 10px;
259 259 background: url('onion_skin_sprites.gif') -2px 0px no-repeat;
260 260 }
261   -
  261 +
262 262 .opaque {
263 263 display: block;
264 264 position: absolute;
... ... @@ -275,19 +275,19 @@
275 275  
276 276 padding: 10px;
277 277 text-align: center;
278   -
  278 +
279 279 background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
280 280 background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
281 281 background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
282 282 background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
283   -
  283 +
284 284 ul, li{
285 285 list-style: none;
286 286 margin: 0;
287 287 padding: 0;
288 288 display: inline-block;
289 289 }
290   -
  290 +
291 291 li{
292 292 color: grey;
293 293 border-left: 1px solid #c1c1c1;
... ... @@ -322,12 +322,12 @@
322 322 }
323 323 .commit-author, .commit-committer{
324 324 display: block;
325   - color: #999;
326   - font-weight: normal;
  325 + color: #999;
  326 + font-weight: normal;
327 327 font-style: italic;
328 328 }
329 329 .commit-author strong, .commit-committer strong{
330   - font-weight: bold;
  330 + font-weight: bold;
331 331 font-style: normal;
332 332 }
333 333  
... ... @@ -337,7 +337,6 @@
337 337 */
338 338 .commit {
339 339 .browse_code_link_holder {
340   - @extend .span2;
341 340 float: right;
342 341 }
343 342  
... ...
app/assets/stylesheets/sections/header.scss
... ... @@ -5,15 +5,16 @@
5 5 header {
6 6 &.navbar-gitlab {
7 7 .navbar-inner {
8   - height: 45px;
9   - padding: 5px;
  8 + height: 40px;
  9 + padding: 3px;
10 10 background: #F1F1F1;
  11 + filter: none;
11 12  
12 13 .nav > li > a {
13 14 color: $style_color;
14 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 20 /** NAV block with links and profile **/
... ... @@ -25,7 +26,6 @@ header {
25 26 }
26 27  
27 28 z-index: 10;
28   - /*height: 60px;*/
29 29  
30 30 /**
31 31 *
... ... @@ -34,7 +34,7 @@ header {
34 34 */
35 35 .app_logo {
36 36 float: left;
37   - margin-right: 15px;
  37 + margin-right: 9px;
38 38 position: relative;
39 39 top: -5px;
40 40 padding-top: 5px;
... ... @@ -42,10 +42,10 @@ header {
42 42 a {
43 43 float: left;
44 44 padding: 0px;
45   - margin: 0 10px;
  45 + margin: 0 6px;
46 46  
47 47 h1 {
48   - background: url('logo_dark.png') no-repeat 0px 2px;
  48 + background: url('logo_dark.png') no-repeat center 1px;
49 49 float: left;
50 50 height: 40px;
51 51 width: 40px;
... ... @@ -79,7 +79,6 @@ header {
79 79 .search {
80 80 margin-right: 45px;
81 81 margin-left: 10px;
82   - margin-top: 2px;
83 82  
84 83 .search-input {
85 84 @extend .span2;
... ... @@ -105,7 +104,7 @@ header {
105 104 .account-box {
106 105 position: absolute;
107 106 right: 0;
108   - top: 6px;
  107 + top: 4px;
109 108 z-index: 10000;
110 109 width: 128px;
111 110 font-size: 11px;
... ... @@ -228,6 +227,7 @@ header {
228 227 .search-input {
229 228 background-color: #D2D5DA;
230 229 background-color: rgba(255, 255, 255, 0.5);
  230 + border: 1px solid #AAA;
231 231  
232 232 &:focus {
233 233 background-color: white;
... ... @@ -240,13 +240,16 @@ header {
240 240 .app_logo {
241 241 a {
242 242 h1 {
243   - background: url('logo_white.png') no-repeat center center;
  243 + background: url('logo_white.png') no-repeat center 1px;
244 244 color: #fff;
245 245 text-shadow: 0 1px 1px #111;
246 246 }
247 247 }
248 248 }
249 249 .project_name {
  250 + a {
  251 + color: #FFF;
  252 + }
250 253 color: #fff;
251 254 text-shadow: 0 1px 1px #111;
252 255 }
... ... @@ -261,11 +264,11 @@ header {
261 264  
262 265 .separator {
263 266 float: left;
264   - height: 60px;
  267 + height: 46px;
265 268 width: 1px;
266 269 background: white;
267 270 border-left: 1px solid #DDD;
268   - margin-top: -10px;
  271 + margin-top: -3px;
269 272 margin-left: 10px;
270 273 margin-right: 10px;
271 274 }
... ...
app/assets/stylesheets/sections/projects.scss
... ... @@ -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 8 *
9 9 */
10 10 .ui_mars {
11   -
12 11 /*
13 12 * Application Header
14 13 *
15 14 */
16 15 header {
17   -
  16 + @extend .header-dark;
18 17 &.navbar-gitlab {
19 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 14 end
15 15  
16 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 19 # Filter by specific assignee_id (or lack thereof)?
20 20 if params[:assignee_id].present?
... ...
app/contexts/projects/create_context.rb
... ... @@ -38,6 +38,8 @@ module Projects
38 38 if @project.valid? && @project.import_url.present?
39 39 shell = Gitlab::Shell.new
40 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 43 true
42 44 else
43 45 @project.errors.add(:import_url, 'cannot clone repo')
... ...
app/controllers/dashboard_controller.rb
... ... @@ -5,7 +5,7 @@ class DashboardController < ApplicationController
5 5 before_filter :event_filter, only: :show
6 6  
7 7 def show
8   - @groups = current_user.authorized_groups
  8 + @groups = current_user.authorized_groups.sort_by(&:human_name)
9 9 @has_authorized_projects = @projects.count > 0
10 10 @teams = current_user.authorized_teams
11 11 @projects_count = @projects.count
... ...
app/controllers/files_controller.rb 0 → 100644
... ... @@ -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 73 if @merge_request.unchecked?
74 74 @merge_request.check_if_can_be_merged
75 75 end
76   - render json: {state: @merge_request.human_state}
  76 + render json: {merge_status: @merge_request.human_merge_status}
77 77 rescue Gitlab::SatelliteNotExistError
78   - render json: {state: :no_satellite}
  78 + render json: {merge_status: :no_satellite}
79 79 end
80 80  
81 81 def automerge
82 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 84 @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
85 85 @merge_request.automerge!(current_user)
86 86 @status = true
... ...
app/controllers/milestones_controller.rb
... ... @@ -12,7 +12,7 @@ class MilestonesController &lt; ProjectResourceController
12 12  
13 13 def index
14 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 16 when 'closed'; @project.milestones.closed.order("due_date DESC")
17 17 else @project.milestones.active.order("due_date ASC")
18 18 end
... ...
app/controllers/profiles_controller.rb
... ... @@ -51,7 +51,9 @@ class ProfilesController &lt; ApplicationController
51 51 end
52 52  
53 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 58 respond_to do |format|
57 59 format.js
... ...
app/controllers/team_members_controller.rb
... ... @@ -4,7 +4,11 @@ class TeamMembersController &lt; ProjectResourceController
4 4 before_filter :authorize_admin_project!, except: [:index, :show]
5 5  
6 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 12 end
9 13  
10 14 def show
... ...
app/controllers/teams/members_controller.rb
... ... @@ -27,7 +27,13 @@ class Teams::MembersController &lt; Teams::ApplicationController
27 27 end
28 28  
29 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 37 if user_team.update_membership(team_member, options)
32 38 redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
33 39 else
... ... @@ -45,5 +51,4 @@ class Teams::MembersController &lt; Teams::ApplicationController
45 51 def team_member
46 52 @member ||= user_team.members.find_by_username(params[:id])
47 53 end
48   -
49 54 end
... ...
app/controllers/teams_controller.rb
... ... @@ -9,13 +9,11 @@ class TeamsController &lt; ApplicationController
9 9 layout 'user_team', except: [:new, :create]
10 10  
11 11 def show
12   - user_team
13 12 projects
14 13 @events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0)
15 14 end
16 15  
17 16 def edit
18   - user_team
19 17 end
20 18  
21 19 def update
... ... @@ -41,6 +39,9 @@ class TeamsController &lt; ApplicationController
41 39 @team.path = @team.name.dup.parameterize if @team.name
42 40  
43 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 45 redirect_to team_path(@team)
45 46 else
46 47 render action: :new
... ...
app/helpers/application_helper.rb
... ... @@ -73,8 +73,8 @@ module ApplicationHelper
73 73  
74 74 def search_autocomplete_source
75 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 79 default_nav = [
80 80 { label: "My Profile", url: profile_path },
... ... @@ -159,8 +159,13 @@ module ApplicationHelper
159 159 alt: "Sign in with #{provider.to_s.titleize}")
160 160 end
161 161  
  162 + def simple_sanitize str
  163 + sanitize(str, tags: %w(a span))
  164 + end
  165 +
162 166 def image_url(source)
163 167 root_url + path_to_image(source)
164 168 end
  169 +
165 170 alias_method :url_to_image, :image_url
166 171 end
... ...
app/helpers/dashboard_helper.rb
... ... @@ -27,6 +27,6 @@ module DashboardHelper
27 27 items.opened
28 28 end
29 29  
30   - items.where(assignee_id: current_user.id).count
  30 + items.cared(current_user).count
31 31 end
32 32 end
... ...
app/helpers/issues_helper.rb
... ... @@ -6,7 +6,7 @@ module IssuesHelper
6 6  
7 7 def issue_css_classes issue
8 8 classes = "issue"
9   - classes << " closed" if issue.closed
  9 + classes << " closed" if issue.closed?
10 10 classes << " today" if issue.today?
11 11 classes
12 12 end
... ...
app/helpers/merge_requests_helper.rb
... ... @@ -12,7 +12,7 @@ module MergeRequestsHelper
12 12  
13 13 def mr_css_classes mr
14 14 classes = "merge_request"
15   - classes << " closed" if mr.closed
  15 + classes << " closed" if mr.closed?
16 16 classes << " merged" if mr.merged?
17 17 classes
18 18 end
... ...
app/helpers/namespaces_helper.rb
... ... @@ -10,8 +10,8 @@ module NamespacesHelper
10 10  
11 11  
12 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 16 options = []
17 17 options << global_opts if current_user.admin
... ...
app/helpers/projects_helper.rb
1 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 2 def remove_from_project_team_message(project, user)
11 3 "You are going to remove #{user.name} from #{project.name} project team. Are you sure?"
12 4 end
... ... @@ -56,7 +48,7 @@ module ProjectsHelper
56 48 def project_title project
57 49 if project.group
58 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 52 end
61 53 else
62 54 project.name
... ...
app/models/ability.rb
... ... @@ -123,7 +123,7 @@ class Ability
123 123 def user_team_abilities user, team
124 124 rules = []
125 125  
126   - # Only group owner and administrators can manage group
  126 + # Only group owner and administrators can manage team
127 127 if team.owner == user || team.admin?(user) || user.admin?
128 128 rules << [ :manage_user_team ]
129 129 end
... ...
app/models/concerns/issuable.rb
... ... @@ -17,10 +17,9 @@ module Issuable
17 17 validates :project, presence: true
18 18 validates :author, presence: true
19 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 23 scope :of_group, ->(group) { where(project_id: group.project_ids) }
25 24 scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
26 25 scope :assigned, ->(u) { where(assignee_id: u.id)}
... ... @@ -62,14 +61,6 @@ module Issuable
62 61 assignee_id_changed?
63 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 65 # Votes
75 66 #
... ...
app/models/event.rb
... ... @@ -130,10 +130,6 @@ class Event &lt; ActiveRecord::Base
130 130 target if target_type == "MergeRequest"
131 131 end
132 132  
133   - def author
134   - @author ||= User.find(author_id)
135   - end
136   -
137 133 def action_name
138 134 if closed?
139 135 "closed"
... ...
app/models/issue.rb
... ... @@ -9,7 +9,7 @@
9 9 # project_id :integer
10 10 # created_at :datetime not null
11 11 # updated_at :datetime not null
12   -# closed :boolean default(FALSE), not null
  12 +# state :string default(FALSE), not null
13 13 # position :integer default(0)
14 14 # branch_name :string(255)
15 15 # description :text
... ... @@ -19,12 +19,35 @@
19 19 class Issue < ActiveRecord::Base
20 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 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 52 end
30 53 end
... ...
app/models/key.rb
... ... @@ -35,7 +35,7 @@ class Key &lt; ActiveRecord::Base
35 35  
36 36 def fingerprintable_key
37 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 39 file = Tempfile.new('key_file')
40 40 begin
41 41 file.puts key
... ... @@ -45,7 +45,7 @@ class Key &lt; ActiveRecord::Base
45 45 file.close
46 46 file.unlink # deletes the temp file
47 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 49 end
50 50  
51 51 def set_identifier
... ...
app/models/merge_request.rb
... ... @@ -9,15 +9,14 @@
9 9 # author_id :integer
10 10 # assignee_id :integer
11 11 # title :string(255)
12   -# closed :boolean default(FALSE), not null
  12 +# state :string(255) not null
13 13 # created_at :datetime not null
14 14 # updated_at :datetime not null
15 15 # st_commits :text(2147483647)
16 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 21 require Rails.root.join("app/models/commit")
23 22 require Rails.root.join("lib/static_model")
... ... @@ -25,11 +24,33 @@ require Rails.root.join(&quot;lib/static_model&quot;)
25 24 class MergeRequest < ActiveRecord::Base
26 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 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 54 BROKEN_DIFF = "--broken-diff"
34 55  
35 56 UNCHECKED = 1
... ... @@ -43,21 +64,33 @@ class MergeRequest &lt; ActiveRecord::Base
43 64 validates :target_branch, presence: true
44 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 85 end
53 86  
54   - def human_state
55   - states = {
  87 + def human_merge_status
  88 + merge_statuses = {
56 89 CAN_BE_MERGED => "can_be_merged",
57 90 CANNOT_BE_MERGED => "cannot_be_merged",
58 91 UNCHECKED => "unchecked"
59 92 }
60   - states[self.state]
  93 + merge_statuses[self.merge_status]
61 94 end
62 95  
63 96 def validate_branches
... ... @@ -72,20 +105,20 @@ class MergeRequest &lt; ActiveRecord::Base
72 105 end
73 106  
74 107 def unchecked?
75   - state == UNCHECKED
  108 + merge_status == UNCHECKED
76 109 end
77 110  
78 111 def mark_as_unchecked
79   - self.state = UNCHECKED
  112 + self.merge_status = UNCHECKED
80 113 self.save
81 114 end
82 115  
83 116 def can_be_merged?
84   - state == CAN_BE_MERGED
  117 + merge_status == CAN_BE_MERGED
85 118 end
86 119  
87 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 122 CAN_BE_MERGED
90 123 else
91 124 CANNOT_BE_MERGED
... ... @@ -98,7 +131,7 @@ class MergeRequest &lt; ActiveRecord::Base
98 131 end
99 132  
100 133 def reloaded_diffs
101   - if open? && unmerged_diffs.any?
  134 + if opened? && unmerged_diffs.any?
102 135 self.st_diffs = unmerged_diffs
103 136 self.save
104 137 end
... ... @@ -128,10 +161,6 @@ class MergeRequest &lt; ActiveRecord::Base
128 161 commits.first
129 162 end
130 163  
131   - def merged?
132   - merged && merge_event
133   - end
134   -
135 164 def merge_event
136 165 self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last
137 166 end
... ... @@ -146,26 +175,16 @@ class MergeRequest &lt; ActiveRecord::Base
146 175  
147 176 def probably_merged?
148 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 179 end
161 180  
162 181 def mark_as_unmergable
163   - self.state = CANNOT_BE_MERGED
  182 + self.merge_status = CANNOT_BE_MERGED
164 183 self.save
165 184 end
166 185  
167 186 def reloaded_commits
168   - if open? && unmerged_commits.any?
  187 + if opened? && unmerged_commits.any?
169 188 self.st_commits = unmerged_commits
170 189 save
171 190 end
... ... @@ -181,7 +200,8 @@ class MergeRequest &lt; ActiveRecord::Base
181 200 end
182 201  
183 202 def merge!(user_id)
184   - self.mark_as_merged!
  203 + self.merge
  204 +
185 205 Event.create(
186 206 project: self.project,
187 207 action: Event::MERGED,
... ...
app/models/milestone.rb
... ... @@ -13,19 +13,32 @@
13 13 #
14 14  
15 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 17 attr_accessor :author_id_of_changes
18 18  
19 19 belongs_to :project
20 20 has_many :issues
21 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 26 validates :title, presence: true
27 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 43 def expired?
31 44 if due_date
... ... @@ -68,17 +81,13 @@ class Milestone &lt; ActiveRecord::Base
68 81 end
69 82  
70 83 def can_be_closed?
71   - open? && issues.opened.count.zero?
  84 + active? && issues.opened.count.zero?
72 85 end
73 86  
74 87 def is_empty?
75 88 total_items_count.zero?
76 89 end
77 90  
78   - def open?
79   - !closed
80   - end
81   -
82 91 def author_id
83 92 author_id_of_changes
84 93 end
... ...
app/models/namespace.rb
... ... @@ -17,11 +17,15 @@ class Namespace &lt; ActiveRecord::Base
17 17 has_many :projects, dependent: :destroy
18 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 26 validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
22 27 format: { with: Gitlab::Regex.path_regex,
23 28 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
24   - validates :owner, presence: true
25 29  
26 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 43  
44 44 has_many :events, dependent: :destroy
45 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 47 has_many :milestones, dependent: :destroy
48 48 has_many :users_projects, dependent: :destroy
49 49 has_many :notes, dependent: :destroy
... ... @@ -146,7 +146,7 @@ class Project &lt; ActiveRecord::Base
146 146 end
147 147  
148 148 def saved?
149   - id && valid?
  149 + id && persisted?
150 150 end
151 151  
152 152 def import?
... ...
app/models/repository.rb
... ... @@ -132,16 +132,16 @@ class Repository
132 132 return nil unless commit
133 133  
134 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 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 139 # Put files into a directory before archiving
140 140 prefix = self.path_with_namespace + "/"
141 141  
142 142 # Create file if not exists
143 143 unless File.exists?(file_path)
144   - FileUtils.mkdir_p storage_path
  144 + FileUtils.mkdir_p File.dirname(file_path)
145 145 file = self.repo.archive_to_file(ref, prefix, file_path)
146 146 end
147 147  
... ...
app/models/user.rb
... ... @@ -234,8 +234,12 @@ class User &lt; ActiveRecord::Base
234 234 keys.count == 0
235 235 end
236 236  
  237 + def can_change_username?
  238 + Gitlab.config.gitlab.username_changing_enabled
  239 + end
  240 +
237 241 def can_create_project?
238   - projects_limit > personal_projects.count
  242 + projects_limit > owned_projects.count
239 243 end
240 244  
241 245 def can_create_group?
... ... @@ -263,7 +267,7 @@ class User &lt; ActiveRecord::Base
263 267 end
264 268  
265 269 def cared_merge_requests
266   - MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id)
  270 + MergeRequest.cared(self)
267 271 end
268 272  
269 273 # Remove user from all projects and
... ...
app/models/user_team.rb
... ... @@ -21,8 +21,11 @@ class UserTeam &lt; ActiveRecord::Base
21 21 has_many :projects, through: :user_team_project_relationships
22 22 has_many :members, through: :user_team_user_relationships, source: :user
23 23  
24   - validates :name, presence: true, uniqueness: true
25 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 29 validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
27 30 format: { with: Gitlab::Regex.path_regex,
28 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 26 user_team.name
27 27 end
28 28  
  29 + def human_max_access
  30 + UserTeam.access_roles.key(greatest_access)
  31 + end
  32 +
29 33 private
30 34  
31 35 def check_greatest_access
... ...
app/observers/activity_observer.rb
... ... @@ -20,15 +20,23 @@ class ActivityObserver &lt; ActiveRecord::Observer
20 20 end
21 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 24 Event.create(
26 25 project: record.project,
27 26 target_id: record.id,
28 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 39 author_id: record.author_id_of_changes
31 40 )
32   - end
33 41 end
34 42 end
... ...
app/observers/issue_observer.rb
... ... @@ -7,22 +7,31 @@ class IssueObserver &lt; ActiveRecord::Observer
7 7 end
8 8 end
9 9  
10   - def after_update(issue)
  10 + def after_close(issue, transition)
11 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 24 end
23 25  
24 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 35 def send_reassigned_email(issue)
27 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 7 end
8 8 end
9 9  
10   - def after_update(merge_request)
  10 + def after_close(merge_request, transition)
11 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 24 end
20 25  
21 26 protected
... ...
app/uploaders/attachment_uploader.rb
... ... @@ -19,4 +19,12 @@ class AttachmentUploader &lt; CarrierWave::Uploader::Base
19 19 rescue
20 20 false
21 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 30 end
... ...
app/views/admin/groups/index.html.haml
... ... @@ -28,7 +28,7 @@
28 28 %td= group.path
29 29 %td= group.projects.count
30 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 32 %td.bgred
33 33 = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
34 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 30 %td= team.projects.count
31 31 %td= team.members.count
32 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 34 %td.bgred
35 35 = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
36 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 6 = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
7 7 = commit.author_link avatar: true, size: 24
8 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 12 = time_ago_in_words(commit.committed_date)
13 13 ago
14 14 &nbsp;
... ...
app/views/events/event/_note.html.haml
... ... @@ -26,7 +26,7 @@
26 26 = markdown truncate(event.target.note, length: 70)
27 27 - note = event.target
28 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 30 - if note.attachment.image?
31 31 = image_tag note.attachment.url, class: 'note-image-attach'
32 32 - else
... ...
app/views/groups/_filter.html.haml
... ... @@ -26,6 +26,8 @@
26 26 = link_to group_filter_path(entity, project_id: project.id) do
27 27 = project.name_with_namespace
28 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 32 %fieldset
31 33 %hr
... ...
app/views/groups/_people_filter.html.haml
... ... @@ -7,6 +7,8 @@
7 7 = link_to people_group_path(@group, project_id: project.id) do
8 8 = project.name_with_namespace
9 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 13 %fieldset
12 14 %hr
... ...
app/views/groups/edit.html.haml
... ... @@ -30,6 +30,8 @@
30 30 = link_to 'Team', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
31 31 = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
32 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 36 .span5
35 37 .ui-box
... ...
app/views/help/index.html.haml
1 1 %h3.page_title
2 2 GITLAB
3 3 .pull-right
4   - %span= Gitlab::Version
5   - %small= Gitlab::Revision
  4 + %span= Gitlab::VERSION
  5 + %small= Gitlab::REVISION
6 6 %hr
7 7 %p.lead
8 8 Self Hosted Git Management
... ...
app/views/issues/_show.html.haml
... ... @@ -8,10 +8,10 @@
8 8 %i.icon-comment
9 9 = issue.notes.count
10 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 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 15 = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do
16 16 %i.icon-edit
17 17 Edit
... ...
app/views/issues/show.html.haml
... ... @@ -7,10 +7,10 @@
7 7  
8 8 %span.pull-right
9 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 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 14 - if can?(current_user, :admin_project, @project) || @issue.author == current_user
15 15 = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
16 16 %i.icon-edit
... ... @@ -27,7 +27,7 @@
27 27 .ui-box.ui-box-show
28 28 .ui-box-head
29 29 %h4.box-title
30   - - if @issue.closed
  30 + - if @issue.closed?
31 31 .error.status_info Closed
32 32 = gfm escape_once(@issue.title)
33 33  
... ...
app/views/merge_requests/_show.html.haml
... ... @@ -29,10 +29,10 @@
29 29 $(function(){
30 30 merge_request = new MergeRequest({
31 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 33 url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}",
34 34 ci_enable: #{@project.gitlab_ci? ? "true" : "false"},
35   - current_state: "#{@merge_request.human_state}",
  35 + current_status: "#{@merge_request.human_merge_status}",
36 36 action: "#{controller.action_name}"
37 37 });
38 38 });
... ...
app/views/merge_requests/show/_mr_accept.html.haml
... ... @@ -3,7 +3,7 @@
3 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 7 .automerge_widget.can_be_merged{style: "display:none"}
8 8 .alert.alert-success
9 9 %span
... ...
app/views/merge_requests/show/_mr_box.html.haml
1 1 .ui-box.ui-box-show
2 2 .ui-box-head
3 3 %h4.box-title
4   - - if @merge_request.merged
  4 + - if @merge_request.merged?
5 5 .error.status_info
6 6 %i.icon-ok
7 7 Merged
8   - - elsif @merge_request.closed
  8 + - elsif @merge_request.closed?
9 9 .error.status_info Closed
10 10 = gfm escape_once(@merge_request.title)
11 11  
... ... @@ -21,14 +21,14 @@
21 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 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 2 .ci_widget.ci-success{style: "display:none"}
3 3 .alert.alert-success
4 4 %i.icon-ok
... ...
app/views/merge_requests/show/_mr_title.html.haml
... ... @@ -7,7 +7,7 @@
7 7  
8 8 %span.pull-right
9 9 - if can?(current_user, :modify_merge_request, @merge_request)
10   - - if @merge_request.open?
  10 + - if @merge_request.opened?
11 11 .left.btn-group
12 12 %a.btn.grouped.dropdown-toggle{ data: {toggle: :dropdown} }
13 13 %i.icon-download-alt
... ... @@ -17,7 +17,7 @@
17 17 %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch)
18 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 22 = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do
23 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 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 4 = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link grouped" do
5 5 %i.icon-edit
6 6 Edit
7 7 %h4
8 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 10 %span.cred (Expired)
11 11 %small
12 12 = milestone.expires_at
... ...
app/views/milestones/show.html.haml
... ... @@ -9,7 +9,7 @@
9 9 &larr; To milestones list
10 10 .span6
11 11 .pull-right
12   - - unless @milestone.closed
  12 + - unless @milestone.closed?
13 13 = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small grouped", title: "New Issue" do
14 14 %i.icon-plus
15 15 New Issue
... ... @@ -25,12 +25,12 @@
25 25 %hr
26 26 %p
27 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 30 .ui-box.ui-box-show
31 31 .ui-box-head
32 32 %h4.box-title
33   - - if @milestone.closed
  33 + - if @milestone.closed?
34 34 .error.status_info Closed
35 35 - elsif @milestone.expired?
36 36 .error.status_info Expired
... ... @@ -63,7 +63,7 @@
63 63 %li=link_to('All Issues', '#')
64 64 %ul.well-list
65 65 - @issues.each do |issue|
66   - %li{data: {closed: issue.closed}}
  66 + %li{data: {closed: issue.closed?}}
67 67 = link_to [@project, issue] do
68 68 %span.badge.badge-info ##{issue.id}
69 69 &ndash;
... ... @@ -77,7 +77,7 @@
77 77 %li=link_to('All Merge Requests', '#')
78 78 %ul.well-list
79 79 - @merge_requests.each do |merge_request|
80   - %li{data: {closed: merge_request.closed}}
  80 + %li{data: {closed: merge_request.closed?}}
81 81 = link_to [@project, merge_request] do
82 82 %span.badge.badge-info ##{merge_request.id}
83 83 &ndash;
... ...
app/views/notes/_note.html.haml
... ... @@ -31,7 +31,7 @@
31 31 - if note.attachment.image?
32 32 = image_tag note.attachment.url, class: 'note-image-attach'
33 33 .attachment.pull-right
34   - = link_to note.attachment.url, target: "_blank" do
  34 + = link_to note.attachment.secure_url, target: "_blank" do
35 35 %i.icon-paper-clip
36 36 = note.attachment_identifier
37 37 .clear
... ...
app/views/profiles/account.html.haml
... ... @@ -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 81 - if Gitlab.config.gitlab.signup_enabled
81 82 %fieldset.remove-account
... ... @@ -83,4 +84,4 @@
83 84 Remove account
84 85 %small.cred.pull-right
85 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 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 77 %legend
78 78 Personal projects:
79 79 %small.pull-right
80   - %span= current_user.personal_projects.count
  80 + %span= current_user.owned_projects.count
81 81 of
82 82 %span= current_user.projects_limit
83 83 .padded
... ...
app/views/projects/_new_form.html.haml
... ... @@ -28,7 +28,7 @@
28 28 .input
29 29 = f.text_field :import_url, class: 'xlarge', placeholder: 'https://github.com/randx/six.git'
30 30 .light
31   - URL should be clonable
  31 + URL must be clonable
32 32  
33 33 %p.padded
34 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 9 - @notes.each do |note|
10 10 %tr
11 11 %td
12   - %a{href: note.attachment.url}
  12 + = link_to note.attachment.secure_url, target: "_blank" do
13 13 = image_tag gravatar_icon(note.author_email), class: "avatar s24"
14 14 = note.attachment_identifier
15 15 %td
... ...
app/views/team_members/_assigned_team.html.haml 0 → 100644
... ... @@ -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 @@
  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   -- 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   -- 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 2 .ui-box
3 3 %h5.title
4 4 = Project.access_options.key(access).pluralize
5 5 %small= members.size
6 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 @@
  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   -- 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 18 %hr
19 19  
20 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 1 %h3.page_title= "Edit Team #{@team.name}"
2 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 10 %br
11 11 %small.cgray= user.email
12 12  
13   - .span6.pull-right
  13 + .span4
14 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 17 %li All created teams are public (users can view who enter into team and which project are assigned for this team)
18 18 %li People within a team see only projects they have access to
19 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 21 return false
22 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 26 email = project.repository.commit(newrev).author.email rescue nil
28 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 36 key_id = identifier.gsub("key-", "")
33 37 Key.find_by_id(key_id).try(:user)
34 38 end
... ...
config/gitlab.yml.example
... ... @@ -7,121 +7,132 @@
7 7 # 2. Replace gitlab -> host with your domain
8 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 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 1 class Settings < Settingslogic
2 2 source "#{Rails.root}/config/gitlab.yml"
  3 + namespace Rails.env
3 4  
4 5 class << self
5 6 def gitlab_on_non_standard_port?
... ... @@ -56,6 +57,7 @@ Settings.gitlab[&#39;support_email&#39;] ||= Settings.gitlab.email_from
56 57 Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url)
57 58 Settings.gitlab['user'] ||= 'git'
58 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 63 # Gravatar
... ...
config/initializers/2_app.rb
1 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 5 def self.config
6 6 Settings
... ...
config/routes.rb
... ... @@ -47,6 +47,11 @@ Gitlab::Application.routes.draw do
47 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 55 # Admin Area
51 56 #
52 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 3 projects = [
4 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 16 project_id: project.id,
17 17 author_id: user_id,
18 18 assignee_id: user_id,
19   - closed: [true, false].sample,
  19 + state: ['opened', 'closed'].sample,
20 20 milestone: project.milestones.sample,
21 21 title: Faker::Lorem.sentence(6)
22 22 }])
... ...
db/fixtures/development/10_merge_requests.rb
... ... @@ -17,7 +17,7 @@ Gitlab::Seeder.quiet do
17 17 project_id: project.id,
18 18 author_id: user_id,
19 19 assignee_id: user_id,
20   - closed: [true, false].sample,
  20 + state: ['opened', 'closed'].sample,
21 21 milestone: project.milestones.sample,
22 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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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
... ...
db/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 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 16 create_table "events", :force => true do |t|
17 17 t.string "target_type"
... ... @@ -37,18 +37,17 @@ ActiveRecord::Schema.define(:version =&gt; 20130131070232) do
37 37 t.integer "assignee_id"
38 38 t.integer "author_id"
39 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 42 t.integer "position", :default => 0
44 43 t.string "branch_name"
45 44 t.text "description"
46 45 t.integer "milestone_id"
  46 + t.string "state"
47 47 end
48 48  
49 49 add_index "issues", ["assignee_id"], :name => "index_issues_on_assignee_id"
50 50 add_index "issues", ["author_id"], :name => "index_issues_on_author_id"
51   - add_index "issues", ["closed"], :name => "index_issues_on_closed"
52 51 add_index "issues", ["created_at"], :name => "index_issues_on_created_at"
53 52 add_index "issues", ["milestone_id"], :name => "index_issues_on_milestone_id"
54 53 add_index "issues", ["project_id"], :name => "index_issues_on_project_id"
... ... @@ -69,25 +68,23 @@ ActiveRecord::Schema.define(:version =&gt; 20130131070232) do
69 68 add_index "keys", ["user_id"], :name => "index_keys_on_user_id"
70 69  
71 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 74 t.integer "author_id"
76 75 t.integer "assignee_id"
77 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 79 t.text "st_commits", :limit => 2147483647
82 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 82 t.integer "milestone_id"
  83 + t.string "state"
86 84 end
87 85  
88 86 add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id"
89 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 88 add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at"
92 89 add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id"
93 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 93 add_index "merge_requests", ["title"], :name => "index_merge_requests_on_title"
97 94  
98 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 98 t.text "description"
102 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 103 end
107 104  
108 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 34 Parameters:
35 35  
36 36 + `id` (required) - The ID of a project
37   -+ `milestone_id` (required) - The ID of a project milestone
38 37 + `title` (required) - The title of an milestone
39 38 + `description` (optional) - The description of the milestone
40 39 + `due_date` (optional) - The due date of the milestone
... ...
doc/api/projects.md
... ... @@ -23,7 +23,7 @@ GET /projects
23 23 "blocked": false,
24 24 "created_at": "2012-05-23T08:00:58Z"
25 25 },
26   - "private": true,
  26 + "public": true,
27 27 "path": "rails",
28 28 "path_with_namespace": "rails/rails",
29 29 "issues_enabled": false,
... ... @@ -45,7 +45,7 @@ GET /projects
45 45 "blocked": false,
46 46 "created_at": "2012-05-23T08:00:58Z"
47 47 },
48   - "private": true,
  48 + "public": true,
49 49 "path": "gitlab",
50 50 "path_with_namespace": "randx/gitlab",
51 51 "issues_enabled": true,
... ... @@ -89,7 +89,7 @@ Parameters:
89 89 "blocked": false,
90 90 "created_at": "2012-05-23T08:00:58Z"
91 91 },
92   - "private": true,
  92 + "public": true,
93 93 "path": "gitlab",
94 94 "path_with_namespace": "randx/gitlab",
95 95 "issues_enabled": true,
... ...
doc/install/databases.md
... ... @@ -27,7 +27,7 @@ GitLab supports the following databases:
27 27 mysql> \q
28 28  
29 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 32 ## PostgreSQL
33 33  
... ... @@ -47,5 +47,5 @@ GitLab supports the following databases:
47 47 template1=# \q
48 48  
49 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 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 6 **Important Note:**
... ... @@ -8,12 +8,13 @@ The following steps have been known to work.
8 8 If you deviate from this guide, do it with caution and make sure you don't
9 9 violate any assumptions GitLab makes about its environment.
10 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 15 **Important Note:**
15 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 25 1. Packages / Dependencies
25 26 2. Ruby
26 27 3. System Users
27   -4. Gitolite
  28 +4. GitLab shell
28 29 5. Database
29 30 6. GitLab
30 31 7. Nginx
... ... @@ -32,16 +33,13 @@ The GitLab installation consists of setting up the following components:
32 33  
33 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 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 44 **Note:**
47 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 94  
97 95 # 4. GitLab shell
98 96  
99   - # login as git
  97 + # Login as git
100 98 sudo su git
101 99  
102   - # go to home directory
  100 + # Go to home directory
103 101 cd /home/git
104 102  
105   - # clone gitlab shell
  103 + # Clone gitlab shell
106 104 git clone https://github.com/gitlabhq/gitlab-shell.git
107 105  
108   - # setup
  106 + # Setup
109 107 cd gitlab-shell
110 108 cp config.yml.example config.yml
111 109 ./bin/install
112 110  
113 111  
114   -
115 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 117 # 6. GitLab
... ... @@ -154,9 +151,13 @@ do so with caution!
154 151 sudo chmod -R u+rwX log/
155 152 sudo chmod -R u+rwX tmp/
156 153  
157   - # Make directory for satellites
  154 + # Create directory for satellites
158 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 161 # Copy the example Unicorn config
161 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 188  
188 189  
189 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 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 208  
206 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 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 230  
228 231 **Note:**
229 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 235 ## Installation
233 236 sudo apt-get install nginx
... ... @@ -244,11 +247,11 @@ Make sure to edit the config file to match your setup:
244 247 # Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN**
245 248 # to the IP address and fully-qualified domain name
246 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 252 ## Restart
250 253  
251   - sudo /etc/init.d/nginx restart
  254 + sudo service nginx restart
252 255  
253 256  
254 257 # Done!
... ... @@ -282,7 +285,7 @@ a different host, you can configure its connection string via the
282 285  
283 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 290 # Add to /home/git/.ssh/config
288 291 host localhost # Give your setup a name (here: override localhost)
... ...