diff options
153 files changed, 1758 insertions, 693 deletions
diff --git a/.pkgr.yml b/.pkgr.yml new file mode 100644 index 00000000000..09cb83783dc --- /dev/null +++ b/.pkgr.yml @@ -0,0 +1,19 @@ +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 diff --git a/CHANGELOG b/CHANGELOG index 3ef46f4484e..72bd6984869 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,20 @@ +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 + - Two Step MR creation process + - Remove unwanted files from satellite working directory with git clean -fdx + v 6.8.0 - Ability to at mention users that are participating in issue and merge req. discussion - Enabled GZip Compression for assets in example Nginx, make sure that Nginx is compiled with --with-http_gzip_static_module flag (this is default in Ubuntu) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 290804e6187..780db547f82 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,9 +107,10 @@ For examples of feedback on merge requests please look at already [closed merge ## Style guides 1. [Ruby](https://github.com/bbatsov/ruby-style-guide) -2. [Rails](https://github.com/bbatsov/rails-style-guide) -3. [Formatting](https://github.com/thoughtbot/guides/tree/master/style#formatting) -4. [Naming](https://github.com/thoughtbot/guides/tree/master/style#naming) -8. [Testing](https://github.com/thoughtbot/guides/tree/master/style#testing) -7. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript) -9. [Shell commands](doc/development/shell_commands.md) +1. [Rails](https://github.com/bbatsov/rails-style-guide) +1. [Formatting](https://github.com/thoughtbot/guides/tree/master/style#formatting) +1. [Naming](https://github.com/thoughtbot/guides/tree/master/style#naming) +1. [Testing](https://github.com/thoughtbot/guides/tree/master/style#testing) +1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript) +1. [Shell commands](doc/development/shell_commands.md) +1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) @@ -71,6 +71,7 @@ gem "carrierwave" # for aws storage gem "fog", "~> 1.14", group: :aws +gem "unf", group: :aws # Authorization gem "six" @@ -82,6 +83,9 @@ gem "seed-fu" gem "redcarpet", "~> 2.2.2" gem "github-markup" +# Diffs +gem 'diffy', '~> 3.0.3' + # Asciidoc to HTML gem "asciidoctor" @@ -148,7 +152,7 @@ gem "rack-attack" # Ace editor gem 'ace-rails-ap' -gem "sass-rails" +gem "sass-rails", '~> 4.0.2' gem "coffee-rails" gem "uglifier" gem "therubyracer" @@ -157,8 +161,8 @@ gem 'jquery-turbolinks' gem 'select2-rails' gem 'jquery-atwho-rails', "~> 0.3.3" -gem "jquery-rails", "2.1.3" -gem "jquery-ui-rails", "2.0.2" +gem "jquery-rails" +gem "jquery-ui-rails" gem "raphael-rails", "~> 2.1.2" gem 'bootstrap-sass', '~> 3.0' gem "font-awesome-rails", '~> 3.2' @@ -232,4 +236,4 @@ end group :production do gem "gitlab_meta", '6.0' -end
\ No newline at end of file +end diff --git a/Gemfile.lock b/Gemfile.lock index 155e03e5456..c022868bc2e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,26 +2,26 @@ GEM remote: https://rubygems.org/ specs: ace-rails-ap (2.0.1) - actionmailer (4.0.3) - actionpack (= 4.0.3) + actionmailer (4.0.5) + actionpack (= 4.0.5) mail (~> 2.5.4) - actionpack (4.0.3) - activesupport (= 4.0.3) + actionpack (4.0.5) + activesupport (= 4.0.5) builder (~> 3.1.0) erubis (~> 2.7.0) rack (~> 1.5.2) rack-test (~> 0.6.2) - activemodel (4.0.3) - activesupport (= 4.0.3) + activemodel (4.0.5) + activesupport (= 4.0.5) builder (~> 3.1.0) - activerecord (4.0.3) - activemodel (= 4.0.3) + activerecord (4.0.5) + activemodel (= 4.0.5) activerecord-deprecated_finders (~> 1.0.2) - activesupport (= 4.0.3) + activesupport (= 4.0.5) arel (~> 4.0.0) activerecord-deprecated_finders (1.0.3) - activesupport (4.0.3) - i18n (~> 0.6, >= 0.6.4) + activesupport (4.0.5) + i18n (~> 0.6, >= 0.6.9) minitest (~> 4.2) multi_json (~> 1.3) thread_safe (~> 0.1) @@ -34,7 +34,6 @@ GEM rake (>= 0.8.7) arel (4.0.2) asciidoctor (0.1.4) - atomic (1.1.16) awesome_print (1.2.0) axiom-types (0.0.5) descendants_tracker (~> 0.0.1) @@ -101,6 +100,7 @@ GEM devise-async (0.8.0) devise (>= 2.2, < 3.2) diff-lcs (1.2.5) + diffy (3.0.3) docile (1.1.1) dotenv (0.9.0) email_spec (1.5.0) @@ -162,7 +162,7 @@ GEM multi_json gitlab-grack (2.0.0.pre) rack (~> 1.5.1) - gitlab-grit (2.6.5) + gitlab-grit (2.6.6) charlock_holmes (~> 0.6) diff-lcs (~> 1.1) mime-types (~> 1.15) @@ -247,15 +247,14 @@ GEM rake jasmine-core (2.0.0.rc5) jquery-atwho-rails (0.3.3) - jquery-rails (2.1.3) - railties (>= 3.1.0, < 5.0) - thor (~> 0.14) + jquery-rails (3.1.0) + railties (>= 3.0, < 5.0) + thor (>= 0.14, < 2.0) jquery-turbolinks (2.0.1) railties (>= 3.1.0) turbolinks - jquery-ui-rails (2.0.2) - jquery-rails - railties (>= 3.1.0) + jquery-ui-rails (4.2.1) + railties (>= 3.2.16) json (1.8.1) jwt (0.1.8) multi_json (>= 1.5) @@ -280,7 +279,7 @@ GEM mime-types (1.25.1) mini_portile (0.5.3) minitest (4.7.5) - multi_json (1.9.2) + multi_json (1.10.0) multi_xml (0.5.5) multipart-post (1.2.0) mysql2 (0.3.11) @@ -350,13 +349,13 @@ GEM rack rack-test (0.6.2) rack (>= 1.0) - rails (4.0.3) - actionmailer (= 4.0.3) - actionpack (= 4.0.3) - activerecord (= 4.0.3) - activesupport (= 4.0.3) + rails (4.0.5) + actionmailer (= 4.0.5) + actionpack (= 4.0.5) + activerecord (= 4.0.5) + activesupport (= 4.0.5) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.3) + railties (= 4.0.5) sprockets-rails (~> 2.0.0) rails-observers (0.1.2) activemodel (~> 4.0) @@ -369,13 +368,13 @@ GEM i18n require_all ruby-progressbar - railties (4.0.3) - actionpack (= 4.0.3) - activesupport (= 4.0.3) + railties (4.0.5) + actionpack (= 4.0.5) + activesupport (= 4.0.5) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.12.0) - rake (10.1.1) + rake (10.3.1) raphael-rails (2.1.2) rb-fsevent (0.9.3) rb-inotify (0.9.2) @@ -428,11 +427,12 @@ GEM safe_yaml (0.9.7) sanitize (2.1.0) nokogiri (>= 1.4.4) - sass (3.2.12) - sass-rails (4.0.1) + sass (3.2.19) + sass-rails (4.0.3) railties (>= 4.0.0, < 5.0) - sass (>= 3.1.10) - sprockets-rails (~> 2.0.0) + sass (~> 3.2.0) + sprockets (~> 2.8, <= 2.11.0) + sprockets-rails (~> 2.0) sdoc (0.3.20) json (>= 1.1.3) rdoc (~> 3.10) @@ -479,7 +479,7 @@ GEM spring (>= 0.9.1) spring-commands-spinach (1.0.0) spring (>= 0.9.1) - sprockets (2.10.1) + sprockets (2.11.0) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) @@ -502,9 +502,8 @@ GEM daemons (>= 1.0.9) eventmachine (>= 1.0.0) rack (>= 1.0.0) - thor (0.18.1) - thread_safe (0.3.1) - atomic (>= 1.1.7, < 2) + thor (0.19.1) + thread_safe (0.3.3) tilt (1.4.1) timers (1.1.0) tinder (1.9.3) @@ -531,6 +530,9 @@ GEM execjs (>= 0.3.0) json (>= 1.8.0) underscore-rails (1.4.4) + unf (0.1.4) + unf_ext + unf_ext (0.0.6) unicorn (4.6.3) kgio (~> 2.6) rack @@ -574,6 +576,7 @@ DEPENDENCIES default_value_for (~> 3.0.0) devise (= 3.0.4) devise-async (= 0.8.0) + diffy (~> 3.0.3) email_spec email_validator (~> 1.4.0) enumerize @@ -603,9 +606,9 @@ DEPENDENCIES httparty jasmine (= 2.0.0.rc5) jquery-atwho-rails (~> 0.3.3) - jquery-rails (= 2.1.3) + jquery-rails jquery-turbolinks - jquery-ui-rails (= 2.0.2) + jquery-ui-rails kaminari (~> 0.15.1) launchy letter_opener @@ -634,7 +637,7 @@ DEPENDENCIES redis-rails rspec-rails sanitize (~> 2.0) - sass-rails + sass-rails (~> 4.0.2) sdoc seed-fu select2-rails @@ -659,6 +662,7 @@ DEPENDENCIES turbolinks uglifier underscore-rails (~> 1.4.4) + unf unicorn (~> 4.6.3) unicorn-worker-killer version_sorter diff --git a/README.md b/README.md index 3f3453a267a..ca7bec3b6d6 100644 --- a/README.md +++ b/README.md @@ -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 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. -* 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) and [Ruby API wrapper](https://github.com/NARKOZ/gitlab) for GitLab. ### Requirements @@ -1 +1 @@ -6.8.0.rc1 +6.9.0.pre diff --git a/app/assets/images/ui-icons_222222_256x240.png b/app/assets/images/ui-icons_222222_256x240.png Binary files differdeleted file mode 100644 index 8bc06cbf03b..00000000000 --- a/app/assets/images/ui-icons_222222_256x240.png +++ /dev/null diff --git a/app/assets/images/ui-icons_454545_256x240.png b/app/assets/images/ui-icons_454545_256x240.png Binary files differdeleted file mode 100644 index cfd1eaffaae..00000000000 --- a/app/assets/images/ui-icons_454545_256x240.png +++ /dev/null diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 46d6db0f05c..b61d9875e03 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -59,7 +59,7 @@ class Dispatcher initHighlight: -> $('.highlight pre code').each (i, e) -> - hljs.highlightBlock(e) $(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")) + hljs.highlightBlock(e) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index d200d962cae..47989010d1a 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -1,10 +1,11 @@ class Notes @interval: null - constructor: (notes_url, note_ids) -> + constructor: (notes_url, note_ids, last_fetched_at) -> @notes_url = notes_url @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root? @note_ids = note_ids + @last_fetched_at = last_fetched_at @initRefresh() @setupMainTargetNoteForm() @cleanBinding() @@ -49,6 +50,15 @@ class Notes # hide diff note form $(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: -> $(document).off "ajax:success", ".js-main-target-form" $(document).off "ajax:success", ".js-discussion-note-form" @@ -62,6 +72,8 @@ class Notes $(document).off "click", ".js-choose-note-attachment-button" $(document).off "click", ".js-discussion-reply-button" $(document).off "click", ".js-add-diff-note-button" + $(document).off "visibilitychange" + $(document).off "keypress", @notes_forms initRefresh: -> @@ -71,14 +83,16 @@ class Notes , 15000 refresh: -> - @getContent() + @getContent() unless document.hidden getContent: -> $.ajax url: @notes_url + data: "last_fetched_at=" + @last_fetched_at dataType: "json" success: (data) => notes = data.notes + @last_fetched_at = data.last_fetched_at $.each notes, (i, note) => @renderNote(note) @@ -450,4 +464,10 @@ class Notes filename = $(this).val().replace(/^.*[\\\/]/, "") form.find(".js-attachment-filename").text filename + ### + Called when the tab visibility changes + ### + visibilityChange: => + @refresh() + @Notes = Notes diff --git a/app/assets/javascripts/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee index 03fad41c490..b0e39610feb 100644 --- a/app/assets/javascripts/project_users_select.js.coffee +++ b/app/assets/javascripts/project_users_select.js.coffee @@ -1,7 +1,7 @@ @projectUsersSelect = init: -> $('.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 placeholder: $(select).data('placeholder') || "Search for a user" diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index ce36c1132ea..c53873f95a2 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -12,10 +12,7 @@ *= require nprogress-bootstrap */ -@import "main/variables.scss"; -@import "main/mixins.scss"; -@import "main/fonts.scss"; -@import "main/layout.scss"; +@import "main/*"; /** * Customized Twitter bootstrap @@ -31,64 +28,22 @@ /** * Generic css (forms, nav etc): */ -@import "generic/avatar.scss"; -@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"; +@import "generic/*"; /** * Page specific styles (issues, projects etc): */ -@import "sections/header.scss"; -@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/*"; /** * Code highlight */ -@import "highlight/white.scss"; -@import "highlight/dark.scss"; -@import "highlight/solarized_dark.scss"; -@import "highlight/monokai.scss"; +@import "highlight/*"; /** * UI themes: */ -@import "themes/ui_basic.scss"; -@import "themes/ui_mars.scss"; -@import "themes/ui_modern.scss"; -@import "themes/ui_gray.scss"; -@import "themes/ui_color.scss"; +@import "themes/*"; /** * Styles for JS behaviors. diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/generic/files.scss index 12559f76051..6418f24d97f 100644 --- a/app/assets/stylesheets/generic/files.scss +++ b/app/assets/stylesheets/generic/files.scss @@ -26,6 +26,10 @@ margin-top: -5px; } + .left-options { + margin-top: -3px; + } + .file_name { color: $style_color; font-size: 14px; diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss index 3db4d908d9c..d4d3361bc72 100644 --- a/app/assets/stylesheets/generic/issue_box.scss +++ b/app/assets/stylesheets/generic/issue_box.scss @@ -12,6 +12,7 @@ margin:20px 0; background: #FFF; border: 1px solid #EEE; + @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05)); &.issue-box-closed { border-color: #DA4E49; @@ -70,7 +71,6 @@ } .state { - height: 34px; border-bottom: 1px solid #DDD; line-height: 32px; } @@ -89,6 +89,18 @@ border: none; border-top: 1px solid #eee; 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 { @@ -106,7 +118,11 @@ padding: 1px 25px; text-align: center; text-shadow: none; - margin-right: 20px; display: inline-block; + line-height: 34px; + } + + .creator { + padding: 2px 15px; } } diff --git a/app/assets/stylesheets/generic/jquery.scss b/app/assets/stylesheets/generic/jquery.scss index 4a9341e8f53..6b29accb315 100644 --- a/app/assets/stylesheets/generic/jquery.scss +++ b/app/assets/stylesheets/generic/jquery.scss @@ -8,7 +8,7 @@ width: 270px; .ui-datepicker-header { - background: #EEE; + background: #FFF; border-color: #DDD; } @@ -19,20 +19,37 @@ } &.ui-autocomplete { - @include border-radius(0px); border-color: #DDD; padding: 0; + margin-top: 2px; + z-index: 1001; .ui-menu-item a { - color: #777; - - &:hover { - background: $hover; - border-color: $primary_color; - @include border-radius(0px); - color: #333; - } + padding: 4px 10px; } } -} + .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; + } +} diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index a4419551738..8cc72d7f07a 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -47,7 +47,7 @@ a { text-decoration: underline; } - &.dark { + &.darken { color: $style_color; } diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/main/mixins.scss index fcc7374d0d9..147c759ecb8 100644 --- a/app/assets/stylesheets/main/mixins.scss +++ b/app/assets/stylesheets/main/mixins.scss @@ -41,31 +41,6 @@ * Prefilled mixins * 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 { @include box-shadow(0 0 3px #ddd); diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/sections/diff.scss index eb272f20f40..64e669ac2b3 100644 --- a/app/assets/stylesheets/sections/diff.scss +++ b/app/assets/stylesheets/sections/diff.scss @@ -63,26 +63,14 @@ } } - .text-file-parallel div { - display: inline-block; - padding-bottom: 16px; - } - .diff-side { - overflow-x: scroll; - width: 508px; - 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 + tr.line_holder.parallel{ + .old_line, .new_line, .diff_line { + min-width: 50px; + } + + td.line_content.parallel{ + width: 50%; + } } .old_line, .new_line, .diff_line { diff --git a/app/assets/stylesheets/sections/graph.scss b/app/assets/stylesheets/sections/graph.scss index 1e22d161bfc..8a337a5e206 100644 --- a/app/assets/stylesheets/sections/graph.scss +++ b/app/assets/stylesheets/sections/graph.scss @@ -1,17 +1,16 @@ .project-network { - border: 1px solid #aaa; - padding: 1px; + border: 1px solid #CCC; .tip { color: #888; font-size: 14px; padding: 10px; border-bottom: 1px solid #bbb; - @include bg-gray-gradient; + background: #EEE; } .network-graph { - background: #f1f1f1; + background: #FFF; height: 500px; overflow-y: scroll; overflow-x: hidden; diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index d4f8c8108ab..02c9123178f 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -45,14 +45,6 @@ padding: 6px 10px; border: 1px solid #ccc; @include border-radius(4px); - - - input.check_all_issues { - padding: 0; - margin: 0; - position: relative; - top: 3px; - } } .issues_content { @@ -143,3 +135,36 @@ form.edit-issue { 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; + } + } + } +} diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss index 790496a1a5a..5dcfc449b42 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/sections/merge_requests.scss @@ -31,7 +31,6 @@ .mr_source_commit, .mr_target_commit { - margin-top: 10px; .commit { margin: 0; padding: 2px 0; diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index c9c7b6ecced..7e56781f56a 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -139,6 +139,7 @@ ul.notes { background-color: #fff; border-width: 1px 0; padding-top: 0; + vertical-align: top; li { padding: 5px; diff --git a/app/assets/stylesheets/sections/votes.scss b/app/assets/stylesheets/sections/votes.scss index 13f811e01a1..d683e33e1f0 100644 --- a/app/assets/stylesheets/sections/votes.scss +++ b/app/assets/stylesheets/sections/votes.scss @@ -40,4 +40,10 @@ .votes-holder { float: right; width: 250px; + + @media (max-width: $screen-xs-max) { + width: 100%; + margin-top: 5px; + margin-bottom: 10px; + } } diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 13a7bdcf34a..92ef5963373 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -12,6 +12,7 @@ class Admin::ProjectsController < Admin::ApplicationController @projects = @projects.with_push if params[:with_push].present? @projects = @projects.abandoned if params[:abandoned].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) end diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb index ff5206b6fa1..be611892bb0 100644 --- a/app/controllers/projects/edit_tree_controller.rb +++ b/app/controllers/projects/edit_tree_controller.rb @@ -26,6 +26,18 @@ class Projects::EditTreeController < Projects::BaseTreeController 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 def blob diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 745da9c49e4..d8551db7b01 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -62,11 +62,27 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request.source_project = @project unless @merge_request.source_project @merge_request.target_project ||= (@project.forked_from_project || @project) @target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names - @merge_request.target_branch ||= @merge_request.target_project.default_branch - @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 def edit @@ -80,7 +96,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request = MergeRequests::CreateService.new(project, current_user, params[:merge_request]).execute 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 @source_project = @merge_request.source_project @target_project = @merge_request.target_project diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 85d042a89b5..b5b0446b43f 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -5,9 +5,10 @@ class Projects::NotesController < Projects::ApplicationController before_filter :authorize_admin_note!, only: [:update, :destroy] def index + current_fetched_at = Time.now.to_i @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_json[:notes] << { diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index bcd9e0d5219..496064c9a65 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -12,9 +12,22 @@ class Projects::WikisController < Projects::ApplicationController def show @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 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 return render('empty') unless can?(current_user, :write_wiki, @project) @page = WikiPage.new(@project_wiki) diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb index 384316e14b7..ea055694cd7 100644 --- a/app/finders/notes_finder.rb +++ b/app/finders/notes_finder.rb @@ -1,9 +1,13 @@ class NotesFinder + FETCH_OVERLAP = 5.seconds + def execute(project, current_user, params) target_type = params[:target_type] 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" project.notes.for_commit_id(target_id).not_inline.fresh when "issue" @@ -12,6 +16,11 @@ class NotesFinder project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh when "snippet" project.snippets.find(target_id).notes.fresh + else + raise 'invalid target_type' end + + # Use overlapping intervals to avoid worrying about race conditions + notes.where('updated_at > ?', last_fetched_at - FETCH_OVERLAP) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index faecde299c1..5f07cdf448c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -75,7 +75,7 @@ module ApplicationHelper else gravatar_url = request.ssl? || gitlab_config.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url 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 diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index c6e4f574b67..c4abdbdabc7 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -16,9 +16,10 @@ module CommitsHelper end def each_diff_line(diff, index) - Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old| - yield(full_line, type, line_code, line_new, line_old) - end + Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path) + .each do |full_line, type, line_code, line_new, line_old| + yield(full_line, type, line_code, line_new, line_old) + end end def each_diff_line_near(diff, index, expected_line_code) @@ -116,7 +117,7 @@ module CommitsHelper added_lines[line_new] = { line_code: line_code, type: type, line: line } 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 offset2 = 0 diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 95f0eff58b1..7c58908165c 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -82,7 +82,7 @@ module IssuesHelper end 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 def issue_box_class(item) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 9bb3efc41d8..ef0460f8728 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -163,7 +163,7 @@ module ProjectsHelper end def repository_size(project = nil) - "#{(project || @project).repository.size} MB" + "#{(project || @project).repository_size} MB" rescue # In order to prevent 500 error # when application cannot allocate memory diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index a1fe4488ae9..ab24367c455 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -14,7 +14,7 @@ module SelectsHelper css_class << (opts[:class] || '') value = opts[:selected] || '' placeholder = opts[:placeholder] || 'Select user' - - hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder) + project_id = opts[:project_id] || @project.id + hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id) end end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 50501dffefb..f39d0081dce 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -91,4 +91,12 @@ module TreeHelper def leave_edit_message "Leave edit mode?\nAll unsaved changes will be lost." end + + def editing_preview_title(filename) + if gitlab_markdown?(filename) || markup?(filename) + 'Preview' + else + 'Diff' + end + end end diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index a97d55f1b50..1130969a26d 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -6,7 +6,7 @@ module Emails @target_url = project_merge_request_url(@project, @merge_request) mail(from: sender(@merge_request.author_id), to: recipient(recipient_id), - subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) + subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) end def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id) @@ -16,7 +16,7 @@ module Emails @target_url = project_merge_request_url(@project, @merge_request) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), - subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) + subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) end def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) @@ -26,7 +26,7 @@ module Emails @target_url = project_merge_request_url(@project, @merge_request) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), - subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) + subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) end def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) @@ -35,7 +35,7 @@ module Emails @target_url = project_merge_request_url(@project, @merge_request) mail(from: sender(updated_by_user_id), to: recipient(recipient_id), - subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) + subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) end end diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index ccbdadf010f..2a877bc1593 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -27,7 +27,7 @@ module Emails @target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}") mail(from: sender(@note.author_id), to: recipient(recipient_id), - subject: subject("#{@merge_request.title} (!#{@merge_request.iid})")) + subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) end def note_wall_email(recipient_id, note_id) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 8c885b70a48..134a8c8dd40 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -253,6 +253,14 @@ class MergeRequest < ActiveRecord::Base end end + def target_project_namespace + if target_project && target_project.namespace + target_project.namespace.path + else + "(removed)" + end + end + def source_branch_exists? return false unless self.source_project diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 0684461add7..7dce71a677b 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -86,7 +86,7 @@ class MergeRequestDiff < ActiveRecord::Base # between target and source branches def unmerged_commits commits = if merge_request.for_fork? - Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between + compare_action.commits else repository.commits_between(target_branch, source_branch) end @@ -150,7 +150,7 @@ class MergeRequestDiff < ActiveRecord::Base # between target and source branches def unmerged_diffs diffs = if merge_request.for_fork? - Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite + compare_action.diffs else Gitlab::Git::Diff.between(repository, source_branch, target_branch) end @@ -165,4 +165,16 @@ class MergeRequestDiff < ActiveRecord::Base def repository merge_request.target_project.repository 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 diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 6a2ca767030..39ab0b536a3 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -25,6 +25,7 @@ class Milestone < ActiveRecord::Base scope :active, -> { with_state(:active) } scope :closed, -> { with_state(:closed) } + scope :of_projects, ->(ids) { where(project_id: ids) } validates :title, presence: true validates :project, presence: true diff --git a/app/models/note.rb b/app/models/note.rb index 6f7afcd1f9f..cee10ec90d2 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -184,9 +184,10 @@ class Note < ActiveRecord::Base return @diff_line if @diff_line if diff - Gitlab::DiffParser.new(diff).each do |full_line, type, line_code, line_new, line_old| - @diff_line = full_line if line_code == self.line_code - end + Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path) + .each do |full_line, type, line_code, line_new, line_old| + @diff_line = full_line if line_code == self.line_code + end end @diff_line diff --git a/app/models/project.rb b/app/models/project.rb index 3ae47c18136..7ddcc73cf2a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -203,6 +203,7 @@ class Project < ActiveRecord::Base when 'oldest' then reorder('projects.created_at ASC') when 'recently_updated' then reorder('projects.updated_at DESC') 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") end end @@ -562,4 +563,8 @@ class Project < ActiveRecord::Base def forked_from?(project) forked? && project == forked_from_project end + + def update_repository_size + update_attribute(:repository_size, repository.size) + end end diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 163302a18f7..08a52782475 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -64,7 +64,8 @@ class ProjectWiki # # Returns an initialized WikiPage instance or 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) else nil @@ -90,6 +91,12 @@ class ProjectWiki wiki.delete_page(page, commit_details(:deleted, message, page.title)) end + def page_title_and_dir(title) + title_array = title.split("/") + title = title_array.pop + [title.gsub(/\.[^.]*$/, ""), title_array.join("/")] + end + private def create_repo! diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 76f311ed0b4..b8a0a9eb58b 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -175,14 +175,24 @@ class WikiPage end def save(method, *args) - if valid? && wiki.send(method, *args) - @page = wiki.wiki.paged(title) + project_wiki = wiki + 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 @persisted = true 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 end @persisted diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 351b446457d..715b5690751 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -25,6 +25,7 @@ class GitPushService project.ensure_satellite_exists project.repository.expire_cache + project.update_repository_size if push_to_existing_branch?(ref, oldrev) project.update_merge_requests(oldrev, newrev, ref, @user) diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 4969198b8c2..41014f199d5 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -31,7 +31,8 @@ class SystemHooksService path_with_namespace: model.path_with_namespace, project_id: model.id, owner_name: owner.name, - owner_email: owner.respond_to?(:email) ? owner.email : nil + owner_email: owner.respond_to?(:email) ? owner.email : nil, + project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase }) when User data.merge!({ @@ -46,7 +47,8 @@ class SystemHooksService project_id: model.project_id, user_name: model.user.name, user_email: model.user.email, - project_access: model.human_access + project_access: model.human_access, + project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase }) end end diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml index f28dadfb659..c58ca2c9a33 100644 --- a/app/views/admin/broadcast_messages/index.html.haml +++ b/app/views/admin/broadcast_messages/index.html.haml @@ -22,12 +22,12 @@ = f.label :color, "Background Color", class: 'control-label' .col-sm-10 = 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 = f.label :font, "Font Color", class: 'control-label' .col-sm-10 = 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 = f.label :starts_at, class: 'control-label' .col-sm-10.datetime-controls diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 296094ab29c..51ad702154f 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -32,6 +32,7 @@ = visibility_level_icon(level) = label .form-actions + = hidden_field_tag :sort, params[:sort] = submit_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_projects_path, class: "btn" @@ -40,6 +41,28 @@ .title Projects (#{@projects.total_count}) .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" %ul.well-list - @projects.each do |project| diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index d8001fd76d7..fba56b5dc3b 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -20,6 +20,10 @@ = link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do %i.icon-search %li + = link_to help_path, title: 'Help', class: 'has_bottom_tooltip', + 'data-original-title' => 'Help' do + %i.icon-question-sign + %li = link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do %i.icon-globe %li @@ -39,6 +43,6 @@ %li = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do %i.icon-signout - %li + %li.hidden-xs = link_to current_user, class: "profile-pic", id: 'profile-pic' do = image_tag avatar_icon(current_user.email, 26), alt: 'User activity' diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index c5041dd71b8..5d93ffa50ad 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -10,7 +10,7 @@ %p.light GitLab is open source software to collaborate on code. %br - #{link_to "Sign in", new_user_session_path} or browse for #{link_to "public projects", public_projects_path}. + Sign in or browse for #{link_to "public projects", public_projects_path}. %hr .container .content diff --git a/app/views/notify/closed_merge_request_email.html.haml b/app/views/notify/closed_merge_request_email.html.haml index 809d46f31be..574e8bfef24 100644 --- a/app/views/notify/closed_merge_request_email.html.haml +++ b/app/views/notify/closed_merge_request_email.html.haml @@ -1,2 +1,2 @@ %p - = "Merge Request !#{@merge_request.iid} was closed by #{@updated_by.name}" + = "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}" diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml index ee434ec8cb2..d6b76e906c5 100644 --- a/app/views/notify/closed_merge_request_email.text.haml +++ b/app/views/notify/closed_merge_request_email.text.haml @@ -1,4 +1,4 @@ -= "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)} diff --git a/app/views/notify/merged_merge_request_email.html.haml b/app/views/notify/merged_merge_request_email.html.haml index 0c62d439aed..6762fae7f64 100644 --- a/app/views/notify/merged_merge_request_email.html.haml +++ b/app/views/notify/merged_merge_request_email.html.haml @@ -1,2 +1,2 @@ %p - = "Merge Request !#{@merge_request.iid} was merged" + = "Merge Request ##{@merge_request.iid} was merged" diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml index 550f677fed4..360da60bc3f 100644 --- a/app/views/notify/merged_merge_request_email.text.haml +++ b/app/views/notify/merged_merge_request_email.text.haml @@ -1,4 +1,4 @@ -= "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)} diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml index b8d7fbeb046..a15b8efe1f7 100644 --- a/app/views/notify/repository_push_email.text.haml +++ b/app/views/notify/repository_push_email.text.haml @@ -17,7 +17,7 @@ Changes: - else = diff.new_path || diff.old_path \===================================== - = diff.diff + != diff.diff \ - if @compare.timeout Huge diff. To prevent performance issues it was hidden diff --git a/app/views/projects/commits/_parallel_view.html.haml b/app/views/projects/commits/_parallel_view.html.haml index 5b60ab80ba4..80f5be98f2f 100644 --- a/app/views/projects/commits/_parallel_view.html.haml +++ b/app/views/projects/commits/_parallel_view.html.haml @@ -2,54 +2,37 @@ - old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file) - num_lines = old_lines.length -%div.text-file-parallel - %div.diff-side.diff-side-left - %table - - old_lines.each do |line| +%div.text-file + %table + - num_lines.times do |index| + - 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()); - }); diff --git a/app/views/projects/edit_tree/_diff.html.haml b/app/views/projects/edit_tree/_diff.html.haml new file mode 100644 index 00000000000..cf044feb9a4 --- /dev/null +++ b/app/views/projects/edit_tree/_diff.html.haml @@ -0,0 +1,13 @@ +%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" ? " " : line_old), "##{line_code}", id: line_code + %td.new_line= link_to raw(type == "old" ? " " : 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 --git a/app/views/projects/edit_tree/preview.html.haml b/app/views/projects/edit_tree/preview.html.haml new file mode 100644 index 00000000000..fc6d3bfbc24 --- /dev/null +++ b/app/views/projects/edit_tree/preview.html.haml @@ -0,0 +1,26 @@ +.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" ? " " : line_old), "##{line_code}", id: line_code + %td.new_line= link_to raw(type == "old" ? " " : 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. diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml index 3f2e98f3a7f..48babb43aaf 100644 --- a/app/views/projects/edit_tree/show.html.haml +++ b/app/views/projects/edit_tree/show.html.haml @@ -1,8 +1,11 @@ %h3.page-title Edit mode .file-editor = form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do - .file-holder + .file-holder.file .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 %span.file_name = @path @@ -13,7 +16,8 @@ .btn-group.tree-btn-group = link_to "Cancel", @after_edit_path, class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message } .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 = label_tag 'commit_message', class: "control-label" do @@ -45,3 +49,28 @@ $("#file-content").val(editor.getValue()); $(".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() + } + }) diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml index aae101cf40f..425dcb45ddf 100644 --- a/app/views/projects/issues/_issue_context.html.haml +++ b/app/views/projects/issues/_issue_context.html.haml @@ -1,22 +1,24 @@ = form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f| - %strong.append-right-10 - Assignee: + .row + .col-md-6 + %strong.append-right-10 + Assignee: - - 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) - - elsif issue.assignee - = link_to_member(@project, @issue.assignee) - - else - None + - 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) + - elsif issue.assignee + = link_to_member(@project, @issue.assignee) + - else + None - .pull-right - %strong.append-right-10 - Milestone: - - if can?(current_user, :modify_issue, @issue) - = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone (none):" }, {class: 'select2 select2-compact'}) - = hidden_field_tag :issue_context - = f.submit class: 'btn' - - elsif issue.milestone - = link_to issue.milestone.title, project_milestone_path - - else - None + .col-md-6.text-right + %strong.append-right-10 + Milestone: + - if can?(current_user, :modify_issue, @issue) + = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'}) + = hidden_field_tag :issue_context + = f.submit class: 'btn' + - elsif issue.milestone + = link_to issue.milestone.title, project_milestone_path + - else + None diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 124eb53571d..b6d3a8edf4d 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,7 +1,7 @@ %h3.page-title Issue ##{@issue.iid} - %span.pull-right + %span.pull-right.issue-btn-group - 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 %i.icon-plus @@ -16,28 +16,29 @@ %i.icon-edit Edit -.votes-holder - #votes= render 'votes/votes_block', votable: @issue +.clearfix + .votes-holder + #votes= render 'votes/votes_block', votable: @issue -.back-link - = link_to project_issues_path(@project) do - ← To issues list - %span.milestone-nav-link - - if @issue.milestone - | - %span.light Milestone - = link_to project_milestone_path(@project, @issue.milestone) do - = @issue.milestone.title + .back-link + = link_to project_issues_path(@project) do + ← To issues list + %span.milestone-nav-link + - if @issue.milestone + | + %span.light Milestone + = link_to project_milestone_path(@project, @issue.milestone) do + = @issue.milestone.title .issue-box{ class: issue_box_class(@issue) } - .state - %span.state-label + .state.clearfix + .state-label.col-sm-2.col-xs-12 - if @issue.closed? Closed - else 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)} %h4.title diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 0fe2d1d9801..ddff3dbead8 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -14,33 +14,6 @@ - @merge_request.errors.full_messages.each do |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 - - = 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 - - = 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 .form-group = f.label :title, class: 'control-label' do @@ -51,6 +24,23 @@ .col-sm-10 = 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'}. + %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) + + = 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 - if @merge_request.new_record? @@ -66,20 +56,7 @@ :javascript disableButtonIfEmptyField("#merge_request_title", ".btn-save"); - - 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() }); - }); - target_branch.on("change", function() { - $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() }); + $('.assign-to-me-link').on('click', function(e){ + $('#merge_request_assignee_id').val("#{current_user.id}").trigger("change"); + e.preventDefault(); }); diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 980ac126742..d1cab89a35c 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -11,13 +11,9 @@ - if merge_request.for_fork? %span.light #{merge_request.source_project_namespace}: - = merge_request.source_branch - %i.icon-angle-right.light - = merge_request.target_branch - - else - = merge_request.source_branch - %i.icon-angle-right.light - = merge_request.target_branch + = truncate merge_request.source_branch, length: 25 + %i.icon-angle-right.light + = merge_request.target_branch .merge-request-info - if merge_request.author authored by #{link_to_member(merge_request.source_project, merge_request.author)} diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml new file mode 100644 index 00000000000..a8b774a3cd1 --- /dev/null +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -0,0 +1,84 @@ +%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? }) + + = 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? }) + + = 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 + diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml new file mode 100644 index 00000000000..b5479be708b --- /dev/null +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -0,0 +1,82 @@ +%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) + + = 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(); + }); diff --git a/app/views/projects/merge_requests/branch_from.js.haml b/app/views/projects/merge_requests/branch_from.js.haml index 693c2057a0f..8372afa61b5 100644 --- a/app/views/projects/merge_requests/branch_from.js.haml +++ b/app/views/projects/merge_requests/branch_from.js.haml @@ -1,7 +1,2 @@ :plain $(".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}"); - } diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml index 8ee0e1a8d46..c24e5916721 100644 --- a/app/views/projects/merge_requests/new.html.haml +++ b/app/views/projects/merge_requests/new.html.haml @@ -1,3 +1,4 @@ -%h3.page-title New Merge Request -%hr -= render 'form' +- if @commits.present? + = render 'new_submit' +- else + = render 'new_compare' diff --git a/app/views/projects/merge_requests/show/_context.html.haml b/app/views/projects/merge_requests/show/_context.html.haml index 2bd850426a9..5c6734fd24b 100644 --- a/app/views/projects/merge_requests/show/_context.html.haml +++ b/app/views/projects/merge_requests/show/_context.html.haml @@ -1,22 +1,24 @@ = form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f| - %strong.append-right-10 - Assignee: + .row + .col-md-6 + %strong.append-right-10 + Assignee: - - 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) - - elsif merge_request.assignee - = link_to_member(@project, @merge_request.assignee) - - else - None + - 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) + - elsif merge_request.assignee + = link_to_member(@project, @merge_request.assignee) + - else + None - .pull-right - %strong.append-right-10 - Milestone: - - 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'}) - = hidden_field_tag :merge_request_context - = f.submit class: 'btn' - - elsif merge_request.milestone - = link_to merge_request.milestone.title, project_milestone_path - - else - None + .col-md-6.text-right + %strong.append-right-10 + Milestone: + - if can?(current_user, :modify_merge_request, @merge_request) + = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact'}) + = hidden_field_tag :merge_request_context + = f.submit class: 'btn' + - elsif merge_request.milestone + = link_to merge_request.milestone.title, project_milestone_path + - else + None diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 8855982a2e7..435e916c6dc 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -1,6 +1,6 @@ .issue-box{ class: issue_box_class(@merge_request) } - .state - %span.state-label + .state.clearfix + %span.state-label.col-sm-2.col-xs-12 - if @merge_request.merged? Merged - elsif @merge_request.closed? @@ -8,7 +8,7 @@ - else 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)} %h4.title diff --git a/app/views/projects/merge_requests/show/_mr_ci.html.haml b/app/views/projects/merge_requests/show/_mr_ci.html.haml index c175d2f6b40..507a9e507f1 100644 --- a/app/views/projects/merge_requests/show/_mr_ci.html.haml +++ b/app/views/projects/merge_requests/show/_mr_ci.html.haml @@ -26,4 +26,4 @@ .ci_widget.ci-error{style: "display:none"} %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. diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index 7676fc137c7..2c905413bc3 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -1,7 +1,7 @@ %h3.page-title = "Merge Request ##{@merge_request.iid}" - %span.pull-right + %span.pull-right.issue-btn-group - if can?(current_user, :modify_merge_request, @merge_request) - if @merge_request.open? .btn-group.pull-left @@ -39,4 +39,4 @@ - else %span= @merge_request.source_branch → - %spanh= @merge_request.target_branch + %span= @merge_request.target_branch diff --git a/app/views/projects/merge_requests/show/_state_widget.html.haml b/app/views/projects/merge_requests/show/_state_widget.html.haml index c9ecbceaf54..80fe540489b 100644 --- a/app/views/projects/merge_requests/show/_state_widget.html.haml +++ b/app/views/projects/merge_requests/show/_state_widget.html.haml @@ -21,14 +21,6 @@ #{time_ago_with_tooltip(@merge_request.merge_event.created_at)} = 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? %h4 Nothing to merge %p @@ -38,3 +30,12 @@ %span.label-branch #{@merge_request.target_branch} %br 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) + diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 06cf9946784..5c5df46d33d 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -100,7 +100,7 @@ %ul.bordered-list - @users.each do |user| %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" %strong= truncate(user.name, lenght: 40) %br diff --git a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml index 2012aa021b9..399ce30d1a9 100644 --- a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml +++ b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml @@ -1,34 +1,33 @@ - note1 = notes1.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 - -# Check if line want not changed since comment was left - /- if !defined?(line1) || line1 == note1.diff_line - if note1 + %td.notes_line + %span.btn.disabled + %i.icon-comment + = notes1.count %td.notes_content %ul.notes{ rel: note1.discussion_id } = render notes1 + = render "projects/notes/discussion_reply_button", note: note1 - %td.notes_line2 - %span.btn.disabled.parallel-comment - %i.icon-comment - = notes1.count - else %td= "" %td= "" - %td= "" - - -# Check if line want not changed since comment was left - /- if !defined?(line2) || line2 == note2.diff_line - if note2 %td.notes_line - %span.btn.disabled.parallel-comment + %span.btn.disabled %i.icon-comment = notes2.count %td.notes_content %ul.notes{ rel: note2.discussion_id } = render notes2 + = render "projects/notes/discussion_reply_button", note: note2 - else %td= "" %td= "" + diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml index 3bd592e3982..052661962e4 100644 --- a/app/views/projects/notes/_notes_with_form.html.haml +++ b/app/views/projects/notes/_notes_with_form.html.haml @@ -7,4 +7,4 @@ = render "projects/notes/form" :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}) diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index 8cb7fa8aa0b..1ce292a02df 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -9,6 +9,6 @@ %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) %p.hint - Please don't use spaces and slashes + Please don't use spaces. .modal-footer = link_to 'Build', '#', class: 'build-new-wiki btn btn-create' diff --git a/bin/pkgr_before_precompile.sh b/bin/pkgr_before_precompile.sh new file mode 100755 index 00000000000..283abb6a0cd --- /dev/null +++ b/bin/pkgr_before_precompile.sh @@ -0,0 +1,23 @@ +#!/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 diff --git a/config/environments/production.rb b/config/environments/production.rb index ad3c03d8fc9..47f7e17aeb6 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -53,7 +53,7 @@ Gitlab::Application.configure do else "redis://localhost:6379" 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 # config.action_controller.asset_host = "http://assets.example.com" diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 64fc02fe8c2..3774910cf96 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -19,6 +19,11 @@ production: &base port: 80 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 # 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. @@ -102,7 +107,7 @@ production: &base # ## :id - Issue id (from commit messages) # 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: # ## :project_id - GitLab project identifier # ## :issues_tracker_id - Project Name or Id in external issue tracker @@ -117,6 +122,7 @@ production: &base ## Gravatar gravatar: 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 # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 59564d9ea33..97f29546404 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -1,5 +1,5 @@ class Settings < Settingslogic - source "#{Rails.root}/config/gitlab.yml" + source ENV.fetch('GITLAB_CONFIG') { "#{Rails.root}/config/gitlab.yml" } namespace Rails.env class << self @@ -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_theme'] = Gitlab::Theme::MARS if Settings.gitlab['default_theme'].nil? Settings.gitlab['host'] ||= 'localhost' +Settings.gitlab['ssh_host'] ||= Settings.gitlab.host Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80 Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || '' @@ -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['upload_pack'] = true if Settings.gitlab_shell['upload_pack'].nil? 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_user'] ||= Settings.gitlab.user Settings.gitlab_shell['owner_group'] ||= Settings.gitlab.user diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb index 6875fa74edd..d0065b63e54 100644 --- a/config/initializers/carrierwave.rb +++ b/config/initializers/carrierwave.rb @@ -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. # when fog_public is false and provider is AWS or Google, defaults to 600 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 diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 50669ece7a8..d5cb110e881 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -223,6 +223,7 @@ Devise.setup do |config| method: Gitlab.config.ldap['method'], bind_dn: Gitlab.config.ldap['bind_dn'], password: Gitlab.config.ldap['password'], + filter: Gitlab.config.ldap['user_filter'], name_proc: email_stripping_proc end @@ -244,4 +245,4 @@ Devise.setup do |config| config.omniauth provider['name'].to_sym, *provider_arguments end -end +end
\ No newline at end of file diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb index 98400290113..62a54bc8c63 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_token.rb @@ -9,7 +9,9 @@ require 'securerandom' def find_secure_token token_file = Rails.root.join('.secret') - if File.exist? token_file + if ENV.key?('SECRET_KEY_BASE') + ENV['SECRET_KEY_BASE'] + elsif File.exist? token_file # Use the existing token. File.read(token_file).chomp else diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index f80b67a554b..5fe5270236b 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -2,7 +2,7 @@ Gitlab::Application.config.session_store( :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', secure: Gitlab.config.gitlab.https, httponly: true, diff --git a/config/routes.rb b/config/routes.rb index f23542cc893..7a33686b810 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -187,7 +187,9 @@ Gitlab::Application.routes.draw do resources :blob, only: [:show, :destroy], constraints: {id: /.+/} resources :raw, only: [:show], constraints: {id: /.+/} 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 :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/} @@ -204,7 +206,7 @@ Gitlab::Application.routes.draw do 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 get :pages put ':id' => 'wikis#update' diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index 9303ab93300..164bb637809 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -40,7 +40,8 @@ Gitlab::Seeder.quiet do import_url: url, namespace_id: group.id, 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 diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb index cb08a7c2537..62fd0d84ea3 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/10_merge_requests.rb @@ -1,45 +1,33 @@ Gitlab::Seeder.quiet do - (1..100).each do |i| - # Random Project - project = Project.all.sample - - # Random user - user = project.team.users.sample - - next unless user - - next if project.empty_repo? - - branches = project.repository.branch_names.sample(2) - - next if branches.uniq.size < 2 - - user_id = user.id - - Gitlab::Seeder.by_user(user) do - MergeRequest.seed(:id, [{ - id: i, - source_branch: branches.first, - target_branch: branches.last, - source_project_id: project.id, - target_project_id: project.id, - author_id: user_id, - assignee_id: user_id, - milestone: project.milestones.sample, - title: Faker::Lorem.sentence(6) - }]) + Project.all.reject(&:empty_repo?).each do |project| + branches = project.repository.branch_names + + branches.each do |branch_name| + break if branches.size < 2 + source_branch = branches.pop + target_branch = branches.pop + + # Random user + user = project.team.users.sample + next unless user + + params = { + source_branch: source_branch, + target_branch: target_branch, + title: Faker::Lorem.sentence(6), + description: Faker::Lorem.sentences(3).join(" ") + } + + merge_request = MergeRequests::CreateService.new(project, user, params).execute + + if merge_request.valid? + merge_request.assignee = user + merge_request.milestone = project.milestones.sample + merge_request.save + print '.' + else + print 'F' + end end - print('.') 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 diff --git a/db/migrate/20140415124820_limits_to_mysql.rb b/db/migrate/20140415124820_limits_to_mysql.rb new file mode 100644 index 00000000000..3f6e62617c5 --- /dev/null +++ b/db/migrate/20140415124820_limits_to_mysql.rb @@ -0,0 +1 @@ +require_relative 'limits_to_mysql' diff --git a/db/migrate/20140416074002_add_index_on_iid.rb b/db/migrate/20140416074002_add_index_on_iid.rb new file mode 100644 index 00000000000..85269e2a03b --- /dev/null +++ b/db/migrate/20140416074002_add_index_on_iid.rb @@ -0,0 +1,32 @@ +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 diff --git a/db/migrate/20140416185734_index_on_current_sign_in_at.rb b/db/migrate/20140416185734_index_on_current_sign_in_at.rb new file mode 100644 index 00000000000..0bf80ce154a --- /dev/null +++ b/db/migrate/20140416185734_index_on_current_sign_in_at.rb @@ -0,0 +1,5 @@ +class IndexOnCurrentSignInAt < ActiveRecord::Migration + def change + add_index :users, :current_sign_in_at + end +end diff --git a/db/migrate/20140428105831_add_notes_index_updated_at.rb b/db/migrate/20140428105831_add_notes_index_updated_at.rb new file mode 100644 index 00000000000..6c25570f128 --- /dev/null +++ b/db/migrate/20140428105831_add_notes_index_updated_at.rb @@ -0,0 +1,5 @@ +class AddNotesIndexUpdatedAt < ActiveRecord::Migration + def change + add_index :notes, :updated_at + end +end diff --git a/db/migrate/20140502115131_add_repo_size_to_db.rb b/db/migrate/20140502115131_add_repo_size_to_db.rb new file mode 100644 index 00000000000..7361d1a9440 --- /dev/null +++ b/db/migrate/20140502115131_add_repo_size_to_db.rb @@ -0,0 +1,5 @@ +class AddRepoSizeToDb < ActiveRecord::Migration + def change + add_column :projects, :repository_size, :float, default: 0 + end +end diff --git a/db/migrate/20140502125220_migrate_repo_size.rb b/db/migrate/20140502125220_migrate_repo_size.rb new file mode 100644 index 00000000000..eed6d366814 --- /dev/null +++ b/db/migrate/20140502125220_migrate_repo_size.rb @@ -0,0 +1,21 @@ +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 diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb new file mode 100644 index 00000000000..2b7afae6d7c --- /dev/null +++ b/db/migrate/limits_to_mysql.rb @@ -0,0 +1,10 @@ +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 diff --git a/db/schema.rb b/db/schema.rb index 265d556bd27..93837337afc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # 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 enable_extension "plpgsql" @@ -93,6 +93,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do 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", ["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", ["title"], name: "index_issues_on_title", using: :btree @@ -143,6 +144,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_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_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 create_table "milestones", force: true do |t| @@ -157,6 +159,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do end 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 create_table "namespaces", force: true do |t| @@ -197,6 +200,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do 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"], name: "index_notes_on_project_id", using: :btree + add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree create_table "projects", force: true do |t| t.string "name" @@ -218,6 +222,7 @@ ActiveRecord::Schema.define(version: 20140414131055) do t.integer "visibility_level", default: 0, null: false t.boolean "archived", default: false, null: false t.string "import_status" + t.float "repository_size", default: 0.0 end add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree @@ -319,7 +324,6 @@ ActiveRecord::Schema.define(version: 20140414131055) do t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" - t.datetime "last_credential_check_at" t.string "avatar" t.string "confirmation_token" t.datetime "confirmed_at" @@ -327,11 +331,13 @@ ActiveRecord::Schema.define(version: 20140414131055) do t.string "unconfirmed_email" t.boolean "hide_no_ssh_key", default: false t.string "website_url", default: "", null: false + t.datetime "last_credential_check_at" end 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", ["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", ["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 diff --git a/doc/README.md b/doc/README.md index 6c8fe3a96ce..b73d7bb38e1 100644 --- a/doc/README.md +++ b/doc/README.md @@ -6,7 +6,7 @@ + [Public access](public_access/public_access.md) Learn how you can allow public and internal access to a project. + [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects. + [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. -+ [Workflow](workflow/workflow.md) Learn how to use Git and GitLab together. ++ [Workflow](workflow/README.md) Learn how to use Git and GitLab together. **Administrator documentation** diff --git a/doc/api/README.md b/doc/api/README.md index b1740f35792..4c40589fd4f 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -13,7 +13,7 @@ + [Merge Requests](merge_requests.md) + [Issues](issues.md) + [Milestones](milestones.md) -+ [Notes](notes.md) ++ [Notes](notes.md) (comments) + [Deploy Keys](deploy_keys.md) + [System Hooks](system_hooks.md) + [Groups](groups.md) @@ -21,9 +21,11 @@ ## Clients + [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 + [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python + [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java ++ [node-gitlab](https://github.com/moul/node-gitlab) - Node.js ## Introduction diff --git a/doc/api/issues.md b/doc/api/issues.md index 823b72f5b0c..d18506f9ce6 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -193,3 +193,7 @@ Parameters: + `id` (required) - The project ID + `issue_id` (required) - The ID of the issue + +## Comments on issues + +Comments are done via the notes resource. diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 4e864ae1078..d5b106729c9 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -105,10 +105,11 @@ POST /projects/:id/merge_requests Parameters: + `id` (required) - The ID of a project -+ `source_branch` (required) - The source branch -+ `target_branch` (required) - The target branch -+ `assignee_id` (optional) - Assignee user ID -+ `title` (required) - Title of MR ++ `source_branch` (required) - The source branch ++ `target_branch` (required) - The target branch ++ `assignee_id` (optional) - Assignee user ID ++ `title` (required) - Title of MR ++ `target_project_id` (optional) - The target project (numeric id) ```json { @@ -257,3 +258,7 @@ Parameters: } ] ``` + +## Comments on issues + +Comments are done via the notes resource. diff --git a/doc/api/notes.md b/doc/api/notes.md index b15ebdd2bac..e9ad6e00c73 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -1,3 +1,5 @@ +Notes can be wall notes or comments on snippets, issues or merge requests. + ## Wall ### List project wall notes diff --git a/doc/api/users.md b/doc/api/users.md index 2d5dedb3a39..2b927c30777 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -220,6 +220,18 @@ Parameters: + **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 @@ -286,3 +298,18 @@ Parameters: + `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. + diff --git a/doc/development/README.md b/doc/development/README.md index aa59eb2c3e1..eb88b6c860f 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -1,2 +1,5 @@ -+ [Architecture](architecture.md) -+ [Shell commands](shell_commands.md) +## Development + ++ [Architecture](architecture.md) of GitLab ++ [Shell commands](shell_commands.md) in the GitLab codebase ++ [Rake tasks](rake_tasks.md) for development diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md new file mode 100644 index 00000000000..9e75b3a6275 --- /dev/null +++ b/doc/development/rake_tasks.md @@ -0,0 +1,25 @@ +# 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 +``` diff --git a/doc/install/installation.md b/doc/install/installation.md index fb3faa536f0..eea5c763fcd 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -1,10 +1,10 @@ # 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).  -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 @@ -27,10 +27,9 @@ The GitLab installation consists of setting up the following components: 1. Packages / Dependencies 2. Ruby 3. System Users -4. GitLab shell -5. Database -6. GitLab -7. Nginx +4. Database +5. GitLab +6. Nginx # 1. Packages / Dependencies @@ -119,32 +118,10 @@ Create a `git` user for Gitlab: sudo adduser --disabled-login --gecos 'GitLab' git - -# 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.1 - - 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 +# 4. Database 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 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 sudo -u postgres psql -d template1 # Create a user for GitLab. - template1=# CREATE USER git; + template1=# CREATE USER git CREATEDB; # Create the GitLab production database & grant all privileges on database template1=# CREATE DATABASE gitlabhq_production OWNER git; @@ -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 -# 6. GitLab +# 5. GitLab # We'll install GitLab into home directory of the user "git" cd /home/git @@ -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:' +## 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 @@ -313,7 +302,8 @@ Check if GitLab and its environment are configured correctly: # or sudo /etc/init.d/gitlab restart -# 7. Nginx + +# 6. Nginx **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 diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 56a2a5efeb0..dc21a7e55ab 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -43,18 +43,24 @@ We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/)) but GitLab ## CPU -- 1 core works for under 100 users but the responsiveness might suffer -- **2 cores** is the **recommended** number of cores and supports up to 100 users -- 4 cores supports up to 1,000 users -- 8 cores supports up to 10,000 users +- 1 core works supports up to 100 users but the application will not be responsive +- **2 cores** is the **recommended** number of cores and supports up to 500 users +- 4 cores supports up to 2,000 users +- 8 cores supports up to 5,000 users +- 16 cores supports up to 10,0000 users +- 32 cores supports up to 20,0000 users +- 64 cores supports up to 40,0000 users ## Memory -- 512MB is too little memory, GitLab will be very slow and you will need 250MB of swap -- 768MB is the minimal memory size but we advise against this +- 512MB is the abolute minimum, you need 256MB of swap, you can configure only one slow unicorn worker, only ssh access will work, we do not recommend this - 1GB supports up to 100 users (with individual repositories under 250MB, otherwise git memory usage necessitates using swap space) -- **2GB** is the **recommended** memory size and supports up to 1,000 users -- 4GB supports up to 10,000 users +- **2GB** is the **recommended** memory size and supports up to 500 users +- 4GB supports up to 2,000 users +- 8GB supports up to 5,000 users +- 16GB supports up to 10,000 users +- 32GB supports up to 20,000 users +- 64GB supports up to 40,000 users ## Storage @@ -68,11 +74,14 @@ 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. +## Database + +If you want to run the database separately, the **recommended** database size is **1 MB per user** # Supported webbrowsers - Chrome (Latest stable version) - Firefox (Latest released version) -- Safari 7+ (Know problem: required fields in html5 do not work) +- Safari 7+ (known problem: required fields in html5 do not work) - Opera (Latest released version) -- IE 10+ +- IE 10+
\ No newline at end of file diff --git a/doc/integration/README.md b/doc/integration/README.md index 3e8d329d558..8318113ce92 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -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. + [LDAP](ldap.md) Set up sign in via LDAP + [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, and Google via OAuth. ++ [Slack](slack.md) Integrate with the Slack chat service diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index a4491432caf..84a5a8e8c28 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -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 /> + ``` ## OmniAuth settings omniauth: @@ -50,6 +51,7 @@ Before configuring individual OmniAuth providers there are a few global settings # app_secret: 'YOUR APP SECRET', # args: { scope: 'user:email' } } ``` + 3. Change `enabled` to `true`. 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 diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md index ac4bdefddd5..9be6423f667 100644 --- a/doc/permissions/permissions.md +++ b/doc/permissions/permissions.md @@ -27,7 +27,7 @@ If a user is a GitLab administrator they receive all permissions. |Remove protected branches| |||✓|✓| |Edit project| |||✓|✓| |Add Deploy Keys to project| |||✓|✓| -|Confiure Project Hooks| |||✓|✓| +|Configure Project Hooks| |||✓|✓| |Switch visibility level| ||||✓| |Transfer project to another namespace| ||||✓| |Remove project| ||||✓| diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md index bf9d2784aff..76d83e6f3b6 100644 --- a/doc/public_access/public_access.md +++ b/doc/public_access/public_access.md @@ -4,7 +4,7 @@ Internal projects will only be available to authenticated users. #### Public projects 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. #### Internal projects diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md index 9aa80af12cc..6be24f0102a 100644 --- a/doc/raketasks/README.md +++ b/doc/raketasks/README.md @@ -1,6 +1,7 @@ + [Backup restore](backup_restore.md) + [Cleanup](cleanup.md) + [Features](features.md) -+ [Maintenance](maintenance.md) ++ [Maintenance](maintenance.md) and self-checks + [User management](user_management.md) + [Web hooks](web_hooks.md) ++ [Import](import.md) of git repositories in bulk diff --git a/doc/raketasks/import.md b/doc/raketasks/import.md new file mode 100644 index 00000000000..e11328dc5ce --- /dev/null +++ b/doc/raketasks/import.md @@ -0,0 +1,28 @@ +### 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) +[...] +``` diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md index 3033d8c46b4..2783c4153c5 100644 --- a/doc/raketasks/maintenance.md +++ b/doc/raketasks/maintenance.md @@ -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 ``` - -### 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) -[...] -``` diff --git a/doc/release/monthly.md b/doc/release/monthly.md index 9dbe62f1210..514d73517b2 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -1,15 +1,36 @@ -# Things to do when creating new monthly minor or major release -NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update). +# Monthly Release +NOTE: This is a guide for GitLab developers. -## Install guide up to date? +# **15th - Code Freeze & Release Manager** -* References correct GitLab branch `x-x-stable` and correct GitLab shell tag? +### **1. Stop merging in code, except for important bugfixes** -## Make upgrade guide +### **2. Release Manager** -### From x.x to x.x +A release manager is selected that coordinates the entire release of this version. The release manager has to make sure all the steps below are done and delegated where necessary. This person should also make sure this document is kept up to date and issues are created and updated. -#### 0. Any major changes? Database updates? Web server change? File structure changes? +# **18th - Releasing RC1** + +The RC1 release comes with the task to update the installation and upgrade docs. Be mindful that there might already be merge requests for this on GitLab or GitHub. + +### **1. Create an issue for RC1 release** + +### **2. Update the installation guide** + +1. Check if it references the correct branch `x-x-stable` (doesn't exist yet, but that is okay) +2. Check the [GitLab Shell version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L782) +3. Check the [Git version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L794) +4. There might be other changes. Ask around. + +### **3. Create an update guide** + +It's best to copy paste the previous guide and make changes where necessary. The typical steps are listed below with any points you should specifically look at. + +#### 0. Any major changes? +List any major changes here, so the user is aware of them before starting to upgrade. For instance: +- Database updates +- Web server changes +- File structure changes #### 1. Make backup @@ -17,9 +38,9 @@ NOTE: This is a guide for GitLab developers. If you are trying to install GitLab #### 3. Do users need to update dependencies like `git`? -- Check the [GitLab Shell version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L782) +- Check if the [GitLab Shell version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L782) changed since the last release. -- Check the [Git version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L794) +- Check if the [Git version](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/tasks/gitlab/check.rake#L794) changed since the last release. #### 4. Get latest code @@ -29,7 +50,7 @@ NOTE: This is a guide for GitLab developers. If you are trying to install GitLab #### 7. Any config files updated since last release? -Check if any of these changed since last release (~22nd of last month depending on when last release branch was created): +Check if any of these changed since last release: * https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/nginx/gitlab * https://gitlab.com/gitlab-org/gitlab-shell/commits/master/config.yml.example @@ -40,13 +61,14 @@ Check if any of these changed since last release (~22nd of last month depending #### 8. Need to update init script? -Check if changed since last release (~22nd of last month depending on when last release branch was created): https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/init.d/gitlab +Check if the init.d/gitlab script changed since last release: https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/init.d/gitlab #### 9. Start application #### 10. Check application status -## Make sure the code quality indicatiors are good +### **4. Code quality indicatiors** +Make sure the code quality indicators are green / good. * [](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch) @@ -58,32 +80,88 @@ Check if changed since last release (~22nd of last month depending on when last * [](https://coveralls.io/r/gitlabhq/gitlabhq) -## Make a release branch +### **5. Set VERSION** -After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked. The days of the month are approximately as follows: +Set VERSION tot x.x.0.rc1 -* 1-7th: official merge window (see contributing guide) -* 8-14th: work on bugfixes, sponsored features and GitLab EE -* 15th: code freeze (stop merging into master except essential bugfixes) -* 18th: release candidate 1 (VERSION x.x.0.rc1, annotated tag and tweet about x.x.0.rc1, release on GitLab Cloud) -* 20st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems) -* 22nd: release (VERSION x.x.0, create x-x-stable branch, annotated tag tag, blog and tweet) -* 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 -# Write a blog post +### **6. Tag** -* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code. -* Select and thank the the Most Valuable Person (MVP) of this release. -* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible. +Create an annotated tag that points to the version change commit. +``` +git tag -a vx.x.0.rc1 -m 'Version x.x.0.rc1' +``` + +### **7. Tweet** + +Tweet about the RC release: + +> GitLab x.x.x.rc1 is out. This is a release candidate intended for testing only. Please let us know if you find regressions. + +### **8. Update Cloud** + +Merge the RC1 code into Cloud. Once the build is green, deploy in the morning. + +It is important to do this as soon as possible, so we can catch any errors before we release the full version. + + +# **22nd - Release CE and EE** -# Tweet +For GitLab EE, append -ee to the branches and tags. -Send out a tweet to share the good news with the world. For a major/minor release, list the features in short and link to the blog post. +`x-x-stable-ee` -For a RC, make sure to explain what a RC is. +`v.x.x.0-ee` + +### **1. Create x-x-stable branch and push to the repositories** + +``` +git checkout master +git pull +git checkout -b x-x-stable +git push <remote> x-x-stable +``` + +### **2. Build the Omnibus packages** +[Follow this guide](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/release.md) + +### **3. QA** +Use the omnibus packages to test using [this guide](https://dev.gitlab.org/gitlab/gitlab-ee/blob/master/doc/release/manual_testing.md) + + +### **4. Fix anything coming out of the QA** + +### **5. Set VERSION to x.x.0** + +### **6. Create annotated tag vx.x.0** +``` +git tag -a vx.x.0 -m 'Version x.x.0' +``` + +### **7. Push VERSION + Tag to master, merge into x-x-stable** +``` +git push origin master +``` + +Next, merge the VERSION into the x-x-stable branch. + +### **8. Push to remotes** + +For GitLab CE, push to dev, GitLab.com and GitHub. + +For GitLab EE, push to the subscribers repo. + +NOTE: You might not have the rights to push to master on dev. Ask Dmitriy. + +### **9. Publish blog for new release** +* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code. +* Select and thank the the Most Valuable Person (MVP) of this release. +* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible. -A patch release tweet should specify the fixes it brings and link to the corresponding blog post. +### **10. Tweet to blog** +Send out a tweet to share the good news with the world. List the features in short and link to the blog post. +# **23rd - Optional Patch Release** +# **25th - Release GitLab CI** diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index 76ca2a59911..5c8daf466ab 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -16,6 +16,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser "path": "stormcloud", "path_with_namespace": "jsmith/stormcloud", "project_id": 74, + "project_visibility": "private", } ``` @@ -31,6 +32,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser "path": "underscore", "path_with_namespace": "jsmith/underscore", "project_id": 73, + "project_visibility": "internal", } ``` @@ -38,14 +40,15 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser ```json { - "created_at": "2012-07-21T07:30:56Z", - "event_name": "user_add_to_team", - "project_access": "Master", - "project_id": 74, - "project_name": "StoreCloud", - "project_path": "storecloud", - "user_email": "johnsmith@gmail.com", - "user_name": "John Smith", + "created_at": "2012-07-21T07:30:56Z", + "event_name": "user_add_to_team", + "project_access": "Master", + "project_id": 74, + "project_name": "StoreCloud", + "project_path": "storecloud", + "user_email": "johnsmith@gmail.com", + "user_name": "John Smith", + "project_visibility": "private", } ``` @@ -53,14 +56,15 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser ```json { - "created_at": "2012-07-21T07:30:56Z", - "event_name": "user_remove_from_team", - "project_access": "Master", - "project_id": 74, - "project_name": "StoreCloud", - "project_path": "storecloud", - "user_email": "johnsmith@gmail.com", - "user_name": "John Smith", + "created_at": "2012-07-21T07:30:56Z", + "event_name": "user_remove_from_team", + "project_access": "Master", + "project_id": 74, + "project_name": "StoreCloud", + "project_path": "storecloud", + "user_email": "johnsmith@gmail.com", + "user_name": "John Smith", + "project_visibility": "private", } ``` diff --git a/doc/update/6.0-to-6.7.md b/doc/update/6.0-to-6.8.md index aa1b388fa9a..5c71e99aa52 100644 --- a/doc/update/6.0-to-6.7.md +++ b/doc/update/6.0-to-6.8.md @@ -1,4 +1,4 @@ -# From 6.0 to 6.7 +# From 6.0 to 6.8 # 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. @@ -33,7 +33,7 @@ sudo -u git -H git fetch --all For Gitlab Community Edition: ```bash -sudo -u git -H git checkout 6-7-stable +sudo -u git -H git checkout 6-8-stable ``` OR @@ -41,7 +41,7 @@ OR For GitLab Enterprise Edition: ```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 ```bash cd /home/git/gitlab-shell 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. @@ -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: ``` -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/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/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` 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 ```bash diff --git a/doc/update/6.7-to-6.8.md b/doc/update/6.7-to-6.8.md index 57918bca82d..457433c6482 100644 --- a/doc/update/6.7-to-6.8.md +++ b/doc/update/6.7-to-6.8.md @@ -1,4 +1,4 @@ -# From 6.6 to 6.7 +# From 6.7 to 6.8 ### 0. Backup @@ -9,7 +9,9 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ### 1. Stop server - sudo service gitlab stop +```bash +sudo service gitlab stop +``` ### 2. Get latest code @@ -37,7 +39,7 @@ sudo -u git -H git checkout 6-8-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v1.9.1 +sudo -u git -H git checkout v1.9.3 ``` ### 4. Install libs, migrations, etc. @@ -60,6 +62,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS # Update init.d script 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) sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab @@ -90,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). -### 6. Update Init script - -```bash -sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab -sudo chmod +x /etc/init.d/gitlab -``` - -### 7. Start application +### 6. Start application sudo service gitlab start sudo service nginx restart -### 8. Check application status +### 7. Check application status Check if GitLab and its environment are configured correctly: @@ -114,10 +110,10 @@ To make sure you didn't miss anything run a more thorough check with: If all items are green, then congratulations upgrade is complete! -## Things went south? Revert to previous version (6.6) +## Things went south? Revert to previous version (6.7) ### 1. Revert the code to the previous version -Follow the [`upgrade guide from 6.5 to 6.6`](6.5-to-6.6.md), except for the database migration +Follow the [`upgrade guide from 6.6 to 6.7`](6.6-to-6.7.md), except for the database migration (The backup is already migrated to the previous version) ### 2. Restore from the backup: diff --git a/doc/update/README.md b/doc/update/README.md index 06e3764616f..9ce48a019e8 100644 --- a/doc/update/README.md +++ b/doc/update/README.md @@ -1,5 +1,5 @@ -+ [The indivual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update) -+ [Uprader](upgrader.md) ++ [The individual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update) ++ [Upgrader](upgrader.md) + [Ruby](ruby.md) + [Patch versions](patch_versions.md) + [MySQL to PostgreSQL](mysql_to_postgresql.md) diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index 9a324545eb0..5b9209d7df4 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -1,9 +1,68 @@ -# 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 cd mysql-postgresql-converter mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production python db_converter.py databasename.mysql databasename.psql 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. ``` diff --git a/doc/update/ruby.md b/doc/update/ruby.md index 9d0cafb3f05..e98167f6b66 100644 --- a/doc/update/ruby.md +++ b/doc/update/ruby.md @@ -1,6 +1,6 @@ # Updating Ruby from source -This guide explains how to update Ruby in case you installed it from source according to the instructions in https://gitlab.com/gitlab-org/gitlab-ce/blob/masterdoc/install/installation.md#2-ruby . +This guide explains how to update Ruby in case you installed it from source according to the [instructions](../install/installation.md#2-ruby). ### 1. Look for Ruby versions This guide will only update `/usr/local/bin/ruby`. You can see which Ruby binaries are installed on your system by running: @@ -36,7 +36,7 @@ sudo gem install bundler ``` ### 5. Reinstall GitLab gem bundle -Just to be sure we will reinstall the gems used by GitLab. Note that the `bundle install` command [depends on your choice of database](https://gitlab.com/gitlab-org/gitlab-ce/blob/masterdoc/install/installation.md#install-gems). +Just to be sure we will reinstall the gems used by GitLab. Note that the `bundle install` command [depends on your choice of database](../install/installation.md#install-gems). ```bash cd /home/git/gitlab diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md index fd45154ac82..72a94f67b3c 100644 --- a/doc/update/upgrader.md +++ b/doc/update/upgrader.md @@ -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: - 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 +``` diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index f80891e264d..5ad0c8a138f 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -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. -GitLab will send POST request with data to 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. +You can configure web hooks to listen for specific events like pushes, issues or merge requests. +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. 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 -Triggered when you push to the repository except pushing tags. +Triggered when you push to the repository except when pushing tags. **Request body:** @@ -84,7 +84,7 @@ Triggered when a new issue is created or an existing issue was updated/closed/re #### 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:** diff --git a/doc/workflow/authorization_for_merge_requests.md b/doc/workflow/authorization_for_merge_requests.md new file mode 100644 index 00000000000..4e07d7c04c5 --- /dev/null +++ b/doc/workflow/authorization_for_merge_requests.md @@ -0,0 +1,32 @@ +There are two main ways to have a merge request flow with GitLab: working with protected branches in a single repository, or working with forks of an authoritative project. + +## Protected branch flow + +With the protected branch flow everybody works within the same GitLab project. +The project maintainers get Master access and the regular developers get Developer access. +The maintainers mark the authoritative branches as 'Protected'. +The developers push feature branches to the project and create merge requests to have their feature branches reviewed and merged into one of the protected branches. +Only users with Master access can merge changes into a protected branch. + +### Advantages + +- fewer projects means less clutter +- developers need to consider only one remote repository + +### Disadvantages + +- manual setup of protected branch required for each new project + +## Forking workflow + +With the forking workflow the maintainers get Master access and the regular developers get Reporter access to the authoritative repository, which prohibits them from pushing any changes to it. +Developers create forks of the authoritative project and push their feature branches to their own forks. +To get their changes into master they need to create a merge request across forks. + +### Advantages + +- in an appropriately configured GitLab group, new projects automatically get the required access restrictions for regular developers: fewer manual steps to configure authorization for new projects + +### Disadvantages + +- the project need to keep their forks up to date, which requires more advanced Git skills (managing multiple remotes) diff --git a/features/project/forked_merge_requests.feature b/features/project/forked_merge_requests.feature index 2d94b98c90b..5832b729deb 100644 --- a/features/project/forked_merge_requests.feature +++ b/features/project/forked_merge_requests.feature @@ -30,11 +30,10 @@ Feature: Project Forked Merge Requests Given I visit project "Forked Shop" merge requests page And I click link "New 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 @javascript Scenario: Merge request should target fork repository by default Given I visit project "Forked Shop" merge requests page And I click link "New Merge Request" - Then the target repository should be the original repository
\ No newline at end of file + Then the target repository should be the original repository diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index fd9a2f01a28..a204c3e10c7 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -29,3 +29,13 @@ Feature: Project Browse files Given I click on "Gemfile.lock" file in repo And I click button "edit" 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 + + diff --git a/features/project/wiki.feature b/features/project/wiki.feature index 90eb2b79c66..4a8c771ddac 100644 --- a/features/project/wiki.feature +++ b/features/project/wiki.feature @@ -45,3 +45,20 @@ Feature: Project Wiki And I browse to that Wiki page And I click on the "Pages" button 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 diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index 394acd3fe8f..706c9babcee 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -25,7 +25,6 @@ class Dashboard < Spinach::FeatureSteps find("#merge_request_target_project_id").value.should == @project.id.to_s find("#merge_request_source_branch").value.should == "new_design" find("#merge_request_target_branch").value.should == "master" - find("#merge_request_title").value.should == "New design" end Given 'user with name "John Doe" joined project "Shop"' do diff --git a/features/steps/project/browse_files.rb b/features/steps/project/browse_files.rb index 069086d5eac..7cdd1101ac5 100644 --- a/features/steps/project/browse_files.rb +++ b/features/steps/project/browse_files.rb @@ -41,6 +41,18 @@ class ProjectBrowseFiles < Spinach::FeatureSteps page.evaluate_script('editor.getValue()').should == "GitlabFileEditor" 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 click_link 'new-file-link' end diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index df69cb75437..3c497638d9c 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -53,6 +53,7 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps find(:select, "merge_request_source_branch", {}).value.should == 'master' find(:select, "merge_request_target_branch", {}).value.should == 'stable' + click_button "Compare branches" fill_in "merge_request_title", with: "Merge Request On Forked Project" end @@ -148,29 +149,19 @@ class ProjectForkedMergeRequests < Spinach::FeatureSteps current_path.should == edit_project_merge_request_path(@project, @merge_request) page.should have_content "Edit merge request ##{@merge_request.id}" 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 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" - 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_source_branch", {}).value.should == "" find(:select, "merge_request_target_branch", {}).value.should == "" + click_button "Compare branches" end step 'I should see validation errors' do - page.should have_content "Source branch can't be blank" - page.should have_content "Target branch can't be blank" + page.should have_content "You must select source and target branch" end step 'the target repository should be the original repository' do diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index f42eb6377ce..e0aec699a56 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -61,9 +61,10 @@ class ProjectMergeRequests < Spinach::FeatureSteps end 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 "notes_refactoring", from: "merge_request_target_branch" + click_button "Compare branches" + fill_in "merge_request_title", with: "Wiki Feature" click_button "Submit merge request" end diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index a819ee37d7f..96f2505d24c 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -86,6 +86,47 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps page.should have_content @page.title 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 ", :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 @project_wiki = ProjectWiki.new(project, current_user) end diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 953c6100f8b..d54f9371fbe 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -24,7 +24,7 @@ module API # branch (required) - The name of the branch # Example Request: # 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] } not_found!("Branch does not exist") if @branch.nil? present @branch, with: Entities::RepoObject, project: user_project @@ -37,7 +37,9 @@ module API # branch (required) - The name of the branch # Example Request: # 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 @branch = user_project.repository.find_branch(params[:branch]) @@ -55,7 +57,9 @@ module API # branch (required) - The name of the branch # Example Request: # 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 @branch = user_project.repository.find_branch(params[:branch]) diff --git a/lib/api/users.rb b/lib/api/users.rb index ae808b6272b..6ed2740c333 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -113,6 +113,45 @@ module API 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 # # Example Request: diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 05814fc78f6..28e323fe30d 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -101,7 +101,7 @@ module Backup def 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 diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 214d9824ee1..6f7c4f7c909 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -10,15 +10,12 @@ module Backup Project.find_each(batch_size: 1000) do |project| print " * #{project.path_with_namespace} ... " - if project.empty_repo? - puts "[SKIPPED]".cyan - next - end - # Create namespace dir if missing 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 else puts "[FAILED]".red diff --git a/lib/gitlab/diff_parser.rb b/lib/gitlab/diff_parser.rb index fb27280c4a4..14bbb328637 100644 --- a/lib/gitlab/diff_parser.rb +++ b/lib/gitlab/diff_parser.rb @@ -4,9 +4,9 @@ module Gitlab attr_reader :lines, :new_path - def initialize(diff) - @lines = diff.diff.lines.to_a - @new_path = diff.new_path + def initialize(lines, new_path = '') + @lines = lines + @new_path = new_path end def each @@ -18,10 +18,7 @@ module Gitlab lines_arr.each do |line| raw_line = line.dup - next if line.match(/^\-\-\- \/dev\/null/) - next if line.match(/^\+\+\+ \/dev\/null/) - next if line.match(/^\-\-\- a/) - next if line.match(/^\+\+\+ b/) + next if filename?(line) full_line = html_escape(line.gsub(/\n/, '')) full_line = ::Gitlab::InlineDiff.replace_markers full_line @@ -53,8 +50,17 @@ module Gitlab end end + def empty? + @lines.empty? + end + private + def filename?(line) + line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b', + '--- /tmp/diffy', '+++ /tmp/diffy') + end + def identification_type(line) if line[0] == "+" "new" diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb index 983a2956a35..0777558d643 100644 --- a/lib/gitlab/ldap/adapter.rb +++ b/lib/gitlab/ldap/adapter.rb @@ -44,7 +44,8 @@ module Gitlab def users(field, value) if field.to_sym == :dn options = { - base: value + base: value, + scope: Net::LDAP::SearchScope_BaseObject } else options = { diff --git a/lib/gitlab/oauth/user.rb b/lib/gitlab/oauth/user.rb index 1bac93378ef..d154bd8600b 100644 --- a/lib/gitlab/oauth/user.rb +++ b/lib/gitlab/oauth/user.rb @@ -34,9 +34,11 @@ module Gitlab # In this case we generate temporary email and force user to fill it later if user.email.blank? user.generate_tmp_oauth_email - else + elsif provider != "ldap" # Google oauth returns email but dont return nickname # So we use part of email as username for new user + # For LDAP, username is already set to the user's + # uid/userid/sAMAccountName. user.username = email.match(/^[^@]*/)[0] end @@ -65,7 +67,11 @@ module Gitlab end 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 def username diff --git a/lib/gitlab/satellite/compare_action.rb b/lib/gitlab/satellite/compare_action.rb new file mode 100644 index 00000000000..c923bb9c0f0 --- /dev/null +++ b/lib/gitlab/satellite/compare_action.rb @@ -0,0 +1,53 @@ +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 diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb index c6e4d3351cf..05123ad9c41 100644 --- a/lib/gitlab/satellite/satellite.rb +++ b/lib/gitlab/satellite/satellite.rb @@ -84,6 +84,7 @@ module Gitlab # Clear the working directory def clear_working_dir! repo.git.reset(hard: true) + repo.git.clean(f: true, d: true, x: true) end # Deletes all branches except the parking branch diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index e9258cc626b..bf015a1fe16 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -779,7 +779,7 @@ namespace :gitlab do end def check_gitlab_shell - required_version = Gitlab::VersionInfo.new(1, 9, 1) + required_version = Gitlab::VersionInfo.new(1, 9, 3) current_version = Gitlab::VersionInfo.parse(gitlab_shell_version) print "GitLab Shell version >= #{required_version} ? ... " diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake index 853994dd67d..8b4ccdfc3fe 100644 --- a/lib/tasks/gitlab/setup.rake +++ b/lib/tasks/gitlab/setup.rake @@ -15,14 +15,7 @@ namespace :gitlab do end Rake::Task["db:setup"].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["add_limits_mysql"].invoke Rake::Task["db:seed_fu"].invoke rescue Gitlab::TaskAbortedByUserError puts "Quitting...".red diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake index 08de0f2dd5d..dfc90bb3339 100644 --- a/lib/tasks/gitlab/shell.rake +++ b/lib/tasks/gitlab/shell.rake @@ -1,5 +1,64 @@ namespace :gitlab 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" task setup: :environment do setup diff --git a/lib/tasks/gitlab/test.rake b/lib/tasks/gitlab/test.rake index 2c9b9978933..9516210e205 100644 --- a/lib/tasks/gitlab/test.rake +++ b/lib/tasks/gitlab/test.rake @@ -8,9 +8,9 @@ namespace :gitlab do ] 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 diff --git a/lib/tasks/migrate/add_limits_mysql.rake b/lib/tasks/migrate/add_limits_mysql.rake index 46b6451752b..a1972a682d8 100644 --- a/lib/tasks/migrate/add_limits_mysql.rake +++ b/lib/tasks/migrate/add_limits_mysql.rake @@ -1,14 +1,7 @@ +require Rails.root.join('db/migrate/limits_to_mysql') + desc "GITLAB | Add limits to strings in mysql database" task add_limits_mysql: :environment do puts "Adding limits to schema.rb for mysql" LimitsToMysql.new.up 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 diff --git a/lib/tasks/setup.rake b/lib/tasks/setup.rake new file mode 100644 index 00000000000..93701de8f63 --- /dev/null +++ b/lib/tasks/setup.rake @@ -0,0 +1,4 @@ +desc "GITLAB | Setup gitlab db" +task :setup do + Rake::Task["gitlab:setup"].invoke +end diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png Binary files differnew file mode 100644 index 00000000000..6f2e0dd090f --- /dev/null +++ b/public/apple-touch-icon-precomposed.png diff --git a/public/static.css b/public/static.css index aa834553a1c..c6f92ac01d9 100644 --- a/public/static.css +++ b/public/static.css @@ -2,7 +2,6 @@ body { color: #666; text-align: center; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - sans-serif; margin:0; width: 800px; margin: auto; diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb new file mode 100644 index 00000000000..4f8a5f909df --- /dev/null +++ b/spec/finders/notes_finder_spec.rb @@ -0,0 +1,38 @@ +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 diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 61c561335e5..0376e0aadf0 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -46,7 +46,7 @@ describe ApplicationHelper do group = create(:group) group.avatar = File.open(avatar_file_path) 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 it "should give default avatar_icon when no avatar is present" do @@ -63,7 +63,7 @@ describe ApplicationHelper do user = create(:user) user.avatar = File.open(avatar_file_path) 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 it "should call gravatar_icon when no avatar is present" do diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index e86a60a42b5..3b075044955 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -239,7 +239,7 @@ describe Notify do it_behaves_like 'an assignee email' 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 it 'contains a link to the new merge request' do @@ -275,7 +275,7 @@ describe Notify do end 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 it 'contains the name of the previous assignee' do @@ -303,7 +303,7 @@ describe Notify do end 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 it 'contains the new status' do @@ -426,7 +426,7 @@ describe Notify do it_behaves_like 'a note email' 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 it 'contains a link to the merge request note' do diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 86610c47513..a6d300b099b 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -242,6 +242,67 @@ describe API::API, api: true do 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 before { admin } diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index f1df7e55dd0..3c2eec6cfd9 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -8,10 +8,10 @@ describe SystemHooksService do context 'event data' do it { event_data(user, :create).should include(:event_name, :name, :created_at, :email, :user_id) } it { event_data(user, :destroy).should include(:event_name, :name, :created_at, :email, :user_id) } - it { event_data(project, :create).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email) } - it { event_data(project, :destroy).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email) } - it { event_data(users_project, :create).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access) } - it { event_data(users_project, :destroy).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access) } + it { event_data(project, :create).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } + it { event_data(project, :destroy).should include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } + it { event_data(users_project, :create).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access, :project_visibility) } + it { event_data(users_project, :destroy).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :project_access, :project_visibility) } end context 'event names' do |
