Commit bda7fe38d0b0e39a408c4eb44374a330c24c3a49

Authored by Riyad Preukschas
2 parents d28176b1 d8e697ac

Merge branch 'master' into discussions

Showing 70 changed files with 570 additions and 310 deletions   Show diff stats
.travis.yml
  1 +language: ruby
1 2 env:
2 3 - DB=postgresql
3 4 - DB=mysql
... ... @@ -8,7 +9,7 @@ branches:
8 9 only:
9 10 - 'master'
10 11 rvm:
11   - - 1.9.2
  12 + - 1.9.3-p327
12 13 services:
13 14 - mysql
14 15 - postgresql
... ...
CHANGELOG
  1 +v 4.1.0
  2 + - Project public mode (only admin can set now)
  3 + - Public area with unauthorized access
  4 + - Load dashboard events with ajax
  5 + - remember dashboard filter in cookies
  6 + - replace resque with sidekiq
  7 + - fix routing issues
  8 + - cleanup rake tasks
  9 + - fix backup/restore
  10 + - scss cleanup
  11 + - show preview for note images
  12 + - improved network-graph
  13 + - get rid of app/roles/
  14 + - added new classes Team, Repository
  15 + - Reduce amount of gitolite calls
  16 + - Ability to add user in all group projects
  17 + - remove derecated configs
  18 + - replaced Korolev font with open font
  19 + - restyled admin/dashboard page
  20 + - restyled admin/projects page
  21 +
1 22 v 4.0.0
2 23 - Remove project code and path from API. Use id instead
3 24 - Return valid clonable url to repo for web hook
... ...
Gemfile
... ... @@ -71,7 +71,6 @@ gem "redcarpet", "~> 2.2.2"
71 71 gem "github-markup", "~> 0.7.4", require: 'github/markup'
72 72  
73 73 # Servers
74   -gem "thin", '~> 1.5.0'
75 74 gem "unicorn", "~> 4.4.0"
76 75  
77 76 # Issue tags
... ... @@ -111,7 +110,7 @@ group :assets do
111 110 gem "modernizr", "2.6.2"
112 111 gem "raphael-rails", git: "https://github.com/gitlabhq/raphael-rails.git"
113 112 gem 'bootstrap-sass', "2.2.1.1"
114   - gem "font-awesome-sass-rails", "~> 2.0.0"
  113 + gem "font-awesome-sass-rails", "~> 3.0.0"
115 114 gem "gemoji", "~> 1.2.1", require: 'emoji/railtie'
116 115 end
117 116  
... ...
Gemfile.lock
... ... @@ -144,7 +144,6 @@ GEM
144 144 colorize (0.5.8)
145 145 connection_pool (1.0.0)
146 146 crack (0.3.1)
147   - daemons (1.1.9)
148 147 devise (2.1.2)
149 148 bcrypt-ruby (~> 3.0)
150 149 orm_adapter (~> 0.1)
... ... @@ -174,7 +173,7 @@ GEM
174 173 eventmachine (>= 0.12.0)
175 174 ffaker (1.15.0)
176 175 ffi (1.1.5)
177   - font-awesome-sass-rails (2.0.0.0)
  176 + font-awesome-sass-rails (3.0.0.1)
178 177 railties (>= 3.1.1)
179 178 sass-rails (>= 3.1.1)
180 179 foreman (0.60.2)
... ... @@ -381,7 +380,7 @@ GEM
381 380 rspec-mocks (~> 2.12.0)
382 381 rubyntlm (0.1.1)
383 382 rubyzip (0.9.9)
384   - sass (3.2.3)
  383 + sass (3.2.5)
385 384 sass-rails (3.2.5)
386 385 railties (~> 3.2.0)
387 386 sass (>= 3.1.10)
... ... @@ -437,10 +436,6 @@ GEM
437 436 test_after_commit (0.0.1)
438 437 therubyracer (0.10.2)
439 438 libv8 (~> 3.3.10)
440   - thin (1.5.0)
441   - daemons (>= 1.0.9)
442   - eventmachine (>= 0.12.6)
443   - rack (>= 1.0.0)
444 439 thor (0.16.0)
445 440 tilt (1.3.3)
446 441 timers (1.0.2)
... ... @@ -488,7 +483,7 @@ DEPENDENCIES
488 483 email_spec
489 484 factory_girl_rails
490 485 ffaker
491   - font-awesome-sass-rails (~> 2.0.0)
  486 + font-awesome-sass-rails (~> 3.0.0)
492 487 foreman
493 488 gemoji (~> 1.2.1)
494 489 git
... ... @@ -547,7 +542,6 @@ DEPENDENCIES
547 542 stamp
548 543 test_after_commit
549 544 therubyracer
550   - thin (~> 1.5.0)
551 545 uglifier (~> 1.3.0)
552 546 unicorn (~> 4.4.0)
553 547 webmock
... ...
Procfile
1   -web: bundle exec rails s -p $PORT
  1 +web: bundle exec unicorn_rails -p $PORT
2 2 worker: bundle exec sidekiq -q post_receive,mailer,system_hook,common,default
... ...
app/assets/images/diff_file_add.png

177 Bytes

app/assets/images/diff_file_delete.png

295 Bytes

app/assets/images/diff_file_info.png

749 Bytes

app/assets/images/diff_file_notice.png

302 Bytes

app/assets/images/event_filter_comments.png

750 Bytes

app/assets/images/event_filter_merged.png

463 Bytes

app/assets/images/event_filter_push.png

632 Bytes

app/assets/images/event_filter_team.png

1.31 KB

app/assets/images/event_mr_merged.png

463 Bytes

app/assets/images/event_push.png

632 Bytes

app/assets/images/list_view_icon.jpg

357 Bytes

app/assets/javascripts/dashboard.js.coffee
1   -$ ->
2   - dashboardPage()
3   -
4   -dashboardPage = ->
  1 +window.dashboardPage = ->
5 2 Pager.init 20, true
6 3 $(".event_filter_link").bind "click", (event) ->
7 4 event.preventDefault()
... ...
app/assets/stylesheets/application.scss
... ... @@ -32,6 +32,7 @@
32 32 @import "sections/profile.scss";
33 33 @import "sections/login.scss";
34 34 @import "sections/editor.scss";
  35 +@import "sections/admin.scss";
35 36  
36 37 @import "highlight/white.scss";
37 38 @import "highlight/dark.scss";
... ...
app/assets/stylesheets/common.scss
... ... @@ -371,6 +371,7 @@ li.note {
371 371 font-size: 48px;
372 372 padding: 20px;
373 373 text-align: center;
  374 + font-weight: normal;
374 375 }
375 376 }
376 377 }
... ... @@ -445,6 +446,19 @@ li.note {
445 446 }
446 447 }
447 448  
  449 +.warning_message {
  450 + border-left: 4px solid #ed9;
  451 + color: #b90;
  452 + padding: 10px;
  453 + margin-bottom: 10px;
  454 + background: #ffffe6;
  455 + padding-left: 20px;
  456 +
  457 + &.centered {
  458 + text-align: center;
  459 + }
  460 +}
  461 +
448 462 .oauth_select_holder {
449 463 padding: 20px;
450 464 img {
... ...
app/assets/stylesheets/gitlab_bootstrap/files.scss
... ... @@ -37,24 +37,6 @@
37 37 background: #fff;
38 38 font-size: 11px;
39 39  
40   - &.wiki {
41   - font-size: 13px;
42   - code {
43   - padding: 0 4px;
44   - }
45   - padding: 20px;
46   -
47   - h1 { font-size: 26px; line-height: 46px; }
48   - h2 { font-size: 22px; line-height: 42px; }
49   - h3 { font-size: 20px; line-height: 40px; }
50   - h4 { font-size: 18px; line-height: 32px; }
51   - h5 { font-size: 16px; line-height: 26px; }
52   -
53   - .white .highlight pre {
54   - background: #f5f5f5;
55   - }
56   - }
57   -
58 40 &.image_file {
59 41 background: #eee;
60 42 text-align: center;
... ... @@ -64,6 +46,11 @@
64 46 }
65 47 }
66 48  
  49 + &.wiki {
  50 + padding: 20px;
  51 + font-size: 13px;
  52 + }
  53 +
67 54 &.blob_file {
68 55  
69 56 }
... ...
app/assets/stylesheets/gitlab_bootstrap/typography.scss
... ... @@ -81,3 +81,22 @@ a:focus {
81 81 .monospace {
82 82 font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
83 83 }
  84 +
  85 +/**
  86 + * Wiki typography
  87 + *
  88 + */
  89 +.wiki {
  90 + font-size: 13px;
  91 +
  92 + code { padding: 0 4px; }
  93 + p { font-size: 13px; }
  94 + h1 { font-size: 32px; line-height: 40px; margin: 10px 0;}
  95 + h2 { font-size: 26px; line-height: 40px; margin: 10px 0;}
  96 + h3 { font-size: 22px; line-height: 40px; margin: 10px 0;}
  97 + h4 { font-size: 18px; line-height: 20px; margin: 10px 0;}
  98 + h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
  99 + h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
  100 + .white .highlight pre { background: #f5f5f5; }
  101 + ul { margin: 0 0 9px 25px !important; }
  102 +}
... ...
app/assets/stylesheets/sections/admin.scss 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +.admin-filter form {
  2 + label { width: 110px; }
  3 + .controls { margin-left: 130px; }
  4 + .form-actions { padding-left: 130px; background: #fff }
  5 +}
... ...
app/assets/stylesheets/sections/events.scss
... ... @@ -132,21 +132,25 @@
132 132 .event_filter {
133 133 position: absolute;
134 134 width: 40px;
135   - margin-left: -50px;
  135 + margin-left: -55px;
136 136  
137 137 .filter_icon {
138   - float: left;
139   - border-left: 3px solid #4bc;
140   - padding: 7px;
141   - background: #f9f9f9;
142   - margin-bottom: 10px;
143   - img {
144   - width: 20px;
  138 + a {
  139 + text-align:center;
  140 + border-left: 3px solid #29B;
  141 + background: #f9f9f9;
  142 + margin-bottom: 10px;
  143 + float: left;
  144 + padding: 9px 7px;
  145 + font-size: 18px;
  146 + width: 26px;
145 147 }
146 148  
147 149 &.inactive {
148   - border-left: 3px solid #EEE;
149   - opacity: 0.5;
  150 + a {
  151 + color: #DDD;
  152 + border-left: 3px solid #EEE;
  153 + }
150 154 }
151 155 }
152 156 }
... ...
app/assets/stylesheets/sections/tree.scss
... ... @@ -80,6 +80,18 @@
80 80 margin: 0;
81 81 padding: 0;
82 82 }
  83 + td.blame-commit {
  84 + background: #f9f9f9;
  85 + min-width: 350px;
  86 + }
  87 + td.blame-numbers {
  88 + pre {
  89 + color: #AAA;
  90 + white-space: pre;
  91 + }
  92 + background: #f1f1f1;
  93 + border-left: 1px solid #DDD;
  94 + }
83 95 }
84 96 }
85 97  
... ...
app/controllers/admin/projects_controller.rb
... ... @@ -4,6 +4,9 @@ class Admin::ProjectsController < AdminController
4 4 def index
5 5 @projects = Project.scoped
6 6 @projects = @projects.where(namespace_id: params[:namespace_id]) if params[:namespace_id].present?
  7 + @projects = @projects.where(public: true) if params[:public_only].present?
  8 + @projects = @projects.with_push if params[:with_push].present?
  9 + @projects = @projects.abandoned if params[:abandoned].present?
7 10 @projects = @projects.where(namespace_id: nil) if params[:namespace_id] == Namespace.global_id
8 11 @projects = @projects.search(params[:name]) if params[:name].present?
9 12 @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
... ...
app/controllers/public/projects_controller.rb 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +class Public::ProjectsController < ApplicationController
  2 + skip_before_filter :authenticate_user!,
  3 + :reject_blocked, :set_current_user_for_observers,
  4 + :add_abilities
  5 +
  6 + layout 'public'
  7 +
  8 + def index
  9 + @projects = Project.public
  10 + @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
  11 + end
  12 +end
... ...
app/controllers/wikis_controller.rb
... ... @@ -2,20 +2,19 @@ class WikisController &lt; ProjectResourceController
2 2 before_filter :authorize_read_wiki!
3 3 before_filter :authorize_write_wiki!, only: [:edit, :create, :history]
4 4 before_filter :authorize_admin_wiki!, only: :destroy
5   -
  5 +
6 6 def pages
7   - @wikis = @project.wikis.group(:slug).order("created_at")
  7 + @wiki_pages = @project.wikis.group(:slug).ordered
8 8 end
9 9  
10 10 def show
11   - if params[:old_page_id]
12   - @wiki = @project.wikis.find(params[:old_page_id])
  11 + @most_recent_wiki = @project.wikis.where(slug: params[:id]).ordered.first
  12 + if params[:version_id]
  13 + @wiki = @project.wikis.find(params[:version_id])
13 14 else
14   - @wiki = @project.wikis.where(slug: params[:id]).order("created_at").last
  15 + @wiki = @most_recent_wiki
15 16 end
16 17  
17   - @note = @project.notes.new(noteable: @wiki)
18   -
19 18 if @wiki
20 19 render 'show'
21 20 else
... ... @@ -29,7 +28,7 @@ class WikisController &lt; ProjectResourceController
29 28 end
30 29  
31 30 def edit
32   - @wiki = @project.wikis.where(slug: params[:id]).order("created_at").last
  31 + @wiki = @project.wikis.where(slug: params[:id]).ordered.first
33 32 @wiki = Wiki.regenerate_from @wiki
34 33 end
35 34  
... ... @@ -47,9 +46,9 @@ class WikisController &lt; ProjectResourceController
47 46 end
48 47  
49 48 def history
50   - @wikis = @project.wikis.where(slug: params[:id]).order("created_at")
  49 + @wiki_pages = @project.wikis.where(slug: params[:id]).ordered
51 50 end
52   -
  51 +
53 52 def destroy
54 53 @wikis = @project.wikis.where(slug: params[:id]).delete_all
55 54  
... ...
app/helpers/events_helper.rb
... ... @@ -30,8 +30,17 @@ module EventsHelper
30 30  
31 31 content_tag :div, class: "filter_icon #{inactive}" do
32 32 link_to dashboard_path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do
33   - image_tag "event_filter_#{key}.png"
  33 + content_tag :i, nil, class: icon_for_event[key]
34 34 end
35 35 end
36 36 end
  37 +
  38 + def icon_for_event
  39 + {
  40 + EventFilter.push => "icon-upload-alt",
  41 + EventFilter.merged => "icon-check",
  42 + EventFilter.comments => "icon-comments",
  43 + EventFilter.team => "icon-user",
  44 + }
  45 + end
37 46 end
... ...
app/helpers/projects_helper.rb
... ... @@ -26,19 +26,17 @@ module ProjectsHelper
26 26 # Build avatar image tag
27 27 avatar = image_tag(gravatar_icon(author.try(:email)), width: 16, class: "lil_av")
28 28  
29   - # Build name strong tag
30   - name = content_tag :strong, author.name, class: 'author'
  29 + # Build name span tag
  30 + name = content_tag :span, author.name, class: 'author'
31 31  
32 32 author_html = avatar + name
33 33  
34 34 tm = project.team_member_by_id(author)
35 35  
36   - content_tag :span, class: 'member-link' do
37   - if tm
38   - link_to author_html, project_team_member_path(project, tm), class: "author_link"
39   - else
40   - author_html
41   - end
  36 + if tm
  37 + link_to author_html, project_team_member_path(project, tm), class: "author_link"
  38 + else
  39 + author_html
42 40 end
43 41 end
44 42  
... ...
app/models/project.rb
... ... @@ -28,7 +28,7 @@ class Project &lt; ActiveRecord::Base
28 28 attr_accessible :name, :path, :description, :default_branch, :issues_enabled,
29 29 :wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin]
30 30  
31   - attr_accessible :namespace_id, :creator_id, as: :admin
  31 + attr_accessible :namespace_id, :creator_id, :public, as: :admin
32 32  
33 33 attr_accessor :error_code
34 34  
... ... @@ -81,8 +81,21 @@ class Project &lt; ActiveRecord::Base
81 81 scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") }
82 82 scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
83 83 scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
  84 + scope :public, where(public: true)
84 85  
85 86 class << self
  87 + def abandoned
  88 + project_ids = Event.select('max(created_at) as latest_date, project_id').
  89 + group('project_id').
  90 + having('latest_date < ?', 6.months.ago).map(&:project_id)
  91 +
  92 + where(id: project_ids)
  93 + end
  94 +
  95 + def with_push
  96 + includes(:events).where('events.action = ?', Event::Pushed)
  97 + end
  98 +
86 99 def active
87 100 joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
88 101 end
... ...
app/models/repository.rb
... ... @@ -151,12 +151,12 @@ class Repository
151 151 return nil unless commit
152 152  
153 153 # Build file path
154   - file_name = self.path + "-" + commit.id.to_s + ".tar.gz"
155   - storage_path = Rails.root.join("tmp", "repositories", self.path_with_namespace)
  154 + file_name = self.path_with_namespace + "-" + commit.id.to_s + ".tar.gz"
  155 + storage_path = Rails.root.join("tmp", "repositories")
156 156 file_path = File.join(storage_path, file_name)
157 157  
158 158 # Put files into a directory before archiving
159   - prefix = self.path + "/"
  159 + prefix = self.path_with_namespace + "/"
160 160  
161 161 # Create file if not exists
162 162 unless File.exists?(file_path)
... ...
app/models/wiki.rb
... ... @@ -25,6 +25,8 @@ class Wiki &lt; ActiveRecord::Base
25 25  
26 26 before_update :set_slug
27 27  
  28 + scope :ordered, order("created_at DESC")
  29 +
28 30 def to_param
29 31 slug
30 32 end
... ...
app/views/admin/dashboard/index.html.haml
... ... @@ -25,17 +25,51 @@
25 25 = link_to 'New User', new_admin_user_path, class: "btn small"
26 26  
27 27 .row
28   - .span6
29   - %h3 Latest projects
  28 + .span4
  29 + %h4 Latest projects
30 30 %hr
31 31 - @projects.each do |project|
32 32 %p
33 33 = link_to project.name_with_namespace, [:admin, project]
34   - .span6
35   - %h3 Latest users
  34 + %span.light.right
  35 + = time_ago_in_words project.created_at
  36 + ago
  37 +
  38 + .span4
  39 + %h4 Latest users
36 40 %hr
37 41 - @users.each do |user|
38 42 %p
39 43 = link_to [:admin, user] do
40 44 = user.name
41   - %small= user.email
  45 + %span.light.right
  46 + = time_ago_in_words user.created_at
  47 + ago
  48 +
  49 + .span4
  50 + %h4 Stats
  51 + %hr
  52 + %p
  53 + Issues
  54 + %span.light.right
  55 + = Issue.count
  56 + %p
  57 + Merge Requests
  58 + %span.light.right
  59 + = MergeRequest.count
  60 + %p
  61 + Notes
  62 + %span.light.right
  63 + = Note.count
  64 + %p
  65 + Snippets
  66 + %span.light.right
  67 + = Snippet.count
  68 + %p
  69 + SSH Keys
  70 + %span.light.right
  71 + = Key.count
  72 + %p
  73 + Milestones
  74 + %span.light.right
  75 + = Milestone.count
... ...
app/views/admin/logs/show.html.haml
... ... @@ -5,6 +5,8 @@
5 5 = link_to "application.log", "#application", 'data-toggle' => 'tab'
6 6 %li
7 7 = link_to "production.log", "#production", 'data-toggle' => 'tab'
  8 + %li
  9 + = link_to "sidekiq.log", "#sidekiq", 'data-toggle' => 'tab'
8 10  
9 11 %p.light To prevent perfomance issues admin logs output the last 2000 lines
10 12 .tab-content
... ... @@ -50,3 +52,17 @@
50 52 - Gitlab::Logger.read_latest_for('production.log').each do |line|
51 53 %li
52 54 %p= line
  55 + .tab-pane#sidekiq
  56 + .file_holder#README
  57 + .file_title
  58 + %i.icon-file
  59 + sidekiq.log
  60 + .right
  61 + = link_to '#', class: 'log-bottom' do
  62 + %i.icon-arrow-down
  63 + Scroll down
  64 + .file_content.logs
  65 + %ol
  66 + - Gitlab::Logger.read_latest_for('sidekiq.log').each do |line|
  67 + %li
  68 + %p= line
... ...
app/views/admin/projects/_form.html.haml
... ... @@ -44,6 +44,13 @@
44 44 .input= f.check_box :wiki_enabled
45 45  
46 46 %fieldset.features
  47 + %legend Public mode:
  48 + .clearfix
  49 + = f.label :public do
  50 + %span Allow public http clone
  51 + .input= f.check_box :public
  52 +
  53 + %fieldset.features
47 54 %legend Transfer:
48 55 .control-group
49 56 = f.label :namespace_id do
... ...
app/views/admin/projects/index.html.haml
1 1 %h3.page_title
2   - Projects (#{Project.count})
  2 + Projects
3 3 = link_to 'New Project', new_project_path, class: "btn small right"
4   -%br
5   -= form_tag admin_projects_path, method: :get, class: 'form-inline' do
6   - = select_tag :namespace_id, namespaces_options(params[:namespace_id], :all), class: "chosen xlarge", prompt: "Project namespace"
7   - = text_field_tag :name, params[:name], class: "xlarge"
8   - = submit_tag "Search", class: "btn submit primary"
9 4  
10   -%table
11   - %thead
12   - %tr
13   - %th
14   - Name
15   - %i.icon-sort-down
16   - %th Path
17   - %th Team Members
18   - %th Owner
19   - %th Last Commit
20   - %th Edit
21   - %th.cred Danger Zone!
  5 +%hr
22 6  
23   - - @projects.each do |project|
24   - %tr
25   - %td
26   - = link_to project.name_with_namespace, [:admin, project]
27   - %td
28   - %span.monospace= project.path_with_namespace + ".git"
29   - %td= project.users_projects.count
30   - %td
31   - - if project.owner
32   - = link_to project.owner.name, [:admin, project.owner]
  7 +.row
  8 + .span4
  9 + .admin-filter
  10 + = form_tag admin_projects_path, method: :get, class: 'form-inline' do
  11 + .control-group
  12 + = label_tag :name, 'Name:', class: 'control-label'
  13 + .controls
  14 + = text_field_tag :name, params[:name], class: "span2"
  15 +
  16 + .control-group
  17 + = label_tag :namespace_id, 'Namespace:', class: 'control-label'
  18 + .controls
  19 + = select_tag :namespace_id, namespaces_options(params[:namespace_id], :all), class: "chosen span2", prompt: "Any"
  20 + .control-group
  21 + = label_tag :public_only, 'Public Only', class: 'control-label'
  22 + .controls
  23 + = check_box_tag :public_only, 1, params[:public_only]
  24 + .control-group
  25 + = label_tag :with_push, 'Not empty', class: 'control-label'
  26 + .controls
  27 + = check_box_tag :with_push, 1, params[:with_push]
  28 + &nbsp;
  29 + %span.light Projects with push events
  30 + .control-group
  31 + = label_tag :abandoned, 'Abandoned', class: 'control-label'
  32 + .controls
  33 + = check_box_tag :abandoned, 1, params[:abandoned]
  34 + &nbsp;
  35 + %span.light No activity over 6 month
  36 +
  37 +
  38 +
  39 + .form-actions
  40 + = submit_tag "Search", class: "btn submit primary"
  41 + = link_to "Reset", admin_projects_path, class: "btn"
  42 + .span8
  43 + .ui-box
  44 + %h5.title
  45 + Projects (#{@projects.total_count})
  46 + %ul.well-list
  47 + - @projects.each do |project|
  48 + %li
  49 + - if project.public
  50 + %i.icon-unlock.cred
  51 + - else
  52 + %i.icon-lock.cgreen
  53 + = link_to project.name_with_namespace, [:admin, project]
  54 + .right
  55 + = link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn small"
  56 + = link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn small danger"
  57 + - if @projects.blank?
  58 + %p.nothing_here_message 0 projects matches
33 59 - else
34   - (deleted)
35   - %td= last_commit(project)
36   - %td= link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn small"
37   - %td.bgred= link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn small danger"
38   -= paginate @projects, theme: "admin"
  60 + %li.bottom
  61 + = paginate @projects, theme: "gitlab"
... ...
app/views/admin/projects/show.html.haml
... ... @@ -77,6 +77,13 @@
77 77 SSH:
78 78 %td
79 79 = link_to @project.ssh_url_to_repo
  80 + - if @project.public
  81 + %tr.bgred
  82 + %td
  83 + %b
  84 + Public Read-Only Code access:
  85 + %td
  86 + = check_box_tag 'public', nil, @project.public
80 87  
81 88 - if @repository
82 89 %table.zebra-striped
... ...
app/views/blame/show.html.haml
... ... @@ -20,16 +20,27 @@
20 20 %span.options= render "tree/blob_actions"
21 21 .file_content.blame
22 22 %table
  23 + - current_line = 1
23 24 - @blame.each do |commit, lines|
24   - - commit = Commit.new(commit)
25   - - commit = CommitDecorator.decorate(commit)
  25 + - commit = CommitDecorator.decorate(Commit.new(commit))
26 26 %tr
27   - %td.author= commit.author_link avatar: true, size: 16
28   - %td.blame_commit
29   - &nbsp;
30   - %code= link_to commit.short_id, project_commit_path(@project, commit)
31   - = link_to_gfm truncate(commit.title, length: 30), project_commit_path(@project, commit), class: "row_title" rescue "--broken encoding"
  27 + %td.blame-commit
  28 + %span.commit
  29 + = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
  30 + &nbsp;
  31 + = commit.author_link avatar: true, size: 16
  32 + &nbsp;
  33 + = link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title"
  34 + %td.lines.blame-numbers
  35 + %pre
  36 + - if lines.empty?
  37 + = current_line
  38 + - current_line += 1
  39 + - else
  40 + - lines.each do |line|
  41 + = current_line
  42 + - current_line += 1
32 43 %td.lines
33   - = preserve do
34   - %pre
35   - = lines.join("\n")
  44 + %pre
  45 + - lines.each do |line|
  46 + = line
... ...
app/views/dashboard/index.html.haml
... ... @@ -7,3 +7,6 @@
7 7  
8 8 - else
9 9 = render "zero_authorized_projects"
  10 +
  11 +:javascript
  12 + dashboardPage();
... ...
app/views/dashboard/issues.html.haml
... ... @@ -12,9 +12,9 @@
12 12 - if @issues.any?
13 13 - @issues.group_by(&:project).each do |group|
14 14 %div.ui-box
15   - - @project = group[0]
  15 + - project = group[0]
16 16 %h5.title
17   - = link_to_project @project
  17 + = link_to_project project
18 18 %ul.well-list.issues_table
19 19 - group[1].each do |issue|
20 20 = render(partial: 'issues/show', locals: {issue: issue})
... ...
app/views/dashboard/merge_requests.html.haml
... ... @@ -8,17 +8,4 @@
8 8 .span3
9 9 = render 'filter', entity: 'merge_request'
10 10 .span9
11   - - if @merge_requests.any?
12   - - @merge_requests.group_by(&:project).each do |group|
13   - .ui-box
14   - - @project = group[0]
15   - %h5.title
16   - = link_to_project @project
17   - %ul.well-list
18   - - group[1].each do |merge_request|
19   - = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request})
20   - %hr
21   - = paginate @merge_requests, theme: "gitlab"
22   -
23   - - else
24   - %h3.nothing_here_message Nothing to show here
  11 + = render 'shared/merge_requests'
... ...
app/views/events/_event_last_push.html.haml
1 1 - if show_last_push_widget?(event)
2 2 .event_lp
3   - = image_tag "event_push.png"
4   - &nbsp;
5 3 %span You pushed to
6 4 = link_to project_commits_path(event.project, event.ref_name) do
7 5 %strong= truncate(event.ref_name, length: 28)
... ...
app/views/groups/issues.html.haml
... ... @@ -11,9 +11,9 @@
11 11 - if @issues.any?
12 12 - @issues.group_by(&:project).each do |group|
13 13 %div.ui-box
14   - - @project = group[0]
  14 + - project = group[0]
15 15 %h5.title
16   - = link_to_project @project
  16 + = link_to_project project
17 17 %ul.well-list.issues_table
18 18 - group[1].each do |issue|
19 19 = render(partial: 'issues/show', locals: {issue: issue})
... ...
app/views/groups/merge_requests.html.haml
... ... @@ -8,17 +8,4 @@
8 8 .span3
9 9 = render 'filter', entity: 'merge_request'
10 10 .span9
11   - - if @merge_requests.any?
12   - - @merge_requests.group_by(&:project).each do |group|
13   - .ui-box
14   - - @project = group[0]
15   - %h5.title
16   - = link_to_project @project
17   - %ul.well-list
18   - - group[1].each do |merge_request|
19   - = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request})
20   - %hr
21   - = paginate @merge_requests, theme: "gitlab"
22   -
23   - - else
24   - %h3.nothing_here_message Nothing to show here
  11 + = render 'shared/merge_requests'
... ...
app/views/help/index.html.haml
... ... @@ -47,3 +47,5 @@
47 47 %li
48 48 %span= link_to "System Hooks", help_system_hooks_path
49 49  
  50 + %li
  51 + %span= link_to "Public Area", help_public_area_path
... ...
app/views/help/public_area.html.haml 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +%h3.page_title Public Area
  2 +.back_link
  3 + = link_to help_path do
  4 + &larr; to index
  5 +%hr
  6 +
  7 +%p
  8 + Public area - is part of application with public access.
  9 + %br
  10 + It used to list all projects with public read-only access.
  11 + %br
  12 + If you enable public http access to the project - it will appears there
  13 + %br
  14 +
  15 + Follow #{link_to "this link", public_root_path} to visit Public Area
  16 +
... ...
app/views/issues/show.html.haml
... ... @@ -51,8 +51,9 @@
51 51  
52 52 - if @issue.description.present?
53 53 .ui-box-bottom
54   - = preserve do
55   - = markdown @issue.description
  54 + .wiki
  55 + = preserve do
  56 + = markdown @issue.description
56 57  
57 58  
58 59 .voting_notes#notes= render "notes/notes_with_form"
... ...
app/views/keys/create.js.haml
... ... @@ -1,9 +0,0 @@
1   -- if @key.valid?
2   - :plain
3   - $("#new_key_dialog").dialog("close");
4   - $("#keys-table .data").append("#{escape_javascript(render(partial: 'show', locals: {key: @key}))}");
5   - $("#no_ssh_key_defined").hide();
6   -- else
7   - :plain
8   - $("#new_key_dialog").empty();
9   - $("#new_key_dialog").append("#{escape_javascript(render('form'))}");
app/views/keys/new.js.haml
... ... @@ -1,11 +0,0 @@
1   -:plain
2   - var new_key_dialog = $("<div id='new_key_dialog'></div>");
3   - new_key_dialog.html("#{escape_javascript(render('form'))}");
4   - $(new_key_dialog).dialog({
5   - width: 350,
6   - resizable: false,
7   - draggable: false,
8   - title: "Add new public key",
9   - close: function(event, ui) { $("#new_key_dialog").remove();},
10   - modal: true
11   - });
app/views/layouts/_head.html.haml
... ... @@ -6,12 +6,14 @@
6 6 = favicon_link_tag 'favicon.ico'
7 7 = stylesheet_link_tag "application"
8 8 = javascript_include_tag "application"
9   - -# Atom feed
10   - - if controller_name == 'projects' && action_name == 'index'
11   - = auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed"
12   - - if @project && !@project.new_record?
13   - - if current_controller?(:tree, :commits)
14   - = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}")
15   - - if current_controller?(:issues)
16   - = auto_discovery_link_tag(:atom, project_issues_url(@project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
17 9 = csrf_meta_tags
  10 +
  11 + -# Atom feed
  12 + - if current_user
  13 + - if controller_name == 'projects' && action_name == 'index'
  14 + = auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed"
  15 + - if @project && !@project.new_record?
  16 + - if current_controller?(:tree, :commits)
  17 + = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}")
  18 + - if current_controller?(:issues)
  19 + = auto_discovery_link_tag(:atom, project_issues_url(@project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
... ...
app/views/layouts/application.html.haml
... ... @@ -18,7 +18,7 @@
18 18 %span.count= current_user.cared_merge_requests.opened.count
19 19 = nav_link(path: 'search#show') do
20 20 = link_to "Search", search_path
21   - = nav_link(path: 'help#index') do
  21 + = nav_link(controller: :help) do
22 22 = link_to "Help", help_path
23 23  
24 24 .content= yield
... ...
app/views/layouts/public.html.haml 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +!!! 5
  2 +%html{ lang: "en"}
  3 + = render "layouts/head", title: "Public Area"
  4 + %body{class: "#{app_theme} application"}
  5 + %header.navbar.navbar-static-top.navbar-gitlab
  6 + .navbar-inner
  7 + .container
  8 + %div.app_logo
  9 + %span.separator
  10 + = link_to root_path, class: "home" do
  11 + %h1 GITLAB
  12 + %span.separator
  13 + %h1.project_name Public Area
  14 + .container
  15 + .content
  16 + .prepend-top-20
  17 + = yield
... ...
app/views/public/projects/index.html.haml 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +%h3.page_title
  2 + Projects
  3 + %small Read-Only Access
  4 +%hr
  5 +
  6 +%ul.unstyled
  7 + - @projects.each do |project|
  8 + %li.clearfix
  9 + %h5
  10 + %i.icon-star.cgreen
  11 + = project.name_with_namespace
  12 + .right
  13 + %span.monospace.tiny
  14 + git clone #{project.http_url_to_repo}
  15 +
  16 +
  17 += paginate @projects, theme: "admin"
... ...
app/views/shared/_clone_panel.html.haml
1 1 .input-prepend.project_clone_holder
2 2 %button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH
3 3 %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase
  4 +
4 5 = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge"
... ...
app/views/shared/_merge_requests.html.haml 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +- if @merge_requests.any?
  2 + - @merge_requests.group_by(&:project).each do |group|
  3 + .ui-box
  4 + - project = group[0]
  5 + %h5.title
  6 + = link_to_project project
  7 + %ul.well-list
  8 + - group[1].each do |merge_request|
  9 + = render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request})
  10 + %hr
  11 + = paginate @merge_requests, theme: "gitlab"
  12 +
  13 +- else
  14 + %h3.nothing_here_message Nothing to show here
... ...
app/views/wikis/edit.html.haml
1 1 %h3.page_title Editing page
2 2 %hr
3 3 = render 'form'
  4 +
  5 +.right
  6 + - if can? current_user, :admin_wiki, @project
  7 + = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete, class: "btn small danger" do
  8 + Delete this page
4 9 \ No newline at end of file
... ...
app/views/wikis/empty.html.haml
1 1 %h3.page_title Empty page
2 2 %hr
3   -.alert-message.block-message.warning
4   - %span You are not allowed to create wiki pages
  3 +.error_message
  4 + You are not allowed to create wiki pages
... ...
app/views/wikis/history.html.haml
1 1 %h3.page_title
2 2 %span.cgray History for
3   - = @wikis.last.title
  3 + = @wiki_pages.first.title
4 4 %br
5 5 %table
6 6 %thead
7 7 %tr
8   - %th #
9   - %th last edit
10   - %th created by
  8 + %th Page version
  9 + %th Last updated
  10 + %th Updated by
11 11 %tbody
12   - - @wikis.each_with_index do |wiki_page, i|
  12 + - @wiki_pages.each_with_index do |wiki_page, i|
13 13 %tr
14   - %td= i + 1
15 14 %td
16   - = link_to wiki_page.created_at.to_s(:short), project_wiki_path(@project, wiki_page, old_page_id: wiki_page.id)
  15 + %strong
  16 + = link_to project_wiki_path(@project, wiki_page, version_id: wiki_page.id) do
  17 + Version
  18 + = @wiki_pages.count - i
  19 + %td
  20 + = wiki_page.created_at.to_s(:short)
17 21 (#{time_ago_in_words(wiki_page.created_at)}
18 22 ago)
19   - %td= wiki_page.user.name
20   -
  23 + %td= link_to_member(@project, wiki_page.user)
... ...
app/views/wikis/pages.html.haml
... ... @@ -4,15 +4,17 @@
4 4 %thead
5 5 %tr
6 6 %th Title
7   - %th slug
8   - %th created by
  7 + %th Slug
  8 + %th Last updated
  9 + %th Updated by
9 10 %tbody
10   - - @wikis.each_with_index do |wiki_page, i|
  11 + - @wiki_pages.each do |wiki_page|
11 12 %tr
12 13 %td
13   - = link_to wiki_page.title, project_wiki_path(@project, wiki_page, old_page_id: wiki_page.id)
14   - (#{time_ago_in_words(wiki_page.created_at)}
15   - ago)
  14 + %strong= link_to wiki_page.title, project_wiki_path(@project, wiki_page)
16 15 %td= wiki_page.slug
17   - %td= wiki_page.user.name
18   -
  16 + %td
  17 + = wiki_page.created_at.to_s(:short) do
  18 + (#{time_ago_in_words(wiki_page.created_at)}
  19 + ago)
  20 + %td= link_to_member(@project, wiki_page.user)
... ...
app/views/wikis/show.html.haml
... ... @@ -10,12 +10,14 @@
10 10 %i.icon-edit
11 11 Edit
12 12 %br
  13 +- if @wiki != @most_recent_wiki
  14 + .warning_message
  15 + This is an old version of this page.
  16 + You can view the #{link_to "most recent version", project_wiki_path(@project, @wiki)} or browse the #{link_to "history", history_project_wiki_path(@project, @wiki)}.
  17 +
13 18 .file_holder
14 19 .file_content.wiki
15 20 = preserve do
16 21 = markdown @wiki.content
17 22  
18   -%p.time Last edited by #{@wiki.user.name}, #{time_ago_in_words @wiki.created_at} ago
19   -- if can? current_user, :admin_wiki, @project
20   - = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete do
21   - Delete this page
  23 +%p.time Last edited by #{link_to_member @project, @wiki.user}, #{time_ago_in_words @wiki.created_at} ago
... ...
config/initializers/passenger_fix.rb
... ... @@ -1,16 +0,0 @@
1   -if defined?(PhusionPassenger)
2   -
3   - # When you're using Passenger with smart-lv2 (default) or smart spawn method,
4   - # Resque doesn't recognize that it has been forked and should re-establish
5   - # Redis connection. You can see this error message in log:
6   - # Redis::InheritedError, Tried to use a connection from a child process
7   - # without reconnecting. You need to reconnect to Redis after forking.
8   - #
9   - # This solution is based on
10   - # https://github.com/redis/redis-rb/wiki/redis-rb-on-Phusion-Passenger
11   - #
12   - PhusionPassenger.on_event(:starting_worker_process) do |forked|
13   - # if we're in smart spawning mode, reconnect to Redis
14   - Resque.redis.client.reconnect if forked
15   - end
16   -end
config/routes.rb
... ... @@ -35,6 +35,15 @@ Gitlab::Application.routes.draw do
35 35 get 'help/markdown' => 'help#markdown'
36 36 get 'help/ssh' => 'help#ssh'
37 37 get 'help/raketasks' => 'help#raketasks'
  38 + get 'help/public_area' => 'help#public_area'
  39 +
  40 + #
  41 + # Public namespace
  42 + #
  43 + namespace :public do
  44 + resources :projects, only: [:index]
  45 + root to: "projects#index"
  46 + end
38 47  
39 48 #
40 49 # Admin Area
... ...
db/migrate/20130110172407_add_public_to_project.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class AddPublicToProject < ActiveRecord::Migration
  2 + def change
  3 + add_column :projects, :public, :boolean, default: false, null: false
  4 + end
  5 +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 => 20130102143055) do
  14 +ActiveRecord::Schema.define(:version => 20130110172407) do
15 15  
16 16 create_table "events", :force => true do |t|
17 17 t.string "target_type"
... ... @@ -145,16 +145,17 @@ ActiveRecord::Schema.define(:version =&gt; 20130102143055) do
145 145 t.string "name"
146 146 t.string "path"
147 147 t.text "description"
148   - t.datetime "created_at", :null => false
149   - t.datetime "updated_at", :null => false
150   - t.boolean "private_flag", :default => true, :null => false
  148 + t.datetime "created_at", :null => false
  149 + t.datetime "updated_at", :null => false
  150 + t.boolean "private_flag", :default => true, :null => false
151 151 t.integer "creator_id"
152 152 t.string "default_branch"
153   - t.boolean "issues_enabled", :default => true, :null => false
154   - t.boolean "wall_enabled", :default => true, :null => false
155   - t.boolean "merge_requests_enabled", :default => true, :null => false
156   - t.boolean "wiki_enabled", :default => true, :null => false
  153 + t.boolean "issues_enabled", :default => true, :null => false
  154 + t.boolean "wall_enabled", :default => true, :null => false
  155 + t.boolean "merge_requests_enabled", :default => true, :null => false
  156 + t.boolean "wiki_enabled", :default => true, :null => false
157 157 t.integer "namespace_id"
  158 + t.boolean "public", :default => false, :null => false
158 159 end
159 160  
160 161 add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
... ...
doc/install/installation.md
... ... @@ -270,20 +270,6 @@ used for the `email.from` setting in `config/gitlab.yml`)
270 270 sudo -u gitlab -H bundle exec rake gitlab:app:setup RAILS_ENV=production
271 271  
272 272  
273   -## Check Application Status
274   -
275   -Check if GitLab and its environment is configured correctly:
276   -
277   - sudo -u gitlab -H bundle exec rake gitlab:env:info RAILS_ENV=production
278   -
279   -To make sure you didn't miss anything run a more thorough check with:
280   -
281   - sudo -u gitlab -H bundle exec rake gitlab:check RAILS_ENV=production
282   -
283   -If you are all green: congratulations, you successfully installed GitLab!
284   -Although this is the case, there are still a few steps to go.
285   -
286   -
287 273 ## Install Init Script
288 274  
289 275 Download the init script (will be /etc/init.d/gitlab):
... ... @@ -296,7 +282,20 @@ Make GitLab start on boot:
296 282 sudo update-rc.d gitlab defaults 21
297 283  
298 284  
299   -Start your GitLab instance:
  285 +## Check Application Status
  286 +
  287 +Check if GitLab and its environment is configured correctly:
  288 +
  289 + sudo -u gitlab -H bundle exec rake gitlab:env:info RAILS_ENV=production
  290 +
  291 +To make sure you didn't miss anything run a more thorough check with:
  292 +
  293 + sudo -u gitlab -H bundle exec rake gitlab:check RAILS_ENV=production
  294 +
  295 +If all items are green, then congratulations on successfully installing GitLab!
  296 +However there are still a few steps left.
  297 +
  298 +## Start Your GitLab Instance
300 299  
301 300 sudo service gitlab start
302 301 # or
... ...
lib/gitlab/backend/grack_auth.rb
... ... @@ -2,25 +2,41 @@ module Grack
2 2 class Auth < Rack::Auth::Basic
3 3 attr_accessor :user, :project
4 4  
5   - def valid?
6   - # Authentication with username and password
7   - login, password = @auth.credentials
  5 + def call(env)
  6 + @env = env
  7 + @request = Rack::Request.new(env)
  8 + @auth = Request.new(env)
8 9  
9   - self.user = User.find_by_email(login) || User.find_by_username(login)
  10 + # Pass Gitolite update hook
  11 + ENV['GL_BYPASS_UPDATE_HOOK'] = "true"
10 12  
11   - return false unless user.try(:valid_password?, password)
  13 + # Need this patch due to the rails mount
  14 + @env['PATH_INFO'] = @request.path
  15 + @env['SCRIPT_NAME'] = ""
12 16  
13   - email = user.email
  17 + return render_not_found unless project
  18 + return unauthorized unless project.public || @auth.provided?
  19 + return bad_request if @auth.provided? && !@auth.basic?
14 20  
15   - # Set GL_USER env variable
16   - ENV['GL_USER'] = email
17   - # Pass Gitolite update hook
18   - ENV['GL_BYPASS_UPDATE_HOOK'] = "true"
  21 + if valid?
  22 + if @auth.provided?
  23 + @env['REMOTE_USER'] = @auth.username
  24 + end
  25 + return @app.call(env)
  26 + else
  27 + unauthorized
  28 + end
  29 + end
19 30  
20   - # Find project by PATH_INFO from env
21   - if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a
22   - self.project = Project.find_with_namespace(m.last)
23   - return false unless project
  31 + def valid?
  32 + if @auth.provided?
  33 + # Authentication with username and password
  34 + login, password = @auth.credentials
  35 + self.user = User.find_by_email(login) || User.find_by_username(login)
  36 + return false unless user.try(:valid_password?, password)
  37 +
  38 + # Set GL_USER env variable
  39 + ENV['GL_USER'] = user.email
24 40 end
25 41  
26 42 # Git upload and receive
... ... @@ -34,12 +50,12 @@ module Grack
34 50 end
35 51  
36 52 def validate_get_request
37   - can?(user, :download_code, project)
  53 + project.public || can?(user, :download_code, project)
38 54 end
39 55  
40 56 def validate_post_request
41 57 if @request.path_info.end_with?('git-upload-pack')
42   - can?(user, :download_code, project)
  58 + project.public || can?(user, :download_code, project)
43 59 elsif @request.path_info.end_with?('git-receive-pack')
44 60 action = if project.protected_branch?(current_ref)
45 61 :push_code_to_protected_branches
... ... @@ -68,6 +84,22 @@ module Grack
68 84 /refs\/heads\/([\w\.-]+)/.match(input).to_a.first
69 85 end
70 86  
  87 + def project
  88 + unless instance_variable_defined? :@project
  89 + # Find project by PATH_INFO from env
  90 + if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a
  91 + @project = Project.find_with_namespace(m.last)
  92 + end
  93 + end
  94 + return @project
  95 + end
  96 +
  97 + PLAIN_TYPE = {"Content-Type" => "text/plain"}
  98 +
  99 + def render_not_found
  100 + [404, PLAIN_TYPE, ["Not Found"]]
  101 + end
  102 +
71 103 protected
72 104  
73 105 def abilities
... ...
lib/tasks/gitlab/check.rake
... ... @@ -2,7 +2,7 @@ namespace :gitlab do
2 2 desc "GITLAB | Check the configuration of GitLab and its environment"
3 3 task check: %w{gitlab:env:check
4 4 gitlab:gitolite:check
5   - gitlab:resque:check
  5 + gitlab:sidekiq:check
6 6 gitlab:app:check}
7 7  
8 8  
... ... @@ -317,7 +317,7 @@ namespace :gitlab do
317 317 gitolite_ssh_user = Gitlab.config.gitolite.ssh_user
318 318 print "Has no \"-e\" in ~#{gitolite_ssh_user}/.profile ... "
319 319  
320   - profile_file = File.join(gitolite_home, ".profile")
  320 + profile_file = File.join(gitolite_user_home, ".profile")
321 321  
322 322 unless File.read(profile_file) =~ /^-e PATH/
323 323 puts "yes".green
... ... @@ -475,7 +475,7 @@ namespace :gitlab do
475 475 def check_dot_gitolite_exists
476 476 print "Config directory exists? ... "
477 477  
478   - gitolite_config_path = File.join(gitolite_home, ".gitolite")
  478 + gitolite_config_path = File.join(gitolite_user_home, ".gitolite")
479 479  
480 480 if File.directory?(gitolite_config_path)
481 481 puts "yes".green
... ... @@ -496,13 +496,13 @@ namespace :gitlab do
496 496 def check_dot_gitolite_permissions
497 497 print "Config directory access is drwxr-x---? ... "
498 498  
499   - gitolite_config_path = File.join(gitolite_home, ".gitolite")
  499 + gitolite_config_path = File.join(gitolite_user_home, ".gitolite")
500 500 unless File.exists?(gitolite_config_path)
501 501 puts "can't check because of previous errors".magenta
502 502 return
503 503 end
504 504  
505   - if `stat --printf %a #{gitolite_config_path}` == "750"
  505 + if File.stat(gitolite_config_path).mode.to_s(8).ends_with?("750")
506 506 puts "yes".green
507 507 else
508 508 puts "no".red
... ... @@ -520,18 +520,17 @@ namespace :gitlab do
520 520 gitolite_ssh_user = Gitlab.config.gitolite.ssh_user
521 521 print "Config directory owned by #{gitolite_ssh_user}:#{gitolite_ssh_user} ... "
522 522  
523   - gitolite_config_path = File.join(gitolite_home, ".gitolite")
  523 + gitolite_config_path = File.join(gitolite_user_home, ".gitolite")
524 524 unless File.exists?(gitolite_config_path)
525 525 puts "can't check because of previous errors".magenta
526 526 return
527 527 end
528 528  
529   - if `stat --printf %U #{gitolite_config_path}` == gitolite_ssh_user && # user
530   - `stat --printf %G #{gitolite_config_path}` == gitolite_ssh_user #group
  529 + if File.stat(gitolite_config_path).uid == uid_for(gitolite_ssh_user) &&
  530 + File.stat(gitolite_config_path).gid == gid_for(gitolite_ssh_user)
531 531 puts "yes".green
532 532 else
533 533 puts "no".red
534   - puts "#{gitolite_config_path} is not owned by #{gitolite_ssh_user}".red
535 534 try_fixing_it(
536 535 "sudo chown -R #{gitolite_ssh_user}:#{gitolite_ssh_user} #{gitolite_config_path}"
537 536 )
... ... @@ -559,7 +558,7 @@ namespace :gitlab do
559 558 end
560 559  
561 560 def check_gitoliterc_git_config_keys
562   - gitoliterc_path = File.join(gitolite_home, ".gitolite.rc")
  561 + gitoliterc_path = File.join(gitolite_user_home, ".gitolite.rc")
563 562  
564 563 print "Allow all Git config keys in .gitolite.rc ... "
565 564 option_name = if has_gitolite3?
... ... @@ -588,7 +587,7 @@ namespace :gitlab do
588 587 end
589 588  
590 589 def check_gitoliterc_repo_umask
591   - gitoliterc_path = File.join(gitolite_home, ".gitolite.rc")
  590 + gitoliterc_path = File.join(gitolite_user_home, ".gitolite.rc")
592 591  
593 592 print "Repo umask is 0007 in .gitolite.rc? ... "
594 593 option_name = if has_gitolite3?
... ... @@ -722,11 +721,10 @@ namespace :gitlab do
722 721 return
723 722 end
724 723  
725   - if `stat --printf %a #{repo_base_path}` == "6770"
  724 + if File.stat(repo_base_path).mode.to_s(8).ends_with?("6770")
726 725 puts "yes".green
727 726 else
728 727 puts "no".red
729   - puts "#{repo_base_path} is not writable".red
730 728 try_fixing_it(
731 729 "sudo chmod -R ug+rwXs,o-rwx #{repo_base_path}"
732 730 )
... ... @@ -747,12 +745,11 @@ namespace :gitlab do
747 745 return
748 746 end
749 747  
750   - if `stat --printf %U #{repo_base_path}` == gitolite_ssh_user && # user
751   - `stat --printf %G #{repo_base_path}` == gitolite_ssh_user #group
  748 + if File.stat(repo_base_path).uid == uid_for(gitolite_ssh_user) &&
  749 + File.stat(repo_base_path).gid == gid_for(gitolite_ssh_user)
752 750 puts "yes".green
753 751 else
754 752 puts "no".red
755   - puts "#{repo_base_path} is not owned by #{gitolite_ssh_user}".red
756 753 try_fixing_it(
757 754 "sudo chown -R #{gitolite_ssh_user}:#{gitolite_ssh_user} #{repo_base_path}"
758 755 )
... ... @@ -833,7 +830,8 @@ namespace :gitlab do
833 830 next
834 831 end
835 832  
836   - if run_and_match("stat --format %N #{project_hook_file}", /#{hook_file}.+->.+#{gitolite_hook_file}/)
  833 + if File.lstat(project_hook_file).symlink? &&
  834 + File.realpath(project_hook_file) == File.realpath(gitolite_hook_file)
837 835 puts "ok".green
838 836 else
839 837 puts "not a link to Gitolite's hook".red
... ... @@ -852,12 +850,12 @@ namespace :gitlab do
852 850 # Helper methods
853 851 ########################
854 852  
855   - def gitolite_home
  853 + def gitolite_user_home
856 854 File.expand_path("~#{Gitlab.config.gitolite.ssh_user}")
857 855 end
858 856  
859 857 def gitolite_version
860   - gitolite_version_file = "#{gitolite_home}/gitolite/src/VERSION"
  858 + gitolite_version_file = "#{gitolite_user_home}/gitolite/src/VERSION"
861 859 if File.readable?(gitolite_version_file)
862 860 File.read(gitolite_version_file)
863 861 end
... ... @@ -870,22 +868,22 @@ namespace :gitlab do
870 868  
871 869  
872 870  
873   - namespace :resque do
  871 + namespace :sidekiq do
874 872 desc "GITLAB | Check the configuration of Sidekiq"
875 873 task check: :environment do
876 874 warn_user_is_not_gitlab
877   - start_checking "Resque"
  875 + start_checking "Sidekiq"
878 876  
879   - check_resque_running
  877 + check_sidekiq_running
880 878  
881   - finished_checking "Resque"
  879 + finished_checking "Sidekiq"
882 880 end
883 881  
884 882  
885 883 # Checks
886 884 ########################
887 885  
888   - def check_resque_running
  886 + def check_sidekiq_running
889 887 print "Running? ... "
890 888  
891 889 if run_and_match("ps aux | grep -i sidekiq", /sidekiq \d\.\d\.\d.+$/)
... ... @@ -893,9 +891,7 @@ namespace :gitlab do
893 891 else
894 892 puts "no".red
895 893 try_fixing_it(
896   - "sudo service gitlab restart",
897   - "or",
898   - "sudo /etc/init.d/gitlab restart"
  894 + "sudo -u gitlab -H bundle exec rake sidekiq:start"
899 895 )
900 896 for_more_information(
901 897 see_installation_guide_section("Install Init Script"),
... ...
lib/tasks/gitlab/info.rake
... ... @@ -3,20 +3,6 @@ namespace :gitlab do
3 3 desc "GITLAB | Show information about GitLab and its environment"
4 4 task info: :environment do
5 5  
6   - # check which OS is running
7   - os_name = run("lsb_release -irs")
8   - os_name ||= if File.readable?('/etc/system-release')
9   - File.read('/etc/system-release')
10   - end
11   - os_name ||= if File.readable?('/etc/debian_version')
12   - debian_version = File.read('/etc/debian_version')
13   - "Debian #{debian_version}"
14   - end
15   - os_name ||= if File.readable?('/etc/SuSE-release')
16   - File.read('/etc/SuSE-release')
17   - end
18   - os_name.try(:squish!)
19   -
20 6 # check if there is an RVM environment
21 7 rvm_version = run_and_match("rvm --version", /[\d\.]+/).try(:to_s)
22 8 # check Ruby version
... ...
lib/tasks/gitlab/task_helpers.rake
1 1 namespace :gitlab do
2 2  
  3 + # Check which OS is running
  4 + #
  5 + # It will primarily use lsb_relase to determine the OS.
  6 + # It has fallbacks to Debian, SuSE and OS X.
  7 + def os_name
  8 + os_name = run("lsb_release -irs")
  9 + os_name ||= if File.readable?('/etc/system-release')
  10 + File.read('/etc/system-release')
  11 + end
  12 + os_name ||= if File.readable?('/etc/debian_version')
  13 + debian_version = File.read('/etc/debian_version')
  14 + "Debian #{debian_version}"
  15 + end
  16 + os_name ||= if File.readable?('/etc/SuSE-release')
  17 + File.read('/etc/SuSE-release')
  18 + end
  19 + os_name ||= if os_x_version = run("sw_vers -productVersion")
  20 + "Mac OS X #{os_x_version}"
  21 + end
  22 + os_name.try(:squish!)
  23 + end
  24 +
3 25 # Runs the given command and matches the output agains the given pattern
4 26 #
5 27 # Returns nil if nothing matched
... ... @@ -23,6 +45,15 @@ namespace :gitlab do
23 45 end
24 46 end
25 47  
  48 + def uid_for(user_name)
  49 + run("id -u #{user_name}").chomp.to_i
  50 + end
  51 +
  52 + def gid_for(group_name)
  53 + group_line = File.read("/etc/group").lines.select{|l| l.start_with?("#{group_name}:")}.first
  54 + group_line.split(":")[2].to_i
  55 + end
  56 +
26 57 def warn_user_is_not_gitlab
27 58 unless @warned_user_not_gitlab
28 59 current_user = run("whoami").chomp
... ...
lib/tasks/sidekiq.rake
... ... @@ -6,18 +6,10 @@ namespace :sidekiq do
6 6  
7 7 desc "GITLAB | Start sidekiq"
8 8 task :start do
9   - run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,common,default -e #{rails_env} -P #{pidfile} >> #{root_path}/log/sidekiq.log 2>&1 &"
10   - end
11   -
12   - def root_path
13   - @root_path ||= File.join(File.expand_path(File.dirname(__FILE__)), "../..")
  9 + run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
14 10 end
15 11  
16 12 def pidfile
17   - "#{root_path}/tmp/pids/sidekiq.pid"
18   - end
19   -
20   - def rails_env
21   - ENV['RAILS_ENV'] || "production"
  13 + Rails.root.join("tmp", "pids", "sidekiq.pid")
22 14 end
23 15 end
... ...