Commit a03f59d5 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-6-9' into 'master'

Upstream 6.9.0.pre CE to EE
parents 09ebd837 556ca352
user: git
group: git
before_precompile: ./bin/pkgr_before_precompile.sh
targets:
debian-7: &wheezy
build_dependencies:
- libicu-dev
dependencies:
- libicu48
- libpcre3
- git
ubuntu-12.04: *wheezy
ubuntu-14.04:
build_dependencies:
- libicu-dev
dependencies:
- libicu52
- libpcre3
- git
v 6.9.0
- Store Rails cache data in the Redis `cache:gitlab` namespace
- Adjust MySQL limits for existing installations
- Add db index on project_id+iid column. This prevents duplicate on iid (During migration duplicates will be removed)
- Markdown preview or diff during editing via web editor (Evgeniy Sokovikov)
- Give the Rails cache its own Redis namespace
- Add ability to set different ssh host, if different from http/https
- Fix syntax highlighting for code comments blocks
- Improve comments loading logic
- Stop refreshing comments when the tab is hidden
- Improve issue and merge request mobile UI (Drew Blessing)
- Document how to convert a backup to PostgreSQL
- Fix locale bug in backup manager
- Fix wiki backup skip bug
v 6.8.1 v 6.8.1
- Bump required gitlab-shell version to 1.9.3 - Bump required gitlab-shell version to 1.9.3
......
...@@ -107,9 +107,10 @@ For examples of feedback on merge requests please look at already [closed merge ...@@ -107,9 +107,10 @@ For examples of feedback on merge requests please look at already [closed merge
## Style guides ## Style guides
1. [Ruby](https://github.com/bbatsov/ruby-style-guide) 1. [Ruby](https://github.com/bbatsov/ruby-style-guide)
2. [Rails](https://github.com/bbatsov/rails-style-guide) 1. [Rails](https://github.com/bbatsov/rails-style-guide)
3. [Formatting](https://github.com/thoughtbot/guides/tree/master/style#formatting) 1. [Formatting](https://github.com/thoughtbot/guides/tree/master/style#formatting)
4. [Naming](https://github.com/thoughtbot/guides/tree/master/style#naming) 1. [Naming](https://github.com/thoughtbot/guides/tree/master/style#naming)
8. [Testing](https://github.com/thoughtbot/guides/tree/master/style#testing) 1. [Testing](https://github.com/thoughtbot/guides/tree/master/style#testing)
7. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript) 1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript)
9. [Shell commands](doc/development/shell_commands.md) 1. [Shell commands](doc/development/shell_commands.md)
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
...@@ -72,6 +72,7 @@ gem "carrierwave" ...@@ -72,6 +72,7 @@ gem "carrierwave"
# for aws storage # for aws storage
gem "fog", "~> 1.14", group: :aws gem "fog", "~> 1.14", group: :aws
gem "unf", group: :aws
# Authorization # Authorization
gem "six" gem "six"
...@@ -83,6 +84,9 @@ gem "seed-fu" ...@@ -83,6 +84,9 @@ gem "seed-fu"
gem "redcarpet", "~> 2.2.2" gem "redcarpet", "~> 2.2.2"
gem "github-markup" gem "github-markup"
# Diffs
gem 'diffy', '~> 3.0.3'
# Asciidoc to HTML # Asciidoc to HTML
gem "asciidoctor" gem "asciidoctor"
...@@ -158,8 +162,8 @@ gem 'jquery-turbolinks' ...@@ -158,8 +162,8 @@ gem 'jquery-turbolinks'
gem 'select2-rails' gem 'select2-rails'
gem 'jquery-atwho-rails', "~> 0.3.3" gem 'jquery-atwho-rails', "~> 0.3.3"
gem "jquery-rails", "2.1.3" gem "jquery-rails"
gem "jquery-ui-rails", "2.0.2" gem "jquery-ui-rails"
gem "raphael-rails", "~> 2.1.2" gem "raphael-rails", "~> 2.1.2"
gem 'bootstrap-sass', '~> 3.0' gem 'bootstrap-sass', '~> 3.0'
gem "font-awesome-rails", '~> 3.2' gem "font-awesome-rails", '~> 3.2'
......
...@@ -100,6 +100,7 @@ GEM ...@@ -100,6 +100,7 @@ GEM
devise-async (0.8.0) devise-async (0.8.0)
devise (>= 2.2, < 3.2) devise (>= 2.2, < 3.2)
diff-lcs (1.2.5) diff-lcs (1.2.5)
diffy (3.0.3)
docile (1.1.1) docile (1.1.1)
dotenv (0.9.0) dotenv (0.9.0)
email_spec (1.5.0) email_spec (1.5.0)
...@@ -161,7 +162,7 @@ GEM ...@@ -161,7 +162,7 @@ GEM
multi_json multi_json
gitlab-grack (2.0.0.pre) gitlab-grack (2.0.0.pre)
rack (~> 1.5.1) rack (~> 1.5.1)
gitlab-grit (2.6.5) gitlab-grit (2.6.6)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (~> 1.15) mime-types (~> 1.15)
...@@ -246,15 +247,14 @@ GEM ...@@ -246,15 +247,14 @@ GEM
rake rake
jasmine-core (2.0.0.rc5) jasmine-core (2.0.0.rc5)
jquery-atwho-rails (0.3.3) jquery-atwho-rails (0.3.3)
jquery-rails (2.1.3) jquery-rails (3.1.0)
railties (>= 3.1.0, < 5.0) railties (>= 3.0, < 5.0)
thor (~> 0.14) thor (>= 0.14, < 2.0)
jquery-turbolinks (2.0.1) jquery-turbolinks (2.0.1)
railties (>= 3.1.0) railties (>= 3.1.0)
turbolinks turbolinks
jquery-ui-rails (2.0.2) jquery-ui-rails (4.2.1)
jquery-rails railties (>= 3.2.16)
railties (>= 3.1.0)
json (1.8.1) json (1.8.1)
jwt (0.1.8) jwt (0.1.8)
multi_json (>= 1.5) multi_json (>= 1.5)
...@@ -530,6 +530,9 @@ GEM ...@@ -530,6 +530,9 @@ GEM
execjs (>= 0.3.0) execjs (>= 0.3.0)
json (>= 1.8.0) json (>= 1.8.0)
underscore-rails (1.4.4) underscore-rails (1.4.4)
unf (0.1.4)
unf_ext
unf_ext (0.0.6)
unicorn (4.6.3) unicorn (4.6.3)
kgio (~> 2.6) kgio (~> 2.6)
rack rack
...@@ -573,6 +576,7 @@ DEPENDENCIES ...@@ -573,6 +576,7 @@ DEPENDENCIES
default_value_for (~> 3.0.0) default_value_for (~> 3.0.0)
devise (= 3.0.4) devise (= 3.0.4)
devise-async (= 0.8.0) devise-async (= 0.8.0)
diffy (~> 3.0.3)
email_spec email_spec
email_validator (~> 1.4.0) email_validator (~> 1.4.0)
enumerize enumerize
...@@ -602,9 +606,9 @@ DEPENDENCIES ...@@ -602,9 +606,9 @@ DEPENDENCIES
httparty httparty
jasmine (= 2.0.0.rc5) jasmine (= 2.0.0.rc5)
jquery-atwho-rails (~> 0.3.3) jquery-atwho-rails (~> 0.3.3)
jquery-rails (= 2.1.3) jquery-rails
jquery-turbolinks jquery-turbolinks
jquery-ui-rails (= 2.0.2) jquery-ui-rails
kaminari (~> 0.15.1) kaminari (~> 0.15.1)
launchy launchy
letter_opener letter_opener
...@@ -659,6 +663,7 @@ DEPENDENCIES ...@@ -659,6 +663,7 @@ DEPENDENCIES
turbolinks turbolinks
uglifier uglifier
underscore-rails (~> 1.4.4) underscore-rails (~> 1.4.4)
unf
unicorn (~> 4.6.3) unicorn (~> 4.6.3)
unicorn-worker-killer unicorn-worker-killer
version_sorter version_sorter
......
...@@ -29,11 +29,11 @@ ...@@ -29,11 +29,11 @@
* [GitLab.com](https://www.gitlab.com/) includes information about [subscriptions](https://www.gitlab.com/subscription/), [consultancy](https://www.gitlab.com/consultancy/), the [community](https://www.gitlab.com/community/) and the [hosted GitLab Cloud](https://www.gitlab.com/cloud/). * [GitLab.com](https://www.gitlab.com/) includes information about [subscriptions](https://www.gitlab.com/subscription/), [consultancy](https://www.gitlab.com/consultancy/), the [community](https://www.gitlab.com/community/) and the [hosted GitLab Cloud](https://www.gitlab.com/cloud/).
* [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ce/) offers additional features that are useful for larger organizations (100+ users). * [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ee/) offers additional features aimed at larger organizations.
* [GitLab CI](https://www.gitlab.com/gitlab-ci/) is a continuous integration (CI) server that is easy to integrate with GitLab. * [GitLab CI](https://www.gitlab.com/gitlab-ci/) is a continuous integration (CI) server that is easy to integrate with GitLab.
* Unofficial third-party [iPhone app](http://gitlabcontrol.com/) and [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en) for GitLab * Unofficial third-party [iPhone app](http://gitlabcontrol.com/), [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en) and [command line client](https://github.com/drewblessing/gitlab-cli) for GitLab.
### Requirements ### Requirements
......
6.8.0-ee 6.9.0.pre-ee
...@@ -59,7 +59,7 @@ class Dispatcher ...@@ -59,7 +59,7 @@ class Dispatcher
initHighlight: -> initHighlight: ->
$('.highlight pre code').each (i, e) -> $('.highlight pre code').each (i, e) ->
hljs.highlightBlock(e)
$(e).html($.map($(e).html().split("\n"), (line, i) -> $(e).html($.map($(e).html().split("\n"), (line, i) ->
"<div class='line' id='LC" + (i + 1) + "'>" + line + "</div>" "<span class='line' id='LC" + (i + 1) + "'>" + line + "</span>"
).join("\n")) ).join("\n"))
hljs.highlightBlock(e)
class Notes class Notes
@interval: null @interval: null
constructor: (notes_url, note_ids) -> constructor: (notes_url, note_ids, last_fetched_at) ->
@notes_url = notes_url @notes_url = notes_url
@notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root? @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
@note_ids = note_ids @note_ids = note_ids
@last_fetched_at = last_fetched_at
@initRefresh() @initRefresh()
@setupMainTargetNoteForm() @setupMainTargetNoteForm()
@cleanBinding() @cleanBinding()
...@@ -49,6 +50,15 @@ class Notes ...@@ -49,6 +50,15 @@ class Notes
# hide diff note form # hide diff note form
$(document).on "click", ".js-close-discussion-note-form", @cancelDiscussionForm $(document).on "click", ".js-close-discussion-note-form", @cancelDiscussionForm
# fetch notes when tab becomes visible
$(document).on "visibilitychange", @visibilityChange
@notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
$(document).on('keypress', @notes_forms, (e)->
if event.keyCode == 10 || (event.ctrlKey && event.keyCode == 13)
$(@).parents('form').submit()
)
cleanBinding: -> cleanBinding: ->
$(document).off "ajax:success", ".js-main-target-form" $(document).off "ajax:success", ".js-main-target-form"
$(document).off "ajax:success", ".js-discussion-note-form" $(document).off "ajax:success", ".js-discussion-note-form"
...@@ -62,6 +72,8 @@ class Notes ...@@ -62,6 +72,8 @@ class Notes
$(document).off "click", ".js-choose-note-attachment-button" $(document).off "click", ".js-choose-note-attachment-button"
$(document).off "click", ".js-discussion-reply-button" $(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button" $(document).off "click", ".js-add-diff-note-button"
$(document).off "visibilitychange"
$(document).off "keypress", @notes_forms
initRefresh: -> initRefresh: ->
...@@ -71,14 +83,16 @@ class Notes ...@@ -71,14 +83,16 @@ class Notes
, 15000 , 15000
refresh: -> refresh: ->
@getContent() @getContent() unless document.hidden
getContent: -> getContent: ->
$.ajax $.ajax
url: @notes_url url: @notes_url
data: "last_fetched_at=" + @last_fetched_at
dataType: "json" dataType: "json"
success: (data) => success: (data) =>
notes = data.notes notes = data.notes
@last_fetched_at = data.last_fetched_at
$.each notes, (i, note) => $.each notes, (i, note) =>
@renderNote(note) @renderNote(note)
...@@ -450,4 +464,10 @@ class Notes ...@@ -450,4 +464,10 @@ class Notes
filename = $(this).val().replace(/^.*[\\\/]/, "") filename = $(this).val().replace(/^.*[\\\/]/, "")
form.find(".js-attachment-filename").text filename form.find(".js-attachment-filename").text filename
###
Called when the tab visibility changes
###
visibilityChange: =>
@refresh()
@Notes = Notes @Notes = Notes
@projectUsersSelect = @projectUsersSelect =
init: -> init: ->
$('.ajax-project-users-select').each (i, select) -> $('.ajax-project-users-select').each (i, select) ->
project_id = $('body').data('project-id') project_id = $(select).data('project-id') || $('body').data('project-id')
$(select).select2 $(select).select2
placeholder: $(select).data('placeholder') || "Search for a user" placeholder: $(select).data('placeholder') || "Search for a user"
......
...@@ -12,10 +12,7 @@ ...@@ -12,10 +12,7 @@
*= require nprogress-bootstrap *= require nprogress-bootstrap
*/ */
@import "main/variables.scss"; @import "main/*";
@import "main/mixins.scss";
@import "main/fonts.scss";
@import "main/layout.scss";
/** /**
* Customized Twitter bootstrap * Customized Twitter bootstrap
...@@ -31,65 +28,22 @@ ...@@ -31,65 +28,22 @@
/** /**
* Generic css (forms, nav etc): * Generic css (forms, nav etc):
*/ */
@import "generic/avatar.scss"; @import "generic/*";
@import "generic/common.scss";
@import "generic/typography.scss";
@import "generic/buttons.scss";
@import "generic/blocks.scss";
@import "generic/ui_box.scss";
@import "generic/issue_box.scss";
@import "generic/files.scss";
@import "generic/lists.scss";
@import "generic/flash.scss";
@import "generic/forms.scss";
@import "generic/selects.scss";
@import "generic/highlight.scss";
@import "generic/jquery.scss";
/** /**
* Page specific styles (issues, projects etc): * Page specific styles (issues, projects etc):
*/ */
@import "sections/header.scss"; @import "sections/*";
@import "sections/nav.scss";
@import "sections/commits.scss";
@import "sections/diff.scss";
@import "sections/issues.scss";
@import "sections/projects.scss";
@import "sections/snippets.scss";
@import "sections/votes.scss";
@import "sections/merge_requests.scss";
@import "sections/graph.scss";
@import "sections/events.scss";
@import "sections/themes.scss";
@import "sections/tree.scss";
@import "sections/notes.scss";
@import "sections/profile.scss";
@import "sections/login.scss";
@import "sections/editor.scss";
@import "sections/admin.scss";
@import "sections/wiki.scss";
@import "sections/wall.scss";
@import "sections/dashboard.scss";
@import "sections/stat_graph.scss";
@import "sections/groups.scss";
@import "sections/appearances.scss";
/** /**
* Code highlight * Code highlight
*/ */
@import "highlight/white.scss"; @import "highlight/*";
@import "highlight/dark.scss";
@import "highlight/solarized_dark.scss";
@import "highlight/monokai.scss";
/** /**
* UI themes: * UI themes:
*/ */
@import "themes/ui_basic.scss"; @import "themes/*";
@import "themes/ui_mars.scss";
@import "themes/ui_modern.scss";
@import "themes/ui_gray.scss";
@import "themes/ui_color.scss";
/** /**
* Styles for JS behaviors. * Styles for JS behaviors.
......
...@@ -26,6 +26,10 @@ ...@@ -26,6 +26,10 @@
margin-top: -5px; margin-top: -5px;
} }
.left-options {
margin-top: -3px;
}
.file_name { .file_name {
color: $style_color; color: $style_color;
font-size: 14px; font-size: 14px;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
margin:20px 0; margin:20px 0;
background: #FFF; background: #FFF;
border: 1px solid #EEE; border: 1px solid #EEE;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
&.issue-box-closed { &.issue-box-closed {
border-color: #DA4E49; border-color: #DA4E49;
...@@ -70,7 +71,6 @@ ...@@ -70,7 +71,6 @@
} }
.state { .state {
height: 34px;
border-bottom: 1px solid #DDD; border-bottom: 1px solid #DDD;
line-height: 32px; line-height: 32px;
} }
...@@ -89,6 +89,18 @@ ...@@ -89,6 +89,18 @@
border: none; border: none;
border-top: 1px solid #eee; border-top: 1px solid #eee;
padding: 15px 25px; padding: 15px 25px;
// Reset text align for children
.text-right > * { text-align: left; }
@media (max-width: $screen-xs-max) {
// Don't right align on mobile
.text-right { text-align: left; }
.row .col-md-6 {
padding-top: 5px;
}
}
} }
.description { .description {
...@@ -106,7 +118,11 @@ ...@@ -106,7 +118,11 @@
padding: 1px 25px; padding: 1px 25px;
text-align: center; text-align: center;
text-shadow: none; text-shadow: none;
margin-right: 20px;
display: inline-block; display: inline-block;
line-height: 34px;
}
.creator {
padding: 2px 15px;
} }
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
width: 270px; width: 270px;
.ui-datepicker-header { .ui-datepicker-header {
background: #EEE; background: #FFF;
border-color: #DDD; border-color: #DDD;
} }
...@@ -19,20 +19,37 @@ ...@@ -19,20 +19,37 @@
} }
&.ui-autocomplete { &.ui-autocomplete {
@include border-radius(0px);
border-color: #DDD; border-color: #DDD;
padding: 0; padding: 0;
margin-top: 2px;
z-index: 1001;
.ui-menu-item a { .ui-menu-item a {
color: #777; padding: 4px 10px;
&:hover {
background: $hover;
border-color: $primary_color;
@include border-radius(0px);
color: #333;
}
} }
} }
}
.ui-state-default {
border: 1px solid #FFF;
background: #FFF;
color: #777;
}
.ui-state-highlight {
border: 1px solid #EEE;
background: #EEE;
}
.ui-state-active {
border: 1px solid $bg_style_color;
background: $bg_style_color;
color: #FFF;
}
.ui-state-hover,
.ui-state-focus {
border: 1px solid $hover;
background: $hover;
color: #333;
}
}
...@@ -47,7 +47,7 @@ a { ...@@ -47,7 +47,7 @@ a {
text-decoration: underline; text-decoration: underline;
} }
&.dark { &.darken {
color: $style_color; color: $style_color;
} }
......
...@@ -41,31 +41,6 @@ ...@@ -41,31 +41,6 @@
* Prefilled mixins * Prefilled mixins
* Mixins with fixed values * Mixins with fixed values
*/ */
@mixin bg-light-gray-gradient {
background: #f1f1f1;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));
background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -ms-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
}
@mixin bg-gray-gradient {
background: #eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
}
@mixin bg-dark-gray-gradient {
background: #eee;
background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -ms-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -o-linear-gradient(#e9e9e9, #d7d7d7);
}
@mixin shade { @mixin shade {
@include box-shadow(0 0 3px #ddd); @include box-shadow(0 0 3px #ddd);
......
...@@ -63,26 +63,14 @@ ...@@ -63,26 +63,14 @@
} }
} }
.text-file-parallel div { tr.line_holder.parallel{
display: inline-block; .old_line, .new_line, .diff_line {
padding-bottom: 16px; min-width: 50px;
} }
.diff-side {
overflow-x: scroll; td.line_content.parallel{
width: 508px; width: 50%;
height: 700px; }
}
.diff-side.diff-side-left{
overflow-y:hidden;
}
.diff-side table, td.diff-middle table {
height: 700px;
}
.diff-middle {
width: 114px;
vertical-align: top;
height: 700px;
overflow: hidden
} }
.old_line, .new_line, .diff_line { .old_line, .new_line, .diff_line {
......
.project-network { .project-network {
border: 1px solid #aaa; border: 1px solid #CCC;
padding: 1px;
.tip { .tip {
color: #888; color: #888;
font-size: 14px; font-size: 14px;
padding: 10px; padding: 10px;
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
@include bg-gray-gradient; background: #EEE;
} }
.network-graph { .network-graph {
background: #f1f1f1; background: #FFF;
height: 500px; height: 500px;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
......
...@@ -45,14 +45,6 @@ ...@@ -45,14 +45,6 @@
padding: 6px 10px; padding: 6px 10px;
border: 1px solid #ccc; border: 1px solid #ccc;
@include border-radius(4px); @include border-radius(4px);
input.check_all_issues {
padding: 0;
margin: 0;
position: relative;
top: 3px;
}
} }
.issues_content { .issues_content {
...@@ -143,3 +135,36 @@ form.edit-issue { ...@@ -143,3 +135,36 @@ form.edit-issue {
border-color: #E5E5E5; border-color: #E5E5E5;
} }
} }
@media (max-width: $screen-xs-max) {
.issue-btn-group {
width: 100%;
margin-top: 5px;
.btn-group {
width: 100%;
ul {
width: 100%;
text-align: center;
}
}
.btn {
width: 100%;
margin-top: -1px;
&:first-child:not(:last-child) {
border-radius: 4px 4px 0 0;
}
&:not(:first-child):not(:last-child) {
border-radius: 0;
}
&:last-child:not(:first-child) {
border-radius: 0 0 4px 4px;
}
}
}
}
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
.mr_source_commit, .mr_source_commit,
.mr_target_commit { .mr_target_commit {
margin-top: 10px;
.commit { .commit {
margin: 0; margin: 0;
padding: 2px 0; padding: 2px 0;
......
...@@ -139,6 +139,7 @@ ul.notes { ...@@ -139,6 +139,7 @@ ul.notes {
background-color: #fff; background-color: #fff;
border-width: 1px 0; border-width: 1px 0;
padding-top: 0; padding-top: 0;
vertical-align: top;
li { li {
padding: 5px; padding: 5px;
......
...@@ -40,4 +40,10 @@ ...@@ -40,4 +40,10 @@
.votes-holder { .votes-holder {
float: right; float: right;
width: 250px; width: 250px;
@media (max-width: $screen-xs-max) {
width: 100%;
margin-top: 5px;
margin-bottom: 10px;
}
} }
...@@ -12,6 +12,7 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -12,6 +12,7 @@ class Admin::ProjectsController < Admin::ApplicationController
@projects = @projects.with_push if params[:with_push].present? @projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present? @projects = @projects.abandoned if params[:abandoned].present?
@projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20) @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
end end
......
...@@ -26,6 +26,18 @@ class Projects::EditTreeController < Projects::BaseTreeController ...@@ -26,6 +26,18 @@ class Projects::EditTreeController < Projects::BaseTreeController
end end
end end
def preview
@content = params[:content]
#FIXME workaround https://github.com/gitlabhq/gitlabhq/issues/5936
@content += "\n" if @blob.data.end_with?("\n")
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
include_diff_info: true)
@diff = Gitlab::DiffParser.new(diffy.diff.scan(/.*\n/))
render layout: false
end
private private
def blob def blob
......
...@@ -62,11 +62,27 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -62,11 +62,27 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.source_project = @project unless @merge_request.source_project @merge_request.source_project = @project unless @merge_request.source_project
@merge_request.target_project ||= (@project.forked_from_project || @project) @merge_request.target_project ||= (@project.forked_from_project || @project)
@target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names @target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names
@merge_request.target_branch ||= @merge_request.target_project.default_branch @merge_request.target_branch ||= @merge_request.target_project.default_branch
@source_project = @merge_request.source_project @source_project = @merge_request.source_project
@merge_request
if @merge_request.target_branch && @merge_request.source_branch
compare_action = Gitlab::Satellite::CompareAction.new(
current_user,
@merge_request.target_project,
@merge_request.target_branch,
@merge_request.source_project,
@merge_request.source_branch
)
@commits = compare_action.commits
@commits.map! { |commit| Commit.new(commit) }
@commit = @commits.first
@diffs = compare_action.diffs
@merge_request.title = @merge_request.source_branch.titleize.humanize
@target_project = @merge_request.target_project
@target_repo = @target_project.repository
end
end end
def edit def edit
...@@ -80,7 +96,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -80,7 +96,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request = MergeRequests::CreateService.new(project, current_user, params[:merge_request]).execute @merge_request = MergeRequests::CreateService.new(project, current_user, params[:merge_request]).execute
if @merge_request.valid? if @merge_request.valid?
redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully created.' redirect_to project_merge_request_path(@merge_request.target_project, @merge_request), notice: 'Merge request was successfully created.'
else else
@source_project = @merge_request.source_project @source_project = @merge_request.source_project
@target_project = @merge_request.target_project @target_project = @merge_request.target_project
......
...@@ -5,9 +5,10 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -5,9 +5,10 @@ class Projects::NotesController < Projects::ApplicationController
before_filter :authorize_admin_note!, only: [:update, :destroy] before_filter :authorize_admin_note!, only: [:update, :destroy]
def index def index
current_fetched_at = Time.now.to_i
@notes = NotesFinder.new.execute(project, current_user, params) @notes = NotesFinder.new.execute(project, current_user, params)
notes_json = { notes: [] } notes_json = { notes: [], last_fetched_at: current_fetched_at }
@notes.each do |note| @notes.each do |note|
notes_json[:notes] << { notes_json[:notes] << {
......
...@@ -12,9 +12,22 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -12,9 +12,22 @@ class Projects::WikisController < Projects::ApplicationController
def show def show
@page = @project_wiki.find_page(params[:id], params[:version_id]) @page = @project_wiki.find_page(params[:id], params[:version_id])
gollum_wiki = @project_wiki.wiki
file = gollum_wiki.file(params[:id], gollum_wiki.ref, true)
if @page if @page
render 'show' render 'show'
elsif file
if file.on_disk?
send_file file.on_disk_path, disposition: 'inline'
else
send_data(
file.raw_data,
type: file.mime_type,
disposition: 'inline',
filename: file.name
)
end
else else
return render('empty') unless can?(current_user, :write_wiki, @project) return render('empty') unless can?(current_user, :write_wiki, @project)
@page = WikiPage.new(@project_wiki) @page = WikiPage.new(@project_wiki)
......
class NotesFinder class NotesFinder
FETCH_OVERLAP = 5.seconds
def execute(project, current_user, params) def execute(project, current_user, params)
target_type = params[:target_type] target_type = params[:target_type]
target_id = params[:target_id] target_id = params[:target_id]
# Default to 0 to remain compatible with old clients
last_fetched_at = Time.at(params.fetch(:last_fetched_at, 0).to_i)
case target_type notes = case target_type
when "commit" when "commit"
project.notes.for_commit_id(target_id).not_inline.fresh project.notes.for_commit_id(target_id).not_inline.fresh
when "issue" when "issue"
...@@ -12,6 +16,11 @@ class NotesFinder ...@@ -12,6 +16,11 @@ class NotesFinder
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh
when "snippet" when "snippet"
project.snippets.find(target_id).notes.fresh project.snippets.find(target_id).notes.fresh
else
raise 'invalid target_type'
end end
# Use overlapping intervals to avoid worrying about race conditions
notes.where('updated_at > ?', last_fetched_at - FETCH_OVERLAP)
end end
end end
...@@ -75,7 +75,7 @@ module ApplicationHelper ...@@ -75,7 +75,7 @@ module ApplicationHelper
else else
gravatar_url = request.ssl? || gitlab_config.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url gravatar_url = request.ssl? || gitlab_config.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
user_email.strip! user_email.strip!
sprintf gravatar_url, hash: Digest::MD5.hexdigest(user_email.downcase), size: size sprintf gravatar_url, hash: Digest::MD5.hexdigest(user_email.downcase), size: size, email: user_email
end end
end end
......
...@@ -16,9 +16,10 @@ module CommitsHelper ...@@ -16,9 +16,10 @@ module CommitsHelper
end end
def each_diff_line(diff, index) def each_diff_line(diff, index)
Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old| Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path)
yield(full_line, type, line_code, line_new, line_old) .each do |full_line, type, line_code, line_new, line_old|
end yield(full_line, type, line_code, line_new, line_old)
end
end end
def each_diff_line_near(diff, index, expected_line_code) def each_diff_line_near(diff, index, expected_line_code)
...@@ -116,7 +117,7 @@ module CommitsHelper ...@@ -116,7 +117,7 @@ module CommitsHelper
added_lines[line_new] = { line_code: line_code, type: type, line: line } added_lines[line_new] = { line_code: line_code, type: type, line: line }
end end
end end
max_length = old_file ? old_file.sloc + added_lines.length : file.sloc max_length = old_file ? [old_file.loc, file.loc].max : file.loc
offset1 = 0 offset1 = 0
offset2 = 0 offset2 = 0
......
...@@ -82,7 +82,7 @@ module IssuesHelper ...@@ -82,7 +82,7 @@ module IssuesHelper
end end
def milestone_options object def milestone_options object
options_from_collection_for_select(@project.milestones.active, 'id', 'title', object.milestone_id) options_from_collection_for_select(object.project.milestones.active, 'id', 'title', object.milestone_id)
end end
def issue_box_class(item) def issue_box_class(item)
......
...@@ -163,7 +163,7 @@ module ProjectsHelper ...@@ -163,7 +163,7 @@ module ProjectsHelper
end end
def repository_size(project = nil) def repository_size(project = nil)
"#{(project || @project).repository.size} MB" "#{(project || @project).repository_size} MB"
rescue rescue
# In order to prevent 500 error # In order to prevent 500 error
# when application cannot allocate memory # when application cannot allocate memory
......
...@@ -14,7 +14,7 @@ module SelectsHelper ...@@ -14,7 +14,7 @@ module SelectsHelper
css_class << (opts[:class] || '') css_class << (opts[:class] || '')
value = opts[:selected] || '' value = opts[:selected] || ''
placeholder = opts[:placeholder] || 'Select user' placeholder = opts[:placeholder] || 'Select user'
project_id = opts[:project_id] || @project.id
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder) hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id)
end end
end end
...@@ -91,4 +91,12 @@ module TreeHelper ...@@ -91,4 +91,12 @@ module TreeHelper
def leave_edit_message def leave_edit_message
"Leave edit mode?\nAll unsaved changes will be lost." "Leave edit mode?\nAll unsaved changes will be lost."
end end
def editing_preview_title(filename)
if gitlab_markdown?(filename) || markup?(filename)
'Preview'
else
'Diff'
end
end
end end
...@@ -6,7 +6,7 @@ module Emails ...@@ -6,7 +6,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(@merge_request.author_id), mail(from: sender(@merge_request.author_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id) def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id)
...@@ -16,7 +16,7 @@ module Emails ...@@ -16,7 +16,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(updated_by_user_id), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
...@@ -26,7 +26,7 @@ module Emails ...@@ -26,7 +26,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(updated_by_user_id), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
...@@ -35,7 +35,7 @@ module Emails ...@@ -35,7 +35,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(updated_by_user_id), mail(from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
end end
......
...@@ -27,7 +27,7 @@ module Emails ...@@ -27,7 +27,7 @@ module Emails
@target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}") @target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}")
mail(from: sender(@note.author_id), mail(from: sender(@note.author_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end end
def note_wall_email(recipient_id, note_id) def note_wall_email(recipient_id, note_id)
......
...@@ -253,6 +253,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -253,6 +253,14 @@ class MergeRequest < ActiveRecord::Base
end end
end end
def target_project_namespace
if target_project && target_project.namespace
target_project.namespace.path
else
"(removed)"
end
end
def source_branch_exists? def source_branch_exists?
return false unless self.source_project return false unless self.source_project
......
...@@ -86,7 +86,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -86,7 +86,7 @@ class MergeRequestDiff < ActiveRecord::Base
# between target and source branches # between target and source branches
def unmerged_commits def unmerged_commits
commits = if merge_request.for_fork? commits = if merge_request.for_fork?
Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between compare_action.commits
else else
repository.commits_between(target_branch, source_branch) repository.commits_between(target_branch, source_branch)
end end
...@@ -150,7 +150,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -150,7 +150,7 @@ class MergeRequestDiff < ActiveRecord::Base
# between target and source branches # between target and source branches
def unmerged_diffs def unmerged_diffs
diffs = if merge_request.for_fork? diffs = if merge_request.for_fork?
Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite compare_action.diffs
else else
Gitlab::Git::Diff.between(repository, source_branch, target_branch) Gitlab::Git::Diff.between(repository, source_branch, target_branch)
end end
...@@ -165,4 +165,16 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -165,4 +165,16 @@ class MergeRequestDiff < ActiveRecord::Base
def repository def repository
merge_request.target_project.repository merge_request.target_project.repository
end end
private
def compare_action
Gitlab::Satellite::CompareAction.new(
merge_request.author,
merge_request.target_project,
merge_request.target_branch,
merge_request.source_project,
merge_request.source_branch
)
end
end end
...@@ -25,6 +25,7 @@ class Milestone < ActiveRecord::Base ...@@ -25,6 +25,7 @@ class Milestone < ActiveRecord::Base
scope :active, -> { with_state(:active) } scope :active, -> { with_state(:active) }
scope :closed, -> { with_state(:closed) } scope :closed, -> { with_state(:closed) }
scope :of_projects, ->(ids) { where(project_id: ids) }
validates :title, presence: true validates :title, presence: true
validates :project, presence: true validates :project, presence: true
......
...@@ -184,9 +184,10 @@ class Note < ActiveRecord::Base ...@@ -184,9 +184,10 @@ class Note < ActiveRecord::Base
return @diff_line if @diff_line return @diff_line if @diff_line
if diff if diff
Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old| Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path)
@diff_line = full_line if line_code == self.line_code .each do |full_line, type, line_code, line_new, line_old|
end @diff_line = full_line if line_code == self.line_code
end
end end
@diff_line @diff_line
......
...@@ -207,6 +207,7 @@ class Project < ActiveRecord::Base ...@@ -207,6 +207,7 @@ class Project < ActiveRecord::Base
when 'oldest' then reorder('projects.created_at ASC') when 'oldest' then reorder('projects.created_at ASC')
when 'recently_updated' then reorder('projects.updated_at DESC') when 'recently_updated' then reorder('projects.updated_at DESC')
when 'last_updated' then reorder('projects.updated_at ASC') when 'last_updated' then reorder('projects.updated_at ASC')
when 'largest_repository' then reorder('projects.repository_size DESC')
else reorder("namespaces.path, projects.name ASC") else reorder("namespaces.path, projects.name ASC")
end end
end end
...@@ -566,4 +567,8 @@ class Project < ActiveRecord::Base ...@@ -566,4 +567,8 @@ class Project < ActiveRecord::Base
def forked_from?(project) def forked_from?(project)
forked? && project == forked_from_project forked? && project == forked_from_project
end end
def update_repository_size
update_attribute(:repository_size, repository.size)
end
end end
...@@ -64,7 +64,8 @@ class ProjectWiki ...@@ -64,7 +64,8 @@ class ProjectWiki
# #
# Returns an initialized WikiPage instance or nil # Returns an initialized WikiPage instance or nil
def find_page(title, version = nil) def find_page(title, version = nil)
if page = wiki.page(title, version) page_title, page_dir = page_title_and_dir(title)
if page = wiki.page(page_title, version, page_dir)
WikiPage.new(self, page, true) WikiPage.new(self, page, true)
else else
nil nil
...@@ -90,6 +91,12 @@ class ProjectWiki ...@@ -90,6 +91,12 @@ class ProjectWiki
wiki.delete_page(page, commit_details(:deleted, message, page.title)) wiki.delete_page(page, commit_details(:deleted, message, page.title))
end end
def page_title_and_dir(title)
title_array = title.split("/")
title = title_array.pop
[title.gsub(/\.[^.]*$/, ""), title_array.join("/")]
end
private private
def create_repo! def create_repo!
......
...@@ -175,14 +175,24 @@ class WikiPage ...@@ -175,14 +175,24 @@ class WikiPage
end end
def save(method, *args) def save(method, *args)
if valid? && wiki.send(method, *args) project_wiki = wiki
@page = wiki.wiki.paged(title) if valid? && project_wiki.send(method, *args)
page_details = if method == :update_page
@page.path
else
title
end
page_title, page_dir = project_wiki.page_title_and_dir(page_details)
gollum_wiki = project_wiki.wiki
@page = gollum_wiki.paged(page_title, page_dir)
set_attributes set_attributes
@persisted = true @persisted = true
else else
errors.add(:base, wiki.error_message) if wiki.error_message errors.add(:base, project_wiki.error_message) if project_wiki.error_message
@persisted = false @persisted = false
end end
@persisted @persisted
......
...@@ -25,6 +25,7 @@ class GitPushService ...@@ -25,6 +25,7 @@ class GitPushService
project.ensure_satellite_exists project.ensure_satellite_exists
project.repository.expire_cache project.repository.expire_cache
project.update_repository_size
if push_to_existing_branch?(ref, oldrev) if push_to_existing_branch?(ref, oldrev)
project.update_merge_requests(oldrev, newrev, ref, @user) project.update_merge_requests(oldrev, newrev, ref, @user)
......
...@@ -22,12 +22,12 @@ ...@@ -22,12 +22,12 @@
= f.label :color, "Background Color", class: 'control-label' = f.label :color, "Background Color", class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_field :color, placeholder: "#AA33EE", class: "form-control" = f.text_field :color, placeholder: "#AA33EE", class: "form-control"
.light Hex values as 3 double digit numbers, starting with a # sign. .light 6 character hex values starting with a # sign.
.form-group.js-toggle-colors-container.hide .form-group.js-toggle-colors-container.hide
= f.label :font, "Font Color", class: 'control-label' = f.label :font, "Font Color", class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_field :font, placeholder: "#224466", class: "form-control" = f.text_field :font, placeholder: "#224466", class: "form-control"
.light Hex values as 3 double digit numbers, starting with a # sign. .light 6 character hex values starting with a # sign.
.form-group .form-group
= f.label :starts_at, class: 'control-label' = f.label :starts_at, class: 'control-label'
.col-sm-10.datetime-controls .col-sm-10.datetime-controls
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
= visibility_level_icon(level) = visibility_level_icon(level)
= label = label
.form-actions .form-actions
= hidden_field_tag :sort, params[:sort]
= submit_tag "Search", class: "btn submit btn-primary" = submit_tag "Search", class: "btn submit btn-primary"
= link_to "Reset", admin_projects_path, class: "btn" = link_to "Reset", admin_projects_path, class: "btn"
...@@ -40,6 +41,28 @@ ...@@ -40,6 +41,28 @@
.title .title
Projects (#{@projects.total_count}) Projects (#{@projects.total_count})
.pull-right .pull-right
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
= @sort.humanize
- else
Name
%b.caret
%ul.dropdown-menu
%li
= link_to admin_projects_path(sort: nil) do
Name
= link_to admin_projects_path(sort: 'newest') do
Newest
= link_to admin_projects_path(sort: 'oldest') do
Oldest
= link_to admin_projects_path(sort: 'recently_updated') do
Recently updated
= link_to admin_projects_path(sort: 'last_updated') do
Last updated
= link_to admin_projects_path(sort: 'largest_repository') do
Largest repository
= link_to 'New Project', new_project_path, class: "btn btn-new" = link_to 'New Project', new_project_path, class: "btn btn-new"
%ul.well-list %ul.well-list
- @projects.each do |project| - @projects.each do |project|
......
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
%li.visible-sm.visible-xs %li.visible-sm.visible-xs
= link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do = link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do
%i.icon-search %i.icon-search
%li
= link_to help_path, title: 'Help', class: 'has_bottom_tooltip',
'data-original-title' => 'Help' do
%i.icon-question-sign
%li %li
= link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do = link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
%i.icon-globe %i.icon-globe
...@@ -39,6 +43,6 @@ ...@@ -39,6 +43,6 @@
%li %li
= link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do
%i.icon-signout %i.icon-signout
%li %li.hidden-xs
= link_to current_user, class: "profile-pic", id: 'profile-pic' do = link_to current_user, class: "profile-pic", id: 'profile-pic' do
= image_tag avatar_icon(current_user.email, 26), alt: 'User activity' = image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
%p %p
= "Merge Request !#{@merge_request.iid} was closed by #{@updated_by.name}" = "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
= "Merge Request #{@merge_request.iid} was closed by #{@updated_by.name}" = "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
%p %p
= "Merge Request !#{@merge_request.iid} was merged" = "Merge Request ##{@merge_request.iid} was merged"
= "Merge Request #{@merge_request.iid} was merged" = "Merge Request ##{@merge_request.iid} was merged"
Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
......
...@@ -17,7 +17,7 @@ Changes: ...@@ -17,7 +17,7 @@ Changes:
- else - else
= diff.new_path || diff.old_path = diff.new_path || diff.old_path
\===================================== \=====================================
= diff.diff != diff.diff
\ \
- if @compare.timeout - if @compare.timeout
Huge diff. To prevent performance issues it was hidden Huge diff. To prevent performance issues it was hidden
......
...@@ -2,54 +2,37 @@ ...@@ -2,54 +2,37 @@
- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file) - old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
- num_lines = old_lines.length - num_lines = old_lines.length
%div.text-file-parallel %div.text-file
%div.diff-side.diff-side-left %table
%table - num_lines.times do |index|
- old_lines.each do |line| - new_line = new_lines[index]
- old_line = old_lines[index]
%tr.line_holder.parallel
-# For old line
- if old_line.type == :file_created
%td.old_line= old_line.num
%td.line_content.parallel= "File was created"
- elsif old_line.type == :deleted
%td.old_line.old= old_line.num
%td.line_content{class: "parallel noteable_line old #{old_line.code}", "line_code" => old_line.code}= old_line.content
- else old_line.type == :no_change
%td.old_line= old_line.num
%td.line_content.parallel= old_line.content
-# For new line
- if new_line.type == :file_deleted
%td.new_line= new_line.num
%td.line_content.parallel= "File was deleted"
- elsif new_line.type == :added
%td.new_line.new= new_line.num
%td.line_content{class: "parallel noteable_line new #{new_line.code}", "line_code" => new_line.code}= new_line.content
- else new_line.type == :no_change
%td.new_line= new_line.num
%td.line_content.parallel= new_line.content
- if @reply_allowed
- comments1 = @line_notes.select { |n| n.line_code == old_line.code }.sort_by(&:created_at)
- comments2 = @line_notes.select { |n| n.line_code == new_line.code }.sort_by(&:created_at)
- unless comments1.empty? and comments2.empty?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2
%tr.line_holder.parallel
- if line.type == :file_created
%td.line_content.parallel= "File was created"
- elsif line.type == :deleted
%td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
%div.diff-middle
%table
- num_lines.times do |index|
%tr
- if old_lines[index].type == :deleted
%td.old_line.old= old_lines[index].num
- else
%td.old_line= old_lines[index].num
%td.diff_line=""
- if new_lines[index].type == :added
%td.new_line.new= new_lines[index].num
- else
%td.new_line= new_lines[index].num
%div.diff-side.diff-side-right
%table
- new_lines.each do |line|
%tr.line_holder.parallel
- if line.type == :file_deleted
%td.line_content.parallel= "File was deleted"
- elsif line.type == :added
%td.line_content{class: "parallel noteable_line new #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
:javascript
$('.diff-side-right').on('scroll', function(){
$('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop());
$('.diff-side-left').scrollLeft($(this).scrollLeft());
});
$('.diff-side-left').on('scroll', function(){
$('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant
$('.diff-side-right').scrollLeft($(this).scrollLeft());
});
%table.text-file
- each_diff_line(diff, 1) do |line, type, line_code, line_new, line_old, raw_line|
%tr.line_holder{ id: line_code, class: "#{type}" }
- if type == "match"
%td.old_line= "..."
%td.new_line= "..."
%td.line_content.matched= line
- else
%td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
%td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
%td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line)
.diff-file
.diff-content
- if gitlab_markdown?(@blob.name)
.file-content.wiki
= preserve do
= markdown(@content)
- elsif markup?(@blob.name)
.file-content.wiki
= raw GitHub::Markup.render(@blob.name, @content)
- else
.file-content.code
- unless @diff.empty?
%table.text-file
- @diff.each do |line, type, line_code, line_new, line_old, raw_line|
%tr.line_holder{ id: line_code, class: "#{type}" }
- if type == "match"
%td.old_line= "..."
%td.new_line= "..."
%td.line_content.matched= line
- else
%td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
%td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
%td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line)
- else
%p.nothing_here_message No changes.
%h3.page-title Edit mode %h3.page-title Edit mode
.file-editor .file-editor
= form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do = form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do
.file-holder .file-holder.file
.file-title .file-title
.btn-group.js-edit-mode.left-options
= link_to 'Edit', '#editor', class: 'active hover btn btn-tiny'
= link_to editing_preview_title(@blob.name), '#preview', class: 'btn btn-tiny', 'data-preview-url' => preview_project_edit_tree_path(@project, @id)
%i.icon-file %i.icon-file
%span.file_name %span.file_name
= @path = @path
...@@ -13,7 +16,8 @@ ...@@ -13,7 +16,8 @@
.btn-group.tree-btn-group .btn-group.tree-btn-group
= link_to "Cancel", @after_edit_path, class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message } = link_to "Cancel", @after_edit_path, class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message }
.file-content.code .file-content.code
%pre#editor= @blob.data %pre.js-edit-mode-pane#editor= @blob.data
.js-edit-mode-pane#preview.hide
.form-group.commit_message-group .form-group.commit_message-group
= label_tag 'commit_message', class: "control-label" do = label_tag 'commit_message', class: "control-label" do
...@@ -45,3 +49,28 @@ ...@@ -45,3 +49,28 @@
$("#file-content").val(editor.getValue()); $("#file-content").val(editor.getValue());
$(".file-editor form").submit(); $(".file-editor form").submit();
}); });
var editModePanes = $('.js-edit-mode-pane'),
editModeLinks = $('.js-edit-mode a');
editModeLinks.click(function(event) {
event.preventDefault();
var currentLink = $(this),
paneId = currentLink.attr('href'),
currentPane = editModePanes.filter(paneId);
editModeLinks.removeClass('active hover');
currentLink.addClass('active hover');
editModePanes.hide();
if (paneId == '#preview') {
$.post(currentLink.data('preview-url'), { content: editor.getValue() }, function(response) {
currentPane.empty().append(response);
currentPane.fadeIn(200);
})
} else {
currentPane.fadeIn(200);
editor.focus()
}
})
= form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f| = form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
%strong.append-right-10 .row
Assignee: .col-md-6
%strong.append-right-10
Assignee:
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
= project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @issue.assignee_id) = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @issue.assignee_id)
- elsif issue.assignee - elsif issue.assignee
= link_to_member(@project, @issue.assignee) = link_to_member(@project, @issue.assignee)
- else - else
None None
.pull-right .col-md-6.text-right
%strong.append-right-10 %strong.append-right-10
Milestone: Milestone:
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
= f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone (none):" }, {class: 'select2 select2-compact'}) = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'})
= hidden_field_tag :issue_context = hidden_field_tag :issue_context
= f.submit class: 'btn' = f.submit class: 'btn'
- elsif issue.milestone - elsif issue.milestone
= link_to issue.milestone.title, project_milestone_path = link_to issue.milestone.title, project_milestone_path
- else - else
None None
%h3.page-title %h3.page-title
Issue ##{@issue.iid} Issue ##{@issue.iid}
%span.pull-right %span.pull-right.issue-btn-group
- if can?(current_user, :write_issue, @project) - if can?(current_user, :write_issue, @project)
= link_to new_project_issue_path(@project), class: "btn btn-grouped", title: "New Issue", id: "new_issue_link" do = link_to new_project_issue_path(@project), class: "btn btn-grouped", title: "New Issue", id: "new_issue_link" do
%i.icon-plus %i.icon-plus
...@@ -16,28 +16,29 @@ ...@@ -16,28 +16,29 @@
%i.icon-edit %i.icon-edit
Edit Edit
.votes-holder .clearfix
#votes= render 'votes/votes_block', votable: @issue .votes-holder
#votes= render 'votes/votes_block', votable: @issue
.back-link .back-link
= link_to project_issues_path(@project) do = link_to project_issues_path(@project) do
&larr; To issues list &larr; To issues list
%span.milestone-nav-link %span.milestone-nav-link
- if @issue.milestone - if @issue.milestone
| |
%span.light Milestone %span.light Milestone
= link_to project_milestone_path(@project, @issue.milestone) do = link_to project_milestone_path(@project, @issue.milestone) do
= @issue.milestone.title = @issue.milestone.title
.issue-box{ class: issue_box_class(@issue) } .issue-box{ class: issue_box_class(@issue) }
.state .state.clearfix
%span.state-label .state-label.col-sm-2.col-xs-12
- if @issue.closed? - if @issue.closed?
Closed Closed
- else - else
Open Open
%span.creator %span.creator.col-sm-9.col-xs-12
Created by #{link_to_member(@project, @issue.author)} #{time_ago_with_tooltip(@issue.created_at)} Created by #{link_to_member(@project, @issue.author)} #{time_ago_with_tooltip(@issue.created_at)}
%h4.title %h4.title
......
...@@ -14,33 +14,6 @@ ...@@ -14,33 +14,6 @@
- @merge_request.errors.full_messages.each do |msg| - @merge_request.errors.full_messages.each do |msg|
%div= msg %div= msg
.merge-request-branches
.form-group
= label_tag nil, class: 'control-label' do
From
.col-sm-10
.clearfix
.pull-left
= f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted? })
.pull-left
&nbsp;
= f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2'})
.mr_source_commit
%br
.form-group
= label_tag nil, class: 'control-label' do
To
.col-sm-10
.clearfix
.pull-left
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? })
.pull-left
&nbsp;
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'})
.mr_target_commit
%hr
.merge-request-form-info .merge-request-form-info
.form-group .form-group
= f.label :title, class: 'control-label' do = f.label :title, class: 'control-label' do
...@@ -51,6 +24,23 @@ ...@@ -51,6 +24,23 @@
.col-sm-10 .col-sm-10
= f.text_area :description, class: "form-control js-gfm-input", rows: 14 = f.text_area :description, class: "form-control js-gfm-input", rows: 14
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
%hr
.form-group
.issue-assignee
= f.label :assignee_id, class: 'control-label' do
%i.icon-user
Assign to
.col-sm-10
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id, class: 'control-label' do
%i.icon-time
Milestone
.col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
.form-actions .form-actions
- if @merge_request.new_record? - if @merge_request.new_record?
...@@ -66,20 +56,7 @@ ...@@ -66,20 +56,7 @@
:javascript :javascript
disableButtonIfEmptyField("#merge_request_title", ".btn-save"); disableButtonIfEmptyField("#merge_request_title", ".btn-save");
$('.assign-to-me-link').on('click', function(e){
var source_branch = $("#merge_request_source_branch") $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
, target_branch = $("#merge_request_target_branch") e.preventDefault();
, target_project = $("#merge_request_target_project_id");
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
target_project.on("change", function() {
$.get("#{update_branches_project_merge_requests_path(@source_project)}", {target_project_id: $(this).val() });
});
source_branch.on("change", function() {
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: $(this).val() });
});
target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
}); });
...@@ -11,13 +11,9 @@ ...@@ -11,13 +11,9 @@
- if merge_request.for_fork? - if merge_request.for_fork?
%span.light %span.light
#{merge_request.source_project_namespace}: #{merge_request.source_project_namespace}:
= merge_request.source_branch = truncate merge_request.source_branch, length: 25
%i.icon-angle-right.light %i.icon-angle-right.light
= merge_request.target_branch = merge_request.target_branch
- else
= merge_request.source_branch
%i.icon-angle-right.light
= merge_request.target_branch
.merge-request-info .merge-request-info
- if merge_request.author - if merge_request.author
authored by #{link_to_member(merge_request.source_project, merge_request.author)} authored by #{link_to_member(merge_request.source_project, merge_request.author)}
......
%h3.page-title Compare branches for new Merge Request
%hr
= form_for [@project, @merge_request], url: new_project_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline" } do |f|
.hide.alert.alert-danger.mr-compare-errors
.merge-request-branches.row
.col-md-6
.panel.panel-default
.panel-heading
%strong Source branch
.panel-body
= f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted? })
&nbsp;
= f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2'})
.panel-footer
.mr_source_commit
.col-md-6
.panel.panel-default
.panel-heading
%strong Target branch
.panel-body
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted? })
&nbsp;
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'})
.panel-footer
.mr_target_commit
-if @merge_request.errors.any?
.alert.alert-danger
- @merge_request.errors.full_messages.each do |msg|
%div= msg
- if @merge_request.source_branch.present? && @merge_request.target_branch.present?
.light-well
%center
%h4
There isn't anything to merge.
%p.slead
- if @merge_request.source_branch == @merge_request.target_branch
You'll need to use different branch names to get a valid comparison.
- else
%span.label-branch #{@merge_request.source_branch}
and
%span.label-branch #{@merge_request.target_branch}
are the same.
%hr
= f.submit 'Compare branches', class: "btn btn-primary mr-compare-btn"
:javascript
var source_branch = $("#merge_request_source_branch")
, target_branch = $("#merge_request_target_branch")
, target_project = $("#merge_request_target_project_id");
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
target_project.on("change", function() {
$.get("#{update_branches_project_merge_requests_path(@source_project)}", {target_project_id: $(this).val() });
});
source_branch.on("change", function() {
$.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
:coffeescript
$(".merge-request-form").on 'submit', ->
if $("#merge_request_source_branch").val() is "" or $('#merge_request_target_branch').val() is ""
$(".mr-compare-errors").html("You must select source and target branch to proceed")
$(".mr-compare-errors").fadeIn()
event.preventDefault()
return
%h3.page-title
New merge request
%p.slead
From
%strong.monospace
#{@merge_request.source_project_namespace}:#{@merge_request.source_branch}
into
%strong.monospace
#{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
%span.pull-right
= link_to 'Change branches', new_project_merge_request_path(@project)
= form_for [@project, @merge_request], html: { class: "merge-request-form" } do |f|
.panel.panel-default
.panel-body
.form-group
.light
= f.label :title do
= "Title *"
= f.text_field :title, class: "form-control input-lg js-gfm-input", maxlength: 255, rows: 5, required: true
.form-group
.light
= f.label :description, "Description"
= f.text_area :description, class: "form-control js-gfm-input", rows: 10
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.form-group
.issue-assignee
= f.label :assignee_id do
%i.icon-user
Assign to
%div
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group
.issue-milestone
= f.label :milestone_id do
%i.icon-time
Milestone
%div= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
.panel-footer
- if @target_repo.contribution_guide
- contribution_guide_url = project_blob_path(@target_project, tree_join(@target_repo.root_ref, @target_repo.contribution_guide.name))
%p
Please review the
%strong #{link_to "guidelines for contribution", contribution_guide_url}
to this repository.
= f.hidden_field :source_project_id
= f.hidden_field :target_project_id
= f.hidden_field :target_branch
= f.hidden_field :source_branch
= f.submit 'Submit merge request', class: "btn btn-create"
.mr-compare
%div.ui-box
.title
Commits (#{@commits.count})
- if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
%ul.well-list
- Commit.decorate(@commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE)).each do |commit|
= render "projects/commits/inline_commit", commit: commit, project: @project
%li.warning-row.unstyled
other #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} commits hidden to prevent performance issues.
- else
%ul.well-list= render Commit.decorate(@commits), project: @project
%h4 Changes
- if @diffs.present?
= render "projects/commits/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.bs-callout.bs-callout-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
%p To preserve performance the line changes are not shown.
:javascript
$('.assign-to-me-link').on('click', function(e){
$('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
:plain :plain
$(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}"); $(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}");
var mrTitle = $('#merge_request_title');
if(mrTitle.val().length == 0) {
mrTitle.val("#{params[:ref].titleize.humanize}");
}
%h3.page-title New Merge Request - if @commits.present?
%hr = render 'new_submit'
= render 'form' - else
= render 'new_compare'
= form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f| = form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
%strong.append-right-10 .row
Assignee: .col-md-6
%strong.append-right-10
Assignee:
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @merge_request.assignee_id) = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @merge_request.assignee_id)
- elsif merge_request.assignee - elsif merge_request.assignee
= link_to_member(@project, @merge_request.assignee) = link_to_member(@project, @merge_request.assignee)
- else - else
None None
.pull-right .col-md-6.text-right
%strong.append-right-10 %strong.append-right-10
Milestone: Milestone:
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone (none):" }, {class: 'select2 select2-compact'}) = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'})
= hidden_field_tag :merge_request_context = hidden_field_tag :merge_request_context
= f.submit class: 'btn' = f.submit class: 'btn'
- elsif merge_request.milestone - elsif merge_request.milestone
= link_to merge_request.milestone.title, project_milestone_path = link_to merge_request.milestone.title, project_milestone_path
- else - else
None None
.issue-box{ class: issue_box_class(@merge_request) } .issue-box{ class: issue_box_class(@merge_request) }
.state .state.clearfix
%span.state-label %span.state-label.col-sm-2.col-xs-12
- if @merge_request.merged? - if @merge_request.merged?
Merged Merged
- elsif @merge_request.closed? - elsif @merge_request.closed?
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- else - else
Open Open
%span.creator %span.creator.col-sm-9.col-xs-12
Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)} Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)}
%h4.title %h4.title
......
...@@ -26,4 +26,4 @@ ...@@ -26,4 +26,4 @@
.ci_widget.ci-error{style: "display:none"} .ci_widget.ci-error{style: "display:none"}
%i.icon-remove %i.icon-remove
%strong Cannot connect to CI server. Please check your setting %strong Cannot connect to the CI server. Please check your settings and try again.
%h3.page-title %h3.page-title
= "Merge Request ##{@merge_request.iid}" = "Merge Request ##{@merge_request.iid}"
%span.pull-right %span.pull-right.issue-btn-group
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open? - if @merge_request.open?
.btn-group.pull-left .btn-group.pull-left
...@@ -39,4 +39,4 @@ ...@@ -39,4 +39,4 @@
- else - else
%span= @merge_request.source_branch %span= @merge_request.source_branch
&rarr; &rarr;
%spanh= @merge_request.target_branch %span= @merge_request.target_branch
...@@ -21,14 +21,6 @@ ...@@ -21,14 +21,6 @@
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)} #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
= render "projects/merge_requests/show/remove_source_branch" = render "projects/merge_requests/show/remove_source_branch"
- if !@closes_issues.empty? && @merge_request.open?
.alert.alert-info.alert-info
%span
%i.icon-ok
Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
= succeed '.' do
!= gfm(@closes_issues.map { |i| "##{i.iid}" }.to_sentence)
- unless @commits.any? - unless @commits.any?
%h4 Nothing to merge %h4 Nothing to merge
%p %p
...@@ -38,3 +30,12 @@ ...@@ -38,3 +30,12 @@
%span.label-branch #{@merge_request.target_branch} %span.label-branch #{@merge_request.target_branch}
%br %br
Try to use different branches or push new code. Try to use different branches or push new code.
- if !@closes_issues.empty? && @merge_request.open?
.panel-footer
%span
%i.icon-ok
Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
= succeed '.' do
!= gfm(@closes_issues.map { |i| "##{i.iid}" }.to_sentence)
...@@ -100,7 +100,7 @@ ...@@ -100,7 +100,7 @@
%ul.bordered-list %ul.bordered-list
- @users.each do |user| - @users.each do |user|
%li %li
= link_to user, title: user.name, class: "dark" do = link_to user, title: user.name, class: "darken" do
= image_tag avatar_icon(user.email, 32), class: "avatar s32" = image_tag avatar_icon(user.email, 32), class: "avatar s32"
%strong= truncate(user.name, lenght: 40) %strong= truncate(user.name, lenght: 40)
%br %br
......
- note1 = notes1.first # example note - note1 = notes1.first # example note
- note2 = notes2.first # example note - note2 = notes2.first # example note
-# Check if line want not changed since comment was left
/- if !defined?(line) || line == note.diff_line
%tr.notes_holder.js-toggle-content %tr.notes_holder.js-toggle-content
-# Check if line want not changed since comment was left
/- if !defined?(line1) || line1 == note1.diff_line
- if note1 - if note1
%td.notes_line
%span.btn.disabled
%i.icon-comment
= notes1.count
%td.notes_content %td.notes_content
%ul.notes{ rel: note1.discussion_id } %ul.notes{ rel: note1.discussion_id }
= render notes1 = render notes1
= render "projects/notes/discussion_reply_button", note: note1 = render "projects/notes/discussion_reply_button", note: note1
%td.notes_line2
%span.btn.disabled.parallel-comment
%i.icon-comment
= notes1.count
- else - else
%td= "" %td= ""
%td= "" %td= ""
%td= ""
-# Check if line want not changed since comment was left
/- if !defined?(line2) || line2 == note2.diff_line
- if note2 - if note2
%td.notes_line %td.notes_line
%span.btn.disabled.parallel-comment %span.btn.disabled
%i.icon-comment %i.icon-comment
= notes2.count = notes2.count
%td.notes_content %td.notes_content
%ul.notes{ rel: note2.discussion_id } %ul.notes{ rel: note2.discussion_id }
= render notes2 = render notes2
= render "projects/notes/discussion_reply_button", note: note2 = render "projects/notes/discussion_reply_button", note: note2
- else - else
%td= "" %td= ""
%td= "" %td= ""
...@@ -7,4 +7,4 @@ ...@@ -7,4 +7,4 @@
= render "projects/notes/form" = render "projects/notes/form"
:javascript :javascript
new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}) new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i})
...@@ -9,6 +9,6 @@ ...@@ -9,6 +9,6 @@
%span Page slug %span Page slug
= text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project) = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project)
%p.hint %p.hint
Please don't use spaces and slashes Please don't use spaces.
.modal-footer .modal-footer
= link_to 'Build', '#', class: 'build-new-wiki btn btn-create' = link_to 'Build', '#', class: 'build-new-wiki btn btn-create'
#!/bin/sh
set -e
for file in config/*.yml.example; do
cp ${file} config/$(basename ${file} .example)
done
# Allow to override the Gitlab URL from an environment variable, as this will avoid having to change the configuration file for simple deployments.
config=$(echo '<% gitlab_url = URI(ENV["GITLAB_URL"] || "http://localhost:80") %>' | cat - config/gitlab.yml)
echo "$config" > config/gitlab.yml
sed -i "s/host: localhost/host: <%= gitlab_url.host %>/" config/gitlab.yml
sed -i "s/port: 80/port: <%= gitlab_url.port %>/" config/gitlab.yml
sed -i "s/https: false/https: <%= gitlab_url.scheme == 'https' %>/" config/gitlab.yml
# No need for config file. Will be taken care of by REDIS_URL env variable
rm config/resque.yml
# Set default unicorn.rb file
echo "" > config/unicorn.rb
# Required for assets precompilation
sudo service postgresql start
...@@ -53,7 +53,7 @@ Gitlab::Application.configure do ...@@ -53,7 +53,7 @@ Gitlab::Application.configure do
else else
"redis://localhost:6379" "redis://localhost:6379"
end end
config.cache_store = :redis_store, resque_url config.cache_store = :redis_store, resque_url, {namespace: 'cache:gitlab'}
# Enable serving of images, stylesheets, and JavaScripts from an asset server # Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com" # config.action_controller.asset_host = "http://assets.example.com"
......
...@@ -19,6 +19,11 @@ production: &base ...@@ -19,6 +19,11 @@ production: &base
port: 80 port: 80
https: false https: false
# Uncommment this line below if your ssh host is different from HTTP/HTTPS one
# (you'd obviously need to replace ssh.host_example.com with your own host).
# Otherwise, ssh host will be set to the `host:` value above
# ssh_host: ssh.host_example.com
# Uncomment and customize the last line to run in a non-root path # Uncomment and customize the last line to run in a non-root path
# WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this. # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
# Note that four settings need to be changed for this to work. # Note that four settings need to be changed for this to work.
...@@ -102,7 +107,7 @@ production: &base ...@@ -102,7 +107,7 @@ production: &base
# ## :id - Issue id (from commit messages) # ## :id - Issue id (from commit messages)
# issues_url: "http://redmine.sample/issues/:id" # issues_url: "http://redmine.sample/issues/:id"
# #
# ## If not nil, linkis to creating new issues will be replaced with this # ## If not nil, links to creating new issues will be replaced with this
# ## Use placeholders: # ## Use placeholders:
# ## :project_id - GitLab project identifier # ## :project_id - GitLab project identifier
# ## :issues_tracker_id - Project Name or Id in external issue tracker # ## :issues_tracker_id - Project Name or Id in external issue tracker
...@@ -117,6 +122,7 @@ production: &base ...@@ -117,6 +122,7 @@ production: &base
## Gravatar ## Gravatar
gravatar: gravatar:
enabled: true # Use user avatar image from Gravatar.com (default: true) enabled: true # Use user avatar image from Gravatar.com (default: true)
# gravatar urls: possible placeholders: %{hash} %{size} %{email}
# plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
# ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
......
...@@ -73,6 +73,7 @@ Settings.gitlab['default_projects_limit'] ||= 10 ...@@ -73,6 +73,7 @@ Settings.gitlab['default_projects_limit'] ||= 10
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil? Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
Settings.gitlab['default_theme'] = Gitlab::Theme::MARS if Settings.gitlab['default_theme'].nil? Settings.gitlab['default_theme'] = Gitlab::Theme::MARS if Settings.gitlab['default_theme'].nil?
Settings.gitlab['host'] ||= 'localhost' Settings.gitlab['host'] ||= 'localhost'
Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80 Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80
Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || '' Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
...@@ -117,7 +118,7 @@ Settings.gitlab_shell['hooks_path'] ||= Settings.gitlab['user_home'] + '/gitla ...@@ -117,7 +118,7 @@ Settings.gitlab_shell['hooks_path'] ||= Settings.gitlab['user_home'] + '/gitla
Settings.gitlab_shell['receive_pack'] = true if Settings.gitlab_shell['receive_pack'].nil? Settings.gitlab_shell['receive_pack'] = true if Settings.gitlab_shell['receive_pack'].nil?
Settings.gitlab_shell['upload_pack'] = true if Settings.gitlab_shell['upload_pack'].nil? Settings.gitlab_shell['upload_pack'] = true if Settings.gitlab_shell['upload_pack'].nil?
Settings.gitlab_shell['repos_path'] ||= Settings.gitlab['user_home'] + '/repositories/' Settings.gitlab_shell['repos_path'] ||= Settings.gitlab['user_home'] + '/repositories/'
Settings.gitlab_shell['ssh_host'] ||= (Settings.gitlab.host || 'localhost') Settings.gitlab_shell['ssh_host'] ||= Settings.gitlab.ssh_host
Settings.gitlab_shell['ssh_port'] ||= 22 Settings.gitlab_shell['ssh_port'] ||= 22
Settings.gitlab_shell['ssh_user'] ||= Settings.gitlab.user Settings.gitlab_shell['ssh_user'] ||= Settings.gitlab.user
Settings.gitlab_shell['owner_group'] ||= Settings.gitlab.user Settings.gitlab_shell['owner_group'] ||= Settings.gitlab.user
......
...@@ -18,4 +18,16 @@ if File.exists?(aws_file) ...@@ -18,4 +18,16 @@ if File.exists?(aws_file)
config.fog_authenticated_url_expiration = 1 << 29 # optional time (in seconds) that authenticated urls will be valid. config.fog_authenticated_url_expiration = 1 << 29 # optional time (in seconds) that authenticated urls will be valid.
# when fog_public is false and provider is AWS or Google, defaults to 600 # when fog_public is false and provider is AWS or Google, defaults to 600
end end
# Mocking Fog requests, based on: https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Test-Fog-based-uploaders
if Rails.env.test?
Fog.mock!
connection = ::Fog::Storage.new(
:aws_access_key_id => AWS_CONFIG['access_key_id'],
:aws_secret_access_key => AWS_CONFIG['secret_access_key'],
:provider => 'AWS',
:region => AWS_CONFIG['region']
)
connection.directories.create(:key => AWS_CONFIG['bucket'])
end
end end
...@@ -223,6 +223,7 @@ Devise.setup do |config| ...@@ -223,6 +223,7 @@ Devise.setup do |config|
method: Gitlab.config.ldap['method'], method: Gitlab.config.ldap['method'],
bind_dn: Gitlab.config.ldap['bind_dn'], bind_dn: Gitlab.config.ldap['bind_dn'],
password: Gitlab.config.ldap['password'], password: Gitlab.config.ldap['password'],
filter: Gitlab.config.ldap['user_filter'],
name_proc: email_stripping_proc name_proc: email_stripping_proc
end end
...@@ -244,4 +245,4 @@ Devise.setup do |config| ...@@ -244,4 +245,4 @@ Devise.setup do |config|
config.omniauth provider['name'].to_sym, *provider_arguments config.omniauth provider['name'].to_sym, *provider_arguments
end end
end end
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Gitlab::Application.config.session_store( Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks. :redis_store, # Using the cookie_store would enable session replay attacks.
servers: Gitlab::Application.config.cache_store.last, # re-use the Redis config from the Rails cache store servers: Gitlab::Application.config.cache_store[1], # re-use the Redis config from the Rails cache store
key: '_gitlab_session', key: '_gitlab_session',
secure: Gitlab.config.gitlab.https, secure: Gitlab.config.gitlab.https,
httponly: true, httponly: true,
......
...@@ -201,7 +201,9 @@ Gitlab::Application.routes.draw do ...@@ -201,7 +201,9 @@ Gitlab::Application.routes.draw do
resources :blob, only: [:show, :destroy], constraints: {id: /.+/} resources :blob, only: [:show, :destroy], constraints: {id: /.+/}
resources :raw, only: [:show], constraints: {id: /.+/} resources :raw, only: [:show], constraints: {id: /.+/}
resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit' resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do
post :preview, on: :member
end
resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new' resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new'
resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
...@@ -218,7 +220,7 @@ Gitlab::Application.routes.draw do ...@@ -218,7 +220,7 @@ Gitlab::Application.routes.draw do
end end
end end
resources :wikis, only: [:show, :edit, :destroy, :create], constraints: {id: /[a-zA-Z.0-9_\-]+/} do resources :wikis, only: [:show, :edit, :destroy, :create], constraints: {id: /[a-zA-Z.0-9_\-\/]+/} do
collection do collection do
get :pages get :pages
put ':id' => 'wikis#update' put ':id' => 'wikis#update'
......
...@@ -40,7 +40,8 @@ Gitlab::Seeder.quiet do ...@@ -40,7 +40,8 @@ Gitlab::Seeder.quiet do
import_url: url, import_url: url,
namespace_id: group.id, namespace_id: group.id,
name: project_path.titleize, name: project_path.titleize,
description: Faker::Lorem.sentence description: Faker::Lorem.sentence,
visibility_level: Gitlab::VisibilityLevel.values.sample
} }
project = Projects::CreateService.new(User.first, params).execute project = Projects::CreateService.new(User.first, params).execute
......
Gitlab::Seeder.quiet do Gitlab::Seeder.quiet do
(1..100).each do |i| Project.all.reject(&:empty_repo?).each do |project|
# Random Project branches = project.repository.branch_names
project = Project.all.sample
branches.each do |branch_name|
# Random user break if branches.size < 2
user = project.team.users.sample source_branch = branches.pop
target_branch = branches.pop
next unless user
# Random user
next if project.empty_repo? user = project.team.users.sample
next unless user
branches = project.repository.branch_names.sample(2)
params = {
next if branches.uniq.size < 2 source_branch: source_branch,
target_branch: target_branch,
user_id = user.id title: Faker::Lorem.sentence(6),
description: Faker::Lorem.sentences(3).join(" ")
Gitlab::Seeder.by_user(user) do }
MergeRequest.seed(:id, [{
id: i, merge_request = MergeRequests::CreateService.new(project, user, params).execute
source_branch: branches.first,
target_branch: branches.last, if merge_request.valid?
source_project_id: project.id, merge_request.assignee = user
target_project_id: project.id, merge_request.milestone = project.milestones.sample
author_id: user_id, merge_request.save
assignee_id: user_id, print '.'
milestone: project.milestones.sample, else
title: Faker::Lorem.sentence(6) print 'F'
}]) end
end end
print('.')
end end
end end
MergeRequest.all.map do |mr|
mr.set_iid
mr.save
end
puts 'Load diffs for Merge Requests (it will take some time)...'
MergeRequest.all.each do |mr|
mr.reload_code
print '.'
end
require_relative 'limits_to_mysql'
class AddIndexOnIid < ActiveRecord::Migration
def change
RemoveDuplicateIid.clean(Issue)
RemoveDuplicateIid.clean(MergeRequest, 'target_project_id')
RemoveDuplicateIid.clean(Milestone)
add_index :issues, [:project_id, :iid], unique: true
add_index :merge_requests, [:target_project_id, :iid], unique: true
add_index :milestones, [:project_id, :iid], unique: true
end
end
class RemoveDuplicateIid
def self.clean(klass, project_field = 'project_id')
duplicates = klass.find_by_sql("SELECT iid, #{project_field} FROM #{klass.table_name} GROUP BY #{project_field}, iid HAVING COUNT(*) > 1")
duplicates.each do |duplicate|
project_id = duplicate.send(project_field)
iid = duplicate.iid
items = klass.of_projects(project_id).where(iid: iid)
if items.size > 1
puts "Remove #{klass.name} duplicates for iid: #{iid} and project_id: #{project_id}"
items.shift
items.each do |item|
item.destroy
puts '.'
end
end
end
end
end
class IndexOnCurrentSignInAt < ActiveRecord::Migration
def change
add_index :users, :current_sign_in_at
end
end
class AddNotesIndexUpdatedAt < ActiveRecord::Migration
def change
add_index :notes, :updated_at
end
end
class AddRepoSizeToDb < ActiveRecord::Migration
def change
add_column :projects, :repository_size, :float, default: 0
end
end
class MigrateRepoSize < ActiveRecord::Migration
def up
Project.reset_column_information
Project.find_each(batch_size: 500) do |project|
begin
if project.empty_repo?
print '-'
else
project.update_repository_size
print '.'
end
rescue
print 'F'
end
end
puts 'Done'
end
def down
end
end
class LimitsToMysql < ActiveRecord::Migration
def up
return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647
change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
change_column :snippets, :content, :text, limit: 2147483647
change_column :notes, :st_diff, :text, limit: 2147483647
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20140414131055) do ActiveRecord::Schema.define(version: 20140502125220) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -112,6 +112,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do ...@@ -112,6 +112,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
add_index "issues", ["author_id"], name: "index_issues_on_author_id", using: :btree add_index "issues", ["author_id"], name: "index_issues_on_author_id", using: :btree
add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree
add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree
add_index "issues", ["project_id"], name: "index_issues_on_project_id", using: :btree add_index "issues", ["project_id"], name: "index_issues_on_project_id", using: :btree
add_index "issues", ["title"], name: "index_issues_on_title", using: :btree add_index "issues", ["title"], name: "index_issues_on_title", using: :btree
...@@ -162,6 +163,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do ...@@ -162,6 +163,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_source_project_id", using: :btree add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_source_project_id", using: :btree
add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree
add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
create_table "milestones", force: true do |t| create_table "milestones", force: true do |t|
...@@ -176,6 +178,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do ...@@ -176,6 +178,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
end end
add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree
add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree
add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree
create_table "namespaces", force: true do |t| create_table "namespaces", force: true do |t|
...@@ -218,6 +221,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do ...@@ -218,6 +221,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree
create_table "project_group_links", force: true do |t| create_table "project_group_links", force: true do |t|
t.integer "project_id", null: false t.integer "project_id", null: false
...@@ -247,6 +251,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do ...@@ -247,6 +251,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do
t.integer "visibility_level", default: 0, null: false t.integer "visibility_level", default: 0, null: false
t.boolean "archived", default: false, null: false t.boolean "archived", default: false, null: false
t.string "import_status" t.string "import_status"
t.float "repository_size", default: 0.0
end end
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
...@@ -348,7 +353,6 @@ ActiveRecord::Schema.define(version: 20140414131055) do ...@@ -348,7 +353,6 @@ ActiveRecord::Schema.define(version: 20140414131055) do
t.integer "notification_level", default: 1, null: false t.integer "notification_level", default: 1, null: false
t.datetime "password_expires_at" t.datetime "password_expires_at"
t.integer "created_by_id" t.integer "created_by_id"
t.datetime "last_credential_check_at"
t.string "avatar" t.string "avatar"
t.string "confirmation_token" t.string "confirmation_token"
t.datetime "confirmed_at" t.datetime "confirmed_at"
...@@ -356,11 +360,13 @@ ActiveRecord::Schema.define(version: 20140414131055) do ...@@ -356,11 +360,13 @@ ActiveRecord::Schema.define(version: 20140414131055) do
t.string "unconfirmed_email" t.string "unconfirmed_email"
t.boolean "hide_no_ssh_key", default: false t.boolean "hide_no_ssh_key", default: false
t.string "website_url", default: "", null: false t.string "website_url", default: "", null: false
t.datetime "last_credential_check_at"
end end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
add_index "users", ["name"], name: "index_users_on_name", using: :btree add_index "users", ["name"], name: "index_users_on_name", using: :btree
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
+ [Merge Requests](merge_requests.md) + [Merge Requests](merge_requests.md)
+ [Issues](issues.md) + [Issues](issues.md)
+ [Milestones](milestones.md) + [Milestones](milestones.md)
+ [Notes](notes.md) + [Notes](notes.md) (comments)
+ [Deploy Keys](deploy_keys.md) + [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md) + [System Hooks](system_hooks.md)
+ [Groups](groups.md) + [Groups](groups.md)
...@@ -21,9 +21,11 @@ ...@@ -21,9 +21,11 @@
## Clients ## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP + [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Laravel API Wrapper for GitLab CE](https://github.com/adamgoose/gitlab) - PHP / [Laravel](http://laravel.com)
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby + [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python + [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java + [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
+ [node-gitlab](https://github.com/moul/node-gitlab) - Node.js
## Introduction ## Introduction
......
...@@ -193,3 +193,7 @@ Parameters: ...@@ -193,3 +193,7 @@ Parameters:
+ `id` (required) - The project ID + `id` (required) - The project ID
+ `issue_id` (required) - The ID of the issue + `issue_id` (required) - The ID of the issue
## Comments on issues
Comments are done via the notes resource.
...@@ -105,10 +105,11 @@ POST /projects/:id/merge_requests ...@@ -105,10 +105,11 @@ POST /projects/:id/merge_requests
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `source_branch` (required) - The source branch + `source_branch` (required) - The source branch
+ `target_branch` (required) - The target branch + `target_branch` (required) - The target branch
+ `assignee_id` (optional) - Assignee user ID + `assignee_id` (optional) - Assignee user ID
+ `title` (required) - Title of MR + `title` (required) - Title of MR
+ `target_project_id` (optional) - The target project (numeric id)
```json ```json
{ {
...@@ -257,3 +258,7 @@ Parameters: ...@@ -257,3 +258,7 @@ Parameters:
} }
] ]
``` ```
## Comments on issues
Comments are done via the notes resource.
Notes can be wall notes or comments on snippets, issues or merge requests.
## Wall ## Wall
### List project wall notes ### List project wall notes
......
...@@ -220,6 +220,18 @@ Parameters: ...@@ -220,6 +220,18 @@ Parameters:
+ **none** + **none**
## List SSH keys for user
Get a list of a specified user's SSH keys. Available only for admin
```
GET /users/:uid/keys
```
Parameters:
+ `uid` (required) - id of specified user
## Single SSH key ## Single SSH key
...@@ -286,3 +298,18 @@ Parameters: ...@@ -286,3 +298,18 @@ Parameters:
+ `id` (required) - SSH key ID + `id` (required) - SSH key ID
## Delete SSH key
Deletes key owned by a specified user. Available only for admin.
```
DELETE /users/:uid/keys/:id
```
Parameters:
+ `uid` (required) - id of specified user
+ `id` (required) - SSH key ID
Will return `200 Ok` on success, or `404 Not found` if either user or key cannot be found.
+ [Architecture](architecture.md) ## Development
+ [Shell commands](shell_commands.md)
+ [Architecture](architecture.md) of GitLab
+ [Shell commands](shell_commands.md) in the GitLab codebase
+ [Rake tasks](rake_tasks.md) for development
# Rake tasks for developers
## Setup db with developer seeds:
Note that if your db user does not have advanced privilegies you must create db manually before run this command
```
bundle exec rake setup
```
## Run tests
This runs all test suite present in GitLab
```
bundle exec rake test
```
## Generate searchable docs for source code
You can find results under `doc/code` directory
```
bundle exec rake gitlab:generate_docs
```
# Select Version to Install # Select Version to Install
Make sure you view this installation guide from the branch (version) of GitLab you would like to install. In most cases Make sure you view [this installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md) from the branch (version) of GitLab you would like to install. In most cases
this should be the highest numbered stable branch (example shown below). this should be the highest numbered stable branch (example shown below).
![capture](http://i.imgur.com/d2AlIVj.png) ![capture](http://i.imgur.com/d2AlIVj.png)
If this is unclear check the [GitLab Blog](https://www.gitlab.com/blog/) for installation guide links by version. If the highest number stable branch is unclear please check the [GitLab Blog](https://www.gitlab.com/blog/) for installation guide links by version.
# Important notes # Important notes
...@@ -27,10 +27,9 @@ The GitLab installation consists of setting up the following components: ...@@ -27,10 +27,9 @@ The GitLab installation consists of setting up the following components:
1. Packages / Dependencies 1. Packages / Dependencies
2. Ruby 2. Ruby
3. System Users 3. System Users
4. GitLab shell 4. Database
5. Database 5. GitLab
6. GitLab 6. Nginx
7. Nginx
# 1. Packages / Dependencies # 1. Packages / Dependencies
...@@ -119,32 +118,10 @@ Create a `git` user for Gitlab: ...@@ -119,32 +118,10 @@ Create a `git` user for Gitlab:
sudo adduser --disabled-login --gecos 'GitLab' git sudo adduser --disabled-login --gecos 'GitLab' git
# 4. Database
# 4. GitLab shell
GitLab Shell is an ssh access and repository management software developed specially for GitLab.
# Go to home directory
cd /home/git
# Clone gitlab shell
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.9.3
cd gitlab-shell
sudo -u git -H cp config.yml.example config.yml
# Edit config and replace gitlab_url
# with something like 'http://domain.com/'
sudo -u git -H editor config.yml
# Do setup
sudo -u git -H ./bin/install
# 5. Database
We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](database_mysql.md). We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](database_mysql.md).
NOTE: because we need to make use of extensions you need at least pgsql 9.1.
# Install the database packages # Install the database packages
sudo apt-get install -y postgresql-9.1 postgresql-client libpq-dev sudo apt-get install -y postgresql-9.1 postgresql-client libpq-dev
...@@ -153,7 +130,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -153,7 +130,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
sudo -u postgres psql -d template1 sudo -u postgres psql -d template1
# Create a user for GitLab. # Create a user for GitLab.
template1=# CREATE USER git; template1=# CREATE USER git CREATEDB;
# Create the GitLab production database & grant all privileges on database # Create the GitLab production database & grant all privileges on database
template1=# CREATE DATABASE gitlabhq_production OWNER git; template1=# CREATE DATABASE gitlabhq_production OWNER git;
...@@ -165,7 +142,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -165,7 +142,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
sudo -u git -H psql -d gitlabhq_production sudo -u git -H psql -d gitlabhq_production
# 6. GitLab # 5. GitLab
# We'll install GitLab into home directory of the user "git" # We'll install GitLab into home directory of the user "git"
cd /home/git cd /home/git
...@@ -276,6 +253,18 @@ that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2. ...@@ -276,6 +253,18 @@ that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2.
# When done you see 'Administrator account created:' # When done you see 'Administrator account created:'
## Install GitLab shell
GitLab Shell is an ssh access and repository management software developed specially for GitLab.
# Go to the Gitlab installation folder:
cd /home/git/gitlab
# Run the installation task for gitlab-shell (replace `REDIS_URL` if needed):
sudo -u git -H bundle exec rake gitlab:shell:install[v1.9.3] REDIS_URL=redis://localhost:6379 RAILS_ENV=production
# By default, the gitlab-shell config is generated from your main gitlab config. You can review (and modify) it as follows:
sudo -u git -H editor /home/git/gitlab-shell/config.yml
## Install Init Script ## Install Init Script
...@@ -313,7 +302,8 @@ Check if GitLab and its environment are configured correctly: ...@@ -313,7 +302,8 @@ Check if GitLab and its environment are configured correctly:
# or # or
sudo /etc/init.d/gitlab restart sudo /etc/init.d/gitlab restart
# 7. Nginx
# 6. Nginx
**Note:** **Note:**
Nginx is the officially supported web server for GitLab. If you cannot or do not want to use Nginx as your web server, have a look at the Nginx is the officially supported web server for GitLab. If you cannot or do not want to use Nginx as your web server, have a look at the
......
...@@ -74,6 +74,9 @@ Apart from a local hard drive you can also mount a volume that supports the netw ...@@ -74,6 +74,9 @@ Apart from a local hard drive you can also mount a volume that supports the netw
If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab. If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab.
## Database
If you want to run the database separately, the **recommended** database size is **1 MB per user**
# Supported webbrowsers # Supported webbrowsers
......
...@@ -6,3 +6,4 @@ See the documentation below for details on how to configure these services. ...@@ -6,3 +6,4 @@ See the documentation below for details on how to configure these services.
+ [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc. + [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc.
+ [LDAP](ldap.md) Set up sign in via LDAP + [LDAP](ldap.md) Set up sign in via LDAP
+ [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, and Google via OAuth. + [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, and Google via OAuth.
+ [Slack](slack.md) Integrate with the Slack chat service
...@@ -21,6 +21,7 @@ Before configuring individual OmniAuth providers there are a few global settings ...@@ -21,6 +21,7 @@ Before configuring individual OmniAuth providers there are a few global settings
``` ```
2. Find the section dealing with OmniAuth. The section will look similar to the following.<br /> 2. Find the section dealing with OmniAuth. The section will look similar to the following.<br />
``` ```
## OmniAuth settings ## OmniAuth settings
omniauth: omniauth:
...@@ -50,6 +51,7 @@ Before configuring individual OmniAuth providers there are a few global settings ...@@ -50,6 +51,7 @@ Before configuring individual OmniAuth providers there are a few global settings
# app_secret: 'YOUR APP SECRET', # app_secret: 'YOUR APP SECRET',
# args: { scope: 'user:email' } } # args: { scope: 'user:email' } }
``` ```
3. Change `enabled` to `true`. 3. Change `enabled` to `true`.
4. Consider the next two configuration options: `allow_single_sign_on` and `block_auto_created_users`. 4. Consider the next two configuration options: `allow_single_sign_on` and `block_auto_created_users`.
* `allow_single_sign_on` defaults to `false`. If `false` users must be created manually or they will not be able to * `allow_single_sign_on` defaults to `false`. If `false` users must be created manually or they will not be able to
......
...@@ -27,7 +27,7 @@ If a user is a GitLab administrator they receive all permissions. ...@@ -27,7 +27,7 @@ If a user is a GitLab administrator they receive all permissions.
|Remove protected branches| |||✓|✓| |Remove protected branches| |||✓|✓|
|Edit project| |||✓|✓| |Edit project| |||✓|✓|
|Add Deploy Keys to project| |||✓|✓| |Add Deploy Keys to project| |||✓|✓|
|Confiure Project Hooks| |||✓|✓| |Configure Project Hooks| |||✓|✓|
|Switch visibility level| ||||✓| |Switch visibility level| ||||✓|
|Transfer project to another namespace| ||||✓| |Transfer project to another namespace| ||||✓|
|Remove project| ||||✓| |Remove project| ||||✓|
......
...@@ -4,7 +4,7 @@ Internal projects will only be available to authenticated users. ...@@ -4,7 +4,7 @@ Internal projects will only be available to authenticated users.
#### Public projects #### Public projects
Public projects can be cloned **without any** authentication. Public projects can be cloned **without any** authentication.
It will also be listen on the [public access directory](/public). It will also be listed on the [public access directory](/public).
**Any logged in user** will have [Guest](/help/permissions) permissions on the repository. **Any logged in user** will have [Guest](/help/permissions) permissions on the repository.
#### Internal projects #### Internal projects
......
+ [Backup restore](backup_restore.md) + [Backup restore](backup_restore.md)
+ [Cleanup](cleanup.md) + [Cleanup](cleanup.md)
+ [Features](features.md) + [Features](features.md)
+ [Maintenance](maintenance.md) + [Maintenance](maintenance.md) and self-checks
+ [User management](user_management.md) + [User management](user_management.md)
+ [Web hooks](web_hooks.md) + [Web hooks](web_hooks.md)
+ [Import](import.md) of git repositories in bulk
### Import bare repositories into GitLab project instance
Notes:
* project owner will be a first admin
* groups will be created as needed
* group owner will be the first admin
* existing projects will be skipped
How to use:
1. copy your bare repos under git repos_path (see `config/gitlab.yml` gitlab_shell -> repos_path)
2. run the command below
```
bundle exec rake gitlab:import:repos RAILS_ENV=production
```
Example output:
```
Processing abcd.git
* Created abcd (abcd.git)
Processing group/xyz.git
* Created Group group (2)
* Created xyz (group/xyz.git)
[...]
```
...@@ -110,32 +110,3 @@ If necessary, remove the `tmp/repo_satellites` directory and rerun the command b ...@@ -110,32 +110,3 @@ If necessary, remove the `tmp/repo_satellites` directory and rerun the command b
``` ```
bundle exec rake gitlab:satellites:create RAILS_ENV=production bundle exec rake gitlab:satellites:create RAILS_ENV=production
``` ```
### Import bare repositories into GitLab project instance
Notes:
* project owner will be a first admin
* groups will be created as needed
* group owner will be the first admin
* existing projects will be skipped
How to use:
1. copy your bare repos under git repos_path (see `config/gitlab.yml` gitlab_shell -> repos_path)
2. run the command below
```
bundle exec rake gitlab:import:repos RAILS_ENV=production
```
Example output:
```
Processing abcd.git
* Created abcd (abcd.git)
Processing group/xyz.git
* Created Group group (2)
* Created xyz (group/xyz.git)
[...]
```
...@@ -84,8 +84,9 @@ After making the release branch new commits are cherry-picked from master. When ...@@ -84,8 +84,9 @@ After making the release branch new commits are cherry-picked from master. When
- Push VERSION + Tag to master, merge into x-x-stable - Push VERSION + Tag to master, merge into x-x-stable
- Publish blog for new release - Publish blog for new release
- Tweet to blog (see below) - Tweet to blog (see below)
* 22th: release GitLab EE
* 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems) * 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems)
* 24-end of month: release GitLab EE and GitLab CI * 25th: release GitLab CI
# Write a blog post # Write a blog post
......
# From 6.0 to 6.7 # From 6.0 to 6.8
# In 6.1 we remove a lot of deprecated code. # In 6.1 we remove a lot of deprecated code.
# You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run. # You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run.
...@@ -33,7 +33,7 @@ sudo -u git -H git fetch --all ...@@ -33,7 +33,7 @@ sudo -u git -H git fetch --all
For Gitlab Community Edition: For Gitlab Community Edition:
```bash ```bash
sudo -u git -H git checkout 6-7-stable sudo -u git -H git checkout 6-8-stable
``` ```
OR OR
...@@ -41,7 +41,7 @@ OR ...@@ -41,7 +41,7 @@ OR
For GitLab Enterprise Edition: For GitLab Enterprise Edition:
```bash ```bash
sudo -u git -H git checkout 6-7-stable-ee sudo -u git -H git checkout 6-8-stable-ee
``` ```
...@@ -57,7 +57,7 @@ sudo apt-get install logrotate ...@@ -57,7 +57,7 @@ sudo apt-get install logrotate
```bash ```bash
cd /home/git/gitlab-shell cd /home/git/gitlab-shell
sudo -u git -H git fetch sudo -u git -H git fetch
sudo -u git -H git checkout v1.9.1 # Addresses multiple critical security vulnerabilities sudo -u git -H git checkout v1.9.3 # Addresses multiple critical security vulnerabilities
``` ```
### 5. Install libs, migrations, etc. ### 5. Install libs, migrations, etc.
...@@ -90,11 +90,12 @@ sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites ...@@ -90,11 +90,12 @@ sudo chmod u+rwx,g+rx,o-rwx /home/git/gitlab-satellites
TIP: to see what changed in gitlab.yml.example in this release use next command: TIP: to see what changed in gitlab.yml.example in this release use next command:
``` ```
git diff 6-0-stable:config/gitlab.yml.example 6-7-stable:config/gitlab.yml.example git diff 6-0-stable:config/gitlab.yml.example 6-8-stable:config/gitlab.yml.example
``` ```
* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-7-stable/config/gitlab.yml.example but with your settings. * Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-8-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-7-stable/config/unicorn.rb.example but with your settings. * Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-8-stable/config/unicorn.rb.example but with your settings.
* Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-8-stable/lib/support/nginx/gitlab but with your settings.
* Copy rack attack middleware config * Copy rack attack middleware config
```bash ```bash
......
...@@ -62,6 +62,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS ...@@ -62,6 +62,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS
# Update init.d script # Update init.d script
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab
# Update the logrotate configuration (keep logs for 90 days instead of 52 weeks) # Update the logrotate configuration (keep logs for 90 days instead of 52 weeks)
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
...@@ -92,19 +93,12 @@ If you are using HTTPS, disable gzip as in [this commit](https://gitlab.com/gitl ...@@ -92,19 +93,12 @@ If you are using HTTPS, disable gzip as in [this commit](https://gitlab.com/gitl
To improve performance, enable gzip asset compression as seen [in this commit](https://gitlab.com/gitlab-org/gitlab-ce/commit/8af94ed75505f0253823b9b2d44320fecea5b5fb). To improve performance, enable gzip asset compression as seen [in this commit](https://gitlab.com/gitlab-org/gitlab-ce/commit/8af94ed75505f0253823b9b2d44320fecea5b5fb).
### 6. Update Init script ### 6. Start application
```bash
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab
```
### 7. Start application
sudo service gitlab start sudo service gitlab start
sudo service nginx restart sudo service nginx restart
### 8. Check application status ### 7. Check application status
Check if GitLab and its environment are configured correctly: Check if GitLab and its environment are configured correctly:
......
# Use the shell commands below to convert a MySQL GitLab database to a PostgreSQL one. # Migrating GitLab from MySQL to Postgres
If you are replacing MySQL with Postgres while keeping GitLab on the same
server all you need to do is to export from MySQL and import into Postgres as
described below. If you are also moving GitLab to another server, or if you are
switching to omnibus-gitlab, you may want to use a GitLab backup file. The
second part of this documents explains the procedure to do this.
## Export from MySQL and import into Postgres
Use this if you are keeping GitLab on the same server.
``` ```
sudo service gitlab stop
# Update /home/git/gitlab/config/database.yml
git clone https://github.com/lanyrd/mysql-postgresql-converter.git git clone https://github.com/lanyrd/mysql-postgresql-converter.git
cd mysql-postgresql-converter cd mysql-postgresql-converter
mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production
python db_converter.py databasename.mysql databasename.psql python db_converter.py databasename.mysql databasename.psql
psql -f databasename.psql -d gitlabhq_production psql -f databasename.psql -d gitlabhq_production
sudo service gitlab start
```
## Converting a GitLab backup file from MySQL to Postgres
GitLab backup files (<timestamp>_gitlab_backup.tar) contain a SQL dump. Using
the lanyrd database converter we can replace a MySQL database dump inside the
tar file with a Postgres database dump. This can be useful if you are moving to
another server.
```
# Stop GitLab
sudo service gitlab stop
# Create the backup
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
# Note the filename of the backup that was created. We will call it
# TIMESTAMP_gitlab_backup.tar below.
# Move the backup file we will convert to its own directory
sudo -u git -H mkdir -p tmp/backups/postgresql
sudo -u git -H mv tmp/backups/TIMESTAMP_gitlab_backup.tar tmp/backups/postgresql/
# Create a separate database dump with PostgreSQL compatibility
cd tmp/backups/postgresql
sudo -u git -H mysqldump --compatible=postgresql --default-character-set=utf8 -r gitlabhq_production.mysql -u root gitlabhq_production
# Clone the database converter
sudo -u git -H git clone https://github.com/lanyrd/mysql-postgresql-converter.git
# Convert gitlabhq_production.mysql
sudo -u git -H mkdir db
sudo -u git -H python mysql-postgresql-converter/db_converter.py gitlabhq_production.mysql db/database.sql
# Replace the MySQL dump in TIMESTAMP_gitlab_backup.tar.
# Warning: if you forget to replace TIMESTAMP below, tar will create a new file
# 'TIMESTAMP_gitlab_backup.tar' without giving an error.
sudo -u git -H tar rf TIMESTAMP_gitlab_backup.tar db/database.sql
# Done! TIMESTAMP_gitlab_backup.tar can now be restored into a Postgres GitLab installation.
``` ```
...@@ -46,4 +46,8 @@ If all items are green, then congratulations upgrade is complete! ...@@ -46,4 +46,8 @@ If all items are green, then congratulations upgrade is complete!
You've read through the entire guide, and probably did all the steps manually. Here is a one liner for convenience, the next time you upgrade: You've read through the entire guide, and probably did all the steps manually. Here is a one liner for convenience, the next time you upgrade:
cd /home/git/gitlab; sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; sudo service gitlab stop; sudo -u git -H ruby script/upgrade.rb -y; sudo service gitlab start; sudo service nginx restart; sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production ```bash
cd /home/git/gitlab; sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; \
sudo service gitlab stop; sudo -u git -H ruby script/upgrade.rb -y; sudo service gitlab start; \
sudo service nginx restart; sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
```
...@@ -2,16 +2,16 @@ Project web hooks allow you to trigger an URL if new code is pushed or a new iss ...@@ -2,16 +2,16 @@ Project web hooks allow you to trigger an URL if new code is pushed or a new iss
--- ---
You can configure web hook to listen for specific events like pushes, issues, merge requests. You can configure web hooks to listen for specific events like pushes, issues or merge requests.
GitLab will send POST request with data to web hook URL. GitLab will send a POST request with data to the web hook URL.
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. 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.
If you send a web hook to an SSL endpoint [the certificate will not be verified](https://gitlab.com/gitlab-org/gitlab-ce/blob/ccd617e58ea71c42b6b073e692447d0fe3c00be6/app/models/web_hook.rb#L35) since many people use self-signed certificates. If you send a web hook to an SSL endpoint [the certificate will not be verified](https://gitlab.com/gitlab-org/gitlab-ce/blob/ccd617e58ea71c42b6b073e692447d0fe3c00be6/app/models/web_hook.rb#L35) since many people use self-signed certificates.
--- ---
#### Push events #### Push events
Triggered when you push to the repository except pushing tags. Triggered when you push to the repository except when pushing tags.
**Request body:** **Request body:**
...@@ -84,7 +84,7 @@ Triggered when a new issue is created or an existing issue was updated/closed/re ...@@ -84,7 +84,7 @@ Triggered when a new issue is created or an existing issue was updated/closed/re
#### Merge request events #### Merge request events
Triggered when a new merge request is created or an existing merge request was updated/merges/closed. Triggered when a new merge request is created or an existing merge request was updated/merged/closed.
**Request body:** **Request body:**
......
...@@ -30,11 +30,10 @@ Feature: Project Forked Merge Requests ...@@ -30,11 +30,10 @@ Feature: Project Forked Merge Requests
Given I visit project "Forked Shop" merge requests page Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request" And I click link "New Merge Request"
And I fill out an invalid "Merge Request On Forked Project" merge request And I fill out an invalid "Merge Request On Forked Project" merge request
And I submit the merge request
Then I should see validation errors Then I should see validation errors
@javascript @javascript
Scenario: Merge request should target fork repository by default Scenario: Merge request should target fork repository by default
Given I visit project "Forked Shop" merge requests page Given I visit project "Forked Shop" merge requests page
And I click link "New Merge Request" And I click link "New Merge Request"
Then the target repository should be the original repository Then the target repository should be the original repository
\ No newline at end of file
...@@ -29,3 +29,13 @@ Feature: Project Browse files ...@@ -29,3 +29,13 @@ Feature: Project Browse files
Given I click on "Gemfile.lock" file in repo Given I click on "Gemfile.lock" file in repo
And I click button "edit" And I click button "edit"
Then I can edit code Then I can edit code
@javascript
Scenario: I can see editing preview
Given I click on "Gemfile.lock" file in repo
And I click button "edit"
And I edit code
And I click link "Diff"
Then I see diff
...@@ -45,3 +45,20 @@ Feature: Project Wiki ...@@ -45,3 +45,20 @@ Feature: Project Wiki
And I browse to that Wiki page And I browse to that Wiki page
And I click on the "Pages" button And I click on the "Pages" button
Then I should see the existing page in the pages list Then I should see the existing page in the pages list
Scenario: File exists in wiki repo
Given I have an existing Wiki page with images linked on page
And I browse to wiki page with images
And I click on existing image link
Then I should see the image from wiki repo
Scenario: Image in wiki repo shown on the page
Given I have an existing Wiki page with images linked on page
And I browse to wiki page with images
Then Image should be shown on the page
Scenario: File does not exist in wiki repo
Given I have an existing Wiki page with images linked on page
And I browse to wiki page with images
And I click on image link
Then I should see the new wiki page form
...@@ -25,7 +25,6 @@ class Dashboard < Spinach::FeatureSteps ...@@ -25,7 +25,6 @@ class Dashboard < Spinach::FeatureSteps
find("#merge_request_target_project_id").value.should == @project.id.to_s find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should == "new_design" find("#merge_request_source_branch").value.should == "new_design"
find("#merge_request_target_branch").value.should == "master" find("#merge_request_target_branch").value.should == "master"
find("#merge_request_title").value.should == "New design"
end end
Given 'user with name "John Doe" joined project "Shop"' do Given 'user with name "John Doe" joined project "Shop"' do
......
...@@ -41,6 +41,18 @@ class ProjectBrowseFiles < Spinach::FeatureSteps ...@@ -41,6 +41,18 @@ class ProjectBrowseFiles < Spinach::FeatureSteps
page.evaluate_script('editor.getValue()').should == "GitlabFileEditor" page.evaluate_script('editor.getValue()').should == "GitlabFileEditor"
end end
step 'I edit code' do
page.execute_script('editor.setValue("GitlabFileEditor")')
end
step 'I click link "Diff"' do
click_link 'Diff'
end
step 'I see diff' do
page.should have_css '.line_holder.new'
end
step 'I click on "new file" link in repo' do step 'I click on "new file" link in repo' do
click_link 'new-file-link' click_link 'new-file-link'
end end
......
...@@ -53,6 +53,7 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -53,6 +53,7 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps
find(:select, "merge_request_source_branch", {}).value.should == 'master' find(:select, "merge_request_source_branch", {}).value.should == 'master'
find(:select, "merge_request_target_branch", {}).value.should == 'stable' find(:select, "merge_request_target_branch", {}).value.should == 'stable'
click_button "Compare branches"
fill_in "merge_request_title", with: "Merge Request On Forked Project" fill_in "merge_request_title", with: "Merge Request On Forked Project"
end end
...@@ -148,29 +149,19 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -148,29 +149,19 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps
current_path.should == edit_project_merge_request_path(@project, @merge_request) current_path.should == edit_project_merge_request_path(@project, @merge_request)
page.should have_content "Edit merge request ##{@merge_request.id}" page.should have_content "Edit merge request ##{@merge_request.id}"
find("#merge_request_title").value.should == "Merge Request On Forked Project" find("#merge_request_title").value.should == "Merge Request On Forked Project"
find("#merge_request_source_project_id").value.should == @forked_project.id.to_s
find("#merge_request_target_project_id").value.should == @project.id.to_s
find("#merge_request_source_branch").value.should have_content "master"
verify_commit_link(".mr_source_commit",@forked_project)
find("#merge_request_target_branch").value.should have_content "stable"
verify_commit_link(".mr_target_commit",@project)
end end
step 'I fill out an invalid "Merge Request On Forked Project" merge request' do step 'I fill out an invalid "Merge Request On Forked Project" merge request' do
#If this isn't filled in the rest of the validations won't be triggered
fill_in "merge_request_title", with: "Merge Request On Forked Project"
select "Select branch", from: "merge_request_target_branch" select "Select branch", from: "merge_request_target_branch"
find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s find(:select, "merge_request_source_project_id", {}).value.should == @forked_project.id.to_s
find(:select, "merge_request_target_project_id", {}).value.should == project.id.to_s find(:select, "merge_request_target_project_id", {}).value.should == project.id.to_s
find(:select, "merge_request_source_branch", {}).value.should == "" find(:select, "merge_request_source_branch", {}).value.should == ""
find(:select, "merge_request_target_branch", {}).value.should == "" find(:select, "merge_request_target_branch", {}).value.should == ""
click_button "Compare branches"
end end
step 'I should see validation errors' do step 'I should see validation errors' do
page.should have_content "Source branch can't be blank" page.should have_content "You must select source and target branch"
page.should have_content "Target branch can't be blank"
end end
step 'the target repository should be the original repository' do step 'the target repository should be the original repository' do
......
...@@ -61,9 +61,10 @@ class ProjectMergeRequests < Spinach::FeatureSteps ...@@ -61,9 +61,10 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I submit new merge request "Wiki Feature"' do step 'I submit new merge request "Wiki Feature"' do
fill_in "merge_request_title", with: "Wiki Feature"
select "master", from: "merge_request_source_branch" select "master", from: "merge_request_source_branch"
select "notes_refactoring", from: "merge_request_target_branch" select "notes_refactoring", from: "merge_request_target_branch"
click_button "Compare branches"
fill_in "merge_request_title", with: "Wiki Feature"
click_button "Submit merge request" click_button "Submit merge request"
end end
......
...@@ -86,6 +86,47 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps ...@@ -86,6 +86,47 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
page.should have_content @page.title page.should have_content @page.title
end end
Given 'I have an existing Wiki page with images linked on page' do
wiki.create_page("pictures", "Look at this [image](image.jpg)\n\n ![image](image.jpg)", :markdown, "first commit")
@wiki_page = wiki.find_page("pictures")
end
And 'I browse to wiki page with images' do
visit project_wiki_path(project, @wiki_page)
end
And 'I click on existing image link' do
file = Gollum::File.new(wiki.wiki)
Gollum::Wiki.any_instance.stub(:file).with("image.jpg", "master", true).and_return(file)
Gollum::File.any_instance.stub(:mime_type).and_return("image/jpeg")
page.should have_link('image', href: "image.jpg")
click_on "image"
end
Then 'I should see the image from wiki repo' do
url = URI.parse(current_url)
url.path.should match("wikis/image.jpg")
page.should_not have_xpath('/html') # Page should render the image which means there is no html involved
Gollum::Wiki.any_instance.unstub(:file)
Gollum::File.any_instance.unstub(:mime_type)
end
Then 'Image should be shown on the page' do
page.should have_xpath("//img[@src=\"image.jpg\"]")
end
And 'I click on image link' do
page.should have_link('image', href: "image.jpg")
click_on "image"
end
Then 'I should see the new wiki page form' do
url = URI.parse(current_url)
url.path.should match("wikis/image.jpg")
page.should have_content('New Wiki Page')
page.should have_content('Editing - image.jpg')
end
def wiki def wiki
@project_wiki = ProjectWiki.new(project, current_user) @project_wiki = ProjectWiki.new(project, current_user)
end end
......
...@@ -24,7 +24,7 @@ module API ...@@ -24,7 +24,7 @@ module API
# branch (required) - The name of the branch # branch (required) - The name of the branch
# Example Request: # Example Request:
# GET /projects/:id/repository/branches/:branch # GET /projects/:id/repository/branches/:branch
get ":id/repository/branches/:branch" do get ':id/repository/branches/:branch', requirements: { branch: /.*/ } do
@branch = user_project.repo.heads.find { |item| item.name == params[:branch] } @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
not_found!("Branch does not exist") if @branch.nil? not_found!("Branch does not exist") if @branch.nil?
present @branch, with: Entities::RepoObject, project: user_project present @branch, with: Entities::RepoObject, project: user_project
...@@ -37,7 +37,9 @@ module API ...@@ -37,7 +37,9 @@ module API
# branch (required) - The name of the branch # branch (required) - The name of the branch
# Example Request: # Example Request:
# PUT /projects/:id/repository/branches/:branch/protect # PUT /projects/:id/repository/branches/:branch/protect
put ":id/repository/branches/:branch/protect" do put ':id/repository/branches/:branch/protect',
requirements: { branch: /.*/ } do
authorize_admin_project authorize_admin_project
@branch = user_project.repository.find_branch(params[:branch]) @branch = user_project.repository.find_branch(params[:branch])
...@@ -55,7 +57,9 @@ module API ...@@ -55,7 +57,9 @@ module API
# branch (required) - The name of the branch # branch (required) - The name of the branch
# Example Request: # Example Request:
# PUT /projects/:id/repository/branches/:branch/unprotect # PUT /projects/:id/repository/branches/:branch/unprotect
put ":id/repository/branches/:branch/unprotect" do put ':id/repository/branches/:branch/unprotect',
requirements: { branch: /.*/ } do
authorize_admin_project authorize_admin_project
@branch = user_project.repository.find_branch(params[:branch]) @branch = user_project.repository.find_branch(params[:branch])
......
...@@ -113,6 +113,45 @@ module API ...@@ -113,6 +113,45 @@ module API
end end
end end
# Get ssh keys of a specified user. Only available to admin users.
#
# Parameters:
# uid (required) - The ID of a user
# Example Request:
# GET /users/:uid/keys
get ':uid/keys' do
authenticated_as_admin!
user = User.find_by(id: params[:uid])
if user
present user.keys, with: Entities::SSHKey
else
not_found!
end
end
# Delete existing ssh key of a specified user. Only available to admin
# users.
#
# Parameters:
# uid (required) - The ID of a user
# id (required) - SSH Key ID
# Example Request:
# DELETE /users/:uid/keys/:id
delete ':uid/keys/:id' do
authenticated_as_admin!
user = User.find_by(id: params[:uid])
if user
begin
key = user.keys.find params[:id]
key.destroy
rescue ActiveRecord::RecordNotFound
not_found!
end
else
not_found!
end
end
# Delete user. Available only for admin # Delete user. Available only for admin
# #
# Example Request: # Example Request:
......
...@@ -101,7 +101,7 @@ module Backup ...@@ -101,7 +101,7 @@ module Backup
def tar_version def tar_version
tar_version, _ = Gitlab::Popen.popen(%W(tar --version)) tar_version, _ = Gitlab::Popen.popen(%W(tar --version))
tar_version.split("\n").first tar_version.force_encoding('locale').split("\n").first
end end
end end
end end
...@@ -10,15 +10,12 @@ module Backup ...@@ -10,15 +10,12 @@ module Backup
Project.find_each(batch_size: 1000) do |project| Project.find_each(batch_size: 1000) do |project|
print " * #{project.path_with_namespace} ... " print " * #{project.path_with_namespace} ... "
if project.empty_repo?
puts "[SKIPPED]".cyan
next
end
# Create namespace dir if missing # Create namespace dir if missing
FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
if system(*%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all), silent) if project.empty_repo?
puts "[SKIPPED]".cyan
elsif system(*%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all), silent)
puts "[DONE]".green puts "[DONE]".green
else else
puts "[FAILED]".red puts "[FAILED]".red
......
...@@ -4,9 +4,9 @@ module Gitlab ...@@ -4,9 +4,9 @@ module Gitlab
attr_reader :lines, :new_path attr_reader :lines, :new_path
def initialize(diff) def initialize(lines, new_path = '')
@lines = diff.diff.lines.to_a @lines = lines
@new_path = diff.new_path @new_path = new_path
end end
def each def each
...@@ -18,10 +18,7 @@ module Gitlab ...@@ -18,10 +18,7 @@ module Gitlab
lines_arr.each do |line| lines_arr.each do |line|
raw_line = line.dup raw_line = line.dup
next if line.match(/^\-\-\- \/dev\/null/) next if filename?(line)
next if line.match(/^\+\+\+ \/dev\/null/)
next if line.match(/^\-\-\- a/)
next if line.match(/^\+\+\+ b/)
full_line = html_escape(line.gsub(/\n/, '')) full_line = html_escape(line.gsub(/\n/, ''))
full_line = ::Gitlab::InlineDiff.replace_markers full_line full_line = ::Gitlab::InlineDiff.replace_markers full_line
...@@ -53,8 +50,17 @@ module Gitlab ...@@ -53,8 +50,17 @@ module Gitlab
end end
end end
def empty?
@lines.empty?
end
private private
def filename?(line)
line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b',
'--- /tmp/diffy', '+++ /tmp/diffy')
end
def identification_type(line) def identification_type(line)
if line[0] == "+" if line[0] == "+"
"new" "new"
......
...@@ -65,7 +65,11 @@ module Gitlab ...@@ -65,7 +65,11 @@ module Gitlab
end end
def name def name
auth.info.name.to_s.force_encoding("utf-8") if auth.info.name.nil?
"#{auth.info.first_name} #{auth.info.last_name}".force_encoding('utf-8')
else
auth.info.name.to_s.force_encoding('utf-8')
end
end end
def username def username
......
module Gitlab
module Satellite
class CompareAction < Action
def initialize(user, target_project, target_branch, source_project, source_branch)
super user, target_project
@target_project, @target_branch = target_project, target_branch
@source_project, @source_branch = source_project, source_branch
end
# Only show what is new in the source branch compared to the target branch, not the other way around.
# The line below with merge_base is equivalent to diff with three dots (git diff branch1...branch2)
# From the git documentation: "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B"
def diffs
in_locked_and_timed_satellite do |target_repo|
prepare_satellite!(target_repo)
update_satellite_source_and_target!(target_repo)
common_commit = target_repo.git.native(:merge_base, default_options, ["origin/#{@target_branch}", "source/#{@source_branch}"]).strip
#this method doesn't take default options
diffs = target_repo.diff(common_commit, "source/#{@source_branch}")
diffs = diffs.map { |diff| Gitlab::Git::Diff.new(diff) }
diffs
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
# Retrieve an array of commits between the source and the target
def commits
in_locked_and_timed_satellite do |target_repo|
prepare_satellite!(target_repo)
update_satellite_source_and_target!(target_repo)
commits = target_repo.commits_between("origin/#{@target_branch}", "source/#{@source_branch}")
commits = commits.map { |commit| Gitlab::Git::Commit.new(commit, nil) }
commits
end
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
private
# Assumes a satellite exists that is a fresh clone of the projects repo, prepares satellite for diffs
def update_satellite_source_and_target!(target_repo)
target_repo.remote_add('source', @source_project.repository.path_to_repo)
target_repo.remote_fetch('source')
target_repo.git.checkout(default_options({b: true}), @target_branch, "origin/#{@target_branch}")
rescue Grit::Git::CommandFailed => ex
handle_exception(ex)
end
end
end
end
...@@ -84,6 +84,7 @@ module Gitlab ...@@ -84,6 +84,7 @@ module Gitlab
# Clear the working directory # Clear the working directory
def clear_working_dir! def clear_working_dir!
repo.git.reset(hard: true) repo.git.reset(hard: true)
repo.git.clean(f: true, d: true, x: true)
end end
# Deletes all branches except the parking branch # Deletes all branches except the parking branch
......
...@@ -15,14 +15,7 @@ namespace :gitlab do ...@@ -15,14 +15,7 @@ namespace :gitlab do
end end
Rake::Task["db:setup"].invoke Rake::Task["db:setup"].invoke
Rake::Task["add_limits_mysql"].invoke
config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
success = case config["adapter"]
when /^mysql/ then
Rake::Task["add_limits_mysql"].invoke
when "postgresql" then
end
Rake::Task["db:seed_fu"].invoke Rake::Task["db:seed_fu"].invoke
rescue Gitlab::TaskAbortedByUserError rescue Gitlab::TaskAbortedByUserError
puts "Quitting...".red puts "Quitting...".red
......
namespace :gitlab do namespace :gitlab do
namespace :shell do namespace :shell do
desc "GITLAB | Install or upgrade gitlab-shell"
task :install, [:tag, :repo] => :environment do |t, args|
warn_user_is_not_gitlab
args.with_defaults(tag: "v1.9.3", repo: "https://gitlab.com/gitlab-org/gitlab-shell.git")
user = Settings.gitlab.user
home_dir = Settings.gitlab.user_home
gitlab_url = Settings.gitlab.url
# gitlab-shell requires a / at the end of the url
gitlab_url += "/" unless gitlab_url.match(/\/$/)
repos_path = Gitlab.config.gitlab_shell.repos_path
target_dir = Gitlab.config.gitlab_shell.path
# Clone if needed
unless File.directory?(target_dir)
sh "git clone '#{args.repo}' '#{target_dir}'"
end
# Make sure we're on the right tag
Dir.chdir(target_dir) do
sh "git fetch origin && git reset --hard $(git describe #{args.tag} || git describe origin/#{args.tag})"
redis_url = URI.parse(ENV['REDIS_URL'] || "redis://localhost:6379")
config = {
user: user,
gitlab_url: gitlab_url,
http_settings: {self_signed_cert: false}.stringify_keys,
repos_path: repos_path,
auth_file: File.join(home_dir, ".ssh", "authorized_keys"),
redis: {
bin: %x{which redis-cli}.chomp,
host: redis_url.host,
port: redis_url.port,
namespace: "resque:gitlab"
}.stringify_keys,
log_level: "INFO",
audit_usernames: false
}.stringify_keys
# Generate config.yml based on existing gitlab settings
File.open("config.yml", "w+") {|f| f.puts config.to_yaml}
# Launch installation process
sh "bin/install"
end
# Required for debian packaging with PKGR: Setup .ssh/environment with
# the current PATH, so that the correct ruby version gets loaded
# Requires to set "PermitUserEnvironment yes" in sshd config (should not
# be an issue since it is more than likely that there are no "normal"
# user accounts on a gitlab server). The alternative is for the admin to
# install a ruby (1.9.3+) in the global path.
File.open(File.join(home_dir, ".ssh", "environment"), "w+") do |f|
f.puts "PATH=#{ENV['PATH']}"
end
end
desc "GITLAB | Setup gitlab-shell" desc "GITLAB | Setup gitlab-shell"
task setup: :environment do task setup: :environment do
setup setup
......
...@@ -8,9 +8,9 @@ namespace :gitlab do ...@@ -8,9 +8,9 @@ namespace :gitlab do
] ]
cmds.each do |cmd| cmds.each do |cmd|
system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) result = system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd)
raise "#{cmd} failed!" unless $?.exitstatus.zero? raise "#{cmd} failed!" unless result
end end
end end
end end
require Rails.root.join('db/migrate/limits_to_mysql')
desc "GITLAB | Add limits to strings in mysql database" desc "GITLAB | Add limits to strings in mysql database"
task add_limits_mysql: :environment do task add_limits_mysql: :environment do
puts "Adding limits to schema.rb for mysql" puts "Adding limits to schema.rb for mysql"
LimitsToMysql.new.up LimitsToMysql.new.up
end end
class LimitsToMysql < ActiveRecord::Migration
def up
change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647
change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
change_column :snippets, :content, :text, limit: 2147483647
change_column :notes, :st_diff, :text, limit: 2147483647
end
end
desc "GITLAB | Setup gitlab db"
task :setup do
Rake::Task["gitlab:setup"].invoke
end
...@@ -2,7 +2,6 @@ body { ...@@ -2,7 +2,6 @@ body {
color: #666; color: #666;
text-align: center; text-align: center;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
sans-serif;
margin:0; margin:0;
width: 800px; width: 800px;
margin: auto; margin: auto;
......
require 'spec_helper'
describe NotesFinder do
let(:user) { create :user }
let(:project) { create :project }
let(:note1) { create :note_on_commit, project: project }
let(:note2) { create :note_on_commit, project: project }
let(:commit) { note1.noteable }
before do
project.team << [user, :master]
end
describe :execute do
let(:params) { { target_id: commit.id, target_type: 'commit', last_fetched_at: 1.hour.ago.to_i } }
before do
note1
note2
end
it 'should find all notes' do
notes = NotesFinder.new.execute(project, user, params)
notes.size.should eq(2)
end
it 'should raise an exception for an invalid target_type' do
params.merge!(target_type: 'invalid')
expect { NotesFinder.new.execute(project, user, params) }.to raise_error('invalid target_type')
end
it 'filters out old notes' do
note2.update_attribute(:updated_at, 2.hours.ago)
notes = NotesFinder.new.execute(project, user, params)
notes.should eq([note1])
end
end
end
...@@ -46,7 +46,7 @@ describe ApplicationHelper do ...@@ -46,7 +46,7 @@ describe ApplicationHelper do
group = create(:group) group = create(:group)
group.avatar = File.open(avatar_file_path) group.avatar = File.open(avatar_file_path)
group.save! group.save!
group_icon(group.path).to_s.should == "/uploads/group/avatar/#{ group.id }/gitlab_logo.png" group_icon(group.path).to_s.should match("/uploads/group/avatar/#{ group.id }/gitlab_logo.png")
end end
it "should give default avatar_icon when no avatar is present" do it "should give default avatar_icon when no avatar is present" do
...@@ -63,7 +63,7 @@ describe ApplicationHelper do ...@@ -63,7 +63,7 @@ describe ApplicationHelper do
user = create(:user) user = create(:user)
user.avatar = File.open(avatar_file_path) user.avatar = File.open(avatar_file_path)
user.save! user.save!
avatar_icon(user.email).to_s.should == "/uploads/user/avatar/#{ user.id }/gitlab_logo.png" avatar_icon(user.email).to_s.should match("/uploads/user/avatar/#{ user.id }/gitlab_logo.png")
end end
it "should call gravatar_icon when no avatar is present" do it "should call gravatar_icon when no avatar is present" do
......
...@@ -239,7 +239,7 @@ describe Notify do ...@@ -239,7 +239,7 @@ describe Notify do
it_behaves_like 'an assignee email' it_behaves_like 'an assignee email'
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/ should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end end
it 'contains a link to the new merge request' do it 'contains a link to the new merge request' do
...@@ -275,7 +275,7 @@ describe Notify do ...@@ -275,7 +275,7 @@ describe Notify do
end end
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/ should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end end
it 'contains the name of the previous assignee' do it 'contains the name of the previous assignee' do
...@@ -303,7 +303,7 @@ describe Notify do ...@@ -303,7 +303,7 @@ describe Notify do
end end
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/ should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end end
it 'contains the new status' do it 'contains the new status' do
...@@ -426,7 +426,7 @@ describe Notify do ...@@ -426,7 +426,7 @@ describe Notify do
it_behaves_like 'a note email' it_behaves_like 'a note email'
it 'has the correct subject' do it 'has the correct subject' do
should have_subject /#{merge_request.title} \(!#{merge_request.iid}\)/ should have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
end end
it 'contains a link to the merge request note' do it 'contains a link to the merge request note' do
......
...@@ -242,6 +242,67 @@ describe API::API, api: true do ...@@ -242,6 +242,67 @@ describe API::API, api: true do
end end
end end
describe 'GET /user/:uid/keys' do
before { admin }
context 'when unauthenticated' do
it 'should return authentication error' do
get api("/users/#{user.id}/keys")
response.status.should == 401
end
end
context 'when authenticated' do
it 'should return 404 for non-existing user' do
get api('/users/999999/keys', admin)
response.status.should == 404
end
it 'should return array of ssh keys' do
user.keys << key
user.save
get api("/users/#{user.id}/keys", admin)
response.status.should == 200
json_response.should be_an Array
json_response.first['title'].should == key.title
end
end
end
describe 'DELETE /user/:uid/keys/:id' do
before { admin }
context 'when unauthenticated' do
it 'should return authentication error' do
delete api("/users/#{user.id}/keys/42")
response.status.should == 401
end
end
context 'when authenticated' do
it 'should delete existing key' do
user.keys << key
user.save
expect {
delete api("/users/#{user.id}/keys/#{key.id}", admin)
}.to change { user.keys.count }.by(-1)
response.status.should == 200
end
it 'should return 404 error if user not found' do
user.keys << key
user.save
delete api("/users/999999/keys/#{key.id}", admin)
response.status.should == 404
end
it 'should return 404 error if key not foud' do
delete api("/users/#{user.id}/keys/42", admin)
response.status.should == 404
end
end
end
describe "DELETE /users/:id" do describe "DELETE /users/:id" do
before { admin } before { admin }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment