diff options
35 files changed, 449 insertions, 161 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dbdbae9d787..5dfeb8a1f90 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -137,23 +137,104 @@ bundler:audit: # Ruby 2.1 jobs -spec:ruby21: +spec:feature:ruby21: image: ruby:2.1 + only: + - master script: - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature + tags: + - ruby + - mysql + +spec:api:ruby21: + image: ruby:2.1 + only: + - master + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api + tags: + - ruby + - mysql + +spec:models:ruby21: + image: ruby:2.1 + only: + - master + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models tags: - ruby - mysql + +spec:lib:ruby21: + image: ruby:2.1 only: - master + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib + tags: + - ruby + - mysql -spinach:ruby21: +spec:services:ruby21: image: ruby:2.1 + only: + - master script: - - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services tags: - ruby - mysql + +spec:benchmark:ruby21: + image: ruby:2.1 only: - master + script: + - RAILS_ENV=test bundle exec rake spec:benchmark + tags: + - ruby + - mysql + allow_failure: true + +spec:other:ruby21: + image: ruby:2.1 + only: + - master + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other + tags: + - ruby + - mysql + +spinach:project:half:ruby21: + image: ruby:2.1 + only: + - master + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half + tags: + - ruby + - mysql + +spinach:project:rest:ruby21: + image: ruby:2.1 + only: + - master + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest + tags: + - ruby + - mysql + +spinach:other:ruby21: + image: ruby:2.1 + only: + - master + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other + tags: + - ruby + - mysql diff --git a/CHANGELOG b/CHANGELOG index 25acdbcb485..b08aa4fb925 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.5.0 (unreleased) + - Cache various Repository methods to improve performance (Yorick Peterse) - Ensure rake tasks that don't need a DB connection can be run without one - Update New Relic gem to 3.14.1.311 (Stan Hu) - Add "visibility" flag to GET /projects api endpoint @@ -15,13 +16,16 @@ v 8.5.0 (unreleased) - Whitelist raw "abbr" elements when parsing Markdown (Benedict Etzel) - Fix label links for a merge request pointing to issues list - Don't vendor minified JS + - Increase project import timeout to 15 minutes - Display 404 error on group not found - Track project import failure - Support Two-factor Authentication for LDAP users - Display database type and version in Administration dashboard + - Allow limited Markdown in Broadcast Messages - Fix visibility level text in admin area (Zeger-Jan van de Weg) - Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg) - Update the ExternalIssue regex pattern (Blake Hitchcock) + - Remember user's inline/side-by-side diff view preference in a cookie (Kirill Katsnelson) - Optimized performance of finding issues to be closed by a merge request - Revert "Add IP check against DNSBLs at account sign-up" - Fix API to keep request parameters in Link header (Michael Potthoff) @@ -35,6 +39,8 @@ v 8.5.0 (unreleased) - Fixed logo animation on Safari (Roman Rott) - Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg) - In seach autocomplete show only groups and projects you are member of + - Fix: init.d script not working on OS X + - Faster snippet search - Title for milestones should be unique (Zeger-Jan van de Weg) v 8.4.4 diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee index eb951f71711..b2b8e1b7ffb 100644 --- a/app/assets/javascripts/admin.js.coffee +++ b/app/assets/javascripts/admin.js.coffee @@ -12,19 +12,6 @@ class @Admin e.preventDefault() $('.js-toggle-colors-container').toggle() - $('input#broadcast_message_color').on 'input', -> - previewColor = $(@).val() - $('div.broadcast-message-preview').css('background-color', previewColor) - - $('input#broadcast_message_font').on 'input', -> - previewColor = $(@).val() - $('div.broadcast-message-preview').css('color', previewColor) - - $('textarea#broadcast_message_message').on 'input', -> - previewMessage = $(@).val() - previewMessage = "Your message here" if previewMessage.trim() == '' - $('div.broadcast-message-preview span').text(previewMessage) - $('.log-tabs a').click (e) -> e.preventDefault() $(this).tab('show') diff --git a/app/assets/javascripts/broadcast_message.js.coffee b/app/assets/javascripts/broadcast_message.js.coffee new file mode 100644 index 00000000000..a38a329c4c2 --- /dev/null +++ b/app/assets/javascripts/broadcast_message.js.coffee @@ -0,0 +1,22 @@ +$ -> + $('input#broadcast_message_color').on 'input', -> + previewColor = $(@).val() + $('div.broadcast-message-preview').css('background-color', previewColor) + + $('input#broadcast_message_font').on 'input', -> + previewColor = $(@).val() + $('div.broadcast-message-preview').css('color', previewColor) + + previewPath = $('textarea#broadcast_message_message').data('preview-path') + + $('textarea#broadcast_message_message').on 'input', -> + message = $(@).val() + + if message == '' + $('.js-broadcast-message-preview').text("Your message here") + else + $.ajax( + url: previewPath + type: "POST" + data: { broadcast_message: { message: message } } + ) diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss index 144852e7874..a61161810a3 100644 --- a/app/assets/stylesheets/pages/admin.scss +++ b/app/assets/stylesheets/pages/admin.scss @@ -55,6 +55,16 @@ @extend .alert-warning; padding: 10px; text-align: center; + + > div, p { + display: inline; + margin: 0; + + a { + color: inherit; + text-decoration: underline; + } + } } .broadcast-message-preview { diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index a470d865408..fc342924987 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -36,6 +36,10 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController end end + def preview + @message = broadcast_message_params[:message] + end + protected def finder diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index dd32d509191..a326bc58215 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -28,6 +28,11 @@ class Projects::ApplicationController < ApplicationController private + def apply_diff_view_cookie! + view = params[:view] || cookies[:diff_view] + cookies.permanent[:diff_view] = params[:view] = view if view + end + def builds_enabled return render_404 unless @project.builds_enabled? end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index f5a169e5aa9..5a084e123a1 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -13,6 +13,8 @@ class Projects::CommitController < Projects::ApplicationController def show return git_not_found! unless @commit + apply_diff_view_cookie! + @line_notes = commit.notes.inline @note = @project.build_commit_note(commit) @notes = commit.notes.not_inline.fresh diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index ed3050d59aa..9d588c370aa 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -57,6 +57,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def diffs + apply_diff_view_cookie! + @commit = @merge_request.last_commit @base_commit = @merge_request.diff_base_commit diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb index 1ed8c710f77..43a29c96bca 100644 --- a/app/helpers/broadcast_messages_helper.rb +++ b/app/helpers/broadcast_messages_helper.rb @@ -3,7 +3,7 @@ module BroadcastMessagesHelper return unless message.present? content_tag :div, class: 'broadcast-message', style: broadcast_message_style(message) do - icon('bullhorn') << ' ' << message.message + icon('bullhorn') << ' ' << render_broadcast_message(message.message) end end @@ -31,4 +31,8 @@ module BroadcastMessagesHelper 'Pending' end end + + def render_broadcast_message(message) + Banzai.render(message, pipeline: :broadcast_message).html_safe + end end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index bc36434f549..41ae4048992 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -33,7 +33,7 @@ module SnippetsHelper # surrounding code. # # @returns Array, unique and sorted. - def matching_lines(lined_content, surrounding_lines) + def matching_lines(lined_content, surrounding_lines, query) used_lines = [] lined_content.each_with_index do |line, line_number| used_lines.concat bounded_line_numbers( @@ -51,9 +51,9 @@ module SnippetsHelper # surrounding_lines() worth of unmatching lines. # # @returns a hash with {snippet_object, snippet_chunks:{data,start_line}} - def chunk_snippet(snippet, surrounding_lines = 3) + def chunk_snippet(snippet, query, surrounding_lines = 3) lined_content = snippet.content.split("\n") - used_lines = matching_lines(lined_content, surrounding_lines) + used_lines = matching_lines(lined_content, surrounding_lines, query) snippet_chunk = [] snippet_chunks = [] diff --git a/app/models/project.rb b/app/models/project.rb index 043f08b9a13..f11c6d7c6be 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -790,6 +790,8 @@ class Project < ActiveRecord::Base def change_head(branch) # Cached divergent commit counts are based on repository head repository.expire_branch_cache + repository.expire_root_ref_cache + gitlab_shell.update_repository_head(self.path_with_namespace, branch) reload_default_branch end diff --git a/app/models/repository.rb b/app/models/repository.rb index e813c946bc1..27bdbac3e52 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -44,7 +44,9 @@ class Repository end def empty? - raw_repository.empty? + return @empty unless @empty.nil? + + @empty = cache.fetch(:empty?) { raw_repository.empty? } end # @@ -57,7 +59,11 @@ class Repository # This method return true if repository contains some content visible in project page. # def has_visible_content? - raw_repository.branch_count > 0 + return @has_visible_content unless @has_visible_content.nil? + + @has_visible_content = cache.fetch(:has_visible_content?) do + raw_repository.branch_count > 0 + end end def commit(id = 'HEAD') @@ -243,6 +249,16 @@ class Repository end end + def expire_root_ref_cache + cache.expire(:root_ref) + @root_ref = nil + end + + def expire_has_visible_content_cache + cache.expire(:has_visible_content?) + @has_visible_content = nil + end + def rebuild_cache cache_keys.each do |key| cache.expire(key) @@ -480,7 +496,7 @@ class Repository end def root_ref - @root_ref ||= raw_repository.root_ref + @root_ref ||= cache.fetch(:root_ref) { raw_repository.root_ref } end def commit_dir(user, path, message, branch) diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index d7ea30bc315..0f31756797d 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -21,8 +21,12 @@ class GitPushService project.repository.expire_cache if push_remove_branch?(ref, newrev) + project.repository.expire_has_visible_content_cache + @push_commits = [] elsif push_to_new_branch?(ref, oldrev) + project.repository.expire_has_visible_content_cache + # Re-find the pushed commits. if is_default_branch?(ref) # Initial push to the default branch. Take the full history of that branch as "newly pushed". diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml index 953b8b69368..5c9403fa0c2 100644 --- a/app/views/admin/broadcast_messages/_form.html.haml +++ b/app/views/admin/broadcast_messages/_form.html.haml @@ -1,6 +1,7 @@ .broadcast-message-preview{ style: broadcast_message_style(@broadcast_message) } = icon('bullhorn') - %span= @broadcast_message.message || "Your message here" + .js-broadcast-message-preview + = render_broadcast_message(@broadcast_message.message.presence || "Your message here") = form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-requires-input'} do |f| -if @broadcast_message.errors.any? @@ -10,7 +11,9 @@ .form-group = f.label :message, class: 'control-label' .col-sm-10 - = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true + = f.text_area :message, class: "form-control js-quick-submit js-autosize", + required: true, + data: { preview_path: preview_admin_broadcast_messages_path } .form-group.js-toggle-colors-container .col-sm-10.col-sm-offset-2 = link_to 'Customize colors', '#', class: 'js-toggle-colors-link' diff --git a/app/views/admin/broadcast_messages/preview.js.haml b/app/views/admin/broadcast_messages/preview.js.haml new file mode 100644 index 00000000000..fbc9453c72e --- /dev/null +++ b/app/views/admin/broadcast_messages/preview.js.haml @@ -0,0 +1 @@ +$('.js-broadcast-message-preview').html("#{j(render_broadcast_message(@message))}"); diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index 2e3c956ddc4..943b025c80f 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -15,7 +15,7 @@ %strong ##{commit_status.id} - if commit_status.show_warning? - %i.fa.fa-warning.text-warning + %i.fa.fa-warning.text-warning{data: { toggle: "tooltip" }, title: "This build is stuck, open it to know more"} - if defined?(commit_sha) && commit_sha %td diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml index dcd61199717..6b77d24f50c 100644 --- a/app/views/search/results/_snippet_blob.html.haml +++ b/app/views/search/results/_snippet_blob.html.haml @@ -1,46 +1,50 @@ +- snippet_blob = chunk_snippet(snippet_blob, @search_term) +- snippet = snippet_blob[:snippet_object] +- snippet_chunks = snippet_blob[:snippet_chunks] + .search-result-row %span - = snippet_blob[:snippet_object].title + = snippet.title by - = link_to user_snippets_path(snippet_blob[:snippet_object].author) do - = image_tag avatar_icon(snippet_blob[:snippet_object].author_email), class: "avatar avatar-inline s16", alt: '' - = snippet_blob[:snippet_object].author_name - %span.light #{time_ago_with_tooltip(snippet_blob[:snippet_object].created_at)} + = link_to user_snippets_path(snippet.author) do + = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: '' + = snippet.author_name + %span.light #{time_ago_with_tooltip(snippet.created_at)} %h4.snippet-title - - snippet_path = reliable_snippet_path(snippet_blob[:snippet_object]) + - snippet_path = reliable_snippet_path(snippet) = link_to snippet_path do .file-holder .file-title %i.fa.fa-file - %strong= snippet_blob[:snippet_object].file_name - - if markup?(snippet_blob[:snippet_object].file_name) + %strong= snippet.file_name + - if markup?(snippet.file_name) .file-content.wiki - - snippet_blob[:snippet_chunks].each do |snippet| - - unless snippet[:data].empty? - = render_markup(snippet_blob[:snippet_object].file_name, snippet[:data]) + - snippet_chunks.each do |chunk| + - unless chunk[:data].empty? + = render_markup(snippet.file_name, chunk[:data]) - else .file-content.code .nothing-here-block Empty file - else .file-content.code.js-syntax-highlight .line-numbers - - snippet_blob[:snippet_chunks].each do |snippet| - - unless snippet[:data].empty? - - snippet[:data].lines.to_a.size.times do |index| - - offset = defined?(snippet[:start_line]) ? snippet[:start_line] : 1 + - snippet_chunks.each do |chunk| + - unless chunk[:data].empty? + - chunk[:data].lines.to_a.size.times do |index| + - offset = defined?(chunk[:start_line]) ? chunk[:start_line] : 1 - i = index + offset = link_to snippet_path+"#L#{i}", id: "L#{i}", rel: "#L#{i}", class: "diff-line-num" do %i.fa.fa-link = i - - unless snippet == snippet_blob[:snippet_chunks].last + - unless snippet == snippet_chunks.last %a.diff-line-num = "." %pre.code %code - - snippet_blob[:snippet_chunks].each do |snippet| - - unless snippet[:data].empty? - = snippet[:data] - - unless snippet == snippet_blob[:snippet_chunks].last + - snippet_chunks.each do |chunk| + - unless chunk[:data].empty? + = chunk[:data] + - unless chunk == snippet_chunks.last %a = "..." - else diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml index 285af56ad73..627814bcfae 100644 --- a/app/views/shared/_import_form.html.haml +++ b/app/views/shared/_import_form.html.haml @@ -11,6 +11,6 @@ %li If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>. %li - The import will time out after 4 minutes. For big repositories, use a clone/push combination. + The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination. %li To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}. diff --git a/config/routes.rb b/config/routes.rb index 034bfaf1bcd..f5951bf3e8d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -227,7 +227,10 @@ Rails.application.routes.draw do get :test end - resources :broadcast_messages, only: [:index, :edit, :create, :update, :destroy] + resources :broadcast_messages, only: [:index, :edit, :create, :update, :destroy] do + post :preview, on: :collection + end + resource :logs, only: [:show] resource :background_jobs, controller: 'background_jobs', only: [:show] diff --git a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb index 091de54978b..d3ea956952e 100644 --- a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb +++ b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb @@ -48,7 +48,7 @@ class RemoveDotAtomPathEndingOfProjects < ActiveRecord::Migration end def projects_with_dot_atom - select_all("SELECT p.id, p.path, n.path as namespace_path, n.id as namespace_id FROM projects p inner join namespaces n on n.id = p.namespace_id WHERE lower(p.path) LIKE '%.atom'") + select_all("SELECT p.id, p.path, n.path as namespace_path, n.id as namespace_id FROM projects p inner join namespaces n on n.id = p.namespace_id WHERE p.path LIKE '%.atom'") end def up diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 006dae8ca9a..c6a1c20d02f 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -66,7 +66,7 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim You need at least 2GB of addressable memory (RAM + swap) to install and use GitLab! With less memory GitLab will give strange errors during the reconfigure run and 500 errors during usage. -- 512MB RAM + 1.5GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advise. +- 512MB RAM + 1.5GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice. - 1GB RAM + 1GB swap supports up to 100 users but it will be slow - **2GB RAM** is the **recommended** memory size and supports up to 100 users - 4GB RAM supports up to 1,000 users diff --git a/features/admin/broadcast_messages.feature b/features/admin/broadcast_messages.feature index fd3bac77f86..4f9c651561e 100644 --- a/features/admin/broadcast_messages.feature +++ b/features/admin/broadcast_messages.feature @@ -25,3 +25,9 @@ Feature: Admin Broadcast Messages When I remove an existing broadcast message Then I should be redirected to admin messages page And I should not see the removed broadcast message + + @javascript + Scenario: Live preview a customized broadcast message + When I visit admin messages page + And I enter a broadcast message with Markdown + Then I should see a live preview of the rendered broadcast message diff --git a/features/steps/admin/broadcast_messages.rb b/features/steps/admin/broadcast_messages.rb index 6cacdf4764c..af2b4a29313 100644 --- a/features/steps/admin/broadcast_messages.rb +++ b/features/steps/admin/broadcast_messages.rb @@ -19,7 +19,7 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps end step 'submit form with new customized broadcast message' do - fill_in 'broadcast_message_message', with: 'Application update from 4:00 CST to 5:00 CST' + fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**' fill_in 'broadcast_message_color', with: '#f2dede' fill_in 'broadcast_message_font', with: '#b94a48' select Date.today.next_year.year, from: "broadcast_message_ends_at_1i" @@ -28,6 +28,7 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps step 'I should see a customized broadcast message' do expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' + expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"]) end @@ -51,4 +52,15 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps step 'I should not see the removed broadcast message' do expect(page).not_to have_content 'Migration to new server' end + + step 'I enter a broadcast message with Markdown' do + fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:" + end + + step 'I should see a live preview of the rendered broadcast message' do + page.within('.broadcast-message-preview') do + expect(page).to have_selector('strong', text: 'Markdown') + expect(page).to have_selector('img.emoji') + end + end end diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 099062eeb8b..4962f5e53ce 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -1,6 +1,9 @@ module Backup class Manager def pack + # Make sure there is a connection + ActiveRecord::Base.connection.reconnect! + # saving additional informations s = {} s[:db_version] = "#{ActiveRecord::Migrator.current_version}" diff --git a/lib/banzai/pipeline/broadcast_message_pipeline.rb b/lib/banzai/pipeline/broadcast_message_pipeline.rb new file mode 100644 index 00000000000..4bb85e24c38 --- /dev/null +++ b/lib/banzai/pipeline/broadcast_message_pipeline.rb @@ -0,0 +1,16 @@ +module Banzai + module Pipeline + class BroadcastMessagePipeline < DescriptionPipeline + def self.filters + @filters ||= [ + Filter::MarkdownFilter, + Filter::SanitizationFilter, + + Filter::EmojiFilter, + Filter::AutolinkFilter, + Filter::ExternalLinkFilter + ] + end + end + end +end diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index f751458ac66..b9bb6e76081 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -36,7 +36,7 @@ module Gitlab # import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git") # def import_repository(name, url) - output, status = Popen::popen([gitlab_shell_projects_path, 'import-project', "#{name}.git", url, '240']) + output, status = Popen::popen([gitlab_shell_projects_path, 'import-project', "#{name}.git", url, '900']) raise Error, output unless status.zero? true end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 8531c7e87e1..761b63e98f6 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -7,8 +7,8 @@ module Gitlab settings = nil if connect_to_db? - settings = ApplicationSetting.current - settings ||= ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration? + settings = ::ApplicationSetting.current + settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration? end settings || fake_application_settings diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb index 38364a0b151..addda95be2b 100644 --- a/lib/gitlab/snippet_search_results.rb +++ b/lib/gitlab/snippet_search_results.rb @@ -12,9 +12,9 @@ module Gitlab def objects(scope, page = nil) case scope when 'snippet_titles' - Kaminari.paginate_array(snippet_titles).page(page).per(per_page) + snippet_titles.page(page).per(per_page) when 'snippet_blobs' - Kaminari.paginate_array(snippet_blobs).page(page).per(per_page) + snippet_blobs.page(page).per(per_page) else super end @@ -39,11 +39,7 @@ module Gitlab end def snippet_blobs - search = Snippet.where(id: limit_snippet_ids).search_code(query) - search = search.order('updated_at DESC').to_a - snippets = [] - search.each { |e| snippets << chunk_snippet(e) } - snippets + Snippet.where(id: limit_snippet_ids).search_code(query).order('updated_at DESC') end def default_scope diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index 9e90a99f15b..d95e7023d2e 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -38,7 +38,7 @@ web_server_pid_path="$pid_path/unicorn.pid" sidekiq_pid_path="$pid_path/sidekiq.pid" mail_room_enabled=false mail_room_pid_path="$pid_path/mail_room.pid" -gitlab_workhorse_dir=$(cd $app_root/../gitlab-workhorse && pwd) +gitlab_workhorse_dir=$(cd $app_root/../gitlab-workhorse 2> /dev/null && pwd) gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid" gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $rails_socket -documentRoot $app_root/public" gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log" @@ -49,7 +49,7 @@ test -f /etc/default/gitlab && . /etc/default/gitlab # Switch to the app_user if it is not he/she who is running the script. if [ `whoami` != "$app_user" ]; then - eval su - "$app_user" -s $shell_path -c $(echo \")$0 "$@"$(echo \"); exit; + eval su - "$app_user" -c $(echo \")$shell_path -l -c \'$0 "$@"\'$(echo \"); exit; fi # Switch to the gitlab path, exit on failure. diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 6aaec224f6e..9450a389d81 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -188,7 +188,7 @@ describe Projects::MergeRequestsController do expect(response).to render_template('diffs') end end - + context 'as json' do it 'renders the diffs template to a string' do go format: 'json' @@ -199,6 +199,32 @@ describe Projects::MergeRequestsController do end end + describe 'GET diffs with view' do + def go(extra_params = {}) + params = { + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: merge_request.iid + } + + get :diffs, params.merge(extra_params) + end + + it 'saves the preferred diff view in a cookie' do + go view: 'parallel' + + expect(response.cookies['diff_view']).to eq('parallel') + end + + it 'assigns :view param based on cookie' do + request.cookies['diff_view'] = 'parallel' + + go + + expect(controller.params[:view]).to eq 'parallel' + end + end + describe 'GET commits' do def go(format: 'html') get :commits, diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index d2db77f6286..c1b6ecd329a 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -1,30 +1,3 @@ -# == Schema Information -# -# Table name: builds -# -# id :integer not null, primary key -# project_id :integer -# status :string(255) -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# commit_id :integer -# coverage :float -# commands :text -# job_id :integer -# name :string(255) -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string(255) -# trigger_request_id :integer -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - FactoryGirl.define do factory :ci_build, class: Ci::Build do name 'test' @@ -65,5 +38,20 @@ FactoryGirl.define do build.trace = 'BUILD TRACE' end end + + trait :artifacts do + after(:create) do |build, _| + build.artifacts_file = + fixture_file_upload(Rails.root + + 'spec/fixtures/ci_build_artifacts.zip', + 'application/zip') + + build.artifacts_metadata = + fixture_file_upload(Rails.root + + 'spec/fixtures/ci_build_artifacts_metadata.gz', + 'application/x-gzip') + build.save! + end + end end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index c484ae8fc8c..753012be578 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -232,11 +232,92 @@ describe Repository, models: true do end describe 'when there are branches' do - before do - allow(repository.raw_repository).to receive(:branch_count).and_return(3) + it 'returns true' do + expect(repository.raw_repository).to receive(:branch_count).and_return(3) + + expect(subject).to eq(true) + end + + it 'caches the output' do + expect(repository.raw_repository).to receive(:branch_count). + once. + and_return(3) + + repository.has_visible_content? + repository.has_visible_content? end + end + end + + describe '#empty?' do + let(:empty_repository) { create(:project_empty_repo).repository } + + it 'returns true for an empty repository' do + expect(empty_repository.empty?).to eq(true) + end + + it 'returns false for a non-empty repository' do + expect(repository.empty?).to eq(false) + end + + it 'caches the output' do + expect(repository.raw_repository).to receive(:empty?). + once. + and_return(false) + + repository.empty? + repository.empty? + end + end + + describe '#root_ref' do + it 'returns a branch name' do + expect(repository.root_ref).to be_an_instance_of(String) + end + + it 'caches the output' do + expect(repository.raw_repository).to receive(:root_ref). + once. + and_return('master') + + repository.root_ref + repository.root_ref + end + end + + describe '#expire_cache' do + it 'expires all caches' do + expect(repository).to receive(:expire_branch_cache) + + repository.expire_cache + end + end + + describe '#expire_root_ref_cache' do + it 'expires the root reference cache' do + repository.root_ref + + expect(repository.raw_repository).to receive(:root_ref). + once. + and_return('foo') + + repository.expire_root_ref_cache + + expect(repository.root_ref).to eq('foo') + end + end + + describe '#expire_has_visible_content_cache' do + it 'expires the visible content cache' do + repository.has_visible_content? + + expect(repository.raw_repository).to receive(:branch_count). + once. + and_return(0) + + repository.expire_has_visible_content_cache - it { is_expected.to eq(true) } + expect(repository.has_visible_content?).to eq(false) end end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 244947762dd..01b369720ca 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -151,8 +151,8 @@ describe Ci::API::API do context "Artifacts" do let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') } - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } - let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) } + let(:commit) { create(:ci_commit, project: project) } + let(:build) { create(:ci_build, commit: commit, runner_id: runner.id) } let(:authorize_url) { ci_api("/builds/#{build.id}/artifacts/authorize") } let(:post_url) { ci_api("/builds/#{build.id}/artifacts") } let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") } @@ -160,12 +160,10 @@ describe Ci::API::API do let(:headers) { { "GitLab-Workhorse" => "1.0" } } let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token) } + before { build.run! } + describe "POST /builds/:id/artifacts/authorize" do context "should authorize posting artifact to running build" do - before do - build.run! - end - it "using token as parameter" do post authorize_url, { token: build.token }, headers expect(response.status).to eq(200) @@ -180,10 +178,6 @@ describe Ci::API::API do end context "should fail to post too large artifact" do - before do - build.run! - end - it "using token as parameter" do stub_application_setting(max_artifacts_size: 0) post authorize_url, { token: build.token, filesize: 100 }, headers @@ -197,8 +191,8 @@ describe Ci::API::API do end end - context "should get denied" do - it do + context 'token is invalid' do + it 'should respond with forbidden'do post authorize_url, { token: 'invalid', filesize: 100 } expect(response.status).to eq(403) end @@ -206,17 +200,13 @@ describe Ci::API::API do end describe "POST /builds/:id/artifacts" do - context "Disable sanitizer" do + context "disable sanitizer" do before do # by configuring this path we allow to pass temp file from any path allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return('/') end context "should post artifact to running build" do - before do - build.run! - end - it "uses regual file post" do upload_artifacts(file_upload, headers_with_token, false) expect(response.status).to eq(201) @@ -244,10 +234,7 @@ describe Ci::API::API do let(:stored_artifacts_file) { build.reload.artifacts_file.file } let(:stored_metadata_file) { build.reload.artifacts_metadata.file } - before do - build.run! - post(post_url, post_data, headers_with_token) - end + before { post(post_url, post_data, headers_with_token) } context 'post data accelerated by workhorse is correct' do let(:post_data) do @@ -257,11 +244,8 @@ describe Ci::API::API do 'metadata.name' => metadata.original_filename } end - it 'responds with valid status' do - expect(response.status).to eq(201) - end - it 'stores artifacts and artifacts metadata' do + expect(response.status).to eq(201) expect(stored_artifacts_file.original_filename).to eq(artifacts.original_filename) expect(stored_metadata_file.original_filename).to eq(metadata.original_filename) end @@ -282,56 +266,42 @@ describe Ci::API::API do end end - - context "should fail to post too large artifact" do - before do - build.run! - end - - it do + context "artifacts file is too large" do + it "should fail to post too large artifact" do stub_application_setting(max_artifacts_size: 0) upload_artifacts(file_upload, headers_with_token) expect(response.status).to eq(413) end end - context "should fail to post artifacts without file" do - before do - build.run! - end - - it do + context "artifacts post request does not contain file" do + it "should fail to post artifacts without file" do post post_url, {}, headers_with_token expect(response.status).to eq(400) end end - context "should fail to post artifacts without GitLab-Workhorse" do - before do - build.run! - end - - it do + context 'GitLab Workhorse is not configured' do + it "should fail to post artifacts without GitLab-Workhorse" do post post_url, { token: build.token }, {} expect(response.status).to eq(403) end end end - context "should fail to post artifacts for outside of tmp path" do + context "artifacts are being stored outside of tmp path" do before do # by configuring this path we allow to pass file from @tmpdir only # but all temporary files are stored in system tmp directory @tmpdir = Dir.mktmpdir allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return(@tmpdir) - build.run! end after do FileUtils.remove_entry @tmpdir end - it do + it "should fail to post artifacts for outside of tmp path" do upload_artifacts(file_upload, headers_with_token) expect(response.status).to eq(400) end @@ -349,33 +319,37 @@ describe Ci::API::API do end end - describe "DELETE /builds/:id/artifacts" do - before do - build.run! - post delete_url, token: build.token, file: file_upload - end + describe 'DELETE /builds/:id/artifacts' do + let(:build) { create(:ci_build, :artifacts) } + before { delete delete_url, token: build.token } - it "should delete artifact build" do - build.success - delete delete_url, token: build.token + it 'should remove build artifacts' do expect(response.status).to eq(200) + expect(build.artifacts_file.exists?).to be_falsy + expect(build.artifacts_metadata.exists?).to be_falsy end end - describe "GET /builds/:id/artifacts" do - before do - build.run! - end + describe 'GET /builds/:id/artifacts' do + before { get get_url, token: build.token } - it "should download artifact" do - build.update_attributes(artifacts_file: file_upload) - get get_url, token: build.token - expect(response.status).to eq(200) + context 'build has artifacts' do + let(:build) { create(:ci_build, :artifacts) } + let(:download_headers) do + { 'Content-Transfer-Encoding'=>'binary', + 'Content-Disposition'=>'attachment; filename=ci_build_artifacts.zip' } + end + + it 'should download artifact' do + expect(response.status).to eq(200) + expect(response.headers).to include download_headers + end end - it "should fail to download if no artifact uploaded" do - get get_url, token: build.token - expect(response.status).to eq(404) + context 'build does not has artifacts' do + it 'should respond with not found' do + expect(response.status).to eq(404) + end end end end diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index c1080ef190a..349809743c7 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -21,6 +21,18 @@ describe GitPushService, services: true do end it { is_expected.to be_truthy } + + it 'flushes general cached data' do + expect(project.repository).to receive(:expire_cache) + + subject + end + + it 'flushes the visible content cache' do + expect(project.repository).to receive(:expire_has_visible_content_cache) + + subject + end end context 'existing branch' do @@ -29,6 +41,12 @@ describe GitPushService, services: true do end it { is_expected.to be_truthy } + + it 'flushes general cached data' do + expect(project.repository).to receive(:expire_cache) + + subject + end end context 'rm branch' do @@ -37,6 +55,18 @@ describe GitPushService, services: true do end it { is_expected.to be_truthy } + + it 'flushes the visible content cache' do + expect(project.repository).to receive(:expire_has_visible_content_cache) + + subject + end + + it 'flushes general cached data' do + expect(project.repository).to receive(:expire_cache) + + subject + end end end |