Commit 48443d20ca9aa10323c1b81835e519680b10debc
Exists in
master
and in
4 other branches
Merge branch 'master' of git://github.com/gitlabhq/gitlabhq
Showing
116 changed files
with
1640 additions
and
1790 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 116 files displayed.
.rails_footnotes
CHANGELOG
| 1 | +v 2.8.1 | |
| 2 | + - ability to disable gravatars | |
| 3 | + - improved MR diff logic | |
| 4 | + - ssh key help page | |
| 5 | + | |
| 1 | 6 | v 2.8.0 |
| 2 | 7 | - Gitlab Flavored Markdown |
| 3 | 8 | - Bulk issues update |
| 4 | 9 | - Issues API |
| 5 | 10 | - Cucumber coverage increased |
| 11 | + - Post-receive files fixed | |
| 12 | + - UI improved | |
| 13 | + - Application cleanup | |
| 14 | + - more cucumber | |
| 15 | + - capybara-webkit + headless | |
| 6 | 16 | |
| 7 | 17 | v 2.7.0 |
| 8 | 18 | - Issue Labels | ... | ... |
Gemfile
| ... | ... | @@ -58,7 +58,7 @@ gem "unicorn" |
| 58 | 58 | gem "acts-as-taggable-on", "2.3.1" |
| 59 | 59 | |
| 60 | 60 | # Decorators |
| 61 | -gem "drapper" | |
| 61 | +gem "draper" | |
| 62 | 62 | |
| 63 | 63 | # Background jobs |
| 64 | 64 | gem "resque", "~> 1.20.0" |
| ... | ... | @@ -80,10 +80,6 @@ gem 'settingslogic' |
| 80 | 80 | gem "foreman" |
| 81 | 81 | gem "git" |
| 82 | 82 | |
| 83 | -# Unused | |
| 84 | -gem 'tabs_on_rails' | |
| 85 | -gem "acts_as_list" | |
| 86 | - | |
| 87 | 83 | group :assets do |
| 88 | 84 | gem "sass-rails", "3.2.5" |
| 89 | 85 | gem "coffee-rails", "3.2.2" |
| ... | ... | @@ -95,12 +91,11 @@ group :assets do |
| 95 | 91 | gem "jquery-ui-rails", "0.5.0" |
| 96 | 92 | gem "modernizr", "2.5.3" |
| 97 | 93 | gem "raphael-rails", "1.5.2" |
| 98 | - gem 'bootstrap-sass', "2.0.3.1" | |
| 94 | + gem 'bootstrap-sass', "2.0.4" | |
| 99 | 95 | end |
| 100 | 96 | |
| 101 | 97 | group :development do |
| 102 | 98 | gem "letter_opener" |
| 103 | - gem "rails-footnotes" | |
| 104 | 99 | gem "annotate", :git => "https://github.com/ctran/annotate_models.git" |
| 105 | 100 | gem 'rack-mini-profiler' |
| 106 | 101 | end |
| ... | ... | @@ -109,6 +104,7 @@ group :development, :test do |
| 109 | 104 | gem "rspec-rails" |
| 110 | 105 | gem "capybara" |
| 111 | 106 | gem "capybara-webkit" |
| 107 | + gem "headless" | |
| 112 | 108 | gem "autotest" |
| 113 | 109 | gem "autotest-rails" |
| 114 | 110 | gem "pry" |
| ... | ... | @@ -119,11 +115,13 @@ end |
| 119 | 115 | |
| 120 | 116 | group :test do |
| 121 | 117 | gem 'cucumber-rails', :require => false |
| 122 | - gem 'minitest', ">= 2.10" | |
| 123 | - gem "turn", :require => false | |
| 124 | 118 | gem "simplecov", :require => false |
| 125 | 119 | gem "shoulda-matchers" |
| 126 | 120 | gem 'email_spec' |
| 127 | 121 | gem 'resque_spec' |
| 128 | 122 | gem "webmock" |
| 129 | 123 | end |
| 124 | + | |
| 125 | +group :production do | |
| 126 | + gem "gitlab_meta", '2.8' | |
| 127 | +end | ... | ... |
Gemfile.lock
| ... | ... | @@ -98,9 +98,7 @@ GEM |
| 98 | 98 | multi_json (~> 1.0) |
| 99 | 99 | acts-as-taggable-on (2.3.1) |
| 100 | 100 | rails (~> 3.0) |
| 101 | - acts_as_list (0.1.6) | |
| 102 | 101 | addressable (2.2.8) |
| 103 | - ansi (1.4.2) | |
| 104 | 102 | arel (3.0.2) |
| 105 | 103 | autotest (4.4.6) |
| 106 | 104 | ZenTest (>= 4.4.1) |
| ... | ... | @@ -109,7 +107,7 @@ GEM |
| 109 | 107 | awesome_print (1.0.2) |
| 110 | 108 | bcrypt-ruby (3.0.1) |
| 111 | 109 | blankslate (2.1.2.4) |
| 112 | - bootstrap-sass (2.0.3.1) | |
| 110 | + bootstrap-sass (2.0.4.0) | |
| 113 | 111 | builder (3.0.0) |
| 114 | 112 | capybara (1.1.2) |
| 115 | 113 | mime-types (>= 1.16) |
| ... | ... | @@ -157,7 +155,9 @@ GEM |
| 157 | 155 | railties (~> 3.1) |
| 158 | 156 | warden (~> 1.2.1) |
| 159 | 157 | diff-lcs (1.1.3) |
| 160 | - drapper (0.8.4) | |
| 158 | + draper (0.17.0) | |
| 159 | + actionpack (~> 3.2) | |
| 160 | + activesupport (~> 3.2) | |
| 161 | 161 | email_spec (1.2.1) |
| 162 | 162 | mail (~> 2.2) |
| 163 | 163 | rspec (~> 2.0) |
| ... | ... | @@ -175,6 +175,7 @@ GEM |
| 175 | 175 | gherkin (2.11.0) |
| 176 | 176 | json (>= 1.4.6) |
| 177 | 177 | git (1.2.5) |
| 178 | + gitlab_meta (2.8) | |
| 178 | 179 | grape (0.2.1) |
| 179 | 180 | hashie (~> 1.2) |
| 180 | 181 | multi_json |
| ... | ... | @@ -189,6 +190,7 @@ GEM |
| 189 | 190 | railties (~> 3.0) |
| 190 | 191 | hashery (1.4.0) |
| 191 | 192 | hashie (1.2.0) |
| 193 | + headless (0.3.1) | |
| 192 | 194 | hike (1.2.1) |
| 193 | 195 | httparty (0.8.3) |
| 194 | 196 | multi_json (~> 1.0) |
| ... | ... | @@ -223,7 +225,6 @@ GEM |
| 223 | 225 | treetop (~> 1.4.8) |
| 224 | 226 | method_source (0.7.1) |
| 225 | 227 | mime-types (1.19) |
| 226 | - minitest (3.1.0) | |
| 227 | 228 | modernizr (2.5.3) |
| 228 | 229 | sprockets (~> 2.0) |
| 229 | 230 | multi_json (1.3.6) |
| ... | ... | @@ -286,8 +287,6 @@ GEM |
| 286 | 287 | activesupport (= 3.2.8) |
| 287 | 288 | bundler (~> 1.0) |
| 288 | 289 | railties (= 3.2.8) |
| 289 | - rails-footnotes (3.7.8) | |
| 290 | - rails (>= 3.0.0) | |
| 291 | 290 | railties (3.2.8) |
| 292 | 291 | actionpack (= 3.2.8) |
| 293 | 292 | activesupport (= 3.2.8) |
| ... | ... | @@ -366,7 +365,6 @@ GEM |
| 366 | 365 | tilt (~> 1.1, != 1.3.0) |
| 367 | 366 | sqlite3 (1.3.6) |
| 368 | 367 | stamp (0.1.6) |
| 369 | - tabs_on_rails (2.1.1) | |
| 370 | 368 | therubyracer (0.10.1) |
| 371 | 369 | libv8 (~> 3.3.10) |
| 372 | 370 | thin (1.3.1) |
| ... | ... | @@ -378,8 +376,6 @@ GEM |
| 378 | 376 | treetop (1.4.10) |
| 379 | 377 | polyglot |
| 380 | 378 | polyglot (>= 0.3.1) |
| 381 | - turn (0.9.5) | |
| 382 | - ansi | |
| 383 | 379 | tzinfo (0.3.33) |
| 384 | 380 | uglifier (1.0.3) |
| 385 | 381 | execjs (>= 0.3.0) |
| ... | ... | @@ -403,12 +399,11 @@ PLATFORMS |
| 403 | 399 | |
| 404 | 400 | DEPENDENCIES |
| 405 | 401 | acts-as-taggable-on (= 2.3.1) |
| 406 | - acts_as_list | |
| 407 | 402 | annotate! |
| 408 | 403 | autotest |
| 409 | 404 | autotest-rails |
| 410 | 405 | awesome_print |
| 411 | - bootstrap-sass (= 2.0.3.1) | |
| 406 | + bootstrap-sass (= 2.0.4) | |
| 412 | 407 | capybara |
| 413 | 408 | capybara-webkit |
| 414 | 409 | carrierwave |
| ... | ... | @@ -419,16 +414,18 @@ DEPENDENCIES |
| 419 | 414 | cucumber-rails |
| 420 | 415 | database_cleaner |
| 421 | 416 | devise (~> 2.1.0) |
| 422 | - drapper | |
| 417 | + draper | |
| 423 | 418 | email_spec |
| 424 | 419 | ffaker |
| 425 | 420 | foreman |
| 426 | 421 | git |
| 422 | + gitlab_meta (= 2.8) | |
| 427 | 423 | gitolite! |
| 428 | 424 | grack! |
| 429 | 425 | grape (~> 0.2.1) |
| 430 | 426 | grit! |
| 431 | 427 | haml-rails |
| 428 | + headless | |
| 432 | 429 | httparty |
| 433 | 430 | jquery-rails (= 2.0.2) |
| 434 | 431 | jquery-ui-rails (= 0.5.0) |
| ... | ... | @@ -436,7 +433,6 @@ DEPENDENCIES |
| 436 | 433 | launchy |
| 437 | 434 | letter_opener |
| 438 | 435 | linguist (~> 1.0.0)! |
| 439 | - minitest (>= 2.10) | |
| 440 | 436 | modernizr (= 2.5.3) |
| 441 | 437 | mysql2 |
| 442 | 438 | omniauth |
| ... | ... | @@ -448,7 +444,6 @@ DEPENDENCIES |
| 448 | 444 | pygments.rb! |
| 449 | 445 | rack-mini-profiler |
| 450 | 446 | rails (= 3.2.8) |
| 451 | - rails-footnotes | |
| 452 | 447 | raphael-rails (= 1.5.2) |
| 453 | 448 | redcarpet (~> 2.1.1) |
| 454 | 449 | resque (~> 1.20.0) |
| ... | ... | @@ -463,10 +458,8 @@ DEPENDENCIES |
| 463 | 458 | six |
| 464 | 459 | sqlite3 |
| 465 | 460 | stamp |
| 466 | - tabs_on_rails | |
| 467 | 461 | therubyracer |
| 468 | 462 | thin |
| 469 | - turn | |
| 470 | 463 | uglifier (= 1.0.3) |
| 471 | 464 | unicorn |
| 472 | 465 | webmock | ... | ... |
VERSION
No preview for this file type
2.79 KB
1.64 KB
app/assets/javascripts/application.js
| ... | ... | @@ -7,8 +7,6 @@ |
| 7 | 7 | //= require jquery |
| 8 | 8 | //= require jquery.ui.all |
| 9 | 9 | //= require jquery_ujs |
| 10 | -//= require jquery.ui.selectmenu | |
| 11 | -//= require jquery.tagify | |
| 12 | 10 | //= require jquery.cookie |
| 13 | 11 | //= require jquery.endless-scroll |
| 14 | 12 | //= require jquery.highlight |
| ... | ... | @@ -74,7 +72,7 @@ $(document).ready(function(){ |
| 74 | 72 | * Note markdown preview |
| 75 | 73 | * |
| 76 | 74 | */ |
| 77 | - $('#preview-link').on('click', function(e) { | |
| 75 | + $(document).on('click', '#preview-link', function(e) { | |
| 78 | 76 | $('#preview-note').text('Loading...'); |
| 79 | 77 | |
| 80 | 78 | var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview'); | ... | ... |
app/assets/javascripts/merge_requests.js
app/assets/stylesheets/application.css
| ... | ... | @@ -3,8 +3,7 @@ |
| 3 | 3 | * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at |
| 4 | 4 | * the top of the compiled file, but it's generally better to create a new file per style scope. |
| 5 | 5 | *= require jquery.ui.all |
| 6 | - *= require jquery-ui/jquery.ui.selectmenu | |
| 7 | - *= require jquery-ui/jquery.tagify | |
| 6 | + *= require jquery.ui.aristo | |
| 8 | 7 | *= require chosen |
| 9 | 8 | *= require_self |
| 10 | 9 | *= require main | ... | ... |
app/assets/stylesheets/common.scss
app/assets/stylesheets/gitlab_bootstrap.scss
| 1 | 1 | body { |
| 2 | 2 | margin-bottom:20px; |
| 3 | 3 | } |
| 4 | + | |
| 5 | +pre { | |
| 6 | + font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; | |
| 7 | + | |
| 8 | + &.dark { | |
| 9 | + background: #333; | |
| 10 | + color:#f5f5f5; | |
| 11 | + } | |
| 12 | +} | |
| 13 | + | |
| 4 | 14 | a { |
| 5 | 15 | outline: none; |
| 6 | 16 | color: $link_color; |
| ... | ... | @@ -325,16 +335,20 @@ img.avatar { |
| 325 | 335 | float:left; |
| 326 | 336 | margin-right:15px; |
| 327 | 337 | width:40px; |
| 328 | - border:2px solid #ddd; | |
| 338 | + border:1px solid #ddd; | |
| 339 | + padding:1px; | |
| 329 | 340 | |
| 330 | 341 | &.s16 { |
| 331 | 342 | width:16px; |
| 343 | + height:16px; | |
| 332 | 344 | } |
| 333 | 345 | &.s24 { |
| 334 | 346 | width:24px; |
| 347 | + height:24px; | |
| 335 | 348 | } |
| 336 | 349 | &.s32 { |
| 337 | 350 | width:32px; |
| 351 | + height:32px; | |
| 338 | 352 | } |
| 339 | 353 | } |
| 340 | 354 | ... | ... |
app/assets/stylesheets/jquery_ui.scss
| ... | ... | @@ -1,33 +0,0 @@ |
| 1 | -/** | |
| 2 | - * JQUERY UI datepicker | |
| 3 | - * | |
| 4 | - */ | |
| 5 | -.ui-datepicker { | |
| 6 | - border-color:#eee; | |
| 7 | - padding:20px; | |
| 8 | - | |
| 9 | - .ui-state-default { | |
| 10 | - background:#f1f1f1; | |
| 11 | - padding:5px; | |
| 12 | - } | |
| 13 | - .ui-state-active { | |
| 14 | - background:#fff; | |
| 15 | - } | |
| 16 | -} | |
| 17 | - | |
| 18 | -/** | |
| 19 | - * JQUERY UI progressbar | |
| 20 | - * | |
| 21 | - */ | |
| 22 | -.ui-progressbar { | |
| 23 | - border:1px solid #ddd; | |
| 24 | - height:6px; | |
| 25 | - margin:0; | |
| 26 | - padding:0; | |
| 27 | - | |
| 28 | - .ui-progressbar-value { | |
| 29 | - background-color: #62C462;//$blue_link; | |
| 30 | - margin:0; | |
| 31 | - } | |
| 32 | -} | |
| 33 | - |
app/assets/stylesheets/main.scss
| ... | ... | @@ -23,6 +23,8 @@ $blue_link: #2fa0bb; |
| 23 | 23 | $style_color: #474D57; |
| 24 | 24 | $hover: #FDF5D9; |
| 25 | 25 | |
| 26 | +/** GITLAB Fonts **/ | |
| 27 | +@font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); } | |
| 26 | 28 | |
| 27 | 29 | /** MIXINS **/ |
| 28 | 30 | @mixin shade { |
| ... | ... | @@ -165,9 +167,3 @@ $hover: #FDF5D9; |
| 165 | 167 | * |
| 166 | 168 | */ |
| 167 | 169 | @import "highlight/dark.scss"; |
| 168 | - | |
| 169 | -/** | |
| 170 | - * JQUERY UI ext | |
| 171 | - * | |
| 172 | - */ | |
| 173 | -@import "jquery_ui.scss"; | ... | ... |
app/assets/stylesheets/sections/header.scss
| ... | ... | @@ -26,23 +26,25 @@ header { |
| 26 | 26 | float:left; |
| 27 | 27 | position:relative; |
| 28 | 28 | top:-5px; |
| 29 | - | |
| 30 | 29 | a { |
| 31 | 30 | float:left; |
| 32 | 31 | |
| 33 | 32 | h1 { |
| 34 | - text-indent:-9999px; | |
| 33 | + padding-top: 5px; | |
| 35 | 34 | width:102px; |
| 36 | - background: url('logo_text.png') no-repeat 0px -3px; | |
| 35 | + background: url('logo_dark.png') no-repeat 0px -3px; | |
| 37 | 36 | float:left; |
| 38 | 37 | margin-left:5px; |
| 39 | - font-size:20px; | |
| 38 | + font-size:36px; | |
| 40 | 39 | line-height:36px; |
| 41 | - font-weight:bold; | |
| 42 | - color:#aaa; | |
| 40 | + font-weight:normal; | |
| 41 | + color:$style_color; | |
| 43 | 42 | text-shadow: 0 1px 1px #FFF; |
| 44 | 43 | padding-left:50px; |
| 44 | + height:40px; | |
| 45 | + font-family: 'Korolev', sans-serif; | |
| 45 | 46 | } |
| 47 | + | |
| 46 | 48 | } |
| 47 | 49 | .separator { |
| 48 | 50 | margin-left:20px; |
| ... | ... | @@ -68,14 +70,16 @@ header { |
| 68 | 70 | * |
| 69 | 71 | */ |
| 70 | 72 | .project_name { |
| 73 | + position:relative; | |
| 71 | 74 | float:left; |
| 72 | 75 | margin:0; |
| 73 | 76 | margin-right:30px; |
| 74 | - font-size:24px; | |
| 77 | + font-size:36px; | |
| 75 | 78 | line-height:36px; |
| 76 | - font-weight:500; | |
| 79 | + font-weight:normal; | |
| 77 | 80 | color:$style_color; |
| 78 | 81 | text-shadow: 0 1px 1px #FFF; |
| 82 | + font-family: 'Korolev', sans-serif; | |
| 79 | 83 | } |
| 80 | 84 | |
| 81 | 85 | .fbtn { | ... | ... |
app/assets/stylesheets/sections/login.scss
app/assets/stylesheets/sections/merge_requests.scss
app/assets/stylesheets/sections/notes.scss
app/assets/stylesheets/sections/projects.scss
| 1 | -.projects { | |
| 1 | +.projects { | |
| 2 | 2 | @extend .row; |
| 3 | 3 | .activities { |
| 4 | 4 | } |
| 5 | 5 | |
| 6 | - .side { | |
| 6 | + .side { | |
| 7 | 7 | @extend .span4; |
| 8 | 8 | @extend .right; |
| 9 | 9 | |
| 10 | - .projects_box { | |
| 11 | - h5 { | |
| 10 | + .projects_box { | |
| 11 | + h5 { | |
| 12 | 12 | color:$style_color; |
| 13 | 13 | font-size:16px; |
| 14 | 14 | text-shadow: 0 1px 1px #fff; |
| 15 | + padding: 2px 10px; | |
| 15 | 16 | } |
| 16 | 17 | @extend .leftbar; |
| 17 | 18 | @extend .ui-box; |
| ... | ... | @@ -19,21 +20,22 @@ |
| 19 | 20 | } |
| 20 | 21 | } |
| 21 | 22 | |
| 22 | -.new_project, | |
| 23 | -.edit_project { | |
| 24 | - .project_name_holder { | |
| 23 | +.new_project, | |
| 24 | +.edit_project { | |
| 25 | + .project_name_holder { | |
| 25 | 26 | input, |
| 26 | - label { | |
| 27 | + label { | |
| 27 | 28 | font-size:16px; |
| 28 | 29 | line-height:20px; |
| 29 | 30 | padding:8px; |
| 30 | 31 | } |
| 31 | - label { | |
| 32 | + label { | |
| 32 | 33 | color:#888; |
| 33 | 34 | } |
| 34 | - .btn { | |
| 35 | + .btn { | |
| 35 | 36 | padding:6px; |
| 36 | 37 | margin-left:10px; |
| 38 | + margin-bottom:8px; | |
| 37 | 39 | } |
| 38 | 40 | } |
| 39 | 41 | } | ... | ... |
app/assets/stylesheets/themes/ui_basic.scss
| ... | ... | @@ -15,4 +15,36 @@ |
| 15 | 15 | color: $blue_link; |
| 16 | 16 | } |
| 17 | 17 | } |
| 18 | + | |
| 19 | + header { | |
| 20 | + .fbtn { | |
| 21 | + .btn { | |
| 22 | + background-color: #F8F8F8; | |
| 23 | + background-image: -webkit-gradient(linear,left top,left bottom,from(#F8F8F8),to(#ECECEC)); | |
| 24 | + background-image: -webkit-linear-gradient(top,#F8F8F8,#ECECEC); | |
| 25 | + background-image: -moz-linear-gradient(top,#F8F8F8,#ECECEC); | |
| 26 | + background-image: -ms-linear-gradient(top,#F8F8F8,#ECECEC); | |
| 27 | + background-image: -o-linear-gradient(top,#F8F8F8,#ECECEC); | |
| 28 | + background-image: linear-gradient(top,#F8F8F8,#ECECEC); | |
| 29 | + filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#f8f8f8',EndColorStr='#ececec'); | |
| 30 | + border-color: #C6C6C6; | |
| 31 | + margin-left:7px; | |
| 32 | + @include border-radius(3px); | |
| 33 | + box-shadow:none; | |
| 34 | + color:#666; | |
| 35 | + } | |
| 36 | + } | |
| 37 | + .search { | |
| 38 | + .search-input { | |
| 39 | + @include border-radius(3px); | |
| 40 | + border-color: #C6C6C6; | |
| 41 | + box-shadow:none; | |
| 42 | + } | |
| 43 | + } | |
| 44 | + .pic { | |
| 45 | + img { | |
| 46 | + @include border-radius(3px); | |
| 47 | + } | |
| 48 | + } | |
| 49 | + } | |
| 18 | 50 | } | ... | ... |
app/assets/stylesheets/themes/ui_mars.scss
| ... | ... | @@ -20,6 +20,10 @@ |
| 20 | 20 | |
| 21 | 21 | .fbtn { |
| 22 | 22 | .btn { |
| 23 | + i { | |
| 24 | + position: relative; | |
| 25 | + top: 1px; | |
| 26 | + } | |
| 23 | 27 | margin-left:8px; |
| 24 | 28 | background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #595D63), to(#31363E)); |
| 25 | 29 | background-image: -webkit-linear-gradient(#595D63 6.6%, #31363E); |
| ... | ... | @@ -32,6 +36,10 @@ |
| 32 | 36 | background-image: -moz-linear-gradient(#595D63 6.6%, #202227); |
| 33 | 37 | background-image: -o-linear-gradient(#595D63 6.6%, #202227); |
| 34 | 38 | background-position:0 0; |
| 39 | + color:#fff; | |
| 40 | + i { | |
| 41 | + @extend .icon-white; | |
| 42 | + } | |
| 35 | 43 | } |
| 36 | 44 | |
| 37 | 45 | border: 1px solid #31363E; |
| ... | ... | @@ -59,14 +67,9 @@ |
| 59 | 67 | .app_logo { |
| 60 | 68 | a { |
| 61 | 69 | h1 { |
| 62 | - background: url('images.png') no-repeat -3px -6px; | |
| 63 | - width: 65px; | |
| 64 | - height: 26px; | |
| 65 | - margin: 6px 0; | |
| 66 | - padding: 0; | |
| 67 | - float: left; | |
| 68 | - text-indent: -1000em; | |
| 69 | - float:left; | |
| 70 | + background: url('logo_white.png') no-repeat 0px -3px; | |
| 71 | + color:#fff; | |
| 72 | + text-shadow: 0 1px 1px #111; | |
| 70 | 73 | } |
| 71 | 74 | } |
| 72 | 75 | .separator { |
| ... | ... | @@ -75,7 +78,6 @@ |
| 75 | 78 | |
| 76 | 79 | } |
| 77 | 80 | .project_name { |
| 78 | - line-height:38px; | |
| 79 | 81 | color:#fff; |
| 80 | 82 | text-shadow: 0 1px 1px #111; |
| 81 | 83 | } | ... | ... |
app/assets/stylesheets/themes/ui_modern.scss
| ... | ... | @@ -37,26 +37,20 @@ |
| 37 | 37 | * |
| 38 | 38 | */ |
| 39 | 39 | .app_logo { |
| 40 | + width:160px; | |
| 40 | 41 | a { |
| 41 | 42 | h1 { |
| 42 | - opacity: 0.7; | |
| 43 | - background: url('images.png') no-repeat -3px -6px; | |
| 44 | - width: 65px; | |
| 45 | - height: 26px; | |
| 46 | - margin: 6px 0; | |
| 47 | - padding: 0; | |
| 48 | - float: left; | |
| 49 | - text-indent: -1000em; | |
| 50 | - float:left; | |
| 51 | - &:hover { | |
| 52 | - opacity: 1.0; | |
| 53 | - } | |
| 43 | + background: none; | |
| 44 | + color:#DDD; | |
| 45 | + font-size:30px; | |
| 46 | + text-shadow: 0 1px 1px #111; | |
| 47 | + padding-left: 0; | |
| 54 | 48 | } |
| 55 | 49 | } |
| 56 | 50 | .separator { |
| 57 | 51 | width: 1px; |
| 58 | 52 | height: 40px; |
| 59 | - margin: 0 9px; | |
| 53 | + margin: 0 10px; | |
| 60 | 54 | overflow: hidden; |
| 61 | 55 | background: #222; |
| 62 | 56 | border-left: 1px solid #333; |
| ... | ... | @@ -66,7 +60,6 @@ |
| 66 | 60 | .fbtn { |
| 67 | 61 | .btn { |
| 68 | 62 | i { |
| 69 | - @extend .icon-white; | |
| 70 | 63 | position: relative; |
| 71 | 64 | top: 2px; |
| 72 | 65 | } |
| ... | ... | @@ -77,10 +70,14 @@ |
| 77 | 70 | color:#ccc; |
| 78 | 71 | &:hover { |
| 79 | 72 | color:#fff; |
| 73 | + i { | |
| 74 | + @extend .icon-white; | |
| 75 | + } | |
| 80 | 76 | } |
| 81 | 77 | border: none; |
| 82 | 78 | box-shadow:none; |
| 83 | 79 | text-shadow: 0 -1px 0 #000000; |
| 80 | + border-left: 1px solid #333; | |
| 84 | 81 | } |
| 85 | 82 | } |
| 86 | 83 | |
| ... | ... | @@ -113,9 +110,9 @@ |
| 113 | 110 | * |
| 114 | 111 | */ |
| 115 | 112 | .project_name { |
| 116 | - line-height:34px; | |
| 117 | - font-size:22px; | |
| 118 | - color:#fff; | |
| 113 | + line-height:36px; | |
| 114 | + font-size:30px; | |
| 115 | + color:#DDD; | |
| 119 | 116 | text-shadow: 0 1px 1px #111; |
| 120 | 117 | } |
| 121 | 118 | ... | ... |
| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | +class LabelsController < ApplicationController | |
| 2 | + before_filter :authenticate_user! | |
| 3 | + before_filter :project | |
| 4 | + before_filter :module_enabled | |
| 5 | + | |
| 6 | + layout "project" | |
| 7 | + | |
| 8 | + # Authorize | |
| 9 | + before_filter :add_project_abilities | |
| 10 | + | |
| 11 | + # Allow read any issue | |
| 12 | + before_filter :authorize_read_issue! | |
| 13 | + | |
| 14 | + respond_to :js, :html | |
| 15 | + | |
| 16 | + def index | |
| 17 | + @labels = Issue.tag_counts_on(:labels) | |
| 18 | + end | |
| 19 | + | |
| 20 | + protected | |
| 21 | + | |
| 22 | + def module_enabled | |
| 23 | + return render_404 unless @project.issues_enabled | |
| 24 | + end | |
| 25 | +end | ... | ... |
app/controllers/omniauth_callbacks_controller.rb
| ... | ... | @@ -12,8 +12,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController |
| 12 | 12 | |
| 13 | 13 | def ldap |
| 14 | 14 | # We only find ourselves here if the authentication to LDAP was successful. |
| 15 | - info = request.env["omniauth.auth"]["info"] | |
| 16 | - @user = User.find_for_ldap_auth(info) | |
| 15 | + @user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user) | |
| 17 | 16 | if @user.persisted? |
| 18 | 17 | @user.remember_me = true |
| 19 | 18 | end |
| ... | ... | @@ -39,7 +38,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController |
| 39 | 38 | current_user.save |
| 40 | 39 | redirect_to profile_path |
| 41 | 40 | else |
| 42 | - @user = User.find_by_provider_and_uid(provider, uid) | |
| 41 | + @user = User.find_by_provider_and_extern_uid(provider, uid) | |
| 43 | 42 | |
| 44 | 43 | if @user |
| 45 | 44 | sign_in_and_redirect @user | ... | ... |
app/controllers/projects_controller.rb
| 1 | -require File.join(Rails.root, 'lib', 'graph_commit') | |
| 1 | +require Rails.root.join('lib', 'gitlab', 'graph_commit') | |
| 2 | 2 | |
| 3 | 3 | class ProjectsController < ApplicationController |
| 4 | 4 | before_filter :project, except: [:index, :new, :create] |
| ... | ... | @@ -78,7 +78,7 @@ class ProjectsController < ApplicationController |
| 78 | 78 | end |
| 79 | 79 | |
| 80 | 80 | def graph |
| 81 | - @days_json, @commits_json = GraphCommit.to_graph(project) | |
| 81 | + @days_json, @commits_json = Gitlab::GraphCommit.to_graph(project) | |
| 82 | 82 | end |
| 83 | 83 | |
| 84 | 84 | def destroy | ... | ... |
app/controllers/team_members_controller.rb
app/decorators/application_decorator.rb
app/helpers/application_helper.rb
| ... | ... | @@ -2,10 +2,13 @@ require 'digest/md5' |
| 2 | 2 | module ApplicationHelper |
| 3 | 3 | |
| 4 | 4 | def gravatar_icon(user_email = '', size = 40) |
| 5 | - return unless user_email | |
| 6 | - gravatar_host = request.ssl? ? "https://secure.gravatar.com" : "http://www.gravatar.com" | |
| 7 | - user_email.strip! | |
| 8 | - "#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon" | |
| 5 | + if Gitlab.config.disable_gravatar? || user_email.blank? | |
| 6 | + 'no_avatar.png' | |
| 7 | + else | |
| 8 | + gravatar_prefix = request.ssl? ? "https://secure" : "http://www" | |
| 9 | + user_email.strip! | |
| 10 | + "#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon" | |
| 11 | + end | |
| 9 | 12 | end |
| 10 | 13 | |
| 11 | 14 | def request_protocol | ... | ... |
app/helpers/gitlab_markdown_helper.rb
| 1 | 1 | module GitlabMarkdownHelper |
| 2 | + # Replaces references (i.e. @abc, #123, !456, ...) in the text with links to | |
| 3 | + # the appropriate items in Gitlab. | |
| 4 | + # | |
| 5 | + # text - the source text | |
| 6 | + # html_options - extra options for the reference links as given to link_to | |
| 7 | + # | |
| 8 | + # note: reference links will only be generated if @project is set | |
| 9 | + # | |
| 10 | + # see Gitlab::Markdown for details on the supported syntax | |
| 2 | 11 | def gfm(text, html_options = {}) |
| 3 | 12 | return text if text.nil? |
| 4 | 13 | return text if @project.nil? |
| 5 | 14 | |
| 6 | - # Extract pre blocks | |
| 15 | + # Extract pre blocks so they are not altered | |
| 7 | 16 | # from http://github.github.com/github-flavored-markdown/ |
| 8 | 17 | extractions = {} |
| 9 | 18 | text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match| |
| ... | ... | @@ -25,7 +34,15 @@ module GitlabMarkdownHelper |
| 25 | 34 | text.html_safe |
| 26 | 35 | end |
| 27 | 36 | |
| 28 | - # circumvents nesting links, which will behave bad in browsers | |
| 37 | + # Use this in places where you would normally use link_to(gfm(...), ...). | |
| 38 | + # | |
| 39 | + # It solves a problem occurring with nested links (i.e. | |
| 40 | + # "<a>outer text <a>gfm ref</a> more outer text</a>"). This will not be | |
| 41 | + # interpreted as intended. Browsers will parse something like | |
| 42 | + # "<a>outer text </a><a>gfm ref</a> more outer text" (notice the last part is | |
| 43 | + # not linked any more). link_to_gfm corrects that. It wraps all parts to | |
| 44 | + # explicitly produce the correct linking behavior (i.e. | |
| 45 | + # "<a>outer text </a><a>gfm ref</a><a> more outer text</a>"). | |
| 29 | 46 | def link_to_gfm(body, url, html_options = {}) |
| 30 | 47 | gfm_body = gfm(body, html_options) |
| 31 | 48 | ... | ... |
app/mailers/notify.rb
| ... | ... | @@ -12,74 +12,102 @@ class Notify < ActionMailer::Base |
| 12 | 12 | def new_user_email(user_id, password) |
| 13 | 13 | @user = User.find(user_id) |
| 14 | 14 | @password = password |
| 15 | - mail(to: @user.email, subject: "gitlab | Account was created for you") | |
| 15 | + mail(to: @user.email, subject: subject("Account was created for you")) | |
| 16 | 16 | end |
| 17 | 17 | |
| 18 | 18 | def new_issue_email(issue_id) |
| 19 | 19 | @issue = Issue.find(issue_id) |
| 20 | 20 | @project = @issue.project |
| 21 | - mail(to: @issue.assignee_email, subject: "gitlab | new issue ##{@issue.id} | #{@issue.title} | #{@project.name}") | |
| 21 | + mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title)) | |
| 22 | 22 | end |
| 23 | 23 | |
| 24 | 24 | def note_wall_email(recipient_id, note_id) |
| 25 | - recipient = User.find(recipient_id) | |
| 26 | 25 | @note = Note.find(note_id) |
| 27 | 26 | @project = @note.project |
| 28 | - mail(to: recipient.email, subject: "gitlab | #{@project.name}") | |
| 27 | + mail(to: recipient(recipient_id), subject: subject) | |
| 29 | 28 | end |
| 30 | 29 | |
| 31 | 30 | def note_commit_email(recipient_id, note_id) |
| 32 | - recipient = User.find(recipient_id) | |
| 33 | 31 | @note = Note.find(note_id) |
| 34 | 32 | @commit = @note.target |
| 35 | 33 | @commit = CommitDecorator.decorate(@commit) |
| 36 | 34 | @project = @note.project |
| 37 | - mail(to: recipient.email, subject: "gitlab | note for commit #{@commit.short_id} | #{@commit.title} | #{@project.name}") | |
| 35 | + mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) | |
| 38 | 36 | end |
| 39 | 37 | |
| 40 | 38 | def note_merge_request_email(recipient_id, note_id) |
| 41 | - recipient = User.find(recipient_id) | |
| 42 | 39 | @note = Note.find(note_id) |
| 43 | 40 | @merge_request = @note.noteable |
| 44 | 41 | @project = @note.project |
| 45 | - mail(to: recipient.email, subject: "gitlab | note for merge request !#{@merge_request.id} | #{@project.name}") | |
| 42 | + mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}")) | |
| 46 | 43 | end |
| 47 | 44 | |
| 48 | 45 | def note_issue_email(recipient_id, note_id) |
| 49 | - recipient = User.find(recipient_id) | |
| 50 | 46 | @note = Note.find(note_id) |
| 51 | 47 | @issue = @note.noteable |
| 52 | 48 | @project = @note.project |
| 53 | - mail(to: recipient.email, subject: "gitlab | note for issue ##{@issue.id} | #{@project.name}") | |
| 49 | + mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.id}")) | |
| 54 | 50 | end |
| 55 | 51 | |
| 56 | 52 | def note_wiki_email(recipient_id, note_id) |
| 57 | - recipient = User.find(recipient_id) | |
| 58 | 53 | @note = Note.find(note_id) |
| 59 | 54 | @wiki = @note.noteable |
| 60 | 55 | @project = @note.project |
| 61 | - mail(to: recipient.email, subject: "gitlab | note for wiki | #{@project.name}") | |
| 56 | + mail(to: recipient(recipient_id), subject: subject("note for wiki")) | |
| 62 | 57 | end |
| 63 | 58 | |
| 64 | 59 | def new_merge_request_email(merge_request_id) |
| 65 | 60 | @merge_request = MergeRequest.find(merge_request_id) |
| 66 | 61 | @project = @merge_request.project |
| 67 | - mail(to: @merge_request.assignee_email, subject: "gitlab | new merge request !#{@merge_request.id} | #{@merge_request.title} | #{@project.name}") | |
| 62 | + mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title)) | |
| 68 | 63 | end |
| 69 | 64 | |
| 70 | 65 | def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) |
| 71 | - recipient = User.find(recipient_id) | |
| 72 | 66 | @merge_request = MergeRequest.find(merge_request_id) |
| 73 | 67 | @previous_assignee ||= User.find(previous_assignee_id) |
| 74 | 68 | @project = @merge_request.project |
| 75 | - mail(to: recipient.email, subject: "gitlab | changed merge request !#{@merge_request.id} | #{@merge_request.title} | #{@project.name}") | |
| 69 | + mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) | |
| 76 | 70 | end |
| 77 | 71 | |
| 78 | 72 | def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) |
| 79 | - recipient = User.find(recipient_id) | |
| 80 | 73 | @issue = Issue.find(issue_id) |
| 81 | 74 | @previous_assignee ||= User.find(previous_assignee_id) |
| 82 | 75 | @project = @issue.project |
| 83 | - mail(to: recipient.email, subject: "gitlab | changed issue ##{@issue.id} | #{@issue.title} | #{@project.name}") | |
| 76 | + mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title)) | |
| 77 | + end | |
| 78 | + | |
| 79 | + private | |
| 80 | + | |
| 81 | + # Look up a User by their ID and return their email address | |
| 82 | + # | |
| 83 | + # recipient_id - User ID | |
| 84 | + # | |
| 85 | + # Returns a String containing the User's email address. | |
| 86 | + def recipient(recipient_id) | |
| 87 | + if recipient = User.find(recipient_id) | |
| 88 | + recipient.email | |
| 89 | + end | |
| 90 | + end | |
| 91 | + | |
| 92 | + # Formats arguments into a String suitable for use as an email subject | |
| 93 | + # | |
| 94 | + # extra - Extra Strings to be inserted into the subject | |
| 95 | + # | |
| 96 | + # Examples | |
| 97 | + # | |
| 98 | + # >> subject('Lorem ipsum') | |
| 99 | + # => "gitlab | Lorem ipsum" | |
| 100 | + # | |
| 101 | + # # Automatically inserts Project name when @project is set | |
| 102 | + # >> @project = Project.last | |
| 103 | + # => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> | |
| 104 | + # >> subject('Lorem ipsum') | |
| 105 | + # => "gitlab | Lorem ipsum | Ruby on Rails" | |
| 106 | + # | |
| 107 | + # # Accepts multiple arguments | |
| 108 | + # >> subject('Lorem ipsum', 'Dolor sit amet') | |
| 109 | + # => "gitlab | Lorem ipsum | Dolor sit amet" | |
| 110 | + def subject(*extra) | |
| 111 | + "gitlab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "") | |
| 84 | 112 | end |
| 85 | 113 | end | ... | ... |
app/models/issue.rb
app/models/merge_request.rb
| ... | ... | @@ -88,8 +88,11 @@ class MergeRequest < ActiveRecord::Base |
| 88 | 88 | end |
| 89 | 89 | |
| 90 | 90 | def unmerged_diffs |
| 91 | - commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)} | |
| 92 | - diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue [] | |
| 91 | + # Only show what is new in the source branch compared to the target branch, not the other way around. | |
| 92 | + # The linex below with merge_base is equivalent to diff with three dots (git diff branch1...branch2) | |
| 93 | + # From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B" | |
| 94 | + common_commit = project.repo.git.native(:merge_base, {}, [target_branch, source_branch]).strip | |
| 95 | + diffs = project.repo.diff(common_commit, source_branch) | |
| 93 | 96 | end |
| 94 | 97 | |
| 95 | 98 | def last_commit | ... | ... |
app/models/project.rb
app/models/user.rb
| ... | ... | @@ -7,7 +7,7 @@ class User < ActiveRecord::Base |
| 7 | 7 | |
| 8 | 8 | attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, |
| 9 | 9 | :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, |
| 10 | - :theme_id, :force_random_password | |
| 10 | + :theme_id, :force_random_password, :extern_uid, :provider | |
| 11 | 11 | |
| 12 | 12 | attr_accessor :force_random_password |
| 13 | 13 | |
| ... | ... | @@ -54,6 +54,8 @@ class User < ActiveRecord::Base |
| 54 | 54 | |
| 55 | 55 | validates :bio, length: { within: 0..255 } |
| 56 | 56 | |
| 57 | + validates :extern_uid, :allow_blank => true, :uniqueness => {:scope => :provider} | |
| 58 | + | |
| 57 | 59 | before_save :ensure_authentication_token |
| 58 | 60 | alias_attribute :private_token, :authentication_token |
| 59 | 61 | |
| ... | ... | @@ -84,21 +86,31 @@ class User < ActiveRecord::Base |
| 84 | 86 | where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') |
| 85 | 87 | end |
| 86 | 88 | |
| 87 | - def self.find_for_ldap_auth(omniauth_info) | |
| 88 | - name = omniauth_info.name.force_encoding("utf-8") | |
| 89 | - email = omniauth_info.email.downcase unless omniauth_info.email.nil? | |
| 90 | - raise OmniAuth::Error, "LDAP accounts must provide an email address" if email.nil? | |
| 89 | + def self.find_for_ldap_auth(auth, signed_in_resource=nil) | |
| 90 | + uid = auth.info.uid | |
| 91 | + provider = auth.provider | |
| 92 | + name = auth.info.name.force_encoding("utf-8") | |
| 93 | + email = auth.info.email.downcase unless auth.info.email.nil? | |
| 94 | + raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil? | |
| 91 | 95 | |
| 92 | - if @user = User.find_by_email(email) | |
| 96 | + if @user = User.find_by_extern_uid_and_provider(uid, provider) | |
| 97 | + @user | |
| 98 | + # workaround for backward compatibility | |
| 99 | + elsif @user = User.find_by_email(email) | |
| 100 | + logger.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}" | |
| 101 | + @user.update_attributes(:extern_uid => uid, :provider => provider) | |
| 93 | 102 | @user |
| 94 | 103 | else |
| 104 | + logger.info "Creating user from LDAP login {uid => #{uid}, name => #{name}, email => #{email}}" | |
| 95 | 105 | password = Devise.friendly_token[0, 8].downcase |
| 96 | 106 | @user = User.create( |
| 97 | - name: name, | |
| 98 | - email: email, | |
| 99 | - password: password, | |
| 100 | - password_confirmation: password, | |
| 101 | - projects_limit: Gitlab.config.default_projects_limit | |
| 107 | + :extern_uid => uid, | |
| 108 | + :provider => provider, | |
| 109 | + :name => name, | |
| 110 | + :email => email, | |
| 111 | + :password => password, | |
| 112 | + :password_confirmation => password, | |
| 113 | + :projects_limit => Gitlab.config.default_projects_limit | |
| 102 | 114 | ) |
| 103 | 115 | end |
| 104 | 116 | end | ... | ... |
app/roles/project_push.rb
| ... | ... | @@ -1,105 +0,0 @@ |
| 1 | -module ProjectPush | |
| 2 | - def observe_push(oldrev, newrev, ref, user) | |
| 3 | - data = post_receive_data(oldrev, newrev, ref, user) | |
| 4 | - | |
| 5 | - Event.create( | |
| 6 | - project: self, | |
| 7 | - action: Event::Pushed, | |
| 8 | - data: data, | |
| 9 | - author_id: data[:user_id] | |
| 10 | - ) | |
| 11 | - end | |
| 12 | - | |
| 13 | - def update_merge_requests(oldrev, newrev, ref, user) | |
| 14 | - return true unless ref =~ /heads/ | |
| 15 | - branch_name = ref.gsub("refs/heads/", "") | |
| 16 | - c_ids = self.commits_between(oldrev, newrev).map(&:id) | |
| 17 | - | |
| 18 | - # Update code for merge requests | |
| 19 | - mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all | |
| 20 | - mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } | |
| 21 | - | |
| 22 | - # Close merge requests | |
| 23 | - mrs = self.merge_requests.opened.where(target_branch: branch_name).all | |
| 24 | - mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } | |
| 25 | - mrs.each { |merge_request| merge_request.merge!(user.id) } | |
| 26 | - | |
| 27 | - true | |
| 28 | - end | |
| 29 | - | |
| 30 | - def execute_hooks(oldrev, newrev, ref, user) | |
| 31 | - ref_parts = ref.split('/') | |
| 32 | - | |
| 33 | - # Return if this is not a push to a branch (e.g. new commits) | |
| 34 | - return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000" | |
| 35 | - | |
| 36 | - data = post_receive_data(oldrev, newrev, ref, user) | |
| 37 | - | |
| 38 | - hooks.each { |hook| hook.execute(data) } | |
| 39 | - end | |
| 40 | - | |
| 41 | - def post_receive_data(oldrev, newrev, ref, user) | |
| 42 | - | |
| 43 | - push_commits = commits_between(oldrev, newrev) | |
| 44 | - | |
| 45 | - # Total commits count | |
| 46 | - push_commits_count = push_commits.size | |
| 47 | - | |
| 48 | - # Get latest 20 commits ASC | |
| 49 | - push_commits_limited = push_commits.last(20) | |
| 50 | - | |
| 51 | - # Hash to be passed as post_receive_data | |
| 52 | - data = { | |
| 53 | - before: oldrev, | |
| 54 | - after: newrev, | |
| 55 | - ref: ref, | |
| 56 | - user_id: user.id, | |
| 57 | - user_name: user.name, | |
| 58 | - repository: { | |
| 59 | - name: name, | |
| 60 | - url: web_url, | |
| 61 | - description: description, | |
| 62 | - homepage: web_url, | |
| 63 | - }, | |
| 64 | - commits: [], | |
| 65 | - total_commits_count: push_commits_count | |
| 66 | - } | |
| 67 | - | |
| 68 | - # For perfomance purposes maximum 20 latest commits | |
| 69 | - # will be passed as post receive hook data. | |
| 70 | - # | |
| 71 | - push_commits_limited.each do |commit| | |
| 72 | - data[:commits] << { | |
| 73 | - id: commit.id, | |
| 74 | - message: commit.safe_message, | |
| 75 | - timestamp: commit.date.xmlschema, | |
| 76 | - url: "#{Gitlab.config.url}/#{code}/commits/#{commit.id}", | |
| 77 | - author: { | |
| 78 | - name: commit.author_name, | |
| 79 | - email: commit.author_email | |
| 80 | - } | |
| 81 | - } | |
| 82 | - end | |
| 83 | - | |
| 84 | - data | |
| 85 | - end | |
| 86 | - | |
| 87 | - | |
| 88 | - # This method will be called after each post receive | |
| 89 | - # and only if user present in gitlab. | |
| 90 | - # All callbacks for post receive should be placed here | |
| 91 | - # | |
| 92 | - def trigger_post_receive(oldrev, newrev, ref, user) | |
| 93 | - # Create push event | |
| 94 | - self.observe_push(oldrev, newrev, ref, user) | |
| 95 | - | |
| 96 | - # Close merged MR | |
| 97 | - self.update_merge_requests(oldrev, newrev, ref, user) | |
| 98 | - | |
| 99 | - # Execute web hooks | |
| 100 | - self.execute_hooks(oldrev, newrev, ref, user) | |
| 101 | - | |
| 102 | - # Create satellite | |
| 103 | - self.satellite.create unless self.satellite.exists? | |
| 104 | - end | |
| 105 | -end |
| ... | ... | @@ -0,0 +1,105 @@ |
| 1 | +module PushObserver | |
| 2 | + def observe_push(oldrev, newrev, ref, user) | |
| 3 | + data = post_receive_data(oldrev, newrev, ref, user) | |
| 4 | + | |
| 5 | + Event.create( | |
| 6 | + project: self, | |
| 7 | + action: Event::Pushed, | |
| 8 | + data: data, | |
| 9 | + author_id: data[:user_id] | |
| 10 | + ) | |
| 11 | + end | |
| 12 | + | |
| 13 | + def update_merge_requests(oldrev, newrev, ref, user) | |
| 14 | + return true unless ref =~ /heads/ | |
| 15 | + branch_name = ref.gsub("refs/heads/", "") | |
| 16 | + c_ids = self.commits_between(oldrev, newrev).map(&:id) | |
| 17 | + | |
| 18 | + # Update code for merge requests | |
| 19 | + mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all | |
| 20 | + mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } | |
| 21 | + | |
| 22 | + # Close merge requests | |
| 23 | + mrs = self.merge_requests.opened.where(target_branch: branch_name).all | |
| 24 | + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } | |
| 25 | + mrs.each { |merge_request| merge_request.merge!(user.id) } | |
| 26 | + | |
| 27 | + true | |
| 28 | + end | |
| 29 | + | |
| 30 | + def execute_hooks(oldrev, newrev, ref, user) | |
| 31 | + ref_parts = ref.split('/') | |
| 32 | + | |
| 33 | + # Return if this is not a push to a branch (e.g. new commits) | |
| 34 | + return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000" | |
| 35 | + | |
| 36 | + data = post_receive_data(oldrev, newrev, ref, user) | |
| 37 | + | |
| 38 | + hooks.each { |hook| hook.execute(data) } | |
| 39 | + end | |
| 40 | + | |
| 41 | + def post_receive_data(oldrev, newrev, ref, user) | |
| 42 | + | |
| 43 | + push_commits = commits_between(oldrev, newrev) | |
| 44 | + | |
| 45 | + # Total commits count | |
| 46 | + push_commits_count = push_commits.size | |
| 47 | + | |
| 48 | + # Get latest 20 commits ASC | |
| 49 | + push_commits_limited = push_commits.last(20) | |
| 50 | + | |
| 51 | + # Hash to be passed as post_receive_data | |
| 52 | + data = { | |
| 53 | + before: oldrev, | |
| 54 | + after: newrev, | |
| 55 | + ref: ref, | |
| 56 | + user_id: user.id, | |
| 57 | + user_name: user.name, | |
| 58 | + repository: { | |
| 59 | + name: name, | |
| 60 | + url: web_url, | |
| 61 | + description: description, | |
| 62 | + homepage: web_url, | |
| 63 | + }, | |
| 64 | + commits: [], | |
| 65 | + total_commits_count: push_commits_count | |
| 66 | + } | |
| 67 | + | |
| 68 | + # For perfomance purposes maximum 20 latest commits | |
| 69 | + # will be passed as post receive hook data. | |
| 70 | + # | |
| 71 | + push_commits_limited.each do |commit| | |
| 72 | + data[:commits] << { | |
| 73 | + id: commit.id, | |
| 74 | + message: commit.safe_message, | |
| 75 | + timestamp: commit.date.xmlschema, | |
| 76 | + url: "#{Gitlab.config.url}/#{code}/commits/#{commit.id}", | |
| 77 | + author: { | |
| 78 | + name: commit.author_name, | |
| 79 | + email: commit.author_email | |
| 80 | + } | |
| 81 | + } | |
| 82 | + end | |
| 83 | + | |
| 84 | + data | |
| 85 | + end | |
| 86 | + | |
| 87 | + | |
| 88 | + # This method will be called after each post receive | |
| 89 | + # and only if user present in gitlab. | |
| 90 | + # All callbacks for post receive should be placed here | |
| 91 | + # | |
| 92 | + def trigger_post_receive(oldrev, newrev, ref, user) | |
| 93 | + # Create push event | |
| 94 | + self.observe_push(oldrev, newrev, ref, user) | |
| 95 | + | |
| 96 | + # Close merged MR | |
| 97 | + self.update_merge_requests(oldrev, newrev, ref, user) | |
| 98 | + | |
| 99 | + # Execute web hooks | |
| 100 | + self.execute_hooks(oldrev, newrev, ref, user) | |
| 101 | + | |
| 102 | + # Create satellite | |
| 103 | + self.satellite.create unless self.satellite.exists? | |
| 104 | + end | |
| 105 | +end | ... | ... |
app/roles/repository.rb
| ... | ... | @@ -30,26 +30,10 @@ module Repository |
| 30 | 30 | Commit.commits_between(repo, from, to) |
| 31 | 31 | end |
| 32 | 32 | |
| 33 | - def write_hooks | |
| 34 | - %w(post-receive).each do |hook| | |
| 35 | - write_hook(hook, File.read(File.join(Rails.root, 'lib', "#{hook}-hook"))) | |
| 36 | - end | |
| 37 | - end | |
| 38 | - | |
| 39 | 33 | def satellite |
| 40 | 34 | @satellite ||= Gitlab::Satellite.new(self) |
| 41 | 35 | end |
| 42 | 36 | |
| 43 | - def write_hook(name, content) | |
| 44 | - hook_file = File.join(path_to_repo, 'hooks', name) | |
| 45 | - | |
| 46 | - File.open(hook_file, 'w') do |f| | |
| 47 | - f.write(content) | |
| 48 | - end | |
| 49 | - | |
| 50 | - File.chmod(0775, hook_file) | |
| 51 | - end | |
| 52 | - | |
| 53 | 37 | def has_post_receive_file? |
| 54 | 38 | hook_file = File.join(path_to_repo, 'hooks', 'post-receive') |
| 55 | 39 | File.exists?(hook_file) |
| ... | ... | @@ -73,8 +57,6 @@ module Repository |
| 73 | 57 | |
| 74 | 58 | def update_repository |
| 75 | 59 | Gitlab::GitHost.system.update_project(path, self) |
| 76 | - | |
| 77 | - write_hooks if File.exists?(path_to_repo) | |
| 78 | 60 | end |
| 79 | 61 | |
| 80 | 62 | def destroy_repository | ... | ... |
app/views/admin/hooks/index.html.haml
| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | Read more about system hooks |
| 6 | 6 | %strong #{link_to "here", help_system_hooks_path, class: "vlink"} |
| 7 | 7 | |
| 8 | -= form_for @hook, as: :hook, url: admin_hooks_path do |f| | |
| 8 | += form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-inline' } do |f| | |
| 9 | 9 | -if @hook.errors.any? |
| 10 | 10 | .alert-message.block-message.error |
| 11 | 11 | - @hook.errors.full_messages.each do |msg| | ... | ... |
app/views/admin/projects/index.html.haml
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | Projects |
| 3 | 3 | = link_to 'New Project', new_admin_project_path, class: "btn small right" |
| 4 | 4 | %br |
| 5 | -= form_tag admin_projects_path, method: :get do | |
| 5 | += form_tag admin_projects_path, method: :get, class: 'form-inline' do | |
| 6 | 6 | = text_field_tag :name, params[:name], class: "xlarge" |
| 7 | 7 | = submit_tag "Search", class: "btn submit primary" |
| 8 | 8 | ... | ... |
app/views/admin/users/index.html.haml
| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | = link_to 'New User', new_admin_user_path, class: "btn small right" |
| 4 | 4 | %br |
| 5 | 5 | |
| 6 | -= form_tag admin_users_path, method: :get do | |
| 6 | += form_tag admin_users_path, method: :get, class: 'form-inline' do | |
| 7 | 7 | = text_field_tag :name, params[:name], class: "xlarge" |
| 8 | 8 | = submit_tag "Search", class: "btn submit primary" |
| 9 | 9 | %ul.nav.nav-pills | ... | ... |
app/views/commits/_commit_box.html.haml
| ... | ... | @@ -5,10 +5,10 @@ |
| 5 | 5 | %span.btn.disabled.grouped |
| 6 | 6 | %i.icon-comment |
| 7 | 7 | = @notes_count |
| 8 | - = link_to patch_project_commit_path(@project, @commit.id), class: "btn small grouped" do | |
| 8 | + = link_to patch_project_commit_path(@project, @commit.id), class: "btn small grouped" do | |
| 9 | 9 | %i.icon-download-alt |
| 10 | - Get Patch | |
| 11 | - = link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do | |
| 10 | + Get Patch | |
| 11 | + = link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do | |
| 12 | 12 | %strong Browse Code » |
| 13 | 13 | %h3.commit-title.page_title |
| 14 | 14 | = gfm @commit.title | ... | ... |
app/views/dashboard/index.html.haml
| 1 | 1 | - if @projects.any? |
| 2 | 2 | .projects |
| 3 | 3 | .activities.span8 |
| 4 | - - if current_user.require_ssh_key? | |
| 5 | - .alert.alert-error.padded | |
| 6 | - %span | |
| 7 | - You wont be able to pull/push project code unless you | |
| 8 | - %strong | |
| 9 | - = link_to new_key_path, class: "vlink" do | |
| 10 | - add new key | |
| 11 | - to your profile | |
| 4 | + = render 'shared/no_ssh' | |
| 12 | 5 | - if @events.any? |
| 13 | 6 | .content_list= render @events |
| 14 | 7 | - else |
| ... | ... | @@ -57,5 +50,5 @@ |
| 57 | 50 | If you will be added to project - it will be displayed here |
| 58 | 51 | |
| 59 | 52 | |
| 60 | -:javascript | |
| 53 | +:javascript | |
| 61 | 54 | $(function(){ Pager.init(20); }); | ... | ... |
app/views/errors/gitolite.html.haml
app/views/help/index.html.haml
app/views/help/permissions.html.haml
| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | +%h3.page_title SSH Keys | |
| 2 | +.back_link | |
| 3 | + = link_to help_path do | |
| 4 | + ← to index | |
| 5 | +%hr | |
| 6 | + | |
| 7 | +%p.slead | |
| 8 | + SSH key allows you to establish a secure connection between your computer and Gitlab | |
| 9 | + | |
| 10 | +%p.slead | |
| 11 | + To generate a new SSH key just open your terminal and use code below. | |
| 12 | + | |
| 13 | +%pre.dark | |
| 14 | + ssh-keygen -t rsa -C "#{current_user.email}" | |
| 15 | + | |
| 16 | + \# Creates a new ssh key using the provided email | |
| 17 | + \# Generating public/private rsa key pair... | |
| 18 | + | |
| 19 | +%p.slead | |
| 20 | + Next just use code below to dump your public key and add to GITLAB SSH Keys | |
| 21 | + | |
| 22 | +%pre.dark | |
| 23 | + cat ~/.ssh/id_rsa.pub | |
| 24 | + | |
| 25 | + \# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6eNtGpNGwstc.... | ... | ... |
app/views/help/system_hooks.html.haml
| 1 | 1 | %h3 System hooks |
| 2 | 2 | .back_link |
| 3 | - = link_to :back do | |
| 3 | + = link_to :back do | |
| 4 | 4 | ← back |
| 5 | 5 | %hr |
| 6 | 6 | |
| 7 | -%p.slead | |
| 7 | +%p.slead | |
| 8 | 8 | Your Gitlab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member. |
| 9 | 9 | %br |
| 10 | 10 | System Hooks can be used for logging or change information in LDAP server. | ... | ... |
app/views/help/web_hooks.html.haml
| 1 | -%h3 Web hooks | |
| 1 | +%h3.page_title Web hooks | |
| 2 | 2 | .back_link |
| 3 | - = link_to help_path do | |
| 3 | + = link_to help_path do | |
| 4 | 4 | ← to index |
| 5 | 5 | %hr |
| 6 | 6 | |
| 7 | -%p.slead | |
| 8 | - Every Gitlab project can trigger a web server whenever the repo is pushed to. | |
| 7 | +%p.slead | |
| 8 | + Every Gitlab project can trigger a web server whenever the repo is pushed to. | |
| 9 | 9 | %br |
| 10 | 10 | Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. |
| 11 | 11 | %br | ... | ... |
app/views/help/workflow.html.haml
| 1 | -- bash_lexer = Pygments::Lexer[:bash] | |
| 2 | -%h3 Workflow | |
| 1 | +%h3.page_title Workflow | |
| 3 | 2 | .back_link |
| 4 | - = link_to help_path do | |
| 3 | + = link_to help_path do | |
| 5 | 4 | ← to index |
| 6 | 5 | %hr |
| 7 | 6 | |
| ... | ... | @@ -9,25 +8,25 @@ |
| 9 | 8 | %li |
| 10 | 9 | %p Clone project |
| 11 | 10 | .bash |
| 12 | - %pre | |
| 11 | + %pre.dark | |
| 13 | 12 | git clone git@example.com:project-name.git |
| 14 | 13 | |
| 15 | 14 | %li |
| 16 | 15 | %p Create branch with your feature |
| 17 | 16 | .bash |
| 18 | - %pre | |
| 17 | + %pre.dark | |
| 19 | 18 | git checkout -b $feature_name |
| 20 | 19 | |
| 21 | 20 | %li |
| 22 | 21 | %p Write code. Commit changes |
| 23 | 22 | .bash |
| 24 | - %pre | |
| 23 | + %pre.dark | |
| 25 | 24 | git commit -am "My feature is ready" |
| 26 | 25 | |
| 27 | 26 | %li |
| 28 | 27 | %p Push your branch to gitlabhq |
| 29 | 28 | .bash |
| 30 | - %pre | |
| 29 | + %pre.dark | |
| 31 | 30 | git push origin $feature_name |
| 32 | 31 | |
| 33 | 32 | %li | ... | ... |
app/views/hooks/index.html.haml
| ... | ... | @@ -8,7 +8,7 @@ |
| 8 | 8 | Read more about web hooks |
| 9 | 9 | %strong #{link_to "here", help_web_hooks_path, class: "vlink"} |
| 10 | 10 | |
| 11 | -= form_for [@project, @hook], as: :hook, url: project_hooks_path(@project) do |f| | |
| 11 | += form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-inline' } do |f| | |
| 12 | 12 | -if @hook.errors.any? |
| 13 | 13 | .alert-message.block-message.error |
| 14 | 14 | - @hook.errors.full_messages.each do |msg| | ... | ... |
app/views/issues/_form.html.haml
| 1 | 1 | %div.issue-form-holder |
| 2 | - %h3= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}" | |
| 2 | + %h3.page_title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.id}" | |
| 3 | 3 | = form_for [@project, @issue], remote: request.xhr? do |f| |
| 4 | 4 | -if @issue.errors.any? |
| 5 | 5 | .alert-message.block-message.error |
| ... | ... | @@ -9,26 +9,26 @@ |
| 9 | 9 | .issue_form_box |
| 10 | 10 | .issue_title |
| 11 | 11 | .clearfix |
| 12 | - = f.label :title do | |
| 12 | + = f.label :title do | |
| 13 | 13 | %strong= "Subject *" |
| 14 | 14 | .input |
| 15 | 15 | = f.text_field :title, maxlength: 255, class: "xxlarge" |
| 16 | 16 | .issue_middle_block |
| 17 | 17 | .issue_assignee |
| 18 | - = f.label :assignee_id do | |
| 18 | + = f.label :assignee_id do | |
| 19 | 19 | %i.icon-user |
| 20 | 20 | Assign to |
| 21 | 21 | .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }) |
| 22 | 22 | .issue_milestone |
| 23 | - = f.label :milestone_id do | |
| 23 | + = f.label :milestone_id do | |
| 24 | 24 | %i.icon-time |
| 25 | 25 | Milestone |
| 26 | 26 | .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }) |
| 27 | 27 | |
| 28 | 28 | .issue_description |
| 29 | 29 | .clearfix |
| 30 | - = f.label :label_list do | |
| 31 | - %i.icon-tag | |
| 30 | + = f.label :label_list do | |
| 31 | + %i.icon-tag | |
| 32 | 32 | Labels |
| 33 | 33 | .input |
| 34 | 34 | = f.text_field :label_list, maxlength: 2000, class: "xxlarge" | ... | ... |
app/views/issues/_head.html.haml
| ... | ... | @@ -5,6 +5,9 @@ |
| 5 | 5 | %li{class: "#{'active' if current_page?(project_milestones_path(@project))}"} |
| 6 | 6 | = link_to project_milestones_path(@project), class: "tab" do |
| 7 | 7 | Milestones |
| 8 | + %li{class: "#{'active' if current_page?(project_labels_path(@project))}"} | |
| 9 | + = link_to project_labels_path(@project), class: "tab" do | |
| 10 | + Labels | |
| 8 | 11 | %li.right |
| 9 | 12 | %span.rss-icon |
| 10 | 13 | = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do | ... | ... |
app/views/issues/index.html.haml
| ... | ... | @@ -7,6 +7,7 @@ |
| 7 | 7 | .span5 |
| 8 | 8 | - if can? current_user, :write_issue, @project |
| 9 | 9 | = link_to new_project_issue_path(@project), class: "right btn small", title: "New Issue", remote: true do |
| 10 | + %i.icon-plus | |
| 10 | 11 | New Issue |
| 11 | 12 | = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do |
| 12 | 13 | = hidden_field_tag :project_id, @project.id, { id: 'project_id' } |
| ... | ... | @@ -21,7 +22,7 @@ |
| 21 | 22 | |
| 22 | 23 | |
| 23 | 24 | .issues_bulk_update.hide |
| 24 | - = form_tag bulk_update_project_issues_path(@project), method: :post do | |
| 25 | + = form_tag bulk_update_project_issues_path(@project), method: :post do | |
| 25 | 26 | %span.update_issues_text Update selected issues with |
| 26 | 27 | .left |
| 27 | 28 | = select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status") |
| ... | ... | @@ -53,7 +54,7 @@ |
| 53 | 54 | = select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + @project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone") |
| 54 | 55 | = hidden_field_tag :f, params[:f] |
| 55 | 56 | .clearfix |
| 56 | - | |
| 57 | + | |
| 57 | 58 | %ul#issues-table.unstyled.issues_table |
| 58 | 59 | = render "issues" |
| 59 | 60 | ... | ... |
app/views/keys/_form.html.haml
| ... | ... | @@ -11,7 +11,13 @@ |
| 11 | 11 | .input= f.text_field :title |
| 12 | 12 | .clearfix |
| 13 | 13 | = f.label :key |
| 14 | - .input= f.text_area :key, class: [:xxlarge, :thin_area] | |
| 14 | + .input | |
| 15 | + = f.text_area :key, class: [:xxlarge, :thin_area] | |
| 16 | + %p.hint | |
| 17 | + Paste your public key here. Read more about how generate it | |
| 18 | + = link_to "here", help_ssh_path | |
| 19 | + | |
| 20 | + | |
| 15 | 21 | .actions |
| 16 | 22 | = f.submit 'Save', class: "primary btn" |
| 17 | 23 | = link_to "Cancel", keys_path, class: "btn" | ... | ... |
app/views/keys/new.html.haml
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | += render "issues/head" | |
| 2 | + | |
| 3 | +%h3.page_title | |
| 4 | + Labels | |
| 5 | +%br | |
| 6 | +%div.ui-box | |
| 7 | + %ul.unstyled.labels-table | |
| 8 | + - @labels.each do |label| | |
| 9 | + = render 'label', label: label | |
| 10 | + | |
| 11 | + - unless @labels.present? | |
| 12 | + %li | |
| 13 | + %h3.nothing_here_message Nothing to show here | |
| 14 | + | ... | ... |
app/views/layouts/_head.html.haml
| ... | ... | @@ -6,7 +6,6 @@ |
| 6 | 6 | = favicon_link_tag 'favicon.ico' |
| 7 | 7 | = stylesheet_link_tag "application" |
| 8 | 8 | = javascript_include_tag "application" |
| 9 | - | |
| 10 | 9 | -# Atom feed |
| 11 | 10 | - if controller_name == 'projects' && action_name == 'index' |
| 12 | 11 | = auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed" | ... | ... |
app/views/merge_requests/_show.html.haml
app/views/merge_requests/show/_how_to_merge.html.haml
| ... | ... | @@ -3,13 +3,12 @@ |
| 3 | 3 | %a.close{href: "#"} × |
| 4 | 4 | %h3 How To Merge |
| 5 | 5 | .modal-body |
| 6 | - %pre | |
| 6 | + %pre.dark | |
| 7 | 7 | = preserve do |
| 8 | - :erb | |
| 9 | - git checkout <%= @merge_request.target_branch %> | |
| 10 | - git fetch origin | |
| 11 | - git merge origin/<%= @merge_request.source_branch %> | |
| 12 | - git push origin <%= @merge_request.target_branch %> | |
| 8 | + git checkout #{@merge_request.target_branch} | |
| 9 | + git fetch origin | |
| 10 | + git merge origin/#{@merge_request.source_branch} | |
| 11 | + git push origin #{@merge_request.target_branch} | |
| 13 | 12 | |
| 14 | 13 | |
| 15 | 14 | :javascript | ... | ... |
app/views/merge_requests/show/_mr_accept.html.haml
| ... | ... | @@ -40,3 +40,6 @@ |
| 40 | 40 | .alert.alert-info |
| 41 | 41 | %strong This merge request already can not be merged. Try to reload page. |
| 42 | 42 | |
| 43 | + .merge_in_progress.hide | |
| 44 | + %span.cgray Merge is in progress. Please wait. Page will be automatically reloaded. | |
| 45 | + = image_tag "ajax_loader.gif" | ... | ... |
app/views/milestones/_form.html.haml
| 1 | -%h3= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.id}" | |
| 1 | +%h3.page_title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.id}" | |
| 2 | 2 | .back_link |
| 3 | 3 | = link_to project_milestones_path(@project) do |
| 4 | 4 | ← To milestones |
| ... | ... | @@ -17,12 +17,12 @@ |
| 17 | 17 | = f.label :title, "Title", class: "control-label" |
| 18 | 18 | .controls |
| 19 | 19 | = f.text_field :title, maxlength: 255, class: "input-xlarge" |
| 20 | - %p.help-block Required | |
| 20 | + %p.hint Required | |
| 21 | 21 | .control-group |
| 22 | 22 | = f.label :description, "Description", class: "control-label" |
| 23 | 23 | .controls |
| 24 | 24 | = f.text_area :description, maxlength: 2000, class: "input-xlarge", rows: 10 |
| 25 | - %p.help-block Markdown is enabled. | |
| 25 | + %p.hint Markdown is enabled. | |
| 26 | 26 | .span6 |
| 27 | 27 | .control-group |
| 28 | 28 | = f.label :due_date, "Due Date", class: "control-label" | ... | ... |
app/views/notes/_create_common.js.haml
| 1 | 1 | - if note.valid? |
| 2 | 2 | :plain |
| 3 | - $("#new_note .errors").remove(); | |
| 3 | + $("#new_note .error").remove(); | |
| 4 | 4 | $('#new_note textarea').val(""); |
| 5 | + $('#preview-link').text('Preview'); | |
| 6 | + $('#preview-note').hide(); $('#note_note').show(); | |
| 5 | 7 | NoteList.prepend(#{note.id}, "#{escape_javascript(render partial: "notes/show", locals: {note: note})}"); |
| 6 | 8 | - else |
| 7 | 9 | :plain | ... | ... |
app/views/notes/_show.html.haml
app/views/projects/empty.html.haml
| 1 | -- if current_user.require_ssh_key? | |
| 2 | - .alert-message.block-message.error | |
| 3 | - %ul | |
| 4 | - %li You have no ssh keys added to your profile. | |
| 5 | - %li You wont be able to pull/push repository. | |
| 6 | - %li Visit profile → keys and add public key of every machine you want to use for work with gitlabhq. | |
| 7 | - | |
| 8 | -.alert-message.block-message.error | |
| 9 | - %ul.unstyled.alert_holder | |
| 10 | - %li You should push repository to proceed. | |
| 11 | - %li After push you will be able to browse code, commits etc. | |
| 12 | - | |
| 13 | -- bash_lexer = Pygments::Lexer[:bash] | |
| 1 | += render 'shared/no_ssh' | |
| 14 | 2 | %div.git-empty |
| 15 | - %h3 Git global setup: | |
| 16 | - - setup_str = ["git config --global user.name \"#{current_user.name}\"", | |
| 17 | - "git config --global user.email \"#{current_user.email}\""].join("\n") | |
| 18 | - = preserve do | |
| 19 | - = raw bash_lexer.highlight(setup_str, lexer: 'bash', options: {encoding: 'utf-8'}) | |
| 20 | - | |
| 21 | - %br | |
| 22 | - %br | |
| 23 | - %h3 Create Repository | |
| 24 | - - repo_setup_str = ["mkdir #{@project.path}", | |
| 25 | - "cd #{@project.path}", | |
| 26 | - "git init", | |
| 27 | - "touch README", | |
| 28 | - "git add README", | |
| 29 | - "git commit -m 'first commit'", | |
| 30 | - "git remote add origin #{@project.url_to_repo}", | |
| 31 | - "git push -u origin master"].join("\n") | |
| 3 | + %h4 Git global setup: | |
| 4 | + %pre.dark | |
| 5 | + = preserve do | |
| 6 | + git config --global user.name "#{current_user.name}" | |
| 7 | + git config --global user.email "#{current_user.email}" | |
| 32 | 8 | |
| 33 | - = preserve do | |
| 34 | - = raw bash_lexer.highlight(repo_setup_str) | |
| 9 | + %h4.prepend-top-20 Create Repository | |
| 10 | + %pre.dark | |
| 11 | + = preserve do | |
| 12 | + mkdir #{@project.path} | |
| 13 | + cd #{@project.path} | |
| 14 | + git init | |
| 15 | + touch README | |
| 16 | + git add README | |
| 17 | + git commit -m 'first commit' | |
| 18 | + git remote add origin #{@project.url_to_repo} | |
| 19 | + git push -u origin master | |
| 35 | 20 | |
| 36 | - %br | |
| 37 | - %br | |
| 38 | - %h3 Existing Git Repo? | |
| 39 | - - exist_repo_setup_str = ["cd existing_git_repo", | |
| 40 | - "git remote add origin #{@project.url_to_repo}", | |
| 41 | - "git push -u origin master"].join("\n") | |
| 42 | - = preserve do | |
| 43 | - = raw bash_lexer.highlight(exist_repo_setup_str) | |
| 21 | + %h4.prepend-top-20 Existing Git Repo? | |
| 22 | + %pre.dark | |
| 23 | + = preserve do | |
| 24 | + cd existing_git_repo | |
| 25 | + git remote add origin #{@project.url_to_repo} | |
| 26 | + git push -u origin master | |
| 44 | 27 | |
| 45 | 28 | - if can? current_user, :admin_project, @project |
| 46 | - .alert-message.block-message.error.prepend-top-20 | |
| 47 | - = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger" | |
| 29 | + .prepend-top-20 | |
| 30 | + = link_to 'Remove project', @project, confirm: 'Are you sure?', method: :delete, class: "btn danger right" | ... | ... |
app/views/refs/_tree.html.haml
| ... | ... | @@ -51,8 +51,6 @@ |
| 51 | 51 | |
| 52 | 52 | :javascript |
| 53 | 53 | $(function(){ |
| 54 | - $('select#branch').selectmenu({style:'popup', width:200}); | |
| 55 | - $('select#tag').selectmenu({style:'popup', width:200}); | |
| 56 | 54 | $('.project-refs-select').chosen(); |
| 57 | 55 | |
| 58 | 56 | history.pushState({ path: this.path }, '', "#{@history_path}"); | ... | ... |
app/views/refs/_tree_commit.html.haml
| 1 | 1 | - if tm |
| 2 | - %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm) | |
| 2 | + = link_to "[#{tm.user_name}]", project_team_member_path(@project, tm) | |
| 3 | 3 | = link_to_gfm truncate(content_commit.title, length: tm ? 30 : 50), project_commit_path(@project, content_commit.id), class: "tree-commit-link" | ... | ... |
app/views/search/show.html.haml
app/views/team_members/_show.html.haml
| ... | ... | @@ -9,7 +9,7 @@ |
| 9 | 9 | %span.label Blocked |
| 10 | 10 | |
| 11 | 11 | = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do |
| 12 | - = image_tag gravatar_icon(user.email, 40), class: "avatar" | |
| 12 | + = image_tag gravatar_icon(user.email, 40), class: "avatar s32" | |
| 13 | 13 | = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do |
| 14 | 14 | %strong= truncate(user.name, lenght: 40) |
| 15 | 15 | %br | ... | ... |
app/views/team_members/show.html.haml
| ... | ... | @@ -51,7 +51,7 @@ |
| 51 | 51 | = form_for(@team_member, as: :team_member, url: project_team_member_path(@project, @team_member)) do |f| |
| 52 | 52 | = f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select", disabled: !allow_admin |
| 53 | 53 | %hr |
| 54 | - = render user.recent_events.limit(5) | |
| 54 | + = render @events | |
| 55 | 55 | :javascript |
| 56 | 56 | $(function(){ |
| 57 | 57 | $('.repo-access-select, .project-access-select').live("change", function() { | ... | ... |
config/application.rb
config/gitlab.yml.example
| ... | ... | @@ -23,7 +23,7 @@ app: |
| 23 | 23 | default_projects_limit: 10 |
| 24 | 24 | # backup_path: "/vol/backups" # default: Rails.root + backups/ |
| 25 | 25 | # backup_keep_time: 604800 # default: 0 (forever) (in seconds) |
| 26 | - | |
| 26 | + # disable_gravatar: true # default: false - Disable user avatars from Gravatar.com | |
| 27 | 27 | |
| 28 | 28 | # |
| 29 | 29 | # 2. Advanced settings: |
| ... | ... | @@ -39,7 +39,6 @@ git_host: |
| 39 | 39 | receive_pack: true |
| 40 | 40 | # port: 22 |
| 41 | 41 | |
| 42 | - | |
| 43 | 42 | # Git settings |
| 44 | 43 | # Use default values unless you understand it |
| 45 | 44 | git: | ... | ... |
config/initializers/1_settings.rb
config/initializers/rails_footnotes.rb
config/routes.rb
| ... | ... | @@ -30,6 +30,7 @@ Gitlab::Application.routes.draw do |
| 30 | 30 | get 'help/web_hooks' => 'help#web_hooks' |
| 31 | 31 | get 'help/system_hooks' => 'help#system_hooks' |
| 32 | 32 | get 'help/markdown' => 'help#markdown' |
| 33 | + get 'help/ssh' => 'help#ssh' | |
| 33 | 34 | |
| 34 | 35 | # |
| 35 | 36 | # Admin Area |
| ... | ... | @@ -196,7 +197,9 @@ Gitlab::Application.routes.draw do |
| 196 | 197 | end |
| 197 | 198 | resources :team_members |
| 198 | 199 | resources :milestones |
| 200 | + resources :labels, :only => [:index] | |
| 199 | 201 | resources :issues do |
| 202 | + | |
| 200 | 203 | collection do |
| 201 | 204 | post :sort |
| 202 | 205 | post :bulk_update | ... | ... |
db/migrate/20120729131232_add_extern_auth_provider_to_users.rb
0 → 100644
db/migrate/20120803152018_add_provider_and_uid_to_users.rb
db/schema.rb
| ... | ... | @@ -171,11 +171,12 @@ ActiveRecord::Schema.define(:version => 20120803152018) do |
| 171 | 171 | t.boolean "blocked", :default => false, :null => false |
| 172 | 172 | t.integer "failed_attempts", :default => 0 |
| 173 | 173 | t.datetime "locked_at" |
| 174 | + t.string "extern_uid" | |
| 174 | 175 | t.string "provider" |
| 175 | - t.string "uid" | |
| 176 | 176 | end |
| 177 | 177 | |
| 178 | 178 | add_index "users", ["email"], :name => "index_users_on_email", :unique => true |
| 179 | + add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true | |
| 179 | 180 | add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true |
| 180 | 181 | |
| 181 | 182 | create_table "users_projects", :force => true do |t| | ... | ... |
doc/installation.md
| ... | ... | @@ -119,7 +119,6 @@ Permissions: |
| 119 | 119 | |
| 120 | 120 | sudo chmod -R g+rwX /home/git/repositories/ |
| 121 | 121 | sudo chown -R git:git /home/git/repositories/ |
| 122 | - sudo chown gitlab:gitlab /home/git/repositories/**/hooks/post-receive | |
| 123 | 122 | |
| 124 | 123 | #### CHECK: Logout & login again to apply git group to your user |
| 125 | 124 | |
| ... | ... | @@ -134,7 +133,7 @@ Permissions: |
| 134 | 133 | |
| 135 | 134 | # 4. Install gitlab and configuration. Check status configuration. |
| 136 | 135 | |
| 137 | - sudo gem install charlock_holmes | |
| 136 | + sudo gem install charlock_holmes --version '0.6.8' | |
| 138 | 137 | sudo pip install pygments |
| 139 | 138 | sudo gem install bundler |
| 140 | 139 | cd /home/gitlab |
| ... | ... | @@ -177,6 +176,11 @@ Permissions: |
| 177 | 176 | #### Setup DB |
| 178 | 177 | |
| 179 | 178 | sudo -u gitlab bundle exec rake gitlab:app:setup RAILS_ENV=production |
| 179 | + | |
| 180 | +#### Setup gitlab hooks | |
| 181 | + | |
| 182 | + sudo cp ./lib/hooks/post-receive /home/git/share/gitolite/hooks/common/post-receive | |
| 183 | + sudo chown git:git /home/git/share/gitolite/hooks/common/post-receive | |
| 180 | 184 | |
| 181 | 185 | Checking status: |
| 182 | 186 | |
| ... | ... | @@ -196,6 +200,7 @@ Checking status: |
| 196 | 200 | Resolving deltas: 100% (174/174), done. |
| 197 | 201 | Can clone gitolite-admin?............YES |
| 198 | 202 | UMASK for .gitolite.rc is 0007? ............YES |
| 203 | + /home/git/share/gitolite/hooks/common/post-receive exists? ............YES | |
| 199 | 204 | |
| 200 | 205 | If you got all YES - congrats! You can go to next step. |
| 201 | 206 | ... | ... |
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +Feature: Labels | |
| 2 | + Background: | |
| 3 | + Given I signin as a user | |
| 4 | + And I own project "Shop" | |
| 5 | + And project "Shop" have issues tags: | |
| 6 | + | name | | |
| 7 | + | bug | | |
| 8 | + | feature | | |
| 9 | + Given I visit project "Shop" labels page | |
| 10 | + | |
| 11 | + Scenario: I should see active milestones | |
| 12 | + Then I should see label "bug" | |
| 13 | + And I should see label "feature" | ... | ... |
features/projects/network.feature
| ... | ... | @@ -4,9 +4,7 @@ Feature: Project Network Graph |
| 4 | 4 | Background: |
| 5 | 5 | Given I signin as a user |
| 6 | 6 | And I own project "Shop" |
| 7 | - And I visit project "Shop" network page | |
| 7 | + And I visit project "Shop" network page | |
| 8 | 8 | |
| 9 | 9 | Scenario: I should see project network |
| 10 | 10 | Then page should have network graph |
| 11 | - | |
| 12 | - | ... | ... |
features/step_definitions/dashboard_steps.rb
| ... | ... | @@ -91,36 +91,28 @@ Then /^I should see my merge requests$/ do |
| 91 | 91 | end |
| 92 | 92 | |
| 93 | 93 | Given /^I have assigned issues$/ do |
| 94 | - project1 = Factory :project, | |
| 95 | - :path => "project1", | |
| 96 | - :code => "TEST1" | |
| 97 | - | |
| 98 | - project2 = Factory :project, | |
| 99 | - :path => "project2", | |
| 100 | - :code => "TEST2" | |
| 101 | - | |
| 102 | - project1.add_access(@user, :read, :write) | |
| 103 | - project2.add_access(@user, :read, :write) | |
| 94 | + project = Factory :project | |
| 95 | + project.add_access(@user, :read, :write) | |
| 104 | 96 | |
| 105 | 97 | issue1 = Factory :issue, |
| 106 | 98 | :author => @user, |
| 107 | 99 | :assignee => @user, |
| 108 | - :project => project1 | |
| 100 | + :project => project | |
| 109 | 101 | |
| 110 | 102 | issue2 = Factory :issue, |
| 111 | 103 | :author => @user, |
| 112 | 104 | :assignee => @user, |
| 113 | - :project => project2 | |
| 105 | + :project => project | |
| 114 | 106 | end |
| 115 | 107 | |
| 116 | 108 | Given /^I have authored merge requests$/ do |
| 117 | 109 | project1 = Factory :project, |
| 118 | - :path => "project1", | |
| 119 | - :code => "TEST1" | |
| 110 | + :path => "gitlabhq_1", | |
| 111 | + :code => "gitlabhq_1" | |
| 120 | 112 | |
| 121 | 113 | project2 = Factory :project, |
| 122 | - :path => "project2", | |
| 123 | - :code => "TEST2" | |
| 114 | + :path => "gitlabhq_2", | |
| 115 | + :code => "gitlabhq_2" | |
| 124 | 116 | |
| 125 | 117 | project1.add_access(@user, :read, :write) |
| 126 | 118 | project2.add_access(@user, :read, :write) | ... | ... |
features/step_definitions/project/project_issues_steps.rb
| ... | ... | @@ -33,6 +33,25 @@ Given /^I visit issue page "(.*?)"$/ do |arg1| |
| 33 | 33 | end |
| 34 | 34 | |
| 35 | 35 | Given /^I submit new issue "(.*?)"$/ do |arg1| |
| 36 | - fill_in "issue_title", :with => arg1 | |
| 36 | + fill_in "issue_title", with: arg1 | |
| 37 | 37 | click_button "Submit new issue" |
| 38 | 38 | end |
| 39 | + | |
| 40 | +Given /^project "(.*?)" have issues tags:$/ do |arg1, table| | |
| 41 | + project = Project.find_by_name(arg1) | |
| 42 | + table.hashes.each do |hash| | |
| 43 | + Factory :issue, | |
| 44 | + project: project, | |
| 45 | + label_list: [hash[:name]] | |
| 46 | + end | |
| 47 | +end | |
| 48 | + | |
| 49 | +Given /^I visit project "(.*?)" labels page$/ do |arg1| | |
| 50 | + visit project_labels_path(Project.find_by_name(arg1)) | |
| 51 | +end | |
| 52 | + | |
| 53 | +Then /^I should see label "(.*?)"$/ do |arg1| | |
| 54 | + within ".labels-table" do | |
| 55 | + page.should have_content arg1 | |
| 56 | + end | |
| 57 | +end | ... | ... |
features/step_definitions/project/projects_steps.rb
| ... | ... | @@ -57,6 +57,11 @@ end |
| 57 | 57 | |
| 58 | 58 | Given /^I visit project "(.*?)" network page$/ do |arg1| |
| 59 | 59 | project = Project.find_by_name(arg1) |
| 60 | + | |
| 61 | + # Stub out find_all to speed this up (10 commits vs. 650) | |
| 62 | + commits = Grit::Commit.find_all(project.repo, nil, {max_count: 10}) | |
| 63 | + Grit::Commit.stub(:find_all).and_return(commits) | |
| 64 | + | |
| 60 | 65 | visit graph_project_path(project) |
| 61 | 66 | end |
| 62 | 67 | |
| ... | ... | @@ -67,8 +72,8 @@ end |
| 67 | 72 | Given /^page should have network graph$/ do |
| 68 | 73 | page.should have_content "Project Network Graph" |
| 69 | 74 | within ".graph" do |
| 70 | - page.should have_content "stable" | |
| 71 | - page.should have_content "notes_refacto..." | |
| 75 | + page.should have_content "master" | |
| 76 | + page.should have_content "scss_refactor..." | |
| 72 | 77 | end |
| 73 | 78 | end |
| 74 | 79 | ... | ... |
features/support/env.rb
| 1 | -require 'simplecov' | |
| 2 | -SimpleCov.start 'rails' | |
| 1 | +unless ENV['CI'] | |
| 2 | + require 'simplecov' | |
| 3 | + SimpleCov.start 'rails' | |
| 4 | +end | |
| 3 | 5 | |
| 4 | 6 | require 'cucumber/rails' |
| 5 | 7 | require 'webmock/cucumber' |
| ... | ... | @@ -39,3 +41,10 @@ rescue NameError |
| 39 | 41 | end |
| 40 | 42 | |
| 41 | 43 | Cucumber::Rails::Database.javascript_strategy = :truncation |
| 44 | + | |
| 45 | +require 'headless' | |
| 46 | + | |
| 47 | +headless = Headless.new | |
| 48 | +headless.start | |
| 49 | + | |
| 50 | +require 'cucumber/rspec/doubles' | ... | ... |
lib/color.rb
| ... | ... | @@ -1,31 +0,0 @@ |
| 1 | -module Color | |
| 2 | - extend self | |
| 3 | - | |
| 4 | - def colorize(text, color_code) | |
| 5 | - "\033[#{color_code}#{text}\033[0m" | |
| 6 | - end | |
| 7 | - | |
| 8 | - def red(text) | |
| 9 | - colorize(text, "31m") | |
| 10 | - end | |
| 11 | - | |
| 12 | - def green(text) | |
| 13 | - colorize(text, "32m") | |
| 14 | - end | |
| 15 | - | |
| 16 | - def yellow(text) | |
| 17 | - colorize(text, "93m") | |
| 18 | - end | |
| 19 | - | |
| 20 | - def command(string) | |
| 21 | - `#{string}` | |
| 22 | - if $?.to_i > 0 | |
| 23 | - puts red " == #{string} - FAIL" | |
| 24 | - puts red " == Error during configure" | |
| 25 | - exit | |
| 26 | - else | |
| 27 | - puts green " == #{string} - OK" | |
| 28 | - end | |
| 29 | - end | |
| 30 | -end | |
| 31 | - |
| ... | ... | @@ -0,0 +1,183 @@ |
| 1 | +require "grit" | |
| 2 | + | |
| 3 | +module Gitlab | |
| 4 | + class GraphCommit | |
| 5 | + attr_accessor :time, :space | |
| 6 | + attr_accessor :refs | |
| 7 | + | |
| 8 | + def self.to_graph(project) | |
| 9 | + @repo = project.repo | |
| 10 | + commits = Grit::Commit.find_all(@repo, nil, {max_count: 650}) | |
| 11 | + | |
| 12 | + ref_cache = {} | |
| 13 | + | |
| 14 | + commits.map! {|c| GraphCommit.new(Commit.new(c))} | |
| 15 | + commits.each { |commit| commit.add_refs(ref_cache, @repo) } | |
| 16 | + | |
| 17 | + days = GraphCommit.index_commits(commits) | |
| 18 | + @days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json | |
| 19 | + @commits_json = commits.map(&:to_graph_hash).to_json | |
| 20 | + | |
| 21 | + return @days_json, @commits_json | |
| 22 | + end | |
| 23 | + | |
| 24 | + # Method is adding time and space on the | |
| 25 | + # list of commits. As well as returns date list | |
| 26 | + # corelated with time set on commits. | |
| 27 | + # | |
| 28 | + # @param [Array<GraphCommit>] comits to index | |
| 29 | + # | |
| 30 | + # @return [Array<TimeDate>] list of commit dates corelated with time on commits | |
| 31 | + def self.index_commits(commits) | |
| 32 | + days, heads = [], [] | |
| 33 | + map = {} | |
| 34 | + | |
| 35 | + commits.reverse.each_with_index do |c,i| | |
| 36 | + c.time = i | |
| 37 | + days[i] = c.committed_date | |
| 38 | + map[c.id] = c | |
| 39 | + heads += c.refs unless c.refs.nil? | |
| 40 | + end | |
| 41 | + | |
| 42 | + heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote} | |
| 43 | + # sort heads so the master is top and current branches are closer | |
| 44 | + heads.sort! do |a,b| | |
| 45 | + if a.name == "master" | |
| 46 | + -1 | |
| 47 | + elsif b.name == "master" | |
| 48 | + 1 | |
| 49 | + else | |
| 50 | + b.commit.committed_date <=> a.commit.committed_date | |
| 51 | + end | |
| 52 | + end | |
| 53 | + | |
| 54 | + @_reserved = {} | |
| 55 | + days.each_index do |i| | |
| 56 | + @_reserved[i] = [] | |
| 57 | + end | |
| 58 | + | |
| 59 | + heads.each do |h| | |
| 60 | + if map.include? h.commit.id then | |
| 61 | + place_chain(map[h.commit.id], map) | |
| 62 | + end | |
| 63 | + end | |
| 64 | + days | |
| 65 | + end | |
| 66 | + | |
| 67 | + # Add space mark on commit and its parents | |
| 68 | + # | |
| 69 | + # @param [GraphCommit] the commit object. | |
| 70 | + # @param [Hash<String,GraphCommit>] map of commits | |
| 71 | + def self.place_chain(commit, map, parent_time = nil) | |
| 72 | + leaves = take_left_leaves(commit, map) | |
| 73 | + if leaves.empty? then | |
| 74 | + return | |
| 75 | + end | |
| 76 | + space = find_free_space(leaves.last.time..leaves.first.time) | |
| 77 | + leaves.each{|l| l.space = space} | |
| 78 | + # and mark it as reserved | |
| 79 | + min_time = leaves.last.time | |
| 80 | + parents = leaves.last.parents.collect | |
| 81 | + parents.each do |p| | |
| 82 | + if map.include? p.id then | |
| 83 | + parent = map[p.id] | |
| 84 | + if parent.time < min_time then | |
| 85 | + min_time = parent.time | |
| 86 | + end | |
| 87 | + end | |
| 88 | + end | |
| 89 | + if parent_time.nil? then | |
| 90 | + max_time = leaves.first.time | |
| 91 | + else | |
| 92 | + max_time = parent_time - 1 | |
| 93 | + end | |
| 94 | + mark_reserved(min_time..max_time, space) | |
| 95 | + # Visit branching chains | |
| 96 | + leaves.each do |l| | |
| 97 | + parents = l.parents.collect | |
| 98 | + .select{|p| map.include? p.id and map[p.id].space == 0} | |
| 99 | + for p in parents | |
| 100 | + place_chain(map[p.id], map, l.time) | |
| 101 | + end | |
| 102 | + end | |
| 103 | + end | |
| 104 | + | |
| 105 | + def self.mark_reserved(time_range, space) | |
| 106 | + for day in time_range | |
| 107 | + @_reserved[day].push(space) | |
| 108 | + end | |
| 109 | + end | |
| 110 | + | |
| 111 | + def self.find_free_space(time_range) | |
| 112 | + reserved = [] | |
| 113 | + for day in time_range | |
| 114 | + reserved += @_reserved[day] | |
| 115 | + end | |
| 116 | + space = 1 | |
| 117 | + while reserved.include? space do | |
| 118 | + space += 1 | |
| 119 | + end | |
| 120 | + space | |
| 121 | + end | |
| 122 | + | |
| 123 | + # Takes most left subtree branch of commits | |
| 124 | + # which don't have space mark yet. | |
| 125 | + # | |
| 126 | + # @param [GraphCommit] the commit object. | |
| 127 | + # @param [Hash<String,GraphCommit>] map of commits | |
| 128 | + # | |
| 129 | + # @return [Array<GraphCommit>] list of branch commits | |
| 130 | + def self.take_left_leaves(commit, map) | |
| 131 | + leaves = [] | |
| 132 | + leaves.push(commit) if commit.space == 0 | |
| 133 | + while true | |
| 134 | + parent = commit.parents.collect | |
| 135 | + .select{|p| map.include? p.id and map[p.id].space == 0} | |
| 136 | + if parent.count == 0 then | |
| 137 | + return leaves | |
| 138 | + else | |
| 139 | + commit = map[parent.first.id] | |
| 140 | + leaves.push(commit) | |
| 141 | + end | |
| 142 | + end | |
| 143 | + end | |
| 144 | + | |
| 145 | + | |
| 146 | + def initialize(commit) | |
| 147 | + @_commit = commit | |
| 148 | + @time = -1 | |
| 149 | + @space = 0 | |
| 150 | + end | |
| 151 | + | |
| 152 | + def method_missing(m, *args, &block) | |
| 153 | + @_commit.send(m, *args, &block) | |
| 154 | + end | |
| 155 | + | |
| 156 | + def to_graph_hash | |
| 157 | + h = {} | |
| 158 | + h[:parents] = self.parents.collect do |p| | |
| 159 | + [p.id,0,0] | |
| 160 | + end | |
| 161 | + h[:author] = Gitlab::Encode.utf8(author.name) | |
| 162 | + h[:time] = time | |
| 163 | + h[:space] = space | |
| 164 | + h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? | |
| 165 | + h[:id] = sha | |
| 166 | + h[:date] = date | |
| 167 | + h[:message] = Gitlab::Encode.utf8(message) | |
| 168 | + h[:login] = author.email | |
| 169 | + h | |
| 170 | + end | |
| 171 | + | |
| 172 | + def add_refs(ref_cache, repo) | |
| 173 | + if ref_cache.empty? | |
| 174 | + repo.refs.each do |ref| | |
| 175 | + ref_cache[ref.commit.id] ||= [] | |
| 176 | + ref_cache[ref.commit.id] << ref | |
| 177 | + end | |
| 178 | + end | |
| 179 | + @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id) | |
| 180 | + @refs ||= [] | |
| 181 | + end | |
| 182 | + end | |
| 183 | +end | ... | ... |
lib/gitlab/markdown.rb
| 1 | 1 | module Gitlab |
| 2 | - # Custom parsing for Gitlab-flavored Markdown | |
| 2 | + # Custom parser for Gitlab-flavored Markdown | |
| 3 | + # | |
| 4 | + # It replaces references in the text with links to the appropriate items in Gitlab. | |
| 5 | + # | |
| 6 | + # Supported reference formats are: | |
| 7 | + # * @foo for team members | |
| 8 | + # * #123 for issues | |
| 9 | + # * !123 for merge requests | |
| 10 | + # * $123 for snippets | |
| 11 | + # * 123456 for commits | |
| 3 | 12 | # |
| 4 | 13 | # Examples |
| 5 | 14 | # |
| ... | ... | @@ -67,25 +76,25 @@ module Gitlab |
| 67 | 76 | def reference_user(identifier) |
| 68 | 77 | if user = @project.users.where(name: identifier).first |
| 69 | 78 | member = @project.users_projects.where(user_id: user).first |
| 70 | - link_to("@#{user.name}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member | |
| 79 | + link_to("@#{identifier}", project_team_member_path(@project, member), html_options.merge(class: "gfm gfm-team_member #{html_options[:class]}")) if member | |
| 71 | 80 | end |
| 72 | 81 | end |
| 73 | 82 | |
| 74 | 83 | def reference_issue(identifier) |
| 75 | 84 | if issue = @project.issues.where(id: identifier).first |
| 76 | - link_to("##{issue.id}", project_issue_path(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}")) | |
| 85 | + link_to("##{identifier}", project_issue_path(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}")) | |
| 77 | 86 | end |
| 78 | 87 | end |
| 79 | 88 | |
| 80 | 89 | def reference_merge_request(identifier) |
| 81 | 90 | if merge_request = @project.merge_requests.where(id: identifier).first |
| 82 | - link_to("!#{merge_request.id}", project_merge_request_path(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}")) | |
| 91 | + link_to("!#{identifier}", project_merge_request_path(@project, merge_request), html_options.merge(title: "Merge Request: #{merge_request.title}", class: "gfm gfm-merge_request #{html_options[:class]}")) | |
| 83 | 92 | end |
| 84 | 93 | end |
| 85 | 94 | |
| 86 | 95 | def reference_snippet(identifier) |
| 87 | 96 | if snippet = @project.snippets.where(id: identifier).first |
| 88 | - link_to("$#{snippet.id}", project_snippet_path(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}")) | |
| 97 | + link_to("$#{identifier}", project_snippet_path(@project, snippet), html_options.merge(title: "Snippet: #{snippet.title}", class: "gfm gfm-snippet #{html_options[:class]}")) | |
| 89 | 98 | end |
| 90 | 99 | end |
| 91 | 100 | ... | ... |
lib/graph_commit.rb
| ... | ... | @@ -1,181 +0,0 @@ |
| 1 | -require "grit" | |
| 2 | - | |
| 3 | -class GraphCommit | |
| 4 | - attr_accessor :time, :space | |
| 5 | - attr_accessor :refs | |
| 6 | - | |
| 7 | - def self.to_graph(project) | |
| 8 | - @repo = project.repo | |
| 9 | - commits = Grit::Commit.find_all(@repo, nil, {max_count: 650}) | |
| 10 | - | |
| 11 | - ref_cache = {} | |
| 12 | - | |
| 13 | - commits.map! {|c| GraphCommit.new(Commit.new(c))} | |
| 14 | - commits.each { |commit| commit.add_refs(ref_cache, @repo) } | |
| 15 | - | |
| 16 | - days = GraphCommit.index_commits(commits) | |
| 17 | - @days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json | |
| 18 | - @commits_json = commits.map(&:to_graph_hash).to_json | |
| 19 | - | |
| 20 | - return @days_json, @commits_json | |
| 21 | - end | |
| 22 | - | |
| 23 | - # Method is adding time and space on the | |
| 24 | - # list of commits. As well as returns date list | |
| 25 | - # corelated with time set on commits. | |
| 26 | - # | |
| 27 | - # @param [Array<GraphCommit>] comits to index | |
| 28 | - # | |
| 29 | - # @return [Array<TimeDate>] list of commit dates corelated with time on commits | |
| 30 | - def self.index_commits(commits) | |
| 31 | - days, heads = [], [] | |
| 32 | - map = {} | |
| 33 | - | |
| 34 | - commits.reverse.each_with_index do |c,i| | |
| 35 | - c.time = i | |
| 36 | - days[i] = c.committed_date | |
| 37 | - map[c.id] = c | |
| 38 | - heads += c.refs unless c.refs.nil? | |
| 39 | - end | |
| 40 | - | |
| 41 | - heads.select!{|h| h.is_a? Grit::Head or h.is_a? Grit::Remote} | |
| 42 | - # sort heads so the master is top and current branches are closer | |
| 43 | - heads.sort! do |a,b| | |
| 44 | - if a.name == "master" | |
| 45 | - -1 | |
| 46 | - elsif b.name == "master" | |
| 47 | - 1 | |
| 48 | - else | |
| 49 | - b.commit.committed_date <=> a.commit.committed_date | |
| 50 | - end | |
| 51 | - end | |
| 52 | - | |
| 53 | - @_reserved = {} | |
| 54 | - days.each_index do |i| | |
| 55 | - @_reserved[i] = [] | |
| 56 | - end | |
| 57 | - | |
| 58 | - heads.each do |h| | |
| 59 | - if map.include? h.commit.id then | |
| 60 | - place_chain(map[h.commit.id], map) | |
| 61 | - end | |
| 62 | - end | |
| 63 | - days | |
| 64 | - end | |
| 65 | - | |
| 66 | - # Add space mark on commit and its parents | |
| 67 | - # | |
| 68 | - # @param [GraphCommit] the commit object. | |
| 69 | - # @param [Hash<String,GraphCommit>] map of commits | |
| 70 | - def self.place_chain(commit, map, parent_time = nil) | |
| 71 | - leaves = take_left_leaves(commit, map) | |
| 72 | - if leaves.empty? then | |
| 73 | - return | |
| 74 | - end | |
| 75 | - space = find_free_space(leaves.last.time..leaves.first.time) | |
| 76 | - leaves.each{|l| l.space = space} | |
| 77 | - # and mark it as reserved | |
| 78 | - min_time = leaves.last.time | |
| 79 | - parents = leaves.last.parents.collect | |
| 80 | - parents.each do |p| | |
| 81 | - if map.include? p.id then | |
| 82 | - parent = map[p.id] | |
| 83 | - if parent.time < min_time then | |
| 84 | - min_time = parent.time | |
| 85 | - end | |
| 86 | - end | |
| 87 | - end | |
| 88 | - if parent_time.nil? then | |
| 89 | - max_time = leaves.first.time | |
| 90 | - else | |
| 91 | - max_time = parent_time - 1 | |
| 92 | - end | |
| 93 | - mark_reserved(min_time..max_time, space) | |
| 94 | - # Visit branching chains | |
| 95 | - leaves.each do |l| | |
| 96 | - parents = l.parents.collect | |
| 97 | - .select{|p| map.include? p.id and map[p.id].space == 0} | |
| 98 | - for p in parents | |
| 99 | - place_chain(map[p.id], map, l.time) | |
| 100 | - end | |
| 101 | - end | |
| 102 | - end | |
| 103 | - | |
| 104 | - def self.mark_reserved(time_range, space) | |
| 105 | - for day in time_range | |
| 106 | - @_reserved[day].push(space) | |
| 107 | - end | |
| 108 | - end | |
| 109 | - | |
| 110 | - def self.find_free_space(time_range) | |
| 111 | - reserved = [] | |
| 112 | - for day in time_range | |
| 113 | - reserved += @_reserved[day] | |
| 114 | - end | |
| 115 | - space = 1 | |
| 116 | - while reserved.include? space do | |
| 117 | - space += 1 | |
| 118 | - end | |
| 119 | - space | |
| 120 | - end | |
| 121 | - | |
| 122 | - # Takes most left subtree branch of commits | |
| 123 | - # which don't have space mark yet. | |
| 124 | - # | |
| 125 | - # @param [GraphCommit] the commit object. | |
| 126 | - # @param [Hash<String,GraphCommit>] map of commits | |
| 127 | - # | |
| 128 | - # @return [Array<GraphCommit>] list of branch commits | |
| 129 | - def self.take_left_leaves(commit, map) | |
| 130 | - leaves = [] | |
| 131 | - leaves.push(commit) if commit.space == 0 | |
| 132 | - while true | |
| 133 | - parent = commit.parents.collect | |
| 134 | - .select{|p| map.include? p.id and map[p.id].space == 0} | |
| 135 | - if parent.count == 0 then | |
| 136 | - return leaves | |
| 137 | - else | |
| 138 | - commit = map[parent.first.id] | |
| 139 | - leaves.push(commit) | |
| 140 | - end | |
| 141 | - end | |
| 142 | - end | |
| 143 | - | |
| 144 | - | |
| 145 | - def initialize(commit) | |
| 146 | - @_commit = commit | |
| 147 | - @time = -1 | |
| 148 | - @space = 0 | |
| 149 | - end | |
| 150 | - | |
| 151 | - def method_missing(m, *args, &block) | |
| 152 | - @_commit.send(m, *args, &block) | |
| 153 | - end | |
| 154 | - | |
| 155 | - def to_graph_hash | |
| 156 | - h = {} | |
| 157 | - h[:parents] = self.parents.collect do |p| | |
| 158 | - [p.id,0,0] | |
| 159 | - end | |
| 160 | - h[:author] = Gitlab::Encode.utf8(author.name) | |
| 161 | - h[:time] = time | |
| 162 | - h[:space] = space | |
| 163 | - h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? | |
| 164 | - h[:id] = sha | |
| 165 | - h[:date] = date | |
| 166 | - h[:message] = Gitlab::Encode.utf8(message) | |
| 167 | - h[:login] = author.email | |
| 168 | - h | |
| 169 | - end | |
| 170 | - | |
| 171 | - def add_refs(ref_cache, repo) | |
| 172 | - if ref_cache.empty? | |
| 173 | - repo.refs.each do |ref| | |
| 174 | - ref_cache[ref.commit.id] ||= [] | |
| 175 | - ref_cache[ref.commit.id] << ref | |
| 176 | - end | |
| 177 | - end | |
| 178 | - @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id) | |
| 179 | - @refs ||= [] | |
| 180 | - end | |
| 181 | -end |
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +#!/usr/bin/env bash | |
| 2 | + | |
| 3 | +# This file was placed here by Gitlab. It makes sure that your pushed commits | |
| 4 | +# will be processed properly. | |
| 5 | + | |
| 6 | +while read oldrev newrev ref | |
| 7 | +do | |
| 8 | + # For every branch or tag that was pushed, create a Resque job in redis. | |
| 9 | + pwd=`pwd` | |
| 10 | + reponame=`basename "$pwd" | cut -d. -f1` | |
| 11 | + env -i redis-cli rpush "resque:queue:post_receive" "{\"class\":\"PostReceive\",\"args\":[\"$reponame\",\"$oldrev\",\"$newrev\",\"$ref\",\"$GL_USER\"]}" > /dev/null 2>&1 | |
| 12 | +done | ... | ... |
lib/post-receive-hook
| ... | ... | @@ -1,12 +0,0 @@ |
| 1 | -#!/usr/bin/env bash | |
| 2 | - | |
| 3 | -# This file was placed here by Gitlab. It makes sure that your pushed commits | |
| 4 | -# will be processed properly. | |
| 5 | - | |
| 6 | -while read oldrev newrev ref | |
| 7 | -do | |
| 8 | - # For every branch or tag that was pushed, create a Resque job in redis. | |
| 9 | - pwd=`pwd` | |
| 10 | - reponame=`basename "$pwd" | cut -d. -f1` | |
| 11 | - env -i redis-cli rpush "resque:queue:post_receive" "{\"class\":\"PostReceive\",\"args\":[\"$reponame\",\"$oldrev\",\"$newrev\",\"$ref\",\"$GL_USER\"]}" > /dev/null 2>&1 | |
| 12 | -done |
lib/tasks/dev/repo.rake
| ... | ... | @@ -1,26 +0,0 @@ |
| 1 | -namespace :dev do | |
| 2 | - desc "Prepare for development (run dev_user.sh first)" | |
| 3 | - task :repos => :environment do | |
| 4 | - key = `sudo -u gitlabdev -H cat /home/gitlabdev/.ssh/id_rsa.pub` | |
| 5 | - raise "\n *** Run ./lib/tasks/dev/user.sh first *** \n" if key.empty? | |
| 6 | - Key.create(:user_id => User.first, :key => key, :title => "gitlabdev") | |
| 7 | - | |
| 8 | - puts "\n *** Clone diaspora from github" | |
| 9 | - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev; git clone git://github.com/diaspora/diaspora.git /home/gitlabdev/diaspora"` | |
| 10 | - | |
| 11 | - puts "\n *** Push diaspora source to gitlab" | |
| 12 | - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev/diaspora; git remote add local git@localhost:diaspora.git; git push local master; git push local --tags; git checkout -b api origin/api; git push local api; git checkout -b heroku origin/heroku; git push local heroku"` | |
| 13 | - | |
| 14 | - puts "\n *** Clone rails from github" | |
| 15 | - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev; git clone git://github.com/rails/rails.git /home/gitlabdev/rails"` | |
| 16 | - | |
| 17 | - puts "\n *** Push rails source to gitlab" | |
| 18 | - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev/rails; git remote add local git@localhost:ruby_on_rails.git; git push local master; git push local --tags"` | |
| 19 | - | |
| 20 | - puts "\n *** Clone rubinius from github" | |
| 21 | - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev; git clone git://github.com/rubinius/rubinius.git /home/gitlabdev/rubinius"` | |
| 22 | - | |
| 23 | - puts "\n *** Push rubinius source to gitlab" | |
| 24 | - `sudo -u gitlabdev -H sh -c "cd /home/gitlabdev/rubinius; git remote add local git@localhost:rubinius.git; git push local master; git push local --tags"` | |
| 25 | - end | |
| 26 | -end |
lib/tasks/dev/tests.rake
lib/tasks/dev/user.sh
lib/tasks/gitlab/setup.rake
lib/tasks/gitlab/status.rake
| ... | ... | @@ -56,6 +56,20 @@ namespace :gitlab do |
| 56 | 56 | return |
| 57 | 57 | end |
| 58 | 58 | |
| 59 | + gitolite_hooks_path = File.join("/home", Gitlab.config.ssh_user, "share", "gitolite", "hooks", "common") | |
| 60 | + gitlab_hook_files = ['post-receive'] | |
| 61 | + gitlab_hook_files.each do |file_name| | |
| 62 | + dest = File.join(gitolite_hooks_path, file_name) | |
| 63 | + print "#{dest} exists? ............" | |
| 64 | + if File.exists?(dest) | |
| 65 | + puts "YES".green | |
| 66 | + else | |
| 67 | + puts "NO".red | |
| 68 | + return | |
| 69 | + end | |
| 70 | + end | |
| 71 | + | |
| 72 | + | |
| 59 | 73 | if Project.count > 0 |
| 60 | 74 | puts "Validating projects repositories:".yellow |
| 61 | 75 | Project.find_each(:batch_size => 100) do |project| |
| ... | ... | @@ -67,12 +81,6 @@ namespace :gitlab do |
| 67 | 81 | next |
| 68 | 82 | end |
| 69 | 83 | |
| 70 | - | |
| 71 | - unless File.owned?(hook_file) | |
| 72 | - puts "post-receive file is not owner by gitlab".red | |
| 73 | - next | |
| 74 | - end | |
| 75 | - | |
| 76 | 84 | puts "post-reveice file ok".green |
| 77 | 85 | end |
| 78 | 86 | end | ... | ... |
lib/tasks/gitlab/update_hooks.rake
| ... | ... | @@ -1,19 +0,0 @@ |
| 1 | -namespace :gitlab do | |
| 2 | - namespace :gitolite do | |
| 3 | - desc "GITLAB | Rewrite hooks for repos" | |
| 4 | - task :update_hooks => :environment do | |
| 5 | - puts "Starting Projects" | |
| 6 | - Project.find_each(:batch_size => 100) do |project| | |
| 7 | - begin | |
| 8 | - if project.commit | |
| 9 | - project.write_hooks | |
| 10 | - print ".".green | |
| 11 | - end | |
| 12 | - rescue Exception => e | |
| 13 | - print e.message.red | |
| 14 | - end | |
| 15 | - end | |
| 16 | - puts "\nDone with projects" | |
| 17 | - end | |
| 18 | - end | |
| 19 | -end |
| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | +namespace :gitlab do | |
| 2 | + namespace :gitolite do | |
| 3 | + desc "GITLAB | Write GITLAB hook for gitolite" | |
| 4 | + task :write_hooks => :environment do | |
| 5 | + gitolite_hooks_path = File.join("/home", Gitlab.config.ssh_user, "share", "gitolite", "hooks", "common") | |
| 6 | + gitlab_hooks_path = Rails.root.join("lib", "hooks") | |
| 7 | + | |
| 8 | + gitlab_hook_files = ['post-receive'] | |
| 9 | + | |
| 10 | + gitlab_hook_files.each do |file_name| | |
| 11 | + source = File.join(gitlab_hooks_path, file_name) | |
| 12 | + dest = File.join(gitolite_hooks_path, file_name) | |
| 13 | + | |
| 14 | + puts "sudo -u root cp #{source} #{dest}".yellow | |
| 15 | + `sudo -u root cp #{source} #{dest}` | |
| 16 | + | |
| 17 | + puts "sudo -u root chown git:git #{dest}".yellow | |
| 18 | + `sudo -u root chown git:git #{dest}` | |
| 19 | + end | |
| 20 | + end | |
| 21 | + end | |
| 22 | +end | |
| 23 | + | ... | ... |