Commit bda7fe38d0b0e39a408c4eb44374a330c24c3a49
Exists in
master
and in
4 other branches
Merge branch 'master' into discussions
Showing
70 changed files
with
570 additions
and
310 deletions
Show diff stats
.travis.yml
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
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
app/assets/stylesheets/application.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/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) | ... | ... |
... | ... | @@ -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 < 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 < 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 < 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 < 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 < 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
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 | + | |
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 | + | |
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 | - | |
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 | + | |
31 | + = commit.author_link avatar: true, size: 16 | |
32 | + | |
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
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
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
... | ... | @@ -0,0 +1,16 @@ |
1 | +%h3.page_title Public Area | |
2 | +.back_link | |
3 | + = link_to help_path do | |
4 | + ← 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
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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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" | ... | ... |
... | ... | @@ -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
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/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 => 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 | ... | ... |