From 3ef71fa4fbea9a5ecc3c094df39cf4ea2f97dc90 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Sat, 26 Sep 2015 20:53:16 +0200 Subject: Add support for HiDPI displays in gravatar service --- app/services/gravatar_service.rb | 4 ++-- spec/helpers/application_helper_spec.rb | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/services/gravatar_service.rb b/app/services/gravatar_service.rb index 4bee0c26a68..433ecc2df32 100644 --- a/app/services/gravatar_service.rb +++ b/app/services/gravatar_service.rb @@ -1,13 +1,13 @@ class GravatarService include Gitlab::CurrentSettings - def execute(email, size = nil) + def execute(email, size = nil, scale = 2) if current_application_settings.gravatar_enabled? && email.present? size = 40 if size.nil? || size <= 0 sprintf gravatar_url, hash: Digest::MD5.hexdigest(email.strip.downcase), - size: size, + size: size * scale, email: email.strip end end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 742420f550e..e6e9bc99a16 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -141,15 +141,19 @@ describe ApplicationHelper do stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}') expect(gravatar_icon(user_email, 20)). - to eq('http://example.local/?s=20&hash=b58c6f14d292556214bd64909bcdb118') + to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118') end it 'accepts a custom size argument' do - expect(helper.gravatar_icon(user_email, 64)).to include '?s=64' + expect(helper.gravatar_icon(user_email, 64)).to include '?s=128' end - it 'defaults size to 40 when given an invalid size' do - expect(helper.gravatar_icon(user_email, nil)).to include '?s=40' + it 'defaults size to 40@2x when given an invalid size' do + expect(helper.gravatar_icon(user_email, nil)).to include '?s=80' + end + + it 'accepts a scaling factor' do + expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120' end it 'ignores case and surrounding whitespace' do -- cgit v1.2.1 From f93177c6a3bee4c02f6a9a18278db13dd1c30c35 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Sat, 26 Sep 2015 21:25:04 +0200 Subject: Add scale parameter to helper --- app/helpers/application_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 39ab83ccf12..162dae1ed32 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -78,8 +78,8 @@ module ApplicationHelper end end - def gravatar_icon(user_email = '', size = nil) - GravatarService.new.execute(user_email, size) || + def gravatar_icon(user_email = '', size = nil, scale = 2) + GravatarService.new.execute(user_email, size, scale) || default_avatar end -- cgit v1.2.1 From 8d620e39c967739f99bd175449864524f05b915c Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Sat, 26 Sep 2015 21:51:02 +0200 Subject: Add scale argument in user class --- app/helpers/application_helper.rb | 4 ++-- app/models/user.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 162dae1ed32..056ffd278f6 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -68,13 +68,13 @@ module ApplicationHelper end end - def avatar_icon(user_email = '', size = nil) + def avatar_icon(user_email = '', size = nil, scale = 2) user = User.find_by(email: user_email) if user user.avatar_url(size) || default_avatar else - gravatar_icon(user_email, size) + gravatar_icon(user_email, size, scale) end end diff --git a/app/models/user.rb b/app/models/user.rb index 25371f9138a..55c095d7d57 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -633,11 +633,11 @@ class User < ActiveRecord::Base email.start_with?('temp-email-for-oauth') end - def avatar_url(size = nil) + def avatar_url(size = nil, scale = 2) if avatar.present? [gitlab_config.url, avatar.url].join else - GravatarService.new.execute(email, size) + GravatarService.new.execute(email, size, scale) end end -- cgit v1.2.1 From 7d7b6dbb9f2a8c6dbee8ab6eb0ab075705abcfbd Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Mon, 19 Oct 2015 01:21:21 +0200 Subject: Update spec --- spec/helpers/application_helper_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 742420f550e..b5a29346dcd 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -95,9 +95,9 @@ describe ApplicationHelper do end it 'should call gravatar_icon when no User exists with the given email' do - expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20) + expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2) - helper.avatar_icon('foo@example.com', 20) + helper.avatar_icon('foo@example.com', 20, 2) end end -- cgit v1.2.1 From 34d0cd438a7173c35c687ecb37eae1f3293b84b5 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Mon, 19 Oct 2015 01:22:20 +0200 Subject: Update spec --- spec/helpers/application_helper_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index e6e9bc99a16..bed6658ce07 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -95,7 +95,7 @@ describe ApplicationHelper do end it 'should call gravatar_icon when no User exists with the given email' do - expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20) + expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2) helper.avatar_icon('foo@example.com', 20) end -- cgit v1.2.1 From ab6d426a480ef45ba3d13db3049294304b33f6c0 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 19 Oct 2015 21:04:05 +0300 Subject: Add missing dot in confirm modal --- app/views/shared/_confirm_modal.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml index 5f51b0d450f..2a44817e05a 100644 --- a/app/views/shared/_confirm_modal.html.haml +++ b/app/views/shared/_confirm_modal.html.haml @@ -14,7 +14,7 @@ %br Please type %code.js-confirm-danger-match #{phrase} - to proceed or close this modal to cancel + to proceed or close this modal to cancel. .form-group = text_field_tag 'confirm_name_input', '', class: 'form-control js-confirm-danger-input' -- cgit v1.2.1 From 3d50b99d016af91c06a40f7a8bf4c298e241220a Mon Sep 17 00:00:00 2001 From: Dirceu Pereira Tiegs Date: Mon, 19 Oct 2015 20:25:35 -0200 Subject: Add option to create merge request when editing/creating a file --- app/assets/javascripts/blob/edit_blob.js.coffee | 7 ++++++ app/assets/javascripts/blob/new_blob.js.coffee | 7 ++++++ app/assets/stylesheets/pages/editor.scss | 4 ++++ app/controllers/application_controller.rb | 30 +++++++++++++++++++++++++ app/controllers/projects/blob_controller.rb | 18 +++++++++++++-- app/helpers/merge_requests_helper.rb | 28 ----------------------- app/views/projects/blob/edit.html.haml | 8 +++++++ app/views/projects/blob/new.html.haml | 8 +++++++ 8 files changed, 80 insertions(+), 30 deletions(-) diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee index 050888f9c15..a6f65642d78 100644 --- a/app/assets/javascripts/blob/edit_blob.js.coffee +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -11,6 +11,13 @@ class @EditBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode + $('#new_branch').keyup -> + if $(this).val() != $('#original_branch').val() + $('.form-group.destination').show() + else + $('.form-group.destination').hide() + $('#create_merge_request').prop('checked', false) + $(".js-commit-button").click -> $("#file-content").val editor.getValue() $(".file-editor form").submit() diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee index 1f36a53f191..f4e06ad088b 100644 --- a/app/assets/javascripts/blob/new_blob.js.coffee +++ b/app/assets/javascripts/blob/new_blob.js.coffee @@ -11,6 +11,13 @@ class @NewBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode + $('#new_branch').keyup -> + if $(this).val() != $('#original_branch').val() + $('.form-group.destination').show() + else + $('.form-group.destination').hide() + $('#create_merge_request').prop('checked', false) + $(".js-commit-button").click -> $("#file-content").val editor.getValue() $(".file-editor form").submit() diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 1d565477dd4..6bc9b1c3eaf 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -63,4 +63,8 @@ margin-top: 0; padding: $gl-padding } + + .destination { + display: none; + } } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f0124c6bd60..258e37e98c0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -22,6 +22,7 @@ class ApplicationController < ActionController::Base helper_method :abilities, :can?, :current_application_settings helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled? + helper_method :new_mr_from_push_event, :new_mr_path_for_fork_from_push_event, :new_mr_path_from_push_event rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -343,4 +344,33 @@ class ApplicationController < ActionController::Base def git_import_enabled? current_application_settings.import_sources.include?('git') end + + # new merge requests routing helpers + def new_mr_path_from_push_event(event, target_branch=nil) + target_project = event.project.forked_from_project || event.project + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, target_project, target_branch) + ) + end + + def new_mr_path_for_fork_from_push_event(event, target_branch=nil) + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, event.project.forked_from_project, target_branch) + ) + end + + def new_mr_from_push_event(event, target_project, target_branch) + { + merge_request: { + source_project_id: event.project.id, + target_project_id: target_project.id, + source_branch: event.branch_name, + target_branch: target_branch || target_project.repository.root_ref + } + } + end end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 8cc2f21d887..f49c094b591 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -27,7 +27,14 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "The changes have been successfully committed" respond_to do |format| - format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } + format.html do + url = if params[:create_merge_request] + new_mr_path_from_push_event(current_user.recent_push(@project.id), @ref) + else + namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) + end + redirect_to url + end format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } } end else @@ -52,7 +59,14 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" respond_to do |format| - format.html { redirect_to after_edit_path } + format.html do + url = if params[:create_merge_request] + new_mr_path_from_push_event(current_user.recent_push(@project.id), @ref) + else + after_edit_path + end + redirect_to url + end format.json { render json: { message: "success", filePath: after_edit_path } } end else diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 728d877ace2..7e8f61fd274 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -1,32 +1,4 @@ module MergeRequestsHelper - def new_mr_path_from_push_event(event) - target_project = event.project.forked_from_project || event.project - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, target_project) - ) - end - - def new_mr_path_for_fork_from_push_event(event) - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, event.project.forked_from_project) - ) - end - - def new_mr_from_push_event(event, target_project) - { - merge_request: { - source_project_id: event.project.id, - target_project_id: target_project.id, - source_branch: event.branch_name, - target_branch: target_project.repository.root_ref - } - } - end - def mr_css_classes(mr) classes = "merge-request" classes << " closed" if mr.closed? diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index a811adc5094..26216462707 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -23,9 +23,17 @@ .col-sm-10 = text_field_tag 'new_branch', @ref, class: "form-control" + .form-group.destination + .col-sm-offset-2.col-sm-10 + .checkbox + = label_tag :create_merge_request do + = check_box_tag :create_merge_request, 1, false + Start a new merge request + = hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] + = hidden_field_tag 'original_branch', @ref = render 'projects/commit_button', ref: @ref, cancel_path: @after_edit_path :javascript diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 7975137c37f..0439e55131e 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -17,7 +17,15 @@ .col-sm-10 = text_field_tag 'new_branch', @ref, class: "form-control js-quick-submit" + .form-group.destination + .col-sm-offset-2.col-sm-10 + .checkbox + = label_tag :create_merge_request do + = check_box_tag :create_merge_request, 1, false + Start a new merge request + = hidden_field_tag 'content', '', id: 'file-content' + = hidden_field_tag 'original_branch', @ref = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_tree_path(@project.namespace, @project, @id) -- cgit v1.2.1 From c5146ca1517412b0389c483f18c13072e2abc4a3 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Fri, 23 Oct 2015 03:00:08 +0200 Subject: Fix merge error --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 596a47938d6..3fa7d2bb1cd 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -68,7 +68,7 @@ module ApplicationHelper end end - def avatar_icon(user_email = nil, size = nil, scale = 2) + def avatar_icon(user_or_email = nil, size = nil, scale = 2) if user_or_email.is_a?(User) user = user_or_email else -- cgit v1.2.1 From c8fe42151291593f0f43509a70235c46fce169a1 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 29 Oct 2015 18:42:29 -0200 Subject: Improve personal snippet access workflow. Fixes #3258 --- CHANGELOG | 1 + app/controllers/snippets_controller.rb | 9 +- app/models/ability.rb | 65 +++++++++++---- spec/controllers/snippets_controller_spec.rb | 118 +++++++++++++++++++++++++++ spec/factories.rb | 12 +++ 5 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 spec/controllers/snippets_controller_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 0d89fca9fc1..325a073b5c5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.2.0 (unreleased) - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. + - Improve personal snippet access workflow v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 9f9f9a92f11..8498efc89d0 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -1,6 +1,9 @@ class SnippetsController < ApplicationController before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] + # Allow read snippet + before_action :authorize_show_snippet!, only: [:show] + # Allow modify snippet before_action :authorize_update_snippet!, only: [:edit, :update] @@ -79,10 +82,14 @@ class SnippetsController < ApplicationController [Snippet::PUBLIC, Snippet::INTERNAL]). find(params[:id]) else - PersonalSnippet.are_public.find(params[:id]) + PersonalSnippet.find(params[:id]) end end + def authorize_show_snippet! + authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet) + end + def authorize_update_snippet! return render_404 unless can?(current_user, :update_personal_snippet, @snippet) end diff --git a/app/models/ability.rb b/app/models/ability.rb index b72178fa126..ee2f7b5f58b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -22,12 +22,17 @@ class Ability # List of possible abilities # for non-authenticated user def not_auth_abilities(user, subject) + return not_auth_personal_snippet_abilities(subject) if subject.kind_of?(PersonalSnippet) + return not_auth_project_abilities(subject) if subject.kind_of?(Project) || subject.respond_to?(:project) + return not_auth_group_abilities(subject) if subject.kind_of?(Group) || subject.respond_to?(:group) + [] + end + + def not_auth_project_abilities(subject) project = if subject.kind_of?(Project) subject - elsif subject.respond_to?(:project) - subject.project else - nil + subject.project end if project && project.public? @@ -47,19 +52,29 @@ class Ability rules - project_disabled_features_rules(project) else - group = if subject.kind_of?(Group) - subject - elsif subject.respond_to?(:group) - subject.group - else - nil - end + [] + end + end - if group && group.public_profile? - [:read_group] - else - [] - end + def not_auth_group_abilities(subject) + group = if subject.kind_of?(Group) + subject + else + subject.group + end + + if group && group.public_profile? + [:read_group] + else + [] + end + end + + def not_auth_personal_snippet_abilities(snippet) + if snippet.public? + [:read_personal_snippet] + else + [] end end @@ -278,7 +293,7 @@ class Ability end end - [:note, :project_snippet, :personal_snippet].each do |name| + [:note, :project_snippet].each do |name| define_method "#{name}_abilities" do |user, subject| rules = [] @@ -298,6 +313,24 @@ class Ability end end + def personal_snippet_abilities(user, snippet) + rules = [] + + if snippet.author == user + rules += [ + :read_personal_snippet, + :update_personal_snippet, + :admin_personal_snippet + ] + end + + if snippet.public? || snippet.internal? + rules.push(:read_snippet) + end + + rules + end + def group_member_abilities(user, subject) rules = [] target_user = subject.user diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb new file mode 100644 index 00000000000..e9b823c523c --- /dev/null +++ b/spec/controllers/snippets_controller_spec.rb @@ -0,0 +1,118 @@ +require 'spec_helper' + +describe SnippetsController do + describe 'GET #show' do + let(:user) { create(:user) } + + context 'when the personal snippet is private' do + let(:personal_snippet) { create(:personal_snippet, :private, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + context 'when signed in user is not the author' do + let(:other_author) { create(:author) } + let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } + + it 'responds with status 404' do + get :show, id: other_personal_snippet.to_param + + expect(response.status).to eq(404) + end + end + + context 'when signed in user is the author' do + it 'renders the snippet' do + get :show, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + end + + context 'when not signed in' do + it 'redirects to the sign in page' do + get :show, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is internal' do + let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'renders the snippet' do + get :show, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + + context 'when not signed in' do + it 'redirects to the sign in page' do + get :show, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is public' do + let(:personal_snippet) { create(:personal_snippet, :public, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'renders the snippet' do + get :show, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + + context 'when not signed in' do + it 'renders the snippet' do + get :show, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + end + + context 'when the personal snippet does not exist' do + context 'when signed in' do + before do + sign_in(user) + end + + it 'responds with status 404' do + get :show, id: 'doesntexist' + + expect(response.status).to eq(404) + end + end + + context 'when not signed in' do + it 'responds with status 404' do + get :show, id: 'doesntexist' + + expect(response.status).to eq(404) + end + end + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 200f18f660d..4bf93adabe2 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -165,6 +165,18 @@ FactoryGirl.define do title content file_name + + trait :public do + visibility_level Gitlab::VisibilityLevel::PUBLIC + end + + trait :internal do + visibility_level Gitlab::VisibilityLevel::INTERNAL + end + + trait :private do + visibility_level Gitlab::VisibilityLevel::PRIVATE + end end factory :snippet do -- cgit v1.2.1 From 3b717c8a8c1e0f10bc06fd8501ce2423c98490d4 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Sun, 1 Nov 2015 02:42:36 +0100 Subject: Fix typo --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3fa7d2bb1cd..3230ff1b004 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -78,7 +78,7 @@ module ApplicationHelper if user user.avatar_url(size) || default_avatar else - gravatar_icon(user_or_email, size, scales) + gravatar_icon(user_or_email, size, scale) end end -- cgit v1.2.1 From 93672c502010787be9102b25a4f93722526968b9 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 2 Nov 2015 14:03:42 -0200 Subject: Use `read` rather than `show` like the ability name --- app/controllers/snippets_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 8498efc89d0..08f2483af33 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -2,7 +2,7 @@ class SnippetsController < ApplicationController before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] # Allow read snippet - before_action :authorize_show_snippet!, only: [:show] + before_action :authorize_read_snippet!, only: [:show] # Allow modify snippet before_action :authorize_update_snippet!, only: [:edit, :update] @@ -86,7 +86,7 @@ class SnippetsController < ApplicationController end end - def authorize_show_snippet! + def authorize_read_snippet! authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet) end -- cgit v1.2.1 From 091979bd1b18f53c246415439d05db9aaf078828 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 2 Nov 2015 14:04:45 -0200 Subject: Fix ability name for public or internal personal snippets --- app/models/ability.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index ee2f7b5f58b..06c199bd8d7 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -325,7 +325,7 @@ class Ability end if snippet.public? || snippet.internal? - rules.push(:read_snippet) + rules.push(:read_personal_snippet) end rules -- cgit v1.2.1 From 88836e2ab6664f0cf7b3dc2fa0d4f3d798aeaaa6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 10 Nov 2015 22:31:16 +0200 Subject: Fix bottom position of scroll buttons in build log page --- app/assets/stylesheets/pages/builds.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 74dc3e321c1..da9965f007a 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -21,7 +21,7 @@ .autoscroll-container { position: fixed; - bottom: 10px; + bottom: 20px; right: 20px; z-index: 100; } @@ -34,7 +34,7 @@ a { display: block; - margin-bottom: 5px; + margin-bottom: 10px; } } -- cgit v1.2.1 From 756d61562bb8c1a2ebcb29eb0f62548d2338db52 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 16:24:02 +0100 Subject: Minor refactoring --- app/models/ability.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 95dd53bf425..f5cd14a89c0 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,7 +2,7 @@ class Ability class << self def allowed(user, subject) return not_auth_abilities(user, subject) if user.nil? - return [] unless user.kind_of?(User) + return [] unless user.is_a?(User) return [] if user.blocked? case subject.class.name @@ -22,14 +22,20 @@ class Ability # List of possible abilities # for non-authenticated user def not_auth_abilities(user, subject) - return not_auth_personal_snippet_abilities(subject) if subject.kind_of?(PersonalSnippet) - return not_auth_project_abilities(subject) if subject.kind_of?(Project) || subject.respond_to?(:project) - return not_auth_group_abilities(subject) if subject.kind_of?(Group) || subject.respond_to?(:group) - [] + case true + when subject.is_a?(PersonalSnippet) + not_auth_personal_snippet_abilities(subject) + when subject.is_a?(Project) || subject.respond_to?(:project) + not_auth_project_abilities(subject) + when subject.is_a?(Group) || subject.respond_to?(:group) + not_auth_group_abilities(subject) + else + [] + end end def not_auth_project_abilities(subject) - project = if subject.kind_of?(Project) + project = if subject.is_a?(Project) subject else subject.project @@ -57,7 +63,7 @@ class Ability end def not_auth_group_abilities(subject) - group = if subject.kind_of?(Group) + group = if subject.is_a?(Group) subject else subject.group @@ -327,7 +333,7 @@ class Ability end if snippet.public? || snippet.internal? - rules.push(:read_personal_snippet) + rules << :read_personal_snippet end rules -- cgit v1.2.1 From 1b3475653f253c62ab493fa62665d8a3e105aa88 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 16:25:02 +0100 Subject: Fix changelog --- CHANGELOG | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b555a050445..ee0da5598a3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -25,8 +25,7 @@ v 8.2.0 (unreleased) - Allow to define cache in `.gitlab-ci.yml` - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - - Use issue editor as cross reference comment author when issue is edited with a new mention. - - Improve personal snippet access workflow + - Improve personal snippet access workflow (Douglas Alexandre) - [API] Add ability to fetch the commit ID of the last commit that actually touched a file - Fix omniauth documentation setting for omnibus configuration (Jon Cairns) - Add "New file" link to dropdown on project page -- cgit v1.2.1 From e945ec02804bb28dbd228d8002a159c8da0fcc38 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 18:53:56 +0100 Subject: Add "Start a new merge request" option to every commit form --- .../javascripts/blob/blob_file_dropzone.js.coffee | 17 ++----- app/assets/javascripts/blob/edit_blob.js.coffee | 7 --- app/assets/javascripts/blob/new_blob.js.coffee | 7 --- app/assets/javascripts/new_commit_form.js.coffee | 21 +++++++++ app/assets/stylesheets/pages/editor.scss | 4 -- app/controllers/application_controller.rb | 30 ------------ .../concerns/creates_merge_request_for_commit.rb | 28 ++++++++++++ app/controllers/projects/blob_controller.rb | 53 ++++++++++++---------- app/controllers/projects/tree_controller.rb | 13 +++++- app/helpers/merge_requests_helper.rb | 20 ++++++++ app/views/projects/blob/_actions.html.haml | 2 +- app/views/projects/blob/_new_dir.html.haml | 14 +++--- app/views/projects/blob/_remove.html.haml | 16 +++---- app/views/projects/blob/_upload.html.haml | 18 +++----- app/views/projects/blob/edit.html.haml | 19 ++------ app/views/projects/blob/new.html.haml | 24 ++-------- app/views/projects/blob/show.html.haml | 4 +- app/views/projects/tree/_tree_content.html.haml | 2 +- .../shared/_commit_message_container.html.haml | 8 ++-- app/views/shared/_new_commit_form.html.haml | 18 ++++++++ 20 files changed, 168 insertions(+), 157 deletions(-) create mode 100644 app/assets/javascripts/new_commit_form.js.coffee create mode 100644 app/controllers/concerns/creates_merge_request_for_commit.rb create mode 100644 app/views/shared/_new_commit_form.html.haml diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee index 5b604adbbb1..195f8b11e5d 100644 --- a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee +++ b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee @@ -23,18 +23,6 @@ class @BlobFileDropzone init: -> this.on 'addedfile', (file) -> $('.dropzone-alerts').html('').hide() - commit_message = form.find('#commit_message')[0] - - if /^Upload/.test(commit_message.placeholder) - commit_message.placeholder = 'Upload ' + file.name - - return - - this.on 'removedfile', (file) -> - commit_message = form.find('#commit_message')[0] - - if /^Upload/.test(commit_message.placeholder) - commit_message.placeholder = 'Upload new file' return @@ -47,8 +35,9 @@ class @BlobFileDropzone return this.on 'sending', (file, xhr, formData) -> - formData.append('new_branch', form.find('#new_branch').val()) - formData.append('commit_message', form.find('#commit_message').val()) + formData.append('new_branch', form.find('.js-new-branch').val()) + formData.append('create_merge_request', form.find('.js-create-merge-request').val()) + formData.append('commit_message', form.find('.js-commit-message').val()) return # Override behavior of adding error underneath preview diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee index bf4cd85aee0..f6bf836f19f 100644 --- a/app/assets/javascripts/blob/edit_blob.js.coffee +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -11,13 +11,6 @@ class @EditBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - $('#new_branch').keyup -> - if $(this).val() != $('#original_branch').val() - $('.form-group.destination').show() - else - $('.form-group.destination').hide() - $('#create_merge_request').prop('checked', false) - # Before a form submission, move the content from the Ace editor into the # submitted textarea $('form').submit -> diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee index c2d6c7acfef..68c5e5195e3 100644 --- a/app/assets/javascripts/blob/new_blob.js.coffee +++ b/app/assets/javascripts/blob/new_blob.js.coffee @@ -11,13 +11,6 @@ class @NewBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - $('#new_branch').keyup -> - if $(this).val() != $('#original_branch').val() - $('.form-group.destination').show() - else - $('.form-group.destination').hide() - $('#create_merge_request').prop('checked', false) - # Before a form submission, move the content from the Ace editor into the # submitted textarea $('form').submit -> diff --git a/app/assets/javascripts/new_commit_form.js.coffee b/app/assets/javascripts/new_commit_form.js.coffee new file mode 100644 index 00000000000..2e561dea3e1 --- /dev/null +++ b/app/assets/javascripts/new_commit_form.js.coffee @@ -0,0 +1,21 @@ +class @NewCommitForm + constructor: (form) -> + @newBranch = form.find('.js-new-branch') + @originalBranch = form.find('.js-original-branch') + @createMergeRequest = form.find('.js-create-merge-request') + @createMergeRequestFormGroup = form.find('.js-create-merge-request-form-group') + + @renderDestination() + @newBranch.keyup @renderDestination + + renderDestination: => + different = @newBranch.val() != @originalBranch.val() + + if different + @createMergeRequestFormGroup.show() + @createMergeRequest.prop('checked', true) unless @wasDifferent + else + @createMergeRequestFormGroup.hide() + @createMergeRequest.prop('checked', false) + + @wasDifferent = different diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index c0defa82bff..e2c521af91e 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -63,8 +63,4 @@ margin-top: 0; padding: $gl-padding } - - .destination { - display: none; - } } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6f87ee08b2d..0d182e8eb04 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -22,7 +22,6 @@ class ApplicationController < ActionController::Base helper_method :abilities, :can?, :current_application_settings helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled? - helper_method :new_mr_from_push_event, :new_mr_path_for_fork_from_push_event, :new_mr_path_from_push_event rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -343,35 +342,6 @@ class ApplicationController < ActionController::Base current_application_settings.import_sources.include?('git') end - # new merge requests routing helpers - def new_mr_path_from_push_event(event, target_branch=nil) - target_project = event.project.forked_from_project || event.project - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, target_project, target_branch) - ) - end - - def new_mr_path_for_fork_from_push_event(event, target_branch=nil) - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, event.project.forked_from_project, target_branch) - ) - end - - def new_mr_from_push_event(event, target_project, target_branch) - { - merge_request: { - source_project_id: event.project.id, - target_project_id: target_project.id, - source_branch: event.branch_name, - target_branch: target_branch || target_project.repository.root_ref - } - } - end - def redirect_to_home_page_url? # If user is not signed-in and tries to access root_path - redirect him to landing page # Don't redirect to the default URL to prevent endless redirections diff --git a/app/controllers/concerns/creates_merge_request_for_commit.rb b/app/controllers/concerns/creates_merge_request_for_commit.rb new file mode 100644 index 00000000000..c7527822158 --- /dev/null +++ b/app/controllers/concerns/creates_merge_request_for_commit.rb @@ -0,0 +1,28 @@ +module CreatesMergeRequestForCommit + extend ActiveSupport::Concern + + def new_merge_request_path + if @project.forked? + target_project = @project.forked_from_project || @project + target_branch = target_project.repository.root_ref + else + target_project = @project + target_branch = @ref + end + + new_namespace_project_merge_request_path( + @project.namespace, + @project, + merge_request: { + source_project_id: @project.id, + target_project_id: target_project.id, + source_branch: @new_branch, + target_branch: target_branch + } + ) + end + + def create_merge_request? + params[:create_merge_request] && @new_branch != @ref + end +end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index d7fae64fcdd..41ec7bde45d 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -1,6 +1,7 @@ # Controller for viewing a file's blame class Projects::BlobController < Projects::ApplicationController include ExtractsPath + include CreatesMergeRequestForCommit include ActionView::Helpers::SanitizeHelper # Raised when given an invalid file path @@ -27,15 +28,8 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "The changes have been successfully committed" respond_to do |format| - format.html do - url = if params[:create_merge_request] - new_mr_path_from_push_event(current_user.recent_push(@project.id), @ref) - else - namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) - end - redirect_to url - end - format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } } + format.html { redirect_to after_create_path } + format.json { render json: { message: "success", filePath: after_create_path } } end else flash[:alert] = result[:message] @@ -59,14 +53,7 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" respond_to do |format| - format.html do - url = if params[:create_merge_request] - new_mr_path_from_push_event(current_user.recent_push(@project.id), @ref) - else - after_edit_path - end - redirect_to url - end + format.html { redirect_to after_edit_path } format.json { render json: { message: "success", filePath: after_edit_path } } end else @@ -91,7 +78,7 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - redirect_to namespace_project_tree_path(@project.namespace, @project, @target_branch) + redirect_to after_destroy_path else flash[:alert] = result[:message] render :show @@ -145,15 +132,33 @@ class Projects::BlobController < Projects::ApplicationController render_404 end + def after_create_path + @after_create_path ||= + if create_merge_request? + new_merge_request_path + else + namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @file_path)) + end + end + def after_edit_path @after_edit_path ||= - if from_merge_request + if create_merge_request? + new_merge_request_path + elsif from_merge_request && @new_branch == @ref diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + "#file-path-#{hexdigest(@path)}" - elsif @target_branch.present? - namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path)) else - namespace_project_blob_path(@project.namespace, @project, @id) + namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @path)) + end + end + + def after_destroy_path + @after_destroy_path ||= + if create_merge_request? + new_merge_request_path + else + namespace_project_tree_path(@project.namespace, @project, @new_branch) end end @@ -168,7 +173,7 @@ class Projects::BlobController < Projects::ApplicationController def editor_variables @current_branch = @ref - @target_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref + @new_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref @file_path = if action_name.to_s == 'create' @@ -188,7 +193,7 @@ class Projects::BlobController < Projects::ApplicationController @commit_params = { file_path: @file_path, current_branch: @current_branch, - target_branch: @target_branch, + target_branch: @new_branch, commit_message: params[:commit_message], file_content: params[:content], file_content_encoding: params[:encoding] diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index bdcb1a3e297..8f272ad1281 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -1,6 +1,7 @@ # Controller for viewing a repository's file structure class Projects::TreeController < Projects::ApplicationController include ExtractsPath + include CreatesMergeRequestForCommit include ActionView::Helpers::SanitizeHelper before_action :require_non_empty_project, except: [:new, :create] @@ -43,7 +44,7 @@ class Projects::TreeController < Projects::ApplicationController if result && result[:status] == :success flash[:notice] = "The directory has been successfully created" respond_to do |format| - format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name)) } + format.html { redirect_to after_create_dir_path } end else flash[:alert] = message @@ -53,6 +54,8 @@ class Projects::TreeController < Projects::ApplicationController end end + private + def assign_dir_vars @new_branch = params[:new_branch].present? ? sanitize(strip_tags(params[:new_branch])) : @ref @dir_name = File.join(@path, params[:dir_name]) @@ -63,4 +66,12 @@ class Projects::TreeController < Projects::ApplicationController commit_message: params[:commit_message], } end + + def after_create_dir_path + if create_merge_request? + new_merge_request_path + else + namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name)) + end + end end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 7e8f61fd274..b804d4f4e3b 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -1,4 +1,24 @@ module MergeRequestsHelper + def new_mr_path_from_push_event(event) + target_project = event.project.forked_from_project || event.project + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, target_project) + ) + end + + def new_mr_from_push_event(event, target_project) + { + merge_request: { + source_project_id: event.project.id, + target_project_id: target_project.id, + source_branch: event.branch_name, + target_branch: target_project.repository.root_ref + } + } + end + def mr_css_classes(mr) classes = "merge-request" classes << " closed" if mr.closed? diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 373b3a0c5b0..ba3e0c3c590 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -19,4 +19,4 @@ - if allowed_tree_edit? .btn-group{ role: "group" } %button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace - %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Remove + %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index a0fc8bbd752..13b5ffd17ff 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -5,21 +5,19 @@ %a.close{href: "#", "data-dismiss" => "modal"} × %h3.page-title Create New Directory .modal-body - = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, id: 'dir-create-form', class: 'form-horizontal' do + = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form' do .form-group = label_tag :dir_name, 'Directory Name', class: 'control-label' .col-sm-10 = text_field_tag :dir_name, params[:dir_name], placeholder: "Directory name", required: true, class: 'form-control' - = render 'shared/commit_message_container', params: params, placeholder: '' - - unless @project.empty_repo? - .form-group - = label_tag :branch_name, 'Branch', class: 'control-label' - .col-sm-10 - = text_field_tag 'new_branch', @ref, class: "form-control" + + = render 'shared/new_commit_form', placeholder: "Add new directory" + .form-group .col-sm-offset-2.col-sm-10 = submit_tag "Create directory", class: 'btn btn-primary btn-create' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript - disableButtonIfAnyEmptyField($("#dir-create-form"), ".form-control", ".btn-create"); + disableButtonIfAnyEmptyField($(".js-create-dir-form"), ".form-control", ".btn-create"); + new NewCommitForm($('.js-create-dir-form')) diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index cae5ff01099..1cf19a7d3db 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -3,16 +3,16 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3.page-title Remove #{@blob.name} - %p.light - From branch - %strong= @ref + %h3.page-title Delete #{@blob.name} .modal-body - = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-requires-input' do - = render 'shared/commit_message_container', params: params, - placeholder: 'Removed this file because...' + = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-replace-blob-form js-requires-input' do + = render 'shared/new_commit_form', placeholder: "Delete #{@blob.name}" + .form-group .col-sm-offset-2.col-sm-10 - = button_tag 'Remove file', class: 'btn btn-remove btn-remove-file' + = button_tag 'Delete file', class: 'btn btn-remove btn-remove-file' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" + +:javascript + new NewCommitForm($('.js-replace-blob-form')) diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index a1c54e731f0..3bb61f0c944 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -5,7 +5,7 @@ %a.close{href: "#", "data-dismiss" => "modal"} × %h3.page-title #{title} .modal-body - = form_tag form_path, method: method, class: 'blob-file-upload-form-js form-horizontal' do + = form_tag form_path, method: method, class: 'js-upload-blob-form form-horizontal' do .dropzone .dropzone-previews.blob-upload-dropzone-previews %p.dz-message.light @@ -13,19 +13,15 @@ = link_to 'click to upload', '#', class: "markdown-selector" %br .dropzone-alerts{class: "alert alert-danger data", style: "display:none"} - = render 'shared/commit_message_container', params: params, - placeholder: placeholder - - unless @project.empty_repo? - .form-group.branch - = label_tag 'branch', class: 'control-label' do - Branch - .col-sm-10 - = text_field_tag 'new_branch', @ref, class: "form-control" + + = render 'shared/new_commit_form', placeholder: placeholder + .form-group .col-sm-offset-2.col-sm-10 = button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript - disableButtonIfEmptyField($('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file'); - new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}'); + disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file'); + new BlobFileDropzone($('.js-upload-blob-form'), '#{method}'); + new NewCommitForm($('.js-upload-blob-form')) diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 26216462707..56745165251 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -13,28 +13,15 @@ %i.fa.fa-eye = editing_preview_title(@blob.name) - = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input') do + = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input js-edit-blob-form') do = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data - = render 'shared/commit_message_container', params: params, placeholder: "Update #{@blob.name}" - - .form-group.branch - = label_tag 'branch', class: 'control-label' do - Branch - .col-sm-10 - = text_field_tag 'new_branch', @ref, class: "form-control" - - .form-group.destination - .col-sm-offset-2.col-sm-10 - .checkbox - = label_tag :create_merge_request do - = check_box_tag :create_merge_request, 1, false - Start a new merge request + = render 'shared/new_commit_form', placeholder: "Update #{@blob.name}" = hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] - = hidden_field_tag 'original_branch', @ref = render 'projects/commit_button', ref: @ref, cancel_path: @after_edit_path :javascript blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}") + new NewCommitForm($('.js-edit-blob-form')) diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 0439e55131e..1ff68005450 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -2,32 +2,18 @@ = render "header_title" .gray-content-block.top-block - Create a new file + %h3.page-title + Create New File .file-editor - = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do + = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-requires-input') do = render 'projects/blob/editor', ref: @ref - = render 'shared/commit_message_container', params: params, - placeholder: 'Add new file' - - - unless @project.empty_repo? - .form-group.branch - = label_tag 'branch', class: 'control-label' do - Branch - .col-sm-10 - = text_field_tag 'new_branch', @ref, class: "form-control js-quick-submit" - - .form-group.destination - .col-sm-offset-2.col-sm-10 - .checkbox - = label_tag :create_merge_request do - = check_box_tag :create_merge_request, 1, false - Start a new merge request + = render 'shared/new_commit_form', placeholder: "Add new file" = hidden_field_tag 'content', '', id: 'file-content' - = hidden_field_tag 'original_branch', @ref = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_tree_path(@project.namespace, @project, @id) :javascript blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null) + new NewCommitForm($('.js-new-blob-form')) diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index f52b89f6921..b7276868ce6 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -10,6 +10,4 @@ = render 'projects/blob/remove' - title = "Replace #{@blob.name}" - = render 'projects/blob/upload', title: title, placeholder: title, - button_title: 'Replace file', form_path: namespace_project_update_blob_path(@project.namespace, @project, @id), - method: :put + = render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index ee4c9d1693d..c64e684df26 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -30,7 +30,7 @@ = render "projects/tree/readme", readme: tree.readme - if allowed_tree_edit? - = render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post + = render 'projects/blob/upload', title: 'Upload New File', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post = render 'projects/blob/new_dir' :javascript diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index cc3f1268f8b..7c57924277e 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -1,13 +1,15 @@ .form-group.commit_message-group - = label_tag 'commit_message', class: 'control-label' do + - nonce = SecureRandom.hex + = label_tag "commit_message-#{nonce}", class: 'control-label' do Commit message .col-sm-10 .commit-message-container .max-width-marker = text_area_tag 'commit_message', (params[:commit_message] || local_assigns[:text]), - class: 'form-control js-quick-submit', placeholder: local_assigns[:placeholder], - required: true, rows: (local_assigns[:rows] || 3) + class: 'form-control js-commit-message js-quick-submit', placeholder: local_assigns[:placeholder], + required: true, rows: (local_assigns[:rows] || 3), + id: "commit_message-#{nonce}" - if local_assigns[:hint] %p.hint Try to keep the first line under 52 characters diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml new file mode 100644 index 00000000000..8636341c60d --- /dev/null +++ b/app/views/shared/_new_commit_form.html.haml @@ -0,0 +1,18 @@ += render 'shared/commit_message_container', placeholder: placeholder + +- unless @project.empty_repo? + .form-group.branch + = label_tag 'branch', class: 'control-label' do + Branch + .col-sm-10 + = text_field_tag 'new_branch', @new_branch || @ref, class: "form-control js-new-branch" + + .form-group.js-create-merge-request-form-group + .col-sm-offset-2.col-sm-10 + .checkbox + - nonce = SecureRandom.hex + = label_tag "create_merge_request-#{nonce}" do + = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}" + Start a new merge request with this commit + + = hidden_field_tag 'original_branch', @ref, class: 'js-original-branch' -- cgit v1.2.1 From 95139a6508ee2ac958c898704eb5a0421ad7487e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 18:54:45 +0100 Subject: Add changelog item --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 3c22df7c9a3..1009b1d1681 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -45,6 +45,7 @@ v 8.2.0 (unreleased) - Fix trailing whitespace issue in merge request/issue title - Fix bug when milestone/label filter was empty for dashboard issues page - Add ability to create milestone in group projects from single form + - Add option to create merge request when editing/creating a file (Dirceu Tiegs) v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) -- cgit v1.2.1 From 08dc38223e0c18233052e04ac95a4f6942fcb1b5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 17 Nov 2015 15:00:14 -0500 Subject: Rename `not_auth_*` ability methods to `anonymous_*` --- app/models/ability.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index f5cd14a89c0..c93139e9039 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,7 +1,7 @@ class Ability class << self def allowed(user, subject) - return not_auth_abilities(user, subject) if user.nil? + return anonymous_abilities(user, subject) if user.nil? return [] unless user.is_a?(User) return [] if user.blocked? @@ -19,22 +19,21 @@ class Ability end.concat(global_abilities(user)) end - # List of possible abilities - # for non-authenticated user - def not_auth_abilities(user, subject) + # List of possible abilities for anonymous user + def anonymous_abilities(user, subject) case true when subject.is_a?(PersonalSnippet) - not_auth_personal_snippet_abilities(subject) + anonymous_personal_snippet_abilities(subject) when subject.is_a?(Project) || subject.respond_to?(:project) - not_auth_project_abilities(subject) + anonymous_project_abilities(subject) when subject.is_a?(Group) || subject.respond_to?(:group) - not_auth_group_abilities(subject) + anonymous_group_abilities(subject) else [] end end - def not_auth_project_abilities(subject) + def anonymous_project_abilities(subject) project = if subject.is_a?(Project) subject else @@ -62,7 +61,7 @@ class Ability end end - def not_auth_group_abilities(subject) + def anonymous_group_abilities(subject) group = if subject.is_a?(Group) subject else @@ -76,7 +75,7 @@ class Ability end end - def not_auth_personal_snippet_abilities(snippet) + def anonymous_personal_snippet_abilities(snippet) if snippet.public? [:read_personal_snippet] else -- cgit v1.2.1 From b6251af40fa8ffdc9e85f5ed4c279774fe0ecaaf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 18 Nov 2015 11:15:49 +0100 Subject: Fix feature spec. --- features/project/source/browse_files.feature | 14 ++++++------- features/steps/project/source/browse_files.rb | 30 +++++++-------------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index 69aa79f2d24..e545ea63ca8 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -42,7 +42,7 @@ Feature: Project Source Browse Files And I fill the new branch name And I click on "Upload file" Then I can see the new text file - And I am redirected to the uploaded file on new branch + And I am redirected to the new merge request page And I can see the new commit message @javascript @@ -64,7 +64,7 @@ Feature: Project Source Browse Files And I fill the commit message And I fill the new branch name And I click on "Commit Changes" - Then I am redirected to the new file on new branch + Then I am redirected to the new merge request page And I should see its new content @javascript @@ -134,7 +134,7 @@ Feature: Project Source Browse Files And I fill the commit message And I fill the new branch name And I click on "Commit Changes" - Then I am redirected to the ".gitignore" on new branch + Then I am redirected to the new merge request page And I should see its new content @javascript @wip @@ -154,7 +154,7 @@ Feature: Project Source Browse Files And I fill the commit message And I fill the new branch name And I click on "Create directory" - Then I am redirected to the new directory + Then I am redirected to the new merge request page @javascript Scenario: I attempt to create an existing directory @@ -174,12 +174,12 @@ Feature: Project Source Browse Files Then I see diff @javascript - Scenario: I can remove file and commit + Scenario: I can delete file and commit Given I click on ".gitignore" file in repo And I see the ".gitignore" - And I click on "Remove" + And I click on "Delete" And I fill the commit message - And I click on "Remove file" + And I click on "Delete file" Then I am redirected to the files URL And I don't see the ".gitignore" diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index 84725b9b585..f40e0f0d528 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -98,12 +98,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps click_button 'Create directory' end - step 'I click on "Remove"' do - click_button 'Remove' + step 'I click on "Delete"' do + click_button 'Delete' end - step 'I click on "Remove file"' do - click_button 'Remove file' + step 'I click on "Delete file"' do + click_button 'Delete file' end step 'I click on "Replace"' do @@ -142,7 +142,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I can see new file page' do - expect(page).to have_content "new file" + expect(page).to have_content "Create New File" expect(page).to have_content "Commit message" end @@ -225,10 +225,6 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps expect(current_path).to eq(namespace_project_blob_path(@project.namespace, @project, 'master/.gitignore')) end - step 'I am redirected to the ".gitignore" on new branch' do - expect(current_path).to eq(namespace_project_blob_path(@project.namespace, @project, 'new_branch_name/.gitignore')) - end - step 'I am redirected to the permalink URL' do expect(current_path).to( eq(namespace_project_blob_path(@project.namespace, @project, @@ -247,20 +243,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps @project.namespace, @project, 'master/' + new_file_name_with_directory)) end - step 'I am redirected to the new file on new branch' do - expect(current_path).to eq(namespace_project_blob_path( - @project.namespace, @project, 'new_branch_name/' + new_file_name)) - end - - step 'I am redirected to the uploaded file on new branch' do - expect(current_path).to eq(namespace_project_blob_path( - @project.namespace, @project, - 'new_branch_name/' + File.basename(test_text_file))) - end - - step 'I am redirected to the new directory' do - expect(current_path).to eq(namespace_project_tree_path( - @project.namespace, @project, 'new_branch_name/' + new_dir_name)) + step 'I am redirected to the new merge request page' do + expect(current_path).to eq(new_namespace_project_merge_request_path(@project.namespace, @project)) end step 'I am redirected to the root directory' do -- cgit v1.2.1 From 531177757eef772cc7ce5dd3898c3e6803187ed6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:19:06 +0100 Subject: Add import_error to project. --- db/migrate/20151110125604_add_import_error_to_project.rb | 5 +++++ db/schema.rb | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20151110125604_add_import_error_to_project.rb diff --git a/db/migrate/20151110125604_add_import_error_to_project.rb b/db/migrate/20151110125604_add_import_error_to_project.rb new file mode 100644 index 00000000000..7fc990f8d0a --- /dev/null +++ b/db/migrate/20151110125604_add_import_error_to_project.rb @@ -0,0 +1,5 @@ +class AddImportErrorToProject < ActiveRecord::Migration + def change + add_column :projects, :import_error, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index aa76cef9fe4..440a33e2006 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -640,7 +640,10 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.integer "commit_count", default: 0 + t.integer "commit_count", default: 0 + t.boolean "merge_requests_ff_only_enabled", default: false + t.text "issues_template" + t.text "import_error" end add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree -- cgit v1.2.1 From 841a7c6897b23957286056498cc3f05ec4330d15 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:22:51 +0100 Subject: Store and show reason why import failed. --- app/models/project.rb | 10 +++-- app/views/projects/imports/new.html.haml | 12 ++++-- app/workers/repository_fork_worker.rb | 14 +++---- app/workers/repository_import_worker.rb | 67 +++++++++++++++++++------------- lib/gitlab/backend/shell.rb | 7 ++-- 5 files changed, 65 insertions(+), 45 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index a099a67cf63..c2ff103759a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -310,15 +310,17 @@ class Project < ActiveRecord::Base def add_import_job if forked? - unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path) - import_fail - end + RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path) else - RepositoryImportWorker.perform_async(id) + RepositoryImportWorker.perform_async(self.id) end end def clear_import_data + update(import_error: nil) + + ProjectCacheWorker.perform_async(self.id) + self.import_data.destroy if self.import_data end diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index 92a87690c54..f7b4416d87e 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -1,12 +1,16 @@ - page_title "Import repository" %h3.page-title - - if @project.import_failed? - Import failed. Retry? - - else - Import repository + Import repository %hr +- if @project.import_failed? + .alert.alert-danger + %p The repository could not be imported. + %pre.prepend-top-10 + :preserve + #{@project.import_error.try(:strip)} + = form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f| .form-group.import-url-data = f.label :import_url, class: 'control-label' do diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index acd1c43f06b..2f991c52339 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -13,22 +13,20 @@ class RepositoryForkWorker end result = gitlab_shell.fork_repository(source_path, target_path) - unless result logger.error("Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}") + project.update(import_error: "The project could not be forked.") project.import_fail - project.save return end - if project.valid_repo? - ProjectCacheWorker.perform_async(project.id) - project.import_finish - else - project.import_fail + unless project.valid_repo? logger.error("Project #{id} had an invalid repository after fork") + project.update(import_error: "The forked repository is invalid.") + project.import_fail + return end - project.save + project.import_finish end end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index ea2808045eb..5be2245df28 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -7,37 +7,52 @@ class RepositoryImportWorker def perform(project_id) project = Project.find(project_id) - unless project.import_url == Project::UNKNOWN_IMPORT_URL - import_result = gitlab_shell.send(:import_repository, - project.path_with_namespace, - project.import_url) - return project.import_fail unless import_result - else + if project.import_url == Project::UNKNOWN_IMPORT_URL + # In this case, we only want to import issues, not a repository. unless project.create_repository - return project.import_fail + project.update(import_error: "The repository could not be created.") + project.import_fail + return + end + else + begin + import_result = gitlab_shell.import_repository(project.path_with_namespace, project.import_url) + rescue Gitlab::Shell::Error => e + project.update(import_error: e.message) + project.import_fail + return end end - data_import_result = case project.import_type - when 'github' - Gitlab::GithubImport::Importer.new(project).execute - when 'gitlab' - Gitlab::GitlabImport::Importer.new(project).execute - when 'bitbucket' - Gitlab::BitbucketImport::Importer.new(project).execute - when 'google_code' - Gitlab::GoogleCodeImport::Importer.new(project).execute - when 'fogbugz' - Gitlab::FogbugzImport::Importer.new(project).execute - else - true - end - return project.import_fail unless data_import_result - - Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket' + data_import_result = + case project.import_type + when 'github' + Gitlab::GithubImport::Importer.new(project).execute + when 'gitlab' + Gitlab::GitlabImport::Importer.new(project).execute + when 'bitbucket' + Gitlab::BitbucketImport::Importer.new(project).execute + when 'google_code' + Gitlab::GoogleCodeImport::Importer.new(project).execute + when 'fogbugz' + Gitlab::FogbugzImport::Importer.new(project).execute + else + true + end + + unless data_import_result + project.update(import_error: "The remote issue data could not be imported.") + project.import_fail + return + end + + if project.import_type == 'bitbucket' + Gitlab::BitbucketImport::KeyDeleter.new(project).execute + end project.import_finish - project.save - ProjectCacheWorker.perform_async(project.id) + + # Explicitly update mirror so that upstream remote is created and fetched + project.update_mirror end end diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index 01b8bda05c6..87ac30b5ffe 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -1,6 +1,6 @@ module Gitlab class Shell - class AccessDenied < StandardError; end + class Error < StandardError; end class KeyAdder < Struct.new(:io) def add_key(id, key) @@ -36,8 +36,9 @@ module Gitlab # import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git") # def import_repository(name, url) - Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'import-project', - "#{name}.git", url, '240']) + output, status = Popen::popen([gitlab_shell_projects_path, 'import-project', "#{name}.git", url, '240']) + raise Error, output unless status.zero? + true end # Move repository -- cgit v1.2.1 From 40470975e863b271f09fe147eb2eb545211e1b08 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:23:51 +0100 Subject: Add Project#safe_import_url helper. --- app/helpers/projects_helper.rb | 8 -------- app/models/project.rb | 8 ++++++++ app/views/projects/imports/show.html.haml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 690ae2090db..c9cd4a0d54c 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -253,14 +253,6 @@ module ProjectsHelper filename_path(project, :version) end - def hidden_pass_url(original_url) - result = URI(original_url) - result.password = '*****' unless result.password.nil? - result - rescue - original_url - end - def project_wiki_path_with_version(proj, page, version, is_newest) url_params = is_newest ? {} : { version_id: version } namespace_project_wiki_path(proj.namespace, proj, page, url_params) diff --git a/app/models/project.rb b/app/models/project.rb index c2ff103759a..70a648e68a3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -348,6 +348,14 @@ class Project < ActiveRecord::Base import_status == 'finished' end + def safe_import_url + result = URI.parse(self.import_url) + result.password = '*****' unless result.password.nil? + result.to_s + rescue + original_url + end + def check_limit unless creator.can_create_project? or namespace.kind == 'group' errors[:limit_reached] << ("Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it") diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml index 06886d215a3..c0d1ce0d120 100644 --- a/app/views/projects/imports/show.html.haml +++ b/app/views/projects/imports/show.html.haml @@ -8,7 +8,7 @@ - else Import in progress. - unless @project.forked? - %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)} + %p.monospace git clone --bare #{@project.safe_import_url} %p Please wait while we import the repository for you. Refresh at will. :javascript new ProjectImport(); -- cgit v1.2.1 From 01d2b1943f009720a3f8a51e441dcc18f1706c9f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:26:12 +0100 Subject: Move import form to partial. --- app/assets/stylesheets/framework/common.scss | 4 ++++ app/views/projects/imports/new.html.haml | 12 ++---------- app/views/projects/new.html.haml | 15 +-------------- app/views/shared/_import_form.html.haml | 16 ++++++++++++++++ 4 files changed, 23 insertions(+), 24 deletions(-) create mode 100644 app/views/shared/_import_form.html.haml diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index ddbacd7fd41..40f4beb1968 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -328,6 +328,10 @@ table { } } +.well { + margin-bottom: 0; +} + .search_box { @extend .well; text-align: center; diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index f7b4416d87e..01b247336ee 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -12,15 +12,7 @@ #{@project.import_error.try(:strip)} = form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f| - .form-group.import-url-data - = f.label :import_url, class: 'control-label' do - %span Import existing git repo - .col-sm-10 - = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' - .well.prepend-top-20 - This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. - %br - The import will time out after 4 minutes. For big repositories, use a clone/push combination. - For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"} + = render "shared/import_form", f: f + .form-actions = f.submit 'Start import', class: "btn btn-create", tabindex: 4 diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index a02c12f06a8..c9d1fc3da21 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -23,7 +23,6 @@ = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} - if import_sources_enabled? - .project-import.js-toggle-container .form-group %label.control-label Import project from @@ -82,19 +81,7 @@ %span Any repo by URL .js-toggle-content.hide - .form-group.import-url-data - = f.label :import_url, class: 'control-label' do - %span Git repository URL - .col-sm-10 - = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git' - .well.prepend-top-20 - %ul - %li - The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL: https://username:password@gitlab.company.com/group/project.git. - %li - The import will time out after 4 minutes. For big repositories, 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"}. + = render "shared/import_form", f: f .prepend-botton-10 diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml new file mode 100644 index 00000000000..285af56ad73 --- /dev/null +++ b/app/views/shared/_import_form.html.haml @@ -0,0 +1,16 @@ +.form-group.import-url-data + = f.label :import_url, class: 'control-label' do + %span Git repository URL + .col-sm-10 + = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git' + + .well.prepend-top-20 + %ul + %li + The repository must be accessible over http://, https:// or git://. + %li + If your HTTP repository is not publicly accessible, add authentication information to the URL: https://username:password@gitlab.company.com/group/project.git. + %li + The import will time out after 4 minutes. For big repositories, 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"}. -- cgit v1.2.1 From 7b405d306431448f384591de792497e719d71caa Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:25:01 +0100 Subject: Fix redirect after import fails. --- app/controllers/projects/imports_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb index 066b66014f8..fb8788f0818 100644 --- a/app/controllers/projects/imports_controller.rb +++ b/app/controllers/projects/imports_controller.rb @@ -28,8 +28,8 @@ class Projects::ImportsController < Projects::ApplicationController if @project.import_finished? redirect_to(project_path(@project)) and return else - redirect_to new_namespace_project_import_path(@project.namespace, - @project) && return + redirect_to(new_namespace_project_import_path(@project.namespace, + @project)) and return end end end -- cgit v1.2.1 From a0519818cb4ff90d6a1d66b6360ec94f05bc79ec Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:26:43 +0100 Subject: Tweak code formatting. --- app/services/projects/create_service.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 5b84527eccf..700a1db04d8 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -55,7 +55,9 @@ module Projects @project.save if @project.persisted? && !@project.import? - raise 'Failed to create repository' unless @project.create_repository + unless @project.create_repository + raise 'Failed to create repository' + end end end @@ -94,9 +96,7 @@ module Projects @project.team << [current_user, :master, current_user] end - if @project.import? - @project.import_start - end + @project.import_start if @project.import? end end end -- cgit v1.2.1 From 153085a93bf7f2350d15009fe7924cea190cd7f2 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:27:55 +0100 Subject: Add Repository#is_ancestor? convenience method. --- app/models/repository.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index f76b770e867..c5b6ee80dc6 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -484,7 +484,7 @@ class Repository root_ref_commit = commit(root_ref) if branch_commit - rugged.merge_base(root_ref_commit.id, branch_commit.id) == branch_commit.id + is_ancestor?(branch_commit.id, root_ref_commit.id) else nil end @@ -494,6 +494,11 @@ class Repository rugged.merge_base(first_commit_id, second_commit_id) end + def is_ancestor?(ancestor_id, descendant_id) + merge_base(ancestor_id, descendant_id) == ancestor_id + end + + def search_files(query, ref) offset = 2 args = %W(#{Gitlab.config.git.bin_path} grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref}) -- cgit v1.2.1 From b1f4d14f7d521b1ceb167a52a188eb93e9384db4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:28:31 +0100 Subject: Clean up Repository cache code. --- app/models/repository.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index c5b6ee80dc6..fd60c7edbb1 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -105,29 +105,25 @@ class Repository end def add_branch(branch_name, ref) - cache.expire(:branch_names) - @branches = nil + expire_branches_cache gitlab_shell.add_branch(path_with_namespace, branch_name, ref) end def add_tag(tag_name, ref, message = nil) - cache.expire(:tag_names) - @tags = nil + expire_tags_cache gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message) end def rm_branch(branch_name) - cache.expire(:branch_names) - @branches = nil + expire_branches_cache gitlab_shell.rm_branch(path_with_namespace, branch_name) end def rm_tag(tag_name) - cache.expire(:tag_names) - @tags = nil + expire_tags_cache gitlab_shell.rm_tag(path_with_namespace, tag_name) end @@ -169,6 +165,16 @@ class Repository end end + def expire_tags_cache + cache.expire(:tag_names) + @tags = nil + end + + def expire_branches_cache + cache.expire(:branch_names) + @branches = nil + end + def expire_cache cache_keys.each do |key| cache.expire(key) -- cgit v1.2.1 From 91fdbdcde7f2d58f6f5987640eed538588822b93 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 12:44:33 +0100 Subject: Add tooltips to home panel buttons. --- app/views/projects/_home_panel.html.haml | 15 +++++---------- app/views/projects/buttons/_download.html.haml | 4 ++++ app/views/projects/buttons/_dropdown.html.haml | 2 -- app/views/projects/buttons/_fork.html.haml | 25 +++++++++++++------------ app/views/projects/buttons/_star.html.haml | 2 +- app/views/shared/_clone_panel.html.haml | 5 ++--- 6 files changed, 25 insertions(+), 28 deletions(-) create mode 100644 app/views/projects/buttons/_download.html.haml diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 8c0980369fd..88d54bf6f21 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -18,17 +18,12 @@ .project-repo-buttons .split-one = render 'projects/buttons/star' + = render 'projects/buttons/fork' - - unless empty_repo - = render 'projects/buttons/fork' - = render "shared/clone_panel" - .split-repo-buttons - - unless empty_repo - - if can? current_user, :download_code, @project - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn', rel: 'nofollow' do - = icon('download fw') - + + .split-repo-buttons + = render "projects/buttons/download" = render 'projects/buttons/dropdown' + = render 'projects/buttons/notifications' - diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml new file mode 100644 index 00000000000..14ee2263b7d --- /dev/null +++ b/app/views/projects/buttons/_download.html.haml @@ -0,0 +1,4 @@ +- unless @project.empty_repo? + - if can? current_user, :download_code, @project + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has_tooltip', rel: 'nofollow', title: "Download ZIP" do + = icon('download') diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 18cae8ef6d3..b277b765b6b 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -32,5 +32,3 @@ = link_to new_namespace_project_tag_path(@project.namespace, @project) do = icon('tags fw') New tag - - diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index 8f2f631eb7d..2d3abf09051 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -1,12 +1,13 @@ -- if current_user && can?(current_user, :fork_project, @project) - - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn' do - = icon('code-fork fw') - Fork - %span.count - = @project.forks_count - - else - = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn' do - = icon('code-fork fw') - %span.count - = @project.forks_count +- unless @project.empty_repo? + - if current_user && can?(current_user, :fork_project, @project) + - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 + = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has_tooltip' do + = icon('code-fork fw') + Fork + %span.count + = @project.forks_count + - else + = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do + = icon('code-fork fw') + %span.count + = @project.forks_count diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 06583902035..41a3ec6d90f 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -1,5 +1,5 @@ - if current_user - = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star', method: :post, remote: true do + = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has_tooltip', method: :post, remote: true, title: "Star project" do = icon('star fw') %span.count = @project.star_count diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 2e4aab36301..8bcb24ae9df 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -21,7 +21,6 @@ = gitlab_config.protocol.upcase = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true - if project.kind_of?(Project) - .input-group-addon - .visibility-level-label.has_tooltip{'data-title' => "#{visibility_level_label(project.visibility_level)} project" } + .input-group-addon.has_tooltip{title: "#{visibility_level_label(project.visibility_level)} project", data: { container: "body" } } + .visibility-level-label = visibility_level_icon(project.visibility_level) - -- cgit v1.2.1 From 5036d8d5d55a8b22e423fb440d75f3d3949038d6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 12:45:49 +0100 Subject: Fix styling of import error. --- app/assets/stylesheets/framework/tw_bootstrap.scss | 2 +- app/views/projects/imports/new.html.haml | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index 99d028d1228..50c0cf61f4e 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -172,7 +172,7 @@ } .panel-body { - form { + form, pre { margin: 0; } diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index 01b247336ee..6027fb23360 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -5,11 +5,12 @@ %hr - if @project.import_failed? - .alert.alert-danger - %p The repository could not be imported. - %pre.prepend-top-10 - :preserve - #{@project.import_error.try(:strip)} + .panel.panel-danger + .panel-heading The repository could not be imported. + .panel-body + %pre + :preserve + #{@project.import_error.try(:strip)} = form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f| = render "shared/import_form", f: f -- cgit v1.2.1 From 5e8ddd3c2d898367f6bb377df02e40754850d4f3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 12:53:00 +0100 Subject: Remove unused variable in repository import --- app/workers/repository_import_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 5be2245df28..1de49161997 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -16,7 +16,7 @@ class RepositoryImportWorker end else begin - import_result = gitlab_shell.import_repository(project.path_with_namespace, project.import_url) + gitlab_shell.import_repository(project.path_with_namespace, project.import_url) rescue Gitlab::Shell::Error => e project.update(import_error: e.message) project.import_fail -- cgit v1.2.1 From d0ec28827d33d00ca637b1962b370313701ac3a0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 13:46:50 +0100 Subject: Fix specs --- features/steps/dashboard/new_project.rb | 1 - spec/services/projects/fork_service_spec.rb | 17 +++++------------ spec/workers/repository_fork_worker_spec.rb | 1 - 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb index 44a4aa9844a..a0aad66184d 100644 --- a/features/steps/dashboard/new_project.rb +++ b/features/steps/dashboard/new_project.rb @@ -44,7 +44,6 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps git_import_instructions = first('.js-toggle-content') expect(git_import_instructions).to be_visible expect(git_import_instructions).to have_content "Git repository URL" - expect(git_import_instructions).to have_content "The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL:" end step 'I click on "Google Code"' do diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index e397b2b9b4a..1feba6ce048 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -25,13 +25,6 @@ describe Projects::ForkService do end end - context 'fork project failure' do - it "fails due to transaction failure" do - @to_project = fork_project(@from_project, @to_user, false) - expect(@to_project.import_failed?) - end - end - context 'project already exists' do it "should fail due to validation, not transaction failure" do @existing_project = create(:project, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace) @@ -66,7 +59,7 @@ describe Projects::ForkService do context 'fork project for group' do it 'group owner successfully forks project into the group' do - to_project = fork_project(@project, @group_owner, true, @opts) + to_project = fork_project(@project, @group_owner, @opts) expect(to_project.owner).to eq(@group) expect(to_project.namespace).to eq(@group) expect(to_project.name).to eq(@project.name) @@ -78,7 +71,7 @@ describe Projects::ForkService do context 'fork project for group when user not owner' do it 'group developer should fail to fork project into the group' do - to_project = fork_project(@project, @developer, true, @opts) + to_project = fork_project(@project, @developer, @opts) expect(to_project.errors[:namespace]).to eq(['is not valid']) end end @@ -87,7 +80,7 @@ describe Projects::ForkService do it 'should fail due to validation, not transaction failure' do existing_project = create(:project, name: @project.name, namespace: @group) - to_project = fork_project(@project, @group_owner, true, @opts) + to_project = fork_project(@project, @group_owner, @opts) expect(existing_project.persisted?).to be_truthy expect(to_project.errors[:name]).to eq(['has already been taken']) expect(to_project.errors[:path]).to eq(['has already been taken']) @@ -95,8 +88,8 @@ describe Projects::ForkService do end end - def fork_project(from_project, user, fork_success = true, params = {}) - allow(RepositoryForkWorker).to receive(:perform_async).and_return(fork_success) + def fork_project(from_project, user, params = {}) + allow(RepositoryForkWorker).to receive(:perform_async).and_return(true) Projects::ForkService.new(from_project, user, params).execute end end diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index aa031106968..245f066df1f 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -12,7 +12,6 @@ describe RepositoryForkWorker do project.path_with_namespace, fork_project.namespace.path). and_return(true) - expect(ProjectCacheWorker).to receive(:perform_async) subject.perform(project.id, project.path_with_namespace, -- cgit v1.2.1 From 8937c5ff7adb309f7b52a3ecf7dfe3181e2ceb1b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 13:47:48 +0100 Subject: Load raw repository lazily to recover from failed read --- app/models/repository.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index fd60c7edbb1..c1836103463 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -6,7 +6,7 @@ class Repository include Gitlab::ShellAdapter - attr_accessor :raw_repository, :path_with_namespace, :project + attr_accessor :path_with_namespace, :project def self.clean_old_archives repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path @@ -19,14 +19,18 @@ class Repository def initialize(path_with_namespace, default_branch = nil, project = nil) @path_with_namespace = path_with_namespace @project = project + end - if path_with_namespace - @raw_repository = Gitlab::Git::Repository.new(path_to_repo) - @raw_repository.autocrlf = :input - end + def raw_repository + return nil unless path_with_namespace - rescue Gitlab::Git::Repository::NoRepository - nil + @raw_repository ||= begin + repo = Gitlab::Git::Repository.new(path_to_repo) + repo.autocrlf = :input + repo + rescue Gitlab::Git::Repository::NoRepository + nil + end end # Return absolute path to repository -- cgit v1.2.1 From e809669383d528e80d6408c4838566cf9f0bb8ac Mon Sep 17 00:00:00 2001 From: Arseny Zarechnev Date: Tue, 17 Nov 2015 17:47:39 +0000 Subject: Fix github importer to handle empty issues --- lib/gitlab/github_import/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index bd7340a80f1..b5720f6e2cb 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -19,7 +19,7 @@ module Gitlab if issue.pull_request.nil? body = @formatter.author_line(issue.user.login) - body += issue.body + body += issue.body || "" if issue.comments > 0 body += @formatter.comments_header -- cgit v1.2.1 From 7763e61ae59b9b610cb87b30ef5ca90c94a011ea Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 18 Nov 2015 12:09:33 +0100 Subject: Use gitlab-shell 2.6.7 --- GITLAB_SHELL_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 338a5b5d8fe..e261122d5c4 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.6.6 +2.6.7 -- cgit v1.2.1 From f5e3d93c28ff9e1a7e0ba9bddbbc242709f0fa8b Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 13:01:35 +0100 Subject: Check which folders and archives should be packed before passing to tar command. --- lib/backup/manager.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 9e15d5411a1..69922eb66ea 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -150,17 +150,15 @@ module Backup private def backup_contents - folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "artifacts.tar.gz", "backup_information.yml"] + folders_to_backup + archives_to_backup + ["backup_information.yml"] end - def folders_to_backup - folders = %w{repositories db} - - if ENV["SKIP"] - return folders.reject{ |folder| ENV["SKIP"].include?(folder) } - end + def archives_to_backup + %w{uploads builds artifacts}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact + end - folders + def folders_to_backup + %w{repositories db}.map{ |name| name unless skipped?(name) }.compact end def settings -- cgit v1.2.1 From 054f2f98eda60682fd796a1f66681accc6f7ce1c Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 17:14:47 +0100 Subject: Faster way of obtaining latest event update time Instead of using MAX(events.updated_at) we can simply sort the events in descending order by the "id" column and grab the first row. In other words, instead of this: SELECT max(events.updated_at) AS max_id FROM events LEFT OUTER JOIN projects ON projects.id = events.project_id LEFT OUTER JOIN namespaces ON namespaces.id = projects.namespace_id WHERE events.author_id IS NOT NULL AND events.project_id IN (13083); we can use this: SELECT events.updated_at AS max_id FROM events LEFT OUTER JOIN projects ON projects.id = events.project_id LEFT OUTER JOIN namespaces ON namespaces.id = projects.namespace_id WHERE events.author_id IS NOT NULL AND events.project_id IN (13083) ORDER BY events.id DESC LIMIT 1; This has the benefit that on PostgreSQL a backwards index scan can be used, which due to the "LIMIT 1" will at most process only a single row. This in turn greatly speeds up the process of grabbing the latest update time. This can be confirmed by looking at the query plans. The first query produces the following plan: Aggregate (cost=43779.84..43779.85 rows=1 width=12) (actual time=2142.462..2142.462 rows=1 loops=1) -> Index Scan using index_events_on_project_id on events (cost=0.43..43704.69 rows=30060 width=12) (actual time=0.033..2138.086 rows=32769 loops=1) Index Cond: (project_id = 13083) Filter: (author_id IS NOT NULL) Planning time: 1.248 ms Execution time: 2142.548 ms The second query in turn produces the following plan: Limit (cost=0.43..41.65 rows=1 width=16) (actual time=1.394..1.394 rows=1 loops=1) -> Index Scan Backward using events_pkey on events (cost=0.43..1238907.96 rows=30060 width=16) (actual time=1.394..1.394 rows=1 loops=1) Filter: ((author_id IS NOT NULL) AND (project_id = 13083)) Rows Removed by Filter: 2104 Planning time: 0.166 ms Execution time: 1.408 ms According to the above plans the 2nd query is around 1500 times faster. However, re-running the first query produces timings of around 80 ms, making the 2nd query "only" around 55 times faster. --- app/models/event.rb | 6 ++++++ app/views/dashboard/projects/index.atom.builder | 2 +- app/views/groups/show.atom.builder | 2 +- app/views/projects/show.atom.builder | 2 +- app/views/users/show.atom.builder | 2 +- spec/models/event_spec.rb | 21 +++++++++++++++++++++ 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index bf64ac29d32..7618b503d84 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -63,6 +63,12 @@ class Event < ActiveRecord::Base Event::PUSHED, ["MergeRequest", "Issue"], [Event::CREATED, Event::CLOSED, Event::MERGED]) end + + def latest_update_time + row = select(:updated_at, :project_id).reorder(id: :desc).take + + row ? row.updated_at : nil + end end def proper? diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder index d2c51486841..c8c219f4cca 100644 --- a/app/views/dashboard/projects/index.atom.builder +++ b/app/views/dashboard/projects/index.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: dashboard_projects_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html" xml.id dashboard_projects_url - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index a91d1a6e94b..7ea574434c3 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: group_url(@group, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: group_url(@group), rel: "alternate", type: "text/html" xml.id group_url(@group) - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder index 242684e5c7c..15c49767556 100644 --- a/app/views/projects/show.atom.builder +++ b/app/views/projects/show.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html" xml.id namespace_project_url(@project.namespace, @project) - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder index 50232dc7186..2fe5b7fac83 100644 --- a/app/views/users/show.atom.builder +++ b/app/views/users/show.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml" xml.link href: user_url(@user), rel: "alternate", type: "text/html" xml.id user_url(@user) - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 0f32f162a10..f46c50ed6f3 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -64,4 +64,25 @@ describe Event do it { expect(@event.branch_name).to eq("master") } it { expect(@event.author).to eq(@user) } end + + describe '.latest_update_time' do + describe 'when events are present' do + let(:time) { Time.utc(2015, 1, 1) } + + before do + create(:closed_issue_event, updated_at: time) + create(:closed_issue_event, updated_at: time + 5) + end + + it 'returns the latest update time' do + expect(Event.latest_update_time).to eq(time + 5) + end + end + + describe 'when no events exist' do + it 'returns nil' do + expect(Event.latest_update_time).to be_nil + end + end + end end -- cgit v1.2.1 From d769596aec7daa2ff43f86c8fad4211fbc4f607d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 16:16:49 +0100 Subject: Added Gitlab::SQL::Union class This class can be used to join multiple AcitveRecord::Relation objects together using a SQL UNION statement. ActiveRecord < 5.0 sadly doesn't support UNION and existing Gems out there don't handle prepared statements (e.g. they never incremented the variable bindings). --- lib/gitlab/sql/union.rb | 34 ++++++++++++++++++++++++++++++++++ spec/lib/gitlab/sql/union_spec.rb | 16 ++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 lib/gitlab/sql/union.rb create mode 100644 spec/lib/gitlab/sql/union_spec.rb diff --git a/lib/gitlab/sql/union.rb b/lib/gitlab/sql/union.rb new file mode 100644 index 00000000000..1a62eff0b31 --- /dev/null +++ b/lib/gitlab/sql/union.rb @@ -0,0 +1,34 @@ +module Gitlab + module SQL + # Class for building SQL UNION statements. + # + # ORDER BYs are dropped from the relations as the final sort order is not + # guaranteed any way. + # + # Example usage: + # + # union = Gitlab::SQL::Union.new(user.personal_projects, user.projects) + # sql = union.to_sql + # + # Project.where("id IN (#{sql})") + class Union + def initialize(relations) + @relations = relations + end + + def to_sql + # Some relations may include placeholders for prepared statements, these + # aren't incremented properly when joining relations together this way. + # By using "unprepared_statements" we remove the usage of placeholders + # (thus fixing this problem), at a slight performance cost. + fragments = ActiveRecord::Base.connection.unprepared_statement do + @relations.map do |rel| + "(#{rel.reorder(nil).to_sql})" + end + end + + fragments.join(' UNION ') + end + end + end +end diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb new file mode 100644 index 00000000000..976360af9b5 --- /dev/null +++ b/spec/lib/gitlab/sql/union_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe Gitlab::SQL::Union do + describe '#to_sql' do + it 'returns a String joining relations together using a UNION' do + rel1 = User.where(email: 'alice@example.com') + rel2 = User.where(email: 'bob@example.com') + union = described_class.new([rel1, rel2]) + + sql1 = rel1.reorder(nil).to_sql + sql2 = rel2.reorder(nil).to_sql + + expect(union.to_sql).to eq("(#{sql1}) UNION (#{sql2})") + end + end +end -- cgit v1.2.1 From 028bd227fb1915edca181331542c433fd171d31a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 16:29:40 +0100 Subject: Use SQL::Union for User#authorized_projects This allows retrieving of the list of authorized projects using a single query, without having to load any IDs into Ruby. This in turn also means we can remove the method User#authorized_projects_id. --- app/models/user.rb | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 61abea1f6ea..8f34f52be1d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -397,26 +397,9 @@ class User < ActiveRecord::Base end end - def authorized_projects_id - @authorized_projects_id ||= begin - project_ids = personal_projects.pluck(:id) - project_ids.push(*groups_projects.pluck(:id)) - project_ids.push(*projects.pluck(:id).uniq) - end - end - - def master_or_owner_projects_id - @master_or_owner_projects_id ||= begin - scope = { access_level: [ Gitlab::Access::MASTER, Gitlab::Access::OWNER ] } - project_ids = personal_projects.pluck(:id) - project_ids.push(*groups_projects.where(members: scope).pluck(:id)) - project_ids.push(*projects.where(members: scope).pluck(:id).uniq) - end - end - # Projects user has access to def authorized_projects - @authorized_projects ||= Project.where(id: authorized_projects_id) + @authorized_projects ||= Project.where("id IN (#{projects_union.to_sql})") end def owned_projects @@ -743,8 +726,8 @@ class User < ActiveRecord::Base Event.contributions.where(author_id: self). where("created_at > ?", Time.now - 1.year). reorder(project_id: :desc). - select(:project_id). - uniq.map(&:project_id) + uniq. + pluck(:project_id) end def restricted_signup_domains @@ -774,11 +757,26 @@ class User < ActiveRecord::Base !solo_owned_groups.present? end + def ci_authorized_projects + @ci_authorized_projects ||= + Ci::Project.where("gitlab_id IN (#{projects_union.to_sql})") + end + def ci_authorized_runners @ci_authorized_runners ||= begin runner_ids = Ci::RunnerProject.joins(:project). - where(ci_projects: { gitlab_id: master_or_owner_projects_id }).select(:runner_id) + where("ci_projects.gitlab_id IN (#{projects_union.to_sql})"). + select(:runner_id) + Ci::Runner.specific.where(id: runner_ids) end end + + private + + def projects_union + Gitlab::SQL::Union.new([personal_projects.select(:id), + groups_projects.select(:id), + projects.select(:id)]) + end end -- cgit v1.2.1 From 656d9ff69b372be8e0aebb94418c487e76e04256 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 16:30:39 +0100 Subject: Make it easier to re-apply default sort orders By moving the default sort order into a separate scope (and calling this from the default scope) we can more easily re-apply a default order without having to specify the exact column/ordering all over the place. --- app/models/concerns/sortable.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 913c747a1c3..7391a77383c 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -8,8 +8,9 @@ module Sortable included do # By default all models should be ordered # by created_at field starting from newest - default_scope { order(id: :desc) } + default_scope { order_id_desc } + scope :order_id_desc, -> { reorder(id: :desc) } scope :order_created_desc, -> { reorder(created_at: :desc) } scope :order_created_asc, -> { reorder(created_at: :asc) } scope :order_updated_desc, -> { reorder(updated_at: :desc) } -- cgit v1.2.1 From 189c40c33d18df08dd40e9f009f6658f89e3af0e Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 17:38:20 +0100 Subject: Use SQL::Union for User#authorized_groups This removes the need for plucking any IDs into Ruby. --- app/models/user.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 8f34f52be1d..ddb3158e0f5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -391,10 +391,13 @@ class User < ActiveRecord::Base # Groups user has access to def authorized_groups - @authorized_groups ||= begin - group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id)) - Group.where(id: group_ids) - end + @authorized_groups ||= + begin + union = Gitlab::SQL::Union. + new([groups.select(:id), authorized_projects.select(:namespace_id)]) + + Group.where("id IN (#{union.to_sql})") + end end # Projects user has access to -- cgit v1.2.1 From bfd9855a2b2a09e8f5bff89e84891d3ad598fe0d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 17:50:43 +0100 Subject: Prefix table names for User UNIONs --- app/models/user.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index ddb3158e0f5..a2258967e27 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -396,13 +396,14 @@ class User < ActiveRecord::Base union = Gitlab::SQL::Union. new([groups.select(:id), authorized_projects.select(:namespace_id)]) - Group.where("id IN (#{union.to_sql})") + Group.where("namespaces.id IN (#{union.to_sql})") end end # Projects user has access to def authorized_projects - @authorized_projects ||= Project.where("id IN (#{projects_union.to_sql})") + @authorized_projects ||= + Project.where("projects.id IN (#{projects_union.to_sql})") end def owned_projects -- cgit v1.2.1 From 5fcd9986b86812786c90a0b8d3461db79ce71051 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 16 Nov 2015 14:28:02 +0100 Subject: Refactor getting user groups/projects/contributions This new setup no longer loads any IDs into memory using "pluck", instead using SQL UNIONs to merge the various datasets together. This results in greatly improved query performance as well as a reduction of memory usage. The old setup was in particular problematic when requesting the authorized projects _including_ public/internal projects as this would result in roughly 65000 project IDs being loaded into memory. These IDs would in turn be passed to other queries. --- app/models/user.rb | 64 ++++++++++++++++++++++++++++++++++++------------ spec/models/user_spec.rb | 52 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 96 insertions(+), 20 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index a2258967e27..d523b3f0491 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -389,21 +389,40 @@ class User < ActiveRecord::Base end end - # Groups user has access to - def authorized_groups - @authorized_groups ||= - begin - union = Gitlab::SQL::Union. - new([groups.select(:id), authorized_projects.select(:namespace_id)]) + # Returns the groups a user has access to, optionally including any public + # groups. + # + # public_internal - When set to "true" all public groups and groups of public + # projects are also included. + # + # Returns an ActiveRecord::Relation + def authorized_groups(public_internal = false) + union = Gitlab::SQL::Union. + new([groups.select(:id), authorized_projects(public_internal). + select(:namespace_id)]) - Group.where("namespaces.id IN (#{union.to_sql})") - end + sql = "namespaces.id IN (#{union.to_sql})" + + if public_internal + sql << ' OR public IS TRUE' + end + + Group.where(sql) end - # Projects user has access to - def authorized_projects - @authorized_projects ||= - Project.where("projects.id IN (#{projects_union.to_sql})") + # Returns the groups a user is authorized to access. + # + # public_internal - When set to "true" all public/internal projects will also + # be included. + def authorized_projects(public_internal = false) + base = "projects.id IN (#{projects_union.to_sql})" + + if public_internal + Project.where("#{base} OR projects.visibility_level IN (?)", + Project.public_and_internal_levels) + else + Project.where(base) + end end def owned_projects @@ -726,12 +745,25 @@ class User < ActiveRecord::Base Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil) end - def contributed_projects_ids - Event.contributions.where(author_id: self). + # Returns the projects a user contributed to in the last year. + # + # This method relies on a subquery as this performs significantly better + # compared to a JOIN when coupled with, for example, + # `Project.visible_to_user`. That is, consider the following code: + # + # some_user.contributed_projects.visible_to_user(other_user) + # + # If this method were to use a JOIN the resulting query would take roughly 200 + # ms on a database with a similar size to gitlab.com's database. On the other + # hand, using a subquery means we can get the exact same data in about 40 ms. + def contributed_projects + events = Event.select(:project_id). + contributions.where(author_id: self). where("created_at > ?", Time.now - 1.year). - reorder(project_id: :desc). uniq. - pluck(:project_id) + reorder(nil) + + Project.where(id: events) end def restricted_signup_domains diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 7d716c23120..71160f8dfef 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -686,7 +686,7 @@ describe User do end end - describe "#contributed_projects_ids" do + describe "#contributed_projects" do subject { create(:user) } let!(:project1) { create(:project) } let!(:project2) { create(:project, forked_from_project: project3) } @@ -701,15 +701,15 @@ describe User do end it "includes IDs for projects the user has pushed to" do - expect(subject.contributed_projects_ids).to include(project1.id) + expect(subject.contributed_projects).to include(project1) end it "includes IDs for projects the user has had merge requests merged into" do - expect(subject.contributed_projects_ids).to include(project3.id) + expect(subject.contributed_projects).to include(project3) end it "doesn't include IDs for unrelated projects" do - expect(subject.contributed_projects_ids).not_to include(project2.id) + expect(subject.contributed_projects).not_to include(project2) end end @@ -758,4 +758,48 @@ describe User do expect(subject.recent_push).to eq(nil) end end + + describe '#authorized_groups' do + let!(:user) { create(:user) } + let!(:private_group) { create(:group) } + let!(:public_group) { create(:group, public: true) } + + before do + private_group.add_user(user, Gitlab::Access::MASTER) + end + + describe 'excluding public groups' do + subject { user.authorized_groups } + + it { is_expected.to eq([private_group]) } + end + + describe 'including public groups' do + subject { user.authorized_groups(true) } + + it { is_expected.to eq([public_group, private_group]) } + end + end + + describe '#authorized_projects' do + let!(:user) { create(:user) } + let!(:private_project) { create(:project, :private) } + let!(:public_project) { create(:project, :public) } + + before do + private_project.team << [user, Gitlab::Access::MASTER] + end + + describe 'excluding public projects' do + subject { user.authorized_projects } + + it { is_expected.to eq([private_project]) } + end + + describe 'including public projects' do + subject { user.authorized_projects(true) } + + it { is_expected.to eq([public_project, private_project]) } + end + end end -- cgit v1.2.1 From b4646391eeb23b9f9828f0913e06ae78d298726e Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:02:14 +0100 Subject: Renamed GroupsFinder spec file so the name matches --- spec/finders/group_finder_spec.rb | 15 --------------- spec/finders/groups_finder_spec.rb | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) delete mode 100644 spec/finders/group_finder_spec.rb create mode 100644 spec/finders/groups_finder_spec.rb diff --git a/spec/finders/group_finder_spec.rb b/spec/finders/group_finder_spec.rb deleted file mode 100644 index 78dc027837c..00000000000 --- a/spec/finders/group_finder_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe GroupsFinder do - let(:user) { create :user } - let!(:group) { create :group } - let!(:public_group) { create :group, public: true } - - describe :execute do - it 'finds public group' do - groups = GroupsFinder.new.execute(user) - expect(groups.size).to eq(1) - expect(groups.first).to eq(public_group) - end - end -end diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb new file mode 100644 index 00000000000..78dc027837c --- /dev/null +++ b/spec/finders/groups_finder_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe GroupsFinder do + let(:user) { create :user } + let!(:group) { create :group } + let!(:public_group) { create :group, public: true } + + describe :execute do + it 'finds public group' do + groups = GroupsFinder.new.execute(user) + expect(groups.size).to eq(1) + expect(groups.first).to eq(public_group) + end + end +end -- cgit v1.2.1 From 2110247f83440f4a1044b999ff0f2630bd36e969 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:03:27 +0100 Subject: Refactoed GroupsFinder into two separate classes In the previous setup the GroupsFinder class had two distinct tasks: 1. Finding the projects user A could see 2. Finding the projects of user A that user B could see Task two was actually handled outside of the GroupsFinder (in the UsersController) by restricting the returned list of groups to those the viewed user was a member of. Moving all this logic into a single finder proved to be far too complex and confusing, hence there are now two finders: * GroupsFinder: for finding groups a user can see * JoinedGroupsFinder: for finding groups that user A is a member of, restricted to either public groups or groups user B can also see. --- app/finders/groups_finder.rb | 69 +++++++++++++++++-------------- app/finders/joined_groups_finder.rb | 49 ++++++++++++++++++++++ spec/finders/groups_finder_spec.rb | 51 +++++++++++++++++++---- spec/finders/joined_groups_finder_spec.rb | 49 ++++++++++++++++++++++ 4 files changed, 177 insertions(+), 41 deletions(-) create mode 100644 app/finders/joined_groups_finder.rb create mode 100644 spec/finders/joined_groups_finder_spec.rb diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb index b5f3176461c..91cb0f228f0 100644 --- a/app/finders/groups_finder.rb +++ b/app/finders/groups_finder.rb @@ -1,39 +1,44 @@ class GroupsFinder - def execute(current_user, options = {}) - all_groups(current_user) + # Finds the groups available to the given user. + # + # current_user - The user to find the groups for. + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil) + if current_user + relation = groups_visible_to_user(current_user) + else + relation = public_groups + end + + relation.order_id_desc end private - def all_groups(current_user) - group_ids = if current_user - if current_user.authorized_groups.any? - # User has access to groups - # - # Return only: - # groups with public projects - # groups with internal projects - # groups with joined projects - # - Project.public_and_internal_only.pluck(:namespace_id) + - current_user.authorized_groups.pluck(:id) - else - # User has no group membership - # - # Return only: - # groups with public projects - # groups with internal projects - # - Project.public_and_internal_only.pluck(:namespace_id) - end - else - # Not authenticated - # - # Return only: - # groups with public projects - Project.public_only.pluck(:namespace_id) - end - - Group.where("public IS TRUE OR id IN(?)", group_ids) + # This method returns the groups "current_user" can see. + def groups_visible_to_user(current_user) + base = groups_for_projects(public_and_internal_projects) + + union = Gitlab::SQL::Union. + new([base.select(:id), current_user.authorized_groups.select(:id)]) + + Group.where("namespaces.id IN (#{union.to_sql})") + end + + def public_groups + groups_for_projects(public_projects) + end + + def groups_for_projects(projects) + Group.public_and_given_groups(projects.select(:namespace_id)) + end + + def public_projects + Project.unscoped.public_only + end + + def public_and_internal_projects + Project.unscoped.public_and_internal_only end end diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb new file mode 100644 index 00000000000..e7523136fea --- /dev/null +++ b/app/finders/joined_groups_finder.rb @@ -0,0 +1,49 @@ +# Class for finding the groups a user is a member of. +class JoinedGroupsFinder + def initialize(user = nil) + @user = user + end + + # Finds the groups of the source user, optionally limited to those visible to + # the current user. + # + # current_user - If given the groups of "@user" will only include the groups + # "current_user" can also see. + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil) + if current_user + relation = groups_visible_to_user(current_user) + else + relation = public_groups + end + + relation.order_id_desc + end + + private + + # Returns the groups the user in "current_user" can see. + # + # This list includes all public/internal projects as well as the projects of + # "@user" that "current_user" also has access to. + def groups_visible_to_user(current_user) + base = @user.authorized_groups.visible_to_user(current_user) + extra = public_and_internal_groups + union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) + + Group.where("namespaces.id IN (#{union.to_sql})") + end + + def public_groups + groups_for_projects(@user.authorized_projects.public_only) + end + + def public_and_internal_groups + groups_for_projects(@user.authorized_projects.public_and_internal_only) + end + + def groups_for_projects(projects) + @user.groups.public_and_given_groups(projects.select(:namespace_id)) + end +end diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb index 78dc027837c..4f6a000822e 100644 --- a/spec/finders/groups_finder_spec.rb +++ b/spec/finders/groups_finder_spec.rb @@ -1,15 +1,48 @@ require 'spec_helper' describe GroupsFinder do - let(:user) { create :user } - let!(:group) { create :group } - let!(:public_group) { create :group, public: true } - - describe :execute do - it 'finds public group' do - groups = GroupsFinder.new.execute(user) - expect(groups.size).to eq(1) - expect(groups.first).to eq(public_group) + describe '#execute' do + let(:user) { create(:user) } + + let(:group1) { create(:group) } + let(:group2) { create(:group) } + let(:group3) { create(:group) } + let(:group4) { create(:group, public: true) } + + let!(:public_project) { create(:project, :public, group: group1) } + let!(:internal_project) { create(:project, :internal, group: group2) } + let!(:private_project) { create(:project, :private, group: group3) } + + let(:finder) { described_class.new } + + describe 'with a user' do + subject { finder.execute(user) } + + describe 'when the user is not a member of any groups' do + it { is_expected.to eq([group4, group2, group1]) } + end + + describe 'when the user is a member of a group' do + before do + group3.add_user(user, Gitlab::Access::DEVELOPER) + end + + it { is_expected.to eq([group4, group3, group2, group1]) } + end + + describe 'when the user is a member of a private project' do + before do + private_project.team.add_user(user, Gitlab::Access::DEVELOPER) + end + + it { is_expected.to eq([group4, group3, group2, group1]) } + end + end + + describe 'without a user' do + subject { finder.execute } + + it { is_expected.to eq([group4, group1]) } end end end diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb new file mode 100644 index 00000000000..2d9068cc720 --- /dev/null +++ b/spec/finders/joined_groups_finder_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe JoinedGroupsFinder do + describe '#execute' do + let(:source_user) { create(:user) } + let(:current_user) { create(:user) } + + let(:group1) { create(:group) } + let(:group2) { create(:group) } + let(:group3) { create(:group) } + let(:group4) { create(:group, public: true) } + + let!(:public_project) { create(:project, :public, group: group1) } + let!(:internal_project) { create(:project, :internal, group: group2) } + let!(:private_project) { create(:project, :private, group: group3) } + + let(:finder) { described_class.new(source_user) } + + before do + [group1, group2, group3, group4].each do |group| + group.add_user(source_user, Gitlab::Access::MASTER) + end + end + + describe 'with a current user' do + describe 'when the current user has access to the projects of the source user' do + before do + private_project.team.add_user(current_user, Gitlab::Access::DEVELOPER) + end + + subject { finder.execute(current_user) } + + it { is_expected.to eq([group4, group3, group2, group1]) } + end + + describe 'when the current user does not have access to the projects of the source user' do + subject { finder.execute(current_user) } + + it { is_expected.to eq([group4, group2, group1]) } + end + end + + describe 'without a current user' do + subject { finder.execute } + + it { is_expected.to eq([group4, group1]) } + end + end +end -- cgit v1.2.1 From fbcf3bd3fc94a39982e2bb11aa61ac014326e53a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:21:06 +0100 Subject: Refactor ProjectsFinder to not pluck IDs This class now uses a UNION (when needed) instead of plucking tens of thousands of project IDs into memory. The tests have also been re-written to ensure all different use cases are tested properly (assuming I didn't forget any cases). The finder has also been broken up into 3 different finder classes: * ContributedProjectsFinder: class for getting the projects a user contributed to. * PersonalProjectsFinder: class for getting the personal projects of a user. * ProjectsFinder: class for getting generic projects visible to a given user. Previously a lot of the logic of these finders was handled directly in the users controller. --- app/finders/contributed_projects_finder.rb | 37 +++++++ app/finders/personal_projects_finder.rb | 37 +++++++ app/finders/projects_finder.rb | 121 ++++++++++------------- spec/finders/contributed_projects_finder_spec.rb | 35 +++++++ spec/finders/personal_projects_finder_spec.rb | 34 +++++++ spec/finders/projects_finder_spec.rb | 75 +++++++------- 6 files changed, 237 insertions(+), 102 deletions(-) create mode 100644 app/finders/contributed_projects_finder.rb create mode 100644 app/finders/personal_projects_finder.rb create mode 100644 spec/finders/contributed_projects_finder_spec.rb create mode 100644 spec/finders/personal_projects_finder_spec.rb diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb new file mode 100644 index 00000000000..0209649b017 --- /dev/null +++ b/app/finders/contributed_projects_finder.rb @@ -0,0 +1,37 @@ +class ContributedProjectsFinder + def initialize(user) + @user = user + end + + # Finds the projects "@user" contributed to, limited to either public projects + # or projects visible to the given user. + # + # current_user - When given the list of the projects is limited to those only + # visible by this user. + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil) + if current_user + relation = projects_visible_to_user(current_user) + else + relation = public_projects + end + + relation.includes(:namespace).order_id_desc + end + + private + + def projects_visible_to_user(current_user) + authorized = @user.contributed_projects.visible_to_user(current_user) + + union = Gitlab::SQL::Union. + new([authorized.select(:id), public_projects.select(:id)]) + + Project.where("projects.id IN (#{union.to_sql})") + end + + def public_projects + @user.contributed_projects.public_only + end +end diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb new file mode 100644 index 00000000000..7c039573614 --- /dev/null +++ b/app/finders/personal_projects_finder.rb @@ -0,0 +1,37 @@ +class PersonalProjectsFinder + def initialize(user) + @user = user + end + + # Finds the projects belonging to the user in "@user", limited to either + # public projects or projects visible to the given user. + # + # current_user - When given the list of projects is limited to those only + # visible by this user. + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil) + if current_user + relation = projects_visible_to_user(current_user) + else + relation = public_projects + end + + relation.includes(:namespace).order_id_desc + end + + private + + def projects_visible_to_user(current_user) + authorized = @user.personal_projects.visible_to_user(current_user) + + union = Gitlab::SQL::Union. + new([authorized.select(:id), public_projects.select(:id)]) + + Project.where("projects.id IN (#{union.to_sql})") + end + + def public_projects + @user.personal_projects.public_only + end +end diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index c81bb51583a..dd35c215c50 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -1,11 +1,39 @@ class ProjectsFinder - def execute(current_user, options = {}) + # Returns all projects, optionally including group projects a user has access + # to. + # + # ## Examples + # + # Retrieving all public projects: + # + # ProjectsFinder.new.execute + # + # Retrieving all public/internal projects and those the given user has access + # to: + # + # ProjectsFinder.new.execute(some_user) + # + # Retrieving all public/internal projects as well as the group's projects the + # user has access to: + # + # ProjectsFinder.new.execute(some_user, group: some_group) + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil, options = {}) group = options[:group] if group - group_projects(current_user, group) + base, extra = group_projects(current_user, group) else - all_projects(current_user) + base, extra = all_projects(current_user) + end + + if base and extra + union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) + + Project.where("projects.id IN (#{union.to_sql})") + else + base end end @@ -13,77 +41,36 @@ class ProjectsFinder def group_projects(current_user, group) if current_user - if group.users.include?(current_user) - # User is group member - # - # Return ALL group projects - group.projects - else - projects_members = ProjectMember.in_projects(group.projects). - with_user(current_user) - - if projects_members.any? - # User is a project member - # - # Return only: - # public projects - # internal projects - # joined projects - # - group.projects.where( - "projects.id IN (?) OR projects.visibility_level IN (?)", - projects_members.pluck(:source_id), - Project.public_and_internal_levels - ) - else - # User has no access to group or group projects - # - # Return only: - # public projects - # internal projects - # - group.projects.public_and_internal_only - end - end + [ + group_projects_for_user(current_user, group), + group.projects.public_and_internal_only + ] else - # Not authenticated - # - # Return only: - # public projects - group.projects.public_only + [group.projects.public_only] end end def all_projects(current_user) if current_user - if current_user.authorized_projects.any? - # User has access to private projects - # - # Return only: - # public projects - # internal projects - # joined projects - # - Project.where( - "projects.id IN (?) OR projects.visibility_level IN (?)", - current_user.authorized_projects.pluck(:id), - Project.public_and_internal_levels - ) - else - # User has no access to private projects - # - # Return only: - # public projects - # internal projects - # - Project.public_and_internal_only - end + [current_user.authorized_projects, public_and_internal_projects] else - # Not authenticated - # - # Return only: - # public projects - Project.public_only + [Project.public_only] end end + + def group_projects_for_user(current_user, group) + if group.users.include?(current_user) + group.projects + else + group.projects.visible_to_user(current_user) + end + end + + def public_projects + Project.unscoped.public_only + end + + def public_and_internal_projects + Project.unscoped.public_and_internal_only + end end diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb new file mode 100644 index 00000000000..8c1e46fe9f2 --- /dev/null +++ b/spec/finders/contributed_projects_finder_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe ContributedProjectsFinder do + let(:source_user) { create(:user) } + let(:current_user) { create(:user) } + + let(:finder) { described_class.new(source_user) } + + let!(:public_project) { create(:project, :public) } + let!(:private_project) { create(:project, :private) } + + before do + private_project.team << [source_user, Gitlab::Access::MASTER] + private_project.team << [current_user, Gitlab::Access::DEVELOPER] + public_project.team << [source_user, Gitlab::Access::MASTER] + + create(:event, action: Event::PUSHED, project: public_project, + target: public_project, author: source_user) + + create(:event, action: Event::PUSHED, project: private_project, + target: private_project, author: source_user) + end + + describe 'without a current user' do + subject { finder.execute } + + it { is_expected.to eq([public_project]) } + end + + describe 'with a current user' do + subject { finder.execute(current_user) } + + it { is_expected.to eq([private_project, public_project]) } + end +end diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb new file mode 100644 index 00000000000..6bd4e6a3f3a --- /dev/null +++ b/spec/finders/personal_projects_finder_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe PersonalProjectsFinder do + let(:source_user) { create(:user) } + let(:current_user) { create(:user) } + + let(:finder) { described_class.new(source_user) } + + let!(:public_project) do + create(:project, :public, namespace: source_user.namespace, name: 'A', + path: 'A') + end + + let!(:private_project) do + create(:project, :private, namespace: source_user.namespace, name: 'B', + path: 'B') + end + + before do + private_project.team << [current_user, Gitlab::Access::DEVELOPER] + end + + describe 'without a current user' do + subject { finder.execute } + + it { is_expected.to eq([public_project]) } + end + + describe 'with a current user' do + subject { finder.execute(current_user) } + + it { is_expected.to eq([private_project, public_project]) } + end +end diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index de9d4cd6128..d1dede78f74 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -1,51 +1,56 @@ require 'spec_helper' describe ProjectsFinder do - let(:user) { create :user } - let(:group) { create :group } + describe '#execute' do + let(:user) { create(:user) } - let(:project1) { create(:empty_project, :public, group: group) } - let(:project2) { create(:empty_project, :internal, group: group) } - let(:project3) { create(:empty_project, :private, group: group) } - let(:project4) { create(:empty_project, :private, group: group) } + let!(:private_project) { create(:project, :private) } + let!(:internal_project) { create(:project, :internal) } + let!(:public_project) { create(:project, :public) } - context 'non authenticated' do - subject { ProjectsFinder.new.execute(nil, group: group) } + let(:finder) { described_class.new } - it { is_expected.to include(project1) } - it { is_expected.not_to include(project2) } - it { is_expected.not_to include(project3) } - it { is_expected.not_to include(project4) } - end + describe 'without a group' do + describe 'without a user' do + subject { finder.execute } - context 'authenticated' do - subject { ProjectsFinder.new.execute(user, group: group) } + it { is_expected.to eq([public_project]) } + end - it { is_expected.to include(project1) } - it { is_expected.to include(project2) } - it { is_expected.not_to include(project3) } - it { is_expected.not_to include(project4) } - end + describe 'with a user' do + subject { finder.execute(user) } - context 'authenticated, project member' do - before { project3.team << [user, :developer] } + describe 'without private projects' do + it { is_expected.to eq([public_project, internal_project]) } + end - subject { ProjectsFinder.new.execute(user, group: group) } + describe 'with private projects' do + before do + private_project.team.add_user(user, Gitlab::Access::MASTER) + end - it { is_expected.to include(project1) } - it { is_expected.to include(project2) } - it { is_expected.to include(project3) } - it { is_expected.not_to include(project4) } - end + it do + is_expected.to eq([public_project, internal_project, + private_project]) + end + end + end + end + + describe 'with a group' do + let(:group) { public_project.group } + + describe 'without a user' do + subject { finder.execute(nil, group: group) } - context 'authenticated, group member' do - before { group.add_developer(user) } + it { is_expected.to eq([public_project]) } + end - subject { ProjectsFinder.new.execute(user, group: group) } + describe 'with a user' do + subject { finder.execute(user, group: group) } - it { is_expected.to include(project1) } - it { is_expected.to include(project2) } - it { is_expected.to include(project3) } - it { is_expected.to include(project4) } + it { is_expected.to eq([public_project, internal_project]) } + end + end end end -- cgit v1.2.1 From 01620dd7e7f3015e31ac0288ef71fcfc4f268a14 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:25:37 +0100 Subject: Added Event.limit_recent This will be used to move some querying logic from the users controller to the Event model (where it belongs). --- app/models/event.rb | 4 ++++ spec/models/event_spec.rb | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/app/models/event.rb b/app/models/event.rb index 7618b503d84..9afd223bce5 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -69,6 +69,10 @@ class Event < ActiveRecord::Base row ? row.updated_at : nil end + + def limit_recent(limit = 20, offset = nil) + recent.limit(limit).offset(offset) + end end def proper? diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index f46c50ed6f3..ae53f7a536b 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -85,4 +85,21 @@ describe Event do end end end + + describe '.limit_recent' do + let!(:event1) { create(:closed_issue_event) } + let!(:event2) { create(:closed_issue_event) } + + describe 'without an explicit limit' do + subject { Event.limit_recent } + + it { is_expected.to eq([event2, event1]) } + end + + describe 'with an explicit limit' do + subject { Event.limit_recent(1) } + + it { is_expected.to eq([event2]) } + end + end end -- cgit v1.2.1 From a74d6d204366c862657a545d999cb33dfde300dd Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:27:21 +0100 Subject: Group methods for filtering public/visible groups These methods will be used to get a list of groups, optionally restricted to only those visible to a given user. --- app/models/group.rb | 8 ++++++++ spec/models/group_spec.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/app/models/group.rb b/app/models/group.rb index 2c9e75496b9..1b5b875a19e 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -49,6 +49,14 @@ class Group < Namespace def reference_pattern User.reference_pattern end + + def public_and_given_groups(ids) + where('public IS TRUE OR namespaces.id IN (?)', ids) + end + + def visible_to_user(user) + where(id: user.authorized_groups.select(:id).reorder(nil)) + end end def to_reference(_from_project = nil) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index bbfc5535eec..6f166b5ab75 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -38,6 +38,33 @@ describe Group do it { is_expected.not_to validate_presence_of :owner } end + describe '.public_and_given_groups' do + let!(:public_group) { create(:group, public: true) } + + subject { described_class.public_and_given_groups([group.id]) } + + it { is_expected.to eq([public_group, group]) } + end + + describe '.visible_to_user' do + let!(:group) { create(:group) } + let!(:user) { create(:user) } + + subject { described_class.visible_to_user(user) } + + describe 'when the user has access to a group' do + before do + group.add_user(user, Gitlab::Access::MASTER) + end + + it { is_expected.to eq([group]) } + end + + describe 'when the user does not have access to any groups' do + it { is_expected.to eq([]) } + end + end + describe '#to_reference' do it 'returns a String reference to the object' do expect(group.to_reference).to eq "@#{group.name}" -- cgit v1.2.1 From a4fc8112df3cf6cb344cfba65f5df46c7a99bef7 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:29:45 +0100 Subject: Added Project.visible_to_user This method can be used to filter projects to those visible to a given user. --- app/models/project.rb | 4 ++++ spec/models/project_spec.rb | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/app/models/project.rb b/app/models/project.rb index a099a67cf63..750df0f1ae1 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -286,6 +286,10 @@ class Project < ActiveRecord::Base joins(join_body).reorder('join_note_counts.amount DESC') end + + def visible_to_user(user) + where(id: user.authorized_projects.select(:id).reorder(nil)) + end end def team diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 8d7e6e76766..c42e8870f8c 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -464,4 +464,23 @@ describe Project do end end end + + describe '.visible_to_user' do + let!(:project) { create(:project, :private) } + let!(:user) { create(:user) } + + subject { described_class.visible_to_user(user) } + + describe 'when a user has access to a project' do + before do + project.team.add_user(user, Gitlab::Access::MASTER) + end + + it { is_expected.to eq([project]) } + end + + describe 'when a user does not have access to any projects' do + it { is_expected.to eq([]) } + end + end end -- cgit v1.2.1 From e116a356b8ac07bd3a935c40ceb274d67d808c83 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:30:24 +0100 Subject: Refactor User#authorized_groups/projects These methods no longer include public groups/projects (that don't belong to the actual user) as this is handled by the various finder classes now. This also removes the need for passing extra arguments. Note that memoizing was removed _explicitly_. For whatever reason doing so messes up the users controller to a point where it claims a certain user does _not_ have access to certain groups/projects when it does have access. Existing code shouldn't be affected as these methods are only called in ways that they'd run queries anyway (e.g. a combination of "any?" and "each" which would run 2 queries regardless of memoizing). --- app/models/user.rb | 35 ++++++----------------------------- spec/models/user_spec.rb | 26 ++++---------------------- 2 files changed, 10 insertions(+), 51 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index d523b3f0491..20a2457eec9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -389,40 +389,17 @@ class User < ActiveRecord::Base end end - # Returns the groups a user has access to, optionally including any public - # groups. - # - # public_internal - When set to "true" all public groups and groups of public - # projects are also included. - # - # Returns an ActiveRecord::Relation - def authorized_groups(public_internal = false) + # Returns the groups a user has access to + def authorized_groups union = Gitlab::SQL::Union. - new([groups.select(:id), authorized_projects(public_internal). - select(:namespace_id)]) - - sql = "namespaces.id IN (#{union.to_sql})" - - if public_internal - sql << ' OR public IS TRUE' - end + new([groups.select(:id), authorized_projects.select(:namespace_id)]) - Group.where(sql) + Group.where("namespaces.id IN (#{union.to_sql})") end # Returns the groups a user is authorized to access. - # - # public_internal - When set to "true" all public/internal projects will also - # be included. - def authorized_projects(public_internal = false) - base = "projects.id IN (#{projects_union.to_sql})" - - if public_internal - Project.where("#{base} OR projects.visibility_level IN (?)", - Project.public_and_internal_levels) - else - Project.where(base) - end + def authorized_projects + Project.where("projects.id IN (#{projects_union.to_sql})") end def owned_projects diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 71160f8dfef..4631b12faf1 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -762,44 +762,26 @@ describe User do describe '#authorized_groups' do let!(:user) { create(:user) } let!(:private_group) { create(:group) } - let!(:public_group) { create(:group, public: true) } before do private_group.add_user(user, Gitlab::Access::MASTER) end - describe 'excluding public groups' do - subject { user.authorized_groups } + subject { user.authorized_groups } - it { is_expected.to eq([private_group]) } - end - - describe 'including public groups' do - subject { user.authorized_groups(true) } - - it { is_expected.to eq([public_group, private_group]) } - end + it { is_expected.to eq([private_group]) } end describe '#authorized_projects' do let!(:user) { create(:user) } let!(:private_project) { create(:project, :private) } - let!(:public_project) { create(:project, :public) } before do private_project.team << [user, Gitlab::Access::MASTER] end - describe 'excluding public projects' do - subject { user.authorized_projects } + subject { user.authorized_projects } - it { is_expected.to eq([private_project]) } - end - - describe 'including public projects' do - subject { user.authorized_projects(true) } - - it { is_expected.to eq([public_project, private_project]) } - end + it { is_expected.to eq([private_project]) } end end -- cgit v1.2.1 From fbdf3767495cd60b002f24ab4e9aa4d0c019de95 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:32:35 +0100 Subject: Refactor UsersController to not kill the database Previously this controller would in multiple places load tons (read: around 65000) project and/or group IDs into memory. These changes in combination with the previous commits significantly cut down loading times of user profile pages and the Atom feeds of users. --- app/controllers/users_controller.rb | 29 +++++++++++------------------ spec/controllers/users_controller_spec.rb | 23 ++++++++++++++++++----- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 1484356a7f4..30cb869eb2a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,14 +3,11 @@ class UsersController < ApplicationController before_action :set_user def show - @contributed_projects = contributed_projects.joined(@user). - reject(&:forked?) + @contributed_projects = contributed_projects.joined(@user).reject(&:forked?) - @projects = @user.personal_projects. - where(id: authorized_projects_ids).includes(:namespace) + @projects = PersonalProjectsFinder.new(@user).execute(current_user) - # Collect only groups common for both users - @groups = @user.groups & GroupsFinder.new.execute(current_user) + @groups = JoinedGroupsFinder.new(@user).execute(current_user) respond_to do |format| format.html @@ -53,16 +50,8 @@ class UsersController < ApplicationController @user = User.find_by_username!(params[:username]) end - def authorized_projects_ids - # Projects user can view - @authorized_projects_ids ||= - ProjectsFinder.new.execute(current_user).pluck(:id) - end - def contributed_projects - @contributed_projects = Project. - where(id: authorized_projects_ids & @user.contributed_projects_ids). - includes(:namespace) + ContributedProjectsFinder.new(@user).execute(current_user) end def contributions_calendar @@ -73,9 +62,13 @@ class UsersController < ApplicationController def load_events # Get user activity feed for projects common for both users @events = @user.recent_events. - where(project_id: authorized_projects_ids). - with_associations + merge(projects_for_current_user). + references(:project). + with_associations. + limit_recent(20, params[:offset]) + end - @events = @events.limit(20).offset(params[:offset] || 0) + def projects_for_current_user + ProjectsFinder.new.execute(current_user) end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 9f89101d7f7..104a5f50143 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -16,13 +16,26 @@ describe UsersController do context 'with rendered views' do render_views - it 'renders the show template' do - sign_in(user) + describe 'when logged in' do + before do + sign_in(user) + end - get :show, username: user.username + it 'renders the show template' do + get :show, username: user.username - expect(response).to be_success - expect(response).to render_template('show') + expect(response).to be_success + expect(response).to render_template('show') + end + end + + describe 'when logged out' do + it 'renders the show template' do + get :show, username: user.username + + expect(response).to be_success + expect(response).to render_template('show') + end end end end -- cgit v1.2.1 From 73f302edf98d6c292c048c913f76282058d6d81c Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 13:02:03 +0100 Subject: Apply CI scope changes to the User model These changes are based on those from commit 03f5ff750b107b30a6d306aafb6699a9c9ecff0d, except they use a UNION instead of plucking IDs into memory. --- app/models/user.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 20a2457eec9..47439ce4b00 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -770,15 +770,10 @@ class User < ActiveRecord::Base !solo_owned_groups.present? end - def ci_authorized_projects - @ci_authorized_projects ||= - Ci::Project.where("gitlab_id IN (#{projects_union.to_sql})") - end - def ci_authorized_runners @ci_authorized_runners ||= begin runner_ids = Ci::RunnerProject.joins(:project). - where("ci_projects.gitlab_id IN (#{projects_union.to_sql})"). + where("ci_projects.gitlab_id IN (#{ci_projects_union.to_sql})"). select(:runner_id) Ci::Runner.specific.where(id: runner_ids) @@ -792,4 +787,13 @@ class User < ActiveRecord::Base groups_projects.select(:id), projects.select(:id)]) end + + def ci_projects_union + scope = { access_level: [Gitlab::Access::MASTER, Gitlab::Access::OWNER] } + groups = groups_projects.where(members: scope) + other = projects.where(members: scope) + + Gitlab::SQL::Union.new([personal_projects.select(:id), groups.select(:id), + other.select(:id)]) + end end -- cgit v1.2.1 From 26482bddb091a085e2368ff20c3e3e797da74ea3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 13:12:44 +0100 Subject: Don't pluck project IDs in User#owned_projects This won't work efficiently if you happen to have a lot of projects. --- app/models/user.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 47439ce4b00..9d75bb3aeb4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -404,10 +404,8 @@ class User < ActiveRecord::Base def owned_projects @owned_projects ||= - begin - namespace_ids = owned_groups.pluck(:id).push(namespace.id) - Project.in_namespace(namespace_ids).joins(:namespace) - end + Project.where('namespace_id IN (?) OR namespace_id = ?', + owned_groups.select(:id), namespace.id).joins(:namespace) end # Team membership in authorized projects -- cgit v1.2.1 From d9c4625c6d5264756f6ca43f7a8f5eb984d753bc Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 13:16:07 +0100 Subject: Specs that failed before the fix. --- spec/tasks/gitlab/backup_rake_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 06559c3925d..fb5e74af648 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -149,7 +149,7 @@ describe 'gitlab:app namespace rake task' do # Redirect STDOUT and run the rake task orig_stdout = $stdout $stdout = StringIO.new - ENV["SKIP"] = "repositories" + ENV["SKIP"] = "repositories,uploads" run_rake_task('gitlab:backup:create') $stdout = orig_stdout @@ -180,6 +180,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:repo:restore"]).not_to receive :invoke + expect(Rake::Task["gitlab:backup:uploads:restore"]).not_to receive :invoke expect(Rake::Task["gitlab:backup:builds:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive :invoke expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke -- cgit v1.2.1 From cc11c44ba997eb32dfa48ea225ae4c6942bc8610 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 13:27:03 +0100 Subject: Align hash literals to keep Rubocop happy --- spec/finders/contributed_projects_finder_spec.rb | 4 ++-- spec/finders/personal_projects_finder_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb index 8c1e46fe9f2..65d7f14c721 100644 --- a/spec/finders/contributed_projects_finder_spec.rb +++ b/spec/finders/contributed_projects_finder_spec.rb @@ -15,10 +15,10 @@ describe ContributedProjectsFinder do public_project.team << [source_user, Gitlab::Access::MASTER] create(:event, action: Event::PUSHED, project: public_project, - target: public_project, author: source_user) + target: public_project, author: source_user) create(:event, action: Event::PUSHED, project: private_project, - target: private_project, author: source_user) + target: private_project, author: source_user) end describe 'without a current user' do diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb index 6bd4e6a3f3a..38817add456 100644 --- a/spec/finders/personal_projects_finder_spec.rb +++ b/spec/finders/personal_projects_finder_spec.rb @@ -8,12 +8,12 @@ describe PersonalProjectsFinder do let!(:public_project) do create(:project, :public, namespace: source_user.namespace, name: 'A', - path: 'A') + path: 'A') end let!(:private_project) do create(:project, :private, namespace: source_user.namespace, name: 'B', - path: 'B') + path: 'B') end before do -- cgit v1.2.1 From 9eefae69171ba199d34bccf504902500a980fcb3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 13:31:18 +0100 Subject: Fix UNION syntax for MySQL Apparently MySQL doesn't support this syntax: (...) UNION (...) instead it only supports: ... UNION ... --- lib/gitlab/sql/union.rb | 4 ++-- spec/lib/gitlab/sql/union_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/sql/union.rb b/lib/gitlab/sql/union.rb index 1a62eff0b31..1cd89b3a9c4 100644 --- a/lib/gitlab/sql/union.rb +++ b/lib/gitlab/sql/union.rb @@ -23,11 +23,11 @@ module Gitlab # (thus fixing this problem), at a slight performance cost. fragments = ActiveRecord::Base.connection.unprepared_statement do @relations.map do |rel| - "(#{rel.reorder(nil).to_sql})" + rel.reorder(nil).to_sql end end - fragments.join(' UNION ') + fragments.join("\nUNION\n") end end end diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb index 976360af9b5..9e1cd4419e0 100644 --- a/spec/lib/gitlab/sql/union_spec.rb +++ b/spec/lib/gitlab/sql/union_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::SQL::Union do sql1 = rel1.reorder(nil).to_sql sql2 = rel2.reorder(nil).to_sql - expect(union.to_sql).to eq("(#{sql1}) UNION (#{sql2})") + expect(union.to_sql).to eq("#{sql1}\nUNION\n#{sql2}") end end end -- cgit v1.2.1 From f486b06c4de26e7eb6468cfc4f864b50e645d5c7 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 15:08:28 +0100 Subject: Return internal projects in PersonalProjectsFinder When getting the projects of a user we should get the public _and_ internal projects, not just the public ones. --- app/finders/personal_projects_finder.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb index 7c039573614..a61ffa22990 100644 --- a/app/finders/personal_projects_finder.rb +++ b/app/finders/personal_projects_finder.rb @@ -26,7 +26,7 @@ class PersonalProjectsFinder authorized = @user.personal_projects.visible_to_user(current_user) union = Gitlab::SQL::Union. - new([authorized.select(:id), public_projects.select(:id)]) + new([authorized.select(:id), public_and_internal_projects.select(:id)]) Project.where("projects.id IN (#{union.to_sql})") end @@ -34,4 +34,8 @@ class PersonalProjectsFinder def public_projects @user.personal_projects.public_only end + + def public_and_internal_projects + @user.personal_projects.public_and_internal_only + end end -- cgit v1.2.1 From e6c6c2234b7d82211928cc2549e5c53f60330d05 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 18 Nov 2015 15:15:47 +0100 Subject: Fix huge line height for diff files list Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/commit.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index fbd7c363de1..a0e5f7554ed 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -56,6 +56,7 @@ li { padding: 3px 0px; + line-height: 20px; } } .new-file { -- cgit v1.2.1 From cab6efa53fe8c2d9d44d6762fd71f97bd8d4e3d4 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Wed, 18 Nov 2015 09:53:08 -0600 Subject: Deploy page should be shown for all pages not just root --- lib/support/nginx/gitlab | 2 +- lib/support/nginx/gitlab-ssl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 93f2ad07aeb..acc547afcce 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -67,7 +67,7 @@ server { location / { ## Serve static files from defined root folder. ## @gitlab is a named location for the upstream fallback, see below. - try_files $uri $uri/index.html $uri.html @gitlab; + try_files $uri /index.html $uri.html @gitlab; } ## We route uploads through GitLab to prevent XSS and enforce access control. diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 90749947fa4..a8f8d48eb1f 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -112,7 +112,7 @@ server { location / { ## Serve static files from defined root folder. ## @gitlab is a named location for the upstream fallback, see below. - try_files $uri $uri/index.html $uri.html @gitlab; + try_files $uri /index.html $uri.html @gitlab; } ## We route uploads through GitLab to prevent XSS and enforce access control. -- cgit v1.2.1 From f3cfd20952411dc7302c78933346a9a11d8e58af Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 18 Nov 2015 17:10:06 +0100 Subject: DRY up code --- app/controllers/projects/blob_controller.rb | 54 +++++++++++++---------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 41ec7bde45d..31a33bfd237 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -23,21 +23,9 @@ class Projects::BlobController < Projects::ApplicationController end def create - result = Files::CreateService.new(@project, current_user, @commit_params).execute - - if result[:status] == :success - flash[:notice] = "The changes have been successfully committed" - respond_to do |format| - format.html { redirect_to after_create_path } - format.json { render json: { message: "success", filePath: after_create_path } } - end - else - flash[:alert] = result[:message] - respond_to do |format| - format.html { render :new } - format.json { render json: { message: "failed", filePath: namespace_project_blob_path(@project.namespace, @project, @id) } } - end - end + create_commit(Files::CreateService, success_path: after_create_path, + failure_view: :new, + failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref)) end def show @@ -48,21 +36,9 @@ class Projects::BlobController < Projects::ApplicationController end def update - result = Files::UpdateService.new(@project, current_user, @commit_params).execute - - if result[:status] == :success - flash[:notice] = "Your changes have been successfully committed" - respond_to do |format| - format.html { redirect_to after_edit_path } - format.json { render json: { message: "success", filePath: after_edit_path } } - end - else - flash[:alert] = result[:message] - respond_to do |format| - format.html { render :edit } - format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } } - end - end + create_commit(Files::UpdateService, success_path: after_edit_path, + failure_view: :edit, + failure_path: namespace_project_blob_path(@project.namespace, @project, @id)) end def preview @@ -132,6 +108,24 @@ class Projects::BlobController < Projects::ApplicationController render_404 end + def create_commit(service, success_path:, failure_view:, failure_path:) + result = service.new(@project, current_user, @commit_params).execute + + if result[:status] == :success + flash[:notice] = "Your changes have been successfully committed" + respond_to do |format| + format.html { redirect_to success_path } + format.json { render json: { message: "success", filePath: success_path } } + end + else + flash[:alert] = result[:message] + respond_to do |format| + format.html { render failure_view } + format.json { render json: { message: "failed", filePath: failure_path } } + end + end + end + def after_create_path @after_create_path ||= if create_merge_request? -- cgit v1.2.1 From ee2739e60682153895ffc4ec274d22acb509c3b3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 17:24:51 +0100 Subject: Added an index on namespaces.public --- db/migrate/20151118162244_add_projects_public_index.rb | 5 +++++ db/schema.rb | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20151118162244_add_projects_public_index.rb diff --git a/db/migrate/20151118162244_add_projects_public_index.rb b/db/migrate/20151118162244_add_projects_public_index.rb new file mode 100644 index 00000000000..fded70e3c0c --- /dev/null +++ b/db/migrate/20151118162244_add_projects_public_index.rb @@ -0,0 +1,5 @@ +class AddProjectsPublicIndex < ActiveRecord::Migration + def change + add_index :namespaces, :public + end +end diff --git a/db/schema.rb b/db/schema.rb index aa76cef9fe4..7f95b4cbdbc 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: 20151116144118) do +ActiveRecord::Schema.define(version: 20151118162244) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -538,6 +538,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree + add_index "namespaces", ["public"], name: "index_namespaces_on_public", using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree create_table "notes", force: true do |t| -- cgit v1.2.1 From bf3c0aafaa17c2e322d225180fd9708ec2827ac4 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 18:14:13 +0100 Subject: USe reject. --- lib/backup/manager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 69922eb66ea..e7eda7c6f45 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -158,7 +158,7 @@ module Backup end def folders_to_backup - %w{repositories db}.map{ |name| name unless skipped?(name) }.compact + %w{repositories db}.reject{ |name| skipped?(name) } end def settings -- cgit v1.2.1 From efd5d93745cede13c3a720fe17388b146a8998ed Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 20:20:55 +0100 Subject: Use "GitLab.com" instead of "gitlab.com" --- app/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index 9d75bb3aeb4..9374f01f99f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -729,7 +729,7 @@ class User < ActiveRecord::Base # some_user.contributed_projects.visible_to_user(other_user) # # If this method were to use a JOIN the resulting query would take roughly 200 - # ms on a database with a similar size to gitlab.com's database. On the other + # ms on a database with a similar size to GitLab.com's database. On the other # hand, using a subquery means we can get the exact same data in about 40 ms. def contributed_projects events = Event.select(:project_id). -- cgit v1.2.1 From fd2c0fe446c7f761b845c91307ef8110d869e8e8 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 11 Nov 2015 15:12:51 +0200 Subject: award emoji --- app/assets/javascripts/awards_handler.coffee | 55 ++++++ app/assets/javascripts/notes.js.coffee | 9 +- app/assets/stylesheets/pages/issuable.scss | 42 +++++ app/controllers/projects/issues_controller.rb | 2 +- .../projects/merge_requests_controller.rb | 2 +- app/controllers/projects/notes_controller.rb | 25 ++- app/helpers/issues_helper.rb | 5 + app/models/concerns/issuable.rb | 49 ------ app/models/note.rb | 46 +---- app/services/notes/create_service.rb | 17 +- app/services/notification_service.rb | 1 + app/views/projects/issues/_issue.html.haml | 2 - .../merge_requests/_merge_request.html.haml | 2 - app/views/projects/notes/_note.html.haml | 20 --- app/views/votes/_votes_block.html.haml | 43 +++-- app/views/votes/_votes_inline.html.haml | 9 - config/routes.rb | 4 + db/migrate/20151106000015_add_is_award_to_notes.rb | 5 + db/schema.rb | 3 +- doc/api/merge_requests.md | 12 -- doc/api/notes.md | 8 +- lib/api/entities.rb | 4 +- spec/lib/votes_spec.rb | 188 --------------------- spec/models/project_spec.rb | 11 -- 24 files changed, 203 insertions(+), 361 deletions(-) create mode 100644 app/assets/javascripts/awards_handler.coffee delete mode 100644 app/views/votes/_votes_inline.html.haml create mode 100644 db/migrate/20151106000015_add_is_award_to_notes.rb delete mode 100644 spec/lib/votes_spec.rb diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee new file mode 100644 index 00000000000..aab3179f10e --- /dev/null +++ b/app/assets/javascripts/awards_handler.coffee @@ -0,0 +1,55 @@ +class @AwardsHandler + constructor: (@post_emoji_url, @noteable_type, @noteable_id) -> + + addAward: (emoji) -> + @postEmoji emoji, => + if @exist(emoji) + if @isActive(emoji) + @decrementCounter(emoji) + else + counter = $(".icon." + emoji).siblings(".counter") + counter.text(parseInt(counter.text()) + 1) + counter.parent().addClass("active") + else + @createEmoji(emoji) + + + exist: (emoji) -> + $(".icon").hasClass(emoji) + + isActive: (emoji) -> + $(".icon." + emoji).parent().hasClass("active") + + decrementCounter: (emoji) -> + counter = $(".icon." + emoji).siblings(".counter") + + if parseInt(counter.text()) > 1 + counter.text(parseInt(counter.text()) - 1) + counter.parent().removeClass("active") + else + counter.parent().remove() + + + createEmoji: (emoji) -> + nodes = [] + nodes.push("
") + nodes.push("
") + nodes.push(@getImage(emoji)) + nodes.push("
") + nodes.push("
1") + nodes.push("
") + + $(".awards").append(nodes.join("\n")) + + getImage: (emoji) -> + $("li." + emoji).html() + + postEmoji: (emoji, callback) -> + emoji = emoji.replace("emoji-", "") + $.post @post_emoji_url, { + emoji: emoji + noteable_type: @noteable_type + noteable_id: @noteable_id + },(data) -> + if data.ok + callback.call() \ No newline at end of file diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index ea75c656bcc..cd27b20dd7c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -113,13 +113,16 @@ class @Notes renderNote: (note) -> # render note if it not present in loaded list # or skip if rendered - if @isNewNote(note) + if @isNewNote(note) && !note.award @note_ids.push(note.id) $('ul.main-notes-list'). append(note.html). syntaxHighlight() @initTaskList() + if note.award + awards_handler.addAward("emoji-" + note.note) + ### Check if note does not exists on page ### @@ -255,7 +258,6 @@ class @Notes ### addNote: (xhr, note, status) => @renderNote(note) - @updateVotes() ### Called in response to the new note form being submitted @@ -473,9 +475,6 @@ class @Notes form = $(e.target).closest(".js-discussion-note-form") @removeDiscussionNoteForm(form) - updateVotes: -> - true - ### Called after an attachment file has been selected. diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index abc27a19e32..efeb2393165 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -101,3 +101,45 @@ background-color: $background-color; } } + +.awards { + .award { + border: 1px solid; + padding: 1px 3px; + width: 50px; + border-radius: 5px; + float:left; + margin: 0 3px; + border-color: #ccc; + cursor: pointer; + + &.active { + border-color: rgba(79,176,252,.4); + background-color: rgba(79,176,252,.08); + + .counter { + font-weight: bold; + } + } + + .icon { + float: left; + margin-right: 10px; + } + } + + #add-award { + font-size: 20px; + border-radius: 5px; + float: left; + width: 50px; + font-weight: bold; + } + + .awards-menu{ + li { + float: left; + margin: 3px; + } + } +} diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index e74c2905e48..5250a0f5e67 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -60,7 +60,7 @@ class Projects::IssuesController < Projects::ApplicationController def show @participants = @issue.participants(current_user) @note = @project.notes.new(noteable: @issue) - @notes = @issue.notes.with_associations.fresh + @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue respond_with(@issue) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 188f0cc4cea..a0468c65d5a 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -254,7 +254,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) - @notes = @merge_request.mr_and_commit_notes.inc_author.fresh + @notes = @merge_request.nonawards.mr_and_commit_notes.inc_author.fresh @discussions = Note.discussions_from_notes(@notes) @noteable = @merge_request diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 41cd08c93c6..357b292980d 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_read_note! before_action :authorize_create_note!, only: [:create] before_action :authorize_admin_note!, only: [:update, :destroy] - before_action :find_current_user_notes, except: [:destroy, :delete_attachment] + before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle]] def index current_fetched_at = Time.now.to_i @@ -58,6 +58,27 @@ class Projects::NotesController < Projects::ApplicationController end end + def award_toggle + noteable = params[:noteable_type] == "Issue" ? Issue : MergeRequest + noteable = noteable.find(params[:noteable_id]) + data = { + noteable: noteable, + author: current_user, + is_award: true, + note: params[:emoji] + } + + note = project.notes.find_by(data) + + if note + note.destroy + else + project.notes.create(data) + end + + render json: {ok: true} + end + private def note @@ -111,6 +132,8 @@ class Projects::NotesController < Projects::ApplicationController id: note.id, discussion_id: note.discussion_id, html: note_to_html(note), + award: note.is_award, + note: note.note, discussion_html: note_to_discussion_html(note), discussion_with_diff_html: note_to_discussion_with_diff_html(note) } diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index beb083d82dc..ff3e0911954 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -87,6 +87,11 @@ module IssuesHelper merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ') end + def url_to_emoji(name) + emoji_path = "emoji/#{Emoji.emoji_filename(name)}.png" + url_to_image(emoji_path) + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 492a026add9..91da6797df7 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -89,41 +89,6 @@ module Issuable opened? || reopened? end - # - # Votes - # - - # Return the number of -1 comments (downvotes) - def downvotes - filter_superceded_votes(notes.select(&:downvote?), notes).size - end - - def downvotes_in_percent - if votes_count.zero? - 0 - else - 100.0 - upvotes_in_percent - end - end - - # Return the number of +1 comments (upvotes) - def upvotes - filter_superceded_votes(notes.select(&:upvote?), notes).size - end - - def upvotes_in_percent - if votes_count.zero? - 0 - else - 100.0 / votes_count * upvotes - end - end - - # Return the total number of votes - def votes_count - upvotes + downvotes - end - def subscribed?(user) subscription = subscriptions.find_by_user_id(user.id) @@ -183,18 +148,4 @@ module Issuable def notes_with_associations notes.includes(:author, :project) end - - private - - def filter_superceded_votes(votes, notes) - filteredvotes = [] + votes - - votes.each do |vote| - if vote.superceded?(notes) - filteredvotes.delete(vote) - end - end - - filteredvotes - end end diff --git a/app/models/note.rb b/app/models/note.rb index 0b3aa30abd7..458d433211c 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -50,6 +50,8 @@ class Note < ActiveRecord::Base mount_uploader :attachment, AttachmentUploader # Scopes + scope :awards, ->{ where("is_award IS TRUE") } + scope :nonawards, ->{ where("is_award IS FALSE") } scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } scope :inline, ->{ where("line_code IS NOT NULL") } scope :not_inline, ->{ where(line_code: [nil, '']) } @@ -97,6 +99,12 @@ class Note < ActiveRecord::Base def search(query) where("LOWER(note) like :query", query: "%#{query.downcase}%") end + + def grouped_awards + select(:note).distinct.map do |note| + [ note.note, where(note: note.note) ] + end + end end def cross_reference? @@ -288,44 +296,6 @@ class Note < ActiveRecord::Base nil end - DOWNVOTES = %w(-1 :-1: :thumbsdown: :thumbs_down_sign:) - - # Check if the note is a downvote - def downvote? - votable? && note.start_with?(*DOWNVOTES) - end - - UPVOTES = %w(+1 :+1: :thumbsup: :thumbs_up_sign:) - - # Check if the note is an upvote - def upvote? - votable? && note.start_with?(*UPVOTES) - end - - def superceded?(notes) - return false unless vote? - - notes.each do |note| - next if note == self - - if note.vote? && - self[:author_id] == note[:author_id] && - self[:created_at] <= note[:created_at] - return true - end - end - - false - end - - def vote? - upvote? || downvote? - end - - def votable? - for_issue? || (for_merge_request? && !for_diff_line?) - end - # Mentionable override. def gfm_reference(from_project = nil) noteable.gfm_reference(from_project) diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 2001dc89c33..f448f61cc86 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -5,11 +5,16 @@ module Notes note.author = current_user note.system = false + if contains_emoji_only?(params[:note]) + note.is_award = true + note.note = emoji_name(params[:note]) + end + if note.save notification_service.new_note(note) - # Skip system notes, like status changes and cross-references. - unless note.system + # Skip system notes, like status changes and cross-references and awards + unless note.system || note.is_award event_service.leave_note(note, note.author) note.create_cross_references! execute_hooks(note) @@ -28,5 +33,13 @@ module Notes note.project.execute_hooks(note_data, :note_hooks) note.project.execute_services(note_data, :note_hooks) end + + def contains_emoji_only?(note) + note =~ /^:[-_+[:alnum:]]*:\s?/ + end + + def emoji_name(note) + note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] + end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index bbfe755f44a..d6550fbb555 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -102,6 +102,7 @@ class NotificationService # ignore gitlab service messages return true if note.note.start_with?('Status changed to closed') return true if note.cross_reference? && note.system == true + return true if note.is_award target = note.noteable diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 55ce912829d..d7657ee7e40 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -29,8 +29,6 @@ .issue-info = "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe - - if issue.votes_count > 0 - = render 'votes/votes_inline', votable: issue - if issue.milestone   %span diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index c5234c0618c..83e8ad11989 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -34,8 +34,6 @@ .merge-request-info = "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe - - if merge_request.votes_count > 0 - = render 'votes/votes_inline', votable: merge_request - if merge_request.milestone_id?   %span diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index efa7dd01cc2..dd0abc8c746 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -35,26 +35,6 @@ - if note.updated_by && note.updated_by != note.author by #{link_to_member(note.project, note.updated_by, avatar: false, author_class: nil)} - - if note.superceded?(@notes) - - if note.upvote? - %span.vote.upvote.label.label-gray.strikethrough - = icon('thumbs-up') - \+1 - - if note.downvote? - %span.vote.downvote.label.label-gray.strikethrough - = icon('thumbs-down') - \-1 - - else - - if note.upvote? - %span.vote.upvote.label.label-success - = icon('thumbs-up') - \+1 - - if note.downvote? - %span.vote.downvote.label.label-danger - = icon('thumbs-down') - \-1 - - .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''} .note-text = preserve do diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 36ea6742064..3c3ae9dd0b2 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,10 +1,33 @@ -.votes.votes-block - .btn-group - - unless votable.upvotes.zero? - .btn.btn-sm.disabled.cgreen - %i.fa.fa-thumbs-up - = votable.upvotes - - unless votable.downvotes.zero? - .btn.btn-sm.disabled.cred - %i.fa.fa-thumbs-down - = votable.downvotes +.awards.votes-block + - votable.notes.awards.grouped_awards.each do | vote | + .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id))} + .icon{class: "emoji-#{vote.first}"} + = image_tag url_to_emoji(vote.first), height: "20px", width: "20px" + .counter + = vote.last.count + + %button.dropdown + %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + + %ul.dropdown-menu.awards-menu + - ["100", "blush", "heart", "smile", "rage", "beers", "thumbsup", "disappointed", "ok_hand", "helicopter"].each do |emoji| + %li{class: "emoji-#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" + + +:coffeescript + post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" + noteable_type = "Issue" + noteable_id = #{@issue.id} + awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) + + $ -> + $(".awards-menu li").click (e)-> + emoji = $(this).attr("class") + awards_handler.addAward(emoji) + + $(".awards").on "click", ".award", (e)-> + emoji = /(emoji-\S*)/.exec($(this).find(".icon").attr("class"))[0] + awards_handler.addAward(emoji) + + + + \ No newline at end of file diff --git a/app/views/votes/_votes_inline.html.haml b/app/views/votes/_votes_inline.html.haml deleted file mode 100644 index 2cb3ae04e1a..00000000000 --- a/app/views/votes/_votes_inline.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -.votes.votes-inline - - unless votable.upvotes.zero? - %span.upvotes.cgreen - + #{votable.upvotes} - - unless votable.downvotes.zero? - \/ - - unless votable.downvotes.zero? - %span.downvotes.cred - \- #{votable.downvotes} diff --git a/config/routes.rb b/config/routes.rb index 0bc2c173453..ac81a2aac76 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -664,6 +664,10 @@ Gitlab::Application.routes.draw do member do delete :delete_attachment end + + collection do + post :award_toggle + end end resources :uploads, only: [:create] do diff --git a/db/migrate/20151106000015_add_is_award_to_notes.rb b/db/migrate/20151106000015_add_is_award_to_notes.rb new file mode 100644 index 00000000000..bffe85df3da --- /dev/null +++ b/db/migrate/20151106000015_add_is_award_to_notes.rb @@ -0,0 +1,5 @@ +class AddIsAwardToNotes < ActiveRecord::Migration + def change + add_column :notes, :is_award, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 440a33e2006..6c322d33682 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -531,7 +531,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.string "type" t.string "description", default: "", null: false t.string "avatar" - t.boolean "public", default: false + t.boolean "visible", default: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree @@ -554,6 +554,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.boolean "system", default: false, null: false t.text "st_diff" t.integer "updated_by_id" + t.boolean "is_award", default: false end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index ffa7f2cdf14..2f17d4ae06b 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -31,8 +31,6 @@ Parameters: "project_id": 3, "title": "test1", "state": "opened", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -77,8 +75,6 @@ Parameters: "project_id": 3, "title": "test1", "state": "merged", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -126,8 +122,6 @@ Parameters: "updated_at": "2015-02-02T20:08:49.959Z", "target_branch": "secret_token", "source_branch": "version-1-9", - "upvotes": 0, - "downvotes": 0, "author": { "name": "Chad Hamill", "username": "jarrett", @@ -198,8 +192,6 @@ Parameters: "project_id": 3, "title": "test1", "state": "opened", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -250,8 +242,6 @@ Parameters: "title": "test1", "description": "description1", "state": "opened", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -304,8 +294,6 @@ Parameters: "project_id": 3, "title": "test1", "state": "merged", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", diff --git a/doc/api/notes.md b/doc/api/notes.md index c683cb883d4..bcba1f62151 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -32,9 +32,7 @@ Parameters: "created_at": "2013-09-30T13:46:01Z" }, "created_at": "2013-10-02T09:22:45Z", - "system": true, - "upvote": false, - "downvote": false + "system": true }, { "id": 305, @@ -49,9 +47,7 @@ Parameters: "created_at": "2013-09-30T13:46:01Z" }, "created_at": "2013-10-02T09:56:03Z", - "system": false, - "upvote": false, - "downvote": false + "system": false } ] ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d6aec03d7f5..3da6bc415d6 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -162,7 +162,7 @@ module API end class MergeRequest < ProjectEntity - expose :target_branch, :source_branch, :upvotes, :downvotes + expose :target_branch, :source_branch expose :author, :assignee, using: Entities::UserBasic expose :source_project_id, :target_project_id expose :label_names, as: :labels @@ -192,8 +192,6 @@ module API expose :author, using: Entities::UserBasic expose :created_at expose :system?, as: :system - expose :upvote?, as: :upvote - expose :downvote?, as: :downvote end class MRNote < Grape::Entity diff --git a/spec/lib/votes_spec.rb b/spec/lib/votes_spec.rb deleted file mode 100644 index 39e5d054e62..00000000000 --- a/spec/lib/votes_spec.rb +++ /dev/null @@ -1,188 +0,0 @@ -require 'spec_helper' - -describe Issue, 'Votes' do - let(:issue) { create(:issue) } - - describe "#upvotes" do - it "with no notes has a 0/0 score" do - expect(issue.upvotes).to eq(0) - end - - it "should recognize non-+1 notes" do - add_note "No +1 here" - expect(issue.notes.size).to eq(1) - expect(issue.notes.first.upvote?).to be_falsey - expect(issue.upvotes).to eq(0) - end - - it "should recognize a single +1 note" do - add_note "+1 This is awesome" - expect(issue.upvotes).to eq(1) - end - - it 'should recognize multiple +1 notes' do - add_note '+1 This is awesome', create(:user) - add_note '+1 I want this', create(:user) - expect(issue.upvotes).to eq(2) - end - - it 'should not count 2 +1 votes from the same user' do - add_note '+1 This is awesome' - add_note '+1 I want this' - expect(issue.upvotes).to eq(1) - end - end - - describe "#downvotes" do - it "with no notes has a 0/0 score" do - expect(issue.downvotes).to eq(0) - end - - it "should recognize non--1 notes" do - add_note "Almost got a -1" - expect(issue.notes.size).to eq(1) - expect(issue.notes.first.downvote?).to be_falsey - expect(issue.downvotes).to eq(0) - end - - it "should recognize a single -1 note" do - add_note "-1 This is bad" - expect(issue.downvotes).to eq(1) - end - - it "should recognize multiple -1 notes" do - add_note('-1 This is bad', create(:user)) - add_note('-1 Away with this', create(:user)) - expect(issue.downvotes).to eq(2) - end - end - - describe "#votes_count" do - it "with no notes has a 0/0 score" do - expect(issue.votes_count).to eq(0) - end - - it "should recognize non notes" do - add_note "No +1 here" - expect(issue.notes.size).to eq(1) - expect(issue.votes_count).to eq(0) - end - - it "should recognize a single +1 note" do - add_note "+1 This is awesome" - expect(issue.votes_count).to eq(1) - end - - it "should recognize a single -1 note" do - add_note "-1 This is bad" - expect(issue.votes_count).to eq(1) - end - - it "should recognize multiple notes" do - add_note('+1 This is awesome', create(:user)) - add_note('-1 This is bad', create(:user)) - add_note('+1 I want this', create(:user)) - expect(issue.votes_count).to eq(3) - end - - it 'should not count 2 -1 votes from the same user' do - add_note '-1 This is suspicious' - add_note '-1 This is bad' - expect(issue.votes_count).to eq(1) - end - end - - describe "#upvotes_in_percent" do - it "with no notes has a 0% score" do - expect(issue.upvotes_in_percent).to eq(0) - end - - it "should count a single 1 note as 100%" do - add_note "+1 This is awesome" - expect(issue.upvotes_in_percent).to eq(100) - end - - it 'should count multiple +1 notes as 100%' do - add_note('+1 This is awesome', create(:user)) - add_note('+1 I want this', create(:user)) - expect(issue.upvotes_in_percent).to eq(100) - end - - it 'should count fractions for multiple +1 and -1 notes correctly' do - add_note('+1 This is awesome', create(:user)) - add_note('+1 I want this', create(:user)) - add_note('-1 This is bad', create(:user)) - add_note('+1 me too', create(:user)) - expect(issue.upvotes_in_percent).to eq(75) - end - end - - describe "#downvotes_in_percent" do - it "with no notes has a 0% score" do - expect(issue.downvotes_in_percent).to eq(0) - end - - it "should count a single -1 note as 100%" do - add_note "-1 This is bad" - expect(issue.downvotes_in_percent).to eq(100) - end - - it 'should count multiple -1 notes as 100%' do - add_note('-1 This is bad', create(:user)) - add_note('-1 Away with this', create(:user)) - expect(issue.downvotes_in_percent).to eq(100) - end - - it 'should count fractions for multiple +1 and -1 notes correctly' do - add_note('+1 This is awesome', create(:user)) - add_note('+1 I want this', create(:user)) - add_note('-1 This is bad', create(:user)) - add_note('+1 me too', create(:user)) - expect(issue.downvotes_in_percent).to eq(25) - end - end - - describe '#filter_superceded_votes' do - - it 'should count a users vote only once amongst multiple votes' do - add_note('-1 This needs work before I will accept it') - add_note('+1 I want this', create(:user)) - add_note('+1 This is is awesome', create(:user)) - add_note('+1 this looks good now') - add_note('+1 This is awesome', create(:user)) - add_note('+1 me too', create(:user)) - expect(issue.downvotes).to eq(0) - expect(issue.upvotes).to eq(5) - end - - it 'should count each users vote only once' do - add_note '-1 This needs work before it will be accepted' - add_note '+1 I like this' - add_note '+1 I still like this' - add_note '+1 I really like this' - add_note '+1 Give me this now!!!!' - expect(issue.downvotes).to eq(0) - expect(issue.upvotes).to eq(1) - end - - it 'should count a users vote only once without caring about comments' do - add_note '-1 This needs work before it will be accepted' - add_note 'Comment 1' - add_note 'Another comment' - add_note '+1 vote' - add_note 'final comment' - expect(issue.downvotes).to eq(0) - expect(issue.upvotes).to eq(1) - end - - end - - def add_note(text, author = issue.author) - created_at = Time.now - 1.hour + Note.count.seconds - issue.notes << create(:note, - note: text, - project: issue.project, - author_id: author.id, - created_at: created_at) - end -end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 8d7e6e76766..3c537220106 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -345,17 +345,6 @@ describe Project do expect(project1.star_count).to eq(0) expect(project2.star_count).to eq(0) end - - it 'is decremented when an upvoter account is deleted' do - user = create :user - project = create :project, :public - user.toggle_star(project) - project.reload - expect(project.star_count).to eq(1) - user.destroy - project.reload - expect(project.star_count).to eq(0) - end end describe :avatar_type do -- cgit v1.2.1 From 06a4fd1035c58d89251fb979dafa8610ba8c5157 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 17 Nov 2015 13:16:16 +0200 Subject: css improvements --- app/assets/javascripts/awards_handler.coffee | 2 +- app/assets/stylesheets/pages/issuable.scss | 20 +++++++++++++++----- app/finders/notes_finder.rb | 4 ++-- app/views/votes/_votes_block.html.haml | 2 +- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index aab3179f10e..1ede7c317c8 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -39,7 +39,7 @@ class @AwardsHandler nodes.push("
1") nodes.push("
") - $(".awards").append(nodes.join("\n")) + $(".awards-controls").before(nodes.join("\n")) getImage: (emoji) -> $("li." + emoji).html() diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index efeb2393165..3f79d0d4967 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -128,12 +128,22 @@ } } - #add-award { - font-size: 20px; - border-radius: 5px; + .awards-controls { + height: 25px; + width: 28px; float: left; - width: 50px; - font-weight: bold; + padding: 0 0 5px 5px; + line-height: 1; + + #add-award { + font-size: 27px; + &:hover { + text-decoration: none; + } + &:link { + text-decoration: none; + } + } } .awards-menu{ diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb index ab252821b52..fa4c635f55c 100644 --- a/app/finders/notes_finder.rb +++ b/app/finders/notes_finder.rb @@ -12,9 +12,9 @@ class NotesFinder when "commit" project.notes.for_commit_id(target_id).not_inline when "issue" - project.issues.find(target_id).notes.inc_author + project.issues.find(target_id).notes.nonawards.inc_author when "merge_request" - project.merge_requests.find(target_id).mr_and_commit_notes.inc_author + project.merge_requests.find(target_id).mr_and_commit_notes.nonawards.inc_author when "snippet", "project_snippet" project.snippets.find(target_id).notes else diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 3c3ae9dd0b2..7f988160ad9 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -6,7 +6,7 @@ .counter = vote.last.count - %button.dropdown + .dropdown.awards-controls %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + %ul.dropdown-menu.awards-menu - ["100", "blush", "heart", "smile", "rage", "beers", "thumbsup", "disappointed", "ok_hand", "helicopter"].each do |emoji| -- cgit v1.2.1 From 36d0442e837cd520dec780590040c83108bc14e6 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 17 Nov 2015 16:44:58 +0200 Subject: replace emoji references from class name to data [ci skip] --- app/assets/javascripts/awards_handler.coffee | 36 +++++++++++++++------------- app/assets/javascripts/notes.js.coffee | 2 +- app/views/votes/_votes_block.html.haml | 10 ++++---- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 1ede7c317c8..8803c0cca2d 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -3,25 +3,27 @@ class @AwardsHandler addAward: (emoji) -> @postEmoji emoji, => - if @exist(emoji) - if @isActive(emoji) - @decrementCounter(emoji) - else - counter = $(".icon." + emoji).siblings(".counter") - counter.text(parseInt(counter.text()) + 1) - counter.parent().addClass("active") - else - @createEmoji(emoji) + @addAwardToEmojiBar(emoji) + addAwardToEmojiBar: (emoji) -> + if @exist(emoji) + if @isActive(emoji) + @decrementCounter(emoji) + else + counter = @findEmojiIcon(emoji).siblings(".counter") + counter.text(parseInt(counter.text()) + 1) + counter.parent().addClass("active") + else + @createEmoji(emoji) exist: (emoji) -> - $(".icon").hasClass(emoji) + @findEmojiIcon(emoji).length > 0 isActive: (emoji) -> - $(".icon." + emoji).parent().hasClass("active") + @findEmojiIcon(emoji).parent().hasClass("active") decrementCounter: (emoji) -> - counter = $(".icon." + emoji).siblings(".counter") + counter = @findEmojiIcon(emoji).siblings(".counter") if parseInt(counter.text()) > 1 counter.text(parseInt(counter.text()) - 1) @@ -33,7 +35,7 @@ class @AwardsHandler createEmoji: (emoji) -> nodes = [] nodes.push("
") - nodes.push("
") + nodes.push("
") nodes.push(@getImage(emoji)) nodes.push("
") nodes.push("
1") @@ -42,14 +44,16 @@ class @AwardsHandler $(".awards-controls").before(nodes.join("\n")) getImage: (emoji) -> - $("li." + emoji).html() + $("li[data-emoji='" + emoji + "'").html() postEmoji: (emoji, callback) -> - emoji = emoji.replace("emoji-", "") $.post @post_emoji_url, { emoji: emoji noteable_type: @noteable_type noteable_id: @noteable_id },(data) -> if data.ok - callback.call() \ No newline at end of file + callback.call() + + findEmojiIcon: (emoji) -> + $(".icon[data-emoji='" + emoji + "'") \ No newline at end of file diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index cd27b20dd7c..73a95c455e8 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -121,7 +121,7 @@ class @Notes @initTaskList() if note.award - awards_handler.addAward("emoji-" + note.note) + awards_handler.addAwardToEmojiBar(note.note) ### Check if note does not exists on page diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 7f988160ad9..118a095181f 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,7 +1,7 @@ .awards.votes-block - votable.notes.awards.grouped_awards.each do | vote | .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id))} - .icon{class: "emoji-#{vote.first}"} + .icon{"data-emoji" => "#{vote.first}"} = image_tag url_to_emoji(vote.first), height: "20px", width: "20px" .counter = vote.last.count @@ -10,22 +10,22 @@ %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + %ul.dropdown-menu.awards-menu - ["100", "blush", "heart", "smile", "rage", "beers", "thumbsup", "disappointed", "ok_hand", "helicopter"].each do |emoji| - %li{class: "emoji-#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" + %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" noteable_type = "Issue" noteable_id = #{@issue.id} - awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) + window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) $ -> $(".awards-menu li").click (e)-> - emoji = $(this).attr("class") + emoji = $(this).data("emoji") awards_handler.addAward(emoji) $(".awards").on "click", ".award", (e)-> - emoji = /(emoji-\S*)/.exec($(this).find(".icon").attr("class"))[0] + emoji = $(this).find(".icon").data("emoji") awards_handler.addAward(emoji) -- cgit v1.2.1 From f021bc5c6aa79147940ee31e800f519962f4d806 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 15:43:53 +0200 Subject: add stats on hover --- app/assets/javascripts/awards_handler.coffee | 32 ++++++++++++++++++++++++++-- app/helpers/issues_helper.rb | 8 +++++++ app/views/votes/_votes_block.html.haml | 4 +++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 8803c0cca2d..29b11b0cc58 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -13,6 +13,7 @@ class @AwardsHandler counter = @findEmojiIcon(emoji).siblings(".counter") counter.text(parseInt(counter.text()) + 1) counter.parent().addClass("active") + @addMeToAuthorList(emoji) else @createEmoji(emoji) @@ -28,13 +29,38 @@ class @AwardsHandler if parseInt(counter.text()) > 1 counter.text(parseInt(counter.text()) - 1) counter.parent().removeClass("active") + @removeMeFromAuthorList(emoji) else - counter.parent().remove() + award = counter.parent() + award.tooltip("destroy") + award.remove() + removeMeFromAuthorList: (emoji) -> + award_block = @findEmojiIcon(emoji).parent() + authors = award_block.attr("data-original-title").split(", ") + authors = _.without(authors, "me").join(", ") + award_block.attr("title", authors) + @resetTooltip(award_block) + + addMeToAuthorList: (emoji) -> + award_block = @findEmojiIcon(emoji).parent() + authors = award_block.attr("data-original-title").split(", ") + authors.push("me") + award_block.attr("title", authors.join(", ")) + @resetTooltip(award_block) + + resetTooltip: (award) -> + award.tooltip("destroy") + + # "destroy" call is asynchronous, this is why we need to set timeout. + setTimeout (-> + award.tooltip() + ), 200 + createEmoji: (emoji) -> nodes = [] - nodes.push("
") + nodes.push("
") nodes.push("
") nodes.push(@getImage(emoji)) nodes.push("
") @@ -43,6 +69,8 @@ class @AwardsHandler $(".awards-controls").before(nodes.join("\n")) + $(".award").tooltip() + getImage: (emoji) -> $("li[data-emoji='" + emoji + "'").html() diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index ff3e0911954..3aa16b66944 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -92,6 +92,14 @@ module IssuesHelper url_to_image(emoji_path) end + def emoji_author_list(notes, current_user) + list = notes.map do |note| + note.author == current_user ? "me" : note.author.username + end + + list.join(", ") + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 118a095181f..a2298f1813a 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,6 +1,6 @@ .awards.votes-block - votable.notes.awards.grouped_awards.each do | vote | - .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id))} + .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id)), title: emoji_author_list(vote.last, current_user)} .icon{"data-emoji" => "#{vote.first}"} = image_tag url_to_emoji(vote.first), height: "20px", width: "20px" .counter @@ -28,6 +28,8 @@ emoji = $(this).find(".icon").data("emoji") awards_handler.addAward(emoji) + $(".award").tooltip() + \ No newline at end of file -- cgit v1.2.1 From 2f6f99d300675b0794b2e96be564db9d405fac36 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 16:48:37 +0200 Subject: award for merge requests[ci skip] --- app/assets/stylesheets/pages/issuable.scss | 4 ++-- app/controllers/projects/merge_requests_controller.rb | 2 +- app/views/votes/_votes_block.html.haml | 10 +++------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 3f79d0d4967..5b73e20df7d 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -114,8 +114,8 @@ cursor: pointer; &.active { - border-color: rgba(79,176,252,.4); - background-color: rgba(79,176,252,.08); + border-color: rgba(79,176,252,0.4); + background-color: rgba(79,176,252,0.1); .counter { font-weight: bold; diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index a0468c65d5a..6378a1f56b0 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -254,7 +254,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) - @notes = @merge_request.nonawards.mr_and_commit_notes.inc_author.fresh + @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh @discussions = Note.discussions_from_notes(@notes) @noteable = @merge_request diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index a2298f1813a..2ae832c31f7 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -15,8 +15,8 @@ :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" - noteable_type = "Issue" - noteable_id = #{@issue.id} + noteable_type = #{votable} + noteable_id = #{votable.id} window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) $ -> @@ -28,8 +28,4 @@ emoji = $(this).find(".icon").data("emoji") awards_handler.addAward(emoji) - $(".award").tooltip() - - - - \ No newline at end of file + $(".award").tooltip() \ No newline at end of file -- cgit v1.2.1 From 2d1fcd802a291cc8e26fbbe5874e20316a5f93af Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 17:38:15 +0200 Subject: Emoji: refactoring --- app/helpers/issues_helper.rb | 4 ++++ app/views/votes/_votes_block.html.haml | 2 +- lib/award_emoji.rb | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 lib/award_emoji.rb diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 3aa16b66944..bca32322096 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -100,6 +100,10 @@ module IssuesHelper list.join(", ") end + def emoji_list + ::AwardEmoji::EMOJI_LIST + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 2ae832c31f7..02947f2979e 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -9,7 +9,7 @@ .dropdown.awards-controls %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + %ul.dropdown-menu.awards-menu - - ["100", "blush", "heart", "smile", "rage", "beers", "thumbsup", "disappointed", "ok_hand", "helicopter"].each do |emoji| + - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb new file mode 100644 index 00000000000..95b9c8f921a --- /dev/null +++ b/lib/award_emoji.rb @@ -0,0 +1,6 @@ +class AwardEmoji + EMOJI_LIST = ["+1", "-1", "100", "blush", "heart", "smile", "rage", + "beers", "disappointed", "ok_hand", + "helicopter", "shit", "airplane", "alarm_clock", + "ambulance", "anguished", "two_hearts", "wink"] +end \ No newline at end of file -- cgit v1.2.1 From d8676f18fcbd6b78af472b8b00c29f20820a74a9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 21:44:48 +0200 Subject: fix --- app/views/votes/_votes_block.html.haml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 02947f2979e..afd1c2745a1 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -15,17 +15,16 @@ :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" - noteable_type = #{votable} - noteable_id = #{votable.id} + noteable_type = "#{votable.class}" + noteable_id = "#{votable.id}" window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) - $ -> - $(".awards-menu li").click (e)-> - emoji = $(this).data("emoji") - awards_handler.addAward(emoji) + $(".awards-menu li").click (e)-> + emoji = $(this).data("emoji") + awards_handler.addAward(emoji) - $(".awards").on "click", ".award", (e)-> - emoji = $(this).find(".icon").data("emoji") - awards_handler.addAward(emoji) + $(".awards").on "click", ".award", (e)-> + emoji = $(this).find(".icon").data("emoji") + awards_handler.addAward(emoji) - $(".award").tooltip() \ No newline at end of file + $(".award").tooltip() \ No newline at end of file -- cgit v1.2.1 From 23df515fd09661a690c0c0a651e131bc3a6d0191 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 23:59:58 +0200 Subject: Emoji: fix image of emoji when it is submitted via comment --- app/assets/javascripts/awards_handler.coffee | 16 ++++++++++------ app/assets/javascripts/notes.js.coffee | 2 +- app/controllers/projects/notes_controller.rb | 1 + app/helpers/issues_helper.rb | 2 +- app/views/votes/_votes_block.html.haml | 1 - lib/award_emoji.rb | 4 ++++ 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 29b11b0cc58..cac9c10332a 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -5,7 +5,7 @@ class @AwardsHandler @postEmoji emoji, => @addAwardToEmojiBar(emoji) - addAwardToEmojiBar: (emoji) -> + addAwardToEmojiBar: (emoji, custom_path = '') -> if @exist(emoji) if @isActive(emoji) @decrementCounter(emoji) @@ -15,7 +15,7 @@ class @AwardsHandler counter.parent().addClass("active") @addMeToAuthorList(emoji) else - @createEmoji(emoji) + @createEmoji(emoji, custom_path) exist: (emoji) -> @findEmojiIcon(emoji).length > 0 @@ -58,11 +58,11 @@ class @AwardsHandler ), 200 - createEmoji: (emoji) -> + createEmoji: (emoji, custom_path) -> nodes = [] nodes.push("
") nodes.push("
") - nodes.push(@getImage(emoji)) + nodes.push(@getImage(emoji, custom_path)) nodes.push("
") nodes.push("
1") nodes.push("
") @@ -71,8 +71,12 @@ class @AwardsHandler $(".award").tooltip() - getImage: (emoji) -> - $("li[data-emoji='" + emoji + "'").html() + getImage: (emoji, custom_path) -> + if custom_path + $(".awards-menu li").first().html().replace(/emoji\/.*\.png/, custom_path) + else + $("li[data-emoji='" + emoji + "'").html() + postEmoji: (emoji, callback) -> $.post @post_emoji_url, { diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 73a95c455e8..7de7632201d 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -121,7 +121,7 @@ class @Notes @initTaskList() if note.award - awards_handler.addAwardToEmojiBar(note.note) + awards_handler.addAwardToEmojiBar(note.note, note.emoji_path) ### Check if note does not exists on page diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 357b292980d..98bf056a605 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -133,6 +133,7 @@ class Projects::NotesController < Projects::ApplicationController discussion_id: note.discussion_id, html: note_to_html(note), award: note.is_award, + emoji_path: note.is_award ? ::AwardEmoji.path_to_emoji_image(note.note) : "", note: note.note, discussion_html: note_to_discussion_html(note), discussion_with_diff_html: note_to_discussion_with_diff_html(note) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index bca32322096..bf289c6db14 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -88,7 +88,7 @@ module IssuesHelper end def url_to_emoji(name) - emoji_path = "emoji/#{Emoji.emoji_filename(name)}.png" + emoji_path = ::AwardEmoji.path_to_emoji_image(name) url_to_image(emoji_path) end diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index afd1c2745a1..5392915b4dd 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -12,7 +12,6 @@ - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" - :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" noteable_type = "#{votable.class}" diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb index 95b9c8f921a..9e296f0bc3c 100644 --- a/lib/award_emoji.rb +++ b/lib/award_emoji.rb @@ -3,4 +3,8 @@ class AwardEmoji "beers", "disappointed", "ok_hand", "helicopter", "shit", "airplane", "alarm_clock", "ambulance", "anguished", "two_hearts", "wink"] + + def self.path_to_emoji_image(name) + "emoji/#{Emoji.emoji_filename(name)}.png" + end end \ No newline at end of file -- cgit v1.2.1 From db91ef3a2a46511da23dff780791a70594e57312 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 00:03:45 +0200 Subject: better regexp --- app/services/notes/create_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index f448f61cc86..dbff58dfb9c 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -35,7 +35,7 @@ module Notes end def contains_emoji_only?(note) - note =~ /^:[-_+[:alnum:]]*:\s?/ + note =~ /\A:[-_+[:alnum:]]*:\s?\z/ end def emoji_name(note) -- cgit v1.2.1 From 92943580cb1647930dbfdd8d2957213326c134d9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 00:10:58 +0200 Subject: improve style --- app/assets/stylesheets/pages/issuable.scss | 4 +++- app/views/votes/_votes_block.html.haml | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 5b73e20df7d..7dd4f239c78 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -135,8 +135,10 @@ padding: 0 0 5px 5px; line-height: 1; - #add-award { + .add-award { font-size: 27px; + color: #ccc; + &:hover { text-decoration: none; } diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 5392915b4dd..fff74745919 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -7,7 +7,8 @@ = vote.last.count .dropdown.awards-controls - %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + + %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + = icon('plus-circle') %ul.dropdown-menu.awards-menu - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" -- cgit v1.2.1 From fdd5a8f2e16cc210f24d93334877f1ca7ce92ce9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 00:59:07 +0200 Subject: addressing comments --- app/helpers/issues_helper.rb | 4 ++++ app/models/note.rb | 4 ++-- app/views/votes/_votes_block.html.haml | 10 +++++----- db/migrate/20151106000015_add_is_award_to_notes.rb | 3 ++- db/schema.rb | 3 ++- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index bf289c6db14..3a238824c0d 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -104,6 +104,10 @@ module IssuesHelper ::AwardEmoji::EMOJI_LIST end + def note_active_class(notes, current_user) + notes.pluck(:author_id).include?(current_user.id) ? "active" : "" + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/models/note.rb b/app/models/note.rb index 458d433211c..d53f568a671 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -50,8 +50,8 @@ class Note < ActiveRecord::Base mount_uploader :attachment, AttachmentUploader # Scopes - scope :awards, ->{ where("is_award IS TRUE") } - scope :nonawards, ->{ where("is_award IS FALSE") } + scope :awards, ->{ where(is_award: true) } + scope :nonawards, ->{ where(is_award: false) } scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } scope :inline, ->{ where("line_code IS NOT NULL") } scope :not_inline, ->{ where(line_code: [nil, '']) } diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index fff74745919..3eadf209a59 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,10 +1,10 @@ .awards.votes-block - - votable.notes.awards.grouped_awards.each do | vote | - .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id)), title: emoji_author_list(vote.last, current_user)} - .icon{"data-emoji" => "#{vote.first}"} - = image_tag url_to_emoji(vote.first), height: "20px", width: "20px" + - votable.notes.awards.grouped_awards.each do | note | + .award{class: (note_active_class(note.last, current_user)), title: emoji_author_list(note.last, current_user)} + .icon{"data-emoji" => "#{note.first}"} + = image_tag url_to_emoji(note.first), height: "20px", width: "20px" .counter - = vote.last.count + = note.last.count .dropdown.awards-controls %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} diff --git a/db/migrate/20151106000015_add_is_award_to_notes.rb b/db/migrate/20151106000015_add_is_award_to_notes.rb index bffe85df3da..02b271637e9 100644 --- a/db/migrate/20151106000015_add_is_award_to_notes.rb +++ b/db/migrate/20151106000015_add_is_award_to_notes.rb @@ -1,5 +1,6 @@ class AddIsAwardToNotes < ActiveRecord::Migration def change - add_column :notes, :is_award, :boolean, default: false + add_column :notes, :is_award, :boolean, default: false, null: false + add_index :notes, :is_award end end diff --git a/db/schema.rb b/db/schema.rb index 6c322d33682..f5511ac1898 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -554,13 +554,14 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.boolean "system", default: false, null: false t.text "st_diff" t.integer "updated_by_id" - t.boolean "is_award", default: false + t.boolean "is_award", default: false, null: false end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree add_index "notes", ["created_at", "id"], name: "index_notes_on_created_at_and_id", using: :btree add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree + add_index "notes", ["is_award"], name: "index_notes_on_is_award", using: :btree add_index "notes", ["line_code"], name: "index_notes_on_line_code", using: :btree add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree -- cgit v1.2.1 From 73bc9edc4410d228db0258c7f0c6fa84dd1d2c49 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 01:00:56 +0200 Subject: add changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 94f07a31689..6bc0d07b1cf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -50,6 +50,7 @@ v 8.2.0 - Fix bug when milestone/label filter was empty for dashboard issues page - Add ability to create milestone in group projects from single form - Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez) + - Add Award Emoji to issue and merge request pages v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) -- cgit v1.2.1 From fa2ed94fe9aa9f4d43c2aec7d99103982976063f Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 01:19:32 +0200 Subject: fix schema --- db/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index f5511ac1898..f77f6dfc66d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -531,7 +531,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.string "type" t.string "description", default: "", null: false t.string "avatar" - t.boolean "visible", default: false + t.boolean "public", default: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree -- cgit v1.2.1 From a2912074be67deb6345a37787c14b7e640be26f8 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 01:31:15 +0200 Subject: satisfy rubocop --- app/controllers/projects/notes_controller.rb | 4 ++-- db/schema.rb | 4 +--- lib/award_emoji.rb | 8 +++++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 98bf056a605..8159cc50838 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_read_note! before_action :authorize_create_note!, only: [:create] before_action :authorize_admin_note!, only: [:update, :destroy] - before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle]] + before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle] def index current_fetched_at = Time.now.to_i @@ -76,7 +76,7 @@ class Projects::NotesController < Projects::ApplicationController project.notes.create(data) end - render json: {ok: true} + render json: { ok: true } end private diff --git a/db/schema.rb b/db/schema.rb index f77f6dfc66d..3062bf4d419 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -642,9 +642,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.integer "commit_count", default: 0 - t.boolean "merge_requests_ff_only_enabled", default: false - t.text "issues_template" + t.integer "commit_count", default: 0 t.text "import_error" end diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb index 9e296f0bc3c..d58a196c4ef 100644 --- a/lib/award_emoji.rb +++ b/lib/award_emoji.rb @@ -1,10 +1,12 @@ class AwardEmoji - EMOJI_LIST = ["+1", "-1", "100", "blush", "heart", "smile", "rage", + EMOJI_LIST = [ + "+1", "-1", "100", "blush", "heart", "smile", "rage", "beers", "disappointed", "ok_hand", "helicopter", "shit", "airplane", "alarm_clock", - "ambulance", "anguished", "two_hearts", "wink"] + "ambulance", "anguished", "two_hearts", "wink" + ] def self.path_to_emoji_image(name) "emoji/#{Emoji.emoji_filename(name)}.png" end -end \ No newline at end of file +end -- cgit v1.2.1 From 060e59f05436f29640f6890c69426be1cd79e5cd Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 11:06:12 +0100 Subject: Lfs on by default. --- config/gitlab.yml.example | 2 +- config/initializers/1_settings.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 8fdb2603ce8..1788d4c2c7c 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -126,7 +126,7 @@ production: &base ## Git LFS lfs: - enabled: false + enabled: true # The location where LFS objects are stored (default: shared/lfs-objects). # storage_path: shared/lfs-objects diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 6b7990c0ab0..b498fb1e1da 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -203,7 +203,7 @@ Settings.incoming_email['mailbox'] = "inbox" if Settings.incoming_email['mail # Git LFS # Settings['lfs'] ||= Settingslogic.new({}) -Settings.lfs['enabled'] = false if Settings.lfs['enabled'].nil? +Settings.lfs['enabled'] = true if Settings.lfs['enabled'].nil? Settings.lfs['storage_path'] = File.expand_path(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects"), Rails.root) # -- cgit v1.2.1 From 3cc2b48a82aae8b535ddf11e9057a29f2242524c Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 11:39:26 +0100 Subject: Backup LFS objects same as any upload. --- doc/raketasks/backup_restore.md | 2 +- lib/backup/lfs.rb | 13 +++++++++++++ lib/backup/manager.rb | 2 +- lib/tasks/gitlab/backup.rake | 21 +++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 lib/backup/lfs.rb diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 1a5442cdac7..4e645b21a85 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -29,7 +29,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ``` Also you can choose what should be backed up by adding environment variable SKIP. Available options: db, -uploads (attachments), repositories, builds(CI build output logs), artifacts (CI build artifacts). +uploads (attachments), repositories, builds(CI build output logs), artifacts (CI build artifacts), lfs (LFS objects). Use a comma to specify several options at the same time. ``` diff --git a/lib/backup/lfs.rb b/lib/backup/lfs.rb new file mode 100644 index 00000000000..4153467fbee --- /dev/null +++ b/lib/backup/lfs.rb @@ -0,0 +1,13 @@ +require 'backup/files' + +module Backup + class Lfs < Files + def initialize + super('lfs', Settings.lfs.storage_path) + end + + def create_files_dir + Dir.mkdir(app_files_dir, 0700) + end + end +end diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index e7eda7c6f45..099062eeb8b 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -154,7 +154,7 @@ module Backup end def archives_to_backup - %w{uploads builds artifacts}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact + %w{uploads builds artifacts lfs}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact end def folders_to_backup diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 3c46bcea40e..cb4abe13799 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -13,6 +13,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:uploads:create"].invoke Rake::Task["gitlab:backup:builds:create"].invoke Rake::Task["gitlab:backup:artifacts:create"].invoke + Rake::Task["gitlab:backup:lfs:create"].invoke backup = Backup::Manager.new backup.pack @@ -34,6 +35,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:uploads:restore"].invoke unless backup.skipped?("uploads") Rake::Task["gitlab:backup:builds:restore"].invoke unless backup.skipped?("builds") Rake::Task["gitlab:backup:artifacts:restore"].invoke unless backup.skipped?("artifacts") + Rake::Task["gitlab:backup:lfs:restore"].invoke unless backup.skipped?("lfs") Rake::Task["gitlab:shell:setup"].invoke backup.cleanup @@ -134,6 +136,25 @@ namespace :gitlab do end end + namespace :lfs do + task create: :environment do + $progress.puts "Dumping lfs objects ... ".blue + + if ENV["SKIP"] && ENV["SKIP"].include?("lfs") + $progress.puts "[SKIPPED]".cyan + else + Backup::Lfs.new.dump + $progress.puts "done".green + end + end + + task restore: :environment do + $progress.puts "Restoring lfs objects ... ".blue + Backup::Lfs.new.restore + $progress.puts "done".green + end + end + def configure_cron_mode if ENV['CRON'] # We need an object we can say 'puts' and 'print' to; let's use a -- cgit v1.2.1 From 2219743d5c6bffd80eaab55db76c0f7d19a8ae61 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 13:21:57 +0100 Subject: Add lfs to backup specs. --- spec/tasks/gitlab/backup_rake_spec.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index fb5e74af648..63bed2414df 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -16,7 +16,7 @@ describe 'gitlab:app namespace rake task' do end def reenable_backup_sub_tasks - %w{db repo uploads builds artifacts}.each do |subtask| + %w{db repo uploads builds artifacts lfs}.each do |subtask| Rake::Task["gitlab:backup:#{subtask}:create"].reenable end end @@ -49,7 +49,7 @@ describe 'gitlab:app namespace rake task' do to raise_error(SystemExit) end - it 'should invoke restoration on mach' do + it 'should invoke restoration on match' do allow(YAML).to receive(:load_file). and_return({ gitlab_version: gitlab_version }) expect(Rake::Task["gitlab:backup:db:restore"]).to receive(:invoke) @@ -57,6 +57,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:builds:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:backup:uploads:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive(:invoke) + expect(Rake::Task["gitlab:backup:lfs:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke) expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error end @@ -114,7 +115,7 @@ describe 'gitlab:app namespace rake task' do it 'should set correct permissions on the tar contents' do tar_contents, exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz} + %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz} ) expect(exit_status).to eq(0) expect(tar_contents).to match('db/') @@ -122,12 +123,13 @@ describe 'gitlab:app namespace rake task' do expect(tar_contents).to match('repositories/') expect(tar_contents).to match('builds.tar.gz') expect(tar_contents).to match('artifacts.tar.gz') + expect(tar_contents).to match('lfs.tar.gz') expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|artifacts.tar.gz)\/$/) end it 'should delete temp directories' do temp_dirs = Dir.glob( - File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts}') + File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts,lfs}') ) expect(temp_dirs).to be_empty @@ -163,13 +165,14 @@ describe 'gitlab:app namespace rake task' do it "does not contain skipped item" do tar_contents, _exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz} + %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz} ) expect(tar_contents).to match('db/') expect(tar_contents).to match('uploads.tar.gz') expect(tar_contents).to match('builds.tar.gz') expect(tar_contents).to match('artifacts.tar.gz') + expect(tar_contents).to match('lfs.tar.gz') expect(tar_contents).not_to match('repositories/') end @@ -183,6 +186,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:uploads:restore"]).not_to receive :invoke expect(Rake::Task["gitlab:backup:builds:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive :invoke + expect(Rake::Task["gitlab:backup:lfs:restore"]).to receive :invoke expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error end -- cgit v1.2.1 From 9b724baa7f03e02ca847d80eff4df529e63e79de Mon Sep 17 00:00:00 2001 From: Ferdinand Rosario Date: Thu, 19 Nov 2015 15:17:12 +0530 Subject: Updated rails patch --- Gemfile | 2 +- Gemfile.lock | 54 +++++++++++++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Gemfile b/Gemfile index 8a19885bcb1..98890108562 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -gem 'rails', '4.1.12' +gem 'rails', '4.1.14' # Specify a sprockets version due to security issue # See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY diff --git a/Gemfile.lock b/Gemfile.lock index 99cdc2a50ae..8dd49bcf968 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,25 +4,25 @@ GEM CFPropertyList (2.3.1) RedCloth (4.2.9) ace-rails-ap (2.0.1) - actionmailer (4.1.12) - actionpack (= 4.1.12) - actionview (= 4.1.12) + actionmailer (4.1.14) + actionpack (= 4.1.14) + actionview (= 4.1.14) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.12) - actionview (= 4.1.12) - activesupport (= 4.1.12) + actionpack (4.1.14) + actionview (= 4.1.14) + activesupport (= 4.1.14) rack (~> 1.5.2) rack-test (~> 0.6.2) - actionview (4.1.12) - activesupport (= 4.1.12) + actionview (4.1.14) + activesupport (= 4.1.14) builder (~> 3.1) erubis (~> 2.7.0) - activemodel (4.1.12) - activesupport (= 4.1.12) + activemodel (4.1.14) + activesupport (= 4.1.14) builder (~> 3.1) - activerecord (4.1.12) - activemodel (= 4.1.12) - activesupport (= 4.1.12) + activerecord (4.1.14) + activemodel (= 4.1.14) + activesupport (= 4.1.14) arel (~> 5.0.0) activerecord-deprecated_finders (1.0.4) activerecord-session_store (0.1.1) @@ -33,7 +33,7 @@ GEM activemodel (~> 4.0) activesupport (~> 4.0) rails-observers (~> 0.1.1) - activesupport (4.1.12) + activesupport (4.1.14) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) @@ -509,21 +509,21 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (4.1.12) - actionmailer (= 4.1.12) - actionpack (= 4.1.12) - actionview (= 4.1.12) - activemodel (= 4.1.12) - activerecord (= 4.1.12) - activesupport (= 4.1.12) + rails (4.1.14) + actionmailer (= 4.1.14) + actionpack (= 4.1.14) + actionview (= 4.1.14) + activemodel (= 4.1.14) + activerecord (= 4.1.14) + activesupport (= 4.1.14) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.12) + railties (= 4.1.14) sprockets-rails (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) - railties (4.1.12) - actionpack (= 4.1.12) - activesupport (= 4.1.12) + railties (4.1.14) + actionpack (= 4.1.14) + activesupport (= 4.1.14) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) @@ -687,7 +687,7 @@ GEM multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.3.2) + sprockets-rails (2.3.3) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) @@ -886,7 +886,7 @@ DEPENDENCIES rack-attack (~> 4.3.0) rack-cors (~> 0.4.0) rack-oauth2 (~> 1.0.5) - rails (= 4.1.12) + rails (= 4.1.14) raphael-rails (~> 2.1.2) rblineprof rdoc (~> 3.6) -- cgit v1.2.1 From 618b54910ef3183cd1a3bf71ffe6301e029973fb Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 19 Nov 2015 10:50:38 +0100 Subject: Improve UI for emoji awards Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/issuable.scss | 51 ++++++++++++++++--------- app/views/projects/issues/_discussion.html.haml | 2 +- app/views/votes/_votes_block.html.haml | 4 +- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 7dd4f239c78..affa34a5f83 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -103,19 +103,23 @@ } .awards { + @include clearfix; + line-height: 32px; + margin: 5px 0; + .award { + @include border-radius(5px); + border: 1px solid; - padding: 1px 3px; - width: 50px; - border-radius: 5px; - float:left; - margin: 0 3px; - border-color: #ccc; + padding: 0px 10px; + float: left; + margin: 0 5px; + border-color: $border-color; cursor: pointer; &.active { - border-color: rgba(79,176,252,0.4); - background-color: rgba(79,176,252,0.1); + border-color: $border-gray-light; + background-color: $gray-light; .counter { font-weight: bold; @@ -126,28 +130,39 @@ float: left; margin-right: 10px; } + + .counter { + float: left; + } } .awards-controls { - height: 25px; - width: 28px; + line-height: 32px; + margin-left: 10px; float: left; - padding: 0 0 5px 5px; - line-height: 1; .add-award { - font-size: 27px; - color: #ccc; + font-size: 24px; + color: $gl-gray; + position: relative; + top: 2px; - &:hover { - text-decoration: none; - } + &:hover, &:link { text-decoration: none; } } + + .awards-menu { + padding: $gl-padding; + min-width: 214px; + + > li { + margin: 5px; + } + } } - + .awards-menu{ li { float: left; diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index c5fd863ae99..020952dd001 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -7,7 +7,7 @@ = render 'shared/show_aside' -.gray-content-block.second-block +.gray-content-block.second-block.oneline-block .row .col-md-9 .votes-holder.pull-right diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 3eadf209a59..f32e5193d1a 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -8,7 +8,7 @@ .dropdown.awards-controls %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} - = icon('plus-circle') + = icon('plus-square-o') %ul.dropdown-menu.awards-menu - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" @@ -27,4 +27,4 @@ emoji = $(this).find(".icon").data("emoji") awards_handler.addAward(emoji) - $(".award").tooltip() \ No newline at end of file + $(".award").tooltip() -- cgit v1.2.1 From caf1c9d85bb9df05b8e7f90e1a844fe8ecb5a857 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 10:53:40 +0100 Subject: Fix CHANGELOG --- CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 94f07a31689..3d0cd3550ec 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,9 @@ v 8.2.0 - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu) - Improved performance of finding users by one of their Email addresses - Add allow_failure field to commit status API (Stan Hu) + - Commits without .gitlab-ci.yml are marked as skipped + - Save detailed error when YAML syntax is invalid + - Added build artifacts - Improved performance of replacing references in comments - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL @@ -119,7 +122,6 @@ v 8.1.0 - Show CI status on Your projects page and Starred projects page - Remove "Continuous Integration" page from dashboard - Add notes and SSL verification entries to hook APIs (Ben Boeckel) - - Added build artifacts - Fix grammar in admin area "labels" .nothing-here-block when no labels exist. - Move CI runners page to project settings area - Move CI variables page to project settings area -- cgit v1.2.1 From 2c7d8678623a9e207d54e2e39d7eef9e2f77cb47 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 19 Nov 2015 10:59:58 +0100 Subject: Few minor improvements to emoji awards UI Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/issuable.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index affa34a5f83..3a08ee70bc7 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -104,8 +104,8 @@ .awards { @include clearfix; - line-height: 32px; - margin: 5px 0; + line-height: 34px; + margin: 2px 0; .award { @include border-radius(5px); @@ -137,7 +137,6 @@ } .awards-controls { - line-height: 32px; margin-left: 10px; float: left; -- cgit v1.2.1 From 96cdacd4eae3fb939f2da4ba0240f7dfa10b63da Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 19 Nov 2015 11:05:21 +0100 Subject: Changelog entries for atom/profile improvements --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 94f07a31689..c979c23e06b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,8 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) v 8.2.0 + - Improved performance of finding projects and groups in various places + - Improved performance of rendering user profile pages and Atom feeds - Fix grouping of contributors by email in graph. - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Fix Drone CI service template not saving properly (Stan Hu) -- cgit v1.2.1 From 50f654161036ac0f97d9f931dc11678a9509d083 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 17:17:40 +0100 Subject: Added index on issues.state This field is queried when filtering issues and due to the lack of an index would end up triggering a sequence scan. --- db/migrate/20151109134526_add_issues_state_index.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/migrate/20151109134526_add_issues_state_index.rb diff --git a/db/migrate/20151109134526_add_issues_state_index.rb b/db/migrate/20151109134526_add_issues_state_index.rb new file mode 100644 index 00000000000..1c4d2e30171 --- /dev/null +++ b/db/migrate/20151109134526_add_issues_state_index.rb @@ -0,0 +1,5 @@ +class AddIssuesStateIndex < ActiveRecord::Migration + def change + add_index :issues, :state + end +end -- cgit v1.2.1 From d63da890943c57694fab0dc250655a2bcd49fabc Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 17:18:26 +0100 Subject: Added index on projects.visibility_level --- db/migrate/20151109134916_add_projects_visibility_level_index.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/migrate/20151109134916_add_projects_visibility_level_index.rb diff --git a/db/migrate/20151109134916_add_projects_visibility_level_index.rb b/db/migrate/20151109134916_add_projects_visibility_level_index.rb new file mode 100644 index 00000000000..600b4bafd98 --- /dev/null +++ b/db/migrate/20151109134916_add_projects_visibility_level_index.rb @@ -0,0 +1,5 @@ +class AddProjectsVisibilityLevelIndex < ActiveRecord::Migration + def change + add_index :projects, :visibility_level + end +end -- cgit v1.2.1 From e4ae8b1385eecce2e4438f83017799644a8b92ce Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:48:27 +0100 Subject: Updated DB schema with new issues/projects indexes --- db/schema.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 462d5ed3b29..d687e68d098 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -384,6 +384,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do 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", ["state"], name: "index_issues_on_state", using: :btree add_index "issues", ["title"], name: "index_issues_on_title", using: :btree create_table "keys", force: true do |t| @@ -641,9 +642,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.integer "commit_count", default: 0 - t.boolean "merge_requests_ff_only_enabled", default: false - t.text "issues_template" + t.integer "commit_count", default: 0 t.text "import_error" end @@ -653,6 +652,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree + add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree create_table "protected_branches", force: true do |t| t.integer "project_id", null: false -- cgit v1.2.1 From 45840426a79f5ab251caf71400d3e4ed5f5eedbf Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:48:56 +0100 Subject: Added benchmark for IssuesFinder --- spec/benchmarks/finders/issues_finder_spec.rb | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 spec/benchmarks/finders/issues_finder_spec.rb diff --git a/spec/benchmarks/finders/issues_finder_spec.rb b/spec/benchmarks/finders/issues_finder_spec.rb new file mode 100644 index 00000000000..c233fa82cf2 --- /dev/null +++ b/spec/benchmarks/finders/issues_finder_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe IssuesFinder, benchmark: true do + describe '#execute' do + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + + let(:label1) { create(:label, project: project, title: 'A') } + let(:label2) { create(:label, project: project, title: 'B') } + + before do + 10.times do |n| + issue = create(:issue, author: user, project: project) + + if n > 4 + create(:label_link, label: label1, target: issue) + create(:label_link, label: label2, target: issue) + end + end + end + + describe 'retrieving issues without labels' do + let(:finder) do + IssuesFinder.new(user, scope: 'all', label_name: Label::None.title, + state: 'opened') + end + + benchmark_subject { finder.execute } + + it { is_expected.to iterate_per_second(2000) } + end + + describe 'retrieving issues with labels' do + let(:finder) do + IssuesFinder.new(user, scope: 'all', label_name: label1.title, + state: 'opened') + end + + benchmark_subject { finder.execute } + + it { is_expected.to iterate_per_second(1000) } + end + + describe 'retrieving issues for a single project' do + let(:finder) do + IssuesFinder.new(user, scope: 'all', label_name: Label::None.title, + state: 'opened', project_id: project.id) + end + + benchmark_subject { finder.execute } + + it { is_expected.to iterate_per_second(2000) } + end + end +end -- cgit v1.2.1 From c232a0f97f9b724a142c728ebeceba25ef15ab32 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:49:16 +0100 Subject: Removed trailing whitespace from IssuableFinder --- app/finders/issuable_finder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index c407dfc163a..30ec8f60098 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -62,10 +62,10 @@ class IssuableFinder if project? @project = Project.find(params[:project_id]) - + unless Ability.abilities.allowed?(current_user, :read_project, @project) @project = nil - end + end else @project = nil end -- cgit v1.2.1 From e9cd58f5d50a7b5cfc14e08cd9526505e24f1071 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:49:31 +0100 Subject: Memoize IssuableFinder#projects Since this method's returned data doesn't change between calls on the same IssuableFinder instance we can just memoize this similar to the "project" method. --- app/finders/issuable_finder.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 30ec8f60098..15b5d6ab34c 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -77,11 +77,11 @@ class IssuableFinder return @projects if defined?(@projects) if project? - project + @projects = project elsif current_user && params[:authorized_only].presence && !current_user_related? - current_user.authorized_projects + @projects = current_user.authorized_projects else - ProjectsFinder.new.execute(current_user) + @projects = ProjectsFinder.new.execute(current_user) end end -- cgit v1.2.1 From 8591cc02be6b12ed60f763a5e0147f2cbbca99e1 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:50:36 +0100 Subject: Use a JOIN in IssuableFinder#by_project When using IssuableFinder/IssuesFinder to find issues for multiple projects it's more efficient to use a JOIN + a "WHERE project_id IN" condition opposed to running a sub-query. This change means that when finding issues without labels we're now using the following SQL: SELECT issues.* FROM issues JOIN projects ON projects.id = issues.project_id LEFT JOIN label_links ON label_links.target_type = 'Issue' AND label_links.target_id = issues.id WHERE ( projects.id IN (...) OR projects.visibility_level IN (20, 10) ) AND issues.state IN ('opened','reopened') AND label_links.id IS NULL ORDER BY issues.id DESC; instead of: SELECT issues.* FROM issues LEFT JOIN label_links ON label_links.target_type = 'Issue' AND label_links.target_id = issues.id WHERE issues.project_id IN ( SELECT id FROM projects WHERE id IN (...) OR visibility_level IN (20,10) ) AND issues.state IN ('opened','reopened') AND label_links.id IS NULL ORDER BY issues.id DESC; The big benefit here is that in the last case PostgreSQL can't properly use all available indexes. In particular it ends up performing a sequence scan on the "label_links" table (processing around 290 000 rows). The new query is roughly 2x as fast as the old query. --- app/finders/issuable_finder.rb | 10 +++++++--- app/models/concerns/issuable.rb | 3 +++ app/models/merge_request.rb | 3 +++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 15b5d6ab34c..3d5e8b6fbe7 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -190,8 +190,10 @@ class IssuableFinder def by_project(items) items = - if projects - items.of_projects(projects).references(:project) + if project? + items.of_projects(projects).references_project + elsif projects + items.merge(projects.reorder(nil)).join_project else items.none end @@ -206,7 +208,9 @@ class IssuableFinder end def sort(items) - items.sort(params[:sort]) + # Ensure we always have an explicit sort order (instead of inheriting + # multiple orders when combining ActiveRecord::Relation objects). + params[:sort] ? items.sort(params[:sort]) : items.reorder(id: :desc) end def by_assignee(items) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 492a026add9..97d3cb18f4e 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -35,6 +35,9 @@ module Issuable scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') } scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') } + scope :join_project, -> { joins(:project) } + scope :references_project, -> { references(:project) } + delegate :name, :email, to: :author, diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 2eb03b8ba5b..1e8d9908f0a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -134,6 +134,9 @@ class MergeRequest < ActiveRecord::Base scope :closed, -> { with_state(:closed) } scope :closed_and_merged, -> { with_states(:closed, :merged) } + scope :join_project, -> { joins(:target_project) } + scope :references_project, -> { references(:target_project) } + def self.reference_prefix '!' end -- cgit v1.2.1 From 901d3dee0fdd0f67b429dd3741fe047f69e96d4a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 13:00:41 +0100 Subject: Changelog entry for finding issues performance --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index ea8d5d6000f..baf7c8e5ad7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.2.0 - Improved performance of finding projects and groups in various places - Improved performance of rendering user profile pages and Atom feeds - Fix grouping of contributors by email in graph. + - Improved performance of finding issues with/without labels - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Fix Drone CI service template not saving properly (Stan Hu) - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu) -- cgit v1.2.1 From 2b907f61ff5db3ff68b27a9d3bb164745ab7703b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 18 Nov 2015 16:32:00 +0100 Subject: Commits without .gitlab-ci.yml are marked as skipped - Save detailed error when YAML syntax --- app/models/ci/commit.rb | 5 ++++- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 6 +++++- spec/services/ci/create_commit_service_spec.rb | 20 +++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 33b57173928..73c1570c212 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -188,12 +188,15 @@ module Ci end def config_processor + return nil unless ci_yaml_file @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) rescue Ci::GitlabCiYamlProcessor::ValidationError => e save_yaml_error(e.message) nil + rescue Psych::SyntaxError => e + save_yaml_error(e.message) + nil rescue Exception => e - logger.error e.message + "\n" + e.backtrace.join("\n") save_yaml_error("Undefined yaml error") nil end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 7d90f9877c6..6f287719ba6 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -425,8 +425,12 @@ module Ci end describe "Error handling" do + it "fails to parse YAML" do + expect{GitlabCiYamlProcessor.new("invalid: yaml: test")}.to raise_error(Psych::SyntaxError) + end + it "indicates that object is invalid" do - expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError) + expect{GitlabCiYamlProcessor.new("invalid_yaml")}.to raise_error(GitlabCiYamlProcessor::ValidationError) end it "returns errors if tags parameter is invalid" do diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index e3a8fe9681b..392f5fce35f 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -100,7 +100,7 @@ module Ci end it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do - stub_ci_commit_yaml_file('invalid: file') + stub_ci_commit_yaml_file('invalid: file: fiile') commits = [{ message: message }] commit = service.execute(project, user, ref: 'refs/tags/0_1', @@ -110,6 +110,24 @@ module Ci ) expect(commit.builds.any?).to be false expect(commit.status).to eq("skipped") + expect(commit.yaml_errors).to be_nil + end + end + + describe :config_processor do + it "skips builds creation if yaml is invalid" do + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "message" } + stub_ci_commit_yaml_file('invalid: file: file') + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq("skipped") + expect(commit.yaml_errors).to_not be_nil end end -- cgit v1.2.1 From 0df7a32ea50baf251f03e6bfc5b91c5ccb68aad0 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 12:08:30 +0100 Subject: Fix tests --- app/models/ci/commit.rb | 2 +- spec/services/ci/create_commit_service_spec.rb | 38 +++++++++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 73c1570c212..b0c78499e49 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -196,7 +196,7 @@ module Ci rescue Psych::SyntaxError => e save_yaml_error(e.message) nil - rescue Exception => e + rescue Exception save_yaml_error("Undefined yaml error") nil end diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index 392f5fce35f..58d81c46a6d 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -53,7 +53,7 @@ module Ci end end - it 'fails commits without .gitlab-ci.yml' do + it 'skips commits without .gitlab-ci.yml' do stub_ci_commit_yaml_file(nil) result = service.execute(project, user, ref: 'refs/heads/0_1', @@ -63,7 +63,24 @@ module Ci ) expect(result).to be_persisted expect(result.builds.any?).to be_falsey - expect(result.status).to eq('failed') + expect(result.status).to eq('skipped') + expect(commit.yaml_errors).to_not be_nil + end + + it 'skips commits if yaml is invalid' do + message = 'message' + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } + stub_ci_commit_yaml_file('invalid: file: file') + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq('skipped') + expect(commit.yaml_errors).to_not be_nil end describe :ci_skip? do @@ -114,23 +131,6 @@ module Ci end end - describe :config_processor do - it "skips builds creation if yaml is invalid" do - allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "message" } - stub_ci_commit_yaml_file('invalid: file: file') - commits = [{ message: message }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.any?).to be false - expect(commit.status).to eq("skipped") - expect(commit.yaml_errors).to_not be_nil - end - end - it "skips build creation if there are already builds" do allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml } -- cgit v1.2.1 From fd6b58949652c5fb9925c061fae823da7e468ca2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 12:09:24 +0100 Subject: Since GitLab CI is enabled by default, remove enabling it by pushing .gitlab-ci.yml --- CHANGELOG | 1 + app/services/git_push_service.rb | 12 ------------ 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ea8d5d6000f..01f18dea23b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ v 8.2.0 - Add allow_failure field to commit status API (Stan Hu) - Commits without .gitlab-ci.yml are marked as skipped - Save detailed error when YAML syntax is invalid + - Since GitLab CI is enabled by default, remove enabling it by pushing .gitlab-ci.yml - Added build artifacts - Improved performance of replacing references in comments - Show last project commit to default branch on project home page diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index ccb6b97858c..f11690aa3f4 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -58,12 +58,6 @@ class GitPushService @push_data = build_push_data(oldrev, newrev, ref) - # If CI was disabled but .gitlab-ci.yml file was pushed - # we enable CI automatically - if !project.builds_enabled? && gitlab_ci_yaml?(newrev) - project.enable_ci - end - EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :push_hooks) project.execute_services(@push_data.dup, :push_hooks) @@ -134,10 +128,4 @@ class GitPushService def commit_user(commit) commit.author || user end - - def gitlab_ci_yaml?(sha) - @project.repository.blob_at(sha, '.gitlab-ci.yml') - rescue Rugged::ReferenceError - nil - end end -- cgit v1.2.1 From 1f9a0bd764ed2935c4438dbc81001b0d69df26ea Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 12:15:54 +0100 Subject: Bump GitLab Workhorse version to 0.4.2 --- GITLAB_WORKHORSE | 1 - GITLAB_WORKHORSE_VERSION | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 GITLAB_WORKHORSE diff --git a/GITLAB_WORKHORSE b/GITLAB_WORKHORSE deleted file mode 100644 index 267577d47e4..00000000000 --- a/GITLAB_WORKHORSE +++ /dev/null @@ -1 +0,0 @@ -0.4.1 diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 9e11b32fcaa..2b7c5ae0184 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -0.3.1 +0.4.2 -- cgit v1.2.1 From 23c5473cc0bd9b9034c5671fbea1356b0fb531e5 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 13:20:09 +0200 Subject: added spinach tests --- app/assets/javascripts/awards_handler.coffee | 4 +- app/helpers/issues_helper.rb | 6 ++- app/models/note.rb | 1 + app/views/votes/_votes_block.html.haml | 38 +++++++------- features/project/issues/award_emoji.feature | 14 ++++++ features/steps/project/issues/award_emoji.rb | 41 +++++++++++++++ spec/models/note_spec.rb | 75 ---------------------------- 7 files changed, 83 insertions(+), 96 deletions(-) create mode 100644 features/project/issues/award_emoji.feature create mode 100644 features/steps/project/issues/award_emoji.rb diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index cac9c10332a..f5b9adbe9e2 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -75,7 +75,7 @@ class @AwardsHandler if custom_path $(".awards-menu li").first().html().replace(/emoji\/.*\.png/, custom_path) else - $("li[data-emoji='" + emoji + "'").html() + $("li[data-emoji='" + emoji + "']").html() postEmoji: (emoji, callback) -> @@ -88,4 +88,4 @@ class @AwardsHandler callback.call() findEmojiIcon: (emoji) -> - $(".icon[data-emoji='" + emoji + "'") \ No newline at end of file + $(".icon[data-emoji='" + emoji + "']") \ No newline at end of file diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 3a238824c0d..2c791aa5682 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -105,7 +105,11 @@ module IssuesHelper end def note_active_class(notes, current_user) - notes.pluck(:author_id).include?(current_user.id) ? "active" : "" + if current_user && notes.pluck(:author_id).include?(current_user.id) + "active" + else + "" + end end # Required for Gitlab::Markdown::IssueReferenceFilter diff --git a/app/models/note.rb b/app/models/note.rb index d53f568a671..39645f8f1ad 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -46,6 +46,7 @@ class Note < ActiveRecord::Base validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' } validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' } + validates :author, presence: true, if: ->(n) { n.is_award } mount_uploader :attachment, AttachmentUploader diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index f32e5193d1a..04e3b5a3814 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -6,25 +6,27 @@ .counter = note.last.count - .dropdown.awards-controls - %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} - = icon('plus-square-o') - %ul.dropdown-menu.awards-menu - - emoji_list.each do |emoji| - %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" + - if current_user + .dropdown.awards-controls + %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + = icon('plus-square-o') + %ul.dropdown-menu.awards-menu + - emoji_list.each do |emoji| + %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" -:coffeescript - post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" - noteable_type = "#{votable.class}" - noteable_id = "#{votable.id}" - window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) +- if current_user + :coffeescript + post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" + noteable_type = "#{votable.class}" + noteable_id = "#{votable.id}" + window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) - $(".awards-menu li").click (e)-> - emoji = $(this).data("emoji") - awards_handler.addAward(emoji) + $(".awards-menu li").click (e)-> + emoji = $(this).data("emoji") + awards_handler.addAward(emoji) - $(".awards").on "click", ".award", (e)-> - emoji = $(this).find(".icon").data("emoji") - awards_handler.addAward(emoji) + $(".awards").on "click", ".award", (e)-> + emoji = $(this).find(".icon").data("emoji") + awards_handler.addAward(emoji) - $(".award").tooltip() + $(".award").tooltip() diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature new file mode 100644 index 00000000000..a9bc8ffb9bb --- /dev/null +++ b/features/project/issues/award_emoji.feature @@ -0,0 +1,14 @@ +Feature: Award Emoji + Background: + Given I sign in as a user + And I own project "Shop" + And project "Shop" has issue "Bugfix" + And I visit "Bugfix" issue page + + @javascript + Scenario: I add and remove award in the issue + Given I click to emoji-picker + And I click to emoji in the picker + Then I have award added + And I can remove it by clicking to icon + \ No newline at end of file diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb new file mode 100644 index 00000000000..8f7a45dec0e --- /dev/null +++ b/features/steps/project/issues/award_emoji.rb @@ -0,0 +1,41 @@ +class Spinach::Features::AwardEmoji < Spinach::FeatureSteps + include SharedAuthentication + include SharedProject + include SharedPaths + include Select2Helper + + step 'I visit "Bugfix" issue page' do + visit namespace_project_issue_path(@project.namespace, @project, @issue) + end + + step 'I click to emoji-picker' do + page.within ".awards-controls" do + page.find(".add-award").click + end + end + + step 'I click to emoji in the picker' do + page.within ".awards-menu" do + page.first("img").click + end + end + + step 'I can remove it by clicking to icon' do + page.within ".awards" do + page.first(".award").click + expect(page).to_not have_selector ".award" + end + end + + step 'I have award added' do + page.within ".awards" do + expect(page).to have_selector ".award" + expect(page.find(".award .counter")).to have_content "1" + end + end + + step 'project "Shop" has issue "Bugfix"' do + @project = Project.find_by(name: "Shop") + @issue = create(:issue, title: "Bugfix", project: project) + end +end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 75564839dcf..6e37dae07d0 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -32,77 +32,6 @@ describe Note do it { is_expected.to validate_presence_of(:project) } end - describe '#votable?' do - it 'is true for issue notes' do - note = build(:note_on_issue) - expect(note).to be_votable - end - - it 'is true for merge request notes' do - note = build(:note_on_merge_request) - expect(note).to be_votable - end - - it 'is false for merge request diff notes' do - note = build(:note_on_merge_request_diff) - expect(note).not_to be_votable - end - - it 'is false for commit notes' do - note = build(:note_on_commit) - expect(note).not_to be_votable - end - - it 'is false for commit diff notes' do - note = build(:note_on_commit_diff) - expect(note).not_to be_votable - end - end - - describe 'voting score' do - it 'recognizes a neutral note' do - note = build(:votable_note, note: 'This is not a +1 note') - expect(note).not_to be_upvote - expect(note).not_to be_downvote - end - - it 'recognizes a neutral emoji note' do - note = build(:votable_note, note: "I would :+1: this, but I don't want to") - expect(note).not_to be_upvote - expect(note).not_to be_downvote - end - - it 'recognizes a +1 note' do - note = build(:votable_note, note: '+1 for this') - expect(note).to be_upvote - end - - it 'recognizes a +1 emoji as a vote' do - note = build(:votable_note, note: ':+1: for this') - expect(note).to be_upvote - end - - it 'recognizes a thumbsup emoji as a vote' do - note = build(:votable_note, note: ':thumbsup: for this') - expect(note).to be_upvote - end - - it 'recognizes a -1 note' do - note = build(:votable_note, note: '-1 for this') - expect(note).to be_downvote - end - - it 'recognizes a -1 emoji as a vote' do - note = build(:votable_note, note: ':-1: for this') - expect(note).to be_downvote - end - - it 'recognizes a thumbsdown emoji as a vote' do - note = build(:votable_note, note: ':thumbsdown: for this') - expect(note).to be_downvote - end - end - describe "Commit notes" do let!(:note) { create(:note_on_commit, note: "+1 from me") } let!(:commit) { note.noteable } @@ -139,10 +68,6 @@ describe Note do it "should be recognized by #for_commit_diff_line?" do expect(note).to be_for_commit_diff_line end - - it "should not be votable" do - expect(note).not_to be_votable - end end describe 'authorization' do -- cgit v1.2.1 From 671a49cfd53230b57acf579a609bab958e066982 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 14:41:05 +0200 Subject: added specs --- spec/helpers/issues_helper_spec.rb | 26 +++++++++++++++++++++++ spec/models/note_spec.rb | 13 ++++++++++++ spec/services/notes/create_service_spec.rb | 34 ++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 78a6b631eb2..1f2c4ee77b5 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -127,4 +127,30 @@ describe IssuesHelper do it { is_expected.to eq("!1, !2, or !3") } end + describe "#url_to_emoji" do + it "returns url" do + expect(url_to_emoji("smile")).to include("emoji/1F604.png") + end + end + + describe "#emoji_list" do + it "returns url" do + expect(emoji_list).to be_kind_of(Array) + end + end + + describe "#note_active_class" do + before do + @note = create :note + @note1 = create :note + end + + it "returns empty string for unauthenticated user" do + expect(note_active_class(Note.all, nil)).to eq("") + end + + it "returns active string for author" do + expect(note_active_class(Note.all, @note.author)).to eq("active") + end + end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 6e37dae07d0..86b6c27a182 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -129,4 +129,17 @@ describe Note do it { expect(Note.search('wow')).to include(note) } end + + describe :grouped_awards do + before do + create :note, note: "smile" + create :note, note: "smile" + end + + it "returns grouped array of notes" do + grouped_array = Note.grouped_awards + expect(Note.grouped_awards.first.first).to eq("smile") + expect(Note.grouped_awards.first.last).to match_array(Note.all) + end + end end diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index f2ea0805b2f..066461f5a12 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -24,4 +24,38 @@ describe Notes::CreateService do it { expect(@note.note).to eq('Awesome comment') } end end + + describe "award emoji" do + before do + project.team << [user, :master] + end + + it "creates emoji note" do + opts = { + note: ':smile: ', + noteable_type: 'Issue', + noteable_id: issue.id + } + + @note = Notes::CreateService.new(project, user, opts).execute + + expect(@note).to be_valid + expect(@note.note).to eq('smile') + expect(@note.is_award).to be_truthy + end + + it "creates regular note if emoji name is invalid" do + opts = { + note: ':smile: moretext: ', + noteable_type: 'Issue', + noteable_id: issue.id + } + + @note = Notes::CreateService.new(project, user, opts).execute + + expect(@note).to be_valid + expect(@note.note).to eq(opts[:note]) + expect(@note.is_award).to be_falsy + end + end end -- cgit v1.2.1 From 372dcc217eeb379f603c10c81115dd7579762b59 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 19 Nov 2015 13:51:18 +0100 Subject: Do not limit workhorse POST/PUT size in NGINX Limiting, if any, should happen in gitlab-workhorse. --- lib/support/nginx/gitlab | 4 +--- lib/support/nginx/gitlab-ssl | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 93f2ad07aeb..f38e2e62097 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -114,7 +114,6 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -140,7 +139,6 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -148,13 +146,13 @@ server { # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location @gitlab-workhorse { + client_max_body_size 0; ## If you use HTTPS make sure you disable gzip compression ## to be safe against BREACH attack. # gzip off; diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 90749947fa4..4cc6a17db04 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -161,7 +161,6 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -187,7 +186,6 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -195,13 +193,13 @@ server { # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location @gitlab-workhorse { + client_max_body_size 0; ## If you use HTTPS make sure you disable gzip compression ## to be safe against BREACH attack. gzip off; -- cgit v1.2.1 From d70da301f61f3333b65a5b92de79eea4b7e032f6 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 14:13:17 +0100 Subject: Add link to git-lfs client [ci skip] --- doc/workflow/git_lfs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/workflow/git_lfs.md b/doc/workflow/git_lfs.md index 4990a9b5aac..e1064051fe8 100644 --- a/doc/workflow/git_lfs.md +++ b/doc/workflow/git_lfs.md @@ -17,7 +17,7 @@ Once the request is authorized, Git LFS client receives instructions from where ## Requirements * Git LFS is supported in GitLab starting with version 8.2 -* Git LFS client version 0.6.0 and up +* Git LFS [client](https://git-lfs.github.com) version 0.6.0 and up ## GitLab and Git LFS -- cgit v1.2.1 From 97842cea7409d80a3bf22f3ab8993a2ce3b02195 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 19 Nov 2015 15:37:20 +0100 Subject: Fix 'Attach a file' link in new tag form --- app/views/projects/_zen.html.haml | 9 ++++++--- app/views/projects/releases/edit.html.haml | 3 +-- app/views/projects/tags/new.html.haml | 13 ++----------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml index 63ebfc9381f..7e6301abde8 100644 --- a/app/views/projects/_zen.html.haml +++ b/app/views/projects/_zen.html.haml @@ -2,9 +2,12 @@ %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox") .zen-backdrop - classes << ' js-gfm-input markdown-area' - = f.text_area attr, class: classes, placeholder: '' + - if defined?(f) && f + = f.text_area attr, class: classes, placeholder: '' + - else + = text_area_tag attr, nil, class: classes, placeholder: '' %a.zen-enter-link(tabindex="-1" href="#") - %i.fa.fa-expand + = icon('expand') Edit in fullscreen %a.zen-leave-link(href="#") - %i.fa.fa-compress + = icon('compress') diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index e7db09cdaa9..f516b65ecd0 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -11,10 +11,9 @@ .prepend-top-default = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form' }) do |f| = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do - = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit' + = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' .error-alert .prepend-top-default = f.submit 'Save changes', class: 'btn btn-save' = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" - diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index e106be794f1..86aa15dc5b3 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -10,7 +10,7 @@ New git tag %hr -= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal tag-form" do += form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do .form-group = label_tag :tag_name, 'Name for new tag', class: 'control-label' .col-sm-10 @@ -30,16 +30,7 @@ = label_tag :release_description, 'Release notes', class: 'control-label' .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do - .zennable - %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox") - .zen-backdrop - = text_area_tag :release_description, nil, class: 'js-gfm-input markdown-area description js-quick-submit form-control', placeholder: '' - %a.zen-enter-link(tabindex="-1" href="#") - = icon('expand') - Edit in fullscreen - %a.zen-leave-link(href="#") - = icon('compress') - + = render 'projects/zen', attr: :release_description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' .help-block (Optional) You can add release notes to your tag. It will be stored in the GitLab database and shown on the tags page .form-actions -- cgit v1.2.1 From a5b10196e649b57cf7cc698fe6fb34f73eec47ea Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 15:56:03 +0100 Subject: Fix tests --- spec/services/ci/create_commit_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index 58d81c46a6d..e0ede1d58b7 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -64,7 +64,7 @@ module Ci expect(result).to be_persisted expect(result.builds.any?).to be_falsey expect(result.status).to eq('skipped') - expect(commit.yaml_errors).to_not be_nil + expect(result.yaml_errors).to be_nil end it 'skips commits if yaml is invalid' do @@ -79,7 +79,7 @@ module Ci commits: commits ) expect(commit.builds.any?).to be false - expect(commit.status).to eq('skipped') + expect(commit.status).to eq('failed') expect(commit.yaml_errors).to_not be_nil end -- cgit v1.2.1 From cf61b8e22c9c78165ce55c7c2f78ae0b3c7af7aa Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 16:01:55 +0100 Subject: Update gitlab-workhorse docs to 0.4.2 --- doc/install/installation.md | 4 ++-- doc/update/8.1-to-8.2.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 7ef46b04065..71b0ef3ebb0 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -327,12 +327,12 @@ GitLab Shell is an SSH access and repository management software developed speci cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git cd gitlab-workhorse - sudo -u git -H git checkout 0.4.1 + sudo -u git -H git checkout 0.4.2 sudo -u git -H make ### Initialize Database and Activate Advanced Features - # Go to Gitlab installation folder + # Go to GitLab installation folder cd /home/git/gitlab diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index 73d899f9c2e..81e13f714b7 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -81,7 +81,7 @@ from GitLab 8.1. cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git cd gitlab-workhorse -sudo -u git -H git checkout 0.4.1 +sudo -u git -H git checkout 0.4.2 sudo -u git -H make ``` -- cgit v1.2.1 From 094e1cc01b4f98ea4b8cd664344f3b8b583af471 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 19 Nov 2015 16:02:21 +0100 Subject: Align hash literals in IssuesFinder spec --- spec/benchmarks/finders/issues_finder_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/benchmarks/finders/issues_finder_spec.rb b/spec/benchmarks/finders/issues_finder_spec.rb index c233fa82cf2..b57a33004a4 100644 --- a/spec/benchmarks/finders/issues_finder_spec.rb +++ b/spec/benchmarks/finders/issues_finder_spec.rb @@ -22,7 +22,7 @@ describe IssuesFinder, benchmark: true do describe 'retrieving issues without labels' do let(:finder) do IssuesFinder.new(user, scope: 'all', label_name: Label::None.title, - state: 'opened') + state: 'opened') end benchmark_subject { finder.execute } @@ -33,7 +33,7 @@ describe IssuesFinder, benchmark: true do describe 'retrieving issues with labels' do let(:finder) do IssuesFinder.new(user, scope: 'all', label_name: label1.title, - state: 'opened') + state: 'opened') end benchmark_subject { finder.execute } @@ -44,7 +44,7 @@ describe IssuesFinder, benchmark: true do describe 'retrieving issues for a single project' do let(:finder) do IssuesFinder.new(user, scope: 'all', label_name: Label::None.title, - state: 'opened', project_id: project.id) + state: 'opened', project_id: project.id) end benchmark_subject { finder.execute } -- cgit v1.2.1 From aec4aac0c2825befe8baff392107dcf7702d3d04 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 16:06:13 +0100 Subject: Update gitlab-shell documentation [ci skip] --- doc/install/installation.md | 2 +- doc/update/8.1-to-8.2.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 7ef46b04065..52ae30af805 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -312,7 +312,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.6] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.7] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index 73d899f9c2e..261031442ac 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -68,7 +68,7 @@ sudo -u git -H git checkout 8-2-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v2.6.6 +sudo -u git -H git checkout v2.6.7 ``` ### 5. Replace gitlab-git-http-server with gitlab-workhorse -- cgit v1.2.1 From 52bcfd037fdc17bd73a674c248045dc750cc55c6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 19 Nov 2015 17:18:07 +0200 Subject: Update public access documentation [ci skip] --- doc/public_access/public_access.md | 51 ++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md index bd439f7c6f3..6e22ea7b72a 100644 --- a/doc/public_access/public_access.md +++ b/doc/public_access/public_access.md @@ -1,44 +1,59 @@ # Public access -GitLab allows you to open selected projects to be accessed **publicly** or **internally**. +GitLab allows you to change your projects' visibility in order be accessed +**publicly** or **internally**. -Projects with either of these visibility levels will be listed in the [public access directory](/public). +Projects with either of these visibility levels will be listed in the +public access directory (`/public` under your GitLab instance). +Here is the [GitLab.com example](https://gitlab.com/public). Internal projects will only be available to authenticated users. -## Public projects +## Visibility of projects + +### Public projects Public projects can be cloned **without any** authentication. -It will also be listed on the [public access directory](/public). +They will also be listed on the public access directory (`/public`). -**Any logged in user** will have [Guest](../permissions/permissions) permissions on the repository. +**Any logged in user** will have [Guest](../permissions/permissions) +permissions on the repository. -## Internal projects +### Internal projects Internal projects can be cloned by any logged in user. -It will also be listed on the [public access directory](/public) for logged in users. +They will also be listed on the public access directory (`/public`) for logged +in users. -Any logged in user will have [Guest](../permissions/permissions) permissions on the repository. +Any logged in user will have [Guest](../permissions/permissions) permissions on +the repository. -## How to change project visibility +### How to change project visibility -1. Go to your project dashboard -1. Click on the "Edit" tab -1. Change "Visibility Level" +1. Go to your project's **Settings** +1. Change "Visibility Level" to either Public, Internal or Private ## Visibility of users -The public page of users, located at `/u/username` is visible if either: +The public page of a user, located at `/u/username`, is always visible whether +you are logged in or not. + +When visiting the public page of a user, you can only see the projects which +you are privileged to. -- You are logged in. -- You are logged out, and the target user is authorized to (is Guest, Reporter, etc.) at least one public project. +## Visibility of groups -Otherwise, you will be redirected to the sign in page. +The public page of a group, located at `/groups/groupname`, is always visible +to everyone. -When visiting the public page of an user, you will only see listed projects which you can view yourself. +Logged out users will be able to see the description and the avatar of the +group as well as all public projects belonging to that group. ## Restricting the use of public or internal projects -In the Admin area under Settings you can disable public projects or public and internal projects for the entire GitLab installation to prevent people making code public by accident. The restricted visibility settings do not apply to admin users. +In the Admin area under **Settings** (`/admin/application_settings`), you can +restrict the use of visibility levels for users when they create a project or a +snippet. This is useful to prevent people exposing their repositories to public +by accident. The restricted visibility settings do not apply to admin users. -- cgit v1.2.1 From de440ee5e3fd03d0e3de33c474b1889e03aaf04a Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 16:25:17 +0100 Subject: More GitLab spellings. No dedicated CI in omnibus [ci skip] --- CONTRIBUTING.md | 10 +++++----- README.md | 2 -- doc/install/installation.md | 2 +- doc/release/monthly.md | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d85c9f3fca..007e410b677 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,8 +23,8 @@ Issues and merge requests should be in English and contain appropriate language ## Helping others Please help other GitLab users when you can. -The channnels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/). -Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the irc channel. +The channels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/). +Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the IRC channel. You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day. ## Issue tracker @@ -59,7 +59,7 @@ We welcome merge requests with fixes and improvements to GitLab code, tests, and Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls). -If you are new to GitLab development (or web development in general), search for the label `easyfix` ([gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [github](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint. +If you are new to GitLab development (or web development in general), search for the label `easyfix` ([GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [GitHub](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint. To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file. @@ -99,7 +99,7 @@ If you contribute to GitLab please know that changes involve more than just code We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html). Please ensure you support the feature you contribute through all of these steps. -1. Description explaning the relevancy (see following item) +1. Description explaining the relevancy (see following item) 1. Working and clean code that is commented where needed 1. Unit and integration tests that pass on the CI server 1. Documented in the /doc directory @@ -163,7 +163,7 @@ If you add a dependency in GitLab (such as an operating system package) please c 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) 1. [Database Migrations](doc/development/migration_style_guide.md) 1. [Documentation styleguide](doc_styleguide.md) -1. Interface text should be written subjectively instead of objectively. It should be the gitlab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing). +1. Interface text should be written subjectively instead of objectively. It should be the GitLab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing). This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com). diff --git a/README.md b/README.md index 52e2d977620..c59c8593eba 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,6 @@ There are two editions of GitLab: - GitLab Community Edition (CE) is available freely under the MIT Expat license. - GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/). -Included with the GitLab Omnibus Packages is [GitLab CI](https://about.gitlab.com/gitlab-ci/) that can easily build, test and deploy code. - ## Website On [about.gitlab.com](https://about.gitlab.com/) you can find more information about: diff --git a/doc/install/installation.md b/doc/install/installation.md index 52ae30af805..a710407d4f6 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -320,7 +320,7 @@ GitLab Shell is an SSH access and repository management software developed speci **Note:** If you want to use HTTPS, see [Using HTTPS](#using-https) for the additional steps. -**Note:** Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in /etc/hosts ("127.0.0.1 hostname"). This might be necessary for example if you set up gitlab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with "Check GitLab API access: FAILED. code: 401" and pushing commits will be rejected with "[remote rejected] master -> master (hook declined)". +**Note:** Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in /etc/hosts ("127.0.0.1 hostname"). This might be necessary for example if you set up GitLab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with "Check GitLab API access: FAILED. code: 401" and pushing commits will be rejected with "[remote rejected] master -> master (hook declined)". ### Install gitlab-workhorse diff --git a/doc/release/monthly.md b/doc/release/monthly.md index c9ab87671d2..aff3f066b24 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -159,7 +159,7 @@ Please do not raise issues directly in this issue but link to issues that might The decision to create a patch release or not is with the release manager who is assigned to this issue. The release manager will comment here about the plans for patch releases. -Assign the issue to the release manager and at mention all members of gitlab core team. If there are any known bugs in the release add them immediately. +Assign the issue to the release manager and at mention all members of GitLab core team. If there are any known bugs in the release add them immediately. ## Tweet about RC1 -- cgit v1.2.1 From 91a76957e3d18e3cb89bc8320f8513e1002e551e Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 17:05:30 +0100 Subject: Update LFS docs for cloning [ci skip] --- doc/workflow/git_lfs.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/workflow/git_lfs.md b/doc/workflow/git_lfs.md index e1064051fe8..616a71522ae 100644 --- a/doc/workflow/git_lfs.md +++ b/doc/workflow/git_lfs.md @@ -55,7 +55,7 @@ In `config/gitlab.yml`: * When SSH is set as a remote, Git LFS objects still go through HTTPS * Any Git LFS request will ask for HTTPS credentials to be provided so good Git credentials store is recommended * Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) is not supported -* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the url to Git config manually (see #troubleshooting-tips) +* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the URL to Git config manually (see #troubleshooting-tips) ## Using Git LFS @@ -77,11 +77,10 @@ git commit -am "Added Debian iso" # commit the file meta data git push origin master # sync the git repo and large file to the GitLab server ``` -Downloading a single large file is also very simple: +Cloning the repository works the same as before. Git automatically detects the LFS-tracked files and clones them via HTTP. If you performed the git clone command with a SSH URL, you have to enter your GitLab credentials for HTTP authentication. ```bash git clone git@gitlab.example.com:group/project.git -git lfs fetch debian.iso # download the large file ``` -- cgit v1.2.1 From bdf4007cb7b18ed6892455d0a9adf78476188563 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 18:12:17 +0200 Subject: adressing comments --- app/assets/javascripts/awards_handler.coffee | 6 +++--- app/controllers/projects/notes_controller.rb | 12 ++++++------ app/models/note.rb | 5 +++-- app/services/notes/create_service.rb | 4 ++-- app/views/votes/_votes_block.html.haml | 14 +++++++------- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index f5b9adbe9e2..ae42e390c43 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -79,11 +79,11 @@ class @AwardsHandler postEmoji: (emoji, callback) -> - $.post @post_emoji_url, { - emoji: emoji + $.post @post_emoji_url, { note: { + note: emoji noteable_type: @noteable_type noteable_id: @noteable_id - },(data) -> + }},(data) -> if data.ok callback.call() diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 8159cc50838..263b8b8d94e 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -59,21 +59,21 @@ class Projects::NotesController < Projects::ApplicationController end def award_toggle - noteable = params[:noteable_type] == "Issue" ? Issue : MergeRequest - noteable = noteable.find(params[:noteable_id]) + noteable = note_params[:noteable_type] == "issue" ? Issue : MergeRequest + noteable = noteable.find_by!(id: note_params[:noteable_id], project: project) + data = { - noteable: noteable, author: current_user, is_award: true, - note: params[:emoji] + note: note_params[:note] } - note = project.notes.find_by(data) + note = noteable.notes.find_by(data) if note note.destroy else - project.notes.create(data) + Notes::CreateService.new(project, current_user, note_params).execute end render json: { ok: true } diff --git a/app/models/note.rb b/app/models/note.rb index 39645f8f1ad..e30be444eb5 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -40,13 +40,14 @@ class Note < ActiveRecord::Base delegate :name, :email, to: :author, prefix: true validates :note, :project, presence: true + validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award } validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true # Attachments are deprecated and are handled by Markdown uploader validates :attachment, file_size: { maximum: :max_attachment_size } validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' } validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' } - validates :author, presence: true, if: ->(n) { n.is_award } + validates :author, presence: true mount_uploader :attachment, AttachmentUploader @@ -102,7 +103,7 @@ class Note < ActiveRecord::Base end def grouped_awards - select(:note).distinct.map do |note| + awards.select(:note).distinct.map do |note| [ note.note, where(note: note.note) ] end end diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index dbff58dfb9c..25a985df4d8 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -35,11 +35,11 @@ module Notes end def contains_emoji_only?(note) - note =~ /\A:[-_+[:alnum:]]*:\s?\z/ + note =~ /\A:?[-_+[:alnum:]]*:?\s?\z/ end def emoji_name(note) - note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] + note.match(/\A:?([-_+[:alnum:]]*):?\s?/)[1] end end end diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 04e3b5a3814..7eb27c12d33 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,15 +1,15 @@ .awards.votes-block - - votable.notes.awards.grouped_awards.each do | note | - .award{class: (note_active_class(note.last, current_user)), title: emoji_author_list(note.last, current_user)} - .icon{"data-emoji" => "#{note.first}"} - = image_tag url_to_emoji(note.first), height: "20px", width: "20px" + - votable.notes.awards.grouped_awards.each do |emoji, notes| + .award{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user)} + .icon{"data-emoji" => "#{emoji}"} + = image_tag url_to_emoji(emoji), height: "20px", width: "20px" .counter - = note.last.count + = notes.count - if current_user .dropdown.awards-controls %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} - = icon('plus-square-o') + = icon('smile-o') %ul.dropdown-menu.awards-menu - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" @@ -17,7 +17,7 @@ - if current_user :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" - noteable_type = "#{votable.class}" + noteable_type = "#{votable.class.name.underscore}" noteable_id = "#{votable.id}" window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) -- cgit v1.2.1 From 888821f9ffb56c6fdf762f28dd42cf3b7226652f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 19:22:46 +0100 Subject: Add support for batch download operation --- lib/gitlab/lfs/response.rb | 105 ++++++++++++++++++++++++--------- lib/gitlab/lfs/router.rb | 2 +- spec/lib/gitlab/lfs/lfs_router_spec.rb | 25 +++++--- 3 files changed, 96 insertions(+), 36 deletions(-) diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index 4202c786466..ddadc07ebba 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -26,7 +26,7 @@ module Gitlab def render_download_object_response(oid) render_response_to_download do - if check_download_sendfile_header? && check_download_accept_header? + if check_download_sendfile_header? render_lfs_sendfile(oid) else render_not_found @@ -34,20 +34,15 @@ module Gitlab end end - def render_lfs_api_auth - render_response_to_push do - request_body = JSON.parse(@request.body.read) - return render_not_found if request_body.empty? || request_body['objects'].empty? - - response = build_response(request_body['objects']) - [ - 200, - { - "Content-Type" => "application/json; charset=utf-8", - "Cache-Control" => "private", - }, - [JSON.dump(response)] - ] + def render_batch_operation_response + request_body = JSON.parse(@request.body.read) + case request_body["operation"] + when "download" + render_batch_download(request_body) + when "upload" + render_batch_upload(request_body) + else + render_forbidden end end @@ -142,6 +137,38 @@ module Gitlab end end + def render_batch_upload(body) + return render_not_found if body.empty? || body['objects'].nil? + + render_response_to_push do + response = build_upload_batch_response(body['objects']) + [ + 200, + { + "Content-Type" => "application/json; charset=utf-8", + "Cache-Control" => "private", + }, + [JSON.dump(response)] + ] + end + end + + def render_batch_download(body) + return render_not_found if body.empty? || body['objects'].nil? + + render_response_to_download do + response = build_download_batch_response(body['objects']) + [ + 200, + { + "Content-Type" => "application/json; charset=utf-8", + "Cache-Control" => "private", + }, + [JSON.dump(response)] + ] + end + end + def render_lfs_download_hypermedia(oid) return render_not_found unless oid.present? @@ -266,10 +293,16 @@ module Gitlab @project.lfs_objects.where(oid: objects_oids).pluck(:oid).to_set end - def build_response(objects) + def build_upload_batch_response(objects) selected_objects = select_existing_objects(objects) - upload_hypermedia(objects, selected_objects) + upload_hypermedia_links(objects, selected_objects) + end + + def build_download_batch_response(objects) + selected_objects = select_existing_objects(objects) + + download_hypermedia_links(objects, selected_objects) end def download_hypermedia(oid) @@ -279,7 +312,6 @@ module Gitlab { 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{oid}", 'header' => { - 'Accept' => "application/vnd.git-lfs+json; charset=utf-8", 'Authorization' => @env['HTTP_AUTHORIZATION'] }.compact } @@ -287,21 +319,40 @@ module Gitlab } end - def upload_hypermedia(all_objects, existing_objects) + def download_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| - object['_links'] = hypermedia_links(object) unless existing_objects.include?(object['oid']) + # generate links only for existing objects + next unless existing_objects.include?(object['oid']) + + object['_links'] = { + 'download' => { + 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}", + 'header' => { + 'Authorization' => @env['HTTP_AUTHORIZATION'] + }.compact + } + } end { 'objects' => all_objects } end - def hypermedia_links(object) - { - "upload" => { - 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}", - 'header' => { 'Authorization' => @env['HTTP_AUTHORIZATION'] } - }.compact - } + def upload_hypermedia_links(all_objects, existing_objects) + all_objects.each do |object| + # generate links only for non-existing objects + next if existing_objects.include?(object['oid']) + + object['_links'] = { + 'upload' => { + 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}", + 'header' => { + 'Authorization' => @env['HTTP_AUTHORIZATION'] + }.compact + } + } + end + + { 'objects' => all_objects } end end end diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb index 4809e834984..2b3f2e8e958 100644 --- a/lib/gitlab/lfs/router.rb +++ b/lib/gitlab/lfs/router.rb @@ -48,7 +48,7 @@ module Gitlab # Check for Batch API if post_path[0].ends_with?("/info/lfs/objects/batch") - lfs.render_lfs_api_auth + lfs.render_batch_operation_response else nil end diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index cebcb5bc887..5eafaad79c9 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -66,7 +66,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth, "Accept" => "application/vnd.git-lfs+json; charset=utf-8") + expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth) end end @@ -107,7 +107,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_public_auth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8") + expect(json_response['_links']['download']['header']).to eq({}) end end @@ -117,7 +117,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_public_noauth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8") + expect(json_response['_links']['download']['header']).to eq({}) end end end @@ -191,7 +191,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8") + expect(json_response['_links']['download']['header']).to eq({}) end end @@ -219,7 +219,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") - expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8", "Authorization" => @auth) + expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth) end end @@ -250,7 +250,8 @@ describe Gitlab::Lfs::Router do body = { 'objects' => [{ 'oid' => sample_oid, 'size' => sample_size - }] + }], + 'operation' => 'upload' }.to_json env['rack.input'] = StringIO.new(body) end @@ -286,7 +287,8 @@ describe Gitlab::Lfs::Router do 'objects' => [{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', 'size' => 1575078 - }] + }], + 'operation' => 'upload' }.to_json env['rack.input'] = StringIO.new(body) end @@ -315,7 +317,8 @@ describe Gitlab::Lfs::Router do { 'oid' => sample_oid, 'size' => sample_size } - ] + ], + 'operation' => 'upload' }.to_json env['rack.input'] = StringIO.new(body) public_project.lfs_objects << lfs_object @@ -351,6 +354,12 @@ describe Gitlab::Lfs::Router do end context 'when user is not authenticated' do + before do + env['rack.input'] = StringIO.new( + { 'objects' => [], 'operation' => 'upload' }.to_json + ) + end + context 'when user has push access' do before do project.team << [user, :master] -- cgit v1.2.1 From 8248314bc9256d3a0252ad6322df098edca7385a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 20:16:56 +0100 Subject: Don't rescue Exception, but StandardError --- app/controllers/ci/lints_controller.rb | 4 ++-- app/models/ci/commit.rb | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb index 24dd1b5c93a..a4f6aff49b4 100644 --- a/app/controllers/ci/lints_controller.rb +++ b/app/controllers/ci/lints_controller.rb @@ -15,10 +15,10 @@ module Ci @builds = @config_processor.builds @status = true end - rescue Ci::GitlabCiYamlProcessor::ValidationError => e + rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e @error = e.message @status = false - rescue Exception + rescue @error = "Undefined error" @status = false end diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index b0c78499e49..971e899de84 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -190,14 +190,11 @@ module Ci def config_processor return nil unless ci_yaml_file @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) - rescue Ci::GitlabCiYamlProcessor::ValidationError => e + rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e save_yaml_error(e.message) nil - rescue Psych::SyntaxError => e - save_yaml_error(e.message) - nil - rescue Exception - save_yaml_error("Undefined yaml error") + rescue + save_yaml_error("Undefined error") nil end -- cgit v1.2.1 From 22bbb379ae976d94d7ddd7addb7384e0b79ddfd9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 23:00:30 +0200 Subject: fox tests --- spec/models/note_spec.rb | 5 ++- spec/services/notes/create_service_spec.rb | 50 +++++++++++++++--------------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 86b6c27a182..f347f537550 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -132,12 +132,11 @@ describe Note do describe :grouped_awards do before do - create :note, note: "smile" - create :note, note: "smile" + create :note, note: "smile", is_award: true + create :note, note: "smile", is_award: true end it "returns grouped array of notes" do - grouped_array = Note.grouped_awards expect(Note.grouped_awards.first.first).to eq("smile") expect(Note.grouped_awards.first.last).to match_array(Note.all) end diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index 066461f5a12..cc38d257792 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -26,36 +26,36 @@ describe Notes::CreateService do end describe "award emoji" do - before do - project.team << [user, :master] - end + before do + project.team << [user, :master] + end - it "creates emoji note" do - opts = { - note: ':smile: ', - noteable_type: 'Issue', - noteable_id: issue.id - } + it "creates emoji note" do + opts = { + note: ':smile: ', + noteable_type: 'Issue', + noteable_id: issue.id + } - @note = Notes::CreateService.new(project, user, opts).execute + @note = Notes::CreateService.new(project, user, opts).execute - expect(@note).to be_valid - expect(@note.note).to eq('smile') - expect(@note.is_award).to be_truthy - end + expect(@note).to be_valid + expect(@note.note).to eq('smile') + expect(@note.is_award).to be_truthy + end - it "creates regular note if emoji name is invalid" do - opts = { - note: ':smile: moretext: ', - noteable_type: 'Issue', - noteable_id: issue.id - } + it "creates regular note if emoji name is invalid" do + opts = { + note: ':smile: moretext: ', + noteable_type: 'Issue', + noteable_id: issue.id + } - @note = Notes::CreateService.new(project, user, opts).execute + @note = Notes::CreateService.new(project, user, opts).execute - expect(@note).to be_valid - expect(@note.note).to eq(opts[:note]) - expect(@note.is_award).to be_falsy - end + expect(@note).to be_valid + expect(@note.note).to eq(opts[:note]) + expect(@note.is_award).to be_falsy + end end end -- cgit v1.2.1 From 2a3680219bb5a4d35aa9b98ba2a2c43855aea27a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 19 Nov 2015 15:46:05 -0500 Subject: Add a fallback for Safari copy-to-clipboard Also, hide the tooltip in a less stupid way. Closes #3547 --- app/assets/javascripts/copy_to_clipboard.js.coffee | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/copy_to_clipboard.js.coffee b/app/assets/javascripts/copy_to_clipboard.js.coffee index ec4b80cca6f..9c68c5cc1bc 100644 --- a/app/assets/javascripts/copy_to_clipboard.js.coffee +++ b/app/assets/javascripts/copy_to_clipboard.js.coffee @@ -9,13 +9,24 @@ $ -> clipboard.on 'success', (e) -> $(e.trigger). tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!'). - tooltip('show') + tooltip('show'). + one('mouseleave', -> $(this).tooltip('hide')) # Clear the selection and blur the trigger so it loses its border e.clearSelection() $(e.trigger).blur() - # Manually hide the tooltip after 1 second - setTimeout(-> - $(e.trigger).tooltip('hide') - , 1000) + # Safari doesn't support `execCommand`, so instead we inform the user to + # copy manually. + # + # See http://clipboardjs.com/#browser-support + clipboard.on 'error', (e) -> + if /Mac/i.test(navigator.userAgent) + title = "Press ⌘-C to copy" + else + title = "Press Ctrl-C to copy" + + $(e.trigger). + tooltip(trigger: 'manual', placement: 'auto bottom', html: true, title: title). + tooltip('show'). + one('mouseleave', -> $(this).tooltip('hide')) -- cgit v1.2.1 From 55c2a69f5fcde26299a5a829a146808a77c56267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Thu, 19 Nov 2015 16:22:41 -0500 Subject: Update copy used in alert message when deleting branches or tags. #2993 --- app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/tags/show.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index cc0ec9483d2..3f95e2a1bf6 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -26,7 +26,7 @@ Compare - if can_remove_branch?(@project, branch.name) - = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do + = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" }, remote: true do = icon("trash-o") - if commit diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index ebe3718afcc..879c6c7d310 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -15,7 +15,7 @@ = render 'projects/tags/download', ref: @tag.name, project: @project - if can?(current_user, :admin_project, @project) .pull-right - = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'} do + = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do %i.fa.fa-trash-o .title %strong= @tag.name -- cgit v1.2.1 From 2df492fd2f5bfb6f9aa9c3f800fa4a6a39bdeeb1 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 19 Nov 2015 19:25:22 -0500 Subject: Add clipboard button to merge request cross-project reference --- app/views/projects/merge_requests/_discussion.html.haml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 7e60782ff5b..cb75bd8c5ba 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -14,8 +14,10 @@ #votes= render 'votes/votes_block', votable: @merge_request = render "projects/merge_requests/show/participants" .col-md-3 - %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'} - = cross_project_reference(@project, @merge_request) + .input-group.cross-project-reference + %span.slead.has_tooltip{title: 'Cross-project reference'} + = cross_project_reference(@project, @merge_request) + = clipboard_button .row %section.col-md-9 -- cgit v1.2.1 From 97afb84b310cfdaa9305e8b79fc00bdbb866bbca Mon Sep 17 00:00:00 2001 From: Ruben Davila Date: Thu, 22 Oct 2015 10:18:59 -0500 Subject: Generate system note after Task item has been updated on Issue or Merge Request. #2296 Everytime the User check or uncheck a Task Item from the Issue or Merge Request description, a new update is going to be added to the activity logs of the Issue or Merge Request. Note that when using the edit form, you can only update the Task item status or add/delete/modify existing ones. Doing both actions is not fully supported. --- app/models/concerns/issuable.rb | 5 ++ app/models/concerns/taskable.rb | 31 +++++++++-- app/services/issuable_base_service.rb | 17 ++++++ app/services/issues/update_service.rb | 4 -- app/services/merge_requests/update_service.rb | 4 -- app/services/system_note_service.rb | 17 ++++++ config/initializers/task_list_ext.rb | 12 ++++ spec/services/issues/update_service_spec.rb | 64 ++++++++++++++++++++-- .../services/merge_requests/update_service_spec.rb | 51 +++++++++++++++-- 9 files changed, 181 insertions(+), 24 deletions(-) create mode 100644 config/initializers/task_list_ext.rb diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 2dafb5e752f..62e8e66b1e0 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -151,4 +151,9 @@ module Issuable def notes_with_associations notes.includes(:author, :project) end + + def updated_tasks + Taskable.get_updated_tasks(old_content: previous_changes['description'].first, + new_content: description) + end end diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index 660e58b876d..3daa4dbe24e 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -7,14 +7,37 @@ require 'task_list/filter' # # Used by MergeRequest and Issue module Taskable + ITEM_PATTERN = / + ^ + (?:\s*[-+*]|(?:\d+\.))? # optional list prefix + \s* # optional whitespace prefix + (\[\s\]|\[[xX]\]) # checkbox + (\s.+) # followed by whitespace and some text. + /x + + def self.get_tasks(content) + content.to_s.scan(ITEM_PATTERN).map do |checkbox, label| + # ITEM_PATTERN strips out the hyphen, but Item requires it. Rabble rabble. + TaskList::Item.new("- #{checkbox}", label.strip) + end + end + + def self.get_updated_tasks(old_content:, new_content:) + old_tasks, new_tasks = get_tasks(old_content), get_tasks(new_content) + + new_tasks.select.with_index do |new_task, i| + old_task = old_tasks[i] + next unless old_task + + new_task.source == new_task.source && new_task.complete? != old_task.complete? + end + end + # Called by `TaskList::Summary` def task_list_items return [] if description.blank? - @task_list_items ||= description.scan(TaskList::Filter::ItemPattern).collect do |item| - # ItemPattern strips out the hyphen, but Item requires it. Rabble rabble. - TaskList::Item.new("- #{item}") - end + @task_list_items ||= Taskable.get_tasks(description) end def tasks diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 11d2b08bba7..19055fb67ff 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -27,6 +27,12 @@ class IssuableBaseService < BaseService old_branch, new_branch) end + def create_task_status_note(issuable) + issuable.updated_tasks.each do |task| + SystemNoteService.change_task_status(issuable, issuable.project, current_user, task) + end + end + def filter_params(issuable_ability_name = :issue) params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE @@ -55,6 +61,7 @@ class IssuableBaseService < BaseService old_labels - issuable.labels) end + handle_common_system_notes(issuable) handle_changes(issuable) issuable.create_new_cross_references!(current_user) execute_hooks(issuable, 'update') @@ -71,4 +78,14 @@ class IssuableBaseService < BaseService close_service.new(project, current_user, {}).execute(issuable) end end + + def handle_common_system_notes(issuable) + if issuable.previous_changes.include?('title') + create_title_change_note(issuable, issuable.previous_changes['title'].first) + end + + if issuable.previous_changes.include?('description') && issuable.tasks? + create_task_status_note(issuable) + end + end end diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index 7c112f731a7..a55a04dd5e0 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -13,10 +13,6 @@ module Issues create_assignee_note(issue) notification_service.reassigned_issue(issue, current_user) end - - if issue.previous_changes.include?('title') - create_title_change_note(issue, issue.previous_changes['title'].first) - end end def reopen_service diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index a5db3776eb6..5ff2cc03dda 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -30,10 +30,6 @@ module MergeRequests notification_service.reassigned_merge_request(merge_request, current_user) end - if merge_request.previous_changes.include?('title') - create_title_change_note(merge_request, merge_request.previous_changes['title'].first) - end - if merge_request.previous_changes.include?('target_branch') || merge_request.previous_changes.include?('source_branch') merge_request.mark_as_unchecked diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 708c2f00486..7c5d523ef39 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -341,4 +341,21 @@ class SystemNoteService "* #{commit_ids} - #{commits_text} from branch `#{branch}`\n" end + + # Called when the status of a Task has changed + # + # noteable - Noteable object. + # project - Project owning noteable + # author - User performing the change + # new_task - TaskList::Item object. + # + # Example Note text: + # + # "Soandso marked the task Whatever as completed." + # + # Returns the created Note object + def self.change_task_status(noteable, project, author, new_task) + body = "Marked the task **#{new_task.source}** as #{new_task.status_label}" + create_note(noteable: noteable, project: project, author: author, note: body) + end end diff --git a/config/initializers/task_list_ext.rb b/config/initializers/task_list_ext.rb new file mode 100644 index 00000000000..c05b683b5be --- /dev/null +++ b/config/initializers/task_list_ext.rb @@ -0,0 +1,12 @@ +require 'task_list' + +class TaskList + class Item + COMPLETED = 'completed'.freeze + INCOMPLETE = 'incomplete'.freeze + + def status_label + complete? ? COMPLETED : INCOMPLETE + end + end +end diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index f55527ee9a3..adb3aa143ae 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -15,6 +15,17 @@ describe Issues::UpdateService do end describe 'execute' do + def find_note(starting_with) + @issue.notes.find do |note| + note && note.note.start_with?(starting_with) + end + end + + def update_issue(opts) + @issue = Issues::UpdateService.new(project, user, opts).execute(issue) + @issue.reload + end + context "valid params" do before do opts = { @@ -44,12 +55,6 @@ describe Issues::UpdateService do expect(email.subject).to include(issue.title) end - def find_note(starting_with) - @issue.notes.find do |note| - note && note.note.start_with?(starting_with) - end - end - it 'should create system note about issue reassign' do note = find_note('Reassigned to') @@ -71,5 +76,52 @@ describe Issues::UpdateService do expect(note.note).to eq 'Title changed from **Old title** to **New title**' end end + + context 'when Issue has tasks' do + before { update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" }) } + + it { expect(@issue.tasks?).to eq(true) } + + context 'when tasks are marked as completed' do + before { update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) } + + it 'creates system note about task status change' do + note1 = find_note('Marked the task **Task 1** as completed') + note2 = find_note('Marked the task **Task 2** as completed') + + expect(note1).not_to be_nil + expect(note2).not_to be_nil + end + end + + context 'when tasks are marked as incomplete' do + before do + update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) + update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" }) + end + + it 'creates system note about task status change' do + note1 = find_note('Marked the task **Task 1** as incomplete') + note2 = find_note('Marked the task **Task 2** as incomplete') + + expect(note1).not_to be_nil + expect(note2).not_to be_nil + end + end + + context 'when tasks position has been modified' do + before do + update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) + update_issue({ description: "- [x] Task 1\n- [ ] Task 3\n- [ ] Task 2" }) + end + + it 'does not create a system note' do + note = find_note('Marked the task **Task 2** as incomplete') + + expect(note).to be_nil + end + end + end + end end diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 2ed51d223b7..97f5c009aec 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -14,6 +14,17 @@ describe MergeRequests::UpdateService do end describe 'execute' do + def find_note(starting_with) + @merge_request.notes.find do |note| + note && note.note.start_with?(starting_with) + end + end + + def update_merge_request(opts) + @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request) + @merge_request.reload + end + context 'valid params' do let(:opts) do { @@ -56,12 +67,6 @@ describe MergeRequests::UpdateService do expect(email.subject).to include(merge_request.title) end - def find_note(starting_with) - @merge_request.notes.find do |note| - note && note.note.start_with?(starting_with) - end - end - it 'should create system note about merge_request reassign' do note = find_note('Reassigned to') @@ -90,5 +95,39 @@ describe MergeRequests::UpdateService do expect(note.note).to eq 'Target branch changed from `master` to `target`' end end + + context 'when MergeRequest has tasks' do + before { update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) } + + it { expect(@merge_request.tasks?).to eq(true) } + + context 'when tasks are marked as completed' do + before { update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) } + + it 'creates system note about task status change' do + note1 = find_note('Marked the task **Task 1** as completed') + note2 = find_note('Marked the task **Task 2** as completed') + + expect(note1).not_to be_nil + expect(note2).not_to be_nil + end + end + + context 'when tasks are marked as incomplete' do + before do + update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) + update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) + end + + it 'creates system note about task status change' do + note1 = find_note('Marked the task **Task 1** as incomplete') + note2 = find_note('Marked the task **Task 2** as incomplete') + + expect(note1).not_to be_nil + expect(note2).not_to be_nil + end + end + end + end end -- cgit v1.2.1 From f31ee525070d335aba8a189b304e3c446aedf1fb Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 20 Nov 2015 11:13:43 +0200 Subject: Fix for Emoji --- app/assets/javascripts/awards_handler.coffee | 2 +- app/controllers/projects/notes_controller.rb | 2 +- app/helpers/issues_helper.rb | 2 ++ app/services/notes/create_service.rb | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index ae42e390c43..635c9b4f8d2 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -80,7 +80,7 @@ class @AwardsHandler postEmoji: (emoji, callback) -> $.post @post_emoji_url, { note: { - note: emoji + note: ":" + emoji + ":" noteable_type: @noteable_type noteable_id: @noteable_id }},(data) -> diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 263b8b8d94e..1e3f1d8fd2f 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -65,7 +65,7 @@ class Projects::NotesController < Projects::ApplicationController data = { author: current_user, is_award: true, - note: note_params[:note] + note: note_params[:note].gsub(":", '') } note = noteable.notes.find_by(data) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 2c791aa5682..493f370d9a9 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -90,6 +90,8 @@ module IssuesHelper def url_to_emoji(name) emoji_path = ::AwardEmoji.path_to_emoji_image(name) url_to_image(emoji_path) + rescue StandardError + "" end def emoji_author_list(notes, current_user) diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 25a985df4d8..dbff58dfb9c 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -35,11 +35,11 @@ module Notes end def contains_emoji_only?(note) - note =~ /\A:?[-_+[:alnum:]]*:?\s?\z/ + note =~ /\A:[-_+[:alnum:]]*:\s?\z/ end def emoji_name(note) - note.match(/\A:?([-_+[:alnum:]]*):?\s?/)[1] + note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] end end end -- cgit v1.2.1 From 14d95b0529eacb8f42c5184fb71bff3f326d1670 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 20 Nov 2015 11:59:32 +0100 Subject: Part of tests done [ci skip] --- lib/gitlab/lfs/response.rb | 27 ++- spec/lib/gitlab/lfs/lfs_router_spec.rb | 415 +++++++++++++++++++++++++-------- 2 files changed, 338 insertions(+), 104 deletions(-) diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index ddadc07ebba..0371e1a7107 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -42,7 +42,7 @@ module Gitlab when "upload" render_batch_upload(request_body) else - render_forbidden + render_not_found end end @@ -322,16 +322,21 @@ module Gitlab def download_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| # generate links only for existing objects - next unless existing_objects.include?(object['oid']) - - object['_links'] = { - 'download' => { - 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}", - 'header' => { - 'Authorization' => @env['HTTP_AUTHORIZATION'] - }.compact + if existing_objects.include?(object['oid']) + object['actions'] = { + 'download' => { + 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}", + 'header' => { + 'Authorization' => @env['HTTP_AUTHORIZATION'] + }.compact + } } - } + else + object['error'] = { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + end end { 'objects' => all_objects } @@ -342,7 +347,7 @@ module Gitlab # generate links only for non-existing objects next if existing_objects.include?(object['oid']) - object['_links'] = { + object['actions'] = { 'upload' => { 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}", 'header' => { diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index 5eafaad79c9..b0cf38e2253 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -238,144 +238,373 @@ describe Gitlab::Lfs::Router do end end - describe 'when initiating pushing of the lfs object' do + describe 'when handling lfs batch request' do before do enable_lfs env['REQUEST_METHOD'] = 'POST' - env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch" + env['PATH_INFO'] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch" end - describe 'when user is authenticated' do - before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'upload' - }.to_json - env['rack.input'] = StringIO.new(body) - end - - describe 'when user has project push access' do + describe 'download' do + describe 'when user is authenticated' do before do - @auth = authorize(user) - env["HTTP_AUTHORIZATION"] = @auth - project.team << [user, :master] + body = { 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size + }], + 'operation' => 'download' + }.to_json + env['rack.input'] = StringIO.new(body) end - context 'when pushing an lfs object that already exists' do + describe 'when user has download access' do before do - public_project.lfs_objects << lfs_object + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.team << [user, :reporter] + end + + context 'when downloading an lfs object that is assigned to our project' do + before do + project.lfs_objects << lfs_object + end + + it 'responds with status 200 and href to download' do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + response_body = ActiveSupport::JSON.decode(response.last.first) + + expect(response_body).to eq( + 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {'Authorization' => @auth} + } + } + }]) + end + end + + context 'when downloading an lfs object that is assigned to other project' do + before do + public_project.lfs_objects << lfs_object + end + + it 'responds with status 200 and error message' do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + response_body = ActiveSupport::JSON.decode(response.last.first) + + expect(response_body).to eq( + 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }]) + end end - it "responds with status 200 and links the object to the project" do - response_body = lfs_router_auth.try_call.last - response = ActiveSupport::JSON.decode(response_body.first) + context 'when downloading a lfs object that does not exist' do + before do + body = { + 'objects' => [{ + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }], + 'operation' => 'download' + }.to_json + env['rack.input'] = StringIO.new(body) + end + + it "responds with status 200 and error message" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + response_body = ActiveSupport::JSON.decode(response.last.first) + + expect(response_body).to eq( + 'objects' => [{ + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }]) + end + end + + context 'when downloading one new and one existing lfs object' do + before do + body = { + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ], + 'operation' => 'download' + }.to_json + env['rack.input'] = StringIO.new(body) + project.lfs_objects << lfs_object + end + + it "responds with status 200 with upload hypermedia link for the new object" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + response_body = ActiveSupport::JSON.decode(response.last.first) + + expect(response_body).to eq( + 'objects' => [{ + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }, + { + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {'Authorization' => @auth} + } + } + }]) + end + end + end + + context 'when user does is not member of the project' do + before do + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.team << [user, :guest] + end - expect(response['objects']).to be_kind_of(Array) - expect(response['objects'].first['oid']).to eq(sample_oid) - expect(response['objects'].first['size']).to eq(sample_size) - expect(lfs_object.projects.pluck(:id)).to_not include(project.id) - expect(lfs_object.projects.pluck(:id)).to include(public_project.id) - expect(response['objects'].first).to have_key('_links') + it 'responds with 403' do + expect(lfs_router_auth.try_call.first).to eq(403) end end - context 'when pushing a lfs object that does not exist' do + context 'when user does not have download access' do before do - body = { - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }], - 'operation' => 'upload' - }.to_json - env['rack.input'] = StringIO.new(body) - end - - it "responds with status 200 and upload hypermedia link" do - response = lfs_router_auth.try_call - expect(response.first).to eq(200) + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.team << [user, :guest] + end - response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body['objects']).to be_kind_of(Array) - expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") - expect(response_body['objects'].first['size']).to eq(1575078) - expect(lfs_object.projects.pluck(:id)).not_to include(project.id) - expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") - expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + it 'responds with 403' do + expect(lfs_router_auth.try_call.first).to eq(403) end end + end + + context 'when user is not authenticated' do + before do + body = { 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size + }], + 'operation' => 'download' + }.to_json + env['rack.input'] = StringIO.new(body) + end - context 'when pushing one new and one existing lfs object' do + describe 'is accessing public project' do before do - body = { - 'objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, - { 'oid' => sample_oid, - 'size' => sample_size - } - ], - 'operation' => 'upload' - }.to_json - env['rack.input'] = StringIO.new(body) public_project.lfs_objects << lfs_object end - it "responds with status 200 with upload hypermedia link for the new object" do - response = lfs_router_auth.try_call + it 'responds with status 200 and href to download' do + response = lfs_router_public_noauth.try_call expect(response.first).to eq(200) - response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body['objects']).to be_kind_of(Array) + expect(response_body).to eq( + 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {} + } + } + }]) + end + end - expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") - expect(response_body['objects'].first['size']).to eq(1575078) - expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") - expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + describe 'is accessing non-public project' do + before do + project.lfs_objects << lfs_object + end - expect(response_body['objects'].last['oid']).to eq(sample_oid) - expect(response_body['objects'].last['size']).to eq(sample_size) - expect(lfs_object.projects.pluck(:id)).to_not include(project.id) - expect(lfs_object.projects.pluck(:id)).to include(public_project.id) - expect(response_body['objects'].last).to have_key('_links') + it 'responds with authorization required' do + expect(lfs_router_noauth.try_call.first).to eq(401) end end end + end - context 'when user does not have push access' do - it 'responds with 403' do - expect(lfs_router_auth.try_call.first).to eq(403) + describe 'upload' do + describe 'when user is authenticated' do + before do + body = { 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size + }], + 'operation' => 'upload' + }.to_json + env['rack.input'] = StringIO.new(body) end - end - end - context 'when user is not authenticated' do - before do - env['rack.input'] = StringIO.new( - { 'objects' => [], 'operation' => 'upload' }.to_json - ) + describe 'when user has project push access' do + before do + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.team << [user, :master] + end + + context 'when pushing an lfs object that already exists' do + before do + public_project.lfs_objects << lfs_object + end + + it "responds with status 200 and links the object to the project" do + response_body = lfs_router_auth.try_call.last + response = ActiveSupport::JSON.decode(response_body.first) + + expect(response['objects']).to be_kind_of(Array) + expect(response['objects'].first['oid']).to eq(sample_oid) + expect(response['objects'].first['size']).to eq(sample_size) + expect(lfs_object.projects.pluck(:id)).to_not include(project.id) + expect(lfs_object.projects.pluck(:id)).to include(public_project.id) + expect(response['objects'].first).to have_key('_links') + end + end + + context 'when pushing a lfs object that does not exist' do + before do + body = { + 'objects' => [{ + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }], + 'operation' => 'upload' + }.to_json + env['rack.input'] = StringIO.new(body) + end + + it "responds with status 200 and upload hypermedia link" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + + response_body = ActiveSupport::JSON.decode(response.last.first) + expect(response_body['objects']).to be_kind_of(Array) + expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") + expect(response_body['objects'].first['size']).to eq(1575078) + expect(lfs_object.projects.pluck(:id)).not_to include(project.id) + expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + end + end + + context 'when pushing one new and one existing lfs object' do + before do + body = { + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ], + 'operation' => 'upload' + }.to_json + env['rack.input'] = StringIO.new(body) + public_project.lfs_objects << lfs_object + end + + it "responds with status 200 with upload hypermedia link for the new object" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + + response_body = ActiveSupport::JSON.decode(response.last.first) + expect(response_body['objects']).to be_kind_of(Array) + + + expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") + expect(response_body['objects'].first['size']).to eq(1575078) + expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + + expect(response_body['objects'].last['oid']).to eq(sample_oid) + expect(response_body['objects'].last['size']).to eq(sample_size) + expect(lfs_object.projects.pluck(:id)).to_not include(project.id) + expect(lfs_object.projects.pluck(:id)).to include(public_project.id) + expect(response_body['objects'].last).to have_key('_links') + end + end + end + + context 'when user does not have push access' do + it 'responds with 403' do + expect(lfs_router_auth.try_call.first).to eq(403) + end + end end - context 'when user has push access' do + context 'when user is not authenticated' do before do - project.team << [user, :master] + env['rack.input'] = StringIO.new( + { 'objects' => [], 'operation' => 'upload' }.to_json + ) end - it "responds with status 401" do - expect(lfs_router_public_noauth.try_call.first).to eq(401) + context 'when user has push access' do + before do + project.team << [user, :master] + end + + it "responds with status 401" do + expect(lfs_router_public_noauth.try_call.first).to eq(401) + end end - end - context 'when user does not have push access' do - it "responds with status 401" do - expect(lfs_router_public_noauth.try_call.first).to eq(401) + context 'when user does not have push access' do + it "responds with status 401" do + expect(lfs_router_public_noauth.try_call.first).to eq(401) + end end end end + + describe 'unsupported' do + before do + body = { 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size + }], + 'operation' => 'other' + }.to_json + env['rack.input'] = StringIO.new(body) + end + + it 'responds with status 404' do + expect(lfs_router_public_noauth.try_call.first).to eq(404) + end + end end describe 'when pushing a lfs object' do -- cgit v1.2.1 From b8d8292bef996a5d074b21a82d4faec0edb2e2bf Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 20 Nov 2015 12:21:41 +0100 Subject: Fix upload tests, reformat code and make rubocop happy --- lib/gitlab/lfs/response.rb | 3 +- spec/lib/gitlab/lfs/lfs_router_spec.rb | 229 ++++++++++++++++----------------- 2 files changed, 110 insertions(+), 122 deletions(-) diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index 0371e1a7107..ada518f9e04 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -321,7 +321,6 @@ module Gitlab def download_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| - # generate links only for existing objects if existing_objects.include?(object['oid']) object['actions'] = { 'download' => { @@ -344,7 +343,7 @@ module Gitlab def upload_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| - # generate links only for non-existing objects + # generate actions only for non-existing objects next if existing_objects.include?(object['oid']) object['actions'] = { diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index b0cf38e2253..f898b76eb5f 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -248,11 +248,11 @@ describe Gitlab::Lfs::Router do describe 'download' do describe 'when user is authenticated' do before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'download' + body = { 'operation' => 'download', + 'objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size + }] }.to_json env['rack.input'] = StringIO.new(body) end @@ -274,17 +274,16 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => sample_oid, + expect(response_body).to eq('objects' => [ + { 'oid' => sample_oid, 'size' => sample_size, 'actions' => { 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => {'Authorization' => @auth} + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => { 'Authorization' => @auth } + } } - } - }]) + }]) end end @@ -298,26 +297,24 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", - } - }]) + expect(response_body).to eq('objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }]) end end context 'when downloading a lfs object that does not exist' do before do - body = { - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }], - 'operation' => 'download' + body = { 'operation' => 'download', + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }] }.to_json env['rack.input'] = StringIO.new(body) end @@ -327,30 +324,28 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", - } - }]) + expect(response_body).to eq('objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }]) end end context 'when downloading one new and one existing lfs object' do before do - body = { - 'objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, - { 'oid' => sample_oid, - 'size' => sample_size - } - ], - 'operation' => 'download' + body = { 'operation' => 'download', + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ] }.to_json env['rack.input'] = StringIO.new(body) project.lfs_objects << lfs_object @@ -361,25 +356,23 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", - } - }, - { - 'oid' => sample_oid, - 'size' => sample_size, - 'actions' => { - 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => {'Authorization' => @auth} - } - } - }]) + expect(response_body).to eq('objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }, + { 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => { 'Authorization' => @auth } + } + } + }]) end end end @@ -411,11 +404,12 @@ describe Gitlab::Lfs::Router do context 'when user is not authenticated' do before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'download' + body = { 'operation' => 'download', + 'objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size + }], + }.to_json env['rack.input'] = StringIO.new(body) end @@ -430,17 +424,16 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size, - 'actions' => { - 'download' => { - 'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => {} - } - } - }]) + expect(response_body).to eq('objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {} + } + } + }]) end end @@ -459,12 +452,12 @@ describe Gitlab::Lfs::Router do describe 'upload' do describe 'when user is authenticated' do before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'upload' - }.to_json + body = { 'operation' => 'upload', + 'objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size + }] + }.to_json env['rack.input'] = StringIO.new(body) end @@ -472,7 +465,7 @@ describe Gitlab::Lfs::Router do before do @auth = authorize(user) env["HTTP_AUTHORIZATION"] = @auth - project.team << [user, :master] + project.team << [user, :developer] end context 'when pushing an lfs object that already exists' do @@ -489,19 +482,19 @@ describe Gitlab::Lfs::Router do expect(response['objects'].first['size']).to eq(sample_size) expect(lfs_object.projects.pluck(:id)).to_not include(project.id) expect(lfs_object.projects.pluck(:id)).to include(public_project.id) - expect(response['objects'].first).to have_key('_links') + expect(response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}") + expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth) end end context 'when pushing a lfs object that does not exist' do before do - body = { - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }], - 'operation' => 'upload' - }.to_json + body = { 'operation' => 'upload', + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }] + }.to_json env['rack.input'] = StringIO.new(body) end @@ -514,26 +507,25 @@ describe Gitlab::Lfs::Router do expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(response_body['objects'].first['size']).to eq(1575078) expect(lfs_object.projects.pluck(:id)).not_to include(project.id) - expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") - expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth) end end context 'when pushing one new and one existing lfs object' do before do - body = { - 'objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, - { 'oid' => sample_oid, - 'size' => sample_size - } - ], - 'operation' => 'upload' + body = { 'operation' => 'upload', + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ] }.to_json env['rack.input'] = StringIO.new(body) - public_project.lfs_objects << lfs_object + project.lfs_objects << lfs_object end it "responds with status 200 with upload hypermedia link for the new object" do @@ -543,17 +535,14 @@ describe Gitlab::Lfs::Router do response_body = ActiveSupport::JSON.decode(response.last.first) expect(response_body['objects']).to be_kind_of(Array) - expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(response_body['objects'].first['size']).to eq(1575078) - expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") - expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['actions']['upload']['header']).to eq("Authorization" => @auth) expect(response_body['objects'].last['oid']).to eq(sample_oid) expect(response_body['objects'].last['size']).to eq(sample_size) - expect(lfs_object.projects.pluck(:id)).to_not include(project.id) - expect(lfs_object.projects.pluck(:id)).to include(public_project.id) - expect(response_body['objects'].last).to have_key('_links') + expect(response_body['objects'].last).to_not have_key('actions') end end end @@ -592,11 +581,11 @@ describe Gitlab::Lfs::Router do describe 'unsupported' do before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'other' + body = { 'operation' => 'other', + 'objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size + }] }.to_json env['rack.input'] = StringIO.new(body) end -- cgit v1.2.1 From dbc0be1b275e92af5432cf7c5cf44de3dc8c9ca5 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Fri, 20 Nov 2015 12:45:30 +0100 Subject: Error 501 when client is using deprecated API. --- lib/gitlab/lfs/response.rb | 60 +++-------- lib/gitlab/lfs/router.rb | 4 +- spec/lib/gitlab/lfs/lfs_router_spec.rb | 180 +++++++-------------------------- 3 files changed, 49 insertions(+), 195 deletions(-) diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index ada518f9e04..aceef53ba60 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -10,20 +10,6 @@ module Gitlab @request = request end - # Return a response for a download request - # Can be a response to: - # Request from a user to get the file - # Request from gitlab-workhorse which file to serve to the user - def render_download_hypermedia_response(oid) - render_response_to_download do - if check_download_accept_header? - render_lfs_download_hypermedia(oid) - else - render_not_found - end - end - end - def render_download_object_response(oid) render_response_to_download do if check_download_sendfile_header? @@ -66,13 +52,24 @@ module Gitlab end end + def render_unsupported_deprecated_api + [ + 501, + { "Content-Type" => "application/json; charset=utf-8" }, + [JSON.dump({ + 'message' => 'Server supports batch API only, please update your Git LFS client to version 0.6.0 and up.', + 'documentation_url' => "#{Gitlab.config.gitlab.url}/help", + })] + ] + end + private def render_not_enabled [ 501, { - "Content-Type" => "application/vnd.git-lfs+json", + "Content-Type" => "application/json; charset=utf-8", }, [JSON.dump({ 'message' => 'Git LFS is not enabled on this GitLab server, contact your admin.', @@ -169,21 +166,6 @@ module Gitlab end end - def render_lfs_download_hypermedia(oid) - return render_not_found unless oid.present? - - lfs_object = object_for_download(oid) - if lfs_object - [ - 200, - { "Content-Type" => "application/vnd.git-lfs+json" }, - [JSON.dump(download_hypermedia(oid))] - ] - else - render_not_found - end - end - def render_lfs_upload_ok(oid, size, tmp_file) if store_file(oid, size, tmp_file) [ @@ -226,10 +208,6 @@ module Gitlab @env['HTTP_X_SENDFILE_TYPE'].to_s == "X-Sendfile" end - def check_download_accept_header? - @env['HTTP_ACCEPT'].to_s == "application/vnd.git-lfs+json; charset=utf-8" - end - def user_can_fetch? # Check user access against the project they used to initiate the pull @user.can?(:download_code, @origin_project) @@ -305,20 +283,6 @@ module Gitlab download_hypermedia_links(objects, selected_objects) end - def download_hypermedia(oid) - { - '_links' => { - 'download' => - { - 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{oid}", - 'header' => { - 'Authorization' => @env['HTTP_AUTHORIZATION'] - }.compact - } - } - } - end - def download_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| if existing_objects.include?(object['oid']) diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb index 2b3f2e8e958..78d02891102 100644 --- a/lib/gitlab/lfs/router.rb +++ b/lib/gitlab/lfs/router.rb @@ -34,7 +34,7 @@ module Gitlab case path_match[1] when "info/lfs" - lfs.render_download_hypermedia_response(oid) + lfs.render_unsupported_deprecated_api when "gitlab-lfs" lfs.render_download_object_response(oid) else @@ -49,6 +49,8 @@ module Gitlab # Check for Batch API if post_path[0].ends_with?("/info/lfs/objects/batch") lfs.render_batch_operation_response + elsif post_path[0].ends_with?("/info/lfs/objects") + lfs.render_unsupported_deprecated_api else nil end diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index f898b76eb5f..92598559aea 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -26,113 +26,52 @@ describe Gitlab::Lfs::Router do let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" } let(:sample_size) { 499013 } + let(:respond_with_deprecated) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 0.6.0 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} + let(:respond_with_disabled) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} describe 'when lfs is disabled' do before do allow(Gitlab.config.lfs).to receive(:enabled).and_return(false) - env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}" + env['REQUEST_METHOD'] = 'POST' + body = { + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ], + 'operation' => 'upload' + }.to_json + env['rack.input'] = StringIO.new(body) + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch" end it 'responds with 501' do - respond_with_disabled = [ 501, - { "Content-Type"=>"application/vnd.git-lfs+json" }, - ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"] - ] expect(lfs_router_auth.try_call).to match_array(respond_with_disabled) end end - describe 'when fetching lfs object' do + describe 'when fetching lfs object using deprecated API' do before do enable_lfs - env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8" env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}" end - describe 'when user is authenticated' do - context 'and user has project download access' do - before do - @auth = authorize(user) - env["HTTP_AUTHORIZATION"] = @auth - project.lfs_objects << lfs_object - project.team << [user, :master] - end - - it "responds with status 200" do - expect(lfs_router_auth.try_call.first).to eq(200) - end - - it "responds with download hypermedia" do - json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first) - - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth) - end - end - - context 'and user does not have project access' do - it "responds with status 403" do - expect(lfs_router_auth.try_call.first).to eq(403) - end - end - end - - describe 'when user is unauthenticated' do - context 'and user does not have download access' do - it "responds with status 401" do - expect(lfs_router_noauth.try_call.first).to eq(401) - end - end - - context 'and user has download access' do - before do - project.team << [user, :master] - end - - it "responds with status 401" do - expect(lfs_router_noauth.try_call.first).to eq(401) - end - end + it 'responds with 501' do + expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated) end + end - describe 'and project is public' do - context 'and project has access to the lfs object' do - before do - public_project.lfs_objects << lfs_object - end - - context 'and user is authenticated' do - it "responds with status 200 and sends download hypermedia" do - expect(lfs_router_public_auth.try_call.first).to eq(200) - json_response = ActiveSupport::JSON.decode(lfs_router_public_auth.try_call.last.first) - - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq({}) - end - end - - context 'and user is unauthenticated' do - it "responds with status 200 and sends download hypermedia" do - expect(lfs_router_public_noauth.try_call.first).to eq(200) - json_response = ActiveSupport::JSON.decode(lfs_router_public_noauth.try_call.last.first) - - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq({}) - end - end - end - - context 'and project does not have access to the lfs object' do - it "responds with status 404" do - expect(lfs_router_public_auth.try_call.first).to eq(404) - end - end + describe 'when fetching lfs object' do + before do + enable_lfs + env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8" + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}" end describe 'and request comes from gitlab-workhorse' do - before do - env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}" - end context 'without user being authorized' do it "responds with status 401" do expect(lfs_router_noauth.try_call.first).to eq(401) @@ -173,68 +112,17 @@ describe Gitlab::Lfs::Router do end end end + end - describe 'from a forked public project' do - before do - env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8" - env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}" - end - - context "when fetching a lfs object" do - context "and user has project download access" do - before do - public_project.lfs_objects << lfs_object - end - - it "can download the lfs object" do - expect(lfs_router_forked_auth.try_call.first).to eq(200) - json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) - - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq({}) - end - end - - context "and user is not authenticated but project is public" do - before do - public_project.lfs_objects << lfs_object - end - - it "can download the lfs object" do - expect(lfs_router_forked_auth.try_call.first).to eq(200) - end - end - - context "and user has project download access" do - before do - env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897" - @auth = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) - env["HTTP_AUTHORIZATION"] = @auth - lfs_object_two = create(:lfs_object, :with_file, oid: "91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", size: 1575078) - public_project.lfs_objects << lfs_object_two - end - - it "can get a lfs object that is not in the forked project" do - expect(lfs_router_forked_auth.try_call.first).to eq(200) - - json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") - expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth) - end - end - - context "and user has project download access" do - before do - env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/267c8b1d876743971e3a9978405818ff5ca731c4c870b06507619cd9b1847b6b" - lfs_object_three = create(:lfs_object, :with_file, oid: "267c8b1d876743971e3a9978405818ff5ca731c4c870b06507619cd9b1847b6b", size: 127192524) - project.lfs_objects << lfs_object_three - end + describe 'when handling lfs request using deprecated API' do + before do + enable_lfs + env['REQUEST_METHOD'] = 'POST' + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects" + end - it "cannot get a lfs object that is not in the project" do - expect(lfs_router_forked_auth.try_call.first).to eq(404) - end - end - end + it 'responds with 501' do + expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated) end end -- cgit v1.2.1 From 5a4c56c38dd7aef414582edb880b343bf67b65b8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 19 Nov 2015 14:49:35 +0100 Subject: Reduce method complexity in AutocompleteController --- app/controllers/autocomplete_controller.rb | 49 +++++++++++------------- spec/controllers/autocomplete_controller_spec.rb | 9 +++-- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 202e9da9eee..aa0268b8d62 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -1,34 +1,8 @@ class AutocompleteController < ApplicationController skip_before_action :authenticate_user!, only: [:users] + before_action :find_users, only: [:users] def users - begin - @users = - if params[:project_id].present? - project = Project.find(params[:project_id]) - - if can?(current_user, :read_project, project) - project.team.users - end - elsif params[:group_id] - group = Group.find(params[:group_id]) - - if can?(current_user, :read_group, group) - group.users - end - elsif current_user - User.all - end - rescue ActiveRecord::RecordNotFound - if current_user - return render json: {}, status: 404 - end - end - - if @users.nil? && current_user.nil? - authenticate_user! - end - @users ||= User.none @users = @users.search(params[:search]) if params[:search].present? @users = @users.active @@ -49,4 +23,25 @@ class AutocompleteController < ApplicationController @user = User.find(params[:id]) render json: @user, only: [:name, :username, :id], methods: [:avatar_url] end + + private + + def find_users + @users = + if params[:project_id].present? + project = Project.find(params[:project_id]) + return render_404 unless can?(current_user, :read_project, project) + + project.team.users + elsif params[:group_id].present? + group = Group.find(params[:group_id]) + return render_404 unless can?(current_user, :read_group, group) + + group.users + elsif current_user + User.all + else + User.none + end + end end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index aa8d6cb807f..85379a8e984 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -114,7 +114,7 @@ describe AutocompleteController do get(:users, project_id: project.id) end - it { expect(response.status).to eq(302) } + it { expect(response.status).to eq(404) } end describe 'GET #users with unknown project' do @@ -122,7 +122,7 @@ describe AutocompleteController do get(:users, project_id: 'unknown') end - it { expect(response.status).to eq(302) } + it { expect(response.status).to eq(404) } end describe 'GET #users with inaccessible group' do @@ -131,7 +131,7 @@ describe AutocompleteController do get(:users, group_id: user.namespace.id) end - it { expect(response.status).to eq(302) } + it { expect(response.status).to eq(404) } end describe 'GET #users with no project' do @@ -139,7 +139,8 @@ describe AutocompleteController do get(:users) end - it { expect(response.status).to eq(302) } + it { expect(body).to be_kind_of(Array) } + it { expect(body.size).to eq 0 } end end end -- cgit v1.2.1 From fed059a12ddde628a7d19d008c199da00990ce19 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 20 Nov 2015 15:48:05 +0100 Subject: Port GitLab EE ProjectsFinder changes These changes were added in GitLab EE commit d39de0ea91b26b8840195e5674b92c353cc16661. The tests were a bit bugged (they used a non existing group, thus not testing a crucial part) which I only noticed when porting CE changes to EE. --- app/finders/projects_finder.rb | 10 +++++----- spec/finders/projects_finder_spec.rb | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index dd35c215c50..3b4e0362e04 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -23,17 +23,17 @@ class ProjectsFinder group = options[:group] if group - base, extra = group_projects(current_user, group) + segments = group_projects(current_user, group) else - base, extra = all_projects(current_user) + segments = all_projects(current_user) end - if base and extra - union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) + if segments.length > 1 + union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) }) Project.where("projects.id IN (#{union.to_sql})") else - base + segments.first end end diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index d1dede78f74..f32641ef0f6 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -3,10 +3,19 @@ require 'spec_helper' describe ProjectsFinder do describe '#execute' do let(:user) { create(:user) } + let(:group) { create(:group) } - let!(:private_project) { create(:project, :private) } - let!(:internal_project) { create(:project, :internal) } - let!(:public_project) { create(:project, :public) } + let!(:private_project) do + create(:project, :private, name: 'A', path: 'A') + end + + let!(:internal_project) do + create(:project, :internal, group: group, name: 'B', path: 'B') + end + + let!(:public_project) do + create(:project, :public, group: group, name: 'C', path: 'C') + end let(:finder) { described_class.new } @@ -38,8 +47,6 @@ describe ProjectsFinder do end describe 'with a group' do - let(:group) { public_project.group } - describe 'without a user' do subject { finder.execute(nil, group: group) } -- cgit v1.2.1 From 899807f604f7923cc40a64cdd0f61c4248b8870d Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Fri, 20 Nov 2015 16:10:08 +0100 Subject: Update required version of lfs client and separate the docs for users and admins. --- doc/README.md | 1 + doc/workflow/README.md | 1 + doc/workflow/git_lfs.md | 135 --------------------- doc/workflow/lfs/lfs_administration.md | 41 +++++++ .../lfs/manage_large_binaries_with_git_lfs.md | 125 +++++++++++++++++++ lib/gitlab/lfs/response.rb | 2 +- spec/lib/gitlab/lfs/lfs_router_spec.rb | 2 +- 7 files changed, 170 insertions(+), 137 deletions(-) delete mode 100644 doc/workflow/git_lfs.md create mode 100644 doc/workflow/lfs/lfs_administration.md create mode 100644 doc/workflow/lfs/manage_large_binaries_with_git_lfs.md diff --git a/doc/README.md b/doc/README.md index 0f6866475f7..58ab5dd08e0 100644 --- a/doc/README.md +++ b/doc/README.md @@ -50,6 +50,7 @@ - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Reply by email](incoming_email/README.md) Allow users to comment on issues and merge requests by replying to notification emails. - [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE. +- [Git LFS configuration](workflow/lfs/lfs_administration.md) ## Contributor documentation diff --git a/doc/workflow/README.md b/doc/workflow/README.md index c1a4f64981f..a6b4d951188 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -17,3 +17,4 @@ - [Milestones](milestones.md) - [Merge Requests](merge_requests.md) - ["Work In Progress" Merge Requests](wip_merge_requests.md) +- [Manage large binaries with Git LFS](lfs/manage_large_binaries_with_git_lfs.md) diff --git a/doc/workflow/git_lfs.md b/doc/workflow/git_lfs.md deleted file mode 100644 index 616a71522ae..00000000000 --- a/doc/workflow/git_lfs.md +++ /dev/null @@ -1,135 +0,0 @@ -# Git LFS - -Managing large files such as audio, video and graphics files has always been one of the shortcomings of Git. -The general recommendation is to not have Git repositories larger than 1GB to preserve performance. - -GitLab already supports [managing large files with git annex](http://doc.gitlab.com/ee/workflow/git_annex.html) (EE only), however in certain -environments it is not always convenient to use different commands to differentiate between the large files and regular ones. - -Git LFS makes this simpler for the end user by removing the requirement to learn new commands. - - -## How it works - -Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication to authorize client requests. -Once the request is authorized, Git LFS client receives instructions from where to fetch or where to push the large file. - -## Requirements - -* Git LFS is supported in GitLab starting with version 8.2 -* Git LFS [client](https://git-lfs.github.com) version 0.6.0 and up - -## GitLab and Git LFS - -### Configuration - -Git LFS objects can be large in size. By default, they are stored on the server GitLab is installed on. - -There are two configuration options to help GitLab server administrators: - -* Enabling/disabling Git LFS support -* Changing the location of LFS object storage - -#### Omnibus packages - -In `/etc/gitlab/gitlab.rb`: - -```ruby -gitlab_rails['lfs_enabled'] = false -gitlab_rails['lfs_storage_path'] = "/mnt/storage/lfs-objects" -``` - -#### Installations from source - -In `config/gitlab.yml`: - -```yaml - lfs: - enabled: false - storage_path: /mnt/storage/lfs-objects -``` - -## Known limitations - -* Git LFS v1 original API is not supported since it was deprecated early in LFS development, starting with Git LFS version 0.6.0 -* When SSH is set as a remote, Git LFS objects still go through HTTPS -* Any Git LFS request will ask for HTTPS credentials to be provided so good Git credentials store is recommended -* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) is not supported -* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the URL to Git config manually (see #troubleshooting-tips) - -## Using Git LFS - -Lets take a look at the workflow when you need to check large files into your Git repository with Git LFS: -For example, if you want to upload a very large file and check it into your Git repository: - -```bash -git clone git@gitlab.example.com:group/project.git -git lfs init # initialize the Git LFS project project -git lfs track "*.iso" # select the file extensions that you want to treat as large files -``` - -Once a certain file extension is marked for tracking as a LFS object you can use Git as usual without having to redo the command to track a file with the same extension: - -```bash -cp ~/tmp/debian.iso ./ # copy a large file into the current directory -git add . # add the large file to the project -git commit -am "Added Debian iso" # commit the file meta data -git push origin master # sync the git repo and large file to the GitLab server -``` - -Cloning the repository works the same as before. Git automatically detects the LFS-tracked files and clones them via HTTP. If you performed the git clone command with a SSH URL, you have to enter your GitLab credentials for HTTP authentication. - -```bash -git clone git@gitlab.example.com:group/project.git -``` - - -## Troubleshooting - -### error: Repository or object not found - -There are a couple of reasons why this error can occur: - -* Wrong version of LFS client used: - -Check the version of Git LFS on the client machine with `git lfs version`. Only version 0.6.0 and newer are supported. - -* Project is using deprecated LFS API - -Check the Git config of the project for traces of deprecated API with `git lfs -l`. If `batch = false` is set in the config, remove the line and try using Git LFS client newer than 0.6.0. - -### Invalid status for : 501 - -When attempting to push a LFS object to a GitLab server that doesn't have Git LFS support enabled, server will return status `error 501`. Check with your GitLab administrator why Git LFS is not enabled on the server. See [Configuration section](#configuration) for instructions on how to enable LFS support. - -### getsockopt: connection refused - -If you push a LFS object to a project and you receive an error similar to: `Post /info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`, -the LFS client is trying to reach GitLab through HTTPS. However, your GitLab instance is being served on HTTP. - -This behaviour is caused by Git LFS using HTTPS connections by default when a `lfsurl` is not set in the Git config. - -To prevent this from happening, set the lfs url in project Git config: - -```bash - -git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs/objects/batch" -``` - -### Credentials are always required when pushing an object - -Given that Git LFS uses HTTP Basic Authentication to authenticate the user pushing the LFS object on every push for every object, user HTTPS credentials are required. - -By default, Git has support for remembering the credentials for each repository you use. This is described in [Git credentials man pages](https://git-scm.com/docs/gitcredentials). - -For example, you can tell Git to remember the password for a period of time in which you expect to push the objects: - -```bash -git config --global credential.helper 'cache --timeout=3600' -``` - -This will remember the credentials for an hour after which Git operations will require re-authentication. - -If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, `wincred` is available. - -More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage) \ No newline at end of file diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md new file mode 100644 index 00000000000..9fa307a9d5b --- /dev/null +++ b/doc/workflow/lfs/lfs_administration.md @@ -0,0 +1,41 @@ +## GitLab Git LFS Administration + +Documentation on how to use Git LFS are under [Managing large binary files with Git LFS doc](manage_large_binaries_with_git_lfs.md). + +## Requirements + +* Git LFS is supported in GitLab starting with version 8.2. +* Users need to install [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up + +## Configuration + +Git LFS objects can be large in size. By default, they are stored on the server GitLab is installed on. + +There are two configuration options to help GitLab server administrators: + +* Enabling/disabling Git LFS support +* Changing the location of LFS object storage + +### Omnibus packages + +In `/etc/gitlab/gitlab.rb`: + +```ruby +gitlab_rails['lfs_enabled'] = false +gitlab_rails['lfs_storage_path'] = "/mnt/storage/lfs-objects" +``` + +### Installations from source + +In `config/gitlab.yml`: + +```yaml + lfs: + enabled: false + storage_path: /mnt/storage/lfs-objects +``` + +## Known limitations + +* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) is not supported +* Currently, removing LFS objects from GitLab Git LFS storage is not supported diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md new file mode 100644 index 00000000000..a93fd3dfb13 --- /dev/null +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -0,0 +1,125 @@ +# Git LFS + +Managing large files such as audio, video and graphics files has always been one of the shortcomings of Git. +The general recommendation is to not have Git repositories larger than 1GB to preserve performance. + +GitLab already supports [managing large files with git annex](http://doc.gitlab.com/ee/workflow/git_annex.html) (EE only), however in certain +environments it is not always convenient to use different commands to differentiate between the large files and regular ones. + +Git LFS makes this simpler for the end user by removing the requirement to learn new commands. + +## How it works + +Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication to authorize client requests. +Once the request is authorized, Git LFS client receives instructions from where to fetch or where to push the large file. + +## GitLab server configuration + +Documentation for GitLab instance administrators is under [LFS administration doc](lfs_administration.md). + +## Requirements + +* Git LFS is supported in GitLab starting with version 8.2 +* [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up + +## Known limitations + +* Git LFS v1 original API is not supported since it was deprecated early in LFS development +* When SSH is set as a remote, Git LFS objects still go through HTTPS +* Any Git LFS request will ask for HTTPS credentials to be provided so good Git credentials store is recommended +* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the URL to Git config manually (see #troubleshooting) + +## Using Git LFS + +Lets take a look at the workflow when you need to check large files into your Git repository with Git LFS: +For example, if you want to upload a very large file and check it into your Git repository: + +```bash +git clone git@gitlab.example.com:group/project.git +git lfs init # initialize the Git LFS project project +git lfs track "*.iso" # select the file extensions that you want to treat as large files +``` + +Once a certain file extension is marked for tracking as a LFS object you can use Git as usual without having to redo the command to track a file with the same extension: + +```bash +cp ~/tmp/debian.iso ./ # copy a large file into the current directory +git add . # add the large file to the project +git commit -am "Added Debian iso" # commit the file meta data +git push origin master # sync the git repo and large file to the GitLab server +``` + +Cloning the repository works the same as before. Git automatically detects the LFS-tracked files and clones them via HTTP. If you performed the git clone command with a SSH URL, you have to enter your GitLab credentials for HTTP authentication. + +```bash +git clone git@gitlab.example.com:group/project.git +``` + +If you already cloned the repository and you want to get the latest LFS object that are on the remote repository, eg. from branch `master`: + +```bash +git lfs fetch master +``` + +## Troubleshooting + +### error: Repository or object not found + +There are a couple of reasons why this error can occur: + +* You don't have permissions to access certain LFS object + +Check if you have permissions to push to the project or fetch from the project. + +* Project is not allowed to access the LFS object + +Check if the LFS object you are trying to push to the project or fetch from the project is available to the project. + +* Project is using deprecated LFS API + +### Invalid status for : 501 + +Git LFS will log the failures into a log file. +To view this log file, while in project directory: + +```bash +git lfs logs last +``` + +If the status `error 501` is shown, it is because: + +* Git LFS support is not enabled on the GitLab server. Check with your GitLab administrator why Git LFS is not enabled on the server. See [LFS administration documentation](lfs_administration.md) for instructions on how to enable LFS support. + +* Git LFS client version is not supported by GitLab server. Check your Git LFS version with `git lfs version`. Check the Git config of the project for traces of deprecated API with `git lfs -l`. If `batch = false` is set in the config, remove the line and try to update your Git LFS client. Only version 1.0.1 and newer are supported. + +### getsockopt: connection refused + +If you push a LFS object to a project and you receive an error similar to: `Post /info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`, +the LFS client is trying to reach GitLab through HTTPS. However, your GitLab instance is being served on HTTP. + +This behaviour is caused by Git LFS using HTTPS connections by default when a `lfsurl` is not set in the Git config. + +To prevent this from happening, set the lfs url in project Git config: + +```bash + +git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs/objects/batch" +``` + +### Credentials are always required when pushing an object + +Given that Git LFS uses HTTP Basic Authentication to authenticate the user pushing the LFS object on every push for every object, user HTTPS credentials are required. + +By default, Git has support for remembering the credentials for each repository you use. This is described in [Git credentials man pages](https://git-scm.com/docs/gitcredentials). + +For example, you can tell Git to remember the password for a period of time in which you expect to push the objects: + +```bash +git config --global credential.helper 'cache --timeout=3600' +``` + +This will remember the credentials for an hour after which Git operations will require re-authentication. + +If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, `wincred` is available. + +More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index aceef53ba60..c18dfbd485d 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -57,7 +57,7 @@ module Gitlab 501, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.dump({ - 'message' => 'Server supports batch API only, please update your Git LFS client to version 0.6.0 and up.', + 'message' => 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.', 'documentation_url' => "#{Gitlab.config.gitlab.url}/help", })] ] diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index 92598559aea..5b13d65c7c0 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -26,7 +26,7 @@ describe Gitlab::Lfs::Router do let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" } let(:sample_size) { 499013 } - let(:respond_with_deprecated) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 0.6.0 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} + let(:respond_with_deprecated) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} let(:respond_with_disabled) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} describe 'when lfs is disabled' do -- cgit v1.2.1 From fc18e96db38f5d5ab5e102fe630682f6779203ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 10:36:12 -0500 Subject: Refactor creation of system notes for Issue/MR labels. #2296 --- app/services/issuable_base_service.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 19055fb67ff..2556f06e2d3 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -53,15 +53,7 @@ class IssuableBaseService < BaseService if params.present? && issuable.update_attributes(params.merge(updated_by: current_user)) issuable.reset_events_cache - - if issuable.labels != old_labels - create_labels_note( - issuable, - issuable.labels - old_labels, - old_labels - issuable.labels) - end - - handle_common_system_notes(issuable) + handle_common_system_notes(issuable, old_labels: old_labels) handle_changes(issuable) issuable.create_new_cross_references!(current_user) execute_hooks(issuable, 'update') @@ -79,7 +71,7 @@ class IssuableBaseService < BaseService end end - def handle_common_system_notes(issuable) + def handle_common_system_notes(issuable, options = {}) if issuable.previous_changes.include?('title') create_title_change_note(issuable, issuable.previous_changes['title'].first) end @@ -87,5 +79,10 @@ class IssuableBaseService < BaseService if issuable.previous_changes.include?('description') && issuable.tasks? create_task_status_note(issuable) end + + old_labels = options[:old_labels] + if old_labels && (issuable.labels != old_labels) + create_labels_note(issuable, issuable.labels - old_labels, old_labels - issuable.labels) + end end end -- cgit v1.2.1 From fa9f2dec0e07ff3ae3a2acd6ee0586e317bdb7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 10:49:12 -0500 Subject: Monkey patching TaskList::Item is no longer required. #2296 --- app/models/concerns/taskable.rb | 2 ++ app/services/system_note_service.rb | 3 ++- config/initializers/task_list_ext.rb | 12 ------------ 3 files changed, 4 insertions(+), 13 deletions(-) delete mode 100644 config/initializers/task_list_ext.rb diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index 3daa4dbe24e..de7588fea86 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -7,6 +7,8 @@ require 'task_list/filter' # # Used by MergeRequest and Issue module Taskable + COMPLETED = 'completed'.freeze + INCOMPLETE = 'incomplete'.freeze ITEM_PATTERN = / ^ (?:\s*[-+*]|(?:\d+\.))? # optional list prefix diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 7c5d523ef39..7e2bc834176 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -355,7 +355,8 @@ class SystemNoteService # # Returns the created Note object def self.change_task_status(noteable, project, author, new_task) - body = "Marked the task **#{new_task.source}** as #{new_task.status_label}" + status_label = new_task.complete? ? Taskable::COMPLETED : Taskable::INCOMPLETE + body = "Marked the task **#{new_task.source}** as #{status_label}" create_note(noteable: noteable, project: project, author: author, note: body) end end diff --git a/config/initializers/task_list_ext.rb b/config/initializers/task_list_ext.rb deleted file mode 100644 index c05b683b5be..00000000000 --- a/config/initializers/task_list_ext.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'task_list' - -class TaskList - class Item - COMPLETED = 'completed'.freeze - INCOMPLETE = 'incomplete'.freeze - - def status_label - complete? ? COMPLETED : INCOMPLETE - end - end -end -- cgit v1.2.1 From ad1f451f249692f9ed27d3f27ee712178cb30742 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 20 Nov 2015 08:13:25 -0800 Subject: Fix Drone web hook URL not being updated --- app/models/project_services/drone_ci_service.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index c240213200d..127684bd274 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -32,6 +32,8 @@ class DroneCiService < CiService def compose_service_hook hook = service_hook || build_service_hook + # If using a service template, project may not be available + hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project hook.enable_ssl_verification = enable_ssl_verification hook.save end -- cgit v1.2.1 From 3aabed3456506d1a917e6daba29cd46ce6a25dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 13:58:45 -0500 Subject: Fix bug that happened when replacing the Task list. #2296 REF: https://gitlab.com/gitlab-org/gitlab-ce/issues/2296#note_2724697 --- app/models/concerns/taskable.rb | 2 +- spec/services/issues/update_service_spec.rb | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index de7588fea86..df2a9e3e84b 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -31,7 +31,7 @@ module Taskable old_task = old_tasks[i] next unless old_task - new_task.source == new_task.source && new_task.complete? != old_task.complete? + new_task.source == old_task.source && new_task.complete? != old_task.complete? end end diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index adb3aa143ae..bc6a26416a2 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -121,6 +121,25 @@ describe Issues::UpdateService do expect(note).to be_nil end end + + context 'when a Task list with a completed item is totally replaced' do + before do + update_issue({ description: "- [ ] Task 1\n- [X] Task 2" }) + update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" }) + end + + it 'does not create a system note referencing the position the old item' do + note = find_note('Marked the task **Two** as incomplete') + + expect(note).to be_nil + end + + it 'should not generate a new note at all' do + expect { + update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" }) + }.not_to change { Note.count } + end + end end end -- cgit v1.2.1 From 976cebe45681766764509c3b4df630ced6cc6e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 14:27:31 -0500 Subject: Little fix for Rubocop's complaints. #2296 --- spec/services/issues/update_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index bc6a26416a2..cc3e6483261 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -135,9 +135,9 @@ describe Issues::UpdateService do end it 'should not generate a new note at all' do - expect { + expect do update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" }) - }.not_to change { Note.count } + end.not_to change { Note.count } end end end -- cgit v1.2.1 From d22435f3ffe005bddcd0ca24137d55cec97c84fb Mon Sep 17 00:00:00 2001 From: Greg Smethells Date: Fri, 20 Nov 2015 14:15:01 -0600 Subject: fix syntax error in common.scss --- app/assets/stylesheets/framework/common.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 40f4beb1968..61689aff57e 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -64,7 +64,7 @@ pre { .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { background: $gl-primary; - color: #FFF + color: #FFF; } .str-truncated { -- cgit v1.2.1 From 2ed48df79af82313539fc0bf5dceac2785350262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 16:50:48 -0500 Subject: Remove accidentally added line. #3598 It should exist in EE only. --- app/workers/repository_import_worker.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 1de49161997..d18c0706b30 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -51,8 +51,5 @@ class RepositoryImportWorker end project.import_finish - - # Explicitly update mirror so that upstream remote is created and fetched - project.update_mirror end end -- cgit v1.2.1 From d496a6b919abd32252f54e59d5607956005cfb15 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 20 Nov 2015 23:43:10 +0100 Subject: Handle removed source projects in MR CI commits When calling MergeRequest#ci_commit the code would previously raise an error if the source project no longer existed (e.g. because the user removed their fork). See #3599 for more information. --- app/models/merge_request.rb | 2 +- spec/models/merge_request_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1e8d9908f0a..1b3d6079d2c 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -476,7 +476,7 @@ class MergeRequest < ActiveRecord::Base end def ci_commit - if last_commit + if last_commit and source_project source_project.ci_commit(last_commit.id) end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 90af75ff0e3..567c911425c 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -193,4 +193,29 @@ describe MergeRequest do it_behaves_like 'a Taskable' do subject { create :merge_request, :simple } end + + describe '#ci_commit' do + describe 'when the source project exists' do + it 'returns the latest commit' do + commit = double(:commit, id: '123abc') + ci_commit = double(:ci_commit) + + allow(subject).to receive(:last_commit).and_return(commit) + + expect(subject.source_project).to receive(:ci_commit). + with('123abc'). + and_return(ci_commit) + + expect(subject.ci_commit).to eq(ci_commit) + end + end + + describe 'when the source project does not exist' do + it 'returns nil' do + allow(subject).to receive(:source_project).and_return(nil) + + expect(subject.ci_commit).to be_nil + end + end + end end -- cgit v1.2.1 From be045553ea4115853847cfcb8a0a40f2a3d7c4a2 Mon Sep 17 00:00:00 2001 From: Jose Corcuera Date: Fri, 20 Nov 2015 23:55:17 -0500 Subject: Fix Assignee selector when 'Unassigned' #2831 --- CHANGELOG | 1 + app/assets/javascripts/users_select.js.coffee | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 75e5b7585f9..fc7b6e75b1d 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.3.0 (unreleased) + - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) v 8.2.0 - Improved performance of finding projects and groups in various places diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 9157562a5c5..f5db74d84e7 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -58,11 +58,8 @@ class @UsersSelect query.callback(data) - initSelection: (element, callback) => - id = $(element).val() - if id != "" && id != "0" - @user(id, callback) - + initSelection: (args...) => + @initSelection(args...) formatResult: (args...) => @formatResult(args...) formatSelection: (args...) => @@ -71,6 +68,14 @@ class @UsersSelect escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results m + initSelection: (element, callback) -> + id = $(element).val() + if id == "0" + nullUser = { name: 'Unassigned' } + callback(nullUser) + else if id != "" + @user(id, callback) + formatResult: (user) -> if user.avatar_url avatar = user.avatar_url -- cgit v1.2.1 From ca735446efe46e9309fa59673ecea97fb11ddcd5 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 12:13:30 +0100 Subject: Add tags page to readme [ci skip] --- doc/api/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/api/README.md b/doc/api/README.md index 6b8528de50c..25a31b235cc 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -10,6 +10,7 @@ - [Repositories](repositories.md) - [Repository Files](repository_files.md) - [Commits](commits.md) +- [Tags](tags.md) - [Branches](branches.md) - [Merge Requests](merge_requests.md) - [Issues](issues.md) -- cgit v1.2.1 From c113e23489fe4ee0a0d0be837090597595800553 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 16:50:37 +0100 Subject: Use award emoticons in GitLab workflow [ci skip] --- doc/workflow/gitlab_flow.md | 9 ++++----- doc/workflow/voting_slider.png | Bin 5329 -> 0 bytes 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 doc/workflow/voting_slider.png diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md index 31495bce76e..8965e5b3654 100644 --- a/doc/workflow/gitlab_flow.md +++ b/doc/workflow/gitlab_flow.md @@ -244,13 +244,12 @@ Developing software happen in small messy steps and it is OK to have your histor You can use tools to view the network graphs of commits and understand the messy history that created your code. If you rebase code the history is incorrect, and there is no way for tools to remedy this because they can't deal with changing commit identifiers. -## Voting on merge requests +## Award emojis on issues and merge requests -![Voting slider in GitLab](voting_slider.png) +![Emoji bar in GitLab](award_emoji.png) -It is common to voice approval or disapproval by using +1 or -1 emoticons. -In GitLab the +1 and -1 are aggregated and shown at the top of the merge request. -As a rule of thumb anything that doesn't have two times more +1's than -1's is suspect and should not be merged yet. +It is common to voice approval or disapproval by using +1 or -1. In GitLab you +can use emojis to give a virtual high five on issues and merge requests. ## Pushing and removing branches diff --git a/doc/workflow/voting_slider.png b/doc/workflow/voting_slider.png deleted file mode 100644 index 4c660ef9593..00000000000 Binary files a/doc/workflow/voting_slider.png and /dev/null differ -- cgit v1.2.1 From 2cba93a0d2d12ee36bf98569e5c6c14ac7ea40e0 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 17:29:26 +0100 Subject: Make tag API consistent for release feature --- doc/api/tags.md | 10 +++++----- lib/api/entities.rb | 3 ++- lib/api/tags.rb | 8 ++++---- spec/requests/api/tags_spec.rb | 6 +++--- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/api/tags.md b/doc/api/tags.md index b5b90cf6b82..cf95f87dc3e 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -29,7 +29,7 @@ Parameters: ] }, "release": { - "tag": "1.0.0", + "tag_name": "1.0.0", "description": "Amazing release. Wow" }, "name": "v1.0.0", @@ -70,7 +70,7 @@ Parameters: ] }, "release": { - "tag": "1.0.0", + "tag_name": "1.0.0", "description": "Amazing release. Wow" }, "name": "v1.0.0", @@ -89,18 +89,18 @@ It returns 200 if the operation succeed. In case of an error, Add release notes to the existing git tag ``` -PUT /projects/:id/repository/:tag/release +PUT /projects/:id/repository/tags/:tag_name/release ``` Parameters: - `id` (required) - The ID of a project -- `tag` (required) - The name of a tag +- `tag_name` (required) - The name of a tag - `description` (required) - Release notes with markdown support ```json { - "tag": "1.0.0", + "tag_name": "1.0.0", "description": "Amazing release. Wow" } ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 3da6bc415d6..5dea74db295 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -322,7 +322,8 @@ module API end class Release < Grape::Entity - expose :tag, :description + expose :tag, as: :tag_name + expose :description end class RepoTag < Grape::Entity diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 673342dd447..2c6c73da08e 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -44,14 +44,14 @@ module API # # Parameters: # id (required) - The ID of a project - # tag (required) - The name of the tag + # tag_name (required) - The name of the tag # description (required) - Release notes with markdown support # Example Request: - # PUT /projects/:id/repository/tags - put ':id/repository/:tag/release', requirements: { tag: /.*/ } do + # PUT /projects/:id/repository/tags/:tag_name/release + put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do authorize_push_project required_attributes! [:description] - release = user_project.releases.find_or_initialize_by(tag: params[:tag]) + release = user_project.releases.find_or_initialize_by(tag: params[:tag_name]) release.update_attributes(description: params[:description]) present release, with: Entities::Release diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index cc9a5f47582..e3fd2d547c4 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -119,16 +119,16 @@ describe API::API, api: true do end end - describe 'PUT /projects/:id/repository/:tag/release' do + describe 'PUT /projects/:id/repository/tags/:tag_name/release' do let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } it 'should create description for existing git tag' do - put api("/projects/#{project.id}/repository/#{tag_name}/release", user), + put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: description expect(response.status).to eq(200) - expect(json_response['tag']).to eq(tag_name) + expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(description) end end -- cgit v1.2.1 From faef95af1a07bdcfd02eead36d144f332b428f1f Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 18:08:45 +0100 Subject: API: Return 404 if the tag for a release does not exist --- app/services/create_release_service.rb | 25 +++++++++++++++++++++++++ app/services/create_tag_service.rb | 10 +++++----- doc/api/tags.md | 3 ++- lib/api/tags.rb | 10 +++++++--- spec/requests/api/tags_spec.rb | 8 ++++++++ 5 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 app/services/create_release_service.rb diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb new file mode 100644 index 00000000000..355374ce252 --- /dev/null +++ b/app/services/create_release_service.rb @@ -0,0 +1,25 @@ +require_relative 'base_service' + +class CreateReleaseService < BaseService + def execute(tag_name, release_description) + + repository = project.repository + existing_tag = repository.find_tag(tag_name) + + # Only create a release if the tag exists + if existing_tag + release = project.releases.find_or_initialize_by(tag: tag_name) + release.update_attributes(description: release_description) + + success(release) + else + error('Tag does not exist') + end + end + + def success(release) + out = super() + out[:release] = release + out + end +end diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb index 9917119fce2..2452999382a 100644 --- a/app/services/create_tag_service.rb +++ b/app/services/create_tag_service.rb @@ -19,16 +19,16 @@ class CreateTagService < BaseService new_tag = repository.find_tag(tag_name) if new_tag - if release_description - release = project.releases.find_or_initialize_by(tag: tag_name) - release.update_attributes(description: release_description) - end - push_data = create_push_data(project, current_user, new_tag) EventCreateService.new.push(project, current_user, push_data) project.execute_hooks(push_data.dup, :tag_push_hooks) project.execute_services(push_data.dup, :tag_push_hooks) + if release_description + CreateReleaseService.new(@project, @current_user). + execute(tag_name, release_description) + end + success(new_tag) else error('Invalid reference name') diff --git a/doc/api/tags.md b/doc/api/tags.md index cf95f87dc3e..ca9e2cef651 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -86,7 +86,8 @@ It returns 200 if the operation succeed. In case of an error, ## New release -Add release notes to the existing git tag +Add release notes to the existing git tag. It returns 200 if the release is +created successfully. If the tag does not exist, 404 is returned. ``` PUT /projects/:id/repository/tags/:tag_name/release diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 2c6c73da08e..0721b9cc844 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -51,10 +51,14 @@ module API put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do authorize_push_project required_attributes! [:description] - release = user_project.releases.find_or_initialize_by(tag: params[:tag_name]) - release.update_attributes(description: params[:description]) + result = CreateReleaseService.new(user_project, current_user). + execute(params[:tag_name], params[:description]) - present release, with: Entities::Release + if result[:status] == :success + present result[:release], with: Entities::Release + else + render_api_error!(result[:message], 404) + end end end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index e3fd2d547c4..4dac3eec84e 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -131,5 +131,13 @@ describe API::API, api: true do expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(description) end + + it 'should return 404 if the tag does not exist' do + put api("/projects/#{project.id}/repository/tags/foobar/release", user), + description: description + + expect(response.status).to eq(404) + expect(json_response['message']).to eq('Tag does not exist') + end end end -- cgit v1.2.1 From fbac9e106dd6acf35ba679b106b438a3bbfd191f Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Sat, 21 Nov 2015 18:32:59 +0200 Subject: Award: merge request fix --- app/controllers/projects/notes_controller.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 1e3f1d8fd2f..ead940aea6c 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -59,8 +59,11 @@ class Projects::NotesController < Projects::ApplicationController end def award_toggle - noteable = note_params[:noteable_type] == "issue" ? Issue : MergeRequest - noteable = noteable.find_by!(id: note_params[:noteable_id], project: project) + noteable = if note_params[:noteable_type] == "issue" + project.issues.find(note_params[:noteable_id]) + else + project.merge_requests.find(note_params[:noteable_id]) + end data = { author: current_user, -- cgit v1.2.1 From 6f7e90f6dba300591281aba08ffbe30ce3cc5c90 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 18:51:41 +0100 Subject: Use POST to create a new release instead of PUT --- doc/api/tags.md | 6 +++--- lib/api/tags.rb | 2 +- spec/requests/api/tags_spec.rb | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/api/tags.md b/doc/api/tags.md index ca9e2cef651..ab135117250 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -84,13 +84,13 @@ It returns 200 if the operation succeed. In case of an error, 405 with an explaining error message is returned. -## New release +## Create a new release -Add release notes to the existing git tag. It returns 200 if the release is +Add release notes to the existing git tag. It returns 201 if the release is created successfully. If the tag does not exist, 404 is returned. ``` -PUT /projects/:id/repository/tags/:tag_name/release +POST /projects/:id/repository/tags/:tag_name/release ``` Parameters: diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 0721b9cc844..48f630d58e5 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -48,7 +48,7 @@ module API # description (required) - Release notes with markdown support # Example Request: # PUT /projects/:id/repository/tags/:tag_name/release - put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do + post ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do authorize_push_project required_attributes! [:description] result = CreateReleaseService.new(user_project, current_user). diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 4dac3eec84e..0ee819ae445 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -119,21 +119,21 @@ describe API::API, api: true do end end - describe 'PUT /projects/:id/repository/tags/:tag_name/release' do + describe 'POST /projects/:id/repository/tags/:tag_name/release' do let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } it 'should create description for existing git tag' do - put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), + post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: description - expect(response.status).to eq(200) + expect(response.status).to eq(201) expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(description) end it 'should return 404 if the tag does not exist' do - put api("/projects/#{project.id}/repository/tags/foobar/release", user), + post api("/projects/#{project.id}/repository/tags/foobar/release", user), description: description expect(response.status).to eq(404) -- cgit v1.2.1 From c502695fdb9b01b4ff8721845e1d39f3d3369008 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 21 Nov 2015 13:15:51 -0500 Subject: Add award_emoji.png doc image [ci skip] --- doc/workflow/award_emoji.png | Bin 0 -> 6620 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/workflow/award_emoji.png diff --git a/doc/workflow/award_emoji.png b/doc/workflow/award_emoji.png new file mode 100644 index 00000000000..fb26ee04393 Binary files /dev/null and b/doc/workflow/award_emoji.png differ -- cgit v1.2.1 From f2318dfc9e80555bae99f543d4e07ec089ab3935 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sat, 21 Nov 2015 20:25:07 +0100 Subject: Fix CI yaml syntax documentation [ci skip] --- doc/ci/yaml/README.md | 90 +++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 3e6071a2ee7..3dbf1afc7a9 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -278,26 +278,23 @@ The above script will: `artifacts` is used to specify list of files and directories which should be attached to build after success. 1. Send all files in `binaries` and `.config`: -``` -artifacts: - paths: - - binaries/ - - .config -``` + + artifacts: + paths: + - binaries/ + - .config 2. Send all git untracked files: -``` -artifacts: - untracked: true -``` + + artifacts: + untracked: true 3. Send all git untracked files and files in `binaries`: -``` -artifacts: - untracked: true - paths: - - binaries/ -``` + + artifacts: + untracked: true + paths: + - binaries/ The artifacts will be send after the build success to GitLab and will be accessible in GitLab interface to download. @@ -307,46 +304,41 @@ This feature requires GitLab Runner v0.7.0 or higher. `cache` is used to specify list of files and directories which should be cached between builds. 1. Cache all files in `binaries` and `.config`: -``` -rspec: - script: test - cache: - paths: - - binaries/ - - .config -``` + + rspec: + script: test + cache: + paths: + - binaries/ + - .config 2. Cache all git untracked files: -``` -rspec: - script: test - cache: - untracked: true -``` + rspec: + script: test + cache: + untracked: true + 3. Cache all git untracked files and files in `binaries`: -``` -rspec: - script: test - cache: - untracked: true - paths: - - binaries/ -``` -4. Locally defined cache overwrites globally defined options. This will cache only `binaries/`: + rspec: + script: test + cache: + untracked: true + paths: + - binaries/ -``` -cache: - paths: - - my/files +4. Locally defined cache overwrites globally defined options. This will cache only `binaries/`: -rspec: - script: test - cache: - paths: - - binaries/ -``` + cache: + paths: + - my/files + + rspec: + script: test + cache: + paths: + - binaries/ The cache is provided on best effort basis, so don't expect that cache will be present. For implementation details please check GitLab Runner. -- cgit v1.2.1 From 26b12e2c374c8f07abda06a8b19bd116448325f4 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 21:36:31 +0100 Subject: Add upvote/downvote fields to merge request and note API to preserve compatibility --- app/models/concerns/issuable.rb | 10 ++++++++++ app/models/note.rb | 10 ++++++++++ doc/api/merge_requests.md | 32 +++++++++++++++++++++++++------- doc/api/notes.md | 11 ++++++++--- lib/api/entities.rb | 5 +++++ 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 2dafb5e752f..98ad5e76da7 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -92,6 +92,16 @@ module Issuable opened? || reopened? end + # Deprecated. Still exists to preserve API compatibility. + def downvotes + 0 + end + + # Deprecated. Still exists to preserve API compatibility. + def upvotes + 0 + end + def subscribed?(user) subscription = subscriptions.find_by_user_id(user.id) diff --git a/app/models/note.rb b/app/models/note.rb index e30be444eb5..1c6345e735c 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -335,6 +335,16 @@ class Note < ActiveRecord::Base read_attribute(:system) end + # Deprecated. Still exists to preserve API compatibility. + def downvote? + false + end + + # Deprecated. Still exists to preserve API compatibility. + def upvote? + false + end + def editable? !system? end diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 2f17d4ae06b..0cef09d5b27 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -3,8 +3,9 @@ ## List merge requests Get all merge requests for this project. -The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). -The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. +The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). +The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. With GitLab 8.2 the return fields `upvotes` and +`downvotes` are deprecated and always return `0`. ``` GET /projects/:id/merge_requests @@ -31,6 +32,8 @@ Parameters: "project_id": 3, "title": "test1", "state": "opened", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -55,7 +58,7 @@ Parameters: ## Get single MR -Shows information about a single merge request. +Shows information about a single merge request. With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and always return `0`. ``` GET /projects/:id/merge_request/:merge_request_id @@ -75,6 +78,8 @@ Parameters: "project_id": 3, "title": "test1", "state": "merged", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -98,7 +103,9 @@ Parameters: ## Get single MR changes -Shows information about the merge request including its files and changes +Shows information about the merge request including its files and changes. +With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and +always return `0`. ``` GET /projects/:id/merge_request/:merge_request_id/changes @@ -122,6 +129,8 @@ Parameters: "updated_at": "2015-02-02T20:08:49.959Z", "target_branch": "secret_token", "source_branch": "version-1-9", + "upvotes": 0, + "downvotes": 0, "author": { "name": "Chad Hamill", "username": "jarrett", @@ -167,7 +176,8 @@ Parameters: ## Create MR -Creates a new merge request. +Creates a new merge request. With GitLab 8.2 the return fields `upvotes` and ` +downvotes` are deprecated and always return `0`. ``` POST /projects/:id/merge_requests @@ -192,6 +202,8 @@ Parameters: "project_id": 3, "title": "test1", "state": "opened", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -217,7 +229,8 @@ If an error occurs, an error number and a message explaining the reason is retur ## Update MR -Updates an existing merge request. You can change the target branch, title, or even close the MR. +Updates an existing merge request. You can change the target branch, title, or even close the MR. With GitLab 8.2 the return fields `upvotes` and `downvotes` +are deprecated and always return `0`. ``` PUT /projects/:id/merge_request/:merge_request_id @@ -242,6 +255,8 @@ Parameters: "title": "test1", "description": "description1", "state": "opened", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -266,7 +281,8 @@ If an error occurs, an error number and a message explaining the reason is retur ## Accept MR -Merge changes submitted with MR using this API. +Merge changes submitted with MR using this API. With GitLab 8.2 the return +fields `upvotes` and `downvotes` are deprecated and always return `0`. If merge success you get `200 OK`. @@ -294,6 +310,8 @@ Parameters: "project_id": 3, "title": "test1", "state": "merged", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", diff --git a/doc/api/notes.md b/doc/api/notes.md index bcba1f62151..e7f299c0994 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -6,7 +6,8 @@ Notes are comments on snippets, issues or merge requests. ### List project issue notes -Gets a list of all notes for a single issue. +Gets a list of all notes for a single issue. With GitLab 8.2 the return fields +`upvote` and `downvote` are deprecated and always return `false`. ``` GET /projects/:id/issues/:issue_id/notes @@ -32,7 +33,9 @@ Parameters: "created_at": "2013-09-30T13:46:01Z" }, "created_at": "2013-10-02T09:22:45Z", - "system": true + "system": true, + "upvote": false, + "downvote": false }, { "id": 305, @@ -47,7 +50,9 @@ Parameters: "created_at": "2013-09-30T13:46:01Z" }, "created_at": "2013-10-02T09:56:03Z", - "system": false + "system": true, + "upvote": false, + "downvote": false } ] ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 3da6bc415d6..7f9dba4b6a5 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -163,6 +163,8 @@ module API class MergeRequest < ProjectEntity expose :target_branch, :source_branch + # deprecated, always returns 0 + expose :upvotes, :downvotes expose :author, :assignee, using: Entities::UserBasic expose :source_project_id, :target_project_id expose :label_names, as: :labels @@ -192,6 +194,9 @@ module API expose :author, using: Entities::UserBasic expose :created_at expose :system?, as: :system + # upvote? and downvote? are deprecated, always return false + expose :upvote?, as: :upvote + expose :downvote?, as: :downvote end class MRNote < Grape::Entity -- cgit v1.2.1 From 3ea05c5b5b253de33d8bf8d615c66e2935b940ef Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 22:24:34 +0100 Subject: Only allow to create a release if it does not exist yet --- app/services/create_release_service.rb | 14 ++++++++++---- doc/api/tags.md | 3 ++- lib/api/tags.rb | 4 ++-- spec/requests/api/tags_spec.rb | 17 ++++++++++++++++- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb index 355374ce252..54e87478e64 100644 --- a/app/services/create_release_service.rb +++ b/app/services/create_release_service.rb @@ -8,12 +8,18 @@ class CreateReleaseService < BaseService # Only create a release if the tag exists if existing_tag - release = project.releases.find_or_initialize_by(tag: tag_name) - release.update_attributes(description: release_description) + release = project.releases.find_by(tag: tag_name) - success(release) + if(release) + error('Release already exists', 409) + else + release = project.releases.new({ tag: tag_name, description: release_description }) + release.save + + success(release) + end else - error('Tag does not exist') + error('Tag does not exist', 404) end end diff --git a/doc/api/tags.md b/doc/api/tags.md index ab135117250..bc61aa1118f 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -87,7 +87,8 @@ It returns 200 if the operation succeed. In case of an error, ## Create a new release Add release notes to the existing git tag. It returns 201 if the release is -created successfully. If the tag does not exist, 404 is returned. +created successfully. If the tag does not exist, 404 is returned. If there +already exists a release for the given tag, 409 is returned. ``` POST /projects/:id/repository/tags/:tag_name/release diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 48f630d58e5..e46d9bb96f0 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -47,7 +47,7 @@ module API # tag_name (required) - The name of the tag # description (required) - Release notes with markdown support # Example Request: - # PUT /projects/:id/repository/tags/:tag_name/release + # POST /projects/:id/repository/tags/:tag_name/release post ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do authorize_push_project required_attributes! [:description] @@ -57,7 +57,7 @@ module API if result[:status] == :success present result[:release], with: Entities::Release else - render_api_error!(result[:message], 404) + render_api_error!(result[:message], result[:http_status]) end end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 0ee819ae445..0a456fc3b5d 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -28,10 +28,10 @@ describe API::API, api: true do before do release = project.releases.find_or_initialize_by(tag: tag_name) release.update_attributes(description: description) - get api("/projects/#{project.id}/repository/tags", user) end it "should return an array of project tags with release info" do + get api("/projects/#{project.id}/repository/tags", user) expect(response.status).to eq(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(tag_name) @@ -139,5 +139,20 @@ describe API::API, api: true do expect(response.status).to eq(404) expect(json_response['message']).to eq('Tag does not exist') end + + context 'on tag with existing release' do + before do + release = project.releases.find_or_initialize_by(tag: tag_name) + release.update_attributes(description: description) + end + + it 'should return 409 if there is already a release' do + post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), + description: description + + expect(response.status).to eq(409) + expect(json_response['message']).to eq('Release already exists') + end + end end end -- cgit v1.2.1 From 04a3d27eaba0312d99e8d88a3a9ee4b5c83ecce1 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 22:34:53 +0100 Subject: Allow editing a release in API via PUT method --- app/services/create_release_service.rb | 2 +- app/services/update_release_service.rb | 29 ++++++++++++++++++++++++++ doc/api/tags.md | 23 ++++++++++++++++++++ lib/api/tags.rb | 21 +++++++++++++++++++ spec/requests/api/tags_spec.rb | 38 ++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 app/services/update_release_service.rb diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb index 54e87478e64..e06a6f2f47a 100644 --- a/app/services/create_release_service.rb +++ b/app/services/create_release_service.rb @@ -10,7 +10,7 @@ class CreateReleaseService < BaseService if existing_tag release = project.releases.find_by(tag: tag_name) - if(release) + if release error('Release already exists', 409) else release = project.releases.new({ tag: tag_name, description: release_description }) diff --git a/app/services/update_release_service.rb b/app/services/update_release_service.rb new file mode 100644 index 00000000000..25eb13ef09a --- /dev/null +++ b/app/services/update_release_service.rb @@ -0,0 +1,29 @@ +require_relative 'base_service' + +class UpdateReleaseService < BaseService + def execute(tag_name, release_description) + + repository = project.repository + existing_tag = repository.find_tag(tag_name) + + if existing_tag + release = project.releases.find_by(tag: tag_name) + + if release + release.update_attributes(description: release_description) + + success(release) + else + error('Release does not exist', 404) + end + else + error('Tag does not exist', 404) + end + end + + def success(release) + out = super() + out[:release] = release + out + end +end diff --git a/doc/api/tags.md b/doc/api/tags.md index bc61aa1118f..085d387e824 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -106,3 +106,26 @@ Parameters: "description": "Amazing release. Wow" } ``` + +## Update a release + +Updates the release notes of a given release. It returns 200 if the release is +successfully updated. If the tag or the release does not exist, it returns 404 +with a proper error message. + +``` +PUT /projects/:id/repository/tags/:tag_name/release +``` + +Parameters: + +- `id` (required) - The ID of a project +- `tag_name` (required) - The name of a tag +- `description` (required) - Release notes with markdown support + +```json +{ + "tag_name": "1.0.0", + "description": "Amazing release. Wow" +} +``` \ No newline at end of file diff --git a/lib/api/tags.rb b/lib/api/tags.rb index e46d9bb96f0..47621f443e6 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -60,6 +60,27 @@ module API render_api_error!(result[:message], result[:http_status]) end end + + # Updates a release notes of a tag + # + # Parameters: + # id (required) - The ID of a project + # tag_name (required) - The name of the tag + # description (required) - Release notes with markdown support + # Example Request: + # PUT /projects/:id/repository/tags/:tag_name/release + put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do + authorize_push_project + required_attributes! [:description] + result = UpdateReleaseService.new(user_project, current_user). + execute(params[:tag_name], params[:description]) + + if result[:status] == :success + present result[:release], with: Entities::Release + else + render_api_error!(result[:message], result[:http_status]) + end + end end end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 0a456fc3b5d..17f2643fd45 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -155,4 +155,42 @@ describe API::API, api: true do end end end + + describe 'PUT id/repository/tags/:tag_name/release' do + let(:tag_name) { project.repository.tag_names.first } + let(:description) { 'Awesome release!' } + let(:new_description) { 'The best release!' } + + context 'on tag with existing release' do + before do + release = project.releases.find_or_initialize_by(tag: tag_name) + release.update_attributes(description: description) + end + + it 'should update the release description' do + put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), + description: new_description + + expect(response.status).to eq(200) + expect(json_response['tag_name']).to eq(tag_name) + expect(json_response['description']).to eq(new_description) + end + end + + it 'should return 404 if the tag does not exist' do + put api("/projects/#{project.id}/repository/tags/foobar/release", user), + description: new_description + + expect(response.status).to eq(404) + expect(json_response['message']).to eq('Tag does not exist') + end + + it 'should return 404 if the release does not exist' do + put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), + description: new_description + + expect(response.status).to eq(404) + expect(json_response['message']).to eq('Release does not exist') + end + end end -- cgit v1.2.1 From 3fc10d46f180d5b1904496e4f4e528d215057dbd Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Sun, 22 Nov 2015 01:51:00 +0200 Subject: Emoji bug: Invalid url to image --- app/assets/javascripts/awards_handler.coffee | 2 +- app/controllers/projects/notes_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 635c9b4f8d2..09b48fe5572 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -73,7 +73,7 @@ class @AwardsHandler getImage: (emoji, custom_path) -> if custom_path - $(".awards-menu li").first().html().replace(/emoji\/.*\.png/, custom_path) + $("").attr({src: custom_path, width: 20, height: 20}).wrap("
").parent().html() else $("li[data-emoji='" + emoji + "']").html() diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index ead940aea6c..5ac18446aa7 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -136,7 +136,7 @@ class Projects::NotesController < Projects::ApplicationController discussion_id: note.discussion_id, html: note_to_html(note), award: note.is_award, - emoji_path: note.is_award ? ::AwardEmoji.path_to_emoji_image(note.note) : "", + emoji_path: note.is_award ? view_context.image_url(::AwardEmoji.path_to_emoji_image(note.note)) : "", note: note.note, discussion_html: note_to_discussion_html(note), discussion_with_diff_html: note_to_discussion_with_diff_html(note) -- cgit v1.2.1 From 075e3661c534a06753065e9e3323168b786cdbe5 Mon Sep 17 00:00:00 2001 From: Felipe Orlando Date: Sun, 22 Nov 2015 06:04:20 -0200 Subject: Update autocomplete_controller to be more readable --- app/controllers/autocomplete_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index aa0268b8d62..77c8dafc012 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -9,7 +9,7 @@ class AutocompleteController < ApplicationController @users = @users.reorder(:name) @users = @users.page(params[:page]).per(PER_PAGE) - unless params[:search].present? + if params[:search].blank? # Include current user if available to filter by "Me" if params[:current_user] && current_user @users = [*@users, current_user].uniq -- cgit v1.2.1 From ab238a767713aeb2ae019e22831fc0049873870c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 22 Nov 2015 07:23:56 -0800 Subject: Fix CSS styling for clipboard icon in new MR page Closes #3602 --- app/assets/stylesheets/framework/tw_bootstrap.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index 50c0cf61f4e..94f0ed761df 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -190,6 +190,10 @@ .btn { min-width: 124px; } + + .btn-clipboard { + min-width: 0px; + } } &.panel-small { -- cgit v1.2.1 From 732c2d15d235ca112db4b1d15c888da52660a1d5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 22 Nov 2015 18:24:53 -0500 Subject: Update doc/release/patch.md with more current patch procedures [ci skip] --- doc/release/patch.md | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/doc/release/patch.md b/doc/release/patch.md index 6aa11b283df..3022e375aca 100644 --- a/doc/release/patch.md +++ b/doc/release/patch.md @@ -1,21 +1,46 @@ # Things to do when doing a patch 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). +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). ## When to do a patch release -Do a patch release when there is a critical regression that needs to be addresses before the next monthly release. - -Otherwise include it in the monthly release and note there was a regression fix in the release announcement. +Patch releases are done as-needed in order to fix regressions in the current +major release that cannot or should not wait until the next major release. +What's included and when to release is at the discretion of the release manager. ## Release Procedure +### Create a patch issue + +Create an issue in the GitLab CE project. Name it "Release x.y.z", tag it with +the `release` label, and assign it to the milestone of the corresponding major +release. + +Use the following template: + +``` +- Picked into respective `stable` branches: +- [ ] Merge `x-y-stable` into `x-y-stable-ee` +- [ ] release-tools: `x.y.z` +- gitlab-omnibus + - [ ] `x.y.z+ee.0` + - [ ] `x.y.z+ce.0` +- [ ] Deploy +- [ ] Add patch notice to [x.y regressions]() +- [ ] [Blog post]() +- [ ] [Tweet]() +- [ ] Add entry to version.gitlab.com +``` + +Update the issue with links to merge requests that need to be/have been picked +into the `stable` branches. + ### Preparation 1. Verify that the issue can be reproduced 1. Note in the 'GitLab X.X regressions' that you will create a patch -1. Create an issue on private GitLab development server -1. Name the issue "Release X.X.X CE and X.X.X EE", this will make searching easier 1. Fix the issue on a feature branch, do this on the private GitLab development server 1. If it is a security issue, then assign it to the release manager and apply a 'security' label 1. Consider creating and testing workarounds @@ -25,7 +50,6 @@ Otherwise include it in the monthly release and note there was a regression fix 1. For EE, update the CHANGELOG-EE if it is EE specific fix. Otherwise, merge the stable CE branch and add to CHANGELOG-EE "Merge community edition changes for version X.X.X" 1. Merge CE stable branch into EE stable branch - ### Bump version Get release tools @@ -54,4 +78,4 @@ bundle exec rake release["x.x.x"] 1. Note in the 'GitLab X.X regressions' issue that the patch was published (CE only) 1. Create the 'x.y.0' version on version.gitlab.com 1. [Create new AMIs](https://dev.gitlab.org/gitlab/AMI/blob/master/README.md) -1. Create a new patch release issue for the next potential release \ No newline at end of file +1. Create a new patch release issue for the next potential release -- cgit v1.2.1 From 11728b50f96a296c760d9e69273d304f92e51f8f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 22 Nov 2015 14:27:30 +0100 Subject: Expose artifacts path --- CHANGELOG | 1 + app/models/application_setting.rb | 2 +- app/uploaders/artifact_uploader.rb | 6 +++--- config/gitlab.yml.example | 6 ++++++ config/initializers/1_settings.rb | 9 ++++++++- lib/ci/api/builds.rb | 2 ++ lib/gitlab/current_settings.rb | 2 +- 7 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 75e5b7585f9..4b7218b594d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,7 @@ v 8.3.0 (unreleased) v 8.2.0 - Improved performance of finding projects and groups in various places - Improved performance of rendering user profile pages and Atom feeds + - Expose build artifacts path as config option - Fix grouping of contributors by email in graph. - Improved performance of finding issues with/without labels - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 9e70247ef51..b2d5fe1558f 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -99,7 +99,7 @@ class ApplicationSetting < ActiveRecord::Base restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], - max_artifacts_size: Settings.gitlab_ci['max_artifacts_size'], + max_artifacts_size: Settings.artifacts['max_size'], ) end diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb index b4e0fc5772d..1dccc39e7e2 100644 --- a/app/uploaders/artifact_uploader.rb +++ b/app/uploaders/artifact_uploader.rb @@ -5,15 +5,15 @@ class ArtifactUploader < CarrierWave::Uploader::Base attr_accessor :build, :field def self.artifacts_path - File.expand_path('shared/artifacts/', Rails.root) + Gitlab.config.artifacts.path end def self.artifacts_upload_path - File.expand_path('shared/artifacts/tmp/uploads/', Rails.root) + File.join(self.artifacts_path, 'tmp/uploads/') end def self.artifacts_cache_path - File.expand_path('shared/artifacts/tmp/cache/', Rails.root) + File.join(self.artifacts_path, 'tmp/cache/') end def initialize(build, field) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 1788d4c2c7c..1da42ab38f3 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -124,6 +124,12 @@ production: &base # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" + ## Build Artifacts + artifacts: + enabled: true + # The location where build artifacts are stored (default: shared/artifacts). + # path: shared/artifacts + ## Git LFS lfs: enabled: true diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index b498fb1e1da..b162b8a83fc 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -187,7 +187,6 @@ Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_br Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url) Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root) -Settings.gitlab_ci['max_artifacts_size'] ||= 100 # in megabytes # # Reply by email @@ -199,6 +198,14 @@ Settings.incoming_email['ssl'] = false if Settings.incoming_email['ssl']. Settings.incoming_email['start_tls'] = false if Settings.incoming_email['start_tls'].nil? Settings.incoming_email['mailbox'] = "inbox" if Settings.incoming_email['mailbox'].nil? +# +# Build Artifacts +# +Settings['artifacts'] ||= Settingslogic.new({}) +Settings.artifacts['enabled'] = true if Settings.artifacts['enabled'].nil? +Settings.artifacts['path'] = File.expand_path(Settings.artifacts['path'] || File.join(Settings.shared['path'], "artifacts"), Rails.root) +Settings.artifacts['max_size'] ||= 100 # in megabytes + # # Git LFS # diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index 0a586672807..15faa6edd84 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -58,6 +58,7 @@ module Ci # POST /builds/:id/artifacts/authorize post ":id/artifacts/authorize" do require_gitlab_workhorse! + not_allowed! unless Gitlab.config.artifacts.enabled build = Ci::Build.find_by_id(params[:id]) not_found! unless build authenticate_build_token!(build) @@ -91,6 +92,7 @@ module Ci # POST /builds/:id/artifacts post ":id/artifacts" do require_gitlab_workhorse! + not_allowed! unless Gitlab.config.artifacts.enabled build = Ci::Build.find_by_id(params[:id]) not_found! unless build authenticate_build_token!(build) diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 2d3e32d9539..46a4ef0e31f 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -25,7 +25,7 @@ module Gitlab session_expire_delay: Settings.gitlab['session_expire_delay'], import_sources: Settings.gitlab['import_sources'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], - max_artifacts_size: Ci::Settings.gitlab_ci['max_artifacts_size'], + max_artifacts_size: Settings.artifacts['max_size'], ) end -- cgit v1.2.1 From 57e974c03b693c37b6ca7e57ce86477e32b770f1 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 23 Nov 2015 13:49:40 +0100 Subject: Fix 500 when using CI - Fix for Ci::Build state machine, allowing to process builds without the project - Forcefully update builds that didn't want to update with state machine - Fix saving GitLabCiService as Admin Template --- CHANGELOG | 2 ++ app/models/ci/build.rb | 2 ++ app/models/project_services/gitlab_ci_service.rb | 1 + app/workers/stuck_ci_builds_worker.rb | 3 +++ 4 files changed, 8 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index fc7b6e75b1d..1e39a9da59b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) + - Forcefully update builds that didn't want to update with state machine + - Fix: saving GitLabCiService as Admin Template v 8.2.0 - Improved performance of finding projects and groups in various places diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index e78b154084b..52ce1b920fa 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -97,6 +97,8 @@ module Ci state_machine :status, initial: :pending do after_transition any => [:success, :failed, :canceled] do |build, transition| + return unless build.gl_project + project = build.project if project.web_hooks? diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 095d04e0df4..c5657b5070e 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -30,6 +30,7 @@ class GitlabCiService < CiService end def ensure_gitlab_ci_project + return unless project project.ensure_gitlab_ci_project end diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb index ad02a3b16d9..4e5eddbaba1 100644 --- a/app/workers/stuck_ci_builds_worker.rb +++ b/app/workers/stuck_ci_builds_worker.rb @@ -14,5 +14,8 @@ class StuckCiBuildsWorker Rails.logger.debug "Dropping stuck #{build.status} build #{build.id} for runner #{build.runner_id}" build.drop end + + # Update builds that failed to drop + builds.update_all(status: 'failed') end end -- cgit v1.2.1 From 93f32b25d23525138ec910f725ebd545ce7ef125 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 23 Nov 2015 14:57:41 +0100 Subject: Add bundler-audit to CI Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 8 ++++++++ Gemfile | 1 + Gemfile.lock | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94753093540..acba37039aa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -87,3 +87,11 @@ flay: tags: - ruby - mysql + +bundler:audit + script: + - bundle exec bundle-audit update + - bundle exec bundle-audit check + tags: + - ruby + - mysql diff --git a/Gemfile b/Gemfile index 8a19885bcb1..56f341aac30 100644 --- a/Gemfile +++ b/Gemfile @@ -261,6 +261,7 @@ group :development, :test do gem 'simplecov', '~> 0.10.0', require: false gem 'flog', require: false gem 'flay', require: false + gem 'bundler-audit', require: false gem 'benchmark-ips', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 99cdc2a50ae..83d4e6927db 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -90,6 +90,9 @@ GEM bullet (4.14.9) activesupport (>= 3.0.0) uniform_notifier (~> 1.9.0) + bundler-audit (0.4.0) + bundler (~> 1.2) + thor (~> 0.18) byebug (6.0.2) cal-heatmap-rails (0.0.1) capybara (2.4.4) @@ -802,6 +805,7 @@ DEPENDENCIES brakeman (= 3.0.1) browser (~> 1.0.0) bullet + bundler-audit byebug cal-heatmap-rails (~> 0.0.1) capybara (~> 2.4.0) -- cgit v1.2.1 From 7ac112d8e814a8e3bb08e24f5af0d8828e38d4e9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 23 Nov 2015 15:18:59 +0100 Subject: Add few fixes to documentation based on comments from review Signed-off-by: Dmitriy Zaporozhets --- doc/workflow/lfs/lfs_administration.md | 4 ++-- doc/workflow/lfs/manage_large_binaries_with_git_lfs.md | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md index 9fa307a9d5b..5076b2697a3 100644 --- a/doc/workflow/lfs/lfs_administration.md +++ b/doc/workflow/lfs/lfs_administration.md @@ -1,11 +1,11 @@ -## GitLab Git LFS Administration +# GitLab Git LFS Administration Documentation on how to use Git LFS are under [Managing large binary files with Git LFS doc](manage_large_binaries_with_git_lfs.md). ## Requirements * Git LFS is supported in GitLab starting with version 8.2. -* Users need to install [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up +* Users need to install [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up. ## Configuration diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index a93fd3dfb13..210a8f71c3b 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -73,9 +73,10 @@ Check if you have permissions to push to the project or fetch from the project. * Project is not allowed to access the LFS object -Check if the LFS object you are trying to push to the project or fetch from the project is available to the project. +LFS object you are trying to push to the project or fetch from the project is not available to the project anymore. +Probably the object was removed from the server. -* Project is using deprecated LFS API +* Local git repository is using deprecated LFS API ### Invalid status for : 501 -- cgit v1.2.1 From 200c82adade27225caf2035721161a99296050f3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 23 Nov 2015 15:21:38 +0100 Subject: Fix gitlab-ci.yml syntax Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index acba37039aa..38fd7b9ac1f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -88,10 +88,10 @@ flay: - ruby - mysql -bundler:audit +bundler:audit: script: - - bundle exec bundle-audit update - - bundle exec bundle-audit check + - "bundle exec bundle-audit update" + - "bundle exec bundle-audit check" tags: - ruby - mysql -- cgit v1.2.1 From b6ed935dcddef1b458b3431dae7a1e8e250e081f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 23 Nov 2015 15:45:16 +0100 Subject: Allow bundler:audit to fail Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 38fd7b9ac1f..e8290fb36b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -95,3 +95,4 @@ bundler:audit: tags: - ruby - mysql + allow_failure: true -- cgit v1.2.1 From e4f3d05c74292ae2dbc3563db38325d982cd6f99 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 23 Nov 2015 18:35:16 +0100 Subject: Add tests for (Create|Update)ReleaseService --- spec/services/create_release_service_spec.rb | 34 ++++++++++++++++++++++++++++ spec/services/update_release_service_spec.rb | 34 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 spec/services/create_release_service_spec.rb create mode 100644 spec/services/update_release_service_spec.rb diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb new file mode 100644 index 00000000000..26d7f365bbb --- /dev/null +++ b/spec/services/create_release_service_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe CreateReleaseService do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:tag_name) { project.repository.tag_names.first } + let(:description) { 'Awesome release!' } + let(:service) { CreateReleaseService.new(project, user) } + + it 'creates a new release' do + result = service.execute(tag_name, description) + expect(result[:status]).to eq(:success) + release = project.releases.find_by(tag: tag_name) + expect(release).not_to be_nil + expect(release.description).to eq(description) + end + + it 'raises an error if the tag does not exist' do + result = service.execute("foobar", description) + expect(result[:status]).to eq(:error) + end + + context 'there already exists a release on a tag' do + before do + service.execute(tag_name, description) + end + + it 'raises an error and does not update the release' do + result = service.execute(tag_name, 'The best release!') + expect(result[:status]).to eq(:error) + expect(project.releases.find_by(tag: tag_name).description).to eq(description) + end + end +end diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb new file mode 100644 index 00000000000..93368c45b88 --- /dev/null +++ b/spec/services/update_release_service_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe UpdateReleaseService do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:tag_name) { project.repository.tag_names.first } + let(:description) { 'Awesome release!' } + let(:new_description) { 'The best release!' } + let(:service) { UpdateReleaseService.new(project, user) } + + context 'with an existing release' do + let(:create_service) { CreateReleaseService.new(project, user) } + + before do + create_service.execute(tag_name, description) + end + + it 'successfully updates an existing release' do + result = service.execute(tag_name, new_description) + expect(result[:status]).to eq(:success) + expect(project.releases.find_by(tag: tag_name).description).to eq(new_description) + end + end + + it 'raises an error if the tag does not exist' do + result = service.execute("foobar", description) + expect(result[:status]).to eq(:error) + end + + it 'raises an error if the release does not exist' do + result = service.execute(tag_name, description) + expect(result[:status]).to eq(:error) + end +end -- cgit v1.2.1 From 62303bfad1268dbb466f0bfa68fef40958c6d287 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 23 Nov 2015 16:51:16 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 1e39a9da59b..eb5badf6a96 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) + +v 8.2.1 - Forcefully update builds that didn't want to update with state machine - Fix: saving GitLabCiService as Admin Template -- cgit v1.2.1 From 97f8c6279fc39c4bad87bb880eba04802f6d351d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 19 Nov 2015 12:33:58 +0100 Subject: Added total query time to Sherlock This makes it easier to see if a problem is caused by slow queries or slow Ruby code (unrelated to any SQL queries that might be used). --- app/views/sherlock/transactions/_general.html.haml | 6 ++++++ config/locales/sherlock.en.yml | 1 + lib/gitlab/sherlock/transaction.rb | 5 +++++ spec/lib/gitlab/sherlock/transaction_spec.rb | 13 +++++++++++++ 4 files changed, 25 insertions(+) diff --git a/app/views/sherlock/transactions/_general.html.haml b/app/views/sherlock/transactions/_general.html.haml index 4287a0c3203..8533b130da6 100644 --- a/app/views/sherlock/transactions/_general.html.haml +++ b/app/views/sherlock/transactions/_general.html.haml @@ -25,6 +25,12 @@ %strong = @transaction.duration.round(2) = t('sherlock.seconds') + %li + %span.light + #{t('sherlock.query_time')} + %strong + = @transaction.query_duration.round(2) + = t('sherlock.seconds') %li %span.light #{t('sherlock.finished_at')}: diff --git a/config/locales/sherlock.en.yml b/config/locales/sherlock.en.yml index 683b09dc329..f24b825f585 100644 --- a/config/locales/sherlock.en.yml +++ b/config/locales/sherlock.en.yml @@ -35,3 +35,4 @@ en: events: Events percent: '%' count: Count + query_time: Query Time diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb index d87a4c9bb4a..3489fb251b6 100644 --- a/lib/gitlab/sherlock/transaction.rb +++ b/lib/gitlab/sherlock/transaction.rb @@ -36,6 +36,11 @@ module Gitlab @duration ||= started_at && finished_at ? finished_at - started_at : 0 end + # Returns the total query duration in seconds. + def query_duration + @query_duration ||= @queries.map { |q| q.duration }.inject(:+) / 1000.0 + end + def to_param @id end diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb index bb49fb65cf8..fb80c62c794 100644 --- a/spec/lib/gitlab/sherlock/transaction_spec.rb +++ b/spec/lib/gitlab/sherlock/transaction_spec.rb @@ -84,6 +84,19 @@ describe Gitlab::Sherlock::Transaction do end end + describe '#query_duration' do + it 'returns the total query duration in seconds' do + time = Time.now + query1 = Gitlab::Sherlock::Query.new('SELECT 1', time, time + 5) + query2 = Gitlab::Sherlock::Query.new('SELECT 2', time, time + 2) + + transaction.queries << query1 + transaction.queries << query2 + + expect(transaction.query_duration).to be_within(0.1).of(7.0) + end + end + describe '#to_param' do it 'returns the transaction ID' do expect(transaction.to_param).to eq(transaction.id) -- cgit v1.2.1 From d1a73563985a2fdaf7a99fc469c38e957d238fde Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 11:36:53 +0100 Subject: Fix ultra-light color for small text Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/typography.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index ba0312ba0db..2c4a58c8db1 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -256,3 +256,9 @@ textarea.js-gfm-input { .strikethrough { text-decoration: line-through; } + +h1, h2, h3, h4 { + small { + color: $gl-gray; + } +} -- cgit v1.2.1 From 9595ec036a0f6f0d2e18e47094431b5a1b6d427f Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 24 Nov 2015 12:11:31 +0100 Subject: Maybe rescue session_expire_delay by setting a default value. --- config/initializers/session_store.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index d7c5432da76..eb534625e33 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -7,6 +7,7 @@ include Gitlab::CurrentSettings begin Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay rescue + Settings.gitlab['session_expire_delay'] ||= 10080 end unless Rails.env.test? -- cgit v1.2.1 From 01bd7baf4fbf23507f6adcdaebf3946b892eb001 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 24 Nov 2015 12:21:11 +0100 Subject: Spell out commands for omnibus-gitlab packages. --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 007e410b677..446eb1189ad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,10 +47,10 @@ Please send a merge request with a tested solution or a merge request with a fai 1. **Observed behavior** 1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise. 1. **Output of checks** - * Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing + * Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:check SANITIZE=true`); For installations from source: `sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing * Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md) * Add the last commit SHA-1 of the GitLab version you used to replicate the issue (obtainable from the help page) - * Describe your setup (use relevant parts from `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) + * Describe your setup (use relevant parts from the env info: For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:env:info`; For installations from source: `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) 1. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem ## Merge requests -- cgit v1.2.1 From 0985032132a2dfa02f2968a3e959df8629b77b12 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 24 Nov 2015 15:57:28 +0100 Subject: Also fallback to a default value if none is set. --- config/initializers/session_store.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index eb534625e33..f30178ff711 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -3,9 +3,9 @@ require 'gitlab/current_settings' include Gitlab::CurrentSettings -# allow it to fail: it may to do so when create_from_defaults is executed before migrations are actually done +# allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done begin - Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay + Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay || 10080 rescue Settings.gitlab['session_expire_delay'] ||= 10080 end -- cgit v1.2.1 From 4caef63d82143518e6f23ec6183021657d7ed1ae Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 16:13:21 +0100 Subject: Fix 500 error when update group member permission Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + .../groups/group_members/_group_member.html.haml | 2 +- app/views/groups/group_members/update.js.haml | 2 +- features/groups.feature | 7 +++ features/steps/groups.rb | 51 +++++++++++++++++----- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 90ade156caf..8916b9f0403 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) + - Fix 500 error when update group member permission v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml index be94b1abc11..bae67552a10 100644 --- a/app/views/groups/group_members/_group_member.html.haml +++ b/app/views/groups/group_members/_group_member.html.haml @@ -30,7 +30,7 @@ - if should_user_see_group_roles?(current_user, @group) %span.pull-right - %strong= member.human_access + %strong.member-access-level= member.human_access - if show_controls - if can?(current_user, :update_group_member, member) = button_tag class: "btn-xs btn js-toggle-button", diff --git a/app/views/groups/group_members/update.js.haml b/app/views/groups/group_members/update.js.haml index 5bad48abafd..df726e2b2b9 100644 --- a/app/views/groups/group_members/update.js.haml +++ b/app/views/groups/group_members/update.js.haml @@ -1,2 +1,2 @@ :plain - $("##{dom_id(@member)}").replaceWith('#{escape_javascript(render(@member, member: @member, show_controls: true))}'); + $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render(@group_member, member: @group_member, show_controls: true))}'); diff --git a/features/groups.feature b/features/groups.feature index abf3769a844..938e658f2a9 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -74,6 +74,13 @@ Feature: Groups When I select "sjobs@apple.com" as "Reporter" Then I should see "sjobs@apple.com" in team list as invited "Reporter" + @javascript + Scenario: Edit group member permissions + Given "Mary Jane" is guest of group "Owned" + And I visit group "Owned" members page + When I change the "Mary Jane" role to "Developer" + Then I should see "Mary Jane" as "Developer" + # Leave @javascript diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 9c0313537b1..1d530a25283 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -26,7 +26,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'Group "Owned" has a public project "Public-project"' do - group = Group.find_by(name: "Owned") + group = owned_group @project = create :empty_project, :public, group: group, @@ -91,7 +91,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'I should see group "Owned" projects list' do - Group.find_by(name: "Owned").projects.each do |project| + owned_group.projects.each do |project| expect(page).to have_link project.name end end @@ -172,12 +172,12 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I change group "Owned" avatar' do attach_file(:group_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')) click_button "Save group" - Group.find_by(name: "Owned").reload + owned_group.reload end step 'I should see new group "Owned" avatar' do - expect(Group.find_by(name: "Owned").avatar).to be_instance_of AvatarUploader - expect(Group.find_by(name: "Owned").avatar.url).to eq "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/banana_sample.gif" + expect(owned_group.avatar).to be_instance_of AvatarUploader + expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/banana_sample.gif" end step 'I should see the "Remove avatar" button' do @@ -187,16 +187,16 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I have group "Owned" avatar' do attach_file(:group_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')) click_button "Save group" - Group.find_by(name: "Owned").reload + owned_group.reload end step 'I remove group "Owned" avatar' do click_link "Remove avatar" - Group.find_by(name: "Owned").reload + owned_group.reload end step 'I should not see group "Owned" avatar' do - expect(Group.find_by(name: "Owned").avatar?).to eq false + expect(owned_group.avatar?).to eq false end step 'I should not see the "Remove avatar" button' do @@ -295,18 +295,49 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end end + step 'I change the "Mary Jane" role to "Developer"' do + member = mary_jane_member + + page.within "#group_member_#{member.id}" do + find(".js-toggle-button").click + page.within "#edit_group_member_#{member.id}" do + select 'Developer', from: 'group_member_access_level' + click_on 'Save' + end + end + end + + step 'I should see "Mary Jane" as "Developer"' do + member = mary_jane_member + + page.within "#group_member_#{member.id}" do + page.within '.member-access-level' do + expect(page).to have_content "Developer" + end + end + end + protected + def owned_group + @owned_group ||= Group.find_by(name: "Owned") + end + + def mary_jane_member + user = User.find_by(name: "Mary Jane") + member = owned_group.members.where(user_id: user.id).first + end + def assigned_to_me(key) project.send(key).where(assignee_id: current_user.id) end def project - Group.find_by(name: "Owned").projects.first + owned_group.projects.first end def group_milestone - group = Group.find_by(name: "Owned") + group = owned_group @project1 = create :project, group: group -- cgit v1.2.1 From cb9f573818098b2fdc556db867bd9a32f469d471 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 16:17:11 +0100 Subject: Fix rubocop complain Signed-off-by: Dmitriy Zaporozhets --- features/steps/groups.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 1d530a25283..5086a9e0b87 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -325,7 +325,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps def mary_jane_member user = User.find_by(name: "Mary Jane") - member = owned_group.members.where(user_id: user.id).first + owned_group.members.where(user_id: user.id).first end def assigned_to_me(key) -- cgit v1.2.1 From cbaa33db8a77efaf6446ed294db2e334ff7de8e7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 17:40:41 +0100 Subject: Small code improvement Signed-off-by: Dmitriy Zaporozhets --- features/steps/groups.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 5086a9e0b87..7c991af4c2b 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -325,7 +325,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps def mary_jane_member user = User.find_by(name: "Mary Jane") - owned_group.members.where(user_id: user.id).first + owned_group.members.find_by(user_id: user.id) end def assigned_to_me(key) -- cgit v1.2.1 From 776027e4c7a4e7ff5df18e92cd79461a7d5de73e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 15:36:36 -0500 Subject: Bump gon to ~> 6.0.1 Closes #2856 --- Gemfile | 2 +- Gemfile.lock | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index bd8e9bd8fa1..3bb9448e405 100644 --- a/Gemfile +++ b/Gemfile @@ -193,7 +193,7 @@ gem 'addressable', '~> 2.3.8' gem 'bootstrap-sass', '~> 3.0' gem 'font-awesome-rails', '~> 4.2' gem 'gitlab_emoji', '~> 0.1' -gem 'gon', '~> 5.0.0' +gem 'gon', '~> 6.0.1' gem 'jquery-atwho-rails', '~> 1.3.2' gem 'jquery-rails', '~> 3.1.3' gem 'jquery-scrollto-rails', '~> 1.4.3' diff --git a/Gemfile.lock b/Gemfile.lock index 448941cfc8d..70c532f05de 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -312,9 +312,11 @@ GEM rouge (~> 1.10.1) sanitize (~> 2.1.0) stringex (~> 2.5.1) - gon (5.0.4) - actionpack (>= 2.3.0) + gon (6.0.1) + actionpack (>= 3.0) json + multi_json + request_store (>= 1.0) grape (0.13.0) activesupport builder @@ -846,7 +848,7 @@ DEPENDENCIES gitlab_meta (= 7.0) gitlab_omniauth-ldap (~> 1.2.1) gollum-lib (~> 4.0.2) - gon (~> 5.0.0) + gon (~> 6.0.1) grape (~> 0.13.0) grape-entity (~> 0.4.2) haml-rails (~> 0.9.0) -- cgit v1.2.1 From 26f9dbf8a95d79eccf6a3dd3b779b7a7bc51c03b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 15:42:42 -0500 Subject: Bump creole to ~> 0.5.0 Closes #2815 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index bd8e9bd8fa1..bcbdabb5d95 100644 --- a/Gemfile +++ b/Gemfile @@ -95,7 +95,7 @@ gem 'redcarpet', '~> 3.3.3' gem 'RedCloth', '~> 4.2.9' gem 'rdoc', '~>3.6' gem 'org-ruby', '~> 0.9.12' -gem 'creole', '~>0.3.6' +gem 'creole', '~> 0.5.0' gem 'wikicloth', '0.8.1' gem 'asciidoctor', '~> 1.5.2' diff --git a/Gemfile.lock b/Gemfile.lock index 448941cfc8d..4d60d8fadc1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -134,7 +134,7 @@ GEM thor (~> 0.19.1) crack (0.4.2) safe_yaml (~> 1.0.0) - creole (0.3.8) + creole (0.5.0) d3_rails (3.5.6) railties (>= 3.1.0) daemons (1.2.3) @@ -816,7 +816,7 @@ DEPENDENCIES colored (~> 1.2) colorize (~> 0.5.8) coveralls (~> 0.8.2) - creole (~> 0.3.6) + creole (~> 0.5.0) d3_rails (~> 3.5.5) database_cleaner (~> 1.4.0) default_value_for (~> 3.0.0) -- cgit v1.2.1 From 446a95d3079e8927dd40c8d41a58d66192721cfc Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 15:48:49 -0500 Subject: Bump rack-oauth2 to ~> 1.2.1 --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index bd8e9bd8fa1..0436e930653 100644 --- a/Gemfile +++ b/Gemfile @@ -28,7 +28,7 @@ gem 'omniauth-saml', '~> 1.4.0' gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth_crowd' -gem 'rack-oauth2', '~> 1.0.5' +gem 'rack-oauth2', '~> 1.2.1' # Two-factor authentication gem 'devise-two-factor', '~> 2.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 448941cfc8d..ecd83b6979a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -357,7 +357,7 @@ GEM httparty (0.13.5) json (~> 1.8) multi_xml (>= 0.5.2) - httpclient (2.6.0.1) + httpclient (2.7.0.1) i18n (0.7.0) ice_cube (0.11.1) ice_nine (0.11.1) @@ -502,7 +502,7 @@ GEM rack-cors (0.4.0) rack-mount (0.8.3) rack (>= 1.0.0) - rack-oauth2 (1.0.10) + rack-oauth2 (1.2.1) activesupport (>= 2.3) attr_required (>= 0.0.5) httpclient (>= 2.4) @@ -889,7 +889,7 @@ DEPENDENCIES quiet_assets (~> 1.0.2) rack-attack (~> 4.3.0) rack-cors (~> 0.4.0) - rack-oauth2 (~> 1.0.5) + rack-oauth2 (~> 1.2.1) rails (= 4.1.14) raphael-rails (~> 2.1.2) rblineprof -- cgit v1.2.1 From b9828278127724ff5eac60eb71e36f675b9985cd Mon Sep 17 00:00:00 2001 From: Eirik Lygre Date: Tue, 24 Nov 2015 20:55:18 +0000 Subject: Add reference to Microsoft's Git Credential Manager for Windows. --- doc/workflow/lfs/manage_large_binaries_with_git_lfs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index 210a8f71c3b..b59e92cb317 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -121,6 +121,6 @@ git config --global credential.helper 'cache --timeout=3600' This will remember the credentials for an hour after which Git operations will require re-authentication. -If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, `wincred` is available. +If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, you can use `wincred` or Microsoft's [Git Credential Manager for Windows](https://github.com/Microsoft/Git-Credential-Manager-for-Windows/releases). -More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). +More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). \ No newline at end of file -- cgit v1.2.1 From a2915aeb662e30537f3f226e3db6d85fd252905f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 16:12:25 -0500 Subject: Rescue StandardError, not Exception --- app/services/merge_requests/merge_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 7963af127e1..d619b72e3c2 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -38,7 +38,7 @@ module MergeRequests } repository.merge(current_user, merge_request.source_sha, merge_request.target_branch, options) - rescue Exception => e + rescue StandardError => e merge_request.update(merge_error: "Something went wrong during merge") Rails.logger.error(e.message) return false -- cgit v1.2.1 From 28de6770fae44f65aa81538ff660fc7050072070 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 16:29:21 -0500 Subject: Bump colorize to ~> 0.7.0 Also removes `colored` which came in during the CI merge and is redundant. Closes #2822 --- Gemfile | 3 +-- Gemfile.lock | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index bd8e9bd8fa1..b7ec16198c9 100644 --- a/Gemfile +++ b/Gemfile @@ -125,8 +125,7 @@ gem 'sidetiq', '~> 0.6.3' gem "httparty", '~> 0.13.3' # Colored output to console -gem "colored", '~> 1.2' -gem "colorize", '~> 0.5.8' +gem "colorize", '~> 0.7.0' # GitLab settings gem 'settingslogic', '~> 2.0.9' diff --git a/Gemfile.lock b/Gemfile.lock index 448941cfc8d..20fc885481a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -123,8 +123,7 @@ GEM coffee-script-source execjs coffee-script-source (1.9.1.1) - colored (1.2) - colorize (0.5.8) + colorize (0.7.7) connection_pool (2.2.0) coveralls (0.8.2) json (~> 1.8) @@ -813,8 +812,7 @@ DEPENDENCIES carrierwave (~> 0.9.0) charlock_holmes (~> 0.7.3) coffee-rails (~> 4.1.0) - colored (~> 1.2) - colorize (~> 0.5.8) + colorize (~> 0.7.0) coveralls (~> 0.8.2) creole (~> 0.3.6) d3_rails (~> 3.5.5) -- cgit v1.2.1 From af65046c5fd9f214124d39a5582b06ee2fe39933 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 22 Nov 2015 23:52:02 -0500 Subject: Move HTTP/SSH clone button logic to helpers --- app/helpers/button_helper.rb | 35 +++++++++++++++++++++++++++++++++ app/views/shared/_clone_panel.html.haml | 18 ++--------------- 2 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 app/helpers/button_helper.rb diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb new file mode 100644 index 00000000000..8963e96ea1d --- /dev/null +++ b/app/helpers/button_helper.rb @@ -0,0 +1,35 @@ +module ButtonHelper + def http_clone_button(project) + klass = 'btn' + klass << ' active' if default_clone_protocol == 'http' + klass << ' has_tooltip' if current_user.try(:require_password?) + + protocol = gitlab_config.protocol.upcase + + content_tag :button, protocol, + class: klass, + data: { + clone: project.http_url_to_repo, + container: 'body', + html: 'true', + title: "Set a password on your account
to pull or push via #{protocol}" + }, + type: :button + end + + def ssh_clone_button(project) + klass = 'btn' + klass << ' active' if default_clone_protocol == 'ssh' + klass << ' has_tooltip' if current_user.try(:require_ssh_key?) + + content_tag :button, 'SSH', + class: klass, + data: { + clone: project.ssh_url_to_repo, + container: 'body', + html: 'true', + title: 'Add an SSH key to your profile
to pull or push via SSH.' + }, + type: :button + end +end diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 8bcb24ae9df..a82971157d3 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -2,23 +2,9 @@ .git-clone-holder.input-group .input-group-addon.git-protocols .input-group-btn - %button{ | - type: 'button', | - class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", | - :"data-clone" => project.ssh_url_to_repo, | - :"data-title" => "Add an SSH key to your profile
to pull or push via SSH.", - :"data-html" => "true", - :"data-container" => "body"} - SSH + = ssh_clone_button(project) .input-group-btn - %button{ | - type: 'button', | - class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", | - :"data-clone" => project.http_url_to_repo, | - :"data-title" => "Set a password on your account
to pull or push via #{gitlab_config.protocol.upcase}.", - :"data-html" => "true", - :"data-container" => "body"} - = gitlab_config.protocol.upcase + = http_clone_button(project) = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true - if project.kind_of?(Project) .input-group-addon.has_tooltip{title: "#{visibility_level_label(project.visibility_level)} project", data: { container: "body" } } -- cgit v1.2.1 From acc0f162c864d2a061461467473fca8761b6611f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 22 Nov 2015 23:53:55 -0500 Subject: Move clipboard_button helper to ButtonHelper module --- app/helpers/button_helper.rb | 7 +++++++ app/helpers/clipboard_helper.rb | 8 -------- 2 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 app/helpers/clipboard_helper.rb diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index 8963e96ea1d..bbb35afca89 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -1,4 +1,11 @@ module ButtonHelper + def clipboard_button + content_tag :button, + icon('clipboard'), + class: 'btn btn-xs btn-clipboard js-clipboard-trigger', + type: :button + end + def http_clone_button(project) klass = 'btn' klass << ' active' if default_clone_protocol == 'http' diff --git a/app/helpers/clipboard_helper.rb b/app/helpers/clipboard_helper.rb deleted file mode 100644 index 3c1d7569fac..00000000000 --- a/app/helpers/clipboard_helper.rb +++ /dev/null @@ -1,8 +0,0 @@ -module ClipboardHelper - def clipboard_button - content_tag :button, - icon('clipboard'), - class: 'btn btn-xs btn-clipboard js-clipboard-trigger', - type: :button - end -end -- cgit v1.2.1 From 7dab8ed739359bc579d8bc4d3de61816993ca57d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 18:35:24 -0500 Subject: Rework the copy_to_clipboard logic It needed to be more flexible in how we set the target text or element. --- app/assets/javascripts/copy_to_clipboard.js.coffee | 57 ++++++++++++---------- app/helpers/button_helper.rb | 20 +++++++- app/views/projects/commits/_commit.html.haml | 4 +- app/views/projects/issues/_discussion.html.haml | 4 +- .../projects/merge_requests/_discussion.html.haml | 4 +- 5 files changed, 55 insertions(+), 34 deletions(-) diff --git a/app/assets/javascripts/copy_to_clipboard.js.coffee b/app/assets/javascripts/copy_to_clipboard.js.coffee index 9c68c5cc1bc..24301e01b10 100644 --- a/app/assets/javascripts/copy_to_clipboard.js.coffee +++ b/app/assets/javascripts/copy_to_clipboard.js.coffee @@ -1,32 +1,37 @@ #= require clipboard -$ -> - clipboard = new Clipboard '.js-clipboard-trigger', - text: (trigger) -> - $target = $(trigger.nextElementSibling || trigger.previousElementSibling) - $target.data('clipboard-text') || $target.text().trim() +genericSuccess = (e) -> + showTooltip(e.trigger, 'Copied!') + + # Clear the selection and blur the trigger so it loses its border + e.clearSelection() + $(e.trigger).blur() - clipboard.on 'success', (e) -> - $(e.trigger). - tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!'). - tooltip('show'). - one('mouseleave', -> $(this).tooltip('hide')) +# Safari doesn't support `execCommand`, so instead we inform the user to +# copy manually. +# +# See http://clipboardjs.com/#browser-support +genericError = (e) -> + if /Mac/i.test(navigator.userAgent) + key = '⌘' # Command + else + key = 'Ctrl' - # Clear the selection and blur the trigger so it loses its border - e.clearSelection() - $(e.trigger).blur() + showTooltip(e.trigger, "Press #{key}-C to copy") - # Safari doesn't support `execCommand`, so instead we inform the user to - # copy manually. - # - # See http://clipboardjs.com/#browser-support - clipboard.on 'error', (e) -> - if /Mac/i.test(navigator.userAgent) - title = "Press ⌘-C to copy" - else - title = "Press Ctrl-C to copy" +showTooltip = (target, title) -> + $(target). + tooltip( + container: 'body' + html: 'true' + placement: 'auto bottom' + title: title + trigger: 'manual' + ). + tooltip('show'). + one('mouseleave', -> $(this).tooltip('hide')) - $(e.trigger). - tooltip(trigger: 'manual', placement: 'auto bottom', html: true, title: title). - tooltip('show'). - one('mouseleave', -> $(this).tooltip('hide')) +$ -> + clipboard = new Clipboard '[data-clipboard-target], [data-clipboard-text]' + clipboard.on 'success', genericSuccess + clipboard.on 'error', genericError diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index bbb35afca89..b9bb1ac8d88 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -1,8 +1,24 @@ module ButtonHelper - def clipboard_button + # Output a "Copy to Clipboard" button + # + # data - Data attributes passed to `content_tag` + # + # Examples: + # + # # Define the clipboard's text + # clipboard_button(clipboard_text: "Foo") + # # => "" + # + # # Define the target element + # clipboard_button(clipboard_target: "#foo") + # # => "" + # + # See http://clipboardjs.com/#usage + def clipboard_button(data = {}) content_tag :button, icon('clipboard'), - class: 'btn btn-xs btn-clipboard js-clipboard-trigger', + class: 'btn btn-xs btn-clipboard', + data: data, type: :button end diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 805be332e64..2e489a0a4d5 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -20,8 +20,8 @@ - if ci_commit = render_ci_status(ci_commit)   - = clipboard_button - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id", data: {clipboard_text: commit.id} + = clipboard_button(clipboard_text: commit.id) + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" .notes_count - if note_count > 0 diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index 020952dd001..8f0a1ed9be2 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -18,9 +18,9 @@ = link_to_member(@project, participant, name: false, size: 24) .col-md-3 .input-group.cross-project-reference - %span.slead.has_tooltip{title: 'Cross-project reference'} + %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} = cross_project_reference(@project, @issue) - = clipboard_button + = clipboard_button(clipboard_target: '#cross-project-reference') .row %section.col-md-9 diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index cb75bd8c5ba..2b3c3eff5e4 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -15,9 +15,9 @@ = render "projects/merge_requests/show/participants" .col-md-3 .input-group.cross-project-reference - %span.slead.has_tooltip{title: 'Cross-project reference'} + %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} = cross_project_reference(@project, @merge_request) - = clipboard_button + = clipboard_button(clipboard_target: '#cross-project-reference') .row %section.col-md-9 -- cgit v1.2.1 From ec002e802677a691d758ebc6f94f23020b2b063a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 19:20:23 -0500 Subject: Remove usage of Colored --- lib/tasks/gitlab/task_helpers.rake | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake index c95b6540ebc..efb863a8764 100644 --- a/lib/tasks/gitlab/task_helpers.rake +++ b/lib/tasks/gitlab/task_helpers.rake @@ -2,16 +2,6 @@ module Gitlab class TaskAbortedByUserError < StandardError; end end -unless STDOUT.isatty - module Colored - extend self - - def colorize(string, options={}) - string - end - end -end - namespace :gitlab do # Ask if the user wants to continue @@ -103,7 +93,7 @@ namespace :gitlab do gitlab_user = Gitlab.config.gitlab.user current_user = run(%W(whoami)).chomp unless current_user == gitlab_user - puts "#{Colored.color(:black)+Colored.color(:on_yellow)} Warning #{Colored.extra(:clear)}" + puts " Warning ".colorize(:black).on_yellow puts " You are running as user #{current_user.magenta}, we hope you know what you are doing." puts " Things may work\/fail for the wrong reasons." puts " For correct results you should run this as user #{gitlab_user.magenta}." -- cgit v1.2.1 From 1d80cd315e403e02f85a99c35eb9e83f4d829f8d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 19:32:06 -0500 Subject: Add clipboard button to project clone panel Closes #3585 --- app/assets/javascripts/project.js.coffee | 10 ++++------ app/assets/stylesheets/pages/projects.scss | 7 ++++++- app/helpers/button_helper.rb | 4 ++-- app/helpers/projects_helper.rb | 3 +-- app/views/shared/_clone_panel.html.haml | 2 ++ 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index 0ea8fffce07..d37a00bdedc 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -3,11 +3,9 @@ class @Project # Git clone panel switcher cloneHolder = $('.git-clone-holder') if cloneHolder.length - $('a, button', cloneHolder).click -> - $('a, button', cloneHolder).removeClass 'active' - $(@).addClass 'active' - $('#project_clone', cloneHolder).val $(@).data 'clone' - $(".clone").text("").append $(@).data 'clone' + $('.js-protocol-switch', cloneHolder).click -> + $('.js-protocol-switch', cloneHolder).toggleClass('active') + $('#project_clone').val($(@).data('clone')) # Ref switcher $('.project-refs-select').on 'change', -> @@ -39,4 +37,4 @@ class @Project when 4 then label = ' On Mention ' $('#notifications-button').empty().append("" + label + "") $(@).parents('ul').find('li.active').removeClass 'active' - $(@).parent().addClass 'active' \ No newline at end of file + $(@).parent().addClass 'active' diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index d3b10040022..ad607ead2bf 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -178,6 +178,11 @@ &:active { outline: none; } + + &.btn-clipboard { + padding-left: 15px; + padding-right: 15px; + } } .active { @@ -552,4 +557,4 @@ pre.light-well { z-index: 100; position: relative; } -} \ No newline at end of file +} diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index b9bb1ac8d88..313b6dde910 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -23,7 +23,7 @@ module ButtonHelper end def http_clone_button(project) - klass = 'btn' + klass = 'btn js-protocol-switch' klass << ' active' if default_clone_protocol == 'http' klass << ' has_tooltip' if current_user.try(:require_password?) @@ -41,7 +41,7 @@ module ButtonHelper end def ssh_clone_button(project) - klass = 'btn' + klass = 'btn js-protocol-switch' klass << ' active' if default_clone_protocol == 'ssh' klass << ' has_tooltip' if current_user.try(:require_ssh_key?) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index c9cd4a0d54c..c0c51aae039 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -173,8 +173,7 @@ module ProjectsHelper 'unknown' end - def default_url_to_repo(project = nil) - project = project || @project + def default_url_to_repo(project = @project) current_user ? project.url_to_repo : project.http_url_to_repo end diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index a82971157d3..408a46aaafc 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -6,6 +6,8 @@ .input-group-btn = http_clone_button(project) = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true + .input-group-btn + = clipboard_button(clipboard_target: '#project_clone') - if project.kind_of?(Project) .input-group-addon.has_tooltip{title: "#{visibility_level_label(project.visibility_level)} project", data: { container: "body" } } .visibility-level-label -- cgit v1.2.1 From 637e64c24472a06b32091cb45f1b81260c5e6ec0 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 20:38:36 -0500 Subject: Clean up the Git protocol switcher JS Also re-adds the `.clone` updating that was removed accidentally. Thanks, tests! --- app/assets/javascripts/project.js.coffee | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index d37a00bdedc..ec919f0cd67 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -1,11 +1,19 @@ class @Project constructor: -> - # Git clone panel switcher - cloneHolder = $('.git-clone-holder') - if cloneHolder.length - $('.js-protocol-switch', cloneHolder).click -> - $('.js-protocol-switch', cloneHolder).toggleClass('active') - $('#project_clone').val($(@).data('clone')) + # Git protocol switcher + $('.js-protocol-switch').click -> + return if $(@).hasClass('active') + + # Toggle 'active' for both buttons + $('.js-protocol-switch').toggleClass('active') + + url = $(@).data('clone') + + # Update the input field + $('#project_clone').val(url) + + # Update the command line instructions + $('.clone').text(url) # Ref switcher $('.project-refs-select').on 'change', -> -- cgit v1.2.1 From 767bd6f8825661c2cd170172f2b0d5ce34c67a93 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 16:18:14 -0500 Subject: Enable the Lint/RescueException cop --- .rubocop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 11e4502849a..d59edbc8b17 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -888,7 +888,7 @@ Lint/RequireParentheses: Lint/RescueException: Description: 'Avoid rescuing the Exception class.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' - Enabled: false + Enabled: true Lint/ShadowingOuterLocalVariable: Description: >- -- cgit v1.2.1 From d9749c8d36750136cbd8989918b1fec8ff2c4b49 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 16:51:01 +0100 Subject: Improve UI for group members page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/lists.scss | 5 ++ app/assets/stylesheets/framework/pagination.scss | 4 ++ app/assets/stylesheets/pages/groups.scss | 4 +- .../groups/group_members/_group_member.html.haml | 4 +- app/views/groups/group_members/index.html.haml | 58 +++++++++++----------- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 45f3b5849bf..a798ae812e3 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -127,3 +127,8 @@ ul.content-list { } } +.panel > .content-list { + li { + margin: 0; + } +} diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss index 6677f94dafd..2cd30491bf5 100644 --- a/app/assets/stylesheets/framework/pagination.scss +++ b/app/assets/stylesheets/framework/pagination.scss @@ -32,3 +32,7 @@ } } } + +.panel > .gl-pagination { + margin: 0; +} diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 07a38a19fad..bcb73a9acd3 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -1,5 +1,5 @@ .new-group-member-holder { - margin-top: 50px; + margin-top: 10px; padding-top: 20px; } @@ -15,4 +15,4 @@ .form-control { height: 42px; } -} \ No newline at end of file +} diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml index bae67552a10..a79a0fcdc8e 100644 --- a/app/views/groups/group_members/_group_member.html.haml +++ b/app/views/groups/group_members/_group_member.html.haml @@ -4,7 +4,7 @@ %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} %span{class: ("list-item-name" if show_controls)} - if member.user - = image_tag avatar_icon(user, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(user, 24), class: "avatar s24", alt: '' %strong = link_to user.name, user_path(user) %span.cgray= user.username @@ -14,7 +14,7 @@ %label.label.label-danger %strong Blocked - else - = image_tag avatar_icon(member.invite_email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: '' %strong = member.invite_email %span.cgray diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index d4ad33a8bf1..b3a811b2669 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,38 +1,40 @@ +- @blank_container = true - page_title "Members" - header_title group_title(@group, "Members", group_group_members_path(@group)) -- if should_user_see_group_roles?(current_user, @group) - %p.light - Members of group have access to all group projects. - Read more about permissions - %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" -.clearfix.js-toggle-container - = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do - .form-group - = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input', spellcheck: false } - = button_tag 'Search', class: 'btn' - - if current_user && current_user.can?(:admin_group_member, @group) - .pull-right - = button_tag class: 'btn btn-new js-toggle-button', type: 'button' do - Add members - %i.fa.fa-chevron-down - - .js-toggle-content.hide.new-group-member-holder - = render "new_group_member" -.panel.panel-default.prepend-top-20 - .panel-heading - %strong #{@group.name} - group members - %small - (#{@members.total_count}) - %ul.well-list - - @members.each do |member| - = render 'groups/group_members/group_member', member: member, show_controls: true +.group-members-page + - if current_user && current_user.can?(:admin_group_member, @group) + .panel.panel-default + .panel-heading + Add new user to group + .panel-body + - if should_user_see_group_roles?(current_user, @group) + %p.light + Members of group have access to all group projects. + Read more about permissions + %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" + .new-group-member-holder + = render "new_group_member" -= paginate @members, theme: 'gitlab' + .panel.panel-default + .panel-heading + %strong #{@group.name} + group members + %small + (#{@members.total_count}) + .pull-right + = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do + .form-group + = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false } + = button_tag class: 'btn' do + = icon("search") + %ul.content-list + - @members.each do |member| + = render 'groups/group_members/group_member', member: member, show_controls: true + = paginate @members, theme: 'gitlab' :javascript $('form.member-search-form').on('submit', function(event) { -- cgit v1.2.1 From 34cc8f4a60f25bfa2503f0ad006b047fd2c2f81c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 17:11:45 +0100 Subject: Improve project members page UI Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/groups.scss | 5 --- app/views/groups/group_members/index.html.haml | 7 +--- .../project_members/_group_members.html.haml | 8 ++-- .../project_members/_project_member.html.haml | 4 +- app/views/projects/project_members/_team.html.haml | 16 +++++++- app/views/projects/project_members/index.html.haml | 45 ++++++++-------------- 6 files changed, 36 insertions(+), 49 deletions(-) diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index bcb73a9acd3..263993f59a5 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -1,8 +1,3 @@ -.new-group-member-holder { - margin-top: 10px; - padding-top: 20px; -} - .member-search-form { float: left; } diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index b3a811b2669..c83766e729a 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,9 +1,6 @@ -- @blank_container = true - page_title "Members" - header_title group_title(@group, "Members", group_group_members_path(@group)) - - - +- @blank_container = true .group-members-page - if current_user && current_user.can?(:admin_group_member, @group) @@ -14,8 +11,6 @@ - if should_user_see_group_roles?(current_user, @group) %p.light Members of group have access to all group projects. - Read more about permissions - %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" .new-group-member-holder = render "new_group_member" diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml index 43e92437cf5..0c73d7e34ac 100644 --- a/app/views/projects/project_members/_group_members.html.haml +++ b/app/views/projects/project_members/_group_members.html.haml @@ -4,11 +4,11 @@ group members %small (#{members.count}) - .panel-head-actions - = link_to group_group_members_path(@group), class: 'btn btn-sm' do - %i.fa.fa-pencil-square-o + .pull-right + = link_to group_group_members_path(@group), class: 'btn' do + = icon('pencil-square-o') Edit group members - %ul.well-list + %ul.content-list - members.each do |member| = render 'groups/group_members/group_member', member: member, show_controls: false - if members.count > 20 diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml index f07cd97e63d..05bf3a7ef6a 100644 --- a/app/views/projects/project_members/_project_member.html.haml +++ b/app/views/projects/project_members/_project_member.html.haml @@ -4,7 +4,7 @@ %li{class: "#{dom_class(member)} js-toggle-container project_member_row access-#{member.human_access.downcase}", id: dom_id(member)} %span.list-item-name - if member.user - = image_tag avatar_icon(user, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(user, 24), class: "avatar s24", alt: '' %strong = link_to user.name, user_path(user) %span.cgray= user.username @@ -14,7 +14,7 @@ %label.label.label-danger %strong Blocked - else - = image_tag avatar_icon(member.invite_email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: '' %strong = member.invite_email %span.cgray diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index b807fb2cc9d..8f4c6134261 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -1,9 +1,21 @@ -.panel.panel-default.prepend-top-20 +.panel.panel-default .panel-heading %strong #{@project.name} project members %small (#{members.count}) - %ul.well-list + .pull-right + = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do + .form-group + = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false } + = button_tag class: 'btn' do + = icon("search") + %ul.content-list - members.each do |project_member| = render 'project_member', member: project_member + +:javascript + $('form.member-search-form').on('submit', function (event) { + event.preventDefault(); + Turbolinks.visit(this.action + '?' + $(this).serialize()); + }); diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 9fc4be583cc..29225a36364 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,36 +1,21 @@ - page_title "Members" = render "header_title" +- @blank_container = true -.gray-content-block.top-block - .clearfix.js-toggle-container - = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do - .form-group - = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input', spellcheck: false } - = button_tag 'Search', class: 'btn' - - - if can?(current_user, :admin_project_member, @project) - %span.pull-right - = button_tag class: 'btn btn-new btn-grouped js-toggle-button', type: 'button' do - Add members - %i.fa.fa-chevron-down - = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do - Import members - - .js-toggle-content.hide.new-group-member-holder +.project-members-page + - if can?(current_user, :admin_project_member, @project) + .panel.panel-default + .panel-heading + Add new user to project + .pull-right + = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do + Import members + .panel-body + %p.light + Users with access to this project are listed below. = render "new_project_member" -%p.prepend-top-default.light - Users with access to this project are listed below. - Read more about project permissions - %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" - -= render "team", members: @project_members - -- if @group - = render "group_members", members: @group_members + = render "team", members: @project_members -:javascript - $('form.member-search-form').on('submit', function (event) { - event.preventDefault(); - Turbolinks.visit(this.action + '?' + $(this).serialize()); - }); + - if @group + = render "group_members", members: @group_members -- cgit v1.2.1 From ea7467d2be0e367ed1ec6df656cab059a9db6da0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 22:27:01 +0100 Subject: Refactor group members tests a bit Signed-off-by: Dmitriy Zaporozhets --- features/groups.feature | 3 --- features/project/team_management.feature | 6 ++---- features/steps/groups.rb | 29 +++++++++++++---------------- features/steps/project/team_management.rb | 4 ---- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/features/groups.feature b/features/groups.feature index 938e658f2a9..65ced5c529d 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -55,7 +55,6 @@ Feature: Groups Scenario: Add user to group Given gitlab user "Mike" When I visit group "Owned" members page - And I click link "Add members" When I select "Mike" as "Reporter" Then I should see "Mike" in team list as "Reporter" @@ -63,14 +62,12 @@ Feature: Groups Scenario: Ignore add user to group when is already Owner Given gitlab user "Mike" When I visit group "Owned" members page - And I click link "Add members" When I select "Mike" as "Reporter" Then I should see "Mike" in team list as "Owner" @javascript Scenario: Invite user to group When I visit group "Owned" members page - And I click link "Add members" When I select "sjobs@apple.com" as "Reporter" Then I should see "sjobs@apple.com" in team list as invited "Reporter" diff --git a/features/project/team_management.feature b/features/project/team_management.feature index 09a7df59df6..06fb45c8bde 100644 --- a/features/project/team_management.feature +++ b/features/project/team_management.feature @@ -13,14 +13,12 @@ Feature: Project Team Management @javascript Scenario: Add user to project - Given I click link "Add members" - And I select "Mike" as "Reporter" + When I select "Mike" as "Reporter" Then I should see "Mike" in team list as "Reporter" @javascript Scenario: Invite user to project - Given I click link "Add members" - And I select "sjobs@apple.com" as "Reporter" + When I select "sjobs@apple.com" as "Reporter" Then I should see "sjobs@apple.com" in team list as invited "Reporter" @javascript diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 7c991af4c2b..5de54d9b1ee 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -13,10 +13,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps create(:user, name: "Mike") end - step 'I click link "Add members"' do - find(:css, 'button.btn-new').click - end - step 'I should see group "Owned"' do expect(page).to have_content '@owned' end @@ -60,14 +56,14 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'I should see "Mike" in team list as "Reporter"' do - page.within '.well-list' do + page.within '.content-list' do expect(page).to have_content('Mike') expect(page).to have_content('Reporter') end end step 'I should see "Mike" in team list as "Owner"' do - page.within '.well-list' do + page.within '.content-list' do expect(page).to have_content('Mike') expect(page).to have_content('Owner') end @@ -83,7 +79,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do - page.within '.well-list' do + page.within '.content-list' do expect(page).to have_content('sjobs@apple.com') expect(page).to have_content('invited') expect(page).to have_content('Reporter') @@ -114,32 +110,29 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I select user "Mary Jane" from list with role "Reporter"' do user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") - click_button 'Add members' + page.within ".users-group-form" do select2(user.id, from: "#user_ids", multiple: true) select "Reporter", from: "access_level" end + click_button "Add users to group" end step 'I should see user "John Doe" in team list' do - projects_with_access = find(".panel .well-list") - expect(projects_with_access).to have_content("John Doe") + expect(group_members_list).to have_content("John Doe") end step 'I should not see user "John Doe" in team list' do - projects_with_access = find(".panel .well-list") - expect(projects_with_access).not_to have_content("John Doe") + expect(group_members_list).not_to have_content("John Doe") end step 'I should see user "Mary Jane" in team list' do - projects_with_access = find(".panel .well-list") - expect(projects_with_access).to have_content("Mary Jane") + expect(group_members_list).to have_content("Mary Jane") end step 'I should not see user "Mary Jane" in team list' do - projects_with_access = find(".panel .well-list") - expect(projects_with_access).not_to have_content("Mary Jane") + expect(group_members_list).not_to have_content("Mary Jane") end step 'project from group "Owned" has issues assigned to me' do @@ -401,4 +394,8 @@ class Spinach::Features::Groups < Spinach::FeatureSteps author: current_user, milestone: milestone2_project3 end + + def group_members_list + find(".panel .content-list") + end end diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb index 97d63016458..caad52def79 100644 --- a/features/steps/project/team_management.rb +++ b/features/steps/project/team_management.rb @@ -15,10 +15,6 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps expect(page).to have_content(user.username) end - step 'I click link "Add members"' do - find(:css, 'button.btn-new').click - end - step 'I select "Mike" as "Reporter"' do user = User.find_by(name: "Mike") -- cgit v1.2.1 From 561634c78f3ab7967709196e8cca39a256d27196 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 25 Nov 2015 10:24:08 +0100 Subject: Refactor group steps Signed-off-by: Dmitriy Zaporozhets --- features/steps/groups.rb | 77 ++++++++++-------------------------------------- 1 file changed, 16 insertions(+), 61 deletions(-) diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 5de54d9b1ee..d99417d178e 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -332,67 +332,22 @@ class Spinach::Features::Groups < Spinach::FeatureSteps def group_milestone group = owned_group - @project1 = create :project, - group: group - project2 = create :project, - path: 'gitlab-ci', - group: group - @project3 = create :project, - path: 'cookbook-gitlab', - group: group - milestone1_project1 = create :milestone, - title: "Version 7.2", - project: @project1 - milestone1_project2 = create :milestone, - title: "Version 7.2", - project: project2 - create :milestone, - title: "Version 7.2", - project: @project3 - milestone2_project1 = create :milestone, - title: "GL-113", - project: @project1 - milestone2_project2 = create :milestone, - title: "GL-113", - project: project2 - milestone2_project3 = create :milestone, - title: "GL-113", - project: @project3, - due_date: '2114-08-20', - description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry' - @issue1 = create :issue, - project: @project1, - assignee: current_user, - author: current_user, - milestone: milestone2_project1 - create :issue, - project: project2, - assignee: current_user, - author: current_user, - milestone: milestone1_project2 - create :issue, - project: @project3, - assignee: current_user, - author: current_user, - milestone: milestone1_project1 - create :merge_request, - source_project: @project1, - target_project: @project1, - assignee: current_user, - author: current_user, - milestone: milestone2_project1 - create :merge_request, - source_project: project2, - target_project: project2, - assignee: current_user, - author: current_user, - milestone: milestone2_project2 - @mr3 = create :merge_request, - source_project: @project3, - target_project: @project3, - assignee: current_user, - author: current_user, - milestone: milestone2_project3 + %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path| + project = create :project, path: path, group: group + milestone = create :milestone, title: "Version 7.2", project: project + create :issue, + project: project, + assignee: current_user, + author: current_user, + milestone: milestone + + milestone = create :milestone, title: "GL-113", project: project + create :issue, + project: project, + assignee: current_user, + author: current_user, + milestone: milestone + end end def group_members_list -- cgit v1.2.1 From d262daa4f0f425ddda189ae9e2eb21bbf287f21a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 25 Nov 2015 11:44:39 +0100 Subject: Split group feature tests Signed-off-by: Dmitriy Zaporozhets --- app/views/groups/group_members/index.html.haml | 2 +- app/views/projects/project_members/_team.html.haml | 2 +- features/group/members.feature | 105 ++++++++++ features/group/milestones.feature | 30 +++ features/groups.feature | 129 ------------ features/steps/group/members.rb | 147 ++++++++++++++ features/steps/group/milestones.rb | 87 ++++++++ features/steps/groups.rb | 226 +-------------------- features/steps/shared/group.rb | 4 + features/steps/shared/user.rb | 4 + 10 files changed, 380 insertions(+), 356 deletions(-) create mode 100644 features/group/members.feature create mode 100644 features/group/milestones.feature create mode 100644 features/steps/group/members.rb create mode 100644 features/steps/group/milestones.rb diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index c83766e729a..335bf036074 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -24,7 +24,7 @@ = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false } - = button_tag class: 'btn' do + = button_tag class: 'btn', title: 'Search' do = icon("search") %ul.content-list - @members.each do |member| diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index 8f4c6134261..ccddab13aaf 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -8,7 +8,7 @@ = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false } - = button_tag class: 'btn' do + = button_tag class: 'btn', title: 'Search' do = icon("search") %ul.content-list - members.each do |project_member| diff --git a/features/group/members.feature b/features/group/members.feature new file mode 100644 index 00000000000..1f9514bac39 --- /dev/null +++ b/features/group/members.feature @@ -0,0 +1,105 @@ +Feature: Group Members + Background: + Given I sign in as "John Doe" + And "John Doe" is owner of group "Owned" + And "John Doe" is guest of group "Guest" + + @javascript + Scenario: I should add user to group "Owned" + Given User "Mary Jane" exists + When I visit group "Owned" members page + And I select user "Mary Jane" from list with role "Reporter" + Then I should see user "Mary Jane" in team list + + @javascript + Scenario: Add user to group + Given gitlab user "Mike" + When I visit group "Owned" members page + When I select "Mike" as "Reporter" + Then I should see "Mike" in team list as "Reporter" + + @javascript + Scenario: Ignore add user to group when is already Owner + Given gitlab user "Mike" + When I visit group "Owned" members page + When I select "Mike" as "Reporter" + Then I should see "Mike" in team list as "Owner" + + @javascript + Scenario: Invite user to group + When I visit group "Owned" members page + When I select "sjobs@apple.com" as "Reporter" + Then I should see "sjobs@apple.com" in team list as invited "Reporter" + + @javascript + Scenario: Edit group member permissions + Given "Mary Jane" is guest of group "Owned" + And I visit group "Owned" members page + When I change the "Mary Jane" role to "Developer" + Then I should see "Mary Jane" as "Developer" + + # Leave + + @javascript + Scenario: Owner should be able to remove himself from group if he is not the last owner + Given "Mary Jane" is owner of group "Owned" + When I visit group "Owned" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + When I click on the "Remove User From Group" button for "John Doe" + And I visit group "Owned" members page + Then I should not see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + + @javascript + Scenario: Owner should not be able to remove himself from group if he is the last owner + Given "Mary Jane" is guest of group "Owned" + When I visit group "Owned" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + Then I should not see the "Remove User From Group" button for "John Doe" + + @javascript + Scenario: Guest should be able to remove himself from group + Given "Mary Jane" is guest of group "Guest" + When I visit group "Guest" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + When I click on the "Remove User From Group" button for "John Doe" + When I visit group "Guest" members page + Then I should not see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + + @javascript + Scenario: Guest should be able to remove himself from group even if he is the only user in the group + When I visit group "Guest" members page + Then I should see user "John Doe" in team list + When I click on the "Remove User From Group" button for "John Doe" + When I visit group "Guest" members page + Then I should not see user "John Doe" in team list + + # Remove others + + Scenario: Owner should be able to remove other users from group + Given "Mary Jane" is owner of group "Owned" + When I visit group "Owned" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + When I click on the "Remove User From Group" button for "Mary Jane" + When I visit group "Owned" members page + Then I should see user "John Doe" in team list + Then I should not see user "Mary Jane" in team list + + Scenario: Guest should not be able to remove other users from group + Given "Mary Jane" is guest of group "Guest" + When I visit group "Guest" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + Then I should not see the "Remove User From Group" button for "Mary Jane" + + Scenario: Search member by name + Given "Mary Jane" is guest of group "Guest" + And I visit group "Guest" members page + When I search for 'Mary' member + Then I should see user "Mary Jane" in team list + Then I should not see user "John Doe" in team list diff --git a/features/group/milestones.feature b/features/group/milestones.feature new file mode 100644 index 00000000000..62ea66a783c --- /dev/null +++ b/features/group/milestones.feature @@ -0,0 +1,30 @@ +Feature: Group Milestones + Background: + Given I sign in as "John Doe" + And "John Doe" is owner of group "Owned" + + Scenario: I should see group "Owned" milestone index page with no milestones + When I visit group "Owned" page + And I click on group milestones + Then I should see group milestones index page has no milestones + + Scenario: I should see group "Owned" milestone index page with milestones + Given Group has projects with milestones + When I visit group "Owned" page + And I click on group milestones + Then I should see group milestones index page with milestones + + Scenario: I should see group "Owned" milestone show page + Given Group has projects with milestones + When I visit group "Owned" page + And I click on group milestones + And I click on one group milestone + Then I should see group milestone with descriptions and expiry date + And I should see group milestone with all issues and MRs assigned to that milestone + + Scenario: Create multiple milestones with one form + Given I visit group "Owned" milestones page + And I click new milestone button + And I fill milestone name + When I press create mileston button + Then milestone in each project should be created diff --git a/features/groups.feature b/features/groups.feature index 65ced5c529d..c803e952980 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -2,7 +2,6 @@ Feature: Groups Background: Given I sign in as "John Doe" And "John Doe" is owner of group "Owned" - And "John Doe" is guest of group "Guest" Scenario: I should have back to group button When I visit group "Owned" page @@ -24,13 +23,6 @@ Feature: Groups When I visit group "Owned" merge requests page Then I should see merge requests from group "Owned" assigned to me - @javascript - Scenario: I should add user to projects in group "Owned" - Given User "Mary Jane" exists - When I visit group "Owned" members page - And I select user "Mary Jane" from list with role "Reporter" - Then I should see user "Mary Jane" in team list - Scenario: I should see edit group "Owned" page When I visit group "Owned" settings page And I change group "Owned" name to "new-name" @@ -51,127 +43,6 @@ Feature: Groups Then I should not see group "Owned" avatar And I should not see the "Remove avatar" button - @javascript - Scenario: Add user to group - Given gitlab user "Mike" - When I visit group "Owned" members page - When I select "Mike" as "Reporter" - Then I should see "Mike" in team list as "Reporter" - - @javascript - Scenario: Ignore add user to group when is already Owner - Given gitlab user "Mike" - When I visit group "Owned" members page - When I select "Mike" as "Reporter" - Then I should see "Mike" in team list as "Owner" - - @javascript - Scenario: Invite user to group - When I visit group "Owned" members page - When I select "sjobs@apple.com" as "Reporter" - Then I should see "sjobs@apple.com" in team list as invited "Reporter" - - @javascript - Scenario: Edit group member permissions - Given "Mary Jane" is guest of group "Owned" - And I visit group "Owned" members page - When I change the "Mary Jane" role to "Developer" - Then I should see "Mary Jane" as "Developer" - - # Leave - - @javascript - Scenario: Owner should be able to remove himself from group if he is not the last owner - Given "Mary Jane" is owner of group "Owned" - When I visit group "Owned" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - When I click on the "Remove User From Group" button for "John Doe" - And I visit group "Owned" members page - Then I should not see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - - @javascript - Scenario: Owner should not be able to remove himself from group if he is the last owner - Given "Mary Jane" is guest of group "Owned" - When I visit group "Owned" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - Then I should not see the "Remove User From Group" button for "John Doe" - - @javascript - Scenario: Guest should be able to remove himself from group - Given "Mary Jane" is guest of group "Guest" - When I visit group "Guest" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - When I click on the "Remove User From Group" button for "John Doe" - When I visit group "Guest" members page - Then I should not see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - - @javascript - Scenario: Guest should be able to remove himself from group even if he is the only user in the group - When I visit group "Guest" members page - Then I should see user "John Doe" in team list - When I click on the "Remove User From Group" button for "John Doe" - When I visit group "Guest" members page - Then I should not see user "John Doe" in team list - - # Remove others - - Scenario: Owner should be able to remove other users from group - Given "Mary Jane" is owner of group "Owned" - When I visit group "Owned" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - When I click on the "Remove User From Group" button for "Mary Jane" - When I visit group "Owned" members page - Then I should see user "John Doe" in team list - Then I should not see user "Mary Jane" in team list - - Scenario: Guest should not be able to remove other users from group - Given "Mary Jane" is guest of group "Guest" - When I visit group "Guest" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - Then I should not see the "Remove User From Group" button for "Mary Jane" - - Scenario: Search member by name - Given "Mary Jane" is guest of group "Guest" - And I visit group "Guest" members page - When I search for 'Mary' member - Then I should see user "Mary Jane" in team list - Then I should not see user "John Doe" in team list - - # Group milestones - - Scenario: I should see group "Owned" milestone index page with no milestones - When I visit group "Owned" page - And I click on group milestones - Then I should see group milestones index page has no milestones - - Scenario: I should see group "Owned" milestone index page with milestones - Given Group has projects with milestones - When I visit group "Owned" page - And I click on group milestones - Then I should see group milestones index page with milestones - - Scenario: I should see group "Owned" milestone show page - Given Group has projects with milestones - When I visit group "Owned" page - And I click on group milestones - And I click on one group milestone - Then I should see group milestone with descriptions and expiry date - And I should see group milestone with all issues and MRs assigned to that milestone - - Scenario: Create multiple milestones with one form - Given I visit group "Owned" milestones page - And I click new milestone button - And I fill milestone name - When I press create mileston button - Then milestone in each project should be created - # Group projects in settings Scenario: I should see all projects in the project list in settings Given Group "Owned" has archived project diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb new file mode 100644 index 00000000000..0706df3aec5 --- /dev/null +++ b/features/steps/group/members.rb @@ -0,0 +1,147 @@ +class Spinach::Features::GroupMembers < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedGroup + include SharedUser + include Select2Helper + + step 'I select "Mike" as "Reporter"' do + user = User.find_by(name: "Mike") + + page.within ".users-group-form" do + select2(user.id, from: "#user_ids", multiple: true) + select "Reporter", from: "access_level" + end + + click_button "Add users to group" + end + + step 'I select "Mike" as "Master"' do + user = User.find_by(name: "Mike") + + page.within ".users-group-form" do + select2(user.id, from: "#user_ids", multiple: true) + select "Master", from: "access_level" + end + + click_button "Add users to group" + end + + step 'I should see "Mike" in team list as "Reporter"' do + page.within '.content-list' do + expect(page).to have_content('Mike') + expect(page).to have_content('Reporter') + end + end + + step 'I should see "Mike" in team list as "Owner"' do + page.within '.content-list' do + expect(page).to have_content('Mike') + expect(page).to have_content('Owner') + end + end + + step 'I select "sjobs@apple.com" as "Reporter"' do + page.within ".users-group-form" do + select2("sjobs@apple.com", from: "#user_ids", multiple: true) + select "Reporter", from: "access_level" + end + + click_button "Add users to group" + end + + step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do + page.within '.content-list' do + expect(page).to have_content('sjobs@apple.com') + expect(page).to have_content('invited') + expect(page).to have_content('Reporter') + end + end + + step 'I select user "Mary Jane" from list with role "Reporter"' do + user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") + + page.within ".users-group-form" do + select2(user.id, from: "#user_ids", multiple: true) + select "Reporter", from: "access_level" + end + + click_button "Add users to group" + end + + step 'I should see user "John Doe" in team list' do + expect(group_members_list).to have_content("John Doe") + end + + step 'I should not see user "John Doe" in team list' do + expect(group_members_list).not_to have_content("John Doe") + end + + step 'I should see user "Mary Jane" in team list' do + expect(group_members_list).to have_content("Mary Jane") + end + + step 'I should not see user "Mary Jane" in team list' do + expect(group_members_list).not_to have_content("Mary Jane") + end + + step 'I click on the "Remove User From Group" button for "John Doe"' do + find(:css, 'li', text: "John Doe").find(:css, 'a.btn-remove').click + # poltergeist always confirms popups. + end + + step 'I click on the "Remove User From Group" button for "Mary Jane"' do + find(:css, 'li', text: "Mary Jane").find(:css, 'a.btn-remove').click + # poltergeist always confirms popups. + end + + step 'I should not see the "Remove User From Group" button for "John Doe"' do + expect(find(:css, 'li', text: "John Doe")).not_to have_selector(:css, 'a.btn-remove') + # poltergeist always confirms popups. + end + + step 'I should not see the "Remove User From Group" button for "Mary Jane"' do + expect(find(:css, 'li', text: "Mary Jane")).not_to have_selector(:css, 'a.btn-remove') + # poltergeist always confirms popups. + end + + step 'I search for \'Mary\' member' do + page.within '.member-search-form' do + fill_in 'search', with: 'Mary' + click_button 'Search' + end + end + + step 'I change the "Mary Jane" role to "Developer"' do + member = mary_jane_member + + page.within "#group_member_#{member.id}" do + find(".js-toggle-button").click + page.within "#edit_group_member_#{member.id}" do + select 'Developer', from: 'group_member_access_level' + click_on 'Save' + end + end + end + + step 'I should see "Mary Jane" as "Developer"' do + member = mary_jane_member + + page.within "#group_member_#{member.id}" do + page.within '.member-access-level' do + expect(page).to have_content "Developer" + end + end + end + + private + + def mary_jane_member + user = User.find_by(name: "Mary Jane") + owned_group.members.find_by(user_id: user.id) + end + + def group_members_list + find(".panel .content-list") + end +end diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb new file mode 100644 index 00000000000..4c4038d44cc --- /dev/null +++ b/features/steps/group/milestones.rb @@ -0,0 +1,87 @@ +class Spinach::Features::GroupMilestones < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedGroup + include SharedUser + + step 'I click on group milestones' do + click_link 'Milestones' + end + + step 'I should see group milestones index page has no milestones' do + expect(page).to have_content('No milestones to show') + end + + step 'Group has projects with milestones' do + group_milestone + end + + step 'I should see group milestones index page with milestones' do + expect(page).to have_content('Version 7.2') + expect(page).to have_content('GL-113') + expect(page).to have_link('3 Issues', href: issues_group_path("owned", milestone_title: "Version 7.2")) + expect(page).to have_link('0 Merge Requests', href: merge_requests_group_path("owned", milestone_title: "GL-113")) + end + + step 'I click on one group milestone' do + click_link 'GL-113' + end + + step 'I should see group milestone with descriptions and expiry date' do + expect(page).to have_content('expires at Aug 20, 2114') + end + + step 'I should see group milestone with all issues and MRs assigned to that milestone' do + expect(page).to have_content('Milestone GL-113') + expect(page).to have_content('Progress: 0 closed – 3 open') + issue = Milestone.find_by(name: 'GL-113').issues.first + expect(page).to have_link(issue.title, href: namespace_project_issue_path(issue.project.namespace, issue.project, issue)) + end + + step 'I fill milestone name' do + fill_in 'milestone_title', with: 'v2.9.0' + end + + step 'I click new milestone button' do + click_link "New Milestone" + end + + step 'I press create mileston button' do + click_button "Create Milestone" + end + + step 'milestone in each project should be created' do + group = Group.find_by(name: 'Owned') + expect(page).to have_content "Milestone v2.9.0" + expect(group.projects).to be_present + + group.projects.each do |project| + expect(page).to have_content project.name + end + end + + private + + def group_milestone + group = owned_group + + %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path| + project = create :project, path: path, group: group + milestone = create :milestone, title: "Version 7.2", project: project + create :issue, + project: project, + assignee: current_user, + author: current_user, + milestone: milestone + + milestone = create :milestone, title: "GL-113", project: project, + due_date: '2114-08-20', description: 'Lorem Ipsum is simply dummy text' + + create :issue, + project: project, + assignee: current_user, + author: current_user, + milestone: milestone + end + end +end diff --git a/features/steps/groups.rb b/features/steps/groups.rb index d99417d178e..f5e3fee61c0 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -3,16 +3,11 @@ class Spinach::Features::Groups < Spinach::FeatureSteps include SharedPaths include SharedGroup include SharedUser - include Select2Helper step 'I should see back to dashboard button' do expect(page).to have_content 'Go to dashboard' end - step 'gitlab user "Mike"' do - create(:user, name: "Mike") - end - step 'I should see group "Owned"' do expect(page).to have_content '@owned' end @@ -33,59 +28,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).to have_content 'Public-project' end - step 'I select "Mike" as "Reporter"' do - user = User.find_by(name: "Mike") - - page.within ".users-group-form" do - select2(user.id, from: "#user_ids", multiple: true) - select "Reporter", from: "access_level" - end - - click_button "Add users to group" - end - - step 'I select "Mike" as "Master"' do - user = User.find_by(name: "Mike") - - page.within ".users-group-form" do - select2(user.id, from: "#user_ids", multiple: true) - select "Master", from: "access_level" - end - - click_button "Add users to group" - end - - step 'I should see "Mike" in team list as "Reporter"' do - page.within '.content-list' do - expect(page).to have_content('Mike') - expect(page).to have_content('Reporter') - end - end - - step 'I should see "Mike" in team list as "Owner"' do - page.within '.content-list' do - expect(page).to have_content('Mike') - expect(page).to have_content('Owner') - end - end - - step 'I select "sjobs@apple.com" as "Reporter"' do - page.within ".users-group-form" do - select2("sjobs@apple.com", from: "#user_ids", multiple: true) - select "Reporter", from: "access_level" - end - - click_button "Add users to group" - end - - step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do - page.within '.content-list' do - expect(page).to have_content('sjobs@apple.com') - expect(page).to have_content('invited') - expect(page).to have_content('Reporter') - end - end - step 'I should see group "Owned" projects list' do owned_group.projects.each do |project| expect(page).to have_link project.name @@ -108,33 +50,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end end - step 'I select user "Mary Jane" from list with role "Reporter"' do - user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") - - page.within ".users-group-form" do - select2(user.id, from: "#user_ids", multiple: true) - select "Reporter", from: "access_level" - end - - click_button "Add users to group" - end - - step 'I should see user "John Doe" in team list' do - expect(group_members_list).to have_content("John Doe") - end - - step 'I should not see user "John Doe" in team list' do - expect(group_members_list).not_to have_content("John Doe") - end - - step 'I should see user "Mary Jane" in team list' do - expect(group_members_list).to have_content("Mary Jane") - end - - step 'I should not see user "Mary Jane" in team list' do - expect(group_members_list).not_to have_content("Mary Jane") - end - step 'project from group "Owned" has issues assigned to me' do create :issue, project: project, @@ -196,67 +111,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).not_to have_link("Remove avatar") end - step 'I click on the "Remove User From Group" button for "John Doe"' do - find(:css, 'li', text: "John Doe").find(:css, 'a.btn-remove').click - # poltergeist always confirms popups. - end - - step 'I click on the "Remove User From Group" button for "Mary Jane"' do - find(:css, 'li', text: "Mary Jane").find(:css, 'a.btn-remove').click - # poltergeist always confirms popups. - end - - step 'I should not see the "Remove User From Group" button for "John Doe"' do - expect(find(:css, 'li', text: "John Doe")).not_to have_selector(:css, 'a.btn-remove') - # poltergeist always confirms popups. - end - - step 'I should not see the "Remove User From Group" button for "Mary Jane"' do - expect(find(:css, 'li', text: "Mary Jane")).not_to have_selector(:css, 'a.btn-remove') - # poltergeist always confirms popups. - end - - step 'I search for \'Mary\' member' do - page.within '.member-search-form' do - fill_in 'search', with: 'Mary' - click_button 'Search' - end - end - - step 'I click on group milestones' do - click_link 'Milestones' - end - - step 'I should see group milestones index page has no milestones' do - expect(page).to have_content('No milestones to show') - end - - step 'Group has projects with milestones' do - group_milestone - end - - step 'I should see group milestones index page with milestones' do - expect(page).to have_content('Version 7.2') - expect(page).to have_content('GL-113') - expect(page).to have_link('2 Issues', href: issues_group_path("owned", milestone_title: "Version 7.2")) - expect(page).to have_link('3 Merge Requests', href: merge_requests_group_path("owned", milestone_title: "GL-113")) - end - - step 'I click on one group milestone' do - click_link 'GL-113' - end - - step 'I should see group milestone with descriptions and expiry date' do - expect(page).to have_content('expires at Aug 20, 2114') - end - - step 'I should see group milestone with all issues and MRs assigned to that milestone' do - expect(page).to have_content('Milestone GL-113') - expect(page).to have_content('Progress: 0 closed – 4 open') - expect(page).to have_link(@issue1.title, href: namespace_project_issue_path(@project1.namespace, @project1, @issue1)) - expect(page).to have_link(@mr3.title, href: namespace_project_merge_request_path(@project3.namespace, @project3, @mr3)) - end - step 'Group "Owned" has archived project' do group = Group.find_by(name: 'Owned') create(:project, namespace: group, archived: true, path: "archived-project") @@ -266,60 +120,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived') end - step 'I fill milestone name' do - fill_in 'milestone_title', with: 'v2.9.0' - end - - step 'I click new milestone button' do - click_link "New Milestone" - end - - step 'I press create mileston button' do - click_button "Create Milestone" - end - - step 'milestone in each project should be created' do - group = Group.find_by(name: 'Owned') - expect(page).to have_content "Milestone v2.9.0" - expect(group.projects).to be_present - - group.projects.each do |project| - expect(page).to have_content project.name - end - end - - step 'I change the "Mary Jane" role to "Developer"' do - member = mary_jane_member - - page.within "#group_member_#{member.id}" do - find(".js-toggle-button").click - page.within "#edit_group_member_#{member.id}" do - select 'Developer', from: 'group_member_access_level' - click_on 'Save' - end - end - end - - step 'I should see "Mary Jane" as "Developer"' do - member = mary_jane_member - - page.within "#group_member_#{member.id}" do - page.within '.member-access-level' do - expect(page).to have_content "Developer" - end - end - end - - protected - - def owned_group - @owned_group ||= Group.find_by(name: "Owned") - end - - def mary_jane_member - user = User.find_by(name: "Mary Jane") - owned_group.members.find_by(user_id: user.id) - end + private def assigned_to_me(key) project.send(key).where(assignee_id: current_user.id) @@ -328,29 +129,4 @@ class Spinach::Features::Groups < Spinach::FeatureSteps def project owned_group.projects.first end - - def group_milestone - group = owned_group - - %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path| - project = create :project, path: path, group: group - milestone = create :milestone, title: "Version 7.2", project: project - create :issue, - project: project, - assignee: current_user, - author: current_user, - milestone: milestone - - milestone = create :milestone, title: "GL-113", project: project - create :issue, - project: project, - assignee: current_user, - author: current_user, - milestone: milestone - end - end - - def group_members_list - find(".panel .content-list") - end end diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb index 83a04576973..58581653f28 100644 --- a/features/steps/shared/group.rb +++ b/features/steps/shared/group.rb @@ -41,4 +41,8 @@ module SharedGroup project.team << [user, :master] @project_count += 1 end + + def owned_group + @owned_group ||= Group.find_by(name: "Owned") + end end diff --git a/features/steps/shared/user.rb b/features/steps/shared/user.rb index fc1e8d6e889..250cc5b94f3 100644 --- a/features/steps/shared/user.rb +++ b/features/steps/shared/user.rb @@ -9,6 +9,10 @@ module SharedUser user_exists("Mary Jane", { username: "mary_jane" }) end + step 'gitlab user "Mike"' do + create(:user, name: "Mike") + end + protected def user_exists(name, options = {}) -- cgit v1.2.1 From acad9d67c7af881c1e4cce1e20ffd9c8f65f3029 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 25 Nov 2015 12:27:34 +0100 Subject: Fix rubocop complain Signed-off-by: Dmitriy Zaporozhets --- features/steps/group/milestones.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index 4c4038d44cc..6e57b16ccb6 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -74,8 +74,11 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps author: current_user, milestone: milestone - milestone = create :milestone, title: "GL-113", project: project, - due_date: '2114-08-20', description: 'Lorem Ipsum is simply dummy text' + milestone = create :milestone, + title: "GL-113", + project: project, + due_date: '2114-08-20', + description: 'Lorem Ipsum is simply dummy text' create :issue, project: project, -- cgit v1.2.1 From 18a5f91ed63322af7f2fed29a6cc9a4c1e96978d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 25 Nov 2015 13:08:47 +0100 Subject: Bump gitlab-shell to 2.6.8 Signed-off-by: Dmitriy Zaporozhets --- GITLAB_SHELL_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index e261122d5c4..743af5e1251 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.6.7 +2.6.8 -- cgit v1.2.1 From 82ff9e65939d2da279b684cf378ca33e60eed66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurens=20St=C3=B6tzel?= Date: Wed, 25 Nov 2015 12:27:07 +0000 Subject: Correction of markdown in SSH docs --- doc/ssh/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/ssh/README.md b/doc/ssh/README.md index 9753504ac8b..fe5b45dd432 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -15,7 +15,8 @@ Note: It is a best practice to use a password for an SSH key, but it is not required and you can skip creating a password by pressing enter. Note that the password you choose here can't be altered or retrieved. -To generate a new SSH key, use the following commandGitLab```bash +To generate a new SSH key, use the following command: +```bash ssh-keygen -t rsa -C "$your_email" ``` This command will prompt you for a location and filename to store the key @@ -108,4 +109,4 @@ Note in the gitlab.com example above a username was specified to override the de Due to the wide variety of SSH clients and their very large number of configuration options, further explanation of these topics is beyond the scope of this document. Public SSH keys need to be unique, as they will bind to your account. Your SSH key is the only identifier you'll -have when pushing code via SSH. That's why it needs to uniquely map to a single user. +have when pushing code via SSH. That's why it needs to uniquely map to a single user. \ No newline at end of file -- cgit v1.2.1 From 7da750cc30308173e6f21f570ecd5902dfd3d89d Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 25 Nov 2015 13:27:31 +0100 Subject: Specs for links in email notifications for Gmail Actions. --- spec/mailers/notify_spec.rb | 94 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 47863d54579..52bad36ae1d 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -77,6 +77,14 @@ describe Notify do end end + shared_examples 'it should have Gmail Actions links' do + it { is_expected.to have_body_text /ViewAction/ } + end + + shared_examples 'it should not have Gmail Actions links' do + it { is_expected.to_not have_body_text /ViewAction/ } + end + describe 'for new users, the email' do let(:example_site_path) { root_path } let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } @@ -87,6 +95,7 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it_behaves_like 'a new user email', new_user_address + it_behaves_like 'it should not have Gmail Actions links' it 'contains the password text' do is_expected.to have_body_text /Click here to set your password/ @@ -115,6 +124,7 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it_behaves_like 'a new user email', new_user_address + it_behaves_like 'it should not have Gmail Actions links' it 'should not contain the new user\'s password' do is_expected.not_to have_body_text /password/ @@ -127,6 +137,7 @@ describe Notify do subject { Notify.new_ssh_key_email(key.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' it 'is sent to the new user' do is_expected.to deliver_to key.user.email @@ -150,6 +161,8 @@ describe Notify do subject { Notify.new_email_email(email.id) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent to the new user' do is_expected.to deliver_to email.user.email end @@ -194,6 +207,7 @@ describe Notify do it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread', 'issue' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/ @@ -202,6 +216,10 @@ describe Notify do it 'contains a link to the new issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Issue/ + end end describe 'that are new with a description' do @@ -217,6 +235,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'it should have Gmail Actions links' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -239,6 +258,10 @@ describe Notify do it 'contains a link to the issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Issue/ + end end describe 'status changed' do @@ -246,6 +269,7 @@ describe Notify do subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'it should have Gmail Actions links' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -268,8 +292,11 @@ describe Notify do it 'contains a link to the issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end - end + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Issue/ + end + end end context 'for merge requests' do @@ -282,6 +309,7 @@ describe Notify do it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ @@ -302,14 +330,24 @@ describe Notify do it 'has the correct message-id set' do is_expected.to have_header 'Message-ID', "" end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'that are new with a description' do subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) } + it_behaves_like 'it should have Gmail Actions links' + it 'contains the description' do is_expected.to have_body_text /#{merge_request_with_description.description}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'that are reassigned' do @@ -317,6 +355,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -339,6 +378,10 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'status changed' do @@ -346,6 +389,8 @@ describe Notify do subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user) } it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -368,6 +413,10 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'that are merged' do @@ -375,6 +424,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' it 'is sent as the merge author' do sender = subject.header[:from].addrs[0] @@ -393,6 +443,10 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end end end @@ -403,6 +457,7 @@ describe Notify do subject { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /Project was moved/ @@ -424,13 +479,16 @@ describe Notify do subject { Notify.project_access_granted_email(project_member.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /Access to project was granted/ end + it 'contains name of project' do is_expected.to have_body_text /#{project.name}/ end + it 'contains new user role' do is_expected.to have_body_text /#{project_member.human_access}/ end @@ -445,6 +503,8 @@ describe Notify do end shared_examples 'a note email' do + it_behaves_like 'it should have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(note_author.name) @@ -469,6 +529,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'commit' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/ @@ -477,6 +538,10 @@ describe Notify do it 'contains a link to the commit' do is_expected.to have_body_text commit.short_id end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Commit/ + end end describe 'on a merge request' do @@ -488,6 +553,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ @@ -496,6 +562,10 @@ describe Notify do it 'contains a link to the merge request note' do is_expected.to have_body_text /#{note_on_merge_request_path}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'on an issue' do @@ -507,6 +577,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/ @@ -515,6 +586,10 @@ describe Notify do it 'contains a link to the issue note' do is_expected.to have_body_text /#{note_on_issue_path}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Issue/ + end end end end @@ -527,6 +602,7 @@ describe Notify do subject { Notify.group_access_granted_email(membership.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /Access to group was granted/ @@ -574,6 +650,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :create) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -600,6 +678,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :create) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -625,6 +705,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :delete) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -646,6 +728,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -671,6 +755,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, send_from_committer_email: send_from_committer_email) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -774,6 +860,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) } + it_behaves_like 'it should have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -799,5 +887,9 @@ describe Notify do it 'contains a link to the diff' do is_expected.to have_body_text /#{diff_path}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Commit/ + end end end -- cgit v1.2.1 From 56135927273cfd722872e893abde3728f3b21d38 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 25 Nov 2015 13:53:02 +0100 Subject: If action link is not found, return nil. --- app/helpers/emails_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index 45788ba95ac..41b5bd7be90 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -28,6 +28,8 @@ module EmailsHelper return "View #{action.humanize.singularize}" end end + + nil end def color_email_diff(diffcontent) -- cgit v1.2.1 From 5c0be319cc6d568cefd339be8bc5f80e157836b4 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 25 Nov 2015 13:59:03 +0100 Subject: Remove some repetition in notify spec. --- spec/mailers/notify_spec.rb | 93 ++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 61 deletions(-) diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 52bad36ae1d..d6796b07a5b 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -85,6 +85,24 @@ describe Notify do it { is_expected.to_not have_body_text /ViewAction/ } end + shared_examples 'it should show Gmail Actions View Issue link' do + it_behaves_like 'it should have Gmail Actions links' + + it { is_expected.to have_body_text /View Issue/ } + end + + shared_examples 'it should show Gmail Actions View Merge request link' do + it_behaves_like 'it should have Gmail Actions links' + + it { is_expected.to have_body_text /View Merge request/ } + end + + shared_examples 'it should show Gmail Actions View Commit link' do + it_behaves_like 'it should have Gmail Actions links' + + it { is_expected.to have_body_text /View Commit/ } + end + describe 'for new users, the email' do let(:example_site_path) { root_path } let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } @@ -207,7 +225,7 @@ describe Notify do it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread', 'issue' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Issue link' it 'has the correct subject' do is_expected.to have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/ @@ -216,15 +234,13 @@ describe Notify do it 'contains a link to the new issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Issue/ - end end describe 'that are new with a description' do subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) } + it_behaves_like 'it should show Gmail Actions View Issue link' + it 'contains the description' do is_expected.to have_body_text /#{issue_with_description.description}/ end @@ -235,7 +251,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'issue' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Issue link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -258,10 +274,6 @@ describe Notify do it 'contains a link to the issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Issue/ - end end describe 'status changed' do @@ -269,7 +281,7 @@ describe Notify do subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } it_behaves_like 'an answer to an existing thread', 'issue' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Issue link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -292,10 +304,6 @@ describe Notify do it 'contains a link to the issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Issue/ - end end end @@ -309,7 +317,7 @@ describe Notify do it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'has the correct subject' do is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ @@ -330,24 +338,16 @@ describe Notify do it 'has the correct message-id set' do is_expected.to have_header 'Message-ID', "" end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'that are new with a description' do subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) } - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'contains the description' do is_expected.to have_body_text /#{merge_request_with_description.description}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'that are reassigned' do @@ -355,7 +355,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -378,10 +378,6 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'status changed' do @@ -389,8 +385,7 @@ describe Notify do subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user) } it_behaves_like 'an answer to an existing thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' - + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -413,10 +408,6 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'that are merged' do @@ -424,7 +415,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'is sent as the merge author' do sender = subject.header[:from].addrs[0] @@ -443,10 +434,6 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end end end @@ -529,7 +516,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'commit' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Commit link' it 'has the correct subject' do is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/ @@ -538,10 +525,6 @@ describe Notify do it 'contains a link to the commit' do is_expected.to have_body_text commit.short_id end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Commit/ - end end describe 'on a merge request' do @@ -553,7 +536,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'has the correct subject' do is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ @@ -562,10 +545,6 @@ describe Notify do it 'contains a link to the merge request note' do is_expected.to have_body_text /#{note_on_merge_request_path}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'on an issue' do @@ -577,7 +556,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'issue' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Issue link' it 'has the correct subject' do is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/ @@ -586,10 +565,6 @@ describe Notify do it 'contains a link to the issue note' do is_expected.to have_body_text /#{note_on_issue_path}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Issue/ - end end end end @@ -860,7 +835,7 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) } - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Commit link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -887,9 +862,5 @@ describe Notify do it 'contains a link to the diff' do is_expected.to have_body_text /#{diff_path}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Commit/ - end end end -- cgit v1.2.1 From 40ff1318d29884e4d17e7e450d8a7633e5ac36a9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 25 Nov 2015 18:18:44 +0200 Subject: Rails update to 4.2.4 --- Gemfile | 7 +- Gemfile.lock | 246 ++++++++++++++++------------ app/models/sent_notification.rb | 5 +- app/workers/emails_on_push_worker.rb | 2 +- bin/ci/upgrade.rb | 0 bin/rails | 6 +- bin/rake | 9 +- bin/setup | 29 ++++ bin/upgrade.rb | 0 config/environment.rb | 2 +- config/environments/development.rb | 2 +- config/environments/production.rb | 2 +- config/environments/test.rb | 2 +- config/initializers/cookies_serializer.rb | 2 +- config/initializers/default_url_options.rb | 2 +- config/initializers/rack_attack.rb.example | 14 +- config/initializers/rack_lineprof.rb | 2 +- config/initializers/secret_token.rb | 8 +- config/initializers/session_store.rb | 4 +- config/initializers/sherlock.rb | 2 +- config/initializers/smtp_settings.rb.sample | 2 +- config/initializers/static_files.rb | 2 +- config/routes.rb | 2 +- db/schema.rb | 106 ++++++------ 24 files changed, 264 insertions(+), 194 deletions(-) mode change 100644 => 100755 bin/ci/upgrade.rb create mode 100755 bin/setup mode change 100644 => 100755 bin/upgrade.rb diff --git a/Gemfile b/Gemfile index 4762e2e223f..4a9f70a1297 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,9 @@ source "https://rubygems.org" -gem 'rails', '4.1.14' +gem 'rails', '4.2.4' + +# Responders respond_to and respond_with +gem 'responders', '~> 2.0' # Specify a sprockets version due to security issue # See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY @@ -98,6 +101,7 @@ gem 'org-ruby', '~> 0.9.12' gem 'creole', '~> 0.5.0' gem 'wikicloth', '0.8.1' gem 'asciidoctor', '~> 1.5.2' +gem 'net-ssh', '~> 3.0.1' # Diffs gem 'diffy', '~> 3.0.3' @@ -213,6 +217,7 @@ group :development do gem 'rerun', '~> 0.10.0' gem 'bullet', require: false gem 'rblineprof', platform: :mri, require: false + gem 'web-console', '~> 2.0' # Better errors handler gem 'better_errors', '~> 1.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index 46247dd88e2..e9460515051 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,31 +1,40 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (2.3.1) + CFPropertyList (2.3.2) RedCloth (4.2.9) ace-rails-ap (2.0.1) - actionmailer (4.1.14) - actionpack (= 4.1.14) - actionview (= 4.1.14) + actionmailer (4.2.4) + actionpack (= 4.2.4) + actionview (= 4.2.4) + activejob (= 4.2.4) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.14) - actionview (= 4.1.14) - activesupport (= 4.1.14) - rack (~> 1.5.2) + rails-dom-testing (~> 1.0, >= 1.0.5) + actionpack (4.2.4) + actionview (= 4.2.4) + activesupport (= 4.2.4) + rack (~> 1.6) rack-test (~> 0.6.2) - actionview (4.1.14) - activesupport (= 4.1.14) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (4.2.4) + activesupport (= 4.2.4) builder (~> 3.1) erubis (~> 2.7.0) - activemodel (4.1.14) - activesupport (= 4.1.14) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + activejob (4.2.4) + activesupport (= 4.2.4) + globalid (>= 0.3.0) + activemodel (4.2.4) + activesupport (= 4.2.4) builder (~> 3.1) - activerecord (4.1.14) - activemodel (= 4.1.14) - activesupport (= 4.1.14) - arel (~> 5.0.0) + activerecord (4.2.4) + activemodel (= 4.2.4) + activesupport (= 4.2.4) + arel (~> 6.0) activerecord-deprecated_finders (1.0.4) - activerecord-session_store (0.1.1) + activerecord-session_store (0.1.2) actionpack (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5) railties (>= 4.0.0, < 5) @@ -33,31 +42,31 @@ GEM activemodel (~> 4.0) activesupport (~> 4.0) rails-observers (~> 0.1.1) - activesupport (4.1.14) - i18n (~> 0.6, >= 0.6.9) + activesupport (4.2.4) + i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) - thread_safe (~> 0.1) + thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) acts-as-taggable-on (3.5.0) activerecord (>= 3.2, < 5) addressable (2.3.8) - after_commit_queue (1.1.0) - rails (>= 3.0) + after_commit_queue (1.3.0) + activerecord (>= 3.0) annotate (2.6.10) activerecord (>= 3.2, <= 4.3) rake (~> 10.4) - arel (5.0.1.20140414130214) + arel (6.0.3) asana (0.0.6) activeresource (>= 3.2.3) - asciidoctor (1.5.2) + asciidoctor (1.5.3) ast (2.1.0) astrolabe (1.3.1) parser (~> 2.2) attr_encrypted (1.3.4) encryptor (>= 1.3.0) attr_required (1.0.0) - autoprefixer-rails (5.2.1.2) + autoprefixer-rails (6.1.1) execjs json awesome_print (1.2.0) @@ -85,15 +94,15 @@ GEM ruby_parser (~> 3.5.0) sass (~> 3.0) terminal-table (~> 1.4) - browser (1.0.0) + browser (1.0.1) builder (3.2.2) - bullet (4.14.9) + bullet (4.14.10) activesupport (>= 3.0.0) uniform_notifier (~> 1.9.0) bundler-audit (0.4.0) bundler (~> 1.2) thor (~> 0.18) - byebug (6.0.2) + byebug (8.2.0) cal-heatmap-rails (0.0.1) capybara (2.4.4) mime-types (>= 1.16) @@ -108,10 +117,25 @@ GEM activemodel (>= 3.2.0) activesupport (>= 3.2.0) json (>= 1.7) - celluloid (0.16.0) - timers (~> 4.0.0) + celluloid (0.17.2) + celluloid-essentials + celluloid-extras + celluloid-fsm + celluloid-pool + celluloid-supervision + timers (>= 4.1.1) + celluloid-essentials (0.20.5) + timers (>= 4.1.1) + celluloid-extras (0.20.5) + timers (>= 4.1.1) + celluloid-fsm (0.20.5) + timers (>= 4.1.1) + celluloid-pool (0.20.5) + timers (>= 4.1.1) + celluloid-supervision (0.20.5) + timers (>= 4.1.1) charlock_holmes (0.7.3) - chunky_png (1.3.4) + chunky_png (1.3.5) cliver (0.3.2) coderay (1.1.0) coercible (1.0.0) @@ -122,15 +146,16 @@ GEM coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.9.1.1) + coffee-script-source (1.10.0) colorize (0.7.7) connection_pool (2.2.0) - coveralls (0.8.2) + coveralls (0.8.9) json (~> 1.8) rest-client (>= 1.6.8, < 2) simplecov (~> 0.10.0) term-ansicolor (~> 1.3) thor (~> 0.19.1) + tins (~> 1.6.0) crack (0.4.2) safe_yaml (~> 1.0.0) creole (0.5.0) @@ -153,7 +178,7 @@ GEM warden (~> 1.2.3) devise-async (0.9.0) devise (~> 3.2) - devise-two-factor (2.0.0) + devise-two-factor (2.0.1) activesupport attr_encrypted (~> 1.3.2) devise (~> 3.5.0) @@ -162,11 +187,11 @@ GEM diff-lcs (1.2.5) diffy (3.0.7) docile (1.1.5) - domain_name (0.5.24) + domain_name (0.5.25) unf (>= 0.0.5, < 1.0.0) doorkeeper (2.1.4) railties (>= 3.2) - dropzonejs-rails (0.7.1) + dropzonejs-rails (0.7.2) rails (> 3.1) email_reply_parser (0.5.8) email_spec (1.6.0) @@ -202,7 +227,7 @@ GEM flog (4.3.2) ruby_parser (~> 3.1, > 3.1.0) sexp_processor (~> 4.4) - flowdock (0.7.0) + flowdock (0.7.1) httparty (~> 0.7) multi_json fog (1.25.0) @@ -224,13 +249,10 @@ GEM fog-core (~> 1.22) fog-json inflecto (~> 0.0.2) - fog-core (1.32.1) + fog-core (1.35.0) builder excon (~> 0.45) formatador (~> 0.2) - mime-types - net-scp (~> 1.1) - net-ssh (>= 2.1.3) fog-json (1.0.2) fog-core (~> 1.0) multi_json (~> 1.10) @@ -242,10 +264,10 @@ GEM fog-core (>= 1.21.0) fog-json fog-xml (>= 0.0.1) - fog-sakuracloud (1.0.1) + fog-sakuracloud (1.4.0) fog-core fog-json - fog-softlayer (0.4.7) + fog-softlayer (1.0.2) fog-core fog-json fog-terremark (0.1.0) @@ -260,7 +282,7 @@ GEM fog-xml (0.1.2) fog-core nokogiri (~> 1.5, >= 1.5.11) - font-awesome-rails (4.4.0.0) + font-awesome-rails (4.5.0.0) railties (>= 3.2, < 5.0) foreman (0.78.0) thor (~> 0.19.1) @@ -270,11 +292,11 @@ GEM ruby-progressbar (~> 1.4) gemnasium-gitlab-service (0.2.6) rugged (~> 0.21) - gemojione (2.0.1) + gemojione (2.1.0) json get_process_mem (0.2.0) gherkin-ruby (0.3.2) - github-linguist (4.7.0) + github-linguist (4.7.2) charlock_holmes (~> 0.7.3) escape_utils (~> 1.1.0) mime-types (>= 1.19) @@ -289,8 +311,8 @@ GEM diff-lcs (~> 1.1) mime-types (~> 1.15) posix-spawn (~> 0.3) - gitlab_emoji (0.1.1) - gemojione (~> 2.0) + gitlab_emoji (0.2.0) + gemojione (~> 2.1) gitlab_git (7.2.20) activesupport (~> 4.0) charlock_holmes (~> 0.7.3) @@ -302,13 +324,15 @@ GEM omniauth (~> 1.0) pyu-ruby-sasl (~> 0.0.3.1) rubyntlm (~> 0.3) + globalid (0.3.6) + activesupport (>= 4.1.0) gollum-grit_adapter (1.0.0) gitlab-grit (~> 2.7, >= 2.7.1) gollum-lib (4.0.3) github-markup (~> 1.3.3) gollum-grit_adapter (~> 1.0) nokogiri (~> 1.6.4) - rouge (~> 1.10.1) + rouge (~> 1.7.4) sanitize (~> 2.1.0) stringex (~> 2.5.1) gon (6.0.1) @@ -337,7 +361,7 @@ GEM haml (>= 4.0.6, < 5.0) html2haml (>= 1.0.1) railties (>= 4.0.1) - hashie (3.4.2) + hashie (3.4.3) highline (1.6.21) hike (1.2.3) hipchat (1.5.2) @@ -355,7 +379,7 @@ GEM http-cookie (1.0.2) domain_name (~> 0.5) http_parser.rb (0.5.3) - httparty (0.13.5) + httparty (0.13.7) json (~> 1.8) multi_xml (>= 0.5.2) httpclient (2.7.0.1) @@ -365,7 +389,7 @@ GEM inflecto (0.0.2) ipaddress (0.8.0) jquery-atwho-rails (1.3.2) - jquery-rails (3.1.3) + jquery-rails (3.1.4) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) jquery-scrollto-rails (1.4.3) @@ -376,19 +400,21 @@ GEM jquery-ui-rails (4.2.1) railties (>= 3.2.16) json (1.8.3) - jwt (1.5.1) + jwt (1.5.2) kaminari (0.16.3) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - kgio (2.9.3) + kgio (2.10.0) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.1.2) launchy (~> 2.2) - listen (2.10.1) - celluloid (~> 0.16.0) + listen (2.9.0) + celluloid (>= 0.15.2) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) + loofah (2.0.3) + nokogiri (>= 1.5.9) macaddr (1.7.1) systemu (~> 2.6.2) mail (2.6.3) @@ -405,16 +431,14 @@ GEM multipart-post (2.0.0) mysql2 (0.3.20) nested_form (0.3.2) - net-ldap (0.11) - net-scp (1.2.1) - net-ssh (>= 2.6.5) - net-ssh (2.9.2) - netrc (0.10.3) + net-ldap (0.12.1) + net-ssh (3.0.1) + netrc (0.11.0) newrelic-grape (2.0.0) grape newrelic_rpm newrelic_rpm (3.9.4.245) - nokogiri (1.6.6.2) + nokogiri (1.6.6.4) mini_portile (~> 0.6.0) nprogress-rails (0.1.6.7) oauth (0.4.7) @@ -438,12 +462,15 @@ GEM omniauth-github (1.1.2) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) - omniauth-gitlab (1.0.0) + omniauth-gitlab (1.0.1) omniauth (~> 1.0) omniauth-oauth2 (~> 1.0) - omniauth-google-oauth2 (0.2.6) - omniauth (> 1.0) - omniauth-oauth2 (~> 1.1) + omniauth-google-oauth2 (0.2.10) + addressable (~> 2.3) + jwt (~> 1.0) + multi_json (~> 1.3) + omniauth (>= 1.1.1) + omniauth-oauth2 (~> 1.3.1) omniauth-kerberos (0.3.0) omniauth-multipassword timfel-krb5-auth (~> 0.8) @@ -467,18 +494,18 @@ GEM activesupport nokogiri (>= 1.4.4) omniauth (~> 1.0) - opennebula (4.12.1) + opennebula (4.14.2) json nokogiri rbvmomi org-ruby (0.9.12) rubypants (~> 0.2) orm_adapter (0.5.0) - paranoia (2.1.3) + paranoia (2.1.4) activerecord (~> 4.0) - parser (2.2.2.6) + parser (2.2.3.0) ast (>= 1.1, < 3.0) - pg (0.18.2) + pg (0.18.4) poltergeist (1.6.0) capybara (~> 2.1) cliver (~> 0.3.1) @@ -486,7 +513,7 @@ GEM websocket-driver (>= 0.2.0) posix-spawn (0.3.11) powerpack (0.0.9) - pry (0.10.1) + pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) @@ -495,7 +522,7 @@ GEM pyu-ruby-sasl (0.0.3.3) quiet_assets (1.0.3) railties (>= 3.1, < 5.0) - rack (1.5.5) + rack (1.6.4) rack-accept (0.4.5) rack (>= 0.4) rack-attack (4.3.0) @@ -513,28 +540,37 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (4.1.14) - actionmailer (= 4.1.14) - actionpack (= 4.1.14) - actionview (= 4.1.14) - activemodel (= 4.1.14) - activerecord (= 4.1.14) - activesupport (= 4.1.14) + rails (4.2.4) + actionmailer (= 4.2.4) + actionpack (= 4.2.4) + actionview (= 4.2.4) + activejob (= 4.2.4) + activemodel (= 4.2.4) + activerecord (= 4.2.4) + activesupport (= 4.2.4) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.14) - sprockets-rails (~> 2.0) + railties (= 4.2.4) + sprockets-rails + rails-deprecated_sanitizer (1.0.3) + activesupport (>= 4.2.0.alpha) + rails-dom-testing (1.0.7) + activesupport (>= 4.2.0.beta, < 5.0) + nokogiri (~> 1.6.0) + rails-deprecated_sanitizer (>= 1.0.1) + rails-html-sanitizer (1.0.2) + loofah (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) - railties (4.1.14) - actionpack (= 4.1.14) - activesupport (= 4.1.14) + railties (4.2.4) + actionpack (= 4.2.4) + activesupport (= 4.2.4) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) raindrops (0.15.0) rake (10.4.2) raphael-rails (2.1.2) - rb-fsevent (0.9.5) + rb-fsevent (0.9.6) rb-inotify (0.9.5) ffi (>= 0.5.0) rblineprof (0.3.6) @@ -546,13 +582,13 @@ GEM rdoc (3.12.2) json (~> 1.4) redcarpet (3.3.3) - redis (3.2.1) - redis-actionpack (4.0.0) + redis (3.2.2) + redis-actionpack (4.0.1) actionpack (~> 4) redis-rack (~> 1.5.0) redis-store (~> 1.1.0) - redis-activesupport (4.1.1) - activesupport (~> 4) + redis-activesupport (4.1.5) + activesupport (>= 3, < 5) redis-store (~> 1.1.0) redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) @@ -563,20 +599,20 @@ GEM redis-actionpack (~> 4) redis-activesupport (~> 4) redis-store (~> 1.1.0) - redis-store (1.1.6) + redis-store (1.1.7) redis (>= 2.2) - request_store (1.2.0) + request_store (1.2.1) rerun (0.10.0) listen (~> 2.7, >= 2.7.3) - responders (1.1.2) - railties (>= 3.2, < 4.2) + responders (2.1.0) + railties (>= 4.2.0, < 5) rest-client (1.8.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) rinku (1.7.3) rotp (2.1.1) - rouge (1.10.1) + rouge (1.7.7) rqrcode (0.7.0) chunky_png rqrcode-rails3 (0.1.7) @@ -716,14 +752,14 @@ GEM terminal-table (1.5.2) test_after_commit (0.2.7) activerecord (>= 3.2) - thin (1.6.3) + thin (1.6.4) daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0) + eventmachine (~> 1.0, >= 1.0.4) rack (~> 1.0) thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) - timers (4.0.4) + timers (4.1.1) hitimes timfel-krb5-auth (0.8.3) tinder (1.10.1) @@ -756,9 +792,9 @@ GEM kgio (~> 2.6) rack raindrops (~> 0.7) - unicorn-worker-killer (0.4.3) + unicorn-worker-killer (0.4.4) get_process_mem (~> 0) - unicorn (~> 4) + unicorn (>= 4, < 6) uniform_notifier (1.9.0) uuid (2.3.8) macaddr (~> 1.0) @@ -770,10 +806,15 @@ GEM equalizer (~> 0.0, >= 0.0.9) warden (1.2.3) rack (>= 1.0) + web-console (2.2.1) + activemodel (>= 4.0) + binding_of_caller (>= 0.7.2) + railties (>= 4.0) + sprockets-rails (>= 2.0, < 4.0) webmock (1.21.0) addressable (>= 2.3.6) crack (>= 0.3.2) - websocket-driver (0.6.2) + websocket-driver (0.6.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) wikicloth (0.8.1) @@ -865,6 +906,7 @@ DEPENDENCIES mousetrap-rails (~> 1.4.6) mysql2 (~> 0.3.16) nested_form (~> 0.3.2) + net-ssh (~> 3.0.1) newrelic-grape newrelic_rpm (~> 3.9.4.245) nprogress-rails (~> 0.1.6.7) @@ -890,7 +932,7 @@ DEPENDENCIES rack-attack (~> 4.3.0) rack-cors (~> 0.4.0) rack-oauth2 (~> 1.2.1) - rails (= 4.1.14) + rails (= 4.2.4) raphael-rails (~> 2.1.2) rblineprof rdoc (~> 3.6) @@ -898,6 +940,7 @@ DEPENDENCIES redis-rails (~> 4.0.0) request_store (~> 1.2.0) rerun (~> 0.10.0) + responders (~> 2.0) rqrcode-rails3 (~> 0.1.7) rspec-rails (~> 3.3.0) rubocop (~> 0.28.0) @@ -938,6 +981,7 @@ DEPENDENCIES unicorn-worker-killer (~> 0.4.2) version_sorter (~> 2.0.0) virtus (~> 1.0.1) + web-console (~> 2.0) webmock (~> 1.21.0) wikicloth (= 0.8.1) diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index 3eed5c16e45..d8fe65b06f6 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -17,9 +17,8 @@ class SentNotification < ActiveRecord::Base belongs_to :noteable, polymorphic: true belongs_to :recipient, class_name: "User" - validate :project, :recipient, :reply_key, presence: true - validate :reply_key, uniqueness: true - + validates :project, :recipient, :reply_key, presence: true + validates :reply_key, uniqueness: true validates :noteable_id, presence: true, unless: :for_commit? validates :commit_id, presence: true, if: :for_commit? validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb index 916a99bb273..c4d8595d45d 100644 --- a/app/workers/emails_on_push_worker.rb +++ b/app/workers/emails_on_push_worker.rb @@ -53,7 +53,7 @@ class EmailsOnPushWorker reverse_compare: reverse_compare, send_from_committer_email: send_from_committer_email, disable_diffs: disable_diffs - ).deliver + ).deliver_now # These are input errors and won't be corrected even if Sidekiq retries rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e logger.info("Failed to send e-mail for project '#{project.name_with_namespace}' to #{recipient}: #{e}") diff --git a/bin/ci/upgrade.rb b/bin/ci/upgrade.rb old mode 100644 new mode 100755 diff --git a/bin/rails b/bin/rails index 7feb6a30e69..5191e6927af 100755 --- a/bin/rails +++ b/bin/rails @@ -1,8 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path("../spring", __FILE__) -rescue LoadError -end -APP_PATH = File.expand_path('../../config/application', __FILE__) +APP_PATH = File.expand_path('../../config/application', __FILE__) require_relative '../config/boot' require 'rails/commands' diff --git a/bin/rake b/bin/rake index 0fb4e07e13a..17240489f64 100755 --- a/bin/rake +++ b/bin/rake @@ -1,7 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path("../spring", __FILE__) -rescue LoadError -end -require 'bundler/setup' -load Gem.bin_path('rake', 'rake') +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 00000000000..acdb2c1389c --- /dev/null +++ b/bin/setup @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +Dir.chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file: + + puts "== Installing dependencies ==" + system "gem install bundler --conservative" + system "bundle check || bundle install" + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # system "cp config/database.yml.sample config/database.yml" + # end + + puts "\n== Preparing database ==" + system "bin/rake db:setup" + + puts "\n== Removing old logs and tempfiles ==" + system "rm -f log/*" + system "rm -rf tmp/cache" + + puts "\n== Restarting application server ==" + system "touch tmp/restart.txt" +end diff --git a/bin/upgrade.rb b/bin/upgrade.rb old mode 100644 new mode 100755 diff --git a/config/environment.rb b/config/environment.rb index 3b186a9d57a..df3006d349c 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -2,4 +2,4 @@ require File.expand_path('../application', __FILE__) # Initialize the rails application -Gitlab::Application.initialize! +Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index 827a110c249..c22722c606b 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,4 +1,4 @@ -Gitlab::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb # In the development environment your application's code is reloaded on diff --git a/config/environments/production.rb b/config/environments/production.rb index 3316ece3873..e8250d66452 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,4 +1,4 @@ -Gitlab::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb # Code is not reloaded between requests diff --git a/config/environments/test.rb b/config/environments/test.rb index 955540837d3..46982be2864 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,4 +1,4 @@ -Gitlab::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb # The test environment is used exclusively to run your application's diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb index 43adac8b2c6..54516e3f23d 100644 --- a/config/initializers/cookies_serializer.rb +++ b/config/initializers/cookies_serializer.rb @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -Gitlab::Application.config.action_dispatch.cookies_serializer = :hybrid +Rails.application.config.action_dispatch.cookies_serializer = :hybrid diff --git a/config/initializers/default_url_options.rb b/config/initializers/default_url_options.rb index f9f88f95db9..8fd27b1d88e 100644 --- a/config/initializers/default_url_options.rb +++ b/config/initializers/default_url_options.rb @@ -8,4 +8,4 @@ unless Gitlab.config.gitlab_on_standard_port? default_url_options[:port] = Gitlab.config.gitlab.port end -Gitlab::Application.routes.default_url_options = default_url_options +Rails.application.routes.default_url_options = default_url_options diff --git a/config/initializers/rack_attack.rb.example b/config/initializers/rack_attack.rb.example index 2155ea14562..b1bbcca1d61 100644 --- a/config/initializers/rack_attack.rb.example +++ b/config/initializers/rack_attack.rb.example @@ -4,13 +4,13 @@ # If you change this file in a Merge Request, please also create a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests paths_to_be_protected = [ - "#{Gitlab::Application.config.relative_url_root}/users/password", - "#{Gitlab::Application.config.relative_url_root}/users/sign_in", - "#{Gitlab::Application.config.relative_url_root}/api/#{API::API.version}/session.json", - "#{Gitlab::Application.config.relative_url_root}/api/#{API::API.version}/session", - "#{Gitlab::Application.config.relative_url_root}/users", - "#{Gitlab::Application.config.relative_url_root}/users/confirmation", - "#{Gitlab::Application.config.relative_url_root}/unsubscribes/" + "#{Rails.application.config.relative_url_root}/users/password", + "#{Rails.application.config.relative_url_root}/users/sign_in", + "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session.json", + "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session", + "#{Rails.application.config.relative_url_root}/users", + "#{Rails.application.config.relative_url_root}/users/confirmation", + "#{Rails.application.config.relative_url_root}/unsubscribes/" ] diff --git a/config/initializers/rack_lineprof.rb b/config/initializers/rack_lineprof.rb index f0c006d811b..22e77a32c61 100644 --- a/config/initializers/rack_lineprof.rb +++ b/config/initializers/rack_lineprof.rb @@ -2,7 +2,7 @@ # with darker backgrounds. This patch tweaks the colors a bit so the output is # actually readable. if Rails.env.development? and RUBY_ENGINE == 'ruby' and ENV['ENABLE_LINEPROF'] - Gitlab::Application.config.middleware.use(Rack::Lineprof) + Rails.application.config.middleware.use(Rack::Lineprof) module Rack class Lineprof diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb index 1b518c3becf..dae3a4a9a93 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_token.rb @@ -22,15 +22,15 @@ def find_secure_token end end -Gitlab::Application.config.secret_token = find_secure_token -Gitlab::Application.config.secret_key_base = find_secure_token +Rails.application.config.secret_token = find_secure_token +Rails.application.config.secret_key_base = find_secure_token # CI def generate_new_secure_token SecureRandom.hex(64) end -if Gitlab::Application.secrets.db_key_base.blank? +if Rails.application.secrets.db_key_base.blank? warn "Missing `db_key_base` for '#{Rails.env}' environment. The secrets will be generated and stored in `config/secrets.yml`" all_secrets = YAML.load_file('config/secrets.yml') if File.exist?('config/secrets.yml') @@ -46,5 +46,5 @@ if Gitlab::Application.secrets.db_key_base.blank? file.write(YAML.dump(all_secrets)) end - Gitlab::Application.secrets.db_key_base = env_secrets['db_key_base'] + Rails.application.secrets.db_key_base = env_secrets['db_key_base'] end diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index f30178ff711..d5208b8c93e 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -13,11 +13,11 @@ end unless Rails.env.test? Gitlab::Application.config.session_store( :redis_store, # Using the cookie_store would enable session replay attacks. - servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store + servers: Rails.application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store key: '_gitlab_session', secure: Gitlab.config.gitlab.https, httponly: true, expire_after: Settings.gitlab['session_expire_delay'] * 60, - path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root + path: (Rails.application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root ) end diff --git a/config/initializers/sherlock.rb b/config/initializers/sherlock.rb index 42b0d78c85f..8f2ababb712 100644 --- a/config/initializers/sherlock.rb +++ b/config/initializers/sherlock.rb @@ -1,5 +1,5 @@ if Gitlab::Sherlock.enabled? - Gitlab::Application.configure do |config| + Rails.application.configure do |config| config.middleware.use(Gitlab::Sherlock::Middleware) end end diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample index 25ec247a095..ec182502d4e 100644 --- a/config/initializers/smtp_settings.rb.sample +++ b/config/initializers/smtp_settings.rb.sample @@ -8,7 +8,7 @@ # If you change this file in a Merge Request, please also create a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests if Rails.env.production? - Gitlab::Application.config.action_mailer.delivery_method = :smtp + Rails.application.config.action_mailer.delivery_method = :smtp ActionMailer::Base.smtp_settings = { address: "email.server.com", diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb index e6d5600edb7..d9042c652bb 100644 --- a/config/initializers/static_files.rb +++ b/config/initializers/static_files.rb @@ -1,4 +1,4 @@ -app = Gitlab::Application +app = Rails.application if app.config.serve_static_assets # The `ActionDispatch::Static` middleware intercepts requests for static files diff --git a/config/routes.rb b/config/routes.rb index ac81a2aac76..5c114452a3f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ require 'sidekiq/web' require 'api/api' -Gitlab::Application.routes.draw do +Rails.application.routes.draw do if Gitlab::Sherlock.enabled? namespace :sherlock do resources :transactions, only: [:index, :show] do diff --git a/db/schema.rb b/db/schema.rb index 5bbe0c908ef..fbcb711e569 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -16,7 +16,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - create_table "abuse_reports", force: true do |t| + create_table "abuse_reports", force: :cascade do |t| t.integer "reporter_id" t.integer "user_id" t.text "message" @@ -24,7 +24,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.datetime "updated_at" end - create_table "application_settings", force: true do |t| + create_table "application_settings", force: :cascade do |t| t.integer "default_projects_limit" t.boolean "signup_enabled" t.boolean "signin_enabled" @@ -51,7 +51,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.integer "max_artifacts_size", default: 100, null: false end - create_table "audit_events", force: true do |t| + create_table "audit_events", force: :cascade do |t| t.integer "author_id", null: false t.string "type", null: false t.integer "entity_id", null: false @@ -65,7 +65,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree - create_table "broadcast_messages", force: true do |t| + create_table "broadcast_messages", force: :cascade do |t| t.text "message", null: false t.datetime "starts_at" t.datetime "ends_at" @@ -76,14 +76,14 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.string "font" end - create_table "ci_application_settings", force: true do |t| + create_table "ci_application_settings", force: :cascade do |t| t.boolean "all_broken_builds" t.boolean "add_pusher" t.datetime "created_at" t.datetime "updated_at" end - create_table "ci_builds", force: true do |t| + create_table "ci_builds", force: :cascade do |t| t.integer "project_id" t.string "status" t.datetime "finished_at" @@ -123,7 +123,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree add_index "ci_builds", ["type"], name: "index_ci_builds_on_type", using: :btree - create_table "ci_commits", force: true do |t| + create_table "ci_commits", force: :cascade do |t| t.integer "project_id" t.string "ref" t.string "sha" @@ -144,7 +144,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree - create_table "ci_events", force: true do |t| + create_table "ci_events", force: :cascade do |t| t.integer "project_id" t.integer "user_id" t.integer "is_admin" @@ -157,7 +157,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_events", ["is_admin"], name: "index_ci_events_on_is_admin", using: :btree add_index "ci_events", ["project_id"], name: "index_ci_events_on_project_id", using: :btree - create_table "ci_jobs", force: true do |t| + create_table "ci_jobs", force: :cascade do |t| t.integer "project_id", null: false t.text "commands" t.boolean "active", default: true, null: false @@ -174,7 +174,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_jobs", ["deleted_at"], name: "index_ci_jobs_on_deleted_at", using: :btree add_index "ci_jobs", ["project_id"], name: "index_ci_jobs_on_project_id", using: :btree - create_table "ci_projects", force: true do |t| + create_table "ci_projects", force: :cascade do |t| t.string "name" t.integer "timeout", default: 3600, null: false t.datetime "created_at" @@ -200,7 +200,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_projects", ["gitlab_id"], name: "index_ci_projects_on_gitlab_id", using: :btree add_index "ci_projects", ["shared_runners_enabled"], name: "index_ci_projects_on_shared_runners_enabled", using: :btree - create_table "ci_runner_projects", force: true do |t| + create_table "ci_runner_projects", force: :cascade do |t| t.integer "runner_id", null: false t.integer "project_id", null: false t.datetime "created_at" @@ -210,7 +210,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree - create_table "ci_runners", force: true do |t| + create_table "ci_runners", force: :cascade do |t| t.string "token" t.datetime "created_at" t.datetime "updated_at" @@ -225,7 +225,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.string "architecture" end - create_table "ci_services", force: true do |t| + create_table "ci_services", force: :cascade do |t| t.string "type" t.string "title" t.integer "project_id", null: false @@ -237,7 +237,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_services", ["project_id"], name: "index_ci_services_on_project_id", using: :btree - create_table "ci_sessions", force: true do |t| + create_table "ci_sessions", force: :cascade do |t| t.string "session_id", null: false t.text "data" t.datetime "created_at" @@ -247,7 +247,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_sessions", ["session_id"], name: "index_ci_sessions_on_session_id", using: :btree add_index "ci_sessions", ["updated_at"], name: "index_ci_sessions_on_updated_at", using: :btree - create_table "ci_taggings", force: true do |t| + create_table "ci_taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" t.string "taggable_type" @@ -260,14 +260,14 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "ci_taggings_idx", unique: true, using: :btree add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree - create_table "ci_tags", force: true do |t| + create_table "ci_tags", force: :cascade do |t| t.string "name" t.integer "taggings_count", default: 0 end add_index "ci_tags", ["name"], name: "index_ci_tags_on_name", unique: true, using: :btree - create_table "ci_trigger_requests", force: true do |t| + create_table "ci_trigger_requests", force: :cascade do |t| t.integer "trigger_id", null: false t.text "variables" t.datetime "created_at" @@ -275,7 +275,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.integer "commit_id" end - create_table "ci_triggers", force: true do |t| + create_table "ci_triggers", force: :cascade do |t| t.string "token" t.integer "project_id", null: false t.datetime "deleted_at" @@ -285,7 +285,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_triggers", ["deleted_at"], name: "index_ci_triggers_on_deleted_at", using: :btree - create_table "ci_variables", force: true do |t| + create_table "ci_variables", force: :cascade do |t| t.integer "project_id", null: false t.string "key" t.text "value" @@ -296,14 +296,14 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree - create_table "ci_web_hooks", force: true do |t| + create_table "ci_web_hooks", force: :cascade do |t| t.string "url", null: false t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" end - create_table "deploy_keys_projects", force: true do |t| + create_table "deploy_keys_projects", force: :cascade do |t| t.integer "deploy_key_id", null: false t.integer "project_id", null: false t.datetime "created_at" @@ -312,7 +312,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree - create_table "emails", force: true do |t| + create_table "emails", force: :cascade do |t| t.integer "user_id", null: false t.string "email", null: false t.datetime "created_at" @@ -322,7 +322,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "emails", ["email"], name: "index_emails_on_email", unique: true, using: :btree add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree - create_table "events", force: true do |t| + create_table "events", force: :cascade do |t| t.string "target_type" t.integer "target_id" t.string "title" @@ -341,7 +341,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "events", ["target_id"], name: "index_events_on_target_id", using: :btree add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree - create_table "forked_project_links", force: true do |t| + create_table "forked_project_links", force: :cascade do |t| t.integer "forked_to_project_id", null: false t.integer "forked_from_project_id", null: false t.datetime "created_at" @@ -350,7 +350,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree - create_table "identities", force: true do |t| + create_table "identities", force: :cascade do |t| t.string "extern_uid" t.string "provider" t.integer "user_id" @@ -361,7 +361,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "identities", ["created_at", "id"], name: "index_identities_on_created_at_and_id", using: :btree add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree - create_table "issues", force: true do |t| + create_table "issues", force: :cascade do |t| t.string "title" t.integer "assignee_id" t.integer "author_id" @@ -387,7 +387,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "issues", ["state"], name: "index_issues_on_state", using: :btree add_index "issues", ["title"], name: "index_issues_on_title", using: :btree - create_table "keys", force: true do |t| + create_table "keys", force: :cascade do |t| t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" @@ -401,7 +401,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree - create_table "label_links", force: true do |t| + create_table "label_links", force: :cascade do |t| t.integer "label_id" t.integer "target_id" t.string "target_type" @@ -412,7 +412,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "label_links", ["label_id"], name: "index_label_links_on_label_id", using: :btree add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree - create_table "labels", force: true do |t| + create_table "labels", force: :cascade do |t| t.string "title" t.string "color" t.integer "project_id" @@ -423,7 +423,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree - create_table "lfs_objects", force: true do |t| + create_table "lfs_objects", force: :cascade do |t| t.string "oid", null: false t.integer "size", null: false t.datetime "created_at" @@ -433,7 +433,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree - create_table "lfs_objects_projects", force: true do |t| + create_table "lfs_objects_projects", force: :cascade do |t| t.integer "lfs_object_id", null: false t.integer "project_id", null: false t.datetime "created_at" @@ -442,7 +442,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree - create_table "members", force: true do |t| + create_table "members", force: :cascade do |t| t.integer "access_level", null: false t.integer "source_id", null: false t.string "source_type", null: false @@ -464,7 +464,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "members", ["type"], name: "index_members_on_type", using: :btree add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree - create_table "merge_request_diffs", force: true do |t| + create_table "merge_request_diffs", force: :cascade do |t| t.string "state" t.text "st_commits" t.text "st_diffs" @@ -475,7 +475,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree - create_table "merge_requests", force: true do |t| + create_table "merge_requests", force: :cascade do |t| t.string "target_branch", null: false t.string "source_branch", null: false t.integer "source_project_id", null: false @@ -507,7 +507,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do 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| + create_table "milestones", force: :cascade do |t| t.string "title", null: false t.integer "project_id", null: false t.text "description" @@ -523,7 +523,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do 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| + create_table "namespaces", force: :cascade do |t| t.string "name", null: false t.string "path", null: false t.integer "owner_id" @@ -542,7 +542,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "namespaces", ["public"], name: "index_namespaces_on_public", using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree - create_table "notes", force: true do |t| + create_table "notes", force: :cascade do |t| t.text "note" t.string "noteable_type" t.integer "author_id" @@ -571,7 +571,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do 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 "oauth_access_grants", force: true do |t| + create_table "oauth_access_grants", force: :cascade do |t| t.integer "resource_owner_id", null: false t.integer "application_id", null: false t.string "token", null: false @@ -584,7 +584,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree - create_table "oauth_access_tokens", force: true do |t| + create_table "oauth_access_tokens", force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" t.string "token", null: false @@ -599,7 +599,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree - create_table "oauth_applications", force: true do |t| + create_table "oauth_applications", force: :cascade do |t| t.string "name", null: false t.string "uid", null: false t.string "secret", null: false @@ -614,12 +614,12 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree - create_table "project_import_data", force: true do |t| + create_table "project_import_data", force: :cascade do |t| t.integer "project_id" t.text "data" end - create_table "projects", force: true do |t| + create_table "projects", force: :cascade do |t| t.string "name" t.string "path" t.text "description" @@ -656,7 +656,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree - create_table "protected_branches", force: true do |t| + create_table "protected_branches", force: :cascade do |t| t.integer "project_id", null: false t.string "name", null: false t.datetime "created_at" @@ -666,7 +666,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree - create_table "releases", force: true do |t| + create_table "releases", force: :cascade do |t| t.string "tag" t.text "description" t.integer "project_id" @@ -677,7 +677,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree - create_table "sent_notifications", force: true do |t| + create_table "sent_notifications", force: :cascade do |t| t.integer "project_id" t.integer "noteable_id" t.string "noteable_type" @@ -689,7 +689,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree - create_table "services", force: true do |t| + create_table "services", force: :cascade do |t| t.string "type" t.string "title" t.integer "project_id" @@ -709,7 +709,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree add_index "services", ["template"], name: "index_services_on_template", using: :btree - create_table "snippets", force: true do |t| + create_table "snippets", force: :cascade do |t| t.string "title" t.text "content" t.integer "author_id", null: false @@ -729,7 +729,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree - create_table "subscriptions", force: true do |t| + create_table "subscriptions", force: :cascade do |t| t.integer "user_id" t.integer "subscribable_id" t.string "subscribable_type" @@ -740,7 +740,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "subscriptions", ["subscribable_id", "subscribable_type", "user_id"], name: "subscriptions_user_id_and_ref_fields", unique: true, using: :btree - create_table "taggings", force: true do |t| + create_table "taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" t.string "taggable_type" @@ -753,14 +753,14 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true, using: :btree add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree - create_table "tags", force: true do |t| + create_table "tags", force: :cascade do |t| t.string "name" t.integer "taggings_count", default: 0 end add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree - create_table "users", force: true do |t| + create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" @@ -826,7 +826,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree - create_table "users_star_projects", force: true do |t| + create_table "users_star_projects", force: :cascade do |t| t.integer "project_id", null: false t.integer "user_id", null: false t.datetime "created_at" @@ -837,7 +837,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "users_star_projects", ["user_id", "project_id"], name: "index_users_star_projects_on_user_id_and_project_id", unique: true, using: :btree add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree - create_table "web_hooks", force: true do |t| + create_table "web_hooks", force: :cascade do |t| t.string "url" t.integer "project_id" t.datetime "created_at" -- cgit v1.2.1 From e55473ad6880a68a86f355b7825dbdaf67e1f375 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 11:30:33 -0800 Subject: Expire application settings from cache at startup If a DB migration occurs, there's a chance that the application settings are loaded from the cache and provide stale values, causing Error 500s. This ensures that at startup the settings are always refreshed. Closes #3643 --- CHANGELOG | 1 + app/models/application_setting.rb | 12 ++++++++++-- app/models/ci/application_setting.rb | 12 ++++++++++-- config/initializers/1_settings.rb | 4 ++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..a6bef82693a 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.3.0 (unreleased) + - Ensure cached application settings are refreshed at startup (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index b2d5fe1558f..3df8135acf1 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -73,15 +73,23 @@ class ApplicationSetting < ActiveRecord::Base end after_commit do - Rails.cache.write('application_setting.last', self) + Rails.cache.write(cache_key, self) end def self.current - Rails.cache.fetch('application_setting.last') do + Rails.cache.fetch(cache_key) do ApplicationSetting.last end end + def self.expire + Rails.cache.delete(cache_key) + end + + def self.cache_key + 'application_setting.last' + end + def self.create_from_defaults create( default_projects_limit: Settings.gitlab['default_projects_limit'], diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb index 1307fa0b472..4e512d290ee 100644 --- a/app/models/ci/application_setting.rb +++ b/app/models/ci/application_setting.rb @@ -14,11 +14,15 @@ module Ci extend Ci::Model after_commit do - Rails.cache.write('ci_application_setting.last', self) + Rails.cache.write(cache_key, self) + end + + def self.expire + Rails.cache.delete(cache_key) end def self.current - Rails.cache.fetch('ci_application_setting.last') do + Rails.cache.fetch(cache_key) do Ci::ApplicationSetting.last end end @@ -29,5 +33,9 @@ module Ci add_pusher: Settings.gitlab_ci['add_pusher'], ) end + + def self.cache_key + 'ci_application_setting.last' + end end end diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index b162b8a83fc..80b480eac37 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -293,3 +293,7 @@ if Rails.env.test? Settings.gitlab['default_can_create_group'] = true Settings.gitlab['default_can_create_team'] = false end + +# Force a refresh of application settings at startup +ApplicationSetting.expire +Ci::ApplicationSetting.expire -- cgit v1.2.1 From 8dcef120cd94717b4f82db864191698826ca02a5 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 25 Nov 2015 17:24:07 -0200 Subject: Fix raw private snippets access workflow --- CHANGELOG | 1 + app/controllers/snippets_controller.rb | 2 +- spec/controllers/snippets_controller_spec.rb | 115 +++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..d61d85e4051 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission + - Fix: Raw private snippets access workflow v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 08f2483af33..c72df73af46 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -2,7 +2,7 @@ class SnippetsController < ApplicationController before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] # Allow read snippet - before_action :authorize_read_snippet!, only: [:show] + before_action :authorize_read_snippet!, only: [:show, :raw] # Allow modify snippet before_action :authorize_update_snippet!, only: [:edit, :update] diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index e9b823c523c..b3dcb52c500 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -115,4 +115,119 @@ describe SnippetsController do end end end + + describe 'GET #raw' do + let(:user) { create(:user) } + + context 'when the personal snippet is private' do + let(:personal_snippet) { create(:personal_snippet, :private, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + context 'when signed in user is not the author' do + let(:other_author) { create(:author) } + let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } + + it 'responds with status 404' do + get :raw, id: other_personal_snippet.to_param + + expect(response.status).to eq(404) + end + end + + context 'when signed in user is the author' do + it 'renders the raw snippet' do + get :raw, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + end + + context 'when not signed in' do + it 'redirects to the sign in page' do + get :raw, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is internal' do + let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'renders the raw snippet' do + get :raw, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + + context 'when not signed in' do + it 'redirects to the sign in page' do + get :raw, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is public' do + let(:personal_snippet) { create(:personal_snippet, :public, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'renders the raw snippet' do + get :raw, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + + context 'when not signed in' do + it 'renders the raw snippet' do + get :raw, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + end + + context 'when the personal snippet does not exist' do + context 'when signed in' do + before do + sign_in(user) + end + + it 'responds with status 404' do + get :raw, id: 'doesntexist' + + expect(response.status).to eq(404) + end + end + + context 'when not signed in' do + it 'responds with status 404' do + get :raw, id: 'doesntexist' + + expect(response.status).to eq(404) + end + end + end + end end -- cgit v1.2.1 From 73eca2a95ef401a8e3061a14949ee1dbf34f99b9 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 25 Nov 2015 16:26:15 -0500 Subject: Decouple Markdown previews from DropzoneInput --- app/assets/javascripts/dropzone_input.js.coffee | 80 ++------------------- app/assets/javascripts/markdown_preview.js.coffee | 87 +++++++++++++++++++++++ app/views/projects/_md_preview.html.haml | 2 +- 3 files changed, 92 insertions(+), 77 deletions(-) create mode 100644 app/assets/javascripts/markdown_preview.js.coffee diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index 6f789e668af..30a35a04339 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -1,3 +1,5 @@ +#= require markdown_preview + class @DropzoneInput constructor: (form) -> Dropzone.autoDiscover = false @@ -11,17 +13,14 @@ class @DropzoneInput uploadProgress = $("
") btnAlert = "" project_uploads_path = window.project_uploads_path or null - markdown_preview_path = window.markdown_preview_path or null max_file_size = gon.max_file_size or 10 form_textarea = $(form).find("textarea.markdown-area") form_textarea.wrap "
" form_textarea.on 'paste', (event) => handlePaste(event) - form_textarea.on "input", -> - hideReferencedUsers() - form_textarea.on "blur", -> - renderMarkdown() + + $(form).setupMarkdownPreview() form_dropzone = $(form).find('.div-dropzone') form_dropzone.parent().addClass "div-dropzone-wrapper" @@ -34,42 +33,6 @@ class @DropzoneInput "opacity": 0 "display": "none" - # Preview button - $(document).off "click", ".js-md-preview-button" - $(document).on "click", ".js-md-preview-button", (e) -> - ### - Shows the Markdown preview. - - Lets the server render GFM into Html and displays it. - ### - e.preventDefault() - form = $(this).closest("form") - # toggle tabs - form.find(".js-md-write-button").parent().removeClass "active" - form.find(".js-md-preview-button").parent().addClass "active" - - # toggle content - form.find(".md-write-holder").hide() - form.find(".md-preview-holder").show() - - renderMarkdown() - - # Write button - $(document).off "click", ".js-md-write-button" - $(document).on "click", ".js-md-write-button", (e) -> - ### - Shows the Markdown textarea. - ### - e.preventDefault() - form = $(this).closest("form") - # toggle tabs - form.find(".js-md-write-button").parent().addClass "active" - form.find(".js-md-preview-button").parent().removeClass "active" - - # toggle content - form.find(".md-write-holder").show() - form.find(".md-preview-holder").hide() - dropzone = form_dropzone.dropzone( url: project_uploads_path dictDefaultMessage: "" @@ -136,41 +99,6 @@ class @DropzoneInput child = $(dropzone[0]).children("textarea") - hideReferencedUsers = -> - referencedUsers = form.find(".referenced-users") - referencedUsers.hide() - - renderReferencedUsers = (users) -> - referencedUsers = form.find(".referenced-users") - - if referencedUsers.length - if users.length >= 10 - referencedUsers.show() - referencedUsers.find(".js-referenced-users-count").text users.length - else - referencedUsers.hide() - - renderMarkdown = -> - preview = form.find(".js-md-preview") - mdText = form.find(".markdown-area").val() - if mdText.trim().length is 0 - preview.text "Nothing to preview." - hideReferencedUsers() - else - preview.text "Loading..." - $.ajax( - type: "POST", - url: markdown_preview_path, - data: { - text: mdText - }, - dataType: "json" - ).success (data) -> - preview.html data.body - preview.syntaxHighlight() - - renderReferencedUsers data.references.users - formatLink = (link) -> text = "[#{link.alt}](#{link.url})" text = "!#{text}" if link.is_image diff --git a/app/assets/javascripts/markdown_preview.js.coffee b/app/assets/javascripts/markdown_preview.js.coffee new file mode 100644 index 00000000000..98fc8f17340 --- /dev/null +++ b/app/assets/javascripts/markdown_preview.js.coffee @@ -0,0 +1,87 @@ +# MarkdownPreview +# +# Handles toggling the "Write" and "Preview" tab clicks, rendering the preview, +# and showing a warning when more than `x` users are referenced. +# +class @MarkdownPreview + # Minimum number of users referenced before triggering a warning + referenceThreshold: 10 + + showPreview: (form) -> + preview = form.find('.js-md-preview') + mdText = form.find('textarea.markdown-area').val() + + if mdText.trim().length == 0 + preview.text('Nothing to preview.') + @hideReferencedUsers(form) + else + preview.text('Loading...') + @renderMarkdown mdText, (response) => + preview.html(response.body) + preview.syntaxHighlight() + @renderReferencedUsers(response.references.users, form) + + renderMarkdown: (text, success) -> + return unless window.markdown_preview_path + + $.ajax + type: 'POST' + url: window.markdown_preview_path + data: { text: text } + dataType: 'json' + success: success + + hideReferencedUsers: (form) -> + referencedUsers = form.find('.referenced-users') + referencedUsers.hide() + + renderReferencedUsers: (users, form) -> + referencedUsers = form.find('.referenced-users') + + if referencedUsers.length + if users.length >= @referenceThreshold + referencedUsers.show() + referencedUsers.find('.js-referenced-users-count').text(users.length) + else + referencedUsers.hide() + +markdownPreview = new MarkdownPreview() + +previewButtonSelector = '.js-md-preview-button' +writeButtonSelector = '.js-md-write-button' + +$.fn.setupMarkdownPreview = -> + $form = $(this) + + form_textarea = $form.find('textarea.markdown-area') + + form_textarea.on 'input', -> markdownPreview.hideReferencedUsers($form) + form_textarea.on 'blur', -> markdownPreview.showPreview($form) + +$(document).on 'click', previewButtonSelector, (e) -> + e.preventDefault() + + $form = $(this).closest('form') + + # toggle tabs + $form.find(writeButtonSelector).parent().removeClass('active') + $form.find(previewButtonSelector).parent().addClass('active') + + # toggle content + $form.find('.md-write-holder').hide() + $form.find('.md-preview-holder').show() + + markdownPreview.showPreview($form) + +$(document).on 'click', writeButtonSelector, (e) -> + e.preventDefault() + + $form = $(this).closest('form') + + # toggle tabs + $form.find(writeButtonSelector).parent().addClass('active') + $form.find(previewButtonSelector).parent().removeClass('active') + + # toggle content + $form.find('.md-write-holder').show() + $form.find('.md-preview-holder').hide() diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index 7b21095ea3e..8218cf11201 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -5,7 +5,7 @@ %a.js-md-write-button(href="#md-write-holder" tabindex="-1") Write %li - %a.js-md-preview-button(href="md-preview-holder" tabindex="-1") + %a.js-md-preview-button(href="#md-preview-holder" tabindex="-1") Preview - if defined?(referenced_users) && referenced_users -- cgit v1.2.1 From 36bde0fcb197aa7ca93bb615cda070f55f5e268f Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 14:00:35 -0800 Subject: Fix Error 500 when viewing user's personal projects from admin page This is a regression introduced in 4d7f00f. Closes #3680 --- CHANGELOG | 1 + app/views/admin/users/_projects.html.haml | 13 +++++++++++++ app/views/admin/users/projects.html.haml | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 app/views/admin/users/_projects.html.haml diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..6c78cc70b73 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.3.0 (unreleased) + - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission diff --git a/app/views/admin/users/_projects.html.haml b/app/views/admin/users/_projects.html.haml new file mode 100644 index 00000000000..a126a858ea8 --- /dev/null +++ b/app/views/admin/users/_projects.html.haml @@ -0,0 +1,13 @@ +- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present? + .panel.panel-default.contributed-projects + .panel-heading Projects contributed to + = render 'shared/projects/list', + projects: contributed_projects.sort_by(&:star_count).reverse, + projects_limit: 5, stars: true, avatar: false + +- if local_assigns.has_key?(:projects) && projects.present? + .panel.panel-default + .panel-heading Personal projects + = render 'shared/projects/list', + projects: projects.sort_by(&:star_count).reverse, + projects_limit: 10, stars: true, avatar: false diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml index 0d7a1a25a80..b655b2a15f5 100644 --- a/app/views/admin/users/projects.html.haml +++ b/app/views/admin/users/projects.html.haml @@ -14,7 +14,7 @@ .row .col-md-6 - if @personal_projects.present? - = render 'users/projects', projects: @personal_projects + = render 'admin/users/projects', projects: @personal_projects - else .nothing-here-block This user has no personal projects. -- cgit v1.2.1 From 59cc61e246dd059d041793cf4fd29ee540c7d7d2 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 25 Nov 2015 17:01:02 -0500 Subject: Bump doorkeeper to ~> 2.2.0 Closes #2746 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 4762e2e223f..491a6acd43b 100644 --- a/Gemfile +++ b/Gemfile @@ -16,7 +16,7 @@ gem "pg", '~> 0.18.2', group: :postgres # Authentication libraries gem 'devise', '~> 3.5.2' gem 'devise-async', '~> 0.9.0' -gem 'doorkeeper', '~> 2.1.3' +gem 'doorkeeper', '~> 2.2.0' gem 'omniauth', '~> 1.2.2' gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-facebook', '~> 3.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 46247dd88e2..3cb986b39d7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -164,7 +164,7 @@ GEM docile (1.1.5) domain_name (0.5.24) unf (>= 0.0.5, < 1.0.0) - doorkeeper (2.1.4) + doorkeeper (2.2.2) railties (>= 3.2) dropzonejs-rails (0.7.1) rails (> 3.1) @@ -824,7 +824,7 @@ DEPENDENCIES devise-async (~> 0.9.0) devise-two-factor (~> 2.0.0) diffy (~> 3.0.3) - doorkeeper (~> 2.1.3) + doorkeeper (~> 2.2.0) dropzonejs-rails (~> 0.7.1) email_reply_parser (~> 0.5.8) email_spec (~> 1.6.0) -- cgit v1.2.1 From 91c0aa5e982ee5e3299e6b8c899bfc396fe51279 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 25 Nov 2015 17:03:30 -0500 Subject: Bump asana to ~> 0.4.0 Closes #2830 --- Gemfile | 2 +- Gemfile.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 4762e2e223f..8325c4d8e6a 100644 --- a/Gemfile +++ b/Gemfile @@ -153,7 +153,7 @@ gem "gemnasium-gitlab-service", "~> 0.2" gem "slack-notifier", "~> 1.2.0" # Asana integration -gem 'asana', '~> 0.0.6' +gem 'asana', '~> 0.4.0' # FogBugz integration gem 'ruby-fogbugz', '~> 0.2.1' diff --git a/Gemfile.lock b/Gemfile.lock index 46247dd88e2..24aadef5c76 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -29,10 +29,6 @@ GEM actionpack (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5) railties (>= 4.0.0, < 5) - activeresource (4.0.0) - activemodel (~> 4.0) - activesupport (~> 4.0) - rails-observers (~> 0.1.1) activesupport (4.1.14) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) @@ -48,8 +44,11 @@ GEM activerecord (>= 3.2, <= 4.3) rake (~> 10.4) arel (5.0.1.20140414130214) - asana (0.0.6) - activeresource (>= 3.2.3) + asana (0.4.0) + faraday (~> 0.9) + faraday_middleware (~> 0.9) + faraday_middleware-multi_json (~> 0.0) + oauth2 (~> 1.0) asciidoctor (1.5.2) ast (2.1.0) astrolabe (1.3.1) @@ -191,6 +190,9 @@ GEM multipart-post (>= 1.2, < 3) faraday_middleware (0.10.0) faraday (>= 0.7.4, < 0.10) + faraday_middleware-multi_json (0.0.6) + faraday_middleware + multi_json fastercsv (1.5.5) ffaker (2.0.0) ffi (1.9.10) @@ -523,8 +525,6 @@ GEM bundler (>= 1.3.0, < 2.0) railties (= 4.1.14) sprockets-rails (~> 2.0) - rails-observers (0.1.2) - activemodel (~> 4.0) railties (4.1.14) actionpack (= 4.1.14) activesupport (= 4.1.14) @@ -795,7 +795,7 @@ DEPENDENCIES addressable (~> 2.3.8) after_commit_queue annotate (~> 2.6.0) - asana (~> 0.0.6) + asana (~> 0.4.0) asciidoctor (~> 1.5.2) attr_encrypted (~> 1.3.4) awesome_print (~> 1.2.0) -- cgit v1.2.1 From 5f0d1f30b0f0e31d31a2eb0db9f92776bf551f26 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 25 Nov 2015 17:06:04 -0500 Subject: Remove enumerize gem --- Gemfile | 3 --- Gemfile.lock | 3 --- app/models/project.rb | 3 +-- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 4762e2e223f..1beeabd44d2 100644 --- a/Gemfile +++ b/Gemfile @@ -62,9 +62,6 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' # based on human-friendly examples gem "stamp", '~> 0.6.0' -# Enumeration fields -gem 'enumerize', '~> 0.7.0' - # Pagination gem "kaminari", "~> 0.16.3" diff --git a/Gemfile.lock b/Gemfile.lock index 46247dd88e2..ef083754f0f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -173,8 +173,6 @@ GEM launchy (~> 2.1) mail (~> 2.2) encryptor (1.3.0) - enumerize (0.7.0) - activesupport (>= 3.2) equalizer (0.0.11) erubis (2.7.0) escape_utils (1.1.0) @@ -828,7 +826,6 @@ DEPENDENCIES dropzonejs-rails (~> 0.7.1) email_reply_parser (~> 0.5.8) email_spec (~> 1.6.0) - enumerize (~> 0.7.0) factory_girl_rails (~> 4.3.0) ffaker (~> 2.0.0) flay diff --git a/app/models/project.rb b/app/models/project.rb index f0a4b6aae7b..6010770a5f2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -42,9 +42,8 @@ class Project < ActiveRecord::Base include Sortable include AfterCommitQueue include CaseSensitivity - + extend Gitlab::ConfigHelper - extend Enumerize UNKNOWN_IMPORT_URL = 'http://unknown.git' -- cgit v1.2.1 From 7adfb30c349396f19b71a703419a475bd1d4cd5b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 17:06:36 -0800 Subject: Remove duplicate entry shipped in 8.1.4 --- CHANGELOG | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..18381984177 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,7 +14,6 @@ v 8.2.0 - Expose build artifacts path as config option - Fix grouping of contributors by email in graph. - Improved performance of finding issues with/without labels - - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Fix Drone CI service template not saving properly (Stan Hu) - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu) - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) -- cgit v1.2.1 From 2497d3d550dc0d4e095c8c3fe75d4452fb163252 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 23:11:35 -0800 Subject: Fix 404 in redirection after removing a project Closes https://github.com/gitlabhq/gitlabhq/issues/9844 Closes #3559 --- CHANGELOG | 1 + app/controllers/projects_controller.rb | 2 +- spec/controllers/projects_controller_spec.rb | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..3dd96e34dcf 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.3.0 (unreleased) + - Fix 404 in redirection after removing a project (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 23453195e85..10c75370d7b 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -123,7 +123,7 @@ class ProjectsController < ApplicationController ::Projects::DestroyService.new(@project, current_user, {}).execute flash[:alert] = "Project '#{@project.name}' was deleted." - redirect_back_or_default(default: dashboard_projects_path, options: {}) + redirect_to dashboard_projects_path rescue Projects::DestroyService::DestroyError => ex redirect_to edit_project_path(@project), alert: ex.message end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 4bb47c6b025..665526fde93 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -88,6 +88,22 @@ describe ProjectsController do end end + describe "#destroy" do + let(:admin) { create(:admin) } + + it "redirects to the dashboard" do + controller.instance_variable_set(:@project, project) + sign_in(admin) + + orig_id = project.id + delete :destroy, namespace_id: project.namespace.path, id: project.path + + expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound) + expect(response.status).to eq(302) + expect(response).to redirect_to(dashboard_projects_path) + end + end + describe "POST #toggle_star" do it "toggles star if user is signed in" do sign_in(user) -- cgit v1.2.1 From 38489d8d7d0a352789c23d3bf9cadb169f765d5b Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 26 Nov 2015 11:57:04 +0200 Subject: update test_after_commit gem to 0.4.2 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 4a9f70a1297..dc801d60b22 100644 --- a/Gemfile +++ b/Gemfile @@ -274,7 +274,7 @@ group :test do gem 'shoulda-matchers', '~> 2.8.0', require: false gem 'email_spec', '~> 1.6.0' gem 'webmock', '~> 1.21.0' - gem 'test_after_commit', '~> 0.2.2' + gem 'test_after_commit', '~> 0.4.2' gem 'sham_rack' end diff --git a/Gemfile.lock b/Gemfile.lock index e9460515051..bb4f94aeef9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -750,7 +750,7 @@ GEM term-ansicolor (1.3.2) tins (~> 1.0) terminal-table (1.5.2) - test_after_commit (0.2.7) + test_after_commit (0.4.2) activerecord (>= 3.2) thin (1.6.4) daemons (~> 1.0, >= 1.0.9) @@ -970,7 +970,7 @@ DEPENDENCIES task_list (~> 1.0.2) teaspoon (~> 1.0.0) teaspoon-jasmine (~> 2.2.0) - test_after_commit (~> 0.2.2) + test_after_commit (~> 0.4.2) thin (~> 1.6.1) tinder (~> 1.10.0) turbolinks (~> 2.5.0) -- cgit v1.2.1 From 7f214cee74796ceaf7b01bd6e133d4d54c5123db Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 26 Nov 2015 15:48:01 +0200 Subject: Migrate mailers to ActiveJob --- Gemfile | 1 + Gemfile.lock | 1 + Procfile | 2 +- app/controllers/abuse_reports_controller.rb | 2 +- app/mailers/base_mailer.rb | 4 -- app/models/project_services/ci/mail_service.rb | 2 +- app/services/notification_service.rb | 74 ++++++++++++++++++-------- app/workers/email_receiver_worker.rb | 2 +- config/application.rb | 4 ++ config/environments/production.rb | 2 +- config/environments/test.rb | 2 +- config/initializers/static_files.rb | 2 +- lib/gitlab/markdown/label_reference_filter.rb | 3 +- lib/gitlab/seeder.rb | 2 +- 14 files changed, 67 insertions(+), 36 deletions(-) diff --git a/Gemfile b/Gemfile index 808c5df7caf..91c909a2e7d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ source "https://rubygems.org" gem 'rails', '4.2.4' +gem 'rails-deprecated_sanitizer', '~> 1.0.3' # Responders respond_to and respond_with gem 'responders', '~> 2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 1671edbc6fd..08001379910 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -930,6 +930,7 @@ DEPENDENCIES rack-cors (~> 0.4.0) rack-oauth2 (~> 1.2.1) rails (= 4.2.4) + rails-deprecated_sanitizer (~> 1.0.3) raphael-rails (~> 2.1.2) rblineprof rdoc (~> 3.6) diff --git a/Procfile b/Procfile index 08880b9c425..fd5f7ecb94b 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,3 @@ web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} -worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default +worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q mailers -q default # mail_room: bundle exec mail_room -q -c config/mail_room.yml diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 2f4054eaa11..d8e90594332 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -10,7 +10,7 @@ class AbuseReportsController < ApplicationController if @abuse_report.save if current_application_settings.admin_notification_email.present? - AbuseReportMailer.delay.notify(@abuse_report.id) + AbuseReportMailer.deliver_later.notify(@abuse_report.id) end message = "Thank you for your report. A GitLab administrator will look into it shortly." diff --git a/app/mailers/base_mailer.rb b/app/mailers/base_mailer.rb index aedb0889185..8b83bbd93b7 100644 --- a/app/mailers/base_mailer.rb +++ b/app/mailers/base_mailer.rb @@ -8,10 +8,6 @@ class BaseMailer < ActionMailer::Base default from: Proc.new { default_sender_address.format } default reply_to: Proc.new { default_reply_to_address.format } - def self.delay - delay_for(2.seconds) - end - def can? Ability.abilities.allowed?(current_user, action, subject) end diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb index d31dd6899c1..bdc85667e9d 100644 --- a/app/models/project_services/ci/mail_service.rb +++ b/app/models/project_services/ci/mail_service.rb @@ -78,7 +78,7 @@ module Ci end def mailer - Ci::Notify.delay + Ci::Notify.deliver_later end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index d6550fbb555..03fe8c8fe11 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -13,14 +13,14 @@ class NotificationService # even if user disabled notifications def new_key(key) if key.user - mailer.new_ssh_key_email(key.id) + mailer.new_ssh_key_email(key.id).deliver_later end end # Always notify user about email added to profile def new_email(email) if email.user - mailer.new_email_email(email.id) + mailer.new_email_email(email.id).deliver_later end end @@ -79,17 +79,27 @@ class NotificationService end def merge_mr(merge_request, current_user) - close_resource_email(merge_request, merge_request.target_project, current_user, 'merged_merge_request_email') + close_resource_email( + merge_request, + merge_request.target_project, + current_user, + 'merged_merge_request_email' + ) end def reopen_mr(merge_request, current_user) - reopen_resource_email(merge_request, merge_request.target_project, current_user, 'merge_request_status_email', 'reopened') + reopen_resource_email( + merge_request, + merge_request.target_project, + current_user, 'merge_request_status_email', + 'reopened' + ) end # Notify new user with email after creation def new_user(user, token = nil) # Don't email omniauth created users - mailer.new_user_email(user.id, token) unless user.identities.any? + mailer.new_user_email(user.id, token).deliver_later unless user.identities.any? end # Notify users on new note in system @@ -140,48 +150,58 @@ class NotificationService notify_method = "note_#{note.noteable_type.underscore}_email".to_sym recipients.each do |recipient| - mailer.send(notify_method, recipient.id, note.id) + mailer.send(notify_method, recipient.id, note.id).deliver_later end end def invite_project_member(project_member, token) - mailer.project_member_invited_email(project_member.id, token) + mailer.project_member_invited_email(project_member.id, token).deliver_later end def accept_project_invite(project_member) - mailer.project_invite_accepted_email(project_member.id) + mailer.project_invite_accepted_email(project_member.id).deliver_later end def decline_project_invite(project_member) - mailer.project_invite_declined_email(project_member.project.id, project_member.invite_email, project_member.access_level, project_member.created_by_id) + mailer.project_invite_declined_email( + project_member.project.id, + project_member.invite_email, + project_member.access_level, + project_member.created_by_id + ).deliver_later end def new_project_member(project_member) - mailer.project_access_granted_email(project_member.id) + mailer.project_access_granted_email(project_member.id).deliver_later end def update_project_member(project_member) - mailer.project_access_granted_email(project_member.id) + mailer.project_access_granted_email(project_member.id).deliver_later end def invite_group_member(group_member, token) - mailer.group_member_invited_email(group_member.id, token) + mailer.group_member_invited_email(group_member.id, token).deliver_later end def accept_group_invite(group_member) - mailer.group_invite_accepted_email(group_member.id) + mailer.group_invite_accepted_email(group_member.id).deliver_later end def decline_group_invite(group_member) - mailer.group_invite_declined_email(group_member.group.id, group_member.invite_email, group_member.access_level, group_member.created_by_id) + mailer.group_invite_declined_email( + group_member.group.id, + group_member.invite_email, + group_member.access_level, + group_member.created_by_id + ).deliver_later end def new_group_member(group_member) - mailer.group_access_granted_email(group_member.id) + mailer.group_access_granted_email(group_member.id).deliver_later end def update_group_member(group_member) - mailer.group_access_granted_email(group_member.id) + mailer.group_access_granted_email(group_member.id).deliver_later end def project_was_moved(project, old_path_with_namespace) @@ -189,7 +209,11 @@ class NotificationService recipients = reject_muted_users(recipients, project) recipients.each do |recipient| - mailer.project_was_moved_email(project.id, recipient.id, old_path_with_namespace) + mailer.project_was_moved_email( + project.id, + recipient.id, + old_path_with_namespace + ).deliver_later end end @@ -339,7 +363,7 @@ class NotificationService recipients = build_recipients(target, project, target.author) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id) + mailer.send(method, recipient.id, target.id).deliver_later end end @@ -347,7 +371,7 @@ class NotificationService recipients = build_recipients(target, project, current_user) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, current_user.id) + mailer.send(method, recipient.id, target.id, current_user.id).deliver end end @@ -358,7 +382,13 @@ class NotificationService recipients = build_recipients(target, project, current_user, [previous_assignee]) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, previous_assignee_id, current_user.id) + mailer.send( + method, + recipient.id, + target.id, + previous_assignee_id, + current_user.id + ).deliver_later end end @@ -366,7 +396,7 @@ class NotificationService recipients = build_recipients(target, project, current_user) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, status, current_user.id) + mailer.send(method, recipient.id, target.id, status, current_user.id).deliver end end @@ -388,7 +418,7 @@ class NotificationService end def mailer - Notify.delay + Notify end def previous_record(object, attribute) diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb index 5a921a73fe9..1df8de1db79 100644 --- a/app/workers/email_receiver_worker.rb +++ b/app/workers/email_receiver_worker.rb @@ -46,6 +46,6 @@ class EmailReceiverWorker return end - EmailRejectionMailer.delay.rejection(reason, raw, can_retry) + EmailRejectionMailer.deliver_later.rejection(reason, raw, can_retry) end end diff --git a/config/application.rb b/config/application.rb index bfa2a809dd7..d255ff0719f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -99,6 +99,10 @@ module Gitlab redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever config.cache_store = :redis_store, redis_config_hash + config.active_record.raise_in_transactional_callbacks = true + + config.active_job.queue_adapter = :sidekiq + # This is needed for gitlab-shell ENV['GITLAB_PATH_OUTSIDE_HOOK'] = ENV['PATH'] end diff --git a/config/environments/production.rb b/config/environments/production.rb index e8250d66452..317b113e100 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -9,7 +9,7 @@ Rails.application.configure do config.action_controller.perform_caching = true # Disable Rails's static asset server (Apache or nginx will already do this) - config.serve_static_assets = false + config.serve_static_files = false # Compress JavaScripts and CSS. config.assets.js_compressor = :uglifier diff --git a/config/environments/test.rb b/config/environments/test.rb index 46982be2864..2eddf0605d2 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -8,7 +8,7 @@ Rails.application.configure do config.cache_classes = false # Configure static asset server for tests with Cache-Control for performance - config.serve_static_assets = true + config.serve_static_files = true config.static_cache_control = "public, max-age=3600" # Show full error reports and disable caching diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb index d9042c652bb..d6dbf8b9fbf 100644 --- a/config/initializers/static_files.rb +++ b/config/initializers/static_files.rb @@ -1,6 +1,6 @@ app = Rails.application -if app.config.serve_static_assets +if app.config.serve_static_files # The `ActionDispatch::Static` middleware intercepts requests for static files # by checking if they exist in the `/public` directory. # We're replacing it with our `Gitlab::Middleware::Static` that does the same, diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index 618acb7a578..13581b8fb13 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -60,8 +60,7 @@ module Gitlab def url_for_label(project, label) h = Gitlab::Application.routes.url_helpers h.namespace_project_issues_path(project.namespace, project, - label_name: label.name, - only_path: context[:only_path]) + label_name: label.name) end def render_colored_label(label) diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb index 31aa3528c4c..2ef0e982256 100644 --- a/lib/gitlab/seeder.rb +++ b/lib/gitlab/seeder.rb @@ -14,7 +14,7 @@ module Gitlab def self.mute_mailer code = <<-eos -def Notify.delay +def Notify.deliver_later self end eos -- cgit v1.2.1 From b9df1a63550c78396d43b661bd24d2745604f6fc Mon Sep 17 00:00:00 2001 From: Jose Corcuera Date: Thu, 26 Nov 2015 10:16:50 -0500 Subject: Strip attributes for Milestone and Issuable. #3428 --- CHANGELOG | 1 + app/controllers/projects/issues_controller.rb | 4 +-- .../projects/merge_requests_controller.rb | 4 +-- app/models/concerns/issuable.rb | 2 ++ app/models/concerns/strip_attribute.rb | 34 ++++++++++++++++++++++ app/models/milestone.rb | 3 ++ spec/models/concerns/strip_attribute_spec.rb | 20 +++++++++++++ 7 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 app/models/concerns/strip_attribute.rb create mode 100644 spec/models/concerns/strip_attribute_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 0e1e1a3671d..39a57231d9c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - Fix: Raw private snippets access workflow + - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 5250a0f5e67..ae474cf8d68 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -158,12 +158,10 @@ class Projects::IssuesController < Projects::ApplicationController end def issue_params - permitted = params.require(:issue).permit( + params.require(:issue).permit( :title, :assignee_id, :position, :description, :milestone_id, :state_event, :task_num, label_ids: [] ) - params[:issue][:title].strip! if params[:issue][:title] - permitted end def bulk_update_params diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 6378a1f56b0..3f47f2ddb2c 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -276,13 +276,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def merge_request_params - permitted = params.require(:merge_request).permit( + params.require(:merge_request).permit( :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :state_event, :description, :task_num, label_ids: [] ) - params[:merge_request][:title].strip! if params[:merge_request][:title] - permitted end # Make sure merge requests created before 8.0 diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 68138688aab..badeadfa418 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -8,6 +8,7 @@ module Issuable extend ActiveSupport::Concern include Participable include Mentionable + include StripAttribute included do belongs_to :author, class_name: "User" @@ -51,6 +52,7 @@ module Issuable attr_mentionable :title, :description participant :author, :assignee, :notes_with_associations + strip_attributes :title end module ClassMethods diff --git a/app/models/concerns/strip_attribute.rb b/app/models/concerns/strip_attribute.rb new file mode 100644 index 00000000000..8806ebe897a --- /dev/null +++ b/app/models/concerns/strip_attribute.rb @@ -0,0 +1,34 @@ +# == Strip Attribute module +# +# Contains functionality to clean attributes before validation +# +# Usage: +# +# class Milestone < ActiveRecord::Base +# strip_attributes :title +# end +# +# +module StripAttribute + extend ActiveSupport::Concern + + module ClassMethods + def strip_attributes(*attrs) + strip_attrs.concat(attrs) + end + + def strip_attrs + @strip_attrs ||= [] + end + end + + included do + before_validation :strip_attributes + end + + def strip_attributes + self.class.strip_attrs.each do |attr| + self[attr].strip! if self[attr] && self[attr].respond_to?(:strip!) + end + end +end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 2ff16e2825c..c2642b75b8a 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -22,6 +22,7 @@ class Milestone < ActiveRecord::Base include InternalId include Sortable + include StripAttribute belongs_to :project has_many :issues @@ -35,6 +36,8 @@ class Milestone < ActiveRecord::Base validates :title, presence: true validates :project, presence: true + strip_attributes :title + state_machine :state, initial: :active do event :close do transition active: :closed diff --git a/spec/models/concerns/strip_attribute_spec.rb b/spec/models/concerns/strip_attribute_spec.rb new file mode 100644 index 00000000000..6445e29c3ef --- /dev/null +++ b/spec/models/concerns/strip_attribute_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Milestone, "StripAttribute" do + let(:milestone) { create(:milestone) } + + describe ".strip_attributes" do + it { expect(Milestone).to respond_to(:strip_attributes) } + it { expect(Milestone.strip_attrs).to include(:title) } + end + + describe "#strip_attributes" do + before do + milestone.title = ' 8.3 ' + milestone.valid? + end + + it { expect(milestone.title).to eq('8.3') } + end + +end -- cgit v1.2.1 From 78c1ab40e20f2c412719a2140d9de61ada26d1b8 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 26 Nov 2015 07:55:21 -0800 Subject: Gracefully handle when Redis is not available --- config/initializers/1_settings.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 80b480eac37..444e91109df 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -295,5 +295,10 @@ if Rails.env.test? end # Force a refresh of application settings at startup -ApplicationSetting.expire -Ci::ApplicationSetting.expire +begin + ApplicationSetting.expire + Ci::ApplicationSetting.expire +rescue + # Gracefully handle when Redis is not available. For example, + # omnibus may fail here during assets:precompile. +end -- cgit v1.2.1 From 295d378e9ab1a8e052e35c6e6fedf8d6890b74d0 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 27 Nov 2015 13:56:26 +0100 Subject: Repeat "client_max_body_size 0" everywhere It turns out that if we do not the declaration from "location /" wins. --- lib/support/nginx/gitlab | 6 ++++++ lib/support/nginx/gitlab-ssl | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 0cf5292b290..1dfcac91cdc 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -114,24 +114,28 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/api/v3/projects/.*/repository/archive { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -139,6 +143,7 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -146,6 +151,7 @@ server { # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 31a651c87fd..7cd32de073b 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -161,24 +161,28 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/api/v3/projects/.*/repository/archive { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -186,6 +190,7 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -193,6 +198,7 @@ server { # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; -- cgit v1.2.1 From 04049b6b17c9354555115b5d2f049daffa311c16 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 27 Nov 2015 13:57:53 +0100 Subject: Fix indentation in NGINX config --- lib/support/nginx/gitlab-ssl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 7cd32de073b..016f7a536fb 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -191,17 +191,17 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; } # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; } location @gitlab-workhorse { -- cgit v1.2.1 From f1710073b46c940245d38b46e5436c0745223aee Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 27 Nov 2015 14:39:55 -0500 Subject: Fix alignment [ci skip] --- lib/support/nginx/gitlab | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 1dfcac91cdc..2a79fbdcf93 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -144,17 +144,17 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; } # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; } location @gitlab-workhorse { -- cgit v1.2.1 From 3723182a8831595502570c90211610abb4770781 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 27 Nov 2015 16:15:06 -0500 Subject: Move project visibility level off of the clone panel --- app/assets/stylesheets/pages/projects.scss | 5 +++++ app/views/projects/_home_panel.html.haml | 10 ++++++---- app/views/shared/_clone_panel.html.haml | 4 ---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index ad607ead2bf..4a0fe546844 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -90,7 +90,12 @@ } .visibility-level-label { + @extend .btn; + @extend .btn-gray; + color: $gray; + cursor: auto; + i { color: inherit; } diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 88d54bf6f21..b30036966a7 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,5 +1,5 @@ - empty_repo = @project.empty_repo? -.project-home-panel.clearfix{:class => ("empty-project" if empty_repo)} +.project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)} .project-identicon-holder = project_icon(@project, alt: '', class: 'project-avatar avatar s90') .project-home-desc @@ -12,8 +12,10 @@ Forked from = link_to project_path(forked_from_project) do = forked_from_project.namespace.try(:name) - - + .cover-controls + .visibility-level-label + = visibility_level_icon(@project.visibility_level) + = visibility_level_label(@project.visibility_level) .project-repo-buttons .split-one @@ -21,7 +23,7 @@ = render 'projects/buttons/fork' = render "shared/clone_panel" - + .split-repo-buttons = render "projects/buttons/download" = render 'projects/buttons/dropdown' diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 408a46aaafc..edb5778f424 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -8,7 +8,3 @@ = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true .input-group-btn = clipboard_button(clipboard_target: '#project_clone') - - if project.kind_of?(Project) - .input-group-addon.has_tooltip{title: "#{visibility_level_label(project.visibility_level)} project", data: { container: "body" } } - .visibility-level-label - = visibility_level_icon(project.visibility_level) -- cgit v1.2.1 From 344cb89718d9424e239a24dbc888d7b7a98cb474 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 27 Nov 2015 16:34:59 -0500 Subject: Bump jquery-turbolinks to ~> 2.1.0 See #2857 --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 685b126a793..51ce6c9ed72 100644 --- a/Gemfile +++ b/Gemfile @@ -183,7 +183,7 @@ gem "sass-rails", '~> 4.0.5' gem "coffee-rails", '~> 4.1.0' gem "uglifier", '~> 2.7.2' gem 'turbolinks', '~> 2.5.0' -gem 'jquery-turbolinks', '~> 2.0.1' +gem 'jquery-turbolinks', '~> 2.1.0' gem 'addressable', '~> 2.3.8' gem 'bootstrap-sass', '~> 3.0' diff --git a/Gemfile.lock b/Gemfile.lock index 8fb2ef778ea..54a63ea22df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -121,7 +121,7 @@ GEM coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.9.1.1) + coffee-script-source (1.10.0) colorize (0.7.7) connection_pool (2.2.0) coveralls (0.8.2) @@ -370,7 +370,7 @@ GEM thor (>= 0.14, < 2.0) jquery-scrollto-rails (1.4.3) railties (> 3.1, < 5.0) - jquery-turbolinks (2.0.2) + jquery-turbolinks (2.1.0) railties (>= 3.1.0) turbolinks jquery-ui-rails (4.2.1) @@ -853,7 +853,7 @@ DEPENDENCIES jquery-atwho-rails (~> 1.3.2) jquery-rails (~> 3.1.3) jquery-scrollto-rails (~> 1.4.3) - jquery-turbolinks (~> 2.0.1) + jquery-turbolinks (~> 2.1.0) jquery-ui-rails (~> 4.2.1) kaminari (~> 0.16.3) letter_opener (~> 1.1.2) -- cgit v1.2.1 From 85ad2140fa4ef2209b9d217403e183e33ce4dd37 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 28 Nov 2015 20:53:17 +0100 Subject: Remove reverence to satellites [ci skip] --- doc/raketasks/backup_restore.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 4e645b21a85..b4d2786bd76 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -274,9 +274,6 @@ sudo gitlab-rake gitlab:backup:restore BACKUP=1393513186 # Start GitLab sudo gitlab-ctl start -# Create satellites -sudo gitlab-rake gitlab:satellites:create - # Check GitLab sudo gitlab-rake gitlab:check SANITIZE=true ``` -- cgit v1.2.1 From 7d6323a9d4ada6be6c00f7d160b366219952f77f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Sun, 29 Nov 2015 11:51:02 +0200 Subject: Update phantomjs to 2.0.0 * Version 1.9 was removed from upstream Debian repos --- scripts/prepare_build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index dca5e1c5db3..5ff4d7fa76a 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -1,7 +1,7 @@ #!/bin/bash if [ -f /.dockerinit ]; then - wget -q http://ftp.de.debian.org/debian/pool/main/p/phantomjs/phantomjs_1.9.0-1+b1_amd64.deb - dpkg -i phantomjs_1.9.0-1+b1_amd64.deb + wget -q http://ftp.de.debian.org/debian/pool/main/p/phantomjs/phantomjs_2.0.0+dfsg-1_amd64.deb + dpkg -i phantomjs_2.0.0+dfsg-1_amd64.deb apt-get update -qq apt-get install -y -qq libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client -- cgit v1.2.1 From bb6ce2bb10734cfc45e488378acaa17000fdacc2 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Sun, 29 Nov 2015 13:44:19 +0200 Subject: Test using a 1.9.8 phantomjs version built with fpm https://gitlab.com/axil/phantomjs-debian --- scripts/prepare_build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index 5ff4d7fa76a..119cc90fc1e 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -1,7 +1,7 @@ #!/bin/bash if [ -f /.dockerinit ]; then - wget -q http://ftp.de.debian.org/debian/pool/main/p/phantomjs/phantomjs_2.0.0+dfsg-1_amd64.deb - dpkg -i phantomjs_2.0.0+dfsg-1_amd64.deb + wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb + dpkg -i phantomjs_1.9.8-0jessie_amd64.deb apt-get update -qq apt-get install -y -qq libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client -- cgit v1.2.1 From e1522ec85582962c12f875a46a5853d6ae570d0b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 29 Nov 2015 17:52:40 -0500 Subject: Simplify `build_gitlab_shell_ssh_path_prefix` --- config/initializers/1_settings.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index b162b8a83fc..abe08d57186 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -33,13 +33,15 @@ class Settings < Settingslogic end def build_gitlab_shell_ssh_path_prefix + user_host = "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}" + if gitlab_shell.ssh_port != 22 - "ssh://#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:#{gitlab_shell.ssh_port}/" + "ssh://#{user_host}:#{gitlab_shell.ssh_port}/" else if gitlab_shell.ssh_host.include? ':' - "[#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}]:" + "[#{user_host}]:" else - "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:" + "#{user_host}:" end end end -- cgit v1.2.1 From 461731f0769a826d00c4d5846ff6d2f55fd4b829 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 30 Nov 2015 11:21:10 +0200 Subject: fix notification_service specs --- app/services/notification_service.rb | 5 +- config/environments/test.rb | 2 + spec/services/notification_service_spec.rb | 345 ++++++++++++----------------- spec/spec_helper.rb | 1 + 4 files changed, 144 insertions(+), 209 deletions(-) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 03fe8c8fe11..388a4defb26 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -148,7 +148,6 @@ class NotificationService # build notify method like 'note_commit_email' notify_method = "note_#{note.noteable_type.underscore}_email".to_sym - recipients.each do |recipient| mailer.send(notify_method, recipient.id, note.id).deliver_later end @@ -371,7 +370,7 @@ class NotificationService recipients = build_recipients(target, project, current_user) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, current_user.id).deliver + mailer.send(method, recipient.id, target.id, current_user.id).deliver_later end end @@ -396,7 +395,7 @@ class NotificationService recipients = build_recipients(target, project, current_user) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, status, current_user.id).deliver + mailer.send(method, recipient.id, target.id, status, current_user.id).deliver_later end end diff --git a/config/environments/test.rb b/config/environments/test.rb index 2eddf0605d2..f96ac6f9753 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -32,4 +32,6 @@ Rails.application.configure do config.eager_load = false config.cache_store = :null_store + + config.active_job.queue_adapter = :test end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 520140917aa..a4e2b2953cc 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -3,6 +3,12 @@ require 'spec_helper' describe NotificationService do let(:notification) { NotificationService.new } + around(:each) do |example| + perform_enqueued_jobs do + example.run + end + end + describe 'Keys' do describe :new_key do let!(:key) { create(:personal_key) } @@ -10,8 +16,7 @@ describe NotificationService do it { expect(notification.new_key(key)).to be_truthy } it 'should sent email to key owner' do - expect(Notify).to receive(:new_ssh_key_email).with(key.id) - notification.new_key(key) + expect{ notification.new_key(key) }.to change{ ActionMailer::Base.deliveries.size }.by(1) end end end @@ -23,8 +28,7 @@ describe NotificationService do it { expect(notification.new_email(email)).to be_truthy } it 'should send email to email owner' do - expect(Notify).to receive(:new_email_email).with(email.id) - notification.new_email(email) + expect{ notification.new_email(email) }.to change{ ActionMailer::Base.deliveries.size }.by(1) end end end @@ -47,18 +51,20 @@ describe NotificationService do it do add_users_with_subscription(note.project, issue) - should_email(@u_watcher.id) - should_email(note.noteable.author_id) - should_email(note.noteable.assignee_id) - should_email(@u_mentioned.id) - should_email(@subscriber.id) - should_not_email(note.author_id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_outsider_mentioned) + ActionMailer::Base.deliveries.clear notification.new_note(note) + + should_email(@u_watcher) + should_email(note.noteable.author) + should_email(note.noteable.assignee) + should_email(@u_mentioned) + should_email(@subscriber) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) + should_not_email(@unsubscriber) + should_not_email(@u_outsider_mentioned) end it 'filters out "mentioned in" notes' do @@ -82,26 +88,20 @@ describe NotificationService do group_member = note.project.group.group_members.find_by_user_id(@u_watcher.id) group_member.notification_level = Notification::N_GLOBAL group_member.save + ActionMailer::Base.deliveries.clear end it do - should_email(note.noteable.author_id) - should_email(note.noteable.assignee_id) - should_email(@u_mentioned.id) - should_not_email(@u_watcher.id) - should_not_email(note.author_id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.new_note(note) - end - end - def should_email(user_id) - expect(Notify).to receive(:note_issue_email).with(user_id, note.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id) + should_email(note.noteable.author) + should_email(note.noteable.assignee) + should_email(@u_mentioned) + should_not_email(@u_watcher) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) + end end end @@ -113,24 +113,26 @@ describe NotificationService do before do build_team(note.project) + ActionMailer::Base.deliveries.clear end describe :new_note do it do + notification.new_note(note) + # Notify all team members note.project.team.members.each do |member| # User with disabled notification should not be notified next if member.id == @u_disabled.id - should_email(member.id) + should_email(member) end - should_email(note.noteable.author_id) - should_email(note.noteable.assignee_id) - should_not_email(note.author_id) - should_not_email(@u_mentioned.id) - should_not_email(@u_disabled.id) - should_not_email(@u_not_mentioned.id) - notification.new_note(note) + should_email(note.noteable.author) + should_email(note.noteable.assignee) + should_not_email(note.author) + should_email(@u_mentioned) + should_not_email(@u_disabled) + should_email(@u_not_mentioned) end it 'filters out "mentioned in" notes' do @@ -140,14 +142,6 @@ describe NotificationService do notification.new_note(mentioned_note) end end - - def should_email(user_id) - expect(Notify).to receive(:note_issue_email).with(user_id, note.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id) - end end context 'commit note' do @@ -156,43 +150,38 @@ describe NotificationService do before do build_team(note.project) + ActionMailer::Base.deliveries.clear allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer) end - describe :new_note do + describe :new_note, :perform_enqueued_jobs do it do - should_email(@u_committer.id, note) - should_email(@u_watcher.id, note) - should_not_email(@u_mentioned.id, note) - should_not_email(note.author_id, note) - should_not_email(@u_participating.id, note) - should_not_email(@u_disabled.id, note) notification.new_note(note) + + should_email(@u_committer) + should_email(@u_watcher) + should_not_email(@u_mentioned) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) end it do note.update_attribute(:note, '@mention referenced') - should_email(@u_committer.id, note) - should_email(@u_watcher.id, note) - should_email(@u_mentioned.id, note) - should_not_email(note.author_id, note) - should_not_email(@u_participating.id, note) - should_not_email(@u_disabled.id, note) notification.new_note(note) + + should_email(@u_committer) + should_email(@u_watcher) + should_email(@u_mentioned) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) end it do @u_committer.update_attributes(notification_level: Notification::N_MENTION) - should_not_email(@u_committer.id, note) notification.new_note(note) - end - - def should_email(user_id, n) - expect(Notify).to receive(:note_commit_email).with(user_id, n.id) - end - - def should_not_email(user_id, n) - expect(Notify).not_to receive(:note_commit_email).with(user_id, n.id) + should_not_email(@u_committer) end end end @@ -205,99 +194,69 @@ describe NotificationService do before do build_team(issue.project) add_users_with_subscription(issue.project, issue) + ActionMailer::Base.deliveries.clear end describe :new_issue do it do - should_email(issue.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_not_email(@u_mentioned.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.new_issue(issue, @u_disabled) + + should_email(issue.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_not_email(@u_mentioned) + should_not_email(@u_participating) + should_not_email(@u_disabled) end it do issue.assignee.update_attributes(notification_level: Notification::N_MENTION) - should_not_email(issue.assignee_id) notification.new_issue(issue, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:new_issue_email).with(user_id, issue.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:new_issue_email).with(user_id, issue.id) + should_not_email(issue.assignee) end end describe :reassigned_issue do it 'should email new assignee' do - should_email(issue.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.reassigned_issue(issue, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:reassigned_issue_email).with(user_id, issue.id, nil, @u_disabled.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id, @u_disabled.id) + should_email(issue.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :close_issue do it 'should sent email to issue assignee and issue author' do - should_email(issue.assignee_id) - should_email(issue.author_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.close_issue(issue, @u_disabled) - end - def should_email(user_id) - expect(Notify).to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) + should_email(issue.assignee) + should_email(issue.author) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :reopen_issue do it 'should send email to issue assignee and issue author' do - should_email(issue.assignee_id) - should_email(issue.author_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.reopen_issue(issue, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id) + should_email(issue.assignee) + should_email(issue.author) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) end end end @@ -309,108 +268,74 @@ describe NotificationService do before do build_team(merge_request.target_project) add_users_with_subscription(merge_request.target_project, merge_request) + ActionMailer::Base.deliveries.clear end describe :new_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.new_merge_request(merge_request, @u_disabled) - end - def should_email(user_id) - expect(Notify).to receive(:new_merge_request_email).with(user_id, merge_request.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:new_merge_request_email).with(user_id, merge_request.id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :reassigned_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.reassigned_merge_request(merge_request, merge_request.author) - end - - def should_email(user_id) - expect(Notify).to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, nil, merge_request.author_id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id, merge_request.author_id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :closed_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.close_mr(merge_request, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :merged_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.merge_mr(merge_request, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :reopen_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.reopen_mr(merge_request, @u_disabled) - end - def should_email(user_id) - expect(Notify).to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end end @@ -420,22 +345,16 @@ describe NotificationService do before do build_team(project) + ActionMailer::Base.deliveries.clear end describe :project_was_moved do it do - should_email(@u_watcher.id) - should_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.project_was_moved(project, "gitlab/gitlab") - end - - def should_email(user_id) - expect(Notify).to receive(:project_was_moved_email).with(project.id, user_id, "gitlab/gitlab") - end - def should_not_email(user_id) - expect(Notify).not_to receive(:project_was_moved_email).with(project.id, user_id, "gitlab/gitlab") + should_email(@u_watcher) + should_email(@u_participating) + should_not_email(@u_disabled) end end end @@ -469,4 +388,18 @@ describe NotificationService do issuable.subscriptions.create(user: @subscriber, subscribed: true) issuable.subscriptions.create(user: @unsubscriber, subscribed: false) end + + def sent_to_user?(user) + ActionMailer::Base.deliveries.any? do |message| + message.to.include?(user.email) + end + end + + def should_email(user) + expect(sent_to_user?(user)).to be_truthy + end + + def should_not_email(user) + expect(sent_to_user?(user)).to be_falsey + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2be13bb3e6a..0225a0ee53f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -31,6 +31,7 @@ RSpec.configure do |config| config.include StubConfiguration config.include RelativeUrl, type: feature config.include TestEnv + config.include ActiveJob::TestHelper config.include StubGitlabCalls config.include StubGitlabData config.include BenchmarkMatchers, benchmark: true -- cgit v1.2.1 From e92ceb7b57139e985674a44cfe75534c52ed4acd Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 30 Nov 2015 16:12:31 +0200 Subject: fix specs --- Gemfile.lock | 4 +- app/controllers/abuse_reports_controller.rb | 2 +- app/models/project_services/ci/mail_service.rb | 6 +-- app/views/ci/admin/runners/show.html.haml | 2 +- app/views/ci/notify/build_success_email.html.haml | 2 +- app/views/projects/edit.html.haml | 2 +- app/views/projects/runners/edit.html.haml | 2 +- app/workers/email_receiver_worker.rb | 2 +- lib/gitlab/github_import/client.rb | 2 +- lib/gitlab/gitlab_import/client.rb | 2 +- spec/controllers/abuse_reports_controller_spec.rb | 36 +++++++------ spec/features/admin/admin_users_spec.rb | 2 +- spec/helpers/application_helper_spec.rb | 2 +- .../gitlab/markdown/label_reference_filter_spec.rb | 6 +-- .../ci/project_services/mail_service_spec.rb | 62 +++++++++------------- spec/models/project_services/jira_service_spec.rb | 6 +-- spec/workers/email_receiver_worker_spec.rb | 14 ++--- 17 files changed, 73 insertions(+), 81 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 08001379910..f06c4a4165f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -332,7 +332,7 @@ GEM github-markup (~> 1.3.3) gollum-grit_adapter (~> 1.0) nokogiri (~> 1.6.4) - rouge (~> 1.7.4) + rouge (~> 1.10.1) sanitize (~> 2.1.0) stringex (~> 2.5.1) gon (6.0.1) @@ -610,7 +610,7 @@ GEM netrc (~> 0.7) rinku (1.7.3) rotp (2.1.1) - rouge (1.7.7) + rouge (1.10.1) rqrcode (0.7.0) chunky_png rqrcode-rails3 (0.1.7) diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index d8e90594332..20bc5173f1d 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -10,7 +10,7 @@ class AbuseReportsController < ApplicationController if @abuse_report.save if current_application_settings.admin_notification_email.present? - AbuseReportMailer.deliver_later.notify(@abuse_report.id) + AbuseReportMailer.notify(@abuse_report.id).deliver_later end message = "Thank you for your report. A GitLab administrator will look into it shortly." diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb index bdc85667e9d..bb961d06972 100644 --- a/app/models/project_services/ci/mail_service.rb +++ b/app/models/project_services/ci/mail_service.rb @@ -64,9 +64,9 @@ module Ci build.project_recipients.each do |recipient| case build.status.to_sym when :success - mailer.build_success_email(build.id, recipient) + mailer.build_success_email(build.id, recipient).deliver_later when :failed - mailer.build_fail_email(build.id, recipient) + mailer.build_fail_email(build.id, recipient).deliver_later end end end @@ -78,7 +78,7 @@ module Ci end def mailer - Ci::Notify.deliver_later + Ci::Notify end end end diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml index 1498db46a80..fd3d33d657b 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/ci/admin/runners/show.html.haml @@ -37,7 +37,7 @@ = label_tag :tag_list, class: 'control-label' do Tags .col-sm-10 - = f.text_field :tag_list, class: 'form-control' + = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control' .help-block You can setup builds to only use runners with specific tags .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml index 24c439e50eb..6ef1fd67d89 100644 --- a/app/views/ci/notify/build_success_email.html.haml +++ b/app/views/ci/notify/build_success_email.html.haml @@ -8,7 +8,7 @@ = @project.name %p - Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 3ebc175648e..0c10de1604c 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -35,7 +35,7 @@ .form-group = f.label :tag_list, "Tags", class: 'control-label' .col-sm-10 - = f.text_field :tag_list, maxlength: 2000, class: "form-control" + = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control" %p.help-block Separate tags with commas. %fieldset.features diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml index dde9e448cb9..a0324701690 100644 --- a/app/views/projects/runners/edit.html.haml +++ b/app/views/projects/runners/edit.html.haml @@ -23,7 +23,7 @@ = label_tag :tag_list, class: 'control-label' do Tags .col-sm-10 - = f.text_field :tag_list, class: 'form-control' + = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control' .help-block You can setup jobs to only use runners with specific tags .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb index 1df8de1db79..f2649e38eb3 100644 --- a/app/workers/email_receiver_worker.rb +++ b/app/workers/email_receiver_worker.rb @@ -46,6 +46,6 @@ class EmailReceiverWorker return end - EmailRejectionMailer.deliver_later.rejection(reason, raw, can_retry) + EmailRejectionMailer.rejection(reason, raw, can_retry).deliver_later end end diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb index 270cbcd9ccd..74d1529e1ff 100644 --- a/lib/gitlab/github_import/client.rb +++ b/lib/gitlab/github_import/client.rb @@ -46,7 +46,7 @@ module Gitlab end def github_options - OmniAuth::Strategies::GitHub.default_options[:client_options].symbolize_keys + OmniAuth::Strategies::GitHub.default_options[:client_options].to_h.symbolize_keys end end end diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb index 9c00896c913..86fb6c51765 100644 --- a/lib/gitlab/gitlab_import/client.rb +++ b/lib/gitlab/gitlab_import/client.rb @@ -75,7 +75,7 @@ module Gitlab end def gitlab_options - OmniAuth::Strategies::GitLab.default_options[:client_options].symbolize_keys + OmniAuth::Strategies::GitLab.default_options[:client_options].to_h.symbolize_keys end end end diff --git a/spec/controllers/abuse_reports_controller_spec.rb b/spec/controllers/abuse_reports_controller_spec.rb index 0faab8d7ff0..15824a1c67f 100644 --- a/spec/controllers/abuse_reports_controller_spec.rb +++ b/spec/controllers/abuse_reports_controller_spec.rb @@ -18,27 +18,31 @@ describe AbuseReportsController do end it "sends a notification email" do - post :create, - abuse_report: { - user_id: user.id, - message: message - } - - email = ActionMailer::Base.deliveries.last - - expect(email.to).to eq([admin_email]) - expect(email.subject).to include(user.username) - expect(email.text_part.body).to include(message) - end - - it "saves the abuse report" do - expect do + perform_enqueued_jobs do post :create, abuse_report: { user_id: user.id, message: message } - end.to change { AbuseReport.count }.by(1) + + email = ActionMailer::Base.deliveries.last + + expect(email.to).to eq([admin_email]) + expect(email.subject).to include(user.username) + expect(email.text_part.body).to include(message) + end + end + + it "saves the abuse report" do + perform_enqueued_jobs do + expect do + post :create, + abuse_report: { + user_id: user.id, + message: message + } + end.to change { AbuseReport.count }.by(1) + end end end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 4c756a8e732..c6b6ac58acb 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -87,7 +87,7 @@ describe "Admin::Users", feature: true do end it "should call send mail" do - expect(Notify).to receive(:new_user_email) + expect_any_instance_of(NotificationService).to receive(:new_user) click_button "Create user" end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 1dfae0fbd3f..4b8000ecc44 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -59,7 +59,7 @@ describe ApplicationHelper do avatar_url = "http://localhost/uploads/project/avatar/#{project.id}/banana_sample.gif" expect(helper.project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s). - to eq "\"Banana" + to eq "\"Banana" end it 'should give uploaded icon when present' do diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index fc21b65a843..ae286c8be2b 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -71,7 +71,7 @@ module Gitlab::Markdown doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) + namespace_project_issues_path(project.namespace, project, label_name: label.name) end it 'links with adjacent text' do @@ -94,7 +94,7 @@ module Gitlab::Markdown doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) + namespace_project_issues_path(project.namespace, project, label_name: label.name) expect(doc.text).to eq 'See gfm' end @@ -118,7 +118,7 @@ module Gitlab::Markdown doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) + namespace_project_issues_path(project.namespace, project, label_name: label.name) expect(doc.text).to eq 'See gfm references' end diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb index d9b3d34ff15..c03be3ef75f 100644 --- a/spec/models/ci/project_services/mail_service_spec.rb +++ b/spec/models/ci/project_services/mail_service_spec.rb @@ -44,13 +44,10 @@ describe Ci::MailService do end it do - should_email("git@example.com") - mail.execute(build) - end - - def should_email(email) - expect(Ci::Notify).to receive(:build_fail_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) + perform_enqueued_jobs do + expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) + expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) + end end end @@ -67,13 +64,10 @@ describe Ci::MailService do end it do - should_email("git@example.com") - mail.execute(build) - end - - def should_email(email) - expect(Ci::Notify).to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) + expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) + end end end @@ -95,14 +89,12 @@ describe Ci::MailService do end it do - should_email("git@example.com") - should_email("jeroen@example.com") - mail.execute(build) - end - - def should_email(email) - expect(Ci::Notify).to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(2) + expect( + ActionMailer::Base.deliveries.map(&:to).flatten + ).to include("git@example.com", "jeroen@example.com") + end end end @@ -124,14 +116,11 @@ describe Ci::MailService do end it do - should_email(commit.git_author_email) - should_email("jeroen@example.com") - mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect do + mail.execute(build) if mail.can_execute?(build) + end.to_not change{ ActionMailer::Base.deliveries.size } + end end end @@ -177,14 +166,11 @@ describe Ci::MailService do it do Ci::Build.retry(build) - should_email(commit.git_author_email) - should_email("jeroen@example.com") - mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect do + mail.execute(build) if mail.can_execute?(build) + end.to_not change{ ActionMailer::Base.deliveries.size } + end end end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index ddd2cce212c..576f5fc79eb 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -94,9 +94,9 @@ describe JiraService do end it 'should be prepopulated with the settings' do - expect(@service.properties[:project_url]).to eq('http://jira.sample/projects/project_a') - expect(@service.properties[:issues_url]).to eq("http://jira.sample/issues/:id") - expect(@service.properties[:new_issue_url]).to eq("http://jira.sample/projects/project_a/issues/new") + expect(@service.properties["project_url"]).to eq('http://jira.sample/projects/project_a') + expect(@service.properties["issues_url"]).to eq("http://jira.sample/issues/:id") + expect(@service.properties["new_issue_url"]).to eq("http://jira.sample/projects/project_a/issues/new") end end end diff --git a/spec/workers/email_receiver_worker_spec.rb b/spec/workers/email_receiver_worker_spec.rb index 65a8d7d9197..de40a6f78af 100644 --- a/spec/workers/email_receiver_worker_spec.rb +++ b/spec/workers/email_receiver_worker_spec.rb @@ -21,12 +21,14 @@ describe EmailReceiverWorker do end it "sends out a rejection email" do - described_class.new.perform(raw_message) - - email = ActionMailer::Base.deliveries.last - expect(email).not_to be_nil - expect(email.to).to eq(["jake@adventuretime.ooo"]) - expect(email.subject).to include("Rejected") + perform_enqueued_jobs do + described_class.new.perform(raw_message) + + email = ActionMailer::Base.deliveries.last + expect(email).not_to be_nil + expect(email.to).to eq(["jake@adventuretime.ooo"]) + expect(email.subject).to include("Rejected") + end end end end -- cgit v1.2.1 From f1504e1ad52df7aec241b93d0d015da13ddce5a9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 30 Nov 2015 18:03:07 +0200 Subject: test fix --- app/models/project_services/gitlab_ci_service.rb | 2 +- spec/features/admin/admin_users_spec.rb | 5 ++++- spec/services/issues/close_service_spec.rb | 4 +++- spec/services/issues/update_service_spec.rb | 5 ++++- spec/services/merge_requests/close_service_spec.rb | 4 +++- spec/services/merge_requests/merge_service_spec.rb | 5 +++-- spec/services/merge_requests/reopen_service_spec.rb | 4 +++- spec/services/merge_requests/update_service_spec.rb | 6 ++++-- 8 files changed, 25 insertions(+), 10 deletions(-) diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index c5657b5070e..234e8e8b580 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -55,7 +55,7 @@ class GitlabCiService < CiService end def get_ci_commit(sha, ref) - Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha!(sha) + Ci::Project.find(project.gitlab_ci_project.id).commits.find_by_sha!(sha) end def commit_status(sha, ref) diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index c6b6ac58acb..86f01faffb4 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -93,7 +93,10 @@ describe "Admin::Users", feature: true do end it "should send valid email to user with email & password" do - click_button "Create user" + perform_enqueued_jobs do + click_button "Create user" + end + user = User.find_by(username: 'bang') email = ActionMailer::Base.deliveries.last expect(email.subject).to have_content('Account was created') diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index db547ce0d50..d711e3da104 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -14,7 +14,9 @@ describe Issues::CloseService do describe :execute do context "valid params" do before do - @issue = Issues::CloseService.new(project, user, {}).execute(issue) + perform_enqueued_jobs do + @issue = Issues::CloseService.new(project, user, {}).execute(issue) + end end it { expect(@issue).to be_valid } diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index cc3e6483261..73d0b7f7abe 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -36,7 +36,10 @@ describe Issues::UpdateService do label_ids: [label.id] } - @issue = Issues::UpdateService.new(project, user, opts).execute(issue) + perform_enqueued_jobs do + @issue = Issues::UpdateService.new(project, user, opts).execute(issue) + end + @issue.reload end diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb index b3cbfd4b5b8..272f3897938 100644 --- a/spec/services/merge_requests/close_service_spec.rb +++ b/spec/services/merge_requests/close_service_spec.rb @@ -18,7 +18,9 @@ describe MergeRequests::CloseService do before do allow(service).to receive(:execute_hooks) - @merge_request = service.execute(merge_request) + perform_enqueued_jobs do + @merge_request = service.execute(merge_request) + end end it { expect(@merge_request).to be_valid } diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 7483f51de03..c0961ceb11e 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -17,8 +17,9 @@ describe MergeRequests::MergeService do before do allow(service).to receive(:execute_hooks) - - service.execute(merge_request, 'Awesome message') + perform_enqueued_jobs do + service.execute(merge_request, 'Awesome message') + end end it { expect(merge_request).to be_valid } diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb index 9401bc3b558..05146bf43f4 100644 --- a/spec/services/merge_requests/reopen_service_spec.rb +++ b/spec/services/merge_requests/reopen_service_spec.rb @@ -19,7 +19,9 @@ describe MergeRequests::ReopenService do allow(service).to receive(:execute_hooks) merge_request.state = :closed - service.execute(merge_request) + perform_enqueued_jobs do + service.execute(merge_request) + end end it { expect(merge_request).to be_valid } diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 97f5c009aec..d899b1f01d1 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -42,8 +42,10 @@ describe MergeRequests::UpdateService do before do allow(service).to receive(:execute_hooks) - @merge_request = service.execute(merge_request) - @merge_request.reload + perform_enqueued_jobs do + @merge_request = service.execute(merge_request) + @merge_request.reload + end end it { expect(@merge_request).to be_valid } -- cgit v1.2.1 From ae18ba16327abd79cf6207b83209d4ef96d3e158 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 24 Nov 2015 13:35:07 +0200 Subject: Fire update hook from GitLab --- CHANGELOG | 1 + app/models/repository.rb | 10 +++++++--- lib/gitlab/git/hook.rb | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 090b54f41a4..4a18ac821f7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Fix 500 error when update group member permission - Fix: Raw private snippets access workflow - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) + - Fire update hook from GitLab v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/models/repository.rb b/app/models/repository.rb index c1836103463..d247b0f5012 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -571,9 +571,13 @@ class Repository # Run GitLab pre-receive hook pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo) - status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref) + pre_receive_hook_status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref) - if status + # Run GitLab update hook + update_hook = Gitlab::Git::Hook.new('update', path_to_repo) + update_hook_status = update_hook.trigger(gl_id, oldrev, newrev, ref) + + if pre_receive_hook_status && update_hook_status if was_empty # Create branch rugged.references.create(ref, newrev) @@ -596,7 +600,7 @@ class Repository # Remove tmp ref and return error to user rugged.references.delete(tmp_ref) - raise PreReceiveError.new('Commit was rejected by pre-receive hook') + raise PreReceiveError.new('Commit was rejected by git hook') end end diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb index dd393fe09d2..07b856ca64c 100644 --- a/lib/gitlab/git/hook.rb +++ b/lib/gitlab/git/hook.rb @@ -16,6 +16,17 @@ module Gitlab def trigger(gl_id, oldrev, newrev, ref) return true unless exists? + case name + when "pre-receive", "post-receive" + call_receive_hook(gl_id, oldrev, newrev, ref) + when "update" + call_update_hook(gl_id, oldrev, newrev, ref) + end + end + + private + + def call_receive_hook(gl_id, oldrev, newrev, ref) changes = [oldrev, newrev, ref].join(" ") # function will return true if succesful @@ -54,6 +65,12 @@ module Gitlab exit_status end + + def call_update_hook(gl_id, oldrev, newrev, ref) + Dir.chdir(repo_path) do + system({ 'GL_ID' => gl_id }, path, ref, oldrev, newrev) + end + end end end end -- cgit v1.2.1 From ceeb93fa77783a3fa9a60529195cd187af191bba Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 30 Nov 2015 12:02:44 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 259a81f42da..1e8fb670325 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,14 +1,17 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) - - Ensure cached application settings are refreshed at startup (Stan Hu) - - Fix 404 in redirection after removing a project (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - - Fix: Raw private snippets access workflow - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) +v 8.2.2 + - Fix 404 in redirection after removing a project (Stan Hu) + - Ensure cached application settings are refreshed at startup (Stan Hu) + - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) + - Fix: Raw private snippets access workflow + - Prevent "413 Request entity too large" errors when pushing large files with LFS + v 8.2.1 - Forcefully update builds that didn't want to update with state machine - Fix: saving GitLabCiService as Admin Template -- cgit v1.2.1 From 7ba00b50827d8d30ea4c54acb2623d6fb387dcf1 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 30 Nov 2015 19:04:44 +0100 Subject: Use gitlab-shell 2.6.8 in update guide --- doc/update/8.1-to-8.2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index b57e999cfb7..7b228d6a22f 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -68,7 +68,7 @@ sudo -u git -H git checkout 8-2-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v2.6.7 +sudo -u git -H git checkout v2.6.8 ``` ### 5. Replace gitlab-git-http-server with gitlab-workhorse -- cgit v1.2.1 From a9642ba046db9aa4490d3361b32cdba48b3fbb9c Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 30 Nov 2015 19:05:28 +0100 Subject: Install gitlab-shell 2.6.8 --- doc/install/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 3f5c03a890a..61647780607 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -312,7 +312,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.7] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.8] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: -- cgit v1.2.1 From 8f43298d967d715628b9b17d357cb9169a3606bf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:09:36 +0100 Subject: Show local issues and MRs in "This X closes... / closed by..." sentence --- app/helpers/issues_helper.rb | 6 +++++- app/helpers/merge_requests_helper.rb | 6 +++++- app/models/merge_request.rb | 2 +- app/views/projects/issues/_closed_by_box.html.haml | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 493f370d9a9..b45d4e1ea8e 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -84,7 +84,11 @@ module IssuesHelper end def merge_requests_sentence(merge_requests) - merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ') + # Sorting based on the `!123` or `group/project!123` reference will sort + # local merge requests first. + merge_requests.map do |merge_request| + merge_request.to_reference(@project) + end.sort.to_sentence(last_word_connector: ', or ') end def url_to_emoji(name) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index b804d4f4e3b..a6010721dff 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -39,7 +39,11 @@ module MergeRequestsHelper end def issues_sentence(issues) - issues.map(&:to_reference).to_sentence + # Sorting based on the `#123` or `group/project#123` reference will sort + # local issues first. + issues.map do |issue| + issue.to_reference(@project) + end.sort.to_sentence end def mr_change_branches_path(merge_request) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1b3d6079d2c..939df8cd8d1 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -316,7 +316,7 @@ class MergeRequest < ActiveRecord::Base issues = commits.flat_map { |c| c.closes_issues(current_user) } issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user). closed_by_message(description)) - issues.uniq.sort_by(&:id) + issues.uniq else [] end diff --git a/app/views/projects/issues/_closed_by_box.html.haml b/app/views/projects/issues/_closed_by_box.html.haml index aef352029d0..917d5181689 100644 --- a/app/views/projects/issues/_closed_by_box.html.haml +++ b/app/views/projects/issues/_closed_by_box.html.haml @@ -1,3 +1,3 @@ .issue-closed-by-widget = icon('check') - This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests.sort))} is accepted. + This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests))} is accepted. -- cgit v1.2.1 From a7be01cd07430a4302668224947b2ed135c2d7bb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:10:52 +0100 Subject: Render commit range reference with short shas, link to full shas. --- app/models/commit_range.rb | 70 ++++++++----- .../markdown/commit_range_reference_filter_spec.rb | 7 +- spec/models/commit_range_spec.rb | 111 +++++++++------------ 3 files changed, 94 insertions(+), 94 deletions(-) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index 86fc9eb01a3..fd23e24aff6 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -2,12 +2,12 @@ # # Examples: # -# range = CommitRange.new('f3f85602...e86e1013') +# range = CommitRange.new('f3f85602...e86e1013', project) # range.exclude_start? # => false # range.reference_title # => "Commits f3f85602 through e86e1013" # range.to_s # => "f3f85602...e86e1013" # -# range = CommitRange.new('f3f856029bc5f966c5a7ee24cf7efefdd20e6019..e86e1013709735be5bb767e2b228930c543f25ae') +# range = CommitRange.new('f3f856029bc5f966c5a7ee24cf7efefdd20e6019..e86e1013709735be5bb767e2b228930c543f25ae', project) # range.exclude_start? # => true # range.reference_title # => "Commits f3f85602^ through e86e1013" # range.to_param # => {from: "f3f856029bc5f966c5a7ee24cf7efefdd20e6019^", to: "e86e1013709735be5bb767e2b228930c543f25ae"} @@ -21,7 +21,7 @@ class CommitRange include ActiveModel::Conversion include Referable - attr_reader :sha_from, :notation, :sha_to + attr_reader :commit_from, :notation, :commit_to # Optional Project model attr_accessor :project @@ -53,17 +53,22 @@ class CommitRange # project - An optional Project model. # # Raises ArgumentError if `range_string` does not match `PATTERN`. - def initialize(range_string, project = nil) + def initialize(range_string, project) + @project = project + range_string.strip! unless range_string.match(/\A#{PATTERN}\z/) raise ArgumentError, "invalid CommitRange string format: #{range_string}" end - @exclude_start = !range_string.include?('...') - @sha_from, @notation, @sha_to = range_string.split(/(\.{2,3})/, 2) + ref_from, @notation, ref_to = range_string.split(/(\.{2,3})/, 2) - @project = project + @exclude_start = @notation == '..' + if project.valid_repo? + @commit_from = project.commit(ref_from) + @commit_to = project.commit(ref_to) + end end def inspect @@ -71,15 +76,16 @@ class CommitRange end def to_s - "#{sha_from[0..7]}#{notation}#{sha_to[0..7]}" + sha_from + notation + sha_to end + alias_method :id, :to_s + def to_reference(from_project = nil) - # Not using to_s because we want the full SHAs - reference = sha_from + notation + sha_to + reference = Commit.truncate_sha(sha_from) + notation + Commit.truncate_sha(sha_to) if cross_project_reference?(from_project) - reference = project.to_reference + '@' + reference + reference = project.to_reference + self.class.reference_prefix + reference end reference @@ -87,14 +93,14 @@ class CommitRange # Returns a String for use in a link's title attribute def reference_title - "Commits #{suffixed_sha_from} through #{sha_to}" + "Commits #{sha_start} through #{sha_to}" end # Return a Hash of parameters for passing to a URL helper # # See `namespace_project_compare_url` def to_param - { from: suffixed_sha_from, to: sha_to } + { from: sha_start, to: sha_to } end def exclude_start? @@ -105,28 +111,42 @@ class CommitRange # repository # # project - An optional Project to check (default: `project`) - def valid_commits?(project = project) - return nil unless project.present? - return false unless project.valid_repo? - - commit_from.present? && commit_to.present? + def valid_commits? + commit_start.present? && commit_end.present? end def persisted? true end - def commit_from - @commit_from ||= project.repository.commit(suffixed_sha_from) + def sha_from + return nil unless @commit_from + + @commit_from.id end - def commit_to - @commit_to ||= project.repository.commit(sha_to) + def sha_to + return nil unless @commit_to + + @commit_to.id + end + + def sha_start + return nil unless sha_from + + exclude_start? ? sha_from + '^' : sha_from end - private + def commit_start + return nil unless sha_start - def suffixed_sha_from - sha_from + (exclude_start? ? '^' : '') + if exclude_start? + @commit_start ||= project.commit(sha_start) + else + commit_from + end end + + alias_method :sha_end, :sha_to + alias_method :commit_end, :commit_to end diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index e5b8d723fe5..e078ff26814 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -8,8 +8,8 @@ module Gitlab::Markdown let(:commit1) { project.commit } let(:commit2) { project.commit("HEAD~2") } - let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}") } - let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}") } + let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}", project) } + let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}", project) } it 'requires project context' do expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) @@ -53,7 +53,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("See (#{reference}.)") - exp = Regexp.escape(range.to_s) + exp = Regexp.escape(range.to_reference) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end @@ -62,6 +62,7 @@ module Gitlab::Markdown expect(project).to receive(:valid_repo?).and_return(true) expect(project.repository).to receive(:commit).with(commit1.id.reverse) + expect(project.repository).to receive(:commit).with(commit2.id) expect(filter(act).to_html).to eq exp end diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 1031af097bd..58283f06972 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -7,50 +7,56 @@ describe CommitRange do it { is_expected.to include_module(Referable) } end - let(:sha_from) { 'f3f85602' } - let(:sha_to) { 'e86e1013' } + let!(:project) { create(:project, :public) } + let!(:commit1) { project.commit("HEAD~2") } + let!(:commit2) { project.commit } - let(:range) { described_class.new("#{sha_from}...#{sha_to}") } - let(:range2) { described_class.new("#{sha_from}..#{sha_to}") } + let(:sha_from) { commit1.short_id } + let(:sha_to) { commit2.short_id } + + let(:full_sha_from) { commit1.id } + let(:full_sha_to) { commit2.id } + + let(:range) { described_class.new("#{sha_from}...#{sha_to}", project) } + let(:range2) { described_class.new("#{sha_from}..#{sha_to}", project) } it 'raises ArgumentError when given an invalid range string' do - expect { described_class.new("Foo") }.to raise_error(ArgumentError) + expect { described_class.new("Foo", project) }.to raise_error(ArgumentError) end describe '#to_s' do it 'is correct for three-dot syntax' do - expect(range.to_s).to eq "#{sha_from[0..7]}...#{sha_to[0..7]}" + expect(range.to_s).to eq "#{full_sha_from}...#{full_sha_to}" end it 'is correct for two-dot syntax' do - expect(range2.to_s).to eq "#{sha_from[0..7]}..#{sha_to[0..7]}" + expect(range2.to_s).to eq "#{full_sha_from}..#{full_sha_to}" end end describe '#to_reference' do - let(:project) { double('project', to_reference: 'namespace1/project') } + let(:cross) { create(:project) } - before do - range.project = project + it 'returns a String reference to the object' do + expect(range.to_reference).to eq "#{sha_from}...#{sha_to}" end it 'returns a String reference to the object' do - expect(range.to_reference).to eq range.to_s + expect(range2.to_reference).to eq "#{sha_from}..#{sha_to}" end it 'supports a cross-project reference' do - cross = double('project') - expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{range.to_s}" + expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}" end end describe '#reference_title' do it 'returns the correct String for three-dot ranges' do - expect(range.reference_title).to eq "Commits #{sha_from} through #{sha_to}" + expect(range.reference_title).to eq "Commits #{full_sha_from} through #{full_sha_to}" end it 'returns the correct String for two-dot ranges' do - expect(range2.reference_title).to eq "Commits #{sha_from}^ through #{sha_to}" + expect(range2.reference_title).to eq "Commits #{full_sha_from}^ through #{full_sha_to}" end end @@ -60,11 +66,11 @@ describe CommitRange do end it 'includes the correct values for a three-dot range' do - expect(range.to_param).to eq({ from: sha_from, to: sha_to }) + expect(range.to_param).to eq({ from: full_sha_from, to: full_sha_to }) end it 'includes the correct values for a two-dot range' do - expect(range2.to_param).to eq({ from: sha_from + '^', to: sha_to }) + expect(range2.to_param).to eq({ from: full_sha_from + '^', to: full_sha_to }) end end @@ -79,64 +85,37 @@ describe CommitRange do end describe '#valid_commits?' do - context 'without a project' do - it 'returns nil' do - expect(range.valid_commits?).to be_nil + context 'with a valid repo' do + before do + expect(project).to receive(:valid_repo?).and_return(true) end - end - it 'accepts an optional project argument' do - project1 = double('project1').as_null_object - project2 = double('project2').as_null_object + it 'is false when `sha_from` is invalid' do + expect(project).to receive(:commit).with(sha_from).and_return(nil) + expect(project).to receive(:commit).with(sha_to).and_call_original - # project1 gets assigned through the accessor, but ignored when not given - # as an argument to `valid_commits?` - expect(project1).not_to receive(:present?) - range.project = project1 + expect(range).not_to be_valid_commits + end - # project2 gets passed to `valid_commits?` - expect(project2).to receive(:present?).and_return(false) + it 'is false when `sha_to` is invalid' do + expect(project).to receive(:commit).with(sha_from).and_call_original + expect(project).to receive(:commit).with(sha_to).and_return(nil) - range.valid_commits?(project2) - end + expect(range).not_to be_valid_commits + end - context 'with a project' do - let(:project) { double('project', repository: double('repository')) } - - context 'with a valid repo' do - before do - expect(project).to receive(:valid_repo?).and_return(true) - range.project = project - end - - it 'is false when `sha_from` is invalid' do - expect(project.repository).to receive(:commit).with(sha_from).and_return(false) - expect(project.repository).not_to receive(:commit).with(sha_to) - expect(range).not_to be_valid_commits - end - - it 'is false when `sha_to` is invalid' do - expect(project.repository).to receive(:commit).with(sha_from).and_return(true) - expect(project.repository).to receive(:commit).with(sha_to).and_return(false) - expect(range).not_to be_valid_commits - end - - it 'is true when both `sha_from` and `sha_to` are valid' do - expect(project.repository).to receive(:commit).with(sha_from).and_return(true) - expect(project.repository).to receive(:commit).with(sha_to).and_return(true) - expect(range).to be_valid_commits - end + it 'is true when both `sha_from` and `sha_to` are valid' do + expect(range).to be_valid_commits end + end - context 'without a valid repo' do - before do - expect(project).to receive(:valid_repo?).and_return(false) - range.project = project - end + context 'without a valid repo' do + before do + expect(project).to receive(:valid_repo?).and_return(false) + end - it 'returns false' do - expect(range).not_to be_valid_commits - end + it 'returns false' do + expect(range).not_to be_valid_commits end end end -- cgit v1.2.1 From d6a5b45c8ea5ec7a68e213636fde405c52bb90e4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:14:46 +0100 Subject: Recognize issue/MR/snippet/commit links as references. --- app/models/commit.rb | 15 +++-- app/models/commit_range.rb | 11 +++- app/models/concerns/referable.rb | 19 ++++++ app/models/issue.rb | 11 +++- app/models/merge_request.rb | 11 +++- app/models/snippet.rb | 11 +++- lib/gitlab/markdown.rb | 5 +- lib/gitlab/markdown/abstract_reference_filter.rb | 31 +++++++--- .../markdown/commit_range_reference_filter.rb | 70 ++++++---------------- lib/gitlab/markdown/commit_reference_filter.rb | 68 +++++---------------- lib/gitlab/markdown/external_link_filter.rb | 3 + .../markdown/merge_request_reference_filter.rb | 10 ++++ lib/gitlab/markdown/redactor_filter.rb | 5 +- .../markdown/commit_range_reference_filter_spec.rb | 39 +++++++++++- .../markdown/commit_reference_filter_spec.rb | 31 ++++++++++ .../gitlab/markdown/issue_reference_filter_spec.rb | 32 ++++++++++ .../merge_request_reference_filter_spec.rb | 29 ++++++++- .../markdown/snippet_reference_filter_spec.rb | 30 ++++++++++ 18 files changed, 301 insertions(+), 130 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index 492f6be1ce3..fc03d2580d7 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -73,16 +73,23 @@ class Commit # This pattern supports cross-project references. def self.reference_pattern %r{ - (?:#{Project.reference_pattern}#{reference_prefix})? - (?\h{6,40}) + #{link_reference_pattern} | + (?: + (?:#{Project.reference_pattern}#{reference_prefix})? + (?\h{6,40}) + ) }x end + def self.link_reference_pattern + super("commit", /(?\h{6,40})/) + end + def to_reference(from_project = nil) if cross_project_reference?(from_project) - "#{project.to_reference}@#{id}" + project.to_reference + self.class.reference_prefix + self.short_id else - id + self.short_id end end diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index fd23e24aff6..98067771b71 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -42,11 +42,18 @@ class CommitRange # This pattern supports cross-project references. def self.reference_pattern %r{ - (?:#{Project.reference_pattern}#{reference_prefix})? - (?#{PATTERN}) + #{link_reference_pattern} | + (?: + (?:#{Project.reference_pattern}#{reference_prefix})? + (?#{PATTERN}) + ) }x end + def self.link_reference_pattern + super("compare", /(?#{PATTERN})/) + end + # Initialize a CommitRange # # range_string - The String commit range. diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb index cced66cc1e4..16e4d054869 100644 --- a/app/models/concerns/referable.rb +++ b/app/models/concerns/referable.rb @@ -44,6 +44,25 @@ module Referable def reference_pattern raise NotImplementedError, "#{self} does not implement #{__method__}" end + + def link_reference_pattern(route, pattern) + %r{ + (? + #{Regexp.escape(Gitlab.config.gitlab.url)} + \/#{Project.reference_pattern} + \/#{Regexp.escape(route)} + \/#{pattern} + (? + (\/[a-z0-9_=-]+)* + )? + (? + \?[a-z0-9_=-]+ + (&[a-z0-9_=-]+)* + )? + (?\#[a-z0-9_-]+)? + ) + }x + end end private diff --git a/app/models/issue.rb b/app/models/issue.rb index 72183108033..e62acfdfd91 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -64,11 +64,18 @@ class Issue < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) + #{link_reference_pattern} | + (?: + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) + ) }x end + def self.link_reference_pattern + super("issues", /(?\d+)/) + end + def to_reference(from_project = nil) reference = "#{self.class.reference_prefix}#{iid}" diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 939df8cd8d1..c1d3874adee 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -146,11 +146,18 @@ class MergeRequest < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) + #{link_reference_pattern} | + (?: + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) + ) }x end + def self.link_reference_pattern + super("merge_requests", /(?\d+)/) + end + def to_reference(from_project = nil) reference = "#{self.class.reference_prefix}#{iid}" diff --git a/app/models/snippet.rb b/app/models/snippet.rb index b0831982aa7..8ec12ddf6ef 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -60,11 +60,18 @@ class Snippet < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) + #{link_reference_pattern} | + (?: + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) + ) }x end + def self.link_reference_pattern + super("snippets", /(?\d+)/) + end + def to_reference(from_project = nil) reference = "#{self.class.reference_prefix}#{id}" diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index b082bfc434b..ee458eda025 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -181,8 +181,6 @@ module Gitlab Gitlab::Markdown::RelativeLinkFilter, Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::TableOfContentsFilter, - Gitlab::Markdown::AutolinkFilter, - Gitlab::Markdown::ExternalLinkFilter, Gitlab::Markdown::UserReferenceFilter, Gitlab::Markdown::IssueReferenceFilter, @@ -193,6 +191,9 @@ module Gitlab Gitlab::Markdown::CommitReferenceFilter, Gitlab::Markdown::LabelReferenceFilter, + Gitlab::Markdown::AutolinkFilter, + Gitlab::Markdown::ExternalLinkFilter, + Gitlab::Markdown::TaskListFilter ] end diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index fd5b7eb9332..4adc44361b7 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -40,7 +40,7 @@ module Gitlab # Returns a String replaced with the return of the block. def self.references_in(text) text.gsub(object_class.reference_pattern) do |match| - yield match, $~[object_sym].to_i, $~[:project] + yield match, $~[object_sym].to_i, $~[:project], $~ end end @@ -74,26 +74,41 @@ module Gitlab # Returns a String with references replaced with links. All links # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. def object_link_filter(text) - references_in(text) do |match, id, project_ref| + references_in(text) do |match, id, project_ref, matches| project = project_from_ref(project_ref) if project && object = find_object(project, id) - title = escape_once("#{object_title}: #{object.title}") + title = escape_once(object_link_title(object)) klass = reference_class(object_sym) - data = data_attribute(project: project.id, object_sym => object.id) - url = url_for_object(object, project) + data = data_attribute(project: project.id, object_sym => object.id, original: match) + url = matches[:url] || url_for_object(object, project) + + text = object.to_reference(context[:project]) + + extras = object_link_text_extras(object, matches) + text += " (#{extras.join(", ")})" if extras.any? %(#{match}) + class="#{klass}">#{text}) else match end end end - def object_title - object_class.name.titleize + def object_link_text_extras(object, matches) + extras = [] + + if matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/ + extras << "comment #{$1}" + end + + extras + end + + def object_link_title(object) + "#{object_class.name.titleize}: #{object.title}" end end end diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb index e070edae0a4..f24bed76193 100644 --- a/lib/gitlab/markdown/commit_range_reference_filter.rb +++ b/lib/gitlab/markdown/commit_range_reference_filter.rb @@ -5,24 +5,14 @@ module Gitlab # HTML filter that replaces commit range references with links. # # This filter supports cross-project references. - class CommitRangeReferenceFilter < ReferenceFilter - include CrossProjectReference + class CommitRangeReferenceFilter < AbstractReferenceFilter + def self.object_class + CommitRange + end - # Public: Find commit range references in text - # - # CommitRangeReferenceFilter.references_in(text) do |match, commit_range, project_ref| - # "#{commit_range}" - # end - # - # text - String text to search. - # - # Yields the String match, the String commit range, and an optional String - # of the external project reference. - # - # Returns a String replaced with the return of the block. def self.references_in(text) text.gsub(CommitRange.reference_pattern) do |match| - yield match, $~[:commit_range], $~[:project] + yield match, $~[:commit_range], $~[:project], $~ end end @@ -31,9 +21,9 @@ module Gitlab return unless project id = node.attr("data-commit-range") - range = CommitRange.new(id, project) + range = find_object(project, id) - return unless range.valid_commits? + return unless range { commit_range: range } end @@ -44,49 +34,25 @@ module Gitlab @commit_map = {} end - def call - replace_text_nodes_matching(CommitRange.reference_pattern) do |content| - commit_range_link_filter(content) - end - end - - # Replace commit range references in text with links to compare the commit - # ranges. - # - # text - String text to replace references in. - # - # Returns a String with commit range references replaced with links. All - # links have `gfm` and `gfm-commit_range` class names attached for - # styling. - def commit_range_link_filter(text) - self.class.references_in(text) do |match, id, project_ref| - project = self.project_from_ref(project_ref) - - range = CommitRange.new(id, project) - - if range.valid_commits? - url = url_for_commit_range(project, range) - - title = range.reference_title - klass = reference_class(:commit_range) - data = data_attribute(project: project.id, commit_range: id) + def self.find_object(project, id) + range = CommitRange.new(id, project) - project_ref += '@' if project_ref + range.valid_commits? ? range : nil + end - %(#{project_ref}#{range}) - else - match - end - end + def find_object(*args) + self.class.find_object(*args) end - def url_for_commit_range(project, range) + def url_for_object(range, project) h = Gitlab::Application.routes.url_helpers h.namespace_project_compare_url(project.namespace, project, range.to_param.merge(only_path: context[:only_path])) end + + def object_link_title(range) + range.reference_title + end end end end diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb index 8cdbeb1f9cf..cc7abc08c87 100644 --- a/lib/gitlab/markdown/commit_reference_filter.rb +++ b/lib/gitlab/markdown/commit_reference_filter.rb @@ -5,24 +5,14 @@ module Gitlab # HTML filter that replaces commit references with links. # # This filter supports cross-project references. - class CommitReferenceFilter < ReferenceFilter - include CrossProjectReference + class CommitReferenceFilter < AbstractReferenceFilter + def self.object_class + Commit + end - # Public: Find commit references in text - # - # CommitReferenceFilter.references_in(text) do |match, commit, project_ref| - # "#{commit}" - # end - # - # text - String text to search. - # - # Yields the String match, the String commit identifier, and an optional - # String of the external project reference. - # - # Returns a String replaced with the return of the block. def self.references_in(text) text.gsub(Commit.reference_pattern) do |match| - yield match, $~[:commit], $~[:project] + yield match, $~[:commit], $~[:project], $~ end end @@ -31,58 +21,32 @@ module Gitlab return unless project id = node.attr("data-commit") - commit = commit_from_ref(project, id) + commit = find_object(project, id) return unless commit { commit: commit } end - def call - replace_text_nodes_matching(Commit.reference_pattern) do |content| - commit_link_filter(content) - end - end - - # Replace commit references in text with links to the commit specified. - # - # text - String text to replace references in. - # - # Returns a String with commit references replaced with links. All links - # have `gfm` and `gfm-commit` class names attached for styling. - def commit_link_filter(text) - self.class.references_in(text) do |match, id, project_ref| - project = self.project_from_ref(project_ref) - - if commit = self.class.commit_from_ref(project, id) - url = url_for_commit(project, commit) - - title = escape_once(commit.link_title) - klass = reference_class(:commit) - data = data_attribute(project: project.id, commit: id) - - project_ref += '@' if project_ref - - %(#{project_ref}#{commit.short_id}) - else - match - end - end - end - - def self.commit_from_ref(project, id) + def self.find_object(project, id) if project && project.valid_repo? project.commit(id) end end - def url_for_commit(project, commit) + def find_object(*args) + self.class.find_object(*args) + end + + def url_for_object(commit, project) h = Gitlab::Application.routes.url_helpers h.namespace_project_commit_url(project.namespace, project, commit, only_path: context[:only_path]) end + + def object_link_title(commit) + commit.link_title + end end end end diff --git a/lib/gitlab/markdown/external_link_filter.rb b/lib/gitlab/markdown/external_link_filter.rb index 29e51b6ade6..b6792932016 100644 --- a/lib/gitlab/markdown/external_link_filter.rb +++ b/lib/gitlab/markdown/external_link_filter.rb @@ -10,6 +10,9 @@ module Gitlab doc.search('a').each do |node| next unless node.has_attribute?('href') + klass = node.attribute('class') + next if klass && klass.include?('gfm') + link = node.attribute('href').value # Skip non-HTTP(S) links diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index 1f47f03c94e..3780a14a130 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -20,6 +20,16 @@ module Gitlab h.namespace_project_merge_request_url(project.namespace, project, mr, only_path: context[:only_path]) end + + def object_link_text_extras(object, matches) + extras = super + + if matches[:path] && matches[:path] == '/diffs' + extras.unshift "diffs" + end + + extras + end end end end diff --git a/lib/gitlab/markdown/redactor_filter.rb b/lib/gitlab/markdown/redactor_filter.rb index a1f3a8a8ebf..2a58c798f9f 100644 --- a/lib/gitlab/markdown/redactor_filter.rb +++ b/lib/gitlab/markdown/redactor_filter.rb @@ -12,7 +12,10 @@ module Gitlab def call doc.css('a.gfm').each do |node| unless user_can_reference?(node) - node.replace(node.text) + # The reference should be replaced by the original text, + # which is not always the same as the rendered text. + text = node.attribute('data-original') || node.text + node.replace(text) end end diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index e078ff26814..753140d60e6 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -125,7 +125,44 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}") + exp = Regexp.escape("#{project2.to_reference}@#{range.to_reference}") + expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) + end + + it 'ignores invalid commit IDs on the referenced project' do + exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" + expect(filter(act).to_html).to eq exp + + exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("See #{reference}") + expect(result[:references][:commit_range]).not_to be_empty + end + end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:project, :public, namespace: namespace) } + let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) } + + before do + range.project = project2 + end + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) + end + + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") + + exp = Regexp.escape(range.to_reference(project)) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index d080efbf3d4..c1bcf29b1ba 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -131,5 +131,36 @@ module Gitlab::Markdown expect(result[:references][:commit]).not_to be_empty end end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:project, :public, namespace: namespace) } + let(:commit) { project2.commit } + let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) } + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id) + end + + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") + + exp = Regexp.escape(project2.to_reference) + expect(doc.to_html).to match(/\(#{commit.to_reference(project)}<\/a>\.\)/) + end + + it 'ignores invalid commit IDs on the referenced project' do + exp = act = "Committed #{invalidate_reference(reference)}" + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("See #{reference}") + expect(result[:references][:commit]).not_to be_empty + end + end end end diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb index 94c80ae6611..296e8868f46 100644 --- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb @@ -135,5 +135,37 @@ module Gitlab::Markdown expect(result[:references][:issue]).to eq [issue] end end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:empty_project, :public, namespace: namespace) } + let(:issue) { create(:issue, project: project2) } + let(:reference) { helper.url_for_issue(issue.iid, project2) + "#note_123" } + + it 'ignores valid references when cross-reference project uses external tracker' do + expect_any_instance_of(Project).to receive(:get_issue). + with(issue.iid).and_return(nil) + + exp = act = "Issue #{reference}" + expect(filter(act).to_html).to eq exp + end + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq reference + end + + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Fixed #{reference}") + expect(result[:references][:issue]).to eq [issue] + end + end end end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index 3ef6cdfff33..49d8a6e44da 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -89,7 +89,7 @@ module Gitlab::Markdown context 'cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } - let(:merge) { create(:merge_request, source_project: project2) } + let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } let(:reference) { merge.to_reference(project) } it 'links to a valid reference' do @@ -97,7 +97,7 @@ module Gitlab::Markdown expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_merge_request_url(project2.namespace, - project, merge) + project2, merge) end it 'links with adjacent text' do @@ -116,5 +116,30 @@ module Gitlab::Markdown expect(result[:references][:merge_request]).to eq [merge] end end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:project, :public, namespace: namespace) } + let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } + let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, + project2, merge) + '/diffs#note_123' } + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq reference + end + + it 'links with adjacent text' do + doc = filter("Merge (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/) + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Merge #{reference}") + expect(result[:references][:merge_request]).to eq [merge] + end + end end end diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index 9d9652dba46..3080a8a3608 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -114,5 +114,35 @@ module Gitlab::Markdown expect(result[:references][:snippet]).to eq [snippet] end end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:empty_project, :public, namespace: namespace) } + let(:snippet) { create(:project_snippet, project: project2) } + let(:reference) { urls.namespace_project_snippet_url(project2.namespace, project2, snippet) } + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet) + end + + it 'links with adjacent text' do + doc = filter("See (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(snippet.to_reference(project))}<\/a>\.\)/) + end + + it 'ignores invalid snippet IDs on the referenced project' do + exp = act = "See #{invalidate_reference(reference)}" + + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Snippet #{reference}") + expect(result[:references][:snippet]).to eq [snippet] + end + end end end -- cgit v1.2.1 From f9017a2b0384eef3f99ec2f3f1f2ef156afff2b8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:15:19 +0100 Subject: Have ClosingIssueExtractor recognize all referenced issues --- config/initializers/1_settings.rb | 2 +- lib/gitlab/closing_issue_extractor.rb | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 62619241001..63d8ae17436 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -164,7 +164,7 @@ Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled']. Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_enabled'].nil? Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? -Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil? +Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil? Settings.gitlab['default_projects_features'] ||= {} Settings.gitlab['webhook_timeout'] ||= 10 Settings.gitlab['max_attachment_size'] ||= 10 diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb index aeec595782c..70b9943d7eb 100644 --- a/lib/gitlab/closing_issue_extractor.rb +++ b/lib/gitlab/closing_issue_extractor.rb @@ -1,6 +1,10 @@ module Gitlab class ClosingIssueExtractor - ISSUE_CLOSING_REGEX = Regexp.new(Gitlab.config.gitlab.issue_closing_pattern) + ISSUE_CLOSING_REGEX = begin + pattern = Gitlab.config.gitlab.issue_closing_pattern + pattern = pattern.sub('%{issue_ref}', "(?:#{Issue.reference_pattern})") + Regexp.new(pattern).freeze + end def initialize(project, current_user = nil) @extractor = Gitlab::ReferenceExtractor.new(project, current_user) @@ -9,10 +13,12 @@ module Gitlab def closed_by_message(message) return [] if message.nil? - closing_statements = message.scan(ISSUE_CLOSING_REGEX). - map { |ref| ref[0] }.join(" ") + closing_statements = [] + message.scan(ISSUE_CLOSING_REGEX) do + closing_statements << Regexp.last_match[0] + end - @extractor.analyze(closing_statements) + @extractor.analyze(closing_statements.join(" ")) @extractor.issues end -- cgit v1.2.1 From 4a292aa6042f33d0b63bf95d223ae87bddcea2ce Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:15:34 +0100 Subject: Proper cross-project references when MR is closed by issue --- app/services/system_note_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 7e2bc834176..09c159510cd 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -125,7 +125,7 @@ class SystemNoteService # Returns the created Note object def self.change_status(noteable, project, author, status, source) body = "Status changed to #{status}" - body += " by #{source.gfm_reference}" if source + body += " by #{source.gfm_reference(project)}" if source create_note(noteable: noteable, project: project, author: author, note: body) end -- cgit v1.2.1 From bf5e7252ee5ffb5198b7916d1ad8f3436723721a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:36:13 +0100 Subject: Recognize commit range with named refs in compare URLs. --- app/models/commit_range.rb | 31 +++++++++++++--------- .../markdown/commit_range_reference_filter_spec.rb | 11 ++++---- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index 98067771b71..7b1164b024c 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -22,16 +22,19 @@ class CommitRange include Referable attr_reader :commit_from, :notation, :commit_to + attr_reader :ref_from, :ref_to # Optional Project model attr_accessor :project - # See `exclude_start?` - attr_reader :exclude_start - - # The beginning and ending SHAs can be between 6 and 40 hex characters, and + # The beginning and ending refs can be named or SHAs, and # the range notation can be double- or triple-dot. - PATTERN = /\h{6,40}\.{2,3}\h{6,40}/ + REF_PATTERN = /[0-9a-zA-Z][0-9a-zA-Z_.-]*[0-9a-zA-Z\^]/ + PATTERN = /#{REF_PATTERN}\.{2,3}#{REF_PATTERN}/ + + # In text references, the beginning and ending refs can only be SHAs + # between 6 and 40 hex characters. + STRICT_PATTERN = /\h{6,40}\.{2,3}\h{6,40}/ def self.reference_prefix '@' @@ -45,7 +48,7 @@ class CommitRange #{link_reference_pattern} | (?: (?:#{Project.reference_pattern}#{reference_prefix})? - (?#{PATTERN}) + (?#{STRICT_PATTERN}) ) }x end @@ -69,12 +72,16 @@ class CommitRange raise ArgumentError, "invalid CommitRange string format: #{range_string}" end - ref_from, @notation, ref_to = range_string.split(/(\.{2,3})/, 2) + @ref_from, @notation, @ref_to = range_string.split(/(\.{2,3})/, 2) - @exclude_start = @notation == '..' if project.valid_repo? - @commit_from = project.commit(ref_from) - @commit_to = project.commit(ref_to) + @commit_from = project.commit(@ref_from) + @commit_to = project.commit(@ref_to) + end + + if valid_commits? + @ref_from = Commit.truncate_sha(sha_from) if sha_from.start_with?(@ref_from) + @ref_to = Commit.truncate_sha(sha_to) if sha_to.start_with?(@ref_to) end end @@ -89,7 +96,7 @@ class CommitRange alias_method :id, :to_s def to_reference(from_project = nil) - reference = Commit.truncate_sha(sha_from) + notation + Commit.truncate_sha(sha_to) + reference = ref_from + notation + ref_to if cross_project_reference?(from_project) reference = project.to_reference + self.class.reference_prefix + reference @@ -111,7 +118,7 @@ class CommitRange end def exclude_start? - exclude_start + @notation == '..' end # Check if both the starting and ending commit IDs exist in a project's diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index 753140d60e6..4b4c769a110 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -5,8 +5,8 @@ module Gitlab::Markdown include FilterSpecHelper let(:project) { create(:project, :public) } - let(:commit1) { project.commit } - let(:commit2) { project.commit("HEAD~2") } + let(:commit1) { project.commit("HEAD~2") } + let(:commit2) { project.commit } let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}", project) } let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}", project) } @@ -89,7 +89,7 @@ module Gitlab::Markdown link = doc.css('a').first expect(link).to have_attribute('data-commit-range') - expect(link.attr('data-commit-range')).to eq range.to_reference + expect(link.attr('data-commit-range')).to eq range.to_s end it 'supports an :only_path option' do @@ -146,7 +146,8 @@ module Gitlab::Markdown context 'URL cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } - let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) } + let(:range) { CommitRange.new("#{commit1.id}...master", project) } + let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: 'master') } before do range.project = project2 @@ -156,7 +157,7 @@ module Gitlab::Markdown doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) + to eq reference end it 'links with adjacent text' do -- cgit v1.2.1 From 4a0ebccf6b96184888f2d28b6e97592c6cb239c7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 22:43:54 +0100 Subject: Fix specs --- app/models/concerns/mentionable.rb | 11 ++++++++--- .../lib/gitlab/markdown/commit_reference_filter_spec.rb | 1 - .../markdown/merge_request_reference_filter_spec.rb | 3 +-- spec/models/commit_spec.rb | 16 ++++++---------- spec/support/mentionable_shared_examples.rb | 17 +++++++---------- 5 files changed, 22 insertions(+), 26 deletions(-) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 193c91f1742..0d6cd86aade 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -62,13 +62,18 @@ module Mentionable return [] if text.blank? refs = all_references(current_user, text, load_lazy_references: load_lazy_references) - (refs.issues + refs.merge_requests + refs.commits) - [local_reference] + refs = (refs.issues + refs.merge_requests + refs.commits) + + # We're using this method instead of Array diffing because that requires + # both of the object's `hash` values to be the same, which may not be the + # case for otherwise identical Commit objects. + refs.reject! { |ref| ref == local_reference } end # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. def create_cross_references!(author = self.author, without = [], text = self.mentionable_text) refs = referenced_mentionables(author, text) - + # We're using this method instead of Array diffing because that requires # both of the object's `hash` values to be the same, which may not be the # case for otherwise identical Commit objects. @@ -111,7 +116,7 @@ module Mentionable # Only include changed fields that are mentionable source.select { |key, val| mentionable.include?(key) } end - + # Determine whether or not a cross-reference Note has already been created between this Mentionable and # the specified target. def cross_reference_exists?(target) diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index c1bcf29b1ba..a8c9c7efd56 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -148,7 +148,6 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape(project2.to_reference) expect(doc.to_html).to match(/\(#{commit.to_reference(project)}<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index 49d8a6e44da..ca3e7151e02 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -121,8 +121,7 @@ module Gitlab::Markdown let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } - let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, - project2, merge) + '/diffs#note_123' } + let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, project2, merge) + '/diffs#note_123' } it 'links to a valid reference' do doc = filter("See #{reference}") diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 90be9324951..1aaa927c216 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -15,12 +15,12 @@ describe Commit do describe '#to_reference' do it 'returns a String reference to the object' do - expect(commit.to_reference).to eq commit.id + expect(commit.to_reference).to eq commit.short_id end it 'supports a cross-project reference' do cross = double('project') - expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.id}" + expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.short_id}" end end @@ -77,14 +77,10 @@ eos let(:other_issue) { create :issue, project: other_project } it 'detects issues that this commit is marked as closing' do - allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid}") - expect(commit.closes_issues).to eq([issue]) - end - - it 'does not detect issues from other projects' do ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}" - allow(commit).to receive(:safe_message).and_return("Fixes #{ext_ref}") - expect(commit.closes_issues).to be_empty + allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid} and #{ext_ref}") + expect(commit.closes_issues).to include(issue) + expect(commit.closes_issues).to include(other_issue) end end @@ -92,7 +88,7 @@ eos subject { create(:project).commit } let(:author) { create(:user, email: subject.author_email) } - let(:backref_text) { "commit #{subject.id}" } + let(:backref_text) { "commit #{subject.short_id}" } let(:set_mentionable_text) do ->(txt) { allow(subject).to receive(:safe_message).and_return(txt) } end diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb index 3bb568f4d49..33d2b14583c 100644 --- a/spec/support/mentionable_shared_examples.rb +++ b/spec/support/mentionable_shared_examples.rb @@ -10,12 +10,12 @@ def common_mentionable_setup let(:mentioned_issue) { create(:issue, project: project) } let!(:mentioned_mr) { create(:merge_request, :simple, source_project: project) } - let(:mentioned_commit) { project.commit } + let(:mentioned_commit) { project.commit("HEAD~1") } let(:ext_proj) { create(:project, :public) } let(:ext_issue) { create(:issue, project: ext_proj) } let(:ext_mr) { create(:merge_request, :simple, source_project: ext_proj) } - let(:ext_commit) { ext_proj.commit } + let(:ext_commit) { ext_proj.commit("HEAD~2") } # Override to add known commits to the repository stub. let(:extra_commits) { [] } @@ -45,14 +45,11 @@ def common_mentionable_setup before do # Wire the project's repository to return the mentioned commit, and +nil+ # for any unrecognized commits. - commitmap = { - mentioned_commit.id => mentioned_commit - } - extra_commits.each { |c| commitmap[c.short_id] = c } - - allow(Project).to receive(:find).and_call_original - allow(Project).to receive(:find).with(project.id.to_s).and_return(project) - allow(project.repository).to receive(:commit) { |sha| commitmap[sha] } + allow_any_instance_of(::Repository).to receive(:commit).and_call_original + allow_any_instance_of(::Repository).to receive(:commit).with(mentioned_commit.short_id).and_return(mentioned_commit) + extra_commits.each do |commit| + allow_any_instance_of(::Repository).to receive(:commit).with(commit.short_id).and_return(commit) + end set_mentionable_text.call(ref_string) end -- cgit v1.2.1 From 8c4a3c77d87e89bf3fd237fef49fc87fb6170d86 Mon Sep 17 00:00:00 2001 From: Minsik Yoon Date: Mon, 30 Nov 2015 18:30:44 +0900 Subject: Add ignore whitespace change option to commit view --- CHANGELOG | 1 + app/controllers/projects/commit_controller.rb | 7 ++++++- spec/controllers/commit_controller_spec.rb | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 090b54f41a4..35bb9951d86 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Fix 500 error when update group member permission - Fix: Raw private snippets access workflow - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) + - Add ignore whitespace change option to commit view v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index deefdd76667..3f137440e28 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -67,7 +67,12 @@ class Projects::CommitController < Projects::ApplicationController end def define_show_vars - @diffs = commit.diffs + if params[:w].to_i == 1 + @diffs = commit.diffs({ ignore_whitespace_change: true }) + else + @diffs = commit.diffs + end + @notes_count = commit.notes.count @builds = ci_commit.builds if ci_commit diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb index bb3d87f3840..5337a69e84b 100644 --- a/spec/controllers/commit_controller_spec.rb +++ b/spec/controllers/commit_controller_spec.rb @@ -69,6 +69,21 @@ describe Projects::CommitController do expect(response.body).to start_with("diff --git") end + + it "should really only be a git diff without whitespace changes" do + get(:show, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: '66eceea0db202bb39c4e445e8ca28689645366c5', + # id: commit.id, + format: format, + w: 1) + + expect(response.body).to start_with("diff --git") + # without whitespace option, there are more than 2 diff_splits + diff_splits = assigns(:diffs)[0].diff.split("\n") + expect(diff_splits.length).to be <= 2 + end end describe "as patch" do -- cgit v1.2.1 From 5f6117c0aa391a6e9c96493ca01a8a23eb40f0cd Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Thu, 5 Nov 2015 16:53:05 +0100 Subject: update ci docs with yml reason and better quickstart --- doc/ci/quick_start/README.md | 202 +++++++++++++++++++++++++++++-------------- doc/ci/yaml/README.md | 30 ++++--- 2 files changed, 156 insertions(+), 76 deletions(-) diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index d69064a91fd..74626c8f8a4 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -1,44 +1,62 @@ # Quick Start -To start building projects with GitLab CI a few steps needs to be done. +GitLab Continuous Integration (CI) is fully integrated into GitLab itself. You +only need to enable it in the Services settings of your project. -## 1. Install GitLab and CI +This guide assumes that you: -First you need to have a working GitLab and GitLab CI instance. +- have a working GitLab instance of version 8.0 or higher or are using + [GitLab.com](https://gitlab.com/users/sign_in) +- have a project in GitLab that you would like to use CI for -You can omit this step if you use [GitLab.com](https://GitLab.com/). +In brief, the steps needed to have a working CI can be summed up to: -## 2. Create repository on GitLab +1. Create a new project +1. Add `.gitlab-ci.yml` to the git repository and push to GitLab +1. Configure a Runner -Once you login on your GitLab add a new repository where you will store your source code. -Push your application to that repository. +From there on, on every push to your git repository the build will be +automagically started by the Runner and will appear under the project's +`/builds` page. -## 3. Add project to CI +Now, let's break it down to pieces and work on solving the GitLab CI puzzle. -The next part is to login to GitLab CI. -Point your browser to the URL you have set GitLab or use [gitlab.com/ci](https://gitlab.com/ci/). +## 1. Creating a `.gitlab-ci.yml` file -On the first screen you will see a list of GitLab's projects that you have access to: + **GitLab CI** service is enabled automatically on the first push of a + `.gitlab-ci.yml` file in your repository and this is the recommended way. -![Projects](projects.png) +For other methods read [how to enable the GitLab CI service](../enable_ci.md). -Click **Add Project to CI**. -This will create project in CI and authorize GitLab CI to fetch sources from GitLab. +### What is `.gitlab-ci.yml` -> GitLab CI creates unique token that is used to configure GitLab CI service in GitLab. -> This token allows to access GitLab's repository and configures GitLab to trigger GitLab CI webhook on **Push events** and **Tag push events**. -> You can see that token by going to Project's Settings > Services > GitLab CI. -> You will see there token, the same token is assigned in GitLab CI settings of project. +The `.gitlab-ci.yml` file is where you configure what CI does with your project. +It lives in the root of your repository. -## 4. Create project's configuration - .gitlab-ci.yml +On any push to your repository, GitLab will look for the `.gitlab-ci.yml` +file and start builds on _Runners_ according to the contents of the file, +for that commit. -The next: You have to define how your project will be built. -GitLab CI uses [YAML](https://en.wikipedia.org/wiki/YAML) file to store build configuration. -You need to create `.gitlab-ci.yml` in root directory of your repository: +Because `.gitlab-ci.yml` is in the repository, it is version controlled, +old versions still build succesfully, forks can easily make use of CI, +branches can have separate builds and you have a single source of truth for CI. +You can read more about the reasons why we are using `.gitlab-ci.yml` +[in our blog about it][blog-ci]. + +`.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file. + +### Creating a simple `.gitlab-ci.yml` file + +You need to create a file named `.gitlab-ci.yml` in the root directory of your +repository. Below is an example for a Ruby on Rails project. ```yaml before_script: - - bundle install + - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs + - ruby -v + - which ruby + - gem install bundler --no-ri --no-rdoc + - bundle install --jobs $(nproc) "${FLAGS[@]}" rspec: script: @@ -49,71 +67,129 @@ rubocop: - bundle exec rubocop ``` -This is the simplest possible build configuration that will work for most Ruby applications: -1. Define two jobs `rspec` and `rubocop` with two different commands to be executed. -1. Before every job execute commands defined by `before_script`. +This is the simplest possible build configuration that will work for most Ruby +applications: + +1. Define two jobs `rspec` and `rubocop` with different commands to be executed. +1. Before every job, the commands defined by `before_script` are executed. + +The `.gitlab-ci.yml` file defines sets of jobs with constraints of how and when +they should be run. The jobs are defined as top-level elements with a name and +always have to contain the `script` name. Jobs are used to create builds, +which are then picked by [Runners](../runners/README.md) and executed within +the environment of the Runner. + +What is important is that each job is run independently from each other. -The `.gitlab-ci.yml` defines set of jobs with constrains how and when they should be run. -The jobs are defined as top-level elements with name and always have to contain the `script`. -Jobs are used to create builds, which are then picked by [runners](../runners/README.md) and executed within environment of the runner. -What is important that each job is run independently from each other. +If you want to check whether your `.gitlab-ci.yml` file is valid, there is a +Lint tool under the page `/ci/lint` of your GitLab instance. You can also find +the link under **Settings** -> **CI settings** in your project. -For more information and complete `.gitlab-ci.yml` syntax, please check the [Configuring project (.gitlab-ci.yml)](../yaml/README.md). +For more information and a complete `.gitlab-ci.yml` syntax, please check +[the documentation on .gitlab-ci.yml](../yaml/README.md). -## 5. Add file and push .gitlab-ci.yml to repository +### Push `.gitlab-ci.yml` to GitLab -Once you created `.gitlab-ci.yml` you should add it to git repository and push it to GitLab. +Once you've created `.gitlab-ci.yml`, you should add it to your git repository +and push it to GitLab. ```bash git add .gitlab-ci.yml -git commit +git commit -m "Add .gitlab-ci.yml" git push origin master ``` -If you refresh the project's page on GitLab CI you will notice a one new commit: +Now if you go to the **Builds** page you will see that the builds are pending. + +You can also go to the **Commits** page and notice the little clock icon next +to the commit SHA. + +![New commit pending](img/new_commit.png) + +Clicking on the clock icon you will be directed to the builds page for that +specific commit. + +![Single commit builds page](img/single_commit_status_pending.png) + +Notice that there are two jobs pending which are named after what we wrote in +`.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured +yet for these builds. + +The next step is to configure a Runner so that it picks the pending jobs. + +## 2. Configuring a Runner + +In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. +A Runner can be a virtual machine, a VPS, a bare-metal machine, a docker +container or even a cluster of containers. GitLab and the Runners communicate +through an API, so the only needed requirement is that the machine on which the +Runner is configured to have Internet access. + +A Runner can be specific to a certain project or serve multiple projects in +GitLab. If it serves all projects it's called a _Shared Runner_. + +Find more information about different Runners in the +[Runners](../runners/README.md) documentation. + +You can find whether any Runners are assigned to your project by going to +**Settings** -> **Runners**. + +Setting up a Runner is easy and straightforward. The official Runner supported +by GitLab is written in Go and can be found at +. + +In order to have a functional Runner you need to: + +1. [Install it][runner-install] +2. [Configure it](../runners/README.md#registering-a-specific-runner) + +For other types of unofficial Runners written in other languages, see the +[instructions for the various GitLab Runners](https://about.gitlab.com/gitlab-ci/#gitlab-runner). + +Once the Runner has been set up, you should see it on the Runners page of your +project, following **Settings** -> **Runners**. -![](new_commit.png) +![Activated runners](img/runners_activated.png) -However the commit has status **pending** which means that commit was not yet picked by runner. +### Shared Runners -## 6. Configure runner +If you use [GitLab.com](https://gitlab.com/) you can use **Shared Runners** +provided by GitLab Inc. -In GitLab CI, Runners run your builds. -A runner is a machine (can be virtual, bare-metal or VPS) that picks up builds through the coordinator API of GitLab CI. +These are special virtual machines that are run on GitLab's infrastructure that +can build any project. -A runner can be specific to a certain project or serve any project in GitLab CI. -A runner that serves all projects is called a shared runner. -More information about different runner types can be found in [Configuring runner](../runners/README.md). +To enable **Shared Runners** you have to go to your project's +**Settings** -> **Runners** and click **Enable shared runners**. -To check if you have runners assigned to your project go to **Runners**. You will find there information how to setup project specific runner: +[Read more on Shared Runners](../runners/README.md). -1. Install GitLab Runner software. Checkout the [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner) section to install it. -1. Specify following URL during runner setup: https://gitlab.com/ci/ -1. Use the following registration token during setup: TOKEN +## 3. Seeing the status of your build -If you do it correctly your runner should be shown under **Runners activated for this project**: +After configuring the Runner succesfully, you should see the status of your +last commit change from _pending_ to either _running_, _success_ or _failed_. -![](runners_activated.png) +You can view all builds, by going to the **Builds** page in your project. -### Shared runners +![Commit status](img/builds_status.png) -If you use [gitlab.com/ci](https://gitlab.com/ci/) you can use **Shared runners** provided by GitLab Inc. -These are special virtual machines that are run on GitLab's infrastructure that can build any project. -To enable **Shared runners** you have to go to **Runners** and click **Enable shared runners** for this project. +By clicking on a Build ID, you will be able to see the log of that build. +This is important to diagnose why a build failed or acted differently than +you expected. -## 7. Check status of commit +![Build log](img/build_log.png) -If everything went OK and you go to commit, the status of the commit should change from **pending** to either **running**, **success** or **failed**. +You are also able to view the status of any commit in the various pages in +GitLab, such as **Commits** and **Merge Requests**. -![](commit_status.png) +## Next steps -You can click **Build ID** to view build log for specific job. +Awesome! You started using CI in GitLab! -## 8. Congratulations! +Next you can look into doing more with the CI. Many people are using GitLab +to package, containerize, test and deploy software. -You managed to build your first project using GitLab CI. -You may need to tune your `.gitlab-ci.yml` file to implement build plan for your project. -A few examples how it can be done you can find on [Examples](../examples/README.md) page. +We have a number of [examples](../examples/README.md) available. -GitLab CI also offers **the Lint** tool to verify validity of your `.gitlab-ci.yml` which can be useful to troubleshoot potential problems. -The Lint is available from project's settings or by adding `/lint` to GitLab CI url. +[runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#installation +[blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/ diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 3dbf1afc7a9..9ee26c41e6d 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -20,6 +20,22 @@ Of course a command can execute code directly (`./configure;make;make install`) Jobs are used to create builds, which are then picked up by [runners](../runners/README.md) and executed within the environment of the runner. What is important, is that each job is run independently from each other. +## Why `.gitlab-ci.yml` + +By placing a single configuration file in the root of your repository, +it is version controlled and you get all the advantages of git. + +In addition, builds for older versions of the repository will work just fine, +as GitLab look at the `.gitlab-ci.yml` of the pushed commit. +This means that forks also build without any problem. + +You can even set up different builds for different branches. This allows you +to only deploy the `production` branch, for instance. + +By having a single source of truth, everyone can view and contribute to the +stability of your CI builds, eventually improving the quality of your development +cycle. + ## .gitlab-ci.yml The YAML syntax allows for using more complex job specifications than in the above example: @@ -185,7 +201,7 @@ This are two parameters that allow for setting a refs policy to limit when jobs There are a few rules that apply to usage of refs policy: -1. `only` and `except` are inclusive. If both `only` and `except` are defined in job specification the ref is filtered by `only` and `except`. +1. `only` and `except` are exclusive. If both `only` and `except` are defined in job specification only `only` is taken into account. 1. `only` and `except` allow for using the regexp expressions. 1. `only` and `except` allow for using special keywords: `branches` and `tags`. These names can be used for example to exclude all tags and all branches. @@ -198,18 +214,6 @@ job: - branches # use special keyword ``` -1. `only` and `except` allow for specify repository path to filter jobs for forks. -The repository path can be used to have jobs executed only for parent repository. - -```yaml -job: - only: - - branches@gitlab-org/gitlab-ce - except: - - master@gitlab-org/gitlab-ce -``` -The above will run `job` for all branches on `gitlab-org/gitlab-ce`, except master . - ### tags `tags` is used to select specific runners from the list of all runners that are allowed to run this project. -- cgit v1.2.1 From 2c30d11e80a4bb4112d01be7e1fbe51a321b3beb Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 5 Nov 2015 22:49:57 +0200 Subject: Clean up intro --- doc/ci/quick_start/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 74626c8f8a4..92dad2be3e8 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -16,8 +16,8 @@ In brief, the steps needed to have a working CI can be summed up to: 1. Configure a Runner From there on, on every push to your git repository the build will be -automagically started by the Runner and will appear under the project's -`/builds` page. +automagically started by the runner and will appear under the project's `/builds` +page. Now, let's break it down to pieces and work on solving the GitLab CI puzzle. -- cgit v1.2.1 From 1058652a920d1106d29452aadfef953937a188e5 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 6 Nov 2015 10:43:28 +0200 Subject: Add section of enabling GitLab CI --- doc/ci/quick_start/README.md | 7 +++++++ doc/ci/quick_start/builds_tab.png | Bin 0 -> 3845 bytes doc/ci/quick_start/ci_service_disabled.png | Bin 0 -> 3598 bytes doc/ci/quick_start/ci_service_enabled.png | Bin 0 -> 3545 bytes doc/ci/quick_start/ci_service_mark_active.png | Bin 0 -> 17193 bytes doc/ci/quick_start/enable_ci.md | 24 ++++++++++++++++++++++++ 6 files changed, 31 insertions(+) create mode 100644 doc/ci/quick_start/builds_tab.png create mode 100644 doc/ci/quick_start/ci_service_disabled.png create mode 100644 doc/ci/quick_start/ci_service_enabled.png create mode 100644 doc/ci/quick_start/ci_service_mark_active.png create mode 100644 doc/ci/quick_start/enable_ci.md diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 92dad2be3e8..d45ad3e5e23 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -21,6 +21,13 @@ page. Now, let's break it down to pieces and work on solving the GitLab CI puzzle. +## 1. Enable GitLab CI + +After creating a new project, the first thing to do is enable the **GitLab CI** +service in your project's settings if it isn't already enabled. + +Read [how to enable the GitLab CI service](enable_ci.md). + ## 1. Creating a `.gitlab-ci.yml` file **GitLab CI** service is enabled automatically on the first push of a diff --git a/doc/ci/quick_start/builds_tab.png b/doc/ci/quick_start/builds_tab.png new file mode 100644 index 00000000000..d088b8b329d Binary files /dev/null and b/doc/ci/quick_start/builds_tab.png differ diff --git a/doc/ci/quick_start/ci_service_disabled.png b/doc/ci/quick_start/ci_service_disabled.png new file mode 100644 index 00000000000..351de4267a4 Binary files /dev/null and b/doc/ci/quick_start/ci_service_disabled.png differ diff --git a/doc/ci/quick_start/ci_service_enabled.png b/doc/ci/quick_start/ci_service_enabled.png new file mode 100644 index 00000000000..dfe7488c1ba Binary files /dev/null and b/doc/ci/quick_start/ci_service_enabled.png differ diff --git a/doc/ci/quick_start/ci_service_mark_active.png b/doc/ci/quick_start/ci_service_mark_active.png new file mode 100644 index 00000000000..8bc8694cec3 Binary files /dev/null and b/doc/ci/quick_start/ci_service_mark_active.png differ diff --git a/doc/ci/quick_start/enable_ci.md b/doc/ci/quick_start/enable_ci.md new file mode 100644 index 00000000000..6f539c752d8 --- /dev/null +++ b/doc/ci/quick_start/enable_ci.md @@ -0,0 +1,24 @@ +# Enable GitLab CI + +GitLab Continuous Integration (CI) is fully integrated into GitLab itself. You +only need to enable it in the **Services** settings of your project. + +First, head over your project's page that you would like to enable CI for. +If you can see the **Builds** tab in the sidebar, then CI is enabled. + +![Builds tab](builds_tab.png) + +If not, go to **Settings > Services** and search for **GitLab CI**. Its state +should be disabled. + +![CI service disabled](ci_service_disabled.png) + +Click on **GitLab CI** to enter its settings, mark it as active and hit +**Save**. + +![Mark CI service as active](ci_service_mark_active.png) + +Do you see that green dot? Then good, the service is now enabled! You can also +check its status under **Services**. + +![CI service enabled](ci_service_enabled.png) -- cgit v1.2.1 From 3b4806bad95e55a40d3efe02e329d271995d8a70 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Sat, 7 Nov 2015 09:11:37 +0200 Subject: Add option to enable GitLab CI via .gitlab-ci.yml --- doc/ci/quick_start/enable_ci.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/doc/ci/quick_start/enable_ci.md b/doc/ci/quick_start/enable_ci.md index 6f539c752d8..42635e35d5d 100644 --- a/doc/ci/quick_start/enable_ci.md +++ b/doc/ci/quick_start/enable_ci.md @@ -1,15 +1,30 @@ # Enable GitLab CI -GitLab Continuous Integration (CI) is fully integrated into GitLab itself. You -only need to enable it in the **Services** settings of your project. +GitLab Continuous Integration (CI) is fully integrated into GitLab itself as +of [version 8.0](https://about.gitlab.com/2015/09/22/gitlab-8-0-released/). -First, head over your project's page that you would like to enable CI for. -If you can see the **Builds** tab in the sidebar, then CI is enabled. +First, head over your project's page that you would like to enable GitLab CI +for. If you can see the **Builds** tab in the sidebar, then GitLab CI is +already enabled and you can skip reading the rest of this guide. ![Builds tab](builds_tab.png) -If not, go to **Settings > Services** and search for **GitLab CI**. Its state -should be disabled. +If not, there are two ways to enable it in your project. + +## Use .gitlab-ci.yml to enable GitLab CI + +GitLab CI will be automatically enabled if you just push a +[`.gitlab-ci.yml`](../yaml/README.md) in your git repository. GitLab will +pick up the change immediately and GitLab CI will be enabled for this project. +This is the recommended way. + +## Manually enable GitLab CI + +The second way is to manually enable it in the project's **Services** settings +and this is also the way to disable it if needed. + +Go to **Settings > Services** and search for **GitLab CI**. Its state should +be disabled. ![CI service disabled](ci_service_disabled.png) -- cgit v1.2.1 From 32a3c3e068e957716eac56a755f44268c07c5957 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 10 Nov 2015 03:26:51 +0200 Subject: Place images in their own dir --- doc/ci/enable_ci.md | 39 ++++++++++++++++++++++++++ doc/ci/img/builds_tab.png | Bin 0 -> 3845 bytes doc/ci/img/ci_service_disabled.png | Bin 0 -> 3598 bytes doc/ci/img/ci_service_enabled.png | Bin 0 -> 3545 bytes doc/ci/img/ci_service_mark_active.png | Bin 0 -> 17193 bytes doc/ci/quick_start/README.md | 14 ++++----- doc/ci/quick_start/build_status.png | Bin 62140 -> 0 bytes doc/ci/quick_start/builds_tab.png | Bin 3845 -> 0 bytes doc/ci/quick_start/ci_service_disabled.png | Bin 3598 -> 0 bytes doc/ci/quick_start/ci_service_enabled.png | Bin 3545 -> 0 bytes doc/ci/quick_start/ci_service_mark_active.png | Bin 17193 -> 0 bytes doc/ci/quick_start/commit_status.png | Bin 33492 -> 0 bytes doc/ci/quick_start/enable_ci.md | 39 -------------------------- doc/ci/quick_start/img/build_status.png | Bin 0 -> 62140 bytes doc/ci/quick_start/img/commit_status.png | Bin 0 -> 33492 bytes doc/ci/quick_start/img/new_commit.png | Bin 0 -> 47527 bytes doc/ci/quick_start/img/projects.png | Bin 0 -> 37014 bytes doc/ci/quick_start/img/runners.png | Bin 0 -> 123048 bytes doc/ci/quick_start/img/runners_activated.png | Bin 0 -> 60769 bytes doc/ci/quick_start/new_commit.png | Bin 47527 -> 0 bytes doc/ci/quick_start/projects.png | Bin 37014 -> 0 bytes doc/ci/quick_start/runners.png | Bin 123048 -> 0 bytes doc/ci/quick_start/runners_activated.png | Bin 60769 -> 0 bytes 23 files changed, 45 insertions(+), 47 deletions(-) create mode 100644 doc/ci/enable_ci.md create mode 100644 doc/ci/img/builds_tab.png create mode 100644 doc/ci/img/ci_service_disabled.png create mode 100644 doc/ci/img/ci_service_enabled.png create mode 100644 doc/ci/img/ci_service_mark_active.png delete mode 100644 doc/ci/quick_start/build_status.png delete mode 100644 doc/ci/quick_start/builds_tab.png delete mode 100644 doc/ci/quick_start/ci_service_disabled.png delete mode 100644 doc/ci/quick_start/ci_service_enabled.png delete mode 100644 doc/ci/quick_start/ci_service_mark_active.png delete mode 100644 doc/ci/quick_start/commit_status.png delete mode 100644 doc/ci/quick_start/enable_ci.md create mode 100644 doc/ci/quick_start/img/build_status.png create mode 100644 doc/ci/quick_start/img/commit_status.png create mode 100644 doc/ci/quick_start/img/new_commit.png create mode 100644 doc/ci/quick_start/img/projects.png create mode 100644 doc/ci/quick_start/img/runners.png create mode 100644 doc/ci/quick_start/img/runners_activated.png delete mode 100644 doc/ci/quick_start/new_commit.png delete mode 100644 doc/ci/quick_start/projects.png delete mode 100644 doc/ci/quick_start/runners.png delete mode 100644 doc/ci/quick_start/runners_activated.png diff --git a/doc/ci/enable_ci.md b/doc/ci/enable_ci.md new file mode 100644 index 00000000000..01c684dabec --- /dev/null +++ b/doc/ci/enable_ci.md @@ -0,0 +1,39 @@ +# Enable GitLab CI + +GitLab Continuous Integration (CI) is fully integrated into GitLab itself as +of [version 8.0](https://about.gitlab.com/2015/09/22/gitlab-8-0-released/). + +First, head over your project's page that you would like to enable GitLab CI +for. If you can see the **Builds** tab in the sidebar, then GitLab CI is +already enabled and you can skip reading the rest of this guide. + +![Builds tab](img/builds_tab.png) + +If not, there are two ways to enable it in your project. + +## Use .gitlab-ci.yml to enable GitLab CI + +GitLab CI will be automatically enabled if you just push a +[`.gitlab-ci.yml`](yaml/README.md) in your git repository. GitLab will +pick up the change immediately and GitLab CI will be enabled for this project. +This is the recommended way. + +## Manually enable GitLab CI + +The second way is to manually enable it in the project's **Services** settings +and this is also the way to disable it if needed. + +Go to **Settings > Services** and search for **GitLab CI**. Its state should +be disabled. + +![CI service disabled](img/ci_service_disabled.png) + +Click on **GitLab CI** to enter its settings, mark it as active and hit +**Save**. + +![Mark CI service as active](img/ci_service_mark_active.png) + +Do you see that green dot? Then good, the service is now enabled! You can also +check its status under **Services**. + +![CI service enabled](img/ci_service_enabled.png) diff --git a/doc/ci/img/builds_tab.png b/doc/ci/img/builds_tab.png new file mode 100644 index 00000000000..d088b8b329d Binary files /dev/null and b/doc/ci/img/builds_tab.png differ diff --git a/doc/ci/img/ci_service_disabled.png b/doc/ci/img/ci_service_disabled.png new file mode 100644 index 00000000000..351de4267a4 Binary files /dev/null and b/doc/ci/img/ci_service_disabled.png differ diff --git a/doc/ci/img/ci_service_enabled.png b/doc/ci/img/ci_service_enabled.png new file mode 100644 index 00000000000..dfe7488c1ba Binary files /dev/null and b/doc/ci/img/ci_service_enabled.png differ diff --git a/doc/ci/img/ci_service_mark_active.png b/doc/ci/img/ci_service_mark_active.png new file mode 100644 index 00000000000..8bc8694cec3 Binary files /dev/null and b/doc/ci/img/ci_service_mark_active.png differ diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index d45ad3e5e23..ce05a6f417c 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -16,19 +16,17 @@ In brief, the steps needed to have a working CI can be summed up to: 1. Configure a Runner From there on, on every push to your git repository the build will be -automagically started by the runner and will appear under the project's `/builds` -page. +automagically started by the runner and will appear under the project's +`/builds` page. Now, let's break it down to pieces and work on solving the GitLab CI puzzle. -## 1. Enable GitLab CI - -After creating a new project, the first thing to do is enable the **GitLab CI** -service in your project's settings if it isn't already enabled. +## 1. Creating a `.gitlab-ci.yml` file -Read [how to enable the GitLab CI service](enable_ci.md). + **GitLab CI** service is enabled automatically on the first push of a + `.gitlab-ci.yml` file in your repository and this is the recommended way. -## 1. Creating a `.gitlab-ci.yml` file +For other methods read [how to enable the GitLab CI service](../enable_ci.md). **GitLab CI** service is enabled automatically on the first push of a `.gitlab-ci.yml` file in your repository and this is the recommended way. diff --git a/doc/ci/quick_start/build_status.png b/doc/ci/quick_start/build_status.png deleted file mode 100644 index 333259e6acd..00000000000 Binary files a/doc/ci/quick_start/build_status.png and /dev/null differ diff --git a/doc/ci/quick_start/builds_tab.png b/doc/ci/quick_start/builds_tab.png deleted file mode 100644 index d088b8b329d..00000000000 Binary files a/doc/ci/quick_start/builds_tab.png and /dev/null differ diff --git a/doc/ci/quick_start/ci_service_disabled.png b/doc/ci/quick_start/ci_service_disabled.png deleted file mode 100644 index 351de4267a4..00000000000 Binary files a/doc/ci/quick_start/ci_service_disabled.png and /dev/null differ diff --git a/doc/ci/quick_start/ci_service_enabled.png b/doc/ci/quick_start/ci_service_enabled.png deleted file mode 100644 index dfe7488c1ba..00000000000 Binary files a/doc/ci/quick_start/ci_service_enabled.png and /dev/null differ diff --git a/doc/ci/quick_start/ci_service_mark_active.png b/doc/ci/quick_start/ci_service_mark_active.png deleted file mode 100644 index 8bc8694cec3..00000000000 Binary files a/doc/ci/quick_start/ci_service_mark_active.png and /dev/null differ diff --git a/doc/ci/quick_start/commit_status.png b/doc/ci/quick_start/commit_status.png deleted file mode 100644 index 725b79e6f91..00000000000 Binary files a/doc/ci/quick_start/commit_status.png and /dev/null differ diff --git a/doc/ci/quick_start/enable_ci.md b/doc/ci/quick_start/enable_ci.md deleted file mode 100644 index 42635e35d5d..00000000000 --- a/doc/ci/quick_start/enable_ci.md +++ /dev/null @@ -1,39 +0,0 @@ -# Enable GitLab CI - -GitLab Continuous Integration (CI) is fully integrated into GitLab itself as -of [version 8.0](https://about.gitlab.com/2015/09/22/gitlab-8-0-released/). - -First, head over your project's page that you would like to enable GitLab CI -for. If you can see the **Builds** tab in the sidebar, then GitLab CI is -already enabled and you can skip reading the rest of this guide. - -![Builds tab](builds_tab.png) - -If not, there are two ways to enable it in your project. - -## Use .gitlab-ci.yml to enable GitLab CI - -GitLab CI will be automatically enabled if you just push a -[`.gitlab-ci.yml`](../yaml/README.md) in your git repository. GitLab will -pick up the change immediately and GitLab CI will be enabled for this project. -This is the recommended way. - -## Manually enable GitLab CI - -The second way is to manually enable it in the project's **Services** settings -and this is also the way to disable it if needed. - -Go to **Settings > Services** and search for **GitLab CI**. Its state should -be disabled. - -![CI service disabled](ci_service_disabled.png) - -Click on **GitLab CI** to enter its settings, mark it as active and hit -**Save**. - -![Mark CI service as active](ci_service_mark_active.png) - -Do you see that green dot? Then good, the service is now enabled! You can also -check its status under **Services**. - -![CI service enabled](ci_service_enabled.png) diff --git a/doc/ci/quick_start/img/build_status.png b/doc/ci/quick_start/img/build_status.png new file mode 100644 index 00000000000..333259e6acd Binary files /dev/null and b/doc/ci/quick_start/img/build_status.png differ diff --git a/doc/ci/quick_start/img/commit_status.png b/doc/ci/quick_start/img/commit_status.png new file mode 100644 index 00000000000..725b79e6f91 Binary files /dev/null and b/doc/ci/quick_start/img/commit_status.png differ diff --git a/doc/ci/quick_start/img/new_commit.png b/doc/ci/quick_start/img/new_commit.png new file mode 100644 index 00000000000..3839e893c17 Binary files /dev/null and b/doc/ci/quick_start/img/new_commit.png differ diff --git a/doc/ci/quick_start/img/projects.png b/doc/ci/quick_start/img/projects.png new file mode 100644 index 00000000000..0b3430a69db Binary files /dev/null and b/doc/ci/quick_start/img/projects.png differ diff --git a/doc/ci/quick_start/img/runners.png b/doc/ci/quick_start/img/runners.png new file mode 100644 index 00000000000..25b4046bc00 Binary files /dev/null and b/doc/ci/quick_start/img/runners.png differ diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png new file mode 100644 index 00000000000..c934bd12f41 Binary files /dev/null and b/doc/ci/quick_start/img/runners_activated.png differ diff --git a/doc/ci/quick_start/new_commit.png b/doc/ci/quick_start/new_commit.png deleted file mode 100644 index 3839e893c17..00000000000 Binary files a/doc/ci/quick_start/new_commit.png and /dev/null differ diff --git a/doc/ci/quick_start/projects.png b/doc/ci/quick_start/projects.png deleted file mode 100644 index 0b3430a69db..00000000000 Binary files a/doc/ci/quick_start/projects.png and /dev/null differ diff --git a/doc/ci/quick_start/runners.png b/doc/ci/quick_start/runners.png deleted file mode 100644 index 25b4046bc00..00000000000 Binary files a/doc/ci/quick_start/runners.png and /dev/null differ diff --git a/doc/ci/quick_start/runners_activated.png b/doc/ci/quick_start/runners_activated.png deleted file mode 100644 index c934bd12f41..00000000000 Binary files a/doc/ci/quick_start/runners_activated.png and /dev/null differ -- cgit v1.2.1 From e70fb9cf75d08e8439d94637726dbee7e2324ab6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 10 Nov 2015 03:49:32 +0200 Subject: New images for the commit build status --- doc/ci/quick_start/README.md | 2 -- doc/ci/quick_start/img/new_commit.png | Bin 47527 -> 9033 bytes .../quick_start/img/single_commit_status_pending.png | Bin 0 -> 36431 bytes doc/ci/quick_start/img/status_pending.png | Bin 0 -> 19782 bytes 4 files changed, 2 deletions(-) create mode 100644 doc/ci/quick_start/img/single_commit_status_pending.png create mode 100644 doc/ci/quick_start/img/status_pending.png diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index ce05a6f417c..073146b9d15 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -120,8 +120,6 @@ Notice that there are two jobs pending which are named after what we wrote in `.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured yet for these builds. -The next step is to configure a Runner so that it picks the pending jobs. - ## 2. Configuring a Runner In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. diff --git a/doc/ci/quick_start/img/new_commit.png b/doc/ci/quick_start/img/new_commit.png index 3839e893c17..3d3c9d5c0bd 100644 Binary files a/doc/ci/quick_start/img/new_commit.png and b/doc/ci/quick_start/img/new_commit.png differ diff --git a/doc/ci/quick_start/img/single_commit_status_pending.png b/doc/ci/quick_start/img/single_commit_status_pending.png new file mode 100644 index 00000000000..23b3bb5acfc Binary files /dev/null and b/doc/ci/quick_start/img/single_commit_status_pending.png differ diff --git a/doc/ci/quick_start/img/status_pending.png b/doc/ci/quick_start/img/status_pending.png new file mode 100644 index 00000000000..a049ec2a5ba Binary files /dev/null and b/doc/ci/quick_start/img/status_pending.png differ -- cgit v1.2.1 From 11b204f1312c3d6aa579b7a04cb4bcf794ad5239 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 10 Nov 2015 21:16:12 +0200 Subject: Final touches for the quick start quide --- doc/ci/quick_start/README.md | 6 ++++-- doc/ci/quick_start/img/build_log.png | Bin 0 -> 63272 bytes doc/ci/quick_start/img/builds_status.png | Bin 0 -> 49121 bytes doc/ci/quick_start/img/runners_activated.png | Bin 60769 -> 27597 bytes 4 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 doc/ci/quick_start/img/build_log.png create mode 100644 doc/ci/quick_start/img/builds_status.png diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 073146b9d15..7771d78d91f 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -16,7 +16,7 @@ In brief, the steps needed to have a working CI can be summed up to: 1. Configure a Runner From there on, on every push to your git repository the build will be -automagically started by the runner and will appear under the project's +automagically started by the Runner and will appear under the project's `/builds` page. Now, let's break it down to pieces and work on solving the GitLab CI puzzle. @@ -120,6 +120,8 @@ Notice that there are two jobs pending which are named after what we wrote in `.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured yet for these builds. +The next step is to configure a Runner so that it picks the pending jobs. + ## 2. Configuring a Runner In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. @@ -136,8 +138,8 @@ Find more information about different Runners in the You can find whether any Runners are assigned to your project by going to **Settings** -> **Runners**. - Setting up a Runner is easy and straightforward. The official Runner supported + by GitLab is written in Go and can be found at . diff --git a/doc/ci/quick_start/img/build_log.png b/doc/ci/quick_start/img/build_log.png new file mode 100644 index 00000000000..89e6cd40cb6 Binary files /dev/null and b/doc/ci/quick_start/img/build_log.png differ diff --git a/doc/ci/quick_start/img/builds_status.png b/doc/ci/quick_start/img/builds_status.png new file mode 100644 index 00000000000..b8e6c2a361a Binary files /dev/null and b/doc/ci/quick_start/img/builds_status.png differ diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png index c934bd12f41..eafcfd6ecd5 100644 Binary files a/doc/ci/quick_start/img/runners_activated.png and b/doc/ci/quick_start/img/runners_activated.png differ -- cgit v1.2.1 From aea5b72faafa6911377cb0c9fc0801269ac59e1e Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 11 Nov 2015 09:57:21 +0200 Subject: Remove old images --- doc/ci/quick_start/img/build_status.png | Bin 62140 -> 0 bytes doc/ci/quick_start/img/commit_status.png | Bin 33492 -> 0 bytes doc/ci/quick_start/img/projects.png | Bin 37014 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/ci/quick_start/img/build_status.png delete mode 100644 doc/ci/quick_start/img/commit_status.png delete mode 100644 doc/ci/quick_start/img/projects.png diff --git a/doc/ci/quick_start/img/build_status.png b/doc/ci/quick_start/img/build_status.png deleted file mode 100644 index 333259e6acd..00000000000 Binary files a/doc/ci/quick_start/img/build_status.png and /dev/null differ diff --git a/doc/ci/quick_start/img/commit_status.png b/doc/ci/quick_start/img/commit_status.png deleted file mode 100644 index 725b79e6f91..00000000000 Binary files a/doc/ci/quick_start/img/commit_status.png and /dev/null differ diff --git a/doc/ci/quick_start/img/projects.png b/doc/ci/quick_start/img/projects.png deleted file mode 100644 index 0b3430a69db..00000000000 Binary files a/doc/ci/quick_start/img/projects.png and /dev/null differ -- cgit v1.2.1 From 6805f1bc47640886020e5febf015a73e5862114c Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 1 Dec 2015 10:05:24 +0200 Subject: Clean up quick start quide [ci skip] * Remove references to enabling CI since it it will be deprecated * Revert changes in yaml/README.md, refine it in a separate MR --- doc/ci/enable_ci.md | 39 ---------------------- doc/ci/img/ci_service_disabled.png | Bin 3598 -> 0 bytes doc/ci/img/ci_service_enabled.png | Bin 3545 -> 0 bytes doc/ci/img/ci_service_mark_active.png | Bin 17193 -> 0 bytes doc/ci/quick_start/README.md | 61 ++++++++++++++++------------------ doc/ci/quick_start/img/runners.png | Bin 123048 -> 0 bytes doc/ci/yaml/README.md | 30 ++++++++--------- 7 files changed, 42 insertions(+), 88 deletions(-) delete mode 100644 doc/ci/enable_ci.md delete mode 100644 doc/ci/img/ci_service_disabled.png delete mode 100644 doc/ci/img/ci_service_enabled.png delete mode 100644 doc/ci/img/ci_service_mark_active.png delete mode 100644 doc/ci/quick_start/img/runners.png diff --git a/doc/ci/enable_ci.md b/doc/ci/enable_ci.md deleted file mode 100644 index 01c684dabec..00000000000 --- a/doc/ci/enable_ci.md +++ /dev/null @@ -1,39 +0,0 @@ -# Enable GitLab CI - -GitLab Continuous Integration (CI) is fully integrated into GitLab itself as -of [version 8.0](https://about.gitlab.com/2015/09/22/gitlab-8-0-released/). - -First, head over your project's page that you would like to enable GitLab CI -for. If you can see the **Builds** tab in the sidebar, then GitLab CI is -already enabled and you can skip reading the rest of this guide. - -![Builds tab](img/builds_tab.png) - -If not, there are two ways to enable it in your project. - -## Use .gitlab-ci.yml to enable GitLab CI - -GitLab CI will be automatically enabled if you just push a -[`.gitlab-ci.yml`](yaml/README.md) in your git repository. GitLab will -pick up the change immediately and GitLab CI will be enabled for this project. -This is the recommended way. - -## Manually enable GitLab CI - -The second way is to manually enable it in the project's **Services** settings -and this is also the way to disable it if needed. - -Go to **Settings > Services** and search for **GitLab CI**. Its state should -be disabled. - -![CI service disabled](img/ci_service_disabled.png) - -Click on **GitLab CI** to enter its settings, mark it as active and hit -**Save**. - -![Mark CI service as active](img/ci_service_mark_active.png) - -Do you see that green dot? Then good, the service is now enabled! You can also -check its status under **Services**. - -![CI service enabled](img/ci_service_enabled.png) diff --git a/doc/ci/img/ci_service_disabled.png b/doc/ci/img/ci_service_disabled.png deleted file mode 100644 index 351de4267a4..00000000000 Binary files a/doc/ci/img/ci_service_disabled.png and /dev/null differ diff --git a/doc/ci/img/ci_service_enabled.png b/doc/ci/img/ci_service_enabled.png deleted file mode 100644 index dfe7488c1ba..00000000000 Binary files a/doc/ci/img/ci_service_enabled.png and /dev/null differ diff --git a/doc/ci/img/ci_service_mark_active.png b/doc/ci/img/ci_service_mark_active.png deleted file mode 100644 index 8bc8694cec3..00000000000 Binary files a/doc/ci/img/ci_service_mark_active.png and /dev/null differ diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 7771d78d91f..a9b36139de9 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -1,7 +1,7 @@ # Quick Start -GitLab Continuous Integration (CI) is fully integrated into GitLab itself. You -only need to enable it in the Services settings of your project. +Starting from version 8.0, GitLab Continuous Integration (CI) is fully +integrated into GitLab itself and is enabled by default on all projects. This guide assumes that you: @@ -21,17 +21,10 @@ automagically started by the Runner and will appear under the project's Now, let's break it down to pieces and work on solving the GitLab CI puzzle. -## 1. Creating a `.gitlab-ci.yml` file +## Creating a `.gitlab-ci.yml` file - **GitLab CI** service is enabled automatically on the first push of a - `.gitlab-ci.yml` file in your repository and this is the recommended way. - -For other methods read [how to enable the GitLab CI service](../enable_ci.md). - - **GitLab CI** service is enabled automatically on the first push of a - `.gitlab-ci.yml` file in your repository and this is the recommended way. - -For other methods read [how to enable the GitLab CI service](../enable_ci.md). +Before you create `.gitlab-ci.yml` let's first explain in brief what this is +all about. ### What is `.gitlab-ci.yml` @@ -48,7 +41,9 @@ branches can have separate builds and you have a single source of truth for CI. You can read more about the reasons why we are using `.gitlab-ci.yml` [in our blog about it][blog-ci]. -`.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file. +**Note:** `.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file +so you have to pay extra attention to the identation. Always use spaces, not +tabs. ### Creating a simple `.gitlab-ci.yml` file @@ -75,20 +70,21 @@ rubocop: This is the simplest possible build configuration that will work for most Ruby applications: -1. Define two jobs `rspec` and `rubocop` with different commands to be executed. +1. Define two jobs `rspec` and `rubocop` (the names are arbitrary) with + different commands to be executed. 1. Before every job, the commands defined by `before_script` are executed. The `.gitlab-ci.yml` file defines sets of jobs with constraints of how and when -they should be run. The jobs are defined as top-level elements with a name and -always have to contain the `script` name. Jobs are used to create builds, -which are then picked by [Runners](../runners/README.md) and executed within -the environment of the Runner. +they should be run. The jobs are defined as top-level elements with a name (in +our case `rspec` and `rubocop`) and always have to contain the `script` keyword. +Jobs are used to create builds, which are then picked by +[Runners](../runners/README.md) and executed within the environment of the Runner. What is important is that each job is run independently from each other. If you want to check whether your `.gitlab-ci.yml` file is valid, there is a Lint tool under the page `/ci/lint` of your GitLab instance. You can also find -the link under **Settings** -> **CI settings** in your project. +the link under **Settings > CI settings** in your project. For more information and a complete `.gitlab-ci.yml` syntax, please check [the documentation on .gitlab-ci.yml](../yaml/README.md). @@ -122,13 +118,13 @@ yet for these builds. The next step is to configure a Runner so that it picks the pending jobs. -## 2. Configuring a Runner +## Configuring a Runner In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. A Runner can be a virtual machine, a VPS, a bare-metal machine, a docker container or even a cluster of containers. GitLab and the Runners communicate through an API, so the only needed requirement is that the machine on which the -Runner is configured to have Internet access. +Runner is configured to has Internet access. A Runner can be specific to a certain project or serve multiple projects in GitLab. If it serves all projects it's called a _Shared Runner_. @@ -137,22 +133,23 @@ Find more information about different Runners in the [Runners](../runners/README.md) documentation. You can find whether any Runners are assigned to your project by going to -**Settings** -> **Runners**. -Setting up a Runner is easy and straightforward. The official Runner supported - -by GitLab is written in Go and can be found at +**Settings > Runners**. Setting up a Runner is easy and straightforward. The +official Runner supported by GitLab is written in Go and can be found at . -In order to have a functional Runner you need to: +In order to have a functional Runner you need to follow two steps: 1. [Install it][runner-install] 2. [Configure it](../runners/README.md#registering-a-specific-runner) +Follow the links above to set up your own Runner or use a Shared Runner as +described in the next section. + For other types of unofficial Runners written in other languages, see the [instructions for the various GitLab Runners](https://about.gitlab.com/gitlab-ci/#gitlab-runner). Once the Runner has been set up, you should see it on the Runners page of your -project, following **Settings** -> **Runners**. +project, following **Settings > Runners**. ![Activated runners](img/runners_activated.png) @@ -161,15 +158,15 @@ project, following **Settings** -> **Runners**. If you use [GitLab.com](https://gitlab.com/) you can use **Shared Runners** provided by GitLab Inc. -These are special virtual machines that are run on GitLab's infrastructure that -can build any project. +These are special virtual machines that run on GitLab's infrastructure and can +build any project. To enable **Shared Runners** you have to go to your project's -**Settings** -> **Runners** and click **Enable shared runners**. +**Settings > Runners** and click **Enable shared runners**. [Read more on Shared Runners](../runners/README.md). -## 3. Seeing the status of your build +## Seeing the status of your build After configuring the Runner succesfully, you should see the status of your last commit change from _pending_ to either _running_, _success_ or _failed_. @@ -194,7 +191,7 @@ Awesome! You started using CI in GitLab! Next you can look into doing more with the CI. Many people are using GitLab to package, containerize, test and deploy software. -We have a number of [examples](../examples/README.md) available. +Visit our various languages examples at . [runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#installation [blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/ diff --git a/doc/ci/quick_start/img/runners.png b/doc/ci/quick_start/img/runners.png deleted file mode 100644 index 25b4046bc00..00000000000 Binary files a/doc/ci/quick_start/img/runners.png and /dev/null differ diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 9ee26c41e6d..3dbf1afc7a9 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -20,22 +20,6 @@ Of course a command can execute code directly (`./configure;make;make install`) Jobs are used to create builds, which are then picked up by [runners](../runners/README.md) and executed within the environment of the runner. What is important, is that each job is run independently from each other. -## Why `.gitlab-ci.yml` - -By placing a single configuration file in the root of your repository, -it is version controlled and you get all the advantages of git. - -In addition, builds for older versions of the repository will work just fine, -as GitLab look at the `.gitlab-ci.yml` of the pushed commit. -This means that forks also build without any problem. - -You can even set up different builds for different branches. This allows you -to only deploy the `production` branch, for instance. - -By having a single source of truth, everyone can view and contribute to the -stability of your CI builds, eventually improving the quality of your development -cycle. - ## .gitlab-ci.yml The YAML syntax allows for using more complex job specifications than in the above example: @@ -201,7 +185,7 @@ This are two parameters that allow for setting a refs policy to limit when jobs There are a few rules that apply to usage of refs policy: -1. `only` and `except` are exclusive. If both `only` and `except` are defined in job specification only `only` is taken into account. +1. `only` and `except` are inclusive. If both `only` and `except` are defined in job specification the ref is filtered by `only` and `except`. 1. `only` and `except` allow for using the regexp expressions. 1. `only` and `except` allow for using special keywords: `branches` and `tags`. These names can be used for example to exclude all tags and all branches. @@ -214,6 +198,18 @@ job: - branches # use special keyword ``` +1. `only` and `except` allow for specify repository path to filter jobs for forks. +The repository path can be used to have jobs executed only for parent repository. + +```yaml +job: + only: + - branches@gitlab-org/gitlab-ce + except: + - master@gitlab-org/gitlab-ce +``` +The above will run `job` for all branches on `gitlab-org/gitlab-ce`, except master . + ### tags `tags` is used to select specific runners from the list of all runners that are allowed to run this project. -- cgit v1.2.1 From bd4ab21c07061e06166b08d86157e4004665ccbc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 12:49:22 +0100 Subject: Fix code docs --- app/models/commit_range.rb | 7 ++----- lib/gitlab/markdown/abstract_reference_filter.rb | 15 +++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index 7b1164b024c..449689faf65 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -13,8 +13,7 @@ # range.to_param # => {from: "f3f856029bc5f966c5a7ee24cf7efefdd20e6019^", to: "e86e1013709735be5bb767e2b228930c543f25ae"} # range.to_s # => "f3f85602..e86e1013" # -# # Assuming `project` is a Project with a repository containing both commits: -# range.project = project +# # Assuming the specified project has a repository containing both commits: # range.valid_commits? # => true # class CommitRange @@ -68,7 +67,7 @@ class CommitRange range_string.strip! - unless range_string.match(/\A#{PATTERN}\z/) + unless range_string =~ /\A#{PATTERN}\z/ raise ArgumentError, "invalid CommitRange string format: #{range_string}" end @@ -123,8 +122,6 @@ class CommitRange # Check if both the starting and ending commit IDs exist in a project's # repository - # - # project - An optional Project to check (default: `project`) def valid_commits? commit_start.present? && commit_end.present? end diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index 4adc44361b7..02a9e05a699 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -2,8 +2,8 @@ require 'gitlab/markdown' module Gitlab module Markdown - # Issues, Snippets and Merge Requests shares similar functionality in refernce filtering. - # All this functionality moved to this class + # Issues, Snippets, Merge Requests, Commits and Commit Ranges share + # similar functionality in refernce filtering. class AbstractReferenceFilter < ReferenceFilter include CrossProjectReference @@ -26,16 +26,15 @@ module Gitlab # Public: Find references in text (like `!123` for merge requests) # - # AnyReferenceFilter.references_in(text) do |match, object| - # "PREFIX#{object}" + # AnyReferenceFilter.references_in(text) do |match, id, project_ref, matches| + # object = find_object(project_ref, id) + # "#{object.to_reference}" # end # - # PREFIX - symbol that detects reference (like ! for merge requests) - # object - reference object (snippet, merget request etc) # text - String text to search. # - # Yields the String match, the Integer referenced object ID, and an optional String - # of the external project reference. + # Yields the String match, the Integer referenced object ID, an optional String + # of the external project reference, and all of the matchdata. # # Returns a String replaced with the return of the block. def self.references_in(text) -- cgit v1.2.1 From 62c14ba2edf9ac4b4bb1e8c46c0c60f1b6574909 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 12:58:45 +0100 Subject: Render commit reference using short sha, but include full sha in comment. --- app/models/commit.rb | 8 ++++++++ app/models/commit_range.rb | 8 ++++++++ app/models/concerns/referable.rb | 4 ++++ lib/gitlab/markdown/abstract_reference_filter.rb | 2 +- .../markdown/commit_range_reference_filter_spec.rb | 6 +++--- .../markdown/commit_reference_filter_spec.rb | 2 +- spec/models/commit_range_spec.rb | 22 +++++++++++++++++++--- spec/models/commit_spec.rb | 17 ++++++++++++++--- 8 files changed, 58 insertions(+), 11 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index fc03d2580d7..a5d041a49c8 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -86,6 +86,14 @@ class Commit end def to_reference(from_project = nil) + if cross_project_reference?(from_project) + project.to_reference + self.class.reference_prefix + self.id + else + self.id + end + end + + def reference_link_text(from_project = nil) if cross_project_reference?(from_project) project.to_reference + self.class.reference_prefix + self.short_id else diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index 449689faf65..f786a749b8e 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -95,6 +95,14 @@ class CommitRange alias_method :id, :to_s def to_reference(from_project = nil) + if cross_project_reference?(from_project) + reference = project.to_reference + self.class.reference_prefix + self.id + else + self.id + end + end + + def reference_link_text(from_project = nil) reference = ref_from + notation + ref_to if cross_project_reference?(from_project) diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb index 16e4d054869..ce064f675ae 100644 --- a/app/models/concerns/referable.rb +++ b/app/models/concerns/referable.rb @@ -21,6 +21,10 @@ module Referable '' end + def reference_link_text(from_project = nil) + to_reference(from_project) + end + module ClassMethods # The character that prefixes the actual reference identifier # diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index 02a9e05a699..f6df9518fc6 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -82,7 +82,7 @@ module Gitlab data = data_attribute(project: project.id, object_sym => object.id, original: match) url = matches[:url] || url_for_object(object, project) - text = object.to_reference(context[:project]) + text = object.reference_link_text(context[:project]) extras = object_link_text_extras(object, matches) text += " (#{extras.join(", ")})" if extras.any? diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index 4b4c769a110..f65a3e8a0bd 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -53,7 +53,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("See (#{reference}.)") - exp = Regexp.escape(range.to_reference) + exp = Regexp.escape(range.reference_link_text) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end @@ -125,7 +125,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape("#{project2.to_reference}@#{range.to_reference}") + exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}") expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end @@ -163,7 +163,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape(range.to_reference(project)) + exp = Regexp.escape(range.reference_link_text(project)) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index a8c9c7efd56..4cc6bbbfe94 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -148,7 +148,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - expect(doc.to_html).to match(/\(#{commit.to_reference(project)}<\/a>\.\)/) + expect(doc.to_html).to match(/\(#{commit.reference_link_text(project)}<\/a>\.\)/) end it 'ignores invalid commit IDs on the referenced project' do diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 58283f06972..3c1009a2eb0 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -38,15 +38,31 @@ describe CommitRange do let(:cross) { create(:project) } it 'returns a String reference to the object' do - expect(range.to_reference).to eq "#{sha_from}...#{sha_to}" + expect(range.to_reference).to eq "#{full_sha_from}...#{full_sha_to}" end it 'returns a String reference to the object' do - expect(range2.to_reference).to eq "#{sha_from}..#{sha_to}" + expect(range2.to_reference).to eq "#{full_sha_from}..#{full_sha_to}" end it 'supports a cross-project reference' do - expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}" + expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{full_sha_from}...#{full_sha_to}" + end + end + + describe '#reference_link_text' do + let(:cross) { create(:project) } + + it 'returns a String reference to the object' do + expect(range.reference_link_text).to eq "#{sha_from}...#{sha_to}" + end + + it 'returns a String reference to the object' do + expect(range2.reference_link_text).to eq "#{sha_from}..#{sha_to}" + end + + it 'supports a cross-project reference' do + expect(range.reference_link_text(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}" end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 1aaa927c216..974b52c1833 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -15,12 +15,23 @@ describe Commit do describe '#to_reference' do it 'returns a String reference to the object' do - expect(commit.to_reference).to eq commit.short_id + expect(commit.to_reference).to eq commit.id end it 'supports a cross-project reference' do cross = double('project') - expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.short_id}" + expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.id}" + end + end + + describe '#reference_link_text' do + it 'returns a String reference to the object' do + expect(commit.reference_link_text).to eq commit.short_id + end + + it 'supports a cross-project reference' do + cross = double('project') + expect(commit.reference_link_text(cross)).to eq "#{project.to_reference}@#{commit.short_id}" end end @@ -88,7 +99,7 @@ eos subject { create(:project).commit } let(:author) { create(:user, email: subject.author_email) } - let(:backref_text) { "commit #{subject.short_id}" } + let(:backref_text) { "commit #{subject.id}" } let(:set_mentionable_text) do ->(txt) { allow(subject).to receive(:safe_message).and_return(txt) } end -- cgit v1.2.1 From 55d0e04ffd3f178f41ae3ef13790eb6882b1ef8e Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 1 Dec 2015 14:26:57 +0100 Subject: Auto-detect the required gitlab-shell version --- doc/install/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 61647780607..618391e16d2 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -312,7 +312,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.8] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: -- cgit v1.2.1 From f3ea06eb7f8bef748be0882cb0b4fb58deed8eef Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 15:51:27 +0100 Subject: Autolink first so we don't pick up numeric anchors as issue references. --- app/models/commit.rb | 7 ++-- app/models/commit_range.rb | 7 ++-- app/models/issue.rb | 7 ++-- app/models/merge_request.rb | 7 ++-- app/models/snippet.rb | 7 ++-- lib/gitlab/closing_issue_extractor.rb | 2 +- lib/gitlab/markdown.rb | 5 ++- lib/gitlab/markdown/abstract_reference_filter.rb | 20 +++++++---- .../markdown/commit_range_reference_filter.rb | 4 +-- lib/gitlab/markdown/commit_reference_filter.rb | 4 +-- lib/gitlab/markdown/external_link_filter.rb | 7 ++-- .../markdown/merge_request_reference_filter.rb | 2 +- lib/gitlab/markdown/redactor_filter.rb | 2 +- lib/gitlab/markdown/reference_filter.rb | 23 +++++++++++++ lib/gitlab/reference_extractor.rb | 14 ++++++-- .../markdown/commit_range_reference_filter_spec.rb | 40 +++++++++++----------- .../markdown/commit_reference_filter_spec.rb | 36 +++++++++---------- .../gitlab/markdown/issue_reference_filter_spec.rb | 36 +++++++++---------- .../merge_request_reference_filter_spec.rb | 30 ++++++++-------- .../markdown/snippet_reference_filter_spec.rb | 32 ++++++++--------- spec/support/filter_spec_helper.rb | 19 ++++++++-- 21 files changed, 171 insertions(+), 140 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index a5d041a49c8..c0998a45709 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -73,11 +73,8 @@ class Commit # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (?:#{Project.reference_pattern}#{reference_prefix})? - (?\h{6,40}) - ) + (?:#{Project.reference_pattern}#{reference_prefix})? + (?\h{6,40}) }x end diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index f786a749b8e..b8bf36b32ce 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -44,11 +44,8 @@ class CommitRange # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (?:#{Project.reference_pattern}#{reference_prefix})? - (?#{STRICT_PATTERN}) - ) + (?:#{Project.reference_pattern}#{reference_prefix})? + (?#{STRICT_PATTERN}) }x end diff --git a/app/models/issue.rb b/app/models/issue.rb index e62acfdfd91..187b6482b6c 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -64,11 +64,8 @@ class Issue < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) - ) + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) }x end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c1d3874adee..2a4aee7e5d9 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -146,11 +146,8 @@ class MergeRequest < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) - ) + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) }x end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 8ec12ddf6ef..f876be7a4c8 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -60,11 +60,8 @@ class Snippet < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) - ) + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) }x end diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb index 70b9943d7eb..0cf4c918736 100644 --- a/lib/gitlab/closing_issue_extractor.rb +++ b/lib/gitlab/closing_issue_extractor.rb @@ -2,7 +2,7 @@ module Gitlab class ClosingIssueExtractor ISSUE_CLOSING_REGEX = begin pattern = Gitlab.config.gitlab.issue_closing_pattern - pattern = pattern.sub('%{issue_ref}', "(?:#{Issue.reference_pattern})") + pattern = pattern.sub('%{issue_ref}', "(?:(?:#{Issue.link_reference_pattern})|(?:#{Issue.reference_pattern}))") Regexp.new(pattern).freeze end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index ee458eda025..b082bfc434b 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -181,6 +181,8 @@ module Gitlab Gitlab::Markdown::RelativeLinkFilter, Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::TableOfContentsFilter, + Gitlab::Markdown::AutolinkFilter, + Gitlab::Markdown::ExternalLinkFilter, Gitlab::Markdown::UserReferenceFilter, Gitlab::Markdown::IssueReferenceFilter, @@ -191,9 +193,6 @@ module Gitlab Gitlab::Markdown::CommitReferenceFilter, Gitlab::Markdown::LabelReferenceFilter, - Gitlab::Markdown::AutolinkFilter, - Gitlab::Markdown::ExternalLinkFilter, - Gitlab::Markdown::TaskListFilter ] end diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index f6df9518fc6..37ed423eeda 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -37,8 +37,8 @@ module Gitlab # of the external project reference, and all of the matchdata. # # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(object_class.reference_pattern) do |match| + def self.references_in(text, pattern = object_class.reference_pattern) + text.gsub(pattern) do |match| yield match, $~[object_sym].to_i, $~[:project], $~ end end @@ -61,7 +61,11 @@ module Gitlab def call replace_text_nodes_matching(object_class.reference_pattern) do |content| - object_link_filter(content) + object_link_filter(content, object_class.reference_pattern) + end + + replace_link_nodes_matching(object_class.link_reference_pattern) do |content| + object_link_filter(content, object_class.link_reference_pattern) end end @@ -72,15 +76,17 @@ module Gitlab # # Returns a String with references replaced with links. All links # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. - def object_link_filter(text) - references_in(text) do |match, id, project_ref, matches| + def object_link_filter(text, pattern) + references_in(text, pattern) do |match, id, project_ref, matches| project = project_from_ref(project_ref) if project && object = find_object(project, id) title = escape_once(object_link_title(object)) klass = reference_class(object_sym) data = data_attribute(project: project.id, object_sym => object.id, original: match) - url = matches[:url] || url_for_object(object, project) + + url = matches[:url] if matches.names.include?("url") + url ||= url_for_object(object, project) text = object.reference_link_text(context[:project]) @@ -99,7 +105,7 @@ module Gitlab def object_link_text_extras(object, matches) extras = [] - if matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/ + if matches.names.include?("anchor") && matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/ extras << "comment #{$1}" end diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb index f24bed76193..36b3258ef76 100644 --- a/lib/gitlab/markdown/commit_range_reference_filter.rb +++ b/lib/gitlab/markdown/commit_range_reference_filter.rb @@ -10,8 +10,8 @@ module Gitlab CommitRange end - def self.references_in(text) - text.gsub(CommitRange.reference_pattern) do |match| + def self.references_in(text, pattern = CommitRange.reference_pattern) + text.gsub(pattern) do |match| yield match, $~[:commit_range], $~[:project], $~ end end diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb index cc7abc08c87..b4036578e60 100644 --- a/lib/gitlab/markdown/commit_reference_filter.rb +++ b/lib/gitlab/markdown/commit_reference_filter.rb @@ -10,8 +10,8 @@ module Gitlab Commit end - def self.references_in(text) - text.gsub(Commit.reference_pattern) do |match| + def self.references_in(text, pattern = Commit.reference_pattern) + text.gsub(pattern) do |match| yield match, $~[:commit], $~[:project], $~ end end diff --git a/lib/gitlab/markdown/external_link_filter.rb b/lib/gitlab/markdown/external_link_filter.rb index b6792932016..e09dfcb83c8 100644 --- a/lib/gitlab/markdown/external_link_filter.rb +++ b/lib/gitlab/markdown/external_link_filter.rb @@ -8,12 +8,9 @@ module Gitlab class ExternalLinkFilter < HTML::Pipeline::Filter def call doc.search('a').each do |node| - next unless node.has_attribute?('href') + link = node.attr('href') - klass = node.attribute('class') - next if klass && klass.include?('gfm') - - link = node.attribute('href').value + next unless link # Skip non-HTTP(S) links next unless link.start_with?('http') diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index 3780a14a130..de71fc76a9b 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -24,7 +24,7 @@ module Gitlab def object_link_text_extras(object, matches) extras = super - if matches[:path] && matches[:path] == '/diffs' + if matches.names.include?("path") && matches[:path] && matches[:path] == '/diffs' extras.unshift "diffs" end diff --git a/lib/gitlab/markdown/redactor_filter.rb b/lib/gitlab/markdown/redactor_filter.rb index 2a58c798f9f..bea714a01e7 100644 --- a/lib/gitlab/markdown/redactor_filter.rb +++ b/lib/gitlab/markdown/redactor_filter.rb @@ -14,7 +14,7 @@ module Gitlab unless user_can_reference?(node) # The reference should be replaced by the original text, # which is not always the same as the rendered text. - text = node.attribute('data-original') || node.text + text = node.attr('data-original') || node.text node.replace(text) end end diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index a4c560f578c..e52633ee74c 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -122,6 +122,29 @@ module Gitlab doc end + def replace_link_nodes_matching(pattern) + return doc if project.nil? + + doc.search('a').each do |node| + klass = node.attr('class') + next if klass && klass.include?('gfm') + + link = node.attr('href') + text = node.text + + # Ignore ending punctionation like periods or commas + next unless link == text && text =~ /\A#{pattern}/ + + html = yield text + + next if html == text + + node.replace(html) + end + + doc + end + # Ensure that a :project key exists in context # # Note that while the key might exist, its value could be nil! diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index da8df8a3025..3c3478a1271 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -41,14 +41,14 @@ module Gitlab # Returns the results Array for the requested filter type def pipeline_result(filter_type) return [] if @text.blank? - + klass = "#{filter_type.to_s.camelize}ReferenceFilter" filter = Gitlab::Markdown.const_get(klass) context = { project: project, current_user: current_user, - + # We don't actually care about the links generated only_path: true, ignore_blockquotes: true, @@ -58,7 +58,15 @@ module Gitlab reference_filter: filter } - pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context) + # We need to autolink first to finds links to referables, and to prevent + # numeric anchors to be parsed as issue references. + filters = [ + Gitlab::Markdown::AutolinkFilter, + filter, + Gitlab::Markdown::ReferenceGathererFilter + ] + + pipeline = HTML::Pipeline.new(filters, context) result = pipeline.call(@text) values = result[:references][filter_type].uniq diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index f65a3e8a0bd..92158382790 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -18,7 +18,7 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Commit Range #{range.to_reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -27,14 +27,14 @@ module Gitlab::Markdown let(:reference2) { range2.to_reference } it 'links to a valid two-dot reference' do - doc = filter("See #{reference2}") + doc = reference_filter("See #{reference2}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param) end it 'links to a valid three-dot reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param) @@ -46,12 +46,12 @@ module Gitlab::Markdown exp = commit1.short_id + '...' + commit2.short_id - expect(filter("See #{reference}").css('a').first.text).to eq exp - expect(filter("See #{reference2}").css('a').first.text).to eq exp + expect(reference_filter("See #{reference}").css('a').first.text).to eq exp + expect(reference_filter("See #{reference2}").css('a').first.text).to eq exp end it 'links with adjacent text' do - doc = filter("See (#{reference}.)") + doc = reference_filter("See (#{reference}.)") exp = Regexp.escape(range.reference_link_text) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) @@ -63,21 +63,21 @@ module Gitlab::Markdown expect(project).to receive(:valid_repo?).and_return(true) expect(project.repository).to receive(:commit).with(commit1.id.reverse) expect(project.repository).to receive(:commit).with(commit2.id) - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('title')).to eq range.reference_title end it 'includes default classes' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range' end it 'includes a data-project attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-project') @@ -85,7 +85,7 @@ module Gitlab::Markdown end it 'includes a data-commit-range attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-commit-range') @@ -93,7 +93,7 @@ module Gitlab::Markdown end it 'supports an :only_path option' do - doc = filter("See #{reference}", only_path: true) + doc = reference_filter("See #{reference}", only_path: true) link = doc.css('a').first.attr('href') expect(link).not_to match %r(https?://) @@ -116,14 +116,14 @@ module Gitlab::Markdown end it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}") expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) @@ -131,10 +131,10 @@ module Gitlab::Markdown it 'ignores invalid commit IDs on the referenced project' do exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -154,14 +154,14 @@ module Gitlab::Markdown end it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq reference end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") exp = Regexp.escape(range.reference_link_text(project)) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) @@ -169,10 +169,10 @@ module Gitlab::Markdown it 'ignores invalid commit IDs on the referenced project' do exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index 4cc6bbbfe94..6fe9b165ff5 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -14,7 +14,7 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Commit #{commit.id}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -24,7 +24,7 @@ module Gitlab::Markdown # Let's test a variety of commit SHA sizes just to be paranoid [6, 8, 12, 18, 20, 32, 40].each do |size| it "links to a valid reference of #{size} characters" do - doc = filter("See #{reference[0...size]}") + doc = reference_filter("See #{reference[0...size]}") expect(doc.css('a').first.text).to eq commit.short_id expect(doc.css('a').first.attr('href')). @@ -33,15 +33,15 @@ module Gitlab::Markdown end it 'always uses the short ID as the link text' do - doc = filter("See #{commit.id}") + doc = reference_filter("See #{commit.id}") expect(doc.text).to eq "See #{commit.short_id}" - doc = filter("See #{commit.id[0...6]}") + doc = reference_filter("See #{commit.id[0...6]}") expect(doc.text).to eq "See #{commit.short_id}" end it 'links with adjacent text' do - doc = filter("See (#{reference}.)") + doc = reference_filter("See (#{reference}.)") expect(doc.to_html).to match(/\(#{commit.short_id}<\/a>\.\)/) end @@ -51,28 +51,28 @@ module Gitlab::Markdown expect(project).to receive(:valid_repo?).and_return(true) expect(project.repository).to receive(:commit).with(invalid) - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('title')).to eq commit.link_title end it 'escapes the title attribute' do allow_any_instance_of(Commit).to receive(:title).and_return(%{">whatever#{exp}@#{commit.short_id}<\/a>\.\)/) @@ -123,7 +123,7 @@ module Gitlab::Markdown it 'ignores invalid commit IDs on the referenced project' do exp = act = "Committed #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -139,21 +139,21 @@ module Gitlab::Markdown let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id) end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") expect(doc.to_html).to match(/\(#{commit.reference_link_text(project)}<\/a>\.\)/) end it 'ignores invalid commit IDs on the referenced project' do exp = act = "Committed #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to match(/#{Regexp.escape(invalidate_reference(reference))}<\/a>/) end it 'adds to the results hash' do diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb index 296e8868f46..0a741688b82 100644 --- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb @@ -18,7 +18,7 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Issue #{issue.to_reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -29,18 +29,18 @@ module Gitlab::Markdown expect(project).to receive(:get_issue).with(issue.iid).and_return(nil) exp = act = "Issue #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'links to a valid reference' do - doc = filter("Fixed #{reference}") + doc = reference_filter("Fixed #{reference}") expect(doc.css('a').first.attr('href')). to eq helper.url_for_issue(issue.iid, project) end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) end @@ -48,28 +48,28 @@ module Gitlab::Markdown invalid = invalidate_reference(reference) exp = act = "Fixed #{invalid}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("Issue #{reference}") + doc = reference_filter("Issue #{reference}") expect(doc.css('a').first.attr('title')).to eq "Issue: #{issue.title}" end it 'escapes the title attribute' do issue.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid issue IDs on the referenced project' do exp = act = "Fixed #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -147,18 +147,18 @@ module Gitlab::Markdown with(issue.iid).and_return(nil) exp = act = "Issue #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to match(/#{Regexp.escape(reference)}<\/a>/) end it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq reference end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index ca3e7151e02..cdb3390e793 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -14,7 +14,7 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Merge #{merge.to_reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -22,42 +22,42 @@ module Gitlab::Markdown let(:reference) { merge.to_reference } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_merge_request_url(project.namespace, project, merge) end it 'links with adjacent text' do - doc = filter("Merge (#{reference}.)") + doc = reference_filter("Merge (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid merge IDs' do exp = act = "Merge #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("Merge #{reference}") + doc = reference_filter("Merge #{reference}") expect(doc.css('a').first.attr('title')).to eq "Merge Request: #{merge.title}" end it 'escapes the title attribute' do merge.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid merge IDs on the referenced project' do exp = act = "Merge #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -124,14 +124,14 @@ module Gitlab::Markdown let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, project2, merge) + '/diffs#note_123' } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq reference end it 'links with adjacent text' do - doc = filter("Merge (#{reference}.)") + doc = reference_filter("Merge (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index 3080a8a3608..73d20957a56 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -15,48 +15,48 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Snippet #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end context 'internal reference' do it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_snippet_url(project.namespace, project, snippet) end it 'links with adjacent text' do - doc = filter("Snippet (#{reference}.)") + doc = reference_filter("Snippet (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid snippet IDs' do exp = act = "Snippet #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("Snippet #{reference}") + doc = reference_filter("Snippet #{reference}") expect(doc.css('a').first.attr('title')).to eq "Snippet: #{snippet.title}" end it 'escapes the title attribute' do snippet.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid snippet IDs on the referenced project' do exp = act = "See #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -122,21 +122,21 @@ module Gitlab::Markdown let(:reference) { urls.namespace_project_snippet_url(project2.namespace, project2, snippet) } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet) end it 'links with adjacent text' do - doc = filter("See (#{reference}.)") + doc = reference_filter("See (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(snippet.to_reference(project))}<\/a>\.\)/) end it 'ignores invalid snippet IDs on the referenced project' do exp = act = "See #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to match(/#{Regexp.escape(invalidate_reference(reference))}<\/a>/) end it 'adds to the results hash' do diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb index 97e5c270a59..91e3bee13c1 100644 --- a/spec/support/filter_spec_helper.rb +++ b/spec/support/filter_spec_helper.rb @@ -35,11 +35,24 @@ module FilterSpecHelper pipeline.call(body) end - def reference_pipeline_result(body, contexts = {}) + def reference_pipeline(contexts = {}) contexts.reverse_merge!(project: project) if defined?(project) - pipeline = HTML::Pipeline.new([described_class, Gitlab::Markdown::ReferenceGathererFilter], contexts) - pipeline.call(body) + filters = [ + Gitlab::Markdown::AutolinkFilter, + described_class, + Gitlab::Markdown::ReferenceGathererFilter + ] + + HTML::Pipeline.new(filters, contexts) + end + + def reference_pipeline_result(body, contexts = {}) + reference_pipeline(contexts).call(body) + end + + def reference_filter(html, contexts = {}) + reference_pipeline(contexts).to_document(html) end # Modify a String reference to make it invalid -- cgit v1.2.1 From 6dad2bc6e67f47149e8981730cf3f08938794ceb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 16:15:53 +0100 Subject: Fix referenced_mentionables method. --- app/models/concerns/mentionable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 0d6cd86aade..634a8d0f274 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -67,7 +67,7 @@ module Mentionable # We're using this method instead of Array diffing because that requires # both of the object's `hash` values to be the same, which may not be the # case for otherwise identical Commit objects. - refs.reject! { |ref| ref == local_reference } + refs.reject { |ref| ref == local_reference } end # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. -- cgit v1.2.1 From b0ef603f71e3a55981f15944f16595470091c344 Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Tue, 1 Dec 2015 16:21:53 +0100 Subject: up for grabs label --- CONTRIBUTING.md | 10 ++++++++++ PROCESS.md | 1 + 2 files changed, 11 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 446eb1189ad..7f5da063fd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,16 @@ The channels people will reach out on can be found on the [getting help page](ht Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the IRC channel. You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day. +## I want to contribute! + +If you want to contribute to GitLab, but are not sure where to start, +look for [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=up-for-grabs) +with the label `up-for-grabs`. +These issues will be of reasonable size and challenge, for anyone to start +contributing to GitLab. + +This was inspired by [an article by Kent C. Dodds](https://medium.com/@kentcdodds/first-timers-only-78281ea47455#.i2f363mx4). + ## Issue tracker To get support for your particular problem please use the [getting help channels](https://about.gitlab.com/getting-help/). diff --git a/PROCESS.md b/PROCESS.md index 482ad5fe9e1..72fc3481447 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -44,6 +44,7 @@ Workflow labels are purposely not very detailed since that would be hard to keep - *UX* needs needs help from a UX designer - *Frontend* needs help from a Front-end engineer - *Graphics* needs help from a Graphics designer +- *up-for-grabs* is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels. Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label. -- cgit v1.2.1 From 1d6d757dbd563500671f57f45faa808510a612d1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 16:25:56 +0100 Subject: Allow reference format as link href --- lib/gitlab/markdown.rb | 3 ++- lib/gitlab/markdown/abstract_reference_filter.rb | 22 ++++++++++------ .../markdown/external_issue_reference_filter.rb | 10 ++++++-- lib/gitlab/markdown/label_reference_filter.rb | 10 ++++++-- lib/gitlab/markdown/reference_filter.rb | 29 +++++++++++++++++++++- lib/gitlab/markdown/relative_link_filter.rb | 3 +++ lib/gitlab/markdown/user_reference_filter.rb | 28 ++++++++++++--------- 7 files changed, 80 insertions(+), 25 deletions(-) diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index b082bfc434b..886a09f52af 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -178,7 +178,6 @@ module Gitlab Gitlab::Markdown::SanitizationFilter, Gitlab::Markdown::UploadLinkFilter, - Gitlab::Markdown::RelativeLinkFilter, Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::TableOfContentsFilter, Gitlab::Markdown::AutolinkFilter, @@ -193,6 +192,8 @@ module Gitlab Gitlab::Markdown::CommitReferenceFilter, Gitlab::Markdown::LabelReferenceFilter, + Gitlab::Markdown::RelativeLinkFilter, + Gitlab::Markdown::TaskListFilter ] end diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index 37ed423eeda..b044a73ed17 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -2,7 +2,7 @@ require 'gitlab/markdown' module Gitlab module Markdown - # Issues, Snippets, Merge Requests, Commits and Commit Ranges share + # Issues, Merge Requests, Snippets, Commits and Commit Ranges share # similar functionality in refernce filtering. class AbstractReferenceFilter < ReferenceFilter include CrossProjectReference @@ -64,8 +64,13 @@ module Gitlab object_link_filter(content, object_class.reference_pattern) end - replace_link_nodes_matching(object_class.link_reference_pattern) do |content| - object_link_filter(content, object_class.link_reference_pattern) + replace_link_nodes_with_href(object_class.reference_pattern) do |link, text| + object_link_filter(link, object_class.reference_pattern, link_text: text) + end + + replace_link_nodes_with_text(object_class.link_reference_pattern) do |text| + object_link_filter(text, object_class.link_reference_pattern) + end end end @@ -76,7 +81,7 @@ module Gitlab # # Returns a String with references replaced with links. All links # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. - def object_link_filter(text, pattern) + def object_link_filter(text, pattern, link_text: nil) references_in(text, pattern) do |match, id, project_ref, matches| project = project_from_ref(project_ref) @@ -88,10 +93,13 @@ module Gitlab url = matches[:url] if matches.names.include?("url") url ||= url_for_object(object, project) - text = object.reference_link_text(context[:project]) + text = link_text + unless text + text = object.reference_link_text(context[:project]) - extras = object_link_text_extras(object, matches) - text += " (#{extras.join(", ")})" if extras.any? + extras = object_link_text_extras(object, matches) + text += " (#{extras.join(", ")})" if extras.any? + end %(#{match}) + class="#{klass}">#{text}) end end diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index 618acb7a578..4d0507b607d 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -30,6 +30,10 @@ module Gitlab replace_text_nodes_matching(Label.reference_pattern) do |content| label_link_filter(content) end + + replace_link_nodes_with_href(Label.reference_pattern) do |link, text| + label_link_filter(link, link_text: text) + end end # Replace label references in text with links to the label specified. @@ -38,7 +42,7 @@ module Gitlab # # Returns a String with label references replaced with links. All links # have `gfm` and `gfm-label` class names attached for styling. - def label_link_filter(text) + def label_link_filter(text, link_text: nil) project = context[:project] self.class.references_in(text) do |match, id, name| @@ -49,8 +53,10 @@ module Gitlab klass = reference_class(:label) data = data_attribute(project: project.id, label: label.id) + text = link_text || render_colored_label(label) + %(#{render_colored_label(label)}) + class="#{klass}">#{text}) else match end diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index e52633ee74c..2597784c7ae 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -122,7 +122,7 @@ module Gitlab doc end - def replace_link_nodes_matching(pattern) + def replace_link_nodes_with_text(pattern) return doc if project.nil? doc.search('a').each do |node| @@ -132,6 +132,9 @@ module Gitlab link = node.attr('href') text = node.text + next unless link && text + + link = URI.decode(link) # Ignore ending punctionation like periods or commas next unless link == text && text =~ /\A#{pattern}/ @@ -145,6 +148,30 @@ module Gitlab doc end + def replace_link_nodes_with_href(pattern) + return doc if project.nil? + + doc.search('a').each do |node| + klass = node.attr('class') + next if klass && klass.include?('gfm') + + link = node.attr('href') + text = node.text + + next unless link && text + link = URI.decode(link) + next unless link && link =~ /\A#{pattern}\z/ + + html = yield link, text + + next if html == link + + node.replace(html) + end + + doc + end + # Ensure that a :project key exists in context # # Note that while the key might exist, its value could be nil! diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb index 632be4d7542..692c51fd324 100644 --- a/lib/gitlab/markdown/relative_link_filter.rb +++ b/lib/gitlab/markdown/relative_link_filter.rb @@ -17,6 +17,9 @@ module Gitlab return doc unless linkable_files? doc.search('a').each do |el| + klass = el.attr('class') + next if klass && klass.include?('gfm') + process_link_attr el.attribute('href') end diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb index ab5e1f6fe9e..0a20d9c0347 100644 --- a/lib/gitlab/markdown/user_reference_filter.rb +++ b/lib/gitlab/markdown/user_reference_filter.rb @@ -52,6 +52,10 @@ module Gitlab replace_text_nodes_matching(User.reference_pattern) do |content| user_link_filter(content) end + + replace_link_nodes_with_href(User.reference_pattern) do |link, text| + user_link_filter(link, link_text: text) + end end # Replace `@user` user references in text with links to the referenced @@ -61,12 +65,12 @@ module Gitlab # # Returns a String with `@user` references replaced with links. All links # have `gfm` and `gfm-project_member` class names attached for styling. - def user_link_filter(text) + def user_link_filter(text, link_text: nil) self.class.references_in(text) do |match, username| if username == 'all' - link_to_all + link_to_all(link_text: link_text) elsif namespace = Namespace.find_by(path: username) - link_to_namespace(namespace) || match + link_to_namespace(namespace, link_text: link_text) || match else match end @@ -83,36 +87,36 @@ module Gitlab reference_class(:project_member) end - def link_to_all + def link_to_all(link_text: nil) project = context[:project] url = urls.namespace_project_url(project.namespace, project, only_path: context[:only_path]) data = data_attribute(project: project.id) - text = User.reference_prefix + 'all' + text = link_text || User.reference_prefix + 'all' link_tag(url, data, text) end - def link_to_namespace(namespace) + def link_to_namespace(namespace, link_text: nil) if namespace.is_a?(Group) - link_to_group(namespace.path, namespace) + link_to_group(namespace.path, namespace, link_text: link_text) else - link_to_user(namespace.path, namespace) + link_to_user(namespace.path, namespace, link_text: link_text) end end - def link_to_group(group, namespace) + def link_to_group(group, namespace, link_text: nil) url = urls.group_url(group, only_path: context[:only_path]) data = data_attribute(group: namespace.id) - text = Group.reference_prefix + group + text = link_text || Group.reference_prefix + group link_tag(url, data, text) end - def link_to_user(user, namespace) + def link_to_user(user, namespace, link_text: nil) url = urls.user_url(user, only_path: context[:only_path]) data = data_attribute(user: namespace.owner_id) - text = User.reference_prefix + user + text = link_text || User.reference_prefix + user link_tag(url, data, text) end -- cgit v1.2.1 From d4030a845eebcb913a7aac1e8fd502706669d0cc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 16:26:05 +0100 Subject: Pick up direct links to issues/MRs as references. --- lib/gitlab/markdown/abstract_reference_filter.rb | 17 +++++- .../markdown/commit_range_reference_filter_spec.rb | 2 +- .../markdown/commit_reference_filter_spec.rb | 2 +- .../gitlab/markdown/issue_reference_filter_spec.rb | 56 ++++++++++++++--- .../gitlab/markdown/label_reference_filter_spec.rb | 71 ++++++++++++++++------ .../merge_request_reference_filter_spec.rb | 2 +- .../markdown/snippet_reference_filter_spec.rb | 2 +- .../gitlab/markdown/user_reference_filter_spec.rb | 51 ++++++++++++---- 8 files changed, 161 insertions(+), 42 deletions(-) diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index b044a73ed17..0ec55c0207e 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -60,17 +60,27 @@ module Gitlab end def call + # `#123` replace_text_nodes_matching(object_class.reference_pattern) do |content| object_link_filter(content, object_class.reference_pattern) end + # `[Issue](#123)`, which is turned into + # `Issue` replace_link_nodes_with_href(object_class.reference_pattern) do |link, text| object_link_filter(link, object_class.reference_pattern, link_text: text) end + # `http://gitlab.example.com/namespace/project/issues/123`, which is turned into + # `http://gitlab.example.com/namespace/project/issues/123` replace_link_nodes_with_text(object_class.link_reference_pattern) do |text| object_link_filter(text, object_class.link_reference_pattern) end + + # `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into + # `Issue` + replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text| + object_link_filter(link, object_class.link_reference_pattern, link_text: text) end end @@ -88,7 +98,12 @@ module Gitlab if project && object = find_object(project, id) title = escape_once(object_link_title(object)) klass = reference_class(object_sym) - data = data_attribute(project: project.id, object_sym => object.id, original: match) + + data = data_attribute( + original: link_text || match, + project: project.id, + object_sym => object.id + ) url = matches[:url] if matches.names.include?("url") url ||= url_for_object(object, project) diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index 92158382790..9ce63f9af46 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -143,7 +143,7 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } let(:range) { CommitRange.new("#{commit1.id}...master", project) } diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index 6fe9b165ff5..78a3603269c 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -132,7 +132,7 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } let(:commit) { project2.commit } diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb index 0a741688b82..078ff3ed4b2 100644 --- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb @@ -136,30 +136,70 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:issue) { create(:issue, project: project2) } let(:reference) { helper.url_for_issue(issue.iid, project2) + "#note_123" } - it 'ignores valid references when cross-reference project uses external tracker' do - expect_any_instance_of(Project).to receive(:get_issue). - with(issue.iid).and_return(nil) + it 'links to a valid reference' do + doc = reference_filter("See #{reference}") - exp = act = "Issue #{reference}" - expect(reference_filter(act).to_html).to match(/#{Regexp.escape(reference)}<\/a>/) + expect(doc.css('a').first.attr('href')). + to eq reference + end + + it 'links with adjacent text' do + doc = reference_filter("Fixed (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Fixed #{reference}") + expect(result[:references][:issue]).to eq [issue] + end + end + + context 'cross-project reference in link href' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:empty_project, :public, namespace: namespace) } + let(:issue) { create(:issue, project: project2) } + let(:reference) { %Q{Reference} } + + it 'links to a valid reference' do + doc = reference_filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq helper.url_for_issue(issue.iid, project2) + end + + it 'links with adjacent text' do + doc = reference_filter("Fixed (#{reference}.)") + expect(doc.to_html).to match(/\(Reference<\/a>\.\)/) end + it 'adds to the results hash' do + result = reference_pipeline_result("Fixed #{reference}") + expect(result[:references][:issue]).to eq [issue] + end + end + + context 'cross-project URL in link href' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:empty_project, :public, namespace: namespace) } + let(:issue) { create(:issue, project: project2) } + let(:reference) { %Q{Reference} } + it 'links to a valid reference' do doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). - to eq reference + to eq helper.url_for_issue(issue.iid, project2) + "#note_123" end it 'links with adjacent text' do doc = reference_filter("Fixed (#{reference}.)") - expect(doc.to_html).to match(/\(#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) + expect(doc.to_html).to match(/\(Reference<\/a>\.\)/) end it 'adds to the results hash' do diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index fc21b65a843..ef6dd524aba 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -16,17 +16,17 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Label #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end it 'includes default classes' do - doc = filter("Label #{reference}") + doc = reference_filter("Label #{reference}") expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label' end it 'includes a data-project attribute' do - doc = filter("Label #{reference}") + doc = reference_filter("Label #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-project') @@ -34,7 +34,7 @@ module Gitlab::Markdown end it 'includes a data-label attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-label') @@ -42,7 +42,7 @@ module Gitlab::Markdown end it 'supports an :only_path context' do - doc = filter("Label #{reference}", only_path: true) + doc = reference_filter("Label #{reference}", only_path: true) link = doc.css('a').first.attr('href') expect(link).not_to match %r(https?://) @@ -56,33 +56,33 @@ module Gitlab::Markdown describe 'label span element' do it 'includes default classes' do - doc = filter("Label #{reference}") + doc = reference_filter("Label #{reference}") expect(doc.css('a span').first.attr('class')).to eq 'label color-label' end it 'includes a style attribute' do - doc = filter("Label #{reference}") + doc = reference_filter("Label #{reference}") expect(doc.css('a span').first.attr('style')).to match(/\Abackground-color: #\h{6}; color: #\h{6}\z/) end end context 'Integer-based references' do it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_issues_url(project.namespace, project, label_name: label.name) end it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") + doc = reference_filter("Label (#{reference}.)") expect(doc.to_html).to match(%r(\(#{label.name}\.\))) end it 'ignores invalid label IDs' do exp = act = "Label #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -91,7 +91,7 @@ module Gitlab::Markdown let(:reference) { "#{Label.reference_prefix}#{label.name}" } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_issues_url(project.namespace, project, label_name: label.name) @@ -99,14 +99,14 @@ module Gitlab::Markdown end it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") + doc = reference_filter("Label (#{reference}.)") expect(doc.to_html).to match(%r(\(#{label.name}\.\))) end it 'ignores invalid label names' do exp = act = "Label #{Label.reference_prefix}#{label.name.reverse}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -115,7 +115,7 @@ module Gitlab::Markdown let(:reference) { label.to_reference(:name) } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_issues_url(project.namespace, project, label_name: label.name) @@ -123,21 +123,58 @@ module Gitlab::Markdown end it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") + doc = reference_filter("Label (#{reference}.)") expect(doc.to_html).to match(%r(\(#{label.name}\.\))) end it 'ignores invalid label names' do exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}") - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end describe 'edge cases' do it 'gracefully handles non-references matching the pattern' do exp = act = '(format nil "~0f" 3.0) ; 3.0' - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp + end + end + + describe 'referencing a label in a link href' do + let(:reference) { %Q{Label} } + + it 'links to a valid reference' do + doc = reference_filter("See #{reference}") + + expect(doc.css('a').first.attr('href')).to eq urls. + namespace_project_issues_url(project.namespace, project, label_name: label.name) + end + + it 'links with adjacent text' do + doc = reference_filter("Label (#{reference}.)") + expect(doc.to_html).to match(%r(\(Label\.\))) + end + + it 'includes a data-project attribute' do + doc = reference_filter("Label #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-label attribute' do + doc = reference_filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-label') + expect(link.attr('data-label')).to eq label.id.to_s + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Label #{reference}") + expect(result[:references][:label]).to eq [label] end end end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index cdb3390e793..4a232051127 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -117,7 +117,7 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index 73d20957a56..b6f05710c3b 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -115,7 +115,7 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:snippet) { create(:project_snippet, project: project2) } diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb index d9e0d7c42db..25379f0670e 100644 --- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb @@ -14,13 +14,13 @@ module Gitlab::Markdown it 'ignores invalid users' do exp = act = "Hey #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq(exp) + expect(reference_filter(act).to_html).to eq(exp) end %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Hey #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -32,7 +32,7 @@ module Gitlab::Markdown end it 'supports a special @all mention' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") expect(doc.css('a').length).to eq 1 expect(doc.css('a').first.attr('href')) .to eq urls.namespace_project_url(project.namespace, project) @@ -46,26 +46,26 @@ module Gitlab::Markdown context 'mentioning a user' do it 'links to a User' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") expect(doc.css('a').first.attr('href')).to eq urls.user_url(user) end it 'links to a User with a period' do user = create(:user, name: 'alphA.Beta') - doc = filter("Hey #{user.to_reference}") + doc = reference_filter("Hey #{user.to_reference}") expect(doc.css('a').length).to eq 1 end it 'links to a User with an underscore' do user = create(:user, name: 'ping_pong_king') - doc = filter("Hey #{user.to_reference}") + doc = reference_filter("Hey #{user.to_reference}") expect(doc.css('a').length).to eq 1 end it 'includes a data-user attribute' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-user') @@ -83,12 +83,12 @@ module Gitlab::Markdown let(:reference) { group.to_reference } it 'links to the Group' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") expect(doc.css('a').first.attr('href')).to eq urls.group_url(group) end it 'includes a data-group attribute' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-group') @@ -102,21 +102,48 @@ module Gitlab::Markdown end it 'links with adjacent text' do - doc = filter("Mention me (#{reference}.)") + doc = reference_filter("Mention me (#{reference}.)") expect(doc.to_html).to match(/\(#{reference}<\/a>\.\)/) end it 'includes default classes' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member' end it 'supports an :only_path context' do - doc = filter("Hey #{reference}", only_path: true) + doc = reference_filter("Hey #{reference}", only_path: true) link = doc.css('a').first.attr('href') expect(link).not_to match %r(https?://) expect(link).to eq urls.user_path(user) end + + context 'referencing a user in a link href' do + let(:reference) { %Q{User} } + + it 'links to a User' do + doc = reference_filter("Hey #{reference}") + expect(doc.css('a').first.attr('href')).to eq urls.user_url(user) + end + + it 'links with adjacent text' do + doc = reference_filter("Mention me (#{reference}.)") + expect(doc.to_html).to match(/\(User<\/a>\.\)/) + end + + it 'includes a data-user attribute' do + doc = reference_filter("Hey #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-user') + expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Hey #{reference}") + expect(result[:references][:user]).to eq [user] + end + end end end -- cgit v1.2.1 From c07f0fa735ba0d4cd926e601ebb3a40cfa197e21 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 17:04:32 +0100 Subject: Add new references to markdown feature spec. --- spec/fixtures/markdown.md.erb | 17 +++++++++++++++++ spec/support/matchers/markdown_matchers.rb | 14 +++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 41d12afa9ce..76e733165ca 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -153,6 +153,7 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Ignores invalid: <%= User.reference_prefix %>fake_user - Ignored in code: `<%= user.to_reference %>` - Ignored in links: [Link to <%= user.to_reference %>](#user-link) +- Link to user by reference: [User](<%= user.to_reference %>) #### IssueReferenceFilter @@ -160,6 +161,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Issue in another project: <%= xissue.to_reference(project) %> - Ignored in code: `<%= issue.to_reference %>` - Ignored in links: [Link to <%= issue.to_reference %>](#issue-link) +- Issue by URL: <%= Gitlab.config.gitlab.url %>/<%= issue.project.to_reference %>/issues/<%= issue.iid %> +- Link to issue by reference: [Issue](<%= issue.to_reference %>) +- Link to issue by URL: [Issue](<%= Gitlab.config.gitlab.url %>/<%= issue.project.to_reference %>/issues/<%= issue.iid %>) #### MergeRequestReferenceFilter @@ -167,6 +171,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Merge request in another project: <%= xmerge_request.to_reference(project) %> - Ignored in code: `<%= merge_request.to_reference %>` - Ignored in links: [Link to <%= merge_request.to_reference %>](#merge-request-link) +- Merge request by URL: <%= Gitlab.config.gitlab.url %>/<%= merge_request.project.to_reference %>/merge_requests/<%= merge_request.iid %> +- Link to merge request by reference: [Merge request](<%= merge_request.to_reference %>) +- Link to merge request by URL: [Merge request](<%= Gitlab.config.gitlab.url %>/<%= merge_request.project.to_reference %>/merge_requests/<%= merge_request.iid %>) #### SnippetReferenceFilter @@ -174,6 +181,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Snippet in another project: <%= xsnippet.to_reference(project) %> - Ignored in code: `<%= snippet.to_reference %>` - Ignored in links: [Link to <%= snippet.to_reference %>](#snippet-link) +- Snippet by URL: <%= Gitlab.config.gitlab.url %>/<%= snippet.project.to_reference %>/snippets/<%= snippet.id %> +- Link to snippet by reference: [Snippet](<%= snippet.to_reference %>) +- Link to snippet by URL: [Snippet](<%= Gitlab.config.gitlab.url %>/<%= snippet.project.to_reference %>/snippets/<%= snippet.id %>) #### CommitRangeReferenceFilter @@ -181,6 +191,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Range in another project: <%= xcommit_range.to_reference(project) %> - Ignored in code: `<%= commit_range.to_reference %>` - Ignored in links: [Link to <%= commit_range.to_reference %>](#commit-range-link) +- Range by URL: <%= Gitlab.config.gitlab.url %>/<%= commit_range.project.to_reference %>/compare/<%= commit_range.id %> +- Link to range by reference: [Range](<%= commit_range.to_reference %>) +- Link to range by URL: [Range](<%= Gitlab.config.gitlab.url %>/<%= commit_range.project.to_reference %>/compare/<%= commit_range.id %>) #### CommitReferenceFilter @@ -188,6 +201,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Commit in another project: <%= xcommit.to_reference(project) %> - Ignored in code: `<%= commit.to_reference %>` - Ignored in links: [Link to <%= commit.to_reference %>](#commit-link) +- Commit by URL: <%= Gitlab.config.gitlab.url %>/<%= commit.project.to_reference %>/commit/<%= commit.id %> +- Link to commit by reference: [Commit](<%= commit.to_reference %>) +- Link to commit by URL: [Commit](<%= Gitlab.config.gitlab.url %>/<%= commit.project.to_reference %>/commit/<%= commit.id %>) #### LabelReferenceFilter @@ -196,6 +212,7 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Label by name in quotes: <%= label.to_reference(:name) %> - Ignored in code: `<%= simple_label.to_reference %>` - Ignored in links: [Link to <%= simple_label.to_reference %>](#label-link) +- Link to label by reference: [Label](<%= label.to_reference %>) ### Task Lists diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index 7500d0fdf80..7eadcd58c1f 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -71,7 +71,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-project_member', count: 3) + expect(actual).to have_selector('a.gfm.gfm-project_member', count: 4) end end @@ -80,7 +80,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-issue', count: 3) + expect(actual).to have_selector('a.gfm.gfm-issue', count: 6) end end @@ -89,7 +89,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-merge_request', count: 3) + expect(actual).to have_selector('a.gfm.gfm-merge_request', count: 6) expect(actual).to have_selector('em a.gfm-merge_request') end end @@ -99,7 +99,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-snippet', count: 2) + expect(actual).to have_selector('a.gfm.gfm-snippet', count: 5) end end @@ -108,7 +108,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-commit_range', count: 2) + expect(actual).to have_selector('a.gfm.gfm-commit_range', count: 5) end end @@ -117,7 +117,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-commit', count: 2) + expect(actual).to have_selector('a.gfm.gfm-commit', count: 5) end end @@ -126,7 +126,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-label', count: 3) + expect(actual).to have_selector('a.gfm.gfm-label', count: 4) end end -- cgit v1.2.1 From 2f5074dc395c784f91abb3bacd23e75ce080a547 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 17:13:47 +0100 Subject: Expand inline docs. --- lib/gitlab/markdown/abstract_reference_filter.rb | 6 ++++-- lib/gitlab/markdown/reference_filter.rb | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index 0ec55c0207e..9488e980c08 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -3,7 +3,7 @@ require 'gitlab/markdown' module Gitlab module Markdown # Issues, Merge Requests, Snippets, Commits and Commit Ranges share - # similar functionality in refernce filtering. + # similar functionality in reference filtering. class AbstractReferenceFilter < ReferenceFilter include CrossProjectReference @@ -88,6 +88,8 @@ module Gitlab # to the referenced object's details page. # # text - String text to replace references in. + # pattern - Reference pattern to match against. + # link_text - Original content of the link being replaced. # # Returns a String with references replaced with links. All links # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. @@ -98,7 +100,7 @@ module Gitlab if project && object = find_object(project, id) title = escape_once(object_link_title(object)) klass = reference_class(object_sym) - + data = data_attribute( original: link_text || match, project: project.id, diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index 2597784c7ae..b6d93e05ec7 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -122,6 +122,18 @@ module Gitlab doc end + # Iterate through the document's link nodes, yielding the current node's + # content if: + # + # * The `project` context value is present AND + # * The node's content matches `pattern` + # + # pattern - Regex pattern against which to match the node's content + # + # Yields the current node's String contents. The result of the block will + # replace the node and update the current document. + # + # Returns the updated Nokogiri::HTML::DocumentFragment object. def replace_link_nodes_with_text(pattern) return doc if project.nil? @@ -148,6 +160,18 @@ module Gitlab doc end + # Iterate through the document's link nodes, yielding the current node's + # content if: + # + # * The `project` context value is present AND + # * The node's HREF matches `pattern` + # + # pattern - Regex pattern against which to match the node's HREF + # + # Yields the current node's String HREF and String content. + # The result of the block will replace the node and update the current document. + # + # Returns the updated Nokogiri::HTML::DocumentFragment object. def replace_link_nodes_with_href(pattern) return doc if project.nil? -- cgit v1.2.1 From 9f30f5c63515709adce95eef166e96ea91811e36 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 1 Dec 2015 18:19:33 -0500 Subject: Make icon-link image transparent --- app/assets/images/icon-link.png | Bin 726 -> 1128 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/app/assets/images/icon-link.png b/app/assets/images/icon-link.png index 60021d5ac47..7d89da97e11 100644 Binary files a/app/assets/images/icon-link.png and b/app/assets/images/icon-link.png differ -- cgit v1.2.1 From e6dadea3891358ac7ed34dbb31eac403c193fcdc Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 2 Dec 2015 08:50:00 +0200 Subject: git rid of deprecated warnings --- app/models/user.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index e1144ca77be..15aab315649 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -794,4 +794,9 @@ class User < ActiveRecord::Base Gitlab::SQL::Union.new([personal_projects.select(:id), groups.select(:id), other.select(:id)]) end + + # Added according to https://github.com/plataformatec/devise/blob/7df57d5081f9884849ca15e4fde179ef164a575f/README.md#activejob-integration + def send_devise_notification(notification, *args) + devise_mailer.send(notification, self, *args).deliver_later + end end -- cgit v1.2.1 From 9aac53bcee8f5fc99ec84036d688801d987959ea Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 10:54:24 +0100 Subject: Satisfy Rubocop --- app/models/commit_range.rb | 2 +- spec/lib/gitlab/markdown/commit_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index b8bf36b32ce..14e7971fa06 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -93,7 +93,7 @@ class CommitRange def to_reference(from_project = nil) if cross_project_reference?(from_project) - reference = project.to_reference + self.class.reference_prefix + self.id + project.to_reference + self.class.reference_prefix + self.id else self.id end diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index 78a3603269c..462a41b4756 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -152,7 +152,7 @@ module Gitlab::Markdown end it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Committed #{invalidate_reference(reference)}" + act = "Committed #{invalidate_reference(reference)}" expect(reference_filter(act).to_html).to match(/#{Regexp.escape(invalidate_reference(reference))}<\/a>/) end diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index b6f05710c3b..3a9acc9d6d4 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -134,7 +134,7 @@ module Gitlab::Markdown end it 'ignores invalid snippet IDs on the referenced project' do - exp = act = "See #{invalidate_reference(reference)}" + act = "See #{invalidate_reference(reference)}" expect(reference_filter(act).to_html).to match(/#{Regexp.escape(invalidate_reference(reference))}<\/a>/) end -- cgit v1.2.1 From a8e463c8aca571ede3691c98f7f3990d3d880d0b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 10:56:05 +0100 Subject: Don't show project fork event as imported --- CHANGELOG | 1 + app/models/event.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index db812796b69..8fbe1e6ff14 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.3.0 (unreleased) - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) - Add ignore whitespace change option to commit view - Fire update hook from GitLab + - Don't show project fork event as "imported" v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/app/models/event.rb b/app/models/event.rb index 9afd223bce5..01d008035a5 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -201,7 +201,7 @@ class Event < ActiveRecord::Base elsif commented? "commented on" elsif created_project? - if project.import? + if project.external_import? "imported" else "created" -- cgit v1.2.1 From edc37c25204d7a4446d15eac94e6e1d92d613ed9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 10:56:39 +0100 Subject: Add changelog item --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 090b54f41a4..01e7e8d20bd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Fix 500 error when update group member permission - Fix: Raw private snippets access workflow - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) + - Recognize issue/MR/snippet/commit links as references v 8.2.1 - Forcefully update builds that didn't want to update with state machine -- cgit v1.2.1 From 927a4576c6e0bff2c9a41e538efcd2c7691a6a74 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 2 Dec 2015 13:26:49 +0100 Subject: The Procfile is for development only --- Procfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Procfile b/Procfile index fd5f7ecb94b..2e41485677c 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,7 @@ +# For DEVELOPMENT only. Production uses Runit in +# https://gitlab.com/gitlab-org/omnibus-gitlab or the init scripts in +# lib/support/init.d, which call scripts in bin/ . +# web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q mailers -q default # mail_room: bundle exec mail_room -q -c config/mail_room.yml -- cgit v1.2.1 From 4d67a2909f46d807c3586a74938d6f7619429808 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 13:59:15 +0100 Subject: Use pointer cursor in award emoji selector --- app/assets/stylesheets/pages/issuable.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 3a08ee70bc7..848ad7dac84 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -157,6 +157,7 @@ min-width: 214px; > li { + cursor: pointer; margin: 5px; } } -- cgit v1.2.1 From b7cac5a8dae03a1c870f58d22f51e156e62b0965 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 13:59:21 +0100 Subject: Use full names in emoji tooltip --- app/helpers/issues_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 493f370d9a9..25befd654d4 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -96,7 +96,7 @@ module IssuesHelper def emoji_author_list(notes, current_user) list = notes.map do |note| - note.author == current_user ? "me" : note.author.username + note.author == current_user ? "me" : note.author.name end list.join(", ") -- cgit v1.2.1 From 275c2a3161ac4d5638b8fa39a7355bbd9fc3cf46 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 13:59:42 +0100 Subject: Use new style for wiki --- app/assets/stylesheets/pages/wiki.scss | 5 +++++ app/views/projects/wikis/_form.html.haml | 16 +++++++-------- app/views/projects/wikis/_main_links.html.haml | 11 ++++------- app/views/projects/wikis/_nav.html.haml | 25 ++++++++++++++++-------- app/views/projects/wikis/_new.html.haml | 4 ++-- app/views/projects/wikis/edit.html.haml | 24 +++++++++++------------ app/views/projects/wikis/git_access.html.haml | 2 +- app/views/projects/wikis/pages.html.haml | 7 +++---- app/views/projects/wikis/show.html.haml | 12 ++++-------- features/steps/project/source/markdown_render.rb | 2 +- features/steps/project/wiki.rb | 12 ++++++------ 11 files changed, 62 insertions(+), 58 deletions(-) diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index dfaeba41cf6..cdf514197cb 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -4,3 +4,8 @@ margin-right: auto; padding-right: 7px; } + +.wiki-last-edit-by { + font-size: 80%; + font-weight: normal; +} diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 9c94c43e747..1d257818dcd 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default' } do |f| -if @page.errors.any? #error_explanation .alert.alert-danger @@ -11,14 +11,7 @@ .col-sm-10 = f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: "form-control" - .row - .col-sm-offset-2.col-sm-10 - %p.cgray - To link to a (new) page you can just type - %code [Link Title](page-slug) - \. - - .form-group.wiki-content + .form-group = f.label :content, class: 'control-label' .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do @@ -27,6 +20,11 @@ .clearfix .error-alert + + .help-block + To link to a (new) page, simply type + %code [Link Title](page-slug) + \. .form-group = f.label :commit_message, class: 'control-label' .col-sm-10= f.text_field :message, class: 'form-control', rows: 18 diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml index 14f25822259..29bf5d62abe 100644 --- a/app/views/projects/wikis/_main_links.html.haml +++ b/app/views/projects/wikis/_main_links.html.haml @@ -1,9 +1,4 @@ %span.pull-right - - if can?(current_user, :create_wiki, @project) - = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new btn-grouped", "data-toggle" => "modal" do - %i.fa.fa-plus - New Page - - if (@page && @page.persisted?) = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn btn-grouped" do Page History @@ -11,5 +6,7 @@ = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn btn-grouped" do %i.fa.fa-pencil-square-o Edit - -= render 'projects/wikis/new' + - if can?(current_user, :admin_wiki, @project) + = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-remove" do + = icon('trash') + Delete diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml index fffb4eb31ab..e6e6ad5bc4b 100644 --- a/app/views/projects/wikis/_nav.html.haml +++ b/app/views/projects/wikis/_nav.html.haml @@ -1,10 +1,19 @@ -%ul.center-top-menu - = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do - = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home) +.project-issuable-filter + .controls + - if can?(current_user, :create_wiki, @project) + = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do + %i.fa.fa-plus + New Page - = nav_link(path: 'wikis#pages') do - = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project) + = render 'projects/wikis/new' - = nav_link(path: 'wikis#git_access') do - = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do - Git Access + %ul.center-top-menu + = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do + = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home) + + = nav_link(path: 'wikis#pages') do + = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project) + + = nav_link(path: 'wikis#git_access') do + = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do + Git Access diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index dace172438c..f0547e9c057 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -12,5 +12,5 @@ The page slug is invalid. Please don't use characters other then: a-z 0-9 _ - and / %p.hint Please don't use spaces. - .modal-footer - = link_to 'Build', '#', class: 'build-new-wiki btn btn-create' + .form-actions + = link_to 'Create Page', '#', class: 'build-new-wiki btn btn-create' diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index 0b709c3695b..23f64fbbd10 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -1,16 +1,16 @@ -- page_title "Edit", @page.title, "Wiki" +- page_title "Edit", @page.title.capitalize, "Wiki" = render "header_title" = render 'nav' -.pull-right - = render 'main_links' -%h3.page-title - Editing - - %span.light #{@page.title} -%hr -= render 'form' +.gray-content-block + .pull-right + = render 'main_links' + + %h3.page-title.oneline + %span.light Edit Page + - if @page.persisted? + = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page) + - else + = @page.title -.pull-right - - if @page.persisted? && can?(current_user, :admin_wiki, @project) - = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-sm btn-remove" do - Delete this page += render 'form' diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index 6417ef4a38b..11c8c4f0eba 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -5,7 +5,7 @@ .gray-content-block .row .col-sm-6 - %h3.page-title + %h3.page-title.oneline Git access for %strong= @project_wiki.path_with_namespace diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index d179a1abec1..aae1ad69ad9 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -1,11 +1,10 @@ -- page_title "All Pages", "Wiki" +- page_title "Pages", "Wiki" = render "header_title" = render 'nav' .gray-content-block - = render 'main_links' - %h3.page-title - All Pages + All pages in this wiki are listed below. + %ul.content-list - @wiki_pages.each do |wiki_page| %li diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 55fbf5a8b6e..309d40f52bc 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -5,11 +5,12 @@ .gray-content-block = render 'main_links' - %h3.page-title + %h3.page-title.oneline = @page.title.capitalize - .wiki-last-edit-by - Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)} + %span.wiki-last-edit-by + · + last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)} - if @page.historical? .warning_message @@ -21,8 +22,3 @@ .wiki = preserve do = render_wiki_content(@page) - -.gray-content-block.footer-block - .wiki-last-edit-by - Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)} - diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb index c78e86fa1a7..ec88c8c20c8 100644 --- a/features/steps/project/source/markdown_render.rb +++ b/features/steps/project/source/markdown_render.rb @@ -238,7 +238,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps step 'I see new wiki page named test' do expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "test") - expect(page).to have_content "Editing" + expect(page).to have_content "Edit Page test" end When 'I go back to wiki page home' do diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index 02207dbffa6..935c1ca8a2e 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -5,7 +5,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps include SharedPaths step 'I click on the Cancel button' do - page.within(:css, ".form-actions") do + page.within(:css, ".wiki-form .form-actions") do click_on "Cancel" end end @@ -24,7 +24,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps expect(page).to have_content "link test" click_link "link test" - expect(page).to have_content "Editing" + expect(page).to have_content "Edit Page" end step 'I have an existing Wiki page' do @@ -68,7 +68,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I click on the "Delete this page" button' do - click_on "Delete this page" + click_on "Delete" end step 'The page should be deleted' do @@ -120,13 +120,13 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps step 'I should see the new wiki page form' do expect(current_path).to match('wikis/image.jpg') expect(page).to have_content('New Wiki Page') - expect(page).to have_content('Editing - image.jpg') + expect(page).to have_content('Edit Page image.jpg') end step 'I create a New page with paths' do click_on 'New Page' fill_in 'Page slug', with: 'one/two/three' - click_on 'Build' + click_on 'Create Page' fill_in "wiki_content", with: 'wiki content' click_on "Create page" expect(current_path).to include 'one/two/three' @@ -135,7 +135,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps step 'I create a New page with an invalid name' do click_on 'New Page' fill_in 'Page slug', with: 'invalid name' - click_on 'Build' + click_on 'Create Page' end step 'I should see an error message' do -- cgit v1.2.1 From b66694d236f71474054c63b1e8a683abacf7fdc3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:00:54 +0100 Subject: Add "New X" link to dashboard/group milestone project-specific issue/MR panels --- app/views/shared/_issues.html.haml | 7 ++++--- app/views/shared/_merge_requests.html.haml | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml index 0dbb6a04393..4b4c9e9eabe 100644 --- a/app/views/shared/_issues.html.haml +++ b/app/views/shared/_issues.html.haml @@ -3,8 +3,10 @@ .panel.panel-default.panel-small - project = group[0] .panel-heading - = link_to_project project - = link_to 'show all', namespace_project_issues_path(project.namespace, project), class: 'pull-right' + = link_to project.name_with_namespace, namespace_project_issues_path(project.namespace, project) + - if can?(current_user, :create_issue, project) + .pull-right + = link_to 'New issue', new_namespace_project_issue_path(project.namespace, project) %ul.well-list.issues-list - group[1].each do |issue| @@ -12,4 +14,3 @@ = paginate @issues, theme: "gitlab" - else .nothing-here-block No issues to show - diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml index c02c5af008a..be17a511b26 100644 --- a/app/views/shared/_merge_requests.html.haml +++ b/app/views/shared/_merge_requests.html.haml @@ -3,8 +3,11 @@ .panel.panel-default.panel-small - project = group[0] .panel-heading - = link_to_project project - = link_to 'show all', namespace_project_merge_requests_path(project.namespace, project), class: 'pull-right' + = link_to project.name_with_namespace, namespace_project_merge_requests_path(project.namespace, project) + - if can?(current_user, :create_merge_request, project) + .pull-right + = link_to 'New merge request', new_namespace_project_merge_request_path(project.namespace, project) + %ul.well-list.mr-list - group[1].each do |merge_request| = render 'projects/merge_requests/merge_request', merge_request: merge_request -- cgit v1.2.1 From 0833057494dcf84c789fee8a1dd2c3f3f541d036 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:01:20 +0100 Subject: Use new style for milestone detail page --- app/views/dashboard/milestones/show.html.haml | 52 ++++++++---- app/views/groups/milestones/show.html.haml | 61 +++++++++----- app/views/projects/milestones/show.html.haml | 113 +++++++++++++++----------- 3 files changed, 140 insertions(+), 86 deletions(-) diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 83077a398bd..3536bbeaf4b 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -1,19 +1,23 @@ - page_title @milestone.title, "Milestones" -%h4.page-title - .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } - - if @milestone.closed? - Closed - - else - Open - Milestone #{@milestone.title} +- header_title "Milestones", dashboard_milestones_path + +.issuable-details + .page-title + .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } + - if @milestone.closed? + Closed + - else + Open + Milestone #{@milestone.title} + + .gray-content-block.middle-block + %h2.issue-title + = gfm escape_once(@milestone.title) -%hr - if @milestone.complete? && @milestone.active? - .alert.alert-success + .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close the milestone now. -.description - .table-holder %table.table %thead @@ -44,7 +48,7 @@ #{@milestone.open_items_count} open = milestone_progress_bar(@milestone) -%ul.nav.nav-tabs +%ul.center-top-menu.no-top.no-bottom %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues @@ -58,25 +62,39 @@ Participants %span.badge= @milestone.participants.count - .pull-right - = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped" - .tab-content .tab-pane.active#tab-issues - .row + .gray-content-block.middle-block + .pull-right + = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All issues in this milestone + + .row.prepend-top-default .col-md-6 = render 'issues', title: "Open", issues: @milestone.opened_issues .col-md-6 = render 'issues', title: "Closed", issues: @milestone.closed_issues .tab-pane#tab-merge-requests - .row + .gray-content-block.middle-block + .pull-right + = link_to 'Browse Merge Requests', merge_requests_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All merge requests in this milestone + + .row.prepend-top-default .col-md-6 = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests .col-md-6 = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests .tab-pane#tab-participants + .gray-content-block.middle-block + .oneline + All participants to this milestone %ul.bordered-list - @milestone.participants.each do |user| %li diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index d161259e4aa..3c1d8815013 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,27 +1,29 @@ - page_title @milestone.title, "Milestones" = render "header_title" -%h4.page-title - .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } - - if @milestone.closed? - Closed - - else - Open - Milestone #{@milestone.title} - .pull-right - - if can?(current_user, :admin_milestones, @group) - - if @milestone.active? - = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close" +.issuable-details + .page-title + .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } + - if @milestone.closed? + Closed - else - = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen" + Open + Milestone #{@milestone.title} + .pull-right + - if can?(current_user, :admin_milestones, @group) + - if @milestone.active? + = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" + - else + = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" + + .gray-content-block.middle-block + %h2.issue-title + = gfm escape_once(@milestone.title) -%hr - if @milestone.complete? && @milestone.active? - .alert.alert-success + .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close the milestone now. -.description - .table-holder %table.table %thead @@ -52,7 +54,7 @@ #{@milestone.open_items_count} open = milestone_progress_bar(@milestone) -%ul.nav.nav-tabs +%ul.center-top-menu.no-top.no-bottom %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues @@ -66,25 +68,40 @@ Participants %span.badge= @milestone.participants.count - .pull-right - = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped" - .tab-content .tab-pane.active#tab-issues - .row + .gray-content-block.middle-block + .pull-right + = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All issues in this milestone + + .row.prepend-top-default .col-md-6 = render 'issues', title: "Open", issues: @milestone.opened_issues .col-md-6 = render 'issues', title: "Closed", issues: @milestone.closed_issues .tab-pane#tab-merge-requests - .row + .gray-content-block.middle-block + .pull-right + = link_to 'Browse Merge Requests', merge_requests_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All merge requests in this milestone + + .row.prepend-top-default .col-md-6 = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests .col-md-6 = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests .tab-pane#tab-participants + .gray-content-block.middle-block + .oneline + All participants to this milestone + %ul.bordered-list - @milestone.participants.each do |user| %li diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 3a898dfbcfd..c3bda794c65 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -1,46 +1,50 @@ - page_title @milestone.title, "Milestones" = render "header_title" -%h4.page-title - .issue-box{ class: issue_box_class(@milestone) } - - if @milestone.closed? - Closed - - elsif @milestone.expired? - Expired - - else - Open - Milestone ##{@milestone.iid} - %small.creator - = @milestone.expires_at - .pull-right - - if can?(current_user, :admin_milestone, @project) - = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do - %i.fa.fa-pencil-square-o - Edit - - if @milestone.active? - = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped" +.issuable-details + .page-title + .issue-box{ class: issue_box_class(@milestone) } + - if @milestone.closed? + Closed + - elsif @milestone.expired? + Expired - else - = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" - = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do - %i.fa.fa-trash-o - Remove + Open + Milestone ##{@milestone.iid} + - if @milestone.expires_at + %span.creator + · + = @milestone.expires_at + .pull-right + - if can?(current_user, :admin_milestone, @project) + = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do + %i.fa.fa-pencil-square-o + Edit + + - if @milestone.active? + = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped" + - else + = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" + + = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do + %i.fa.fa-trash-o + Delete + + .gray-content-block.middle-block + %h2.issue-title + = gfm escape_once(@milestone.title) + %div + - if @milestone.description.present? + .description + .wiki + = preserve do + = markdown @milestone.description -%hr - if @milestone.issues.any? && @milestone.can_be_closed? - .alert.alert-success + .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close milestone now. -%h3.issue-title - = gfm escape_once(@milestone.title) -%div - - if @milestone.description.present? - .description - .wiki - = preserve do - = markdown @milestone.description - -%hr -.context +.context.prepend-top-default %p.lead Progress: #{@milestone.closed_items_count} closed @@ -51,8 +55,7 @@ %span.pull-right= @milestone.expires_at = milestone_progress_bar(@milestone) - -%ul.nav.nav-tabs +%ul.center-top-menu.no-top.no-bottom %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues @@ -66,17 +69,21 @@ Participants %span.badge= @users.count - .pull-right - - if can?(current_user, :create_issue, @project) - = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do - %i.fa.fa-plus - New Issue - - if can?(current_user, :read_issue, @project) - = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped" - .tab-content .tab-pane.active#tab-issues - .row + .gray-content-block.middle-block + .pull-right + - if can?(current_user, :create_issue, @project) + = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do + %i.fa.fa-plus + New Issue + - if can?(current_user, :read_issue, @project) + = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All issues in this milestone + + .row.prepend-top-default .col-md-4 = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned') .col-md-4 @@ -85,7 +92,15 @@ = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed') .tab-pane#tab-merge-requests - .row + .gray-content-block.middle-block + .pull-right + - if can?(current_user, :read_merge_request, @project) + = link_to 'Browse Merge Requests', namespace_project_merge_requests_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All merge requests in this milestone + + .row.prepend-top-default .col-md-3 = render('merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: @merge_requests.opened.unassigned, id: 'unassigned') .col-md-3 @@ -100,6 +115,10 @@ = render 'merge_request', merge_request: merge_request .tab-pane#tab-participants + .gray-content-block.middle-block + .oneline + All participants to this milestone + %ul.bordered-list - @users.each do |user| %li -- cgit v1.2.1 From 05224ae64ac769e3b9b06679d6e44fb120c879ef Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:05:10 +0100 Subject: Restore sidebar tooltips and fix logo tooltip location --- app/assets/javascripts/application.js.coffee | 26 +-- app/assets/javascripts/sidebar.js.coffee | 1 + app/assets/stylesheets/framework/sidebar.scss | 185 +++++++++++----------- app/helpers/nav_helper.rb | 8 + app/views/layouts/_page.html.haml | 10 +- app/views/layouts/ci/_page.html.haml | 10 +- app/views/layouts/nav/_admin.html.haml | 28 ++-- app/views/layouts/nav/_dashboard.html.haml | 18 +-- app/views/layouts/nav/_explore.html.haml | 8 +- app/views/layouts/nav/_group.html.haml | 14 +- app/views/layouts/nav/_group_settings.html.haml | 6 +- app/views/layouts/nav/_profile.html.haml | 20 +-- app/views/layouts/nav/_project.html.haml | 34 ++-- app/views/layouts/nav/_project_settings.html.haml | 24 +-- 14 files changed, 207 insertions(+), 185 deletions(-) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 945ffb660e6..1539eba0faa 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -135,17 +135,25 @@ $ -> ), 1 # Initialize tooltips - $('body').tooltip({ - selector: '.has_tooltip, [data-toggle="tooltip"], .page-sidebar-collapsed .nav-sidebar a' + $('body').tooltip( + selector: '.has_tooltip, [data-toggle="tooltip"]' placement: (_, el) -> $el = $(el) - if $el.attr('id') == 'js-shortcuts-home' - # Place the logo tooltip on the right when collapsed, bottom when expanded - $el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom' - else - # Otherwise use the data-placement attribute, or 'bottom' if undefined - $el.data('placement') or 'bottom' - }) + $el.data('placement') || 'bottom' + ) + + $('.header-logo .home').tooltip( + placement: (_, el) -> + $el = $(el) + if $('.page-with-sidebar').hasClass('page-sidebar-collapsed') then 'right' else 'bottom' + container: 'body' + ) + + $('.page-with-sidebar').tooltip( + selector: '.sidebar-collapsed .nav-sidebar a, .sidebar-collapsed a.sidebar-user' + placement: 'right' + container: 'body' + ) # Form submitter $('.trigger-submit').on 'change', -> diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee index fb08016fbae..ae59480af9e 100644 --- a/app/assets/javascripts/sidebar.js.coffee +++ b/app/assets/javascripts/sidebar.js.coffee @@ -5,6 +5,7 @@ $(document).on("click", '.toggle-nav-collapse', (e) -> $('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}") $('header').toggleClass("header-collapsed header-expanded") + $('.sidebar-wrapper').toggleClass("sidebar-collapsed sidebar-expanded") $('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left") $.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' }) ) diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index c1b0129c866..017f4d2657a 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -1,4 +1,7 @@ .page-with-sidebar { + padding-top: $header-height; + transition-duration: .3s; + .sidebar-wrapper { position: fixed; top: 0; @@ -14,19 +17,15 @@ .sidebar-wrapper { z-index: 99; background: $background-color; - transition-duration: .3s; } .content-wrapper { - min-height: 100vh; width: 100%; padding: 20px; - background: #EAEBEC; .container-fluid { background: #FFF; padding: $gl-padding; - min-height: 90vh; &.container-blank { background: none; @@ -36,6 +35,83 @@ } } +.sidebar-wrapper { + .header-logo { + border-bottom: 1px solid transparent; + float: left; + height: $header-height; + width: $sidebar_width; + position: fixed; + z-index: 999; + overflow: hidden; + transition-duration: .3s; + + a { + float: left; + height: $header-height; + width: 100%; + padding: 11px 0 11px 22px; + overflow: hidden; + outline: none; + transition-duration: .3s; + + img { + width: 36px; + height: 36px; + } + + #tanuki-logo, img { + float: left; + } + + .gitlab-text-container { + width: 230px; + + h3 { + width: 158px; + float: left; + margin: 0; + margin-left: 14px; + font-size: 19px; + line-height: 41px; + font-weight: normal; + } + } + } + + &:hover { + background-color: #EEE; + } + } + + .sidebar-user { + padding: 9px 22px; + position: fixed; + bottom: 40px; + width: $sidebar_width; + overflow: hidden; + transition-duration: .3s; + + .username { + margin-left: 10px; + width: $sidebar_width - 2 * 10px; + font-size: 16px; + line-height: 34px; + } + } +} + + +.tanuki-shape { + transition: all 0.8s; + + &:hover { + fill: rgb(255, 255, 255); + transition: all 0.1s; + } +} + + .nav-sidebar { margin-top: 14 + $header-height; margin-bottom: 100px; @@ -62,7 +138,7 @@ color: $gray; display: block; text-decoration: none; - padding-left: 22px; + padding-left: 23px; font-weight: normal; outline: none; @@ -101,7 +177,6 @@ @mixin expanded-sidebar { padding-left: $sidebar_width; - transition-duration: .3s; .sidebar-wrapper { width: $sidebar_width; @@ -122,9 +197,8 @@ } } -@mixin folded-sidebar { - padding-left: 60px; - transition-duration: .3s; +@mixin collapsed-sidebar { + padding-left: $sidebar_collapsed_width; .sidebar-wrapper { width: $sidebar_collapsed_width; @@ -133,7 +207,7 @@ width: $sidebar_collapsed_width; a { - padding-left: 12px; + padding-left: ($sidebar_collapsed_width - 36) / 2; .gitlab-text-container { display: none; @@ -144,9 +218,13 @@ .nav-sidebar { width: $sidebar_collapsed_width; - li a { - span { - display: none; + li { + width: auto; + + a { + span { + display: none; + } } } } @@ -156,7 +234,7 @@ } .sidebar-user { - padding-left: 12px; + padding-left: ($sidebar_collapsed_width - 36) / 2; width: $sidebar_collapsed_width; .username { @@ -187,11 +265,11 @@ @media (max-width: $screen-md-max) { .page-sidebar-collapsed { - @include folded-sidebar; + @include collapsed-sidebar; } .page-sidebar-expanded { - @include folded-sidebar; + @include collapsed-sidebar; } .collapse-nav { @@ -201,83 +279,10 @@ @media(min-width: $screen-md-max) { .page-sidebar-collapsed { - @include folded-sidebar; + @include collapsed-sidebar; } .page-sidebar-expanded { @include expanded-sidebar; } } - -.sidebar-user { - padding: 9px 22px; - position: fixed; - bottom: 40px; - width: $sidebar_width; - overflow: hidden; - transition-duration: .3s; - - .username { - margin-left: 10px; - width: $sidebar_width - 2 * 10px; - font-size: 16px; - line-height: 34px; - } -} - -.sidebar-wrapper { - .header-logo { - border-bottom: 1px solid transparent; - float: left; - height: $header-height; - width: $sidebar_width; - overflow: hidden; - transition-duration: .3s; - - a { - float: left; - height: $header-height; - width: 100%; - padding: 10px 22px; - overflow: hidden; - outline: none; - - img { - width: 36px; - height: 36px; - } - - #tanuki-logo, img { - float: left; - } - - .gitlab-text-container { - width: 230px; - - h3 { - width: 158px; - float: left; - margin: 0; - margin-left: 14px; - font-size: 19px; - line-height: 41px; - font-weight: normal; - } - } - } - - &:hover { - background-color: #EEE; - } - } -} - - -.tanuki-shape { - transition: all 0.8s; - - &:hover { - fill: rgb(255, 255, 255); - transition: all 0.1s; - } -} diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 9b1dd8b8e54..e6fb8670e57 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -4,6 +4,14 @@ module NavHelper end def nav_sidebar_class + if nav_menu_collapsed? + "sidebar-collapsed" + else + "sidebar-expanded" + end + end + + def page_sidebar_class if nav_menu_collapsed? "page-sidebar-collapsed" else diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 352b8040cf4..ec7cd79bc54 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,8 +1,8 @@ -.page-with-sidebar{ class: nav_sidebar_class } +.page-with-sidebar{ class: page_sidebar_class } = render "layouts/broadcast" - .sidebar-wrapper.nicescroll + .sidebar-wrapper.nicescroll{ class: nav_sidebar_class } .header-logo - = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do = brand_header_logo .gitlab-text-container %h3 GitLab @@ -17,8 +17,8 @@ .collapse-nav = render partial: 'layouts/collapse_button' - if current_user - = link_to current_user, class: 'sidebar-user' do - = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36' + = link_to current_user, class: 'sidebar-user', title: "Profile" do + = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36' .username = current_user.username .content-wrapper diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml index ab3e29c3f42..7e90af21b21 100644 --- a/app/views/layouts/ci/_page.html.haml +++ b/app/views/layouts/ci/_page.html.haml @@ -1,8 +1,8 @@ -.page-with-sidebar{ class: nav_sidebar_class } +.page-with-sidebar{ class: page_sidebar_class } = render "layouts/broadcast" - .sidebar-wrapper.nicescroll + .sidebar-wrapper.nicescroll{ class: nav_sidebar_class } .header-logo - = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do = brand_header_logo .gitlab-text-container %h3 GitLab @@ -14,8 +14,8 @@ .collapse-nav = render partial: 'layouts/collapse_button' - if current_user - = link_to current_user, class: 'sidebar-user' do - = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36' + = link_to current_user, class: 'sidebar-user', title: "Profile" do + = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36' .username = current_user.username .content-wrapper diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 2079feeeab6..d04a3d1f227 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -5,78 +5,78 @@ %span Overview = nav_link(controller: [:admin, :projects]) do - = link_to admin_namespaces_projects_path, title: 'Projects', data: {placement: 'right'} do + = link_to admin_namespaces_projects_path, title: 'Projects' do = icon('cube fw') %span Projects = nav_link(controller: :users) do - = link_to admin_users_path, title: 'Users', data: {placement: 'right'} do + = link_to admin_users_path, title: 'Users' do = icon('user fw') %span Users = nav_link(controller: :groups) do - = link_to admin_groups_path, title: 'Groups', data: {placement: 'right'} do + = link_to admin_groups_path, title: 'Groups' do = icon('group fw') %span Groups = nav_link(controller: :deploy_keys) do - = link_to admin_deploy_keys_path, title: 'Deploy Keys', data: {placement: 'right'} do + = link_to admin_deploy_keys_path, title: 'Deploy Keys' do = icon('key fw') %span Deploy Keys = nav_link do - = link_to ci_admin_projects_path, title: 'Continuous Integration', data: {placement: 'right'} do + = link_to ci_admin_projects_path, title: 'Continuous Integration' do = icon('building fw') %span Continuous Integration = nav_link(controller: :logs) do - = link_to admin_logs_path, title: 'Logs', data: {placement: 'right'} do + = link_to admin_logs_path, title: 'Logs' do = icon('file-text fw') %span Logs = nav_link(controller: :broadcast_messages) do - = link_to admin_broadcast_messages_path, title: 'Broadcast Messages', data: {placement: 'right'} do + = link_to admin_broadcast_messages_path, title: 'Messages' do = icon('bullhorn fw') %span Messages = nav_link(controller: :hooks) do - = link_to admin_hooks_path, title: 'Hooks', data: {placement: 'right'} do + = link_to admin_hooks_path, title: 'Hooks' do = icon('external-link fw') %span Hooks = nav_link(controller: :background_jobs) do - = link_to admin_background_jobs_path, title: 'Background Jobs', data: {placement: 'right'} do + = link_to admin_background_jobs_path, title: 'Background Jobs' do = icon('cog fw') %span Background Jobs = nav_link(controller: :applications) do - = link_to admin_applications_path, title: 'Applications', data: {placement: 'right'} do + = link_to admin_applications_path, title: 'Applications' do = icon('cloud fw') %span Applications = nav_link(controller: :services) do - = link_to admin_application_settings_services_path, title: 'Service Templates', data: {placement: 'right'} do + = link_to admin_application_settings_services_path, title: 'Service Templates' do = icon('copy fw') %span Service Templates = nav_link(controller: :labels) do - = link_to admin_labels_path, title: 'Labels', data: {placement: 'right'} do + = link_to admin_labels_path, title: 'Labels' do = icon('tags fw') %span Labels = nav_link(controller: :abuse_reports) do - = link_to admin_abuse_reports_path, title: "Abuse reports" do + = link_to admin_abuse_reports_path, title: "Abuse Reports" do = icon('exclamation-circle fw') %span Abuse Reports %span.count= AbuseReport.count(:all) = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do - = link_to admin_application_settings_path, title: 'Settings', data: {placement: 'right'} do + = link_to admin_application_settings_path, title: 'Settings' do = icon('cogs fw') %span Settings diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index b1a1d531846..da698831300 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,50 +1,50 @@ %ul.nav.nav-sidebar = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do - = link_to dashboard_projects_path, title: 'Projects', data: {placement: 'right'} do + = link_to dashboard_projects_path, title: 'Projects' do = icon('home fw') %span Projects = nav_link(path: 'dashboard#activity') do - = link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity', data: {placement: 'right'} do + = link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity' do = icon('dashboard fw') %span Activity = nav_link(controller: :groups) do - = link_to dashboard_groups_path, title: 'Groups', data: {placement: 'right'} do + = link_to dashboard_groups_path, title: 'Groups' do = icon('group fw') %span Groups = nav_link(controller: :milestones) do - = link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do + = link_to dashboard_milestones_path, title: 'Milestones' do = icon('clock-o fw') %span Milestones = nav_link(path: 'dashboard#issues') do - = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do + = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do = icon('exclamation-circle fw') %span Issues %span.count= current_user.assigned_issues.opened.count = nav_link(path: 'dashboard#merge_requests') do - = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do + = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do = icon('tasks fw') %span Merge Requests %span.count= current_user.assigned_merge_requests.opened.count = nav_link(controller: :snippets) do - = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do + = link_to dashboard_snippets_path, title: 'Snippets' do = icon('clipboard fw') %span Snippets = nav_link(controller: :help) do - = link_to help_path, title: 'Help', data: {placement: 'right'} do + = link_to help_path, title: 'Help' do = icon('question-circle fw') %span Help %li.separate-item = nav_link(controller: :profile) do - = link_to profile_path, title: 'Profile settings', data: {placement: 'bottom'} do + = link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do = icon('user fw') %span Profile Settings diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml index 21e565972a7..48039ca2918 100644 --- a/app/views/layouts/nav/_explore.html.haml +++ b/app/views/layouts/nav/_explore.html.haml @@ -1,21 +1,21 @@ %ul.nav.nav-sidebar = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do - = link_to explore_root_path, title: 'Projects', data: {placement: 'right'} do + = link_to explore_root_path, title: 'Projects' do = icon('home fw') %span Projects = nav_link(controller: :groups) do - = link_to explore_groups_path, title: 'Groups', data: {placement: 'right'} do + = link_to explore_groups_path, title: 'Groups' do = icon('group fw') %span Groups = nav_link(controller: :snippets) do - = link_to explore_snippets_path, title: 'Snippets', data: {placement: 'right'} do + = link_to explore_snippets_path, title: 'Snippets' do = icon('clipboard fw') %span Snippets = nav_link(controller: :help) do - = link_to help_path, title: 'Help', data: {placement: 'right'} do + = link_to help_path, title: 'Help' do = icon('question-circle fw') %span Help diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 319352876b4..68da8d5de2a 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,6 +1,6 @@ %ul.nav.nav-sidebar = nav_link do - = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to dashboard @@ -8,39 +8,39 @@ %li.separate-item = nav_link(path: 'groups#show', html_options: {class: 'home'}) do - = link_to group_path(@group), title: 'Home', data: {placement: 'right'} do + = link_to group_path(@group), title: 'Home' do = icon('dashboard fw') %span Group - if can?(current_user, :read_group, @group) - if current_user = nav_link(controller: [:group, :milestones]) do - = link_to group_milestones_path(@group), title: 'Milestones', data: {placement: 'right'} do + = link_to group_milestones_path(@group), title: 'Milestones' do = icon('clock-o fw') %span Milestones = nav_link(path: 'groups#issues') do - = link_to issues_group_path(@group), title: 'Issues', data: {placement: 'right'} do + = link_to issues_group_path(@group), title: 'Issues' do = icon('exclamation-circle fw') %span Issues - if current_user %span.count= Issue.opened.of_group(@group).count = nav_link(path: 'groups#merge_requests') do - = link_to merge_requests_group_path(@group), title: 'Merge Requests', data: {placement: 'right'} do + = link_to merge_requests_group_path(@group), title: 'Merge Requests' do = icon('tasks fw') %span Merge Requests - if current_user %span.count= MergeRequest.opened.of_group(@group).count = nav_link(controller: [:group_members]) do - = link_to group_group_members_path(@group), title: 'Members', data: {placement: 'right'} do + = link_to group_group_members_path(@group), title: 'Members' do = icon('users fw') %span Members - if can?(current_user, :admin_group, @group) = nav_link(html_options: { class: "separate-item" }) do - = link_to edit_group_path(@group), title: 'Settings', data: {placement: 'right'} do + = link_to edit_group_path(@group), title: 'Settings' do = icon ('cogs fw') %span Settings diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml index c8411521f36..56a92fe9103 100644 --- a/app/views/layouts/nav/_group_settings.html.haml +++ b/app/views/layouts/nav/_group_settings.html.haml @@ -1,6 +1,6 @@ %ul.nav.nav-sidebar = nav_link do - = link_to group_path(@group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do + = link_to group_path(@group), title: 'Go to group', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to group @@ -9,12 +9,12 @@ %ul.sidebar-subnav = nav_link(path: 'groups#edit') do - = link_to edit_group_path(@group), title: 'Group Settings', data: {placement: 'right'} do + = link_to edit_group_path(@group), title: 'Group Settings' do = icon ('pencil-square-o fw') %span Group Settings = nav_link(path: 'groups#projects') do - = link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do + = link_to projects_group_path(@group), title: 'Projects' do = icon('folder fw') %span Projects diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 0f3a793e30b..64b30783c05 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,6 +1,6 @@ %ul.nav.nav-sidebar = nav_link do - = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to dashboard @@ -8,52 +8,52 @@ %li.separate-item = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do - = link_to profile_path, title: 'Profile', data: {placement: 'right'} do + = link_to profile_path, title: 'Profile Settings' do = icon('user fw') %span Profile Settings = nav_link(controller: [:accounts, :two_factor_auths]) do - = link_to profile_account_path, title: 'Account', data: {placement: 'right'} do + = link_to profile_account_path, title: 'Account' do = icon('gear fw') %span Account = nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new', 'applications#create']) do - = link_to applications_profile_path, title: 'Applications', data: {placement: 'right'} do + = link_to applications_profile_path, title: 'Applications' do = icon('cloud fw') %span Applications = nav_link(controller: :emails) do - = link_to profile_emails_path, title: 'Emails', data: {placement: 'right'} do + = link_to profile_emails_path, title: 'Emails' do = icon('envelope-o fw') %span Emails %span.count= current_user.emails.count + 1 - unless current_user.ldap_user? = nav_link(controller: :passwords) do - = link_to edit_profile_password_path, title: 'Password', data: {placement: 'right'} do + = link_to edit_profile_password_path, title: 'Password' do = icon('lock fw') %span Password = nav_link(controller: :notifications) do - = link_to profile_notifications_path, title: 'Notifications', data: {placement: 'right'} do + = link_to profile_notifications_path, title: 'Notifications' do = icon('inbox fw') %span Notifications = nav_link(controller: :keys) do - = link_to profile_keys_path, title: 'SSH Keys', data: {placement: 'right'} do + = link_to profile_keys_path, title: 'SSH Keys' do = icon('key fw') %span SSH Keys %span.count= current_user.keys.count = nav_link(controller: :preferences) do - = link_to profile_preferences_path, title: 'Preferences', data: {placement: 'right'} do + = link_to profile_preferences_path, title: 'Preferences' do -# TODO (rspeicher): Better icon? = icon('image fw') %span Preferences = nav_link(path: 'profiles#audit_log') do - = link_to audit_log_profile_path, title: 'Audit Log', data: {placement: 'right'} do + = link_to audit_log_profile_path, title: 'Audit Log' do = icon('history fw') %span Audit Log diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 2b91d7721f9..87a7707b095 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -1,13 +1,13 @@ %ul.nav.nav-sidebar - if @project.group = nav_link do - = link_to group_path(@project.group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do + = link_to group_path(@project.group), title: 'Go to group', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to group - else = nav_link do - = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to dashboard @@ -15,32 +15,32 @@ %li.separate-item = nav_link(path: 'projects#show', html_options: {class: 'home'}) do - = link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do + = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do = icon('home fw') %span Project = nav_link(path: 'projects#activity') do - = link_to activity_project_path(@project), title: 'Project Activity', class: 'shortcuts-project-activity', data: {placement: 'right'} do + = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do = icon('dashboard fw') %span Activity - if project_nav_tab? :files = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do - = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do + = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do = icon('files-o fw') %span Files - if project_nav_tab? :commits = nav_link(controller: %w(commit commits compare repositories tags branches releases)) do - = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do + = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do = icon('history fw') %span Commits - if project_nav_tab? :builds = nav_link(controller: %w(builds)) do - = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds', data: {placement: 'right'} do + = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do = icon('cubes fw') %span Builds @@ -48,28 +48,28 @@ - if project_nav_tab? :network = nav_link(controller: %w(network)) do - = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do + = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do = icon('code-fork fw') %span Network - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do - = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do + = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do = icon('area-chart fw') %span Graphs - if project_nav_tab? :milestones = nav_link(controller: :milestones) do - = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do + = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do = icon('clock-o fw') %span Milestones - if project_nav_tab? :issues = nav_link(controller: :issues) do - = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do + = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do = icon('exclamation-circle fw') %span Issues @@ -78,7 +78,7 @@ - if project_nav_tab? :merge_requests = nav_link(controller: :merge_requests) do - = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do + = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do = icon('tasks fw') %span Merge Requests @@ -86,35 +86,35 @@ - if project_nav_tab? :settings = nav_link(controller: [:project_members, :teams]) do - = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do + = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do = icon('users fw') %span Members - if project_nav_tab? :labels = nav_link(controller: :labels) do - = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do + = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do = icon('tags fw') %span Labels - if project_nav_tab? :wiki = nav_link(controller: :wikis) do - = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do + = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do = icon('book fw') %span Wiki - if project_nav_tab? :snippets = nav_link(controller: :snippets) do - = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do + = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do = icon('clipboard fw') %span Snippets - if project_nav_tab? :settings = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do - = link_to edit_project_path(@project), title: 'Settings', data: {placement: 'right'} do + = link_to edit_project_path(@project), title: 'Settings' do = icon('cogs fw') %span Settings diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 377a99e719a..f0b3f27b626 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -1,6 +1,6 @@ %ul.nav.nav-sidebar = nav_link do - = link_to project_path(@project), title: 'Go to project', data: {placement: 'right'}, class: 'back-link' do + = link_to project_path(@project), title: 'Go to project', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to project @@ -9,59 +9,59 @@ %ul.sidebar-subnav = nav_link(path: 'projects#edit') do - = link_to edit_project_path(@project), title: 'Project Settings', data: {placement: 'right'} do + = link_to edit_project_path(@project), title: 'Project Settings' do = icon('pencil-square-o fw') %span Project Settings = nav_link(controller: :deploy_keys) do - = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do + = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do = icon('key fw') %span Deploy Keys = nav_link(controller: :hooks) do - = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do + = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks' do = icon('link fw') %span Web Hooks = nav_link(controller: :services) do - = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do + = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do = icon('cogs fw') %span Services = nav_link(controller: :protected_branches) do - = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do + = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do = icon('lock fw') %span Protected Branches - if @project.builds_enabled? = nav_link(controller: :runners) do - = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners', data: {placement: 'right'} do + = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners' do = icon('cog fw') %span Runners = nav_link(controller: :variables) do - = link_to namespace_project_variables_path(@project.namespace, @project) do + = link_to namespace_project_variables_path(@project.namespace, @project), title: 'Variables' do = icon('code fw') %span Variables = nav_link path: 'triggers#index' do - = link_to namespace_project_triggers_path(@project.namespace, @project) do + = link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do = icon('retweet fw') %span Triggers = nav_link path: 'ci_web_hooks#index' do - = link_to namespace_project_ci_web_hooks_path(@project.namespace, @project) do + = link_to namespace_project_ci_web_hooks_path(@project.namespace, @project), title: 'CI Web Hooks' do = icon('link fw') %span CI Web Hooks = nav_link path: 'ci_settings#edit' do - = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project) do + = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project), title: 'CI Settings' do = icon('building fw') %span CI Settings = nav_link controller: 'ci_services' do - = link_to namespace_project_ci_services_path(@project.namespace, @project) do + = link_to namespace_project_ci_services_path(@project.namespace, @project), title: 'CI Services' do = icon('share fw') %span CI Services -- cgit v1.2.1 From 5c771cca3312dfd447ebe5e0b92ebe279ca1dd67 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:05:24 +0100 Subject: Fix logo height in signed-out header bar --- app/assets/stylesheets/framework/header.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 02ea91602e8..4dbbb56104b 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -6,15 +6,17 @@ header { transition-duration: .3s; &.navbar-empty { + height: 58px; background: #FFF; border-bottom: 1px solid #EEE; .center-logo { - margin: 8px 0; + margin: 11px 0; text-align: center; - img { - height: 32px; + #tanuki-logo, img { + width: 36px; + height: 36px; } } } -- cgit v1.2.1 From 4f7380f94eb7d118b7be51c09e878dfcca84a942 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:05:58 +0100 Subject: Fix header tooltip alignment --- app/views/layouts/header/_default.html.haml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 3ca30d3baab..3892ef8eefa 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -11,27 +11,27 @@ %li.hidden-sm.hidden-xs = render 'layouts/search' %li.visible-sm.visible-xs - = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('search') - if session[:impersonator_id] %li.impersonation - = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop impersonation', data: { toggle: 'tooltip', placement: 'bottom' } do + = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = icon('user-secret fw') - if current_user.is_admin? %li - = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to admin_root_path, title: 'Admin Area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('wrench fw') - if current_user.can_create_project? %li - = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('plus fw') - if Gitlab::Sherlock.enabled? %li = link_to sherlock_transactions_path, title: 'Sherlock Transactions', - data: {toggle: 'tooltip', placement: 'bottom'} do + data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('tachometer fw') %li - = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('sign-out') %h1.title= title -- cgit v1.2.1 From 1ef01e4f6a25743f2034da222407a41b63920df0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:06:11 +0100 Subject: Fade in/out Back icon when sidebar is toggled --- app/assets/stylesheets/framework/sidebar.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 017f4d2657a..458af76cb75 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -162,6 +162,10 @@ padding: 0px 8px; @include border-radius(6px); } + + &.back-link i { + transition-duration: .3s; + } } } } @@ -190,7 +194,7 @@ &.back-link { i { - visibility: hidden; + opacity: 0; } } } -- cgit v1.2.1 From 00a4be759e091bf3e07bde48650e1402da908896 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:06:25 +0100 Subject: Page titles are title case. --- app/views/layouts/admin.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index 1c738719bd8..6591c52bdbd 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -1,5 +1,5 @@ -- page_title "Admin area" -- header_title "Admin area", admin_root_path +- page_title "Admin Area" +- header_title "Admin Area", admin_root_path - sidebar "admin" = render template: "layouts/application" -- cgit v1.2.1 From 112cca872bd7982bd2452f2f0903d21342d5ea5f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:07:21 +0100 Subject: Move project visibility status to the left --- app/assets/stylesheets/framework/blocks.scss | 5 +++++ app/views/projects/_home_panel.html.haml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 1635df9c97b..9e30d3b9c8b 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -112,5 +112,10 @@ position: absolute; top: 10px; right: 10px; + + &.left { + left: 10px; + right: auto; + } } } diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index b30036966a7..d94a279eafd 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -12,9 +12,9 @@ Forked from = link_to project_path(forked_from_project) do = forked_from_project.namespace.try(:name) - .cover-controls .visibility-level-label = visibility_level_icon(@project.visibility_level) + .cover-controls.left = visibility_level_label(@project.visibility_level) .project-repo-buttons -- cgit v1.2.1 From daca985a6e75d6f43c5cc5b487a0942d5bf93f68 Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Tue, 1 Dec 2015 23:40:24 -0500 Subject: Prevent impersonation if blocked --- app/controllers/admin/impersonation_controller.rb | 16 +++++++++++----- app/views/admin/users/_head.html.haml | 2 +- .../admin/impersonation_controller_spec.rb | 19 +++++++++++++++++++ spec/features/admin/admin_users_spec.rb | 10 ++++++++++ 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 spec/controllers/admin/impersonation_controller_spec.rb diff --git a/app/controllers/admin/impersonation_controller.rb b/app/controllers/admin/impersonation_controller.rb index 0382402afa6..102dd437402 100644 --- a/app/controllers/admin/impersonation_controller.rb +++ b/app/controllers/admin/impersonation_controller.rb @@ -5,14 +5,20 @@ class Admin::ImpersonationController < Admin::ApplicationController before_action :authorize_impersonator! def create - session[:impersonator_id] = current_user.username - session[:impersonator_return_to] = request.env['HTTP_REFERER'] + if @user.blocked? + flash[:alert] = "You cannot impersonate a blocked user" - warden.set_user(user, scope: 'user') + redirect_to admin_user_path(@user) + else + session[:impersonator_id] = current_user.username + session[:impersonator_return_to] = request.env['HTTP_REFERER'] + + warden.set_user(user, scope: 'user') - flash[:alert] = "You are impersonating #{user.username}." + flash[:alert] = "You are impersonating #{user.username}." - redirect_to root_path + redirect_to root_path + end end def destroy diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml index 8d1cab4137c..5e17b018163 100644 --- a/app/views/admin/users/_head.html.haml +++ b/app/views/admin/users/_head.html.haml @@ -6,7 +6,7 @@ %span.cred (Admin) .pull-right - - unless @user == current_user + - unless @user == current_user || @user.blocked? = link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info" = link_to edit_admin_user_path(@user), class: "btn btn-grouped" do %i.fa.fa-pencil-square-o diff --git a/spec/controllers/admin/impersonation_controller_spec.rb b/spec/controllers/admin/impersonation_controller_spec.rb new file mode 100644 index 00000000000..d7a7ba1c5b6 --- /dev/null +++ b/spec/controllers/admin/impersonation_controller_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Admin::ImpersonationController do + let(:admin) { create(:admin) } + + before do + sign_in(admin) + end + + describe 'CREATE #impersonation when blocked' do + let(:blocked_user) { create(:user, state: :blocked) } + + it 'does not allow impersonation' do + post :create, id: blocked_user.username + + expect(flash[:alert]).to eq 'You cannot impersonate a blocked user' + end + end +end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 86f01faffb4..4570e409128 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -128,6 +128,16 @@ describe "Admin::Users", feature: true do expect(page).not_to have_content('Impersonate') end + + it 'should not show impersonate button for blocked user' do + another_user.block + + visit admin_user_path(another_user) + + expect(page).not_to have_content('Impersonate') + + another_user.activate + end end context 'when impersonating' do -- cgit v1.2.1 From 085e45a81cbdac73f41702764a1b53a56a298653 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:08:25 +0100 Subject: Add edit and RSS buttons to project home panel --- app/views/projects/_home_panel.html.haml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index d94a279eafd..695da7f07d1 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -17,6 +17,15 @@ .cover-controls.left = visibility_level_label(@project.visibility_level) + .cover-controls + - if can?(current_user, :admin_project, @project) + = link_to edit_project_path(@project), class: 'btn btn-gray' do + = icon('pencil') + - if current_user +   + = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), class: 'btn btn-gray' do + = icon('rss') + .project-repo-buttons .split-one = render 'projects/buttons/star' -- cgit v1.2.1 From d65647e90c25a1cf28353b3d0476aec0a4c6ebb7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:08:59 +0100 Subject: Add visibility description tooltip to snippet and project visibility labels --- app/helpers/icons_helper.rb | 22 +++++++++------ app/helpers/visibility_level_helper.rb | 44 +++++++---------------------- app/views/projects/_home_panel.html.haml | 4 +-- app/views/shared/snippets/_header.html.haml | 4 +-- 4 files changed, 27 insertions(+), 47 deletions(-) diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index 1cf5b96481a..5724d3aabec 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -27,16 +27,20 @@ module IconsHelper end end - def public_icon - icon('globe fw') - end - - def internal_icon - icon('shield fw') - end + def visibility_level_icon(level, fw: true) + name = + case level + when Gitlab::VisibilityLevel::PRIVATE + 'lock' + when Gitlab::VisibilityLevel::INTERNAL + 'shield' + else # Gitlab::VisibilityLevel::PUBLIC + 'globe' + end + + name << " fw" if fw - def private_icon - icon('lock fw') + icon(name) end def file_type_icon_class(type, mode, name) diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index b52cd23aba2..72c65030f94 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -25,48 +25,24 @@ module VisibilityLevelHelper end def project_visibility_level_description(level) - capture_haml do - haml_tag :span do - case level - when Gitlab::VisibilityLevel::PRIVATE - haml_concat "Project access must be granted explicitly for each user." - when Gitlab::VisibilityLevel::INTERNAL - haml_concat "The project can be cloned by" - haml_concat "any logged in user." - when Gitlab::VisibilityLevel::PUBLIC - haml_concat "The project can be cloned" - haml_concat "without any" - haml_concat "authentication." - end - end + case level + when Gitlab::VisibilityLevel::PRIVATE + "Project access must be granted explicitly for each user." + when Gitlab::VisibilityLevel::INTERNAL + "The project can be cloned by any logged in user." + when Gitlab::VisibilityLevel::PUBLIC + "The project can be cloned without any authentication." end end def snippet_visibility_level_description(level) - capture_haml do - haml_tag :span do - case level - when Gitlab::VisibilityLevel::PRIVATE - haml_concat "The snippet is visible only for me." - when Gitlab::VisibilityLevel::INTERNAL - haml_concat "The snippet is visible for any logged in user." - when Gitlab::VisibilityLevel::PUBLIC - haml_concat "The snippet can be accessed" - haml_concat "without any" - haml_concat "authentication." - end - end - end - end - - def visibility_level_icon(level) case level when Gitlab::VisibilityLevel::PRIVATE - private_icon + "The snippet is visible only for me." when Gitlab::VisibilityLevel::INTERNAL - internal_icon + "The snippet is visible for any logged in user." when Gitlab::VisibilityLevel::PUBLIC - public_icon + "The snippet can be accessed without any authentication." end end diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 695da7f07d1..c1669ac046b 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -12,9 +12,9 @@ Forked from = link_to project_path(forked_from_project) do = forked_from_project.namespace.try(:name) - .visibility-level-label - = visibility_level_icon(@project.visibility_level) .cover-controls.left + .visibility-level-label.has_tooltip{title: project_visibility_level_description(@project.visibility_level), data: { container: 'body' } } + = visibility_level_icon(@project.visibility_level, fw: false) = visibility_level_label(@project.visibility_level) .cover-controls diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 0a4a790ec5e..b6c9872f083 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,7 +1,7 @@ .snippet-details .page-title - .snippet-box{class: visibility_level_color(@snippet.visibility_level)} - = visibility_level_icon(@snippet.visibility_level) + .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level), data: { container: 'body' }} + = visibility_level_icon(@snippet.visibility_level, fw: false) = visibility_level_label(@snippet.visibility_level) %span.snippet-id Snippet ##{@snippet.id} %span.creator -- cgit v1.2.1 From de5e28cdcfe8554f2f45f3247d8304aa94f5c3cb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:09:13 +0100 Subject: Use default cursor for project visibility label. --- app/assets/stylesheets/pages/projects.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 4a0fe546844..68a3617512e 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -94,7 +94,7 @@ @extend .btn-gray; color: $gray; - cursor: auto; + cursor: default; i { color: inherit; -- cgit v1.2.1 From 762e759bb3546db7f058d8055ed7b62590d80217 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:09:18 +0100 Subject: Remove duplicate styles. --- app/assets/stylesheets/pages/projects.scss | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 68a3617512e..25d4d0a2c43 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -30,12 +30,6 @@ } .project-home-panel { - text-align: center; - background: #f7f8fa; - margin: -$gl-padding; - padding: $gl-padding; - padding: 44px 0 17px 0; - .project-identicon-holder { margin-bottom: 16px; @@ -105,7 +99,6 @@ display: inline-table; position: relative; top: 17px; - margin-bottom: 44px; } .project-repo-buttons { -- cgit v1.2.1 From 6252fe4510376c917a690e655f8f4257132dde3e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:09:50 +0100 Subject: Unify new project namespace select and path input --- app/assets/stylesheets/framework/forms.scss | 8 ++++++++ app/helpers/namespaces_helper.rb | 6 +++--- app/views/projects/new.html.haml | 20 ++++++++++---------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 0edfe24f195..95674c5812b 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -88,7 +88,15 @@ label { } .input-group { + .select2-container { + display: table-cell; + width: 200px !important; + } .input-group-addon { background-color: #f7f8fa; } + .input-group-addon:not(:first-child):not(:last-child) { + border-left: 0; + border-right: 0; + } } diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index e7f3cb21038..faba418c4db 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -1,10 +1,10 @@ module NamespacesHelper - def namespaces_options(selected = :current_user, scope = :default) + def namespaces_options(selected = :current_user, display_path: false) groups = current_user.owned_groups + current_user.masters_groups users = [current_user.namespace] - group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ] - users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ] + group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [display_path ? g.path : g.human_name, g.id]} ] + users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [display_path ? u.path : u.human_name, u.id]} ] options = [] options << group_opts diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index c9d1fc3da21..f6355ea6703 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -11,16 +11,16 @@ Project path .col-sm-10 .input-group - = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true, required: true - .input-group-addon - \.git - - - if current_user.can_select_namespace? - .form-group - = f.label :namespace_id, class: 'control-label' do - %span Namespace - .col-sm-10 - = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} + - if current_user.can_select_namespace? + .input-group-addon + = root_url + = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2', tabindex: 1} + .input-group-addon + \/ + - else + .input-group-addon + #{root_url}#{current_user.username}/ + = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true - if import_sources_enabled? .project-import.js-toggle-container -- cgit v1.2.1 From 37ab1120c8a252aa29702ef141ff4ea1163e0e75 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:10:03 +0100 Subject: Move "Add Group" button higher up in new project form --- app/views/projects/new.html.haml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index f6355ea6703..a4bd8d54af2 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -22,6 +22,11 @@ #{root_url}#{current_user.username}/ = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true + - if current_user.can_create_group? + .help-block + Want to house several dependent projects under the same namespace? + = link_to "Create a group", new_group_path + - if import_sources_enabled? .project-import.js-toggle-container .form-group @@ -96,14 +101,6 @@ .form-actions = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 - - if current_user.can_create_group? - .pull-right - .light.inline - .space-right - Need a group for several dependent projects? - = link_to new_group_path, class: "btn" do - Create a group - .save-project-loader.hide .center %h2 -- cgit v1.2.1 From a895d5d3b2252cd962b8ea19d2c1092cbee7fca6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:10:32 +0100 Subject: Use select2 for bulk issue edit status --- app/assets/javascripts/issues.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee index 40bb9e9cb0c..ac9e022e727 100644 --- a/app/assets/javascripts/issues.js.coffee +++ b/app/assets/javascripts/issues.js.coffee @@ -29,7 +29,7 @@ $('#filter_issue_search').val($('#issue_search').val()) initSelects: -> - $("select#update_status").select2(width: 'resolve', dropdownAutoWidth: true) + $("select#update_state_event").select2(width: 'resolve', dropdownAutoWidth: true) $("select#update_assignee_id").select2(width: 'resolve', dropdownAutoWidth: true) $("select#update_milestone_id").select2(width: 'resolve', dropdownAutoWidth: true) $("select#label_name").select2(width: 'resolve', dropdownAutoWidth: true) -- cgit v1.2.1 From f385988d1648e54218d68e02a25eaad048b04d0b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:10:43 +0100 Subject: Use "Any Label" and "Any Milestone" in selects rather than the ambiguous "Any" option --- app/models/label.rb | 2 +- app/models/milestone.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/label.rb b/app/models/label.rb index b306aecbac1..bef6063fe88 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -17,7 +17,7 @@ class Label < ActiveRecord::Base # Requests that have no label assigned. LabelStruct = Struct.new(:title, :name) None = LabelStruct.new('No Label', 'No Label') - Any = LabelStruct.new('Any', '') + Any = LabelStruct.new('Any Label', '') DEFAULT_COLOR = '#428BCA' diff --git a/app/models/milestone.rb b/app/models/milestone.rb index c2642b75b8a..d8c7536cd31 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -16,9 +16,9 @@ class Milestone < ActiveRecord::Base # Represents a "No Milestone" state used for filtering Issues and Merge # Requests that have no milestone assigned. - MilestoneStruct = Struct.new(:title, :name) - None = MilestoneStruct.new('No Milestone', 'No Milestone') - Any = MilestoneStruct.new('Any', '') + MilestoneStruct = Struct.new(:title, :name, :id) + None = MilestoneStruct.new('No Milestone', 'No Milestone', 0) + Any = MilestoneStruct.new('Any Milestone', '', -1) include InternalId include Sortable -- cgit v1.2.1 From af541515fa49756d4d23719029648276ed6c2f5b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:11:32 +0100 Subject: Remove "none" username for "Unassigned" and "Any User" select options --- app/assets/javascripts/users_select.js.coffee | 13 +++++-------- app/assets/stylesheets/framework/selects.scss | 8 +++++++- app/helpers/issues_helper.rb | 7 ++++--- app/helpers/selects_helper.rb | 14 ++++++++------ app/views/shared/issuable/_filter.html.haml | 4 ++-- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index f5db74d84e7..12abf806bfa 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -32,17 +32,15 @@ class @UsersSelect if showNullUser nullUser = { name: 'Unassigned', - avatar: null, - username: 'none', id: 0 } data.results.unshift(nullUser) if showAnyUser + name = showAnyUser + name = 'Any User' if name == true anyUser = { - name: 'Any', - avatar: null, - username: 'none', + name: name, id: null } data.results.unshift(anyUser) @@ -50,7 +48,6 @@ class @UsersSelect if showEmailUser && data.results.length == 0 && query.term.match(/^[^@]+@[^@]+$/) emailUser = { name: "Invite \"#{query.term}\"", - avatar: null, username: query.term, id: query.term } @@ -82,10 +79,10 @@ class @UsersSelect else avatar = gon.default_avatar_url - "
+ "
#{user.name}
-
#{user.username}
+
#{user.username || ""}
" formatSelection: (user) -> diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 78fff58d232..5781983c48f 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -123,10 +123,16 @@ } .user-result { + min-height: 24px; + .user-image { float: left; } - .user-name { + + &.no-username { + .user-name { + line-height: 24px; + } } } diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 493f370d9a9..bfaae223b15 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -44,9 +44,10 @@ module IssuesHelper end def bulk_update_milestone_options - options_for_select([['None (backlog)', -1]]) + - options_from_collection_for_select(project_active_milestones, 'id', - 'title', params[:milestone_id]) + milestones = project_active_milestones.to_a + milestones.unshift(Milestone::None) + + options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id]) end def milestone_options(object) diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index 7e54d4d1b5b..7e175d0de8a 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -15,12 +15,14 @@ module SelectsHelper html = { class: css_class, - 'data-placeholder' => placeholder, - 'data-null-user' => null_user, - 'data-any-user' => any_user, - 'data-email-user' => email_user, - 'data-first-user' => first_user, - 'data-current-user' => current_user + data: { + placeholder: placeholder, + null_user: null_user, + any_user: any_user, + email_user: email_user, + first_user: first_user, + current_user: current_user + } } unless opts[:scope] == :all diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index d1231438ee4..c159e85ef21 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -31,11 +31,11 @@ .issues-other-filters .filter-item.inline = users_select_tag(:assignee_id, selected: params[:assignee_id], - placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true, first_user: true, current_user: true) + placeholder: 'Assignee', class: 'trigger-submit', any_user: "Any Assignee", null_user: true, first_user: true, current_user: true) .filter-item.inline = users_select_tag(:author_id, selected: params[:author_id], - placeholder: 'Author', class: 'trigger-submit', any_user: true, first_user: true, current_user: true) + placeholder: 'Author', class: 'trigger-submit', any_user: "Any Author", first_user: true, current_user: true) .filter-item.inline.milestone-filter = select_tag('milestone_title', projects_milestones_options, -- cgit v1.2.1 From 5d0ab2b9e9854733ba174cd3d403600f45c7dbd1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:12:16 +0100 Subject: Make style of select2 box more consistent with controls --- app/assets/stylesheets/framework/selects.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 5781983c48f..5e5ae3d0af8 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -15,6 +15,16 @@ border-left: none; padding-top: 5px; } + + .select2-chosen { + color: $gl-text-color; + } + + &.select2-default { + .select2-chosen { + color: #999; + } + } } } @@ -23,6 +33,7 @@ border: 1px solid #e7e9ed; } + .select2-drop { @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); @include border-radius (0px); -- cgit v1.2.1 From 68ee01cfa0de7656b09eb743aabccf7ca8746d57 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:12:46 +0100 Subject: Use placeholders rather than blank options in bulk issue edit selects. --- app/views/shared/issuable/_filter.html.haml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index c159e85ef21..ac6c248ccf1 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -53,12 +53,16 @@ - if controller.controller_name == 'issues' .issues_bulk_update.hide = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do - = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control') - = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true, first_user: true, current_user: true) - = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone") + .filter-item.inline + = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), include_blank: true, data: { placeholder: "Status" }) + .filter-item.inline + = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true, first_user: true, current_user: true) + .filter-item.inline + = select_tag('update[milestone_id]', bulk_update_milestone_options, include_blank: true, data: { placeholder: "Milestone" }) = hidden_field_tag 'update[issues_ids]', [] = hidden_field_tag :state_event, params[:state_event] - = button_tag "Update issues", class: "btn update_selected_issues btn-save" + .filter-item.inline + = button_tag "Update issues", class: "btn update_selected_issues btn-save" :javascript new UsersSelect(); -- cgit v1.2.1 From d399ffa244511b0bfe0b4de746b6775a3a456465 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:18:46 +0100 Subject: Use "Assigned to USER" tooltip in issue list item --- app/helpers/projects_helper.rb | 5 +++-- app/views/projects/issues/_issue.html.haml | 2 +- app/views/projects/merge_requests/_merge_request.html.haml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index c0c51aae039..48729e5260e 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -21,7 +21,7 @@ module ProjectsHelper end def link_to_member(project, author, opts = {}) - default_opts = { avatar: true, name: true, size: 16, author_class: 'author' } + default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" } opts = default_opts.merge(opts) return "(deleted)" unless author @@ -39,7 +39,8 @@ module ProjectsHelper if opts[:name] link_to(author_html, user_path(author), class: "author_link").html_safe else - link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe + title = opts[:title].sub(":name", sanitize(author.name)) + link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => title, container: 'body' } ).html_safe end end diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index d7657ee7e40..1d452dc601d 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -14,7 +14,7 @@ %span CLOSED - if issue.assignee - = link_to_member(@project, issue.assignee, name: false) + = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name") - note_count = issue.notes.user.count - if note_count > 0   diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 83e8ad11989..5842d7ff163 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -20,7 +20,7 @@ - note_count = merge_request.mr_and_commit_notes.user.count - if merge_request.assignee   - = link_to_member(merge_request.source_project, merge_request.assignee, name: false) + = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name") - if note_count > 0   %span -- cgit v1.2.1 From dc809983a4d5c9f61af9ca4392010d243271f7d3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:18:59 +0100 Subject: Link issue/MR list item comments counter to comments --- app/views/projects/issues/_issue.html.haml | 8 ++++---- app/views/projects/merge_requests/_merge_request.html.haml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 1d452dc601d..4f6ff20caf4 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -18,13 +18,13 @@ - note_count = issue.notes.user.count - if note_count > 0   - %span - %i.fa.fa-comments + = link_to issue_path(issue) + "#notes" do + = icon('comments') = note_count - else   - %span.issue-no-comments - %i.fa.fa-comments + = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do + = icon('comments') = 0 .issue-info diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 5842d7ff163..2f2ebc0894a 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -23,13 +23,13 @@ = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name") - if note_count > 0   - %span - %i.fa.fa-comments + = link_to merge_request_path(merge_request) + "#notes" do + = icon('comments') = note_count - else   - %span.merge-request-no-comments - %i.fa.fa-comments + = link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do + = icon('comments') = 0 .merge-request-info -- cgit v1.2.1 From 08ee38a1c5d55d66070aa6430afa991a395a7da0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:19:25 +0100 Subject: Link milestone item to issues with that milestone --- app/views/projects/issues/_issue.html.haml | 8 +++++--- app/views/projects/merge_requests/_merge_request.html.haml | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 4f6ff20caf4..2e50b603317 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -28,11 +28,13 @@ = 0 .issue-info - = "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe + #{issue.to_reference} · + opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} + by #{link_to_member(@project, issue.author, avatar: false)} - if issue.milestone   - %span - %i.fa.fa-clock-o + = link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do + = icon('clock-o') = issue.milestone.title - if issue.tasks? %span.task-status diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 2f2ebc0894a..4940bc78435 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -33,11 +33,13 @@ = 0 .merge-request-info - = "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe + \##{merge_request.iid} · + opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} + by #{link_to_member(@project, merge_request.author, avatar: false)} - if merge_request.milestone_id?   - %span - %i.fa.fa-clock-o + = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do + = icon('clock-o') = merge_request.milestone.title - if merge_request.target_project.default_branch != merge_request.target_branch   -- cgit v1.2.1 From fbe6432934973021acddcff1d3b0fd3ed5844d2b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:20:03 +0100 Subject: Move labels to second line of issue/MR list item --- app/views/projects/issues/_issue.html.haml | 8 +++++--- app/views/projects/merge_requests/_merge_request.html.haml | 12 +++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 2e50b603317..1eb71990e55 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -6,9 +6,6 @@ .issue-title %span.issue-title-text = link_to_gfm issue.title, issue_path(issue), class: "row_title" - .issue-labels - - issue.labels.each do |label| - = link_to_label(label, project: issue.project) .pull-right.light - if issue.closed? %span @@ -36,7 +33,12 @@ = link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do = icon('clock-o') = issue.milestone.title + - if issue.labels.any? +   + - issue.labels.each do |label| + = link_to_label(label, project: issue.project) - if issue.tasks? +   %span.task-status = issue.task_status diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 4940bc78435..8246a432c77 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -3,19 +3,16 @@ .merge-request-title %span.merge-request-title-text = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title" - .merge-request-labels - - merge_request.labels.each do |label| - = link_to_label(label, project: merge_request.project) .pull-right.light - if ci_commit = render_ci_status(ci_commit) - if merge_request.merged? %span - %i.fa.fa-check + = icon('check') MERGED - elsif merge_request.closed? %span - %i.fa.fa-ban + = icon('ban') CLOSED - note_count = merge_request.mr_and_commit_notes.user.count - if merge_request.assignee @@ -41,12 +38,17 @@ = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do = icon('clock-o') = merge_request.milestone.title + - if merge_request.labels.any? +   + - merge_request.labels.each do |label| + = link_to_label(label, project: merge_request.project) - if merge_request.target_project.default_branch != merge_request.target_branch   %span %i.fa.fa-code-fork = merge_request.target_branch - if merge_request.tasks? +   %span.task-status = merge_request.task_status -- cgit v1.2.1 From ecd9f5ec027a9801bf39a32facf66fe13667b2ca Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:20:43 +0100 Subject: Add "No Milestone" option to issue milestone select --- app/helpers/issues_helper.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 493f370d9a9..efd6418c708 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -50,8 +50,10 @@ module IssuesHelper end def milestone_options(object) - options_from_collection_for_select(object.project.milestones.active, - 'id', 'title', object.milestone_id) + milestones = object.project.milestones.active.to_a + milestones.unshift(Milestone::None) + + options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id) end def issue_box_class(item) -- cgit v1.2.1 From 89488a7cf930c41028a6e2e842cf0b6b8133e78b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:21:03 +0100 Subject: Move "Please review the contribution guide" next to MR/issue Submit button --- app/views/shared/issuable/_form.html.haml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 0fc74d7d2b1..2e1656954e1 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -94,15 +94,17 @@ = link_to 'Change branches', mr_change_branches_path(@merge_request) .form-actions - - if !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) && !issuable.persisted? - %p - Please review the - %strong #{link_to 'guidelines for contribution', guide_url} - to this repository. - if issuable.new_record? = f.submit "Submit new #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' - else = f.submit 'Save changes', class: 'btn btn-save' + + - if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) + .inline.prepend-left-10 + Please review the + %strong #{link_to 'contribution guidelines', guide_url} + for this project. + - if issuable.new_record? - cancel_project = issuable.source_project - else -- cgit v1.2.1 From 6aa43974d4bb674f457833ba6f314bbd7c8a2578 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:21:23 +0100 Subject: Move subscription info below label info in issue/MR sidebar --- app/views/projects/issues/_discussion.html.haml | 7 ------- app/views/projects/merge_requests/_discussion.html.haml | 7 ------- app/views/shared/issuable/_context.html.haml | 8 ++++++++ 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index 8f0a1ed9be2..b5f522f2079 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -29,10 +29,3 @@ .issuable-affix .context = render 'shared/issuable/context', issuable: @issue - - - if @issue.labels.any? - .issuable-context-title - %label Labels - .issue-show-labels - - @issue.labels.each do |label| - = link_to_label(label) diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 2b3c3eff5e4..00b72e5a1a1 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -26,10 +26,3 @@ .issuable-affix .context = render 'shared/issuable/context', issuable: @merge_request - - - if @merge_request.labels.any? - .issuable-context-title - %label Labels - .merge-request-show-labels - - @merge_request.labels.each do |label| - = link_to_label(label) diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index be66256c7b0..5d00c871080 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -29,6 +29,14 @@ = hidden_field_tag :issuable_context = f.submit class: 'btn hide' + - if issuable.labels.any? + %div.prepend-top-default.clearfix + .issuable-context-title + %label Labels + .merge-request-show-labels + - issuable.labels.each do |label| + = link_to_label(label) + - if current_user - subscribed = issuable.subscribed?(current_user) %div.prepend-top-20.clearfix -- cgit v1.2.1 From 3922d6c22c7c148e100eac1635a8d3f0565585db Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:21:39 +0100 Subject: Move (Un)subscribe button below subscription status --- app/views/shared/issuable/_context.html.haml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index 5d00c871080..0b87f11c4db 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -41,17 +41,16 @@ - subscribed = issuable.subscribed?(current_user) %div.prepend-top-20.clearfix .issuable-context-title - %label - Subscription: - %button.btn.btn-block.subscribe-button{:type => 'button'} - = icon('eye') - %span= subscribed ? 'Unsubscribe' : 'Subscribe' + %label Subscription - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' .subscription-status{data: {status: subscribtion_status}} .description-block.unsubscribed{class: ( 'hidden' if subscribed )} You're not receiving notifications from this thread. .description-block.subscribed{class: ( 'hidden' unless subscribed )} You're receiving notifications because you're subscribed to this thread. + %button.btn.btn-block.subscribe-button{:type => 'button'} + = icon('eye') + %span= subscribed ? 'Unsubscribe' : 'Subscribe' :javascript new Subscription("#{toggle_subscription_path(issuable)}"); -- cgit v1.2.1 From e3233016be0a94b88e94700507760c9f672661f4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:22:11 +0100 Subject: Use "opened by" instead of "created by" in issue/MR header, like in list --- app/views/projects/issues/show.html.haml | 3 ++- app/views/projects/merge_requests/show/_mr_title.html.haml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index f01bf2505da..e2de17cee6d 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -11,7 +11,8 @@ Open %span.issue-id Issue ##{@issue.iid} %span.creator - · created by #{link_to_member(@project, @issue.author, size: 24)} + · + opened by #{link_to_member(@project, @issue.author, size: 24)} · = time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago') - if @issue.updated_at != @issue.created_at 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 2bf9cd597a4..4dfe46e2b86 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -4,7 +4,7 @@ %span.issue-id Merge Request ##{@merge_request.iid} %span.creator · - created by #{link_to_member(@project, @merge_request.author, size: 24)} + opened by #{link_to_member(@project, @merge_request.author, size: 24)} · = time_ago_with_tooltip(@merge_request.created_at) - if @merge_request.updated_at != @merge_request.created_at -- cgit v1.2.1 From 7a65cb3abf63bcd87856c6220e87c7fccfdb8a1f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:22:33 +0100 Subject: Don't reset target branch when choosing to "Change branches" in MR --- app/helpers/merge_requests_helper.rb | 5 +++-- app/views/projects/merge_requests/new.html.haml | 2 +- features/steps/project/forked_merge_requests.rb | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index b804d4f4e3b..cc4243e1559 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -49,8 +49,9 @@ module MergeRequestsHelper source_project_id: @merge_request.source_project_id, target_project_id: @merge_request.target_project_id, source_branch: @merge_request.source_branch, - target_branch: nil - } + target_branch: @merge_request.target_branch, + }, + change_branches: true ) end diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml index 9fdde80c6d9..d259968030e 100644 --- a/app/views/projects/merge_requests/new.html.haml +++ b/app/views/projects/merge_requests/new.html.haml @@ -1,7 +1,7 @@ - page_title "New Merge Request" = render "header_title" -- if @merge_request.can_be_created +- if @merge_request.can_be_created && !params[:change_branches] = render 'new_submit' - else = render 'new_compare' diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 2a333222fb2..789befc9df2 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -112,7 +112,6 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps end step 'I fill out an invalid "Merge Request On Forked Project" merge request' do - select "Select branch", from: "merge_request_target_branch" expect(find(:select, "merge_request_source_project_id", {}).value).to eq @forked_project.id.to_s expect(find(:select, "merge_request_target_project_id", {}).value).to eq @project.id.to_s expect(find(:select, "merge_request_source_branch", {}).value).to eq "" -- cgit v1.2.1 From 1df2908b27bbce4a0c67139dc3358799ffa55550 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:23:36 +0100 Subject: Link to branches from MR "Request to merge X into Y" sentence --- app/views/projects/merge_requests/_show.html.haml | 5 +++-- app/views/projects/merge_requests/widget/_merged.html.haml | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index eeaa72ed21b..c52d870f6e2 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -26,9 +26,10 @@ %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) .normal %span Request to merge - %span.label-branch #{source_branch_with_namespace(@merge_request)} + = link_to source_branch_with_namespace(@merge_request), namespace_project_commits_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), class: "label-branch" %span into - %span.label-branch #{@merge_request.target_branch} + = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do + = @merge_request.target_branch = render "projects/merge_requests/show/how_to_merge" = render "projects/merge_requests/widget/show.html.haml" diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index a788fcea23f..ac08e0b498a 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -10,7 +10,8 @@ - if !@merge_request.source_branch_exists? = succeed '.' do The changes were merged into - %span.label-branch= @merge_request.target_branch + = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do + = @merge_request.target_branch The source branch has been removed. - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) @@ -18,7 +19,8 @@ %p = succeed '.' do The changes were merged into - %span.label-branch= @merge_request.target_branch + = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do + = @merge_request.target_branch You can remove the source branch now. = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do %i.fa.fa-times -- cgit v1.2.1 From 6c54823dce9aecf54f78b41180ec75efbc1391f4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:24:29 +0100 Subject: Use select2 placeholder instead of blank option --- app/views/projects/merge_requests/_new_compare.html.haml | 4 ++-- app/views/shared/issuable/_context.html.haml | 2 +- app/views/shared/issuable/_form.html.haml | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index d9eff1f9320..f5a512d385b 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -10,7 +10,7 @@ .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?, required: true })   - = f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2', required: true}) + = f.select(:source_branch, @merge_request.source_branches, { include_blank: true }, { class: 'source_branch select2 span2', required: true, data: { placeholder: "Select source branch" } }) .panel-footer .mr_source_commit @@ -22,7 +22,7 @@ - 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?, required: true })   - = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2', required: true}) + = f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', required: true, data: { placeholder: "Select target branch" } }) .panel-footer .mr_target_commit diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index 0b87f11c4db..e6d753a8b7f 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -25,7 +25,7 @@ none .issuable-context-selectbox - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'}) + = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }}) = hidden_field_tag :issuable_context = f.submit class: 'btn hide' diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 2e1656954e1..942551c2682 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -39,9 +39,9 @@ Assign to .col-sm-10 = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", - placeholder: 'Select a user', class: 'custom-form-control', null_user: true, + placeholder: 'Select assignee', class: 'custom-form-control', null_user: true, selected: issuable.assignee_id, project: @target_project || @project, - first_user: true, current_user: true) + first_user: true, current_user: true, include_blank: true)   = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' .form-group @@ -52,7 +52,7 @@ .col-sm-10 - if milestone_options(issuable).present? = f.select(:milestone_id, milestone_options(issuable), - { include_blank: 'Select milestone' }, { class: 'select2' }) + { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } }) - else .prepend-top-10 %span.light No open milestones available. @@ -66,7 +66,7 @@ .col-sm-10 - if issuable.project.labels.any? = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, - { selected: issuable.label_ids }, multiple: true, class: 'select2' + { selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" } - else .prepend-top-10 %span.light No labels yet. @@ -88,7 +88,7 @@ %i.fa.fa-code-fork Target Branch .col-sm-10 - = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record? }) + = f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} }) - if @merge_request.new_record? %p.help-block = link_to 'Change branches', mr_change_branches_path(@merge_request) -- cgit v1.2.1 From 3b03987a27f3b6b720251b02abfadc4e9ba06fe8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:24:44 +0100 Subject: Redesign multiple select2 options --- app/assets/stylesheets/framework/selects.scss | 41 ++++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 78fff58d232..f541444401c 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -48,17 +48,38 @@ color: #313236; } +.select2-container-multi { + .select2-choices { + @include border-radius(2px); + border-color: $input-border; + background: white; + padding-left: $gl-padding / 2; + + .select2-search-field input { + padding: $gl-padding / 2; + font-size: 13px; + height: auto; + font-family: inherit; + font-size: inherit; + } -.select2-container-multi .select2-choices { - @include border-radius(2px); - border-color: #CCC; -} - -.select2-container-multi .select2-choices .select2-search-field input { - padding: 8px 14px; - font-size: 13px; - line-height: 18px; - height: auto; + .select2-search-choice { + margin: 8px 0 0 8px; + background: white; + box-shadow: none; + border-color: $input-border; + color: $gl-text-color; + line-height: 15px; + + .select2-search-choice-close { + top: 5px; + } + + &.select2-search-choice-focus { + border-color: $gl-text-color; + } + } + } } .select2-drop-active { -- cgit v1.2.1 From 05c9d882166710a26e27a7b79e7855db47ca43c0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:25:03 +0100 Subject: Only show manual merge instructions if current user can merge --- app/views/projects/merge_requests/_show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index c52d870f6e2..09b228b7c0c 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -34,8 +34,8 @@ = render "projects/merge_requests/show/how_to_merge" = render "projects/merge_requests/widget/show.html.haml" - - if @merge_request.open? && @merge_request.can_be_merged? - .light.append-bottom-20 + - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user) + .light.prepend-top-default You can also accept this merge request manually using the = succeed '.' do = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" -- cgit v1.2.1 From ed74fa73e227b9666f3f38f17b35a5cf8328fa44 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:27:29 +0100 Subject: Use consistent casing for page titles --- app/views/admin/labels/edit.html.haml | 8 ++------ app/views/admin/labels/new.html.haml | 6 ++---- app/views/admin/users/edit.html.haml | 3 --- app/views/groups/edit.html.haml | 3 +-- app/views/groups/new.html.haml | 7 ++++++- app/views/projects/blob/new.html.haml | 5 ++--- app/views/projects/branches/new.html.haml | 5 +++-- app/views/projects/deploy_keys/new.html.haml | 2 +- app/views/projects/issues/_form.html.haml | 3 --- app/views/projects/issues/edit.html.haml | 6 ++++++ app/views/projects/issues/new.html.haml | 4 ++++ app/views/projects/labels/edit.html.haml | 8 ++------ app/views/projects/labels/new.html.haml | 6 ++---- app/views/projects/merge_requests/_new_compare.html.haml | 3 ++- app/views/projects/merge_requests/_new_submit.html.haml | 2 +- app/views/projects/merge_requests/edit.html.haml | 2 +- app/views/projects/milestones/_form.html.haml | 7 ------- app/views/projects/milestones/edit.html.haml | 6 ++++++ app/views/projects/milestones/new.html.haml | 6 ++++++ app/views/projects/new.html.haml | 7 ++++++- app/views/projects/services/_form.html.haml | 4 ---- app/views/projects/snippets/edit.html.haml | 2 +- app/views/projects/snippets/new.html.haml | 2 +- app/views/projects/tags/new.html.haml | 2 +- app/views/shared/_confirm_modal.html.haml | 3 ++- app/views/snippets/edit.html.haml | 2 +- app/views/snippets/new.html.haml | 2 +- features/steps/project/forked_merge_requests.rb | 2 +- features/steps/project/source/browse_files.rb | 2 +- 29 files changed, 62 insertions(+), 58 deletions(-) diff --git a/app/views/admin/labels/edit.html.haml b/app/views/admin/labels/edit.html.haml index 45c62a76259..309aedceded 100644 --- a/app/views/admin/labels/edit.html.haml +++ b/app/views/admin/labels/edit.html.haml @@ -1,9 +1,5 @@ - page_title "Edit", @label.name, "Labels" -%h3 - Edit label - %span.light #{@label.name} -.back-link - = link_to admin_labels_path do - ← To labels list +%h3.page-title + Edit Label %hr = render 'form' diff --git a/app/views/admin/labels/new.html.haml b/app/views/admin/labels/new.html.haml index 8d298ad20f7..0135ad0723d 100644 --- a/app/views/admin/labels/new.html.haml +++ b/app/views/admin/labels/new.html.haml @@ -1,7 +1,5 @@ - page_title "New Label" -%h3 New label -.back-link - = link_to admin_labels_path do - ← To labels list +%h3.page-title + New Label %hr = render 'form' diff --git a/app/views/admin/users/edit.html.haml b/app/views/admin/users/edit.html.haml index a8837d74dd9..3b6fd71500d 100644 --- a/app/views/admin/users/edit.html.haml +++ b/app/views/admin/users/edit.html.haml @@ -1,8 +1,5 @@ - page_title "Edit", @user.name, "Users" %h3.page-title Edit user: #{@user.name} -.back-link - = link_to admin_user_path(@user) do - ← Back to user page %hr = render 'form' diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 57308a661c0..b5afb4ae1c2 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -3,8 +3,7 @@ .panel.panel-default .panel-heading - %strong= @group.name - group settings: + Group settings .panel-body = form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f| - if @group.errors.any? diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 0665cdf387a..3e602559ae0 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -1,5 +1,10 @@ - page_title 'New Group' -- header_title 'New Group' +- header_title "Groups", dashboard_groups_path + +%h3.page-title + New Group +%hr + = form_for @group, html: { class: 'group-form form-horizontal' } do |f| - if @group.errors.any? .alert.alert-danger diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 1ff68005450..167fa615182 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,9 +1,8 @@ - page_title "New File", @path.presence, @ref = render "header_title" -.gray-content-block.top-block - %h3.page-title - Create New File +%h3.page-title + New File .file-editor = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-requires-input') do diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index f5577042ca4..d103a713c54 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -6,8 +6,9 @@ %button{ type: "button", class: "close", "data-dismiss" => "alert"} × = @error %h3.page-title - %i.fa.fa-code-fork - New branch + New Branch +%hr + = form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-requires-input" do .form-group = label_tag :branch_name, 'Name for new branch', class: 'control-label' diff --git a/app/views/projects/deploy_keys/new.html.haml b/app/views/projects/deploy_keys/new.html.haml index 01c810aee18..01fab3008a7 100644 --- a/app/views/projects/deploy_keys/new.html.haml +++ b/app/views/projects/deploy_keys/new.html.haml @@ -1,5 +1,5 @@ - page_title "New Deploy Key" -%h3.page-title New Deploy key +%h3.page-title New Deploy Key %hr = render 'form' diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index f39bb7d2574..e0e26a26dae 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,7 +1,4 @@ %div.issue-form-holder - %h3.page-title= @issue.new_record? ? "Create Issue" : "Edit Issue ##{@issue.iid}" - %hr - = form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f| = render 'shared/issuable/form', f: f, issuable: @issue diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml index 53b6f0879c9..20216297d25 100644 --- a/app/views/projects/issues/edit.html.haml +++ b/app/views/projects/issues/edit.html.haml @@ -1,2 +1,8 @@ - page_title "Edit", "#{@issue.title} (##{@issue.iid})", "Issues" += render "header_title" + +%h3.page-title + Edit Issue ##{@issue.iid} +%hr + = render "form" diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml index 153447baa1b..b317a0c1cf4 100644 --- a/app/views/projects/issues/new.html.haml +++ b/app/views/projects/issues/new.html.haml @@ -1,4 +1,8 @@ - page_title "New Issue" = render "header_title" +%h3.page-title + New Issue +%hr + = render "form" diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml index bc4ab0ca27c..675a805e12f 100644 --- a/app/views/projects/labels/edit.html.haml +++ b/app/views/projects/labels/edit.html.haml @@ -1,11 +1,7 @@ - page_title "Edit", @label.name, "Labels" = render "header_title" -%h3 - Edit label - %span.light #{@label.name} -.back-link - = link_to namespace_project_labels_path(@project.namespace, @project) do - ← To labels list +%h3.page-title + Edit Label %hr = render 'form' diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index 342ad4f3f95..e20fd7d6891 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -1,9 +1,7 @@ - page_title "New Label" = render "header_title" -%h3 New label -.back-link - = link_to namespace_project_labels_path(@project.namespace, @project) do - ← To labels list +%h3.page-title + New Label %hr = render 'form' diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index d9eff1f9320..46e72e9dee5 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -1,4 +1,5 @@ -%p.lead Compare branches for new Merge Request +%h3.page-title + New Merge Request = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: new_namespace_project_merge_request_path(@project.namespace, @project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f| .hide.alert.alert-danger.mr-compare-errors diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 6244d3ba0b4..9fa9cc56126 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -1,5 +1,5 @@ %h3.page-title - New merge request + New Merge Request %p.slead - source_title, target_title = format_mr_branch_names(@merge_request) From diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml index 303ca0a880b..fc62bb5bce9 100644 --- a/app/views/projects/merge_requests/edit.html.haml +++ b/app/views/projects/merge_requests/edit.html.haml @@ -2,6 +2,6 @@ = render "header_title" %h3.page-title - = "Edit merge request ##{@merge_request.iid}" + Edit Merge Request ##{@merge_request.iid} %hr = render 'form' diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 24879b19d2b..cc29970f07f 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -1,10 +1,3 @@ -%h3.page-title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.iid}" -.back-link - = link_to namespace_project_milestones_path(@project.namespace, @project) do - ← To milestones - -%hr - = form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-requires-input'} do |f| -if @milestone.errors.any? .alert.alert-danger diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml index e9dc0b77462..43f8863163d 100644 --- a/app/views/projects/milestones/edit.html.haml +++ b/app/views/projects/milestones/edit.html.haml @@ -1,3 +1,9 @@ - page_title "Edit", @milestone.title, "Milestones" = render "header_title" + +%h3.page-title + Edit Milestone ##{@milestone.iid} + +%hr + = render "form" diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml index 9ba9acb6f77..0d016f78313 100644 --- a/app/views/projects/milestones/new.html.haml +++ b/app/views/projects/milestones/new.html.haml @@ -1,3 +1,9 @@ - page_title "New Milestone" = render "header_title" + +%h3.page-title + New Milestone + +%hr + = render "form" diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index c9d1fc3da21..fa75a624222 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,5 +1,10 @@ - page_title 'New Project' -- header_title 'New Project' +- header_title "Projects", root_path + +%h3.page-title + New Project +%hr + .project-edit-container .project-edit-errors = render 'projects/errors' diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index e1823b51198..fecd157c6dc 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -4,10 +4,6 @@ %p= @service.description -.back-link - = link_to namespace_project_services_path(@project.namespace, @project) do - ← to services - %hr = form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml index e69f2d99709..dc3ea1fcf12 100644 --- a/app/views/projects/snippets/edit.html.haml +++ b/app/views/projects/snippets/edit.html.haml @@ -2,6 +2,6 @@ = render "header_title" %h3.page-title - Edit snippet + Edit Snippet %hr = render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet), visibility_level: @snippet.visibility_level diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml index 67cd69fd215..e57237991b4 100644 --- a/app/views/projects/snippets/new.html.haml +++ b/app/views/projects/snippets/new.html.haml @@ -2,6 +2,6 @@ = render "header_title" %h3.page-title - New snippet + New Snippet %hr = render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet), visibility_level: default_snippet_visibility diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 86aa15dc5b3..97abdb239ed 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -7,7 +7,7 @@ = @error %h3.page-title - New git tag + New Tag %hr = form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml index 2a44817e05a..9bc2d33c27e 100644 --- a/app/views/shared/_confirm_modal.html.haml +++ b/app/views/shared/_confirm_modal.html.haml @@ -3,7 +3,8 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h4 Confirmation required + %h3.page-title + Confirmation required .modal-body %p.cred.lead.js-confirm-text diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml index 1a380035661..82f44a9a5c3 100644 --- a/app/views/snippets/edit.html.haml +++ b/app/views/snippets/edit.html.haml @@ -1,5 +1,5 @@ - page_title "Edit", @snippet.title, "Snippets" %h3.page-title - Edit snippet + Edit Snippet %hr = render 'shared/snippets/form', url: snippet_path(@snippet), visibility_level: @snippet.visibility_level diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml index a74d5e792ad..79e2392490d 100644 --- a/app/views/snippets/new.html.haml +++ b/app/views/snippets/new.html.haml @@ -1,5 +1,5 @@ - page_title "New Snippet" %h3.page-title - New snippet + New Snippet %hr = render "shared/snippets/form", url: snippets_path(@snippet), visibility_level: default_snippet_visibility diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 2a333222fb2..155a5ff58ae 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -41,7 +41,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps click_button "Compare branches" - expect(page).to have_content "New merge request" + expect(page).to have_content "New Merge Request" fill_in "merge_request_title", with: "Merge Request On Forked Project" end diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index f40e0f0d528..99e1e9b4af6 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -142,7 +142,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I can see new file page' do - expect(page).to have_content "Create New File" + expect(page).to have_content "New File" expect(page).to have_content "Commit message" end -- cgit v1.2.1 From 67119e15c03d4d1e8abd2ce2cfe1b40aba35c709 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:30:12 +0100 Subject: Use consistent casing for form field labels --- app/views/admin/labels/_form.html.haml | 2 +- app/views/projects/blob/_new_dir.html.haml | 2 +- app/views/projects/branches/new.html.haml | 2 +- app/views/projects/labels/_form.html.haml | 2 +- app/views/projects/tags/new.html.haml | 4 ++-- app/views/shared/_group_form.html.haml | 2 +- app/views/shared/_new_commit_form.html.haml | 3 +-- app/views/shared/issuable/_form.html.haml | 22 ++++++---------------- features/steps/admin/labels.rb | 8 ++++---- spec/features/issues_spec.rb | 2 +- 10 files changed, 19 insertions(+), 30 deletions(-) diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml index a5ace4e7a3b..eaa94ed9e36 100644 --- a/app/views/admin/labels/_form.html.haml +++ b/app/views/admin/labels/_form.html.haml @@ -12,7 +12,7 @@ .col-sm-10 = f.text_field :title, class: "form-control", required: true .form-group - = f.label :color, "Background Color", class: 'control-label' + = f.label :color, "Background color", class: 'control-label' .col-sm-10 .input-group .input-group-addon.label-color-preview   diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 13b5ffd17ff..377f0fa0129 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -7,7 +7,7 @@ .modal-body = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form' do .form-group - = label_tag :dir_name, 'Directory Name', class: 'control-label' + = label_tag :dir_name, 'Directory name', class: 'control-label' .col-sm-10 = text_field_tag :dir_name, params[:dir_name], placeholder: "Directory name", required: true, class: 'form-control' diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index d103a713c54..efb5298a5e5 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -11,7 +11,7 @@ = form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-requires-input" do .form-group - = label_tag :branch_name, 'Name for new branch', class: 'control-label' + = label_tag :branch_name, nil, class: 'control-label' .col-sm-10 = text_field_tag :branch_name, params[:branch_name], placeholder: 'enter new branch name', required: true, tabindex: 1, class: 'form-control' .form-group diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml index 4cf13492e99..291703bd60c 100644 --- a/app/views/projects/labels/_form.html.haml +++ b/app/views/projects/labels/_form.html.haml @@ -12,7 +12,7 @@ .col-sm-10 = f.text_field :title, class: "form-control js-quick-submit", required: true .form-group - = f.label :color, "Background Color", class: 'control-label' + = f.label :color, "Background color", class: 'control-label' .col-sm-10 .input-group .input-group-addon.label-color-preview   diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 97abdb239ed..91e0a5493bd 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -12,7 +12,7 @@ = form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do .form-group - = label_tag :tag_name, 'Name for new tag', class: 'control-label' + = label_tag :tag_name, nil, class: 'control-label' .col-sm-10 = text_field_tag :tag_name, params[:tag_name], placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control' .form-group @@ -21,7 +21,7 @@ = text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control' .help-block Branch name or commit SHA .form-group - = label_tag :message, 'Message', class: 'control-label' + = label_tag :message, nil, class: 'control-label' .col-sm-10 = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' .help-block (Optional) Entering a message will create an annotated tag. diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml index c0a9923348e..67072b9fc2a 100644 --- a/app/views/shared/_group_form.html.haml +++ b/app/views/shared/_group_form.html.haml @@ -23,7 +23,7 @@ %li It will change the git path to repositories under this group. .form-group.group-description-holder - = f.label :description, 'Details', class: 'control-label' + = f.label :description, class: 'control-label' .col-sm-10 = f.text_area :description, maxlength: 250, class: 'form-control js-gfm-input', rows: 4 diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml index 8636341c60d..ce52614e868 100644 --- a/app/views/shared/_new_commit_form.html.haml +++ b/app/views/shared/_new_commit_form.html.haml @@ -2,8 +2,7 @@ - unless @project.empty_repo? .form-group.branch - = label_tag 'branch', class: 'control-label' do - Branch + = label_tag 'new_branch', 'Target branch', class: 'control-label' .col-sm-10 = text_field_tag 'new_branch', @new_branch || @ref, class: "form-control js-new-branch" diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 0fc74d7d2b1..2a5fb534a3c 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -30,13 +30,11 @@ = render 'projects/notes/hints' .clearfix .error-alert - %hr - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) + %hr .form-group .issue-assignee - = f.label :assignee_id, class: 'control-label' do - %i.fa.fa-user - Assign to + = f.label :assignee_id, "Assignee", class: 'control-label' .col-sm-10 = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", placeholder: 'Select a user', class: 'custom-form-control', null_user: true, @@ -46,9 +44,7 @@ = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' .form-group .issue-milestone - = f.label :milestone_id, class: 'control-label' do - %i.fa.fa-clock-o - Milestone + = f.label :milestone_id, "Milestone", class: 'control-label' .col-sm-10 - if milestone_options(issuable).present? = f.select(:milestone_id, milestone_options(issuable), @@ -60,9 +56,7 @@ - if can? current_user, :admin_milestone, issuable.project = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank .form-group - = f.label :label_ids, class: 'control-label' do - %i.fa.fa-tag - Labels + = f.label :label_ids, "Labels", class: 'control-label' .col-sm-10 - if issuable.project.labels.any? = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, @@ -78,15 +72,11 @@ %hr - if @merge_request.new_record? .form-group - = f.label :source_branch, class: 'control-label' do - %i.fa.fa-code-fork - Source Branch + = f.label :source_branch, class: 'control-label' .col-sm-10 = f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true }) .form-group - = f.label :target_branch, class: 'control-label' do - %i.fa.fa-code-fork - Target Branch + = f.label :target_branch, class: 'control-label' .col-sm-10 = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record? }) - if @merge_request.new_record? diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb index b45d98658bc..cb23b869658 100644 --- a/features/steps/admin/labels.rb +++ b/features/steps/admin/labels.rb @@ -45,21 +45,21 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps step 'I submit new label \'support\'' do visit new_admin_label_path fill_in 'Title', with: 'support' - fill_in 'Background Color', with: '#F95610' + fill_in 'Background color', with: '#F95610' click_button 'Save' end step 'I submit new label \'bug\'' do visit new_admin_label_path fill_in 'Title', with: 'bug' - fill_in 'Background Color', with: '#F95610' + fill_in 'Background color', with: '#F95610' click_button 'Save' end step 'I submit new label with invalid color' do visit new_admin_label_path fill_in 'Title', with: 'support' - fill_in 'Background Color', with: '#12' + fill_in 'Background color', with: '#12' click_button 'Save' end @@ -101,7 +101,7 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps step 'I change label \'bug\' to \'fix\'' do fill_in 'Title', with: 'fix' - fill_in 'Background Color', with: '#F15610' + fill_in 'Background color', with: '#F15610' click_button 'Save' end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 32fd4065bb4..0af5e6fc1a6 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -61,7 +61,7 @@ describe 'Issues', feature: true do it 'allows user to select unasigned', js: true do visit edit_namespace_project_issue_path(project.namespace, project, issue) - expect(page).to have_content "Assign to #{@user.name}" + expect(page).to have_content "Assignee #{@user.name}" first('#s2id_issue_assignee_id').click sleep 2 # wait for ajax stuff to complete -- cgit v1.2.1 From ca016903054fe53be08b8afeb53c019f44247cf9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:31:15 +0100 Subject: Add cancel button to forms that didn't have one already --- app/views/groups/new.html.haml | 1 + app/views/profiles/show.html.haml | 8 +++----- app/views/projects/new.html.haml | 1 + app/views/projects/services/_form.html.haml | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 3e602559ae0..4bc31cabea6 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -23,3 +23,4 @@ .form-actions = f.submit 'Create group', class: "btn btn-create", tabindex: 3 + = link_to 'Cancel', dashboard_groups_path, class: 'btn btn-cancel' diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index ac7355dde1f..faab12ca0b9 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -96,8 +96,6 @@ = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" - .row - .col-md-7 - .form-group - .col-sm-offset-2.col-sm-10 - = f.submit 'Save changes', class: "btn btn-success" + .form-actions + = f.submit 'Save changes', class: "btn btn-success" + = link_to "Cancel", user_path(current_user), class: "btn btn-cancel" diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index fa75a624222..2670b9d9cda 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -100,6 +100,7 @@ .form-actions = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 + = link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel' - if current_user.can_create_group? .pull-right diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index fecd157c6dc..ea5a2302a56 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -15,3 +15,4 @@ - if @service.valid? && @service.activated? - disabled = @service.can_test? ? '':'disabled' = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}" + = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel" -- cgit v1.2.1 From ffabf1df50744564ac99f358f13ab4b1c5d54284 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:33:14 +0100 Subject: Add cancel button to forms that didn't have one already --- app/views/profiles/preferences/show.html.haml | 2 +- app/views/projects/deploy_keys/_form.html.haml | 2 +- app/views/projects/edit.html.haml | 20 ++++++++++++-------- app/views/projects/labels/_form.html.haml | 6 ++++-- .../projects/merge_requests/_new_compare.html.haml | 4 ++-- app/views/projects/runners/edit.html.haml | 2 +- app/views/projects/services/_form.html.haml | 2 +- app/views/shared/issuable/_form.html.haml | 2 +- app/views/shared/snippets/_form.html.haml | 2 +- features/steps/project/forked_merge_requests.rb | 2 +- features/steps/project/issues/issues.rb | 4 ++-- features/steps/project/merge_requests.rb | 2 +- 12 files changed, 28 insertions(+), 22 deletions(-) diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index cc41d7dd813..877589dc390 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -54,4 +54,4 @@ .help-block Choose what content you want to see on a project's home page. .panel-footer - = f.submit 'Save', class: 'btn btn-save' + = f.submit 'Save changes', class: 'btn btn-save' diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index 91675b3738e..085c9149b11 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -18,6 +18,6 @@ = f.text_area :key, class: "form-control thin_area", rows: 5 .form-actions - = f.submit 'Create', class: "btn-create btn" + = f.submit 'Create Deploy Key', class: "btn-create btn" = link_to "Cancel", namespace_project_deploy_keys_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 0c10de1604c..5e7c211a424 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -130,9 +130,11 @@ The project can be committed to. %br %strong Once active this project shows up in the search and on the dashboard. - = link_to 'Unarchive', unarchive_namespace_project_path(@project.namespace, @project), - data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, - method: :post, class: "btn btn-success" + + .form-actions + = link_to 'Unarchive project', unarchive_namespace_project_path(@project.namespace, @project), + data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, + method: :post, class: "btn btn-success" - else .panel.panel-warning .panel-heading @@ -144,9 +146,11 @@ It is hidden from the dashboard and doesn't show up in searches. %br %strong Archived projects cannot be committed to! - = link_to 'Archive', archive_namespace_project_path(@project.namespace, @project), - data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, - method: :post, class: "btn btn-warning" + + .form-actions + = link_to 'Archive project', archive_namespace_project_path(@project.namespace, @project), + data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, + method: :post, class: "btn btn-warning" - else .nothing-here-block Only the project owner can archive a project @@ -175,7 +179,7 @@ %li Be careful. Renaming a project's repository can have unintended side effects. %li You will need to update your local repositories to point to the new location. .form-actions - = f.submit 'Rename', class: "btn btn-warning" + = f.submit 'Rename project', class: "btn btn-warning" - if can?(current_user, :change_namespace, @project) .panel.panel-default.panel.panel-danger @@ -194,7 +198,7 @@ %li You can only transfer the project to namespaces you manage. %li You will need to update your local repositories to point to the new location. .form-actions - = f.submit 'Transfer', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) } + = f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) } - else .nothing-here-block Only the project owner can transfer a project diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml index 291703bd60c..2d4311412fd 100644 --- a/app/views/projects/labels/_form.html.haml +++ b/app/views/projects/labels/_form.html.haml @@ -28,6 +28,8 @@   .form-actions - = f.submit 'Save', class: 'btn btn-save js-save-button' + - if @label.persisted? + = f.submit 'Save changes', class: 'btn btn-save js-save-button' + - else + = f.submit 'Create Label', class: 'btn btn-create js-save-button' = link_to "Cancel", namespace_project_labels_path(@project.namespace, @project), class: 'btn btn-cancel' - diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index 46e72e9dee5..cc34f58c54f 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -52,8 +52,8 @@ are the same. - %div - = f.submit 'Compare branches', class: "btn btn-new mr-compare-btn" + .form-actions + = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn" :javascript var source_branch = $("#merge_request_source_branch") diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml index a0324701690..eba03028af8 100644 --- a/app/views/projects/runners/edit.html.haml +++ b/app/views/projects/runners/edit.html.haml @@ -26,4 +26,4 @@ = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control' .help-block You can setup jobs to only use runners with specific tags .form-actions - = f.submit 'Save', class: 'btn btn-save' + = f.submit 'Save changes', class: 'btn btn-save' diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index ea5a2302a56..1b70880043a 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -10,7 +10,7 @@ = render 'shared/service_settings', form: form .form-actions - = form.submit 'Save', class: 'btn btn-save' + = form.submit 'Save changes', class: 'btn btn-save'   - if @service.valid? && @service.activated? - disabled = @service.can_test? ? '':'disabled' diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 2a5fb534a3c..7f32fac6b2c 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -90,7 +90,7 @@ %strong #{link_to 'guidelines for contribution', guide_url} to this repository. - if issuable.new_record? - = f.submit "Submit new #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' + = f.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' - else = f.submit 'Save changes', class: 'btn btn-save' - if issuable.new_record? diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 913b6744844..b46ed0dc0ad 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -27,7 +27,7 @@ - if @snippet.new_record? = f.submit 'Create snippet', class: "btn-create btn" - else - = f.submit 'Save', class: "btn-save btn" + = f.submit 'Save changes', class: "btn-save btn" - if @snippet.project_id = link_to "Cancel", namespace_project_snippets_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 155a5ff58ae..024dc5e72d3 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -39,7 +39,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps select "fix", from: "merge_request_source_branch" select "master", from: "merge_request_target_branch" - click_button "Compare branches" + click_button "Compare branches and continue" expect(page).to have_content "New Merge Request" fill_in "merge_request_title", with: "Merge Request On Forked Project" diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index af2da41badb..202778b180b 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -72,13 +72,13 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps step 'I submit new issue "500 error on profile"' do fill_in "issue_title", with: "500 error on profile" - click_button "Submit new issue" + click_button "Submit issue" end step 'I submit new issue "500 error on profile" with label \'bug\'' do fill_in "issue_title", with: "500 error on profile" select 'bug', from: "Labels" - click_button "Submit new issue" + click_button "Submit issue" end step 'I click link "500 error on profile"' do diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index d5f2c4209a1..822cf0ffe1c 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -86,7 +86,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps select "feature", from: "merge_request_target_branch" click_button "Compare branches" fill_in "merge_request_title", with: "Wiki Feature" - click_button "Submit new merge request" + click_button "Submit merge request" end step 'project "Shop" have "Bug NS-04" open merge request' do -- cgit v1.2.1 From b04c5b069517aa511cdeebbad27169af8940b22c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:34:20 +0100 Subject: Use form-actions where appropriate --- app/views/groups/edit.html.haml | 3 ++- app/views/profiles/accounts/show.html.haml | 15 ++++++++++----- app/views/projects/blob/_new_dir.html.haml | 7 +++---- app/views/projects/blob/_upload.html.haml | 7 +++---- app/views/projects/edit.html.haml | 7 ++++--- app/views/shared/_confirm_modal.html.haml | 2 +- 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index b5afb4ae1c2..8daac585960 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -44,4 +44,5 @@ %br %strong Removed group can not be restored! - = link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove" + .form-actions + = link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove" diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index cd7b1b0fe03..2fd65cc9944 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -23,10 +23,13 @@ %p.cgray - if current_user.private_token = text_field_tag "token", current_user.private_token, class: "form-control" - %div - = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token" - else %span You don`t have one yet. Click generate to fix it. + + .form-actions + - if current_user.private_token + = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token" + - else = f.submit 'Generate', class: "btn btn-default btn-build-token" - unless current_user.ldap_user? @@ -54,7 +57,8 @@ %p Each time you log in you’ll be required to provide your username and password as usual, plus a randomly-generated code from your phone. - %div + + .form-actions = link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success' - if button_based_providers.any? @@ -89,7 +93,7 @@ Saving new username %p.light = user_url(@user) - %div + .form-actions = f.submit 'Save username', class: "btn btn-warning" - if signup_enabled? @@ -104,7 +108,8 @@ - rp = current_user.personal_projects.count - unless rp.zero? %li #{pluralize rp, 'personal project'} will be removed and cannot be restored - = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove" + .form-actions + = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove" - else - if @user.solo_owned_groups.present? %p diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 377f0fa0129..40d0b68c6cc 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -13,10 +13,9 @@ = render 'shared/new_commit_form', placeholder: "Add new directory" - .form-group - .col-sm-offset-2.col-sm-10 - = submit_tag "Create directory", class: 'btn btn-primary btn-create' - = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" + .form-actions + = submit_tag "Create directory", class: 'btn btn-create' + = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript disableButtonIfAnyEmptyField($(".js-create-dir-form"), ".form-control", ".btn-create"); diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 3bb61f0c944..ecc90a30e78 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -16,10 +16,9 @@ = render 'shared/new_commit_form', placeholder: placeholder - .form-group - .col-sm-offset-2.col-sm-10 - = button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all' - = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" + .form-actions + = button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all' + = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file'); diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 5e7c211a424..7bf89d4e550 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -213,7 +213,8 @@ #{link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)}. %br %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source. - = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) } + .form-actions + = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) } - else .nothing-here-block Only the project owner can remove the fork relationship. @@ -226,8 +227,8 @@ Removing the project will delete its repository and all related resources including issues, merge requests etc. %br %strong Removed projects cannot be restored! - - = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } + .form-actions + = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } - else .nothing-here-block Only the project owner can remove a project. diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml index 9bc2d33c27e..34241cd8aad 100644 --- a/app/views/shared/_confirm_modal.html.haml +++ b/app/views/shared/_confirm_modal.html.haml @@ -19,5 +19,5 @@ .form-group = text_field_tag 'confirm_name_input', '', class: 'form-control js-confirm-danger-input' - .form-group + .form-actions = submit_tag 'Confirm', class: "btn btn-danger js-confirm-danger-submit" -- cgit v1.2.1 From da48fdc2a51af0f02ebb22cc13fabf7d1a636690 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:35:19 +0100 Subject: Don't write "Optional" or "Required" unless non-obvious --- app/views/groups/milestones/new.html.haml | 1 - app/views/projects/milestones/_form.html.haml | 1 - app/views/projects/tags/new.html.haml | 4 ++-- app/views/shared/issuable/_form.html.haml | 3 +-- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index 800bac4ef02..ccba58a1ac3 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -15,7 +15,6 @@ = f.label :title, "Title", class: "control-label" .col-sm-10 = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true - %p.hint Required .form-group.milestone-description = f.label :description, "Description", class: "control-label" .col-sm-10 diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index cc29970f07f..c8e0baa1d1b 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -10,7 +10,6 @@ = f.label :title, "Title", class: "control-label" .col-sm-10 = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true - %p.hint Required .form-group.milestone-description = f.label :description, "Description", class: "control-label" .col-sm-10 diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 91e0a5493bd..450d3309e26 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -24,7 +24,7 @@ = label_tag :message, nil, class: 'control-label' .col-sm-10 = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' - .help-block (Optional) Entering a message will create an annotated tag. + .help-block Optionally, enter a message to create an annotated tag. %hr .form-group = label_tag :release_description, 'Release notes', class: 'control-label' @@ -32,7 +32,7 @@ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render 'projects/zen', attr: :release_description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' - .help-block (Optional) You can add release notes to your tag. It will be stored in the GitLab database and shown on the tags page + .help-block Optionally, you can add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page. .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 7f32fac6b2c..ae9d100d1d5 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -6,8 +6,7 @@ %span= msg %br .form-group - = f.label :title, class: 'control-label' do - %strong= 'Title *' + = f.label :title, class: 'control-label' .col-sm-10 = f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off', class: 'form-control pad js-gfm-input js-quick-submit', required: true -- cgit v1.2.1 From a70c507882289a42a9d9b359a730e6f166fedd74 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:36:44 +0100 Subject: Only use input placeholders when they add value --- app/views/projects/blob/_new_dir.html.haml | 2 +- app/views/projects/branches/new.html.haml | 5 +++-- app/views/projects/edit.html.haml | 6 +++--- app/views/projects/new.html.haml | 2 +- app/views/projects/tags/new.html.haml | 6 +++--- app/views/shared/snippets/_form.html.haml | 3 ++- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 40d0b68c6cc..7f95b46efc7 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -9,7 +9,7 @@ .form-group = label_tag :dir_name, 'Directory name', class: 'control-label' .col-sm-10 - = text_field_tag :dir_name, params[:dir_name], placeholder: "Directory name", required: true, class: 'form-control' + = text_field_tag :dir_name, params[:dir_name], required: true, class: 'form-control' = render 'shared/new_commit_form', placeholder: "Add new directory" diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index efb5298a5e5..11567fc4393 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -13,11 +13,12 @@ .form-group = label_tag :branch_name, nil, class: 'control-label' .col-sm-10 - = text_field_tag :branch_name, params[:branch_name], placeholder: 'enter new branch name', required: true, tabindex: 1, class: 'form-control' + = text_field_tag :branch_name, params[:branch_name], required: true, tabindex: 1, autofocus: true, class: 'form-control' .form-group = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10 - = text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control' + = text_field_tag :ref, params[:ref] || @project.default_branch, required: true, tabindex: 2, class: 'form-control' + .help-block Existing branch name, tag, or commit SHA .form-actions = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_branches_path(@project.namespace, @project), class: 'btn btn-cancel' diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 7bf89d4e550..35581ee1aa5 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -14,7 +14,7 @@ = f.label :name, class: 'control-label' do Project name .col-sm-10 - = f.text_field :name, placeholder: "Example Project", class: "form-control", id: "project_name_edit" + = f.text_field :name, class: "form-control", id: "project_name_edit" .form-group @@ -22,7 +22,7 @@ Project description %span.light (optional) .col-sm-10 - = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250 + = f.text_area :description, class: "form-control", rows: 3, maxlength: 250 - if @project.repository.exists? && @project.repository.branch_names.any? .form-group @@ -164,7 +164,7 @@ Project name .col-sm-9 .form-group - = f.text_field :name, placeholder: "Example Project", class: "form-control" + = f.text_field :name, class: "form-control" .form-group = f.label :path, class: 'control-label' do %span Path diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 2670b9d9cda..da0267604e7 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -95,7 +95,7 @@ Description %span.light (optional) .col-sm-10 - = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250, tabindex: 3 + = f.text_area :description, class: "form-control", rows: 3, maxlength: 250, tabindex: 3 = render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project .form-actions diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 450d3309e26..e65ce308d65 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -14,16 +14,16 @@ .form-group = label_tag :tag_name, nil, class: 'control-label' .col-sm-10 - = text_field_tag :tag_name, params[:tag_name], placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control' + = text_field_tag :tag_name, params[:tag_name], required: true, tabindex: 1, autofocus: true, class: 'form-control' .form-group = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10 - = text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control' + = text_field_tag :ref, params[:ref] || @project.default_branch, required: true, tabindex: 2, class: 'form-control' .help-block Branch name or commit SHA .form-group = label_tag :message, nil, class: 'control-label' .col-sm-10 - = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' + = text_field_tag :message, nil, required: false, tabindex: 3, class: 'form-control' .help-block Optionally, enter a message to create an annotated tag. %hr .form-group diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index b46ed0dc0ad..99d93659678 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -8,7 +8,8 @@ .form-group = f.label :title, class: 'control-label' - .col-sm-10= f.text_field :title, placeholder: "Example Snippet", class: 'form-control', required: true + .col-sm-10 + = f.text_field :title, class: 'form-control', required: true, autofocus: true = render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: true, form_model: @snippet -- cgit v1.2.1 From 9bad736fb3ccd0a48cd64fc37538d8b021bce205 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:38:14 +0100 Subject: Remove unnecesary wrapper elements --- app/assets/stylesheets/pages/merge_requests.scss | 4 ---- app/views/projects/_commit_button.html.haml | 4 +--- app/views/projects/issues/_form.html.haml | 5 ++--- app/views/projects/merge_requests/_form.html.haml | 3 +-- app/views/projects/merge_requests/_new_submit.html.haml | 11 +++++------ app/views/projects/notes/_edit_form.html.haml | 5 ++--- app/views/projects/notes/_form.html.haml | 9 ++++----- 7 files changed, 15 insertions(+), 26 deletions(-) diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 08e4bcdf529..8ff0a0cd54b 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -192,10 +192,6 @@ line-height: 1.1; } -.merge-request-form-info { - padding-top: 15px; -} - // hide mr close link for inline diff comment form .diff-file .close-mr-link, .diff-file .reopen-mr-link { diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml index 35f7e7bb34b..2fd3d9e1be4 100644 --- a/app/views/projects/_commit_button.html.haml +++ b/app/views/projects/_commit_button.html.haml @@ -1,6 +1,4 @@ .form-actions - .commit-button-annotation - = button_tag 'Commit Changes', - class: 'btn commit-btn js-commit-button btn-create' + = button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create' = link_to 'Cancel', cancel_path, class: 'btn btn-cancel', data: {confirm: leave_edit_message} diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index e0e26a26dae..6588d9bdbe1 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,6 +1,5 @@ -%div.issue-form-holder - = form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f| - = render 'shared/issuable/form', f: f, issuable: @issue += form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form js-requires-input' } do |f| + = render 'shared/issuable/form', f: f, issuable: @issue :javascript $('.assign-to-me-link').on('click', function(e){ diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 9cf389dbe38..3e4ab09c6d4 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,6 +1,5 @@ = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input' } do |f| - .merge-request-form-info - = render 'shared/issuable/form', f: f, issuable: @merge_request + = render 'shared/issuable/form', f: f, issuable: @merge_request :javascript $('.assign-to-me-link').on('click', function(e){ diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 9fa9cc56126..8b0579f341a 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -11,12 +11,11 @@ = link_to 'Change branches', mr_change_branches_path(@merge_request) %hr = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input' } do |f| - .merge-request-form-info - = render 'shared/issuable/form', f: f, issuable: @merge_request - = f.hidden_field :source_project_id - = f.hidden_field :source_branch - = f.hidden_field :target_project_id - = f.hidden_field :target_branch + = render 'shared/issuable/form', f: f, issuable: @merge_request + = f.hidden_field :source_project_id + = f.hidden_field :source_branch + = f.hidden_field :target_project_id + = f.hidden_field :target_branch .mr-compare.merge-request %ul.merge-request-tabs diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml index a21c019986a..3ccda1b381c 100644 --- a/app/views/projects/notes/_edit_form.html.haml +++ b/app/views/projects/notes/_edit_form.html.haml @@ -6,6 +6,5 @@ = render 'projects/notes/hints' .note-form-actions - .buttons - = f.submit 'Save Comment', class: 'btn btn-primary btn-save btn-grouped js-comment-button' - = link_to 'Cancel', '#', class: 'btn btn-cancel note-edit-cancel' + = f.submit 'Save Comment', class: 'btn btn-primary btn-save btn-grouped js-comment-button' + = link_to 'Cancel', '#', class: 'btn btn-cancel note-edit-cancel' diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 5dd84317e3b..88e711ab534 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -12,8 +12,7 @@ = render 'projects/notes/hints' .error-alert - .note-form-actions - .buttons.clearfix - = f.submit 'Add Comment', class: "btn btn-green comment-btn btn-grouped js-comment-button" - = yield(:note_actions) - %a.btn.grouped.js-close-discussion-note-form Cancel + .note-form-actions.clearfix + = f.submit 'Add Comment', class: "btn btn-create comment-btn btn-grouped js-comment-button" + = yield(:note_actions) + %a.btn.btn-cancel.js-close-discussion-note-form Cancel -- cgit v1.2.1 From 2aeb26bd8823fa681413bd0f64b7b068f41ec4e3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:38:33 +0100 Subject: Use select2 placeholder instead of blank option --- app/views/projects/protected_branches/index.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml index 52b3a50c1e6..2541105b007 100644 --- a/app/views/projects/protected_branches/index.html.haml +++ b/app/views/projects/protected_branches/index.html.haml @@ -22,7 +22,7 @@ .form-group = f.label :name, "Branch", class: 'control-label' .col-sm-10 - = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "select2"}) + = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: true}, {class: "select2", data: {placeholder: "Select branch"}}) .form-group .col-sm-offset-2.col-sm-10 .checkbox @@ -33,4 +33,3 @@ .form-actions = f.submit 'Protect', class: "btn-create btn" = render 'branches_list' - -- cgit v1.2.1 From 72ab3b17d338c15f27e3bb1fbd0e7cefaa10b94d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:39:39 +0100 Subject: Use js-requires-input where appropriate --- app/views/profiles/keys/_form.html.haml | 7 +++---- app/views/projects/blob/_new_dir.html.haml | 3 +-- app/views/projects/deploy_keys/_form.html.haml | 7 +++---- app/views/projects/tags/new.html.haml | 3 +-- app/views/shared/_new_commit_form.html.haml | 2 +- app/views/shared/snippets/_form.html.haml | 2 +- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml index b76a5b636ac..2a8800de60e 100644 --- a/app/views/profiles/keys/_form.html.haml +++ b/app/views/profiles/keys/_form.html.haml @@ -1,5 +1,5 @@ %div - = form_for [:profile, @key], html: { class: 'form-horizontal' } do |f| + = form_for [:profile, @key], html: { class: 'form-horizontal js-requires-input' } do |f| - if @key.errors.any? .alert.alert-danger %ul @@ -9,12 +9,11 @@ .form-group = f.label :key, class: 'control-label' .col-sm-10 - = f.text_area :key, class: "form-control", rows: 8 + = f.text_area :key, class: "form-control", rows: 8, autofocus: true, required: true .form-group = f.label :title, class: 'control-label' - .col-sm-10= f.text_field :title, class: "form-control" + .col-sm-10= f.text_field :title, class: "form-control", required: true .form-actions = f.submit 'Add key', class: "btn btn-create" = link_to "Cancel", profile_keys_path, class: "btn btn-cancel" - diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 7f95b46efc7..fc6c9f5fd09 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -5,7 +5,7 @@ %a.close{href: "#", "data-dismiss" => "modal"} × %h3.page-title Create New Directory .modal-body - = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form' do + = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-requires-input' do .form-group = label_tag :dir_name, 'Directory name', class: 'control-label' .col-sm-10 @@ -18,5 +18,4 @@ = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript - disableButtonIfAnyEmptyField($(".js-create-dir-form"), ".form-control", ".btn-create"); new NewCommitForm($('.js-create-dir-form')) diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index 085c9149b11..5e182af2669 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -1,5 +1,5 @@ %div - = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal' } do |f| + = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal js-requires-input' } do |f| -if @key.errors.any? .alert.alert-danger %ul @@ -8,16 +8,15 @@ .form-group = f.label :title, class: "control-label" - .col-sm-10= f.text_field :title, class: 'form-control' + .col-sm-10= f.text_field :title, class: 'form-control', autofocus: true, required: true .form-group = f.label :key, class: "control-label" .col-sm-10 %p.light Paste a machine public key here. Read more about how to generate it = link_to "here", help_page_path("ssh", "README") - = f.text_area :key, class: "form-control thin_area", rows: 5 + = f.text_area :key, class: "form-control thin_area", rows: 5, required: true .form-actions = f.submit 'Create Deploy Key', class: "btn-create btn" = link_to "Cancel", namespace_project_deploy_keys_path(@project.namespace, @project), class: "btn btn-cancel" - diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index e65ce308d65..58d2e5c7136 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -10,7 +10,7 @@ New Tag %hr -= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do += form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form js-requires-input" do .form-group = label_tag :tag_name, nil, class: 'control-label' .col-sm-10 @@ -38,7 +38,6 @@ = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' :javascript - disableButtonIfAnyEmptyField($("#new-tag-form"), ".form-control", ".btn-create"); var availableTags = #{@project.repository.ref_names.to_json}; $("#ref").autocomplete({ diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml index ce52614e868..31b02ed93d0 100644 --- a/app/views/shared/_new_commit_form.html.haml +++ b/app/views/shared/_new_commit_form.html.haml @@ -4,7 +4,7 @@ .form-group.branch = label_tag 'new_branch', 'Target branch', class: 'control-label' .col-sm-10 - = text_field_tag 'new_branch', @new_branch || @ref, class: "form-control js-new-branch" + = text_field_tag 'new_branch', @new_branch || @ref, required: true, class: "form-control js-new-branch" .form-group.js-create-merge-request-form-group .col-sm-offset-2.col-sm-10 diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 99d93659678..1041eccd1df 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -1,5 +1,5 @@ .snippet-form-holder - = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form" } do |f| + = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input" } do |f| - if @snippet.errors.any? .alert.alert-danger %ul -- cgit v1.2.1 From dccc22775472a7053b85f496011808c35fd6d62c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:40:01 +0100 Subject: Use autofocus where appropriate --- app/views/groups/milestones/new.html.haml | 2 +- app/views/projects/labels/_form.html.haml | 2 +- app/views/projects/milestones/_form.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index ccba58a1ac3..3894a0ece74 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -14,7 +14,7 @@ .form-group = f.label :title, "Title", class: "control-label" .col-sm-10 - = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true + = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true, autofocus: true .form-group.milestone-description = f.label :description, "Description", class: "control-label" .col-sm-10 diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml index 2d4311412fd..5ce2a7b985d 100644 --- a/app/views/projects/labels/_form.html.haml +++ b/app/views/projects/labels/_form.html.haml @@ -10,7 +10,7 @@ .form-group = f.label :title, class: 'control-label' .col-sm-10 - = f.text_field :title, class: "form-control js-quick-submit", required: true + = f.text_field :title, class: "form-control js-quick-submit", required: true, autofocus: true .form-group = f.label :color, "Background color", class: 'control-label' .col-sm-10 diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index c8e0baa1d1b..39aa2437e18 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -9,7 +9,7 @@ .form-group = f.label :title, "Title", class: "control-label" .col-sm-10 - = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true + = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true, autofocus: true .form-group.milestone-description = f.label :description, "Description", class: "control-label" .col-sm-10 -- cgit v1.2.1 From cdd4c331b36e75b2b01a1648f1e5563c4afc5a49 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:40:18 +0100 Subject: Use select2 instead of regular selectbox for profile public email --- app/views/profiles/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index faab12ca0b9..9459d8a6295 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -43,7 +43,7 @@ .form-group = f.label :public_email, class: "control-label" .col-sm-10 - = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show in profile'}, class: "form-control" + = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show on profile'}, class: "select2" %span.help-block This email will be displayed on your public profile. .form-group = f.label :skype, class: "control-label" -- cgit v1.2.1 From 8d98245783832a4d6617fb7076343469918ed273 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:40:36 +0100 Subject: Remove `.git` suffix to project path field. --- app/views/projects/edit.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 35581ee1aa5..b28ada909b7 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -174,7 +174,6 @@ .input-group-addon #{URI.join(root_url, @project.namespace.path)}/ = f.text_field :path, class: 'form-control' - %span.input-group-addon .git %ul %li Be careful. Renaming a project's repository can have unintended side effects. %li You will need to update your local repositories to point to the new location. -- cgit v1.2.1 From d0166334ba9997b5eed89d6f54c99f7bfbe9c816 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:40:52 +0100 Subject: Add `http://gitlab.example.com/u/` prefix to "Change username" field. --- app/views/profiles/accounts/show.html.haml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 2fd65cc9944..319bdd57c39 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -85,14 +85,15 @@ %p Changing your username will change path to all personal projects! %div - = f.text_field :username, required: true, class: 'form-control' + .input-group + .input-group-addon + = "#{root_url}u/" + = f.text_field :username, required: true, class: 'form-control'   .loading-gif.hide %p = icon('spinner spin') Saving new username - %p.light - = user_url(@user) .form-actions = f.submit 'Save username', class: "btn btn-warning" -- cgit v1.2.1 From c0e614d1c46cd273f83385ef61c76200d492d0a4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:41:02 +0100 Subject: Rename variable --- app/views/projects/branches/new.html.haml | 4 ++-- app/views/projects/tags/new.html.haml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 11567fc4393..31943a2407a 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -24,9 +24,9 @@ = link_to 'Cancel', namespace_project_branches_path(@project.namespace, @project), class: 'btn btn-cancel' :javascript - var availableTags = #{@project.repository.ref_names.to_json}; + var availableRefs = #{@project.repository.ref_names.to_json}; $("#ref").autocomplete({ - source: availableTags, + source: availableRefs, minLength: 1 }); diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 58d2e5c7136..9c9bfa3f55f 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -38,9 +38,9 @@ = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' :javascript - var availableTags = #{@project.repository.ref_names.to_json}; + var availableRefs = #{@project.repository.ref_names.to_json}; $("#ref").autocomplete({ - source: availableTags, + source: availableRefs, minLength: 1 }); -- cgit v1.2.1 From 58dad2a9dadea647b5665e1de6a6e997484b96b1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:43:23 +0100 Subject: Remove bottom margin from page-titles --- app/assets/stylesheets/framework/typography.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 2c4a58c8db1..aef338cfa56 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -181,6 +181,10 @@ body { line-height: 1.3; font-size: 1.25em; font-weight: 600; + + &:last-child { + margin-bottom: 0; + } } .page-title-empty { -- cgit v1.2.1 From 3ae7dba931b880b8090edffb247ebfe32d26648e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:46:15 +0100 Subject: Use gl-padding instead of 15px/20px where appropriate --- app/assets/stylesheets/framework.scss | 3 ++- app/assets/stylesheets/framework/common.scss | 10 +++++++++- app/assets/stylesheets/framework/files.scss | 4 +--- app/assets/stylesheets/framework/panels.scss | 15 +++++++++++++++ app/assets/stylesheets/framework/tables.scss | 2 ++ app/assets/stylesheets/pages/merge_requests.scss | 2 +- app/assets/stylesheets/pages/note_form.scss | 7 +++---- app/assets/stylesheets/pages/projects.scss | 2 +- app/views/profiles/notifications/show.html.haml | 4 +--- app/views/projects/compare/show.html.haml | 4 ++-- app/views/projects/merge_requests/_new_compare.html.haml | 3 +-- app/views/projects/merge_requests/_show.html.haml | 2 +- app/views/shared/issuable/_context.html.haml | 6 +++--- 13 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 app/assets/stylesheets/framework/panels.scss diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 1ec9d2fd84f..48a4971c8fc 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -1,9 +1,9 @@ @import "framework/fonts"; @import "framework/variables"; @import "framework/mixins"; -@import "framework/layout"; @import 'framework/tw_bootstrap_variables'; @import 'framework/tw_bootstrap'; +@import "framework/layout"; @import "framework/avatar.scss"; @import "framework/blocks.scss"; @@ -25,6 +25,7 @@ @import "framework/markdown_area.scss"; @import "framework/mobile.scss"; @import "framework/pagination.scss"; +@import "framework/panels.scss"; @import "framework/selects.scss"; @import "framework/sidebar.scss"; @import "framework/tables.scss"; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 61689aff57e..61ecd58e6c5 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -7,7 +7,7 @@ /** COMMON CLASSES **/ .prepend-top-10 { margin-top:10px } -.prepend-top-default { margin-top: $gl-padding; } +.prepend-top-default { margin-top: $gl-padding !important; } .prepend-top-20 { margin-top:20px } .prepend-left-10 { margin-left:10px } .prepend-left-20 { margin-left:20px } @@ -52,6 +52,10 @@ pre { } } +hr { + margin: $gl-padding 0; +} + .dropdown-menu > li > a { text-shadow: none; } @@ -433,3 +437,7 @@ table { .space-right { margin-right: 10px; } + +.alert, .progress { + margin-bottom: $gl-padding; +} diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 35db00281e5..6bf2857e83a 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -8,7 +8,6 @@ border: none; border-top: 1px solid #E7E9EE; border-bottom: 1px solid #E7E9EE; - margin-bottom: 1em; &.readme-holder { border-bottom: 0; @@ -25,7 +24,7 @@ text-shadow: 0 1px 1px #fff; margin: 0; text-align: left; - padding: 10px 15px; + padding: 10px $gl-padding; .file-actions { float: right; @@ -171,4 +170,3 @@ } } } - diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss new file mode 100644 index 00000000000..406aff3d72c --- /dev/null +++ b/app/assets/stylesheets/framework/panels.scss @@ -0,0 +1,15 @@ +.panel { + margin-bottom: $gl-padding; + + .panel-heading { + padding: 10px $gl-padding; + } + .panel-body { + padding: $gl-padding; + + .form-actions { + margin: -$gl-padding; + margin-top: $gl-padding; + } + } +} diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 66e16e8df75..793ab3d9bb9 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -6,6 +6,8 @@ table { &.table { + margin-bottom: $gl-padding; + .dropdown-menu a { text-decoration: none; } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 08e4bcdf529..177cf6ca45b 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -87,7 +87,7 @@ .mr-widget-body, .ci_widget, .mr-widget-footer { - padding: 15px; + padding: $gl-padding; } .normal { diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 268fc995aa7..02db44c8eb0 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -7,6 +7,7 @@ } .reply-btn { @extend .btn-primary; + margin: 10px $gl-padding; } .diff-file .diff-content { tr.line_holder:hover { @@ -38,9 +39,8 @@ } .new_note, .edit_note { - .buttons { - margin-top: 8px; - margin-bottom: 3px; + .note-form-actions { + margin-top: $gl-padding; } .note-preview-holder { @@ -150,7 +150,6 @@ .discussion-reply-holder { background: $background-color; - padding: 10px 15px; border-top: 1px solid $border-color; } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 4a0fe546844..b3054e36247 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -376,7 +376,7 @@ table.table.protected-branches-list tr.no-border { .project-stats { text-align: center; - margin-top: 15px; + margin-top: $gl-padding; margin-bottom: 0; padding-top: 10px; padding-bottom: 4px; diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index 8eebd96b674..d4d9f246273 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -53,9 +53,7 @@ .form-actions = f.submit 'Save changes', class: "btn btn-create" -.clearfix - %hr -.row.all-notifications +.row.all-notifications.prepend-top-default .col-md-6 %p You can also specify notification level per group or per project. diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index 39755efd2fd..51088a7dea8 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -7,11 +7,11 @@ = render "form" - if @commits.present? - .prepend-top-20 + .prepend-top-default = render "projects/commits/commit_list" = render "projects/diffs/diffs", diffs: @diffs, project: @project - else - .light-well.prepend-top-20 + .light-well.prepend-top-default .center %h4 There isn't anything to compare. diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index d9eff1f9320..6def9d6266f 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -37,7 +37,7 @@ %h4 Compare failed %p We can't compare selected branches. It may be because of huge diff. Please try again or select different branches. - else - .light-well.append-bottom-10 + .light-well.append-bottom-default .center %h4 There isn't anything to merge. @@ -86,4 +86,3 @@ return; } }); - diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index eeaa72ed21b..94bd154aebb 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -8,7 +8,7 @@ .merge-request-details.issuable-details = render "projects/merge_requests/show/mr_title" = render "projects/merge_requests/show/mr_box" - .append-bottom-20.mr-source-target.prepend-top-default + .append-bottom-default.mr-source-target.prepend-top-default - if @merge_request.open? .pull-right - if @merge_request.source_branch_exists? diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index be66256c7b0..f1646b4ce64 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -1,5 +1,5 @@ = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| - %div.prepend-top-20 + %div.prepend-top-default .issuable-context-title %label Assignee: @@ -11,7 +11,7 @@ - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true) - %div.prepend-top-20.clearfix + %div.prepend-top-default.clearfix .issuable-context-title %label Milestone: @@ -31,7 +31,7 @@ - if current_user - subscribed = issuable.subscribed?(current_user) - %div.prepend-top-20.clearfix + %div.prepend-top-default.clearfix .issuable-context-title %label Subscription: -- cgit v1.2.1 From c95b64d1cad8aa8f0861feba0c919ae3d1722df0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:46:55 +0100 Subject: Fix alignment of buttons in issue header --- app/assets/stylesheets/framework/issue_box.scss | 5 +++-- app/assets/stylesheets/pages/issuable.scss | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss index 93377e45e70..f12d68b5a1f 100644 --- a/app/assets/stylesheets/framework/issue_box.scss +++ b/app/assets/stylesheets/framework/issue_box.scss @@ -7,8 +7,9 @@ .issue-box { @include border-radius(2px); - display: inline-block; - padding: 10px $gl-padding; + display: block; + float: left; + padding: 0 $gl-padding; font-weight: normal; margin-right: 10px; font-size: $gl-font-size; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 3a08ee70bc7..51d8e5b4657 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -51,11 +51,12 @@ .issuable-details { .page-title { - margin-top: -15px; - padding: 10px 0; + margin-top: -$gl-padding; + padding: 7px 0; margin-bottom: 0; color: #5c5d5e; font-size: 16px; + line-height: 42px; .author { color: #5c5d5e; -- cgit v1.2.1 From aa01b23937d05d4110cf24683bbe96dcc77c730c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:47:19 +0100 Subject: Fix padding around editor path, input and select box. --- app/assets/stylesheets/pages/editor.scss | 46 ++++++++++++------------------- app/views/projects/blob/_editor.html.haml | 22 +++++++-------- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index e2c521af91e..39d916cd336 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -19,48 +19,38 @@ color: #B94A48; } } - .commit-button-annotation { - display: inline-block; - margin: 0; - padding: 2px; - - > * { - float: left; - } - - .message { - display: inline-block; - margin: 5px 8px 0 8px; - } - } .file-title { @extend .monospace; + + line-height: 42px; + padding-top: 7px; + padding-bottom: 7px; } .editor-ref { background: $background-color; - padding: 11px 15px; + padding-right: $gl-padding; border-right: 1px solid $border-color; - display: inline-block; - margin: -5px -5px; + display: block; + float: left; margin-right: 10px; } .editor-file-name { - .new-file-name { - display: inline-block; - width: 450px; - } + @extend .monospace; + + float: left; + margin-right: 10px; + } - .form-control { - margin-top: -3px; - } + .new-file-name { + display: inline-block; + width: 450px; + float: left; } - .form-actions { - margin: -$gl-padding; - margin-top: 0; - padding: $gl-padding + .select2 { + float: right; } } diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index f1ad0c3c403..333f5d470ed 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -1,19 +1,19 @@ -.file-holder.file - .file-title +.file-holder.file.append-bottom-default + .file-title.clearfix .editor-ref - %i.fa.fa-code-fork + = icon('code-fork') = ref %span.editor-file-name - - if @path - %span.monospace - = @path + = @path - - if current_action?(:new) || current_action?(:create) + - if current_action?(:new) || current_action?(:create) + %span.editor-file-name \/ - = text_field_tag 'file_name', params[:file_name], placeholder: "File name", - required: true, class: 'form-control new-file-name js-quick-submit' - .pull-right - = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control' + = text_field_tag 'file_name', params[:file_name], placeholder: "File name", + required: true, class: 'form-control new-file-name js-quick-submit' + + .pull-right + = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2' .file-content.code %pre.js-edit-mode-pane#editor -- cgit v1.2.1 From 0159d78a3b7dbc58f74c90c41adf8ef3049ed0aa Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:48:06 +0100 Subject: Remove padding around form-actions --- app/assets/stylesheets/framework/forms.scss | 7 ++++--- app/views/profiles/notifications/show.html.haml | 2 +- app/views/projects/releases/edit.html.haml | 2 +- app/views/shared/issuable/_form.html.haml | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 0edfe24f195..ddb522bb638 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -22,9 +22,10 @@ input[type='text'].danger { } .form-actions { - padding: 17px 20px 18px; - margin-top: 18px; - margin-bottom: 18px; + margin: -$gl-padding; + margin-top: 0; + margin-bottom: -$gl-padding; + padding: $gl-padding; background-color: $background-color; border-top: 1px solid $border-color; } diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index d4d9f246273..0bcadc965fa 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -50,7 +50,7 @@ Watch %p You will receive notifications for any activity - .form-actions + .gray-content-block = f.submit 'Save changes', class: "btn btn-create" .row.all-notifications.prepend-top-default diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index f516b65ecd0..bc80f2f29ad 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -14,6 +14,6 @@ = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' .error-alert - .prepend-top-default + .form-actions.prepend-top-default = f.submit 'Save changes', class: 'btn btn-save' = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 0fc74d7d2b1..7558b37f83f 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -93,7 +93,8 @@ %p.help-block = link_to 'Change branches', mr_change_branches_path(@merge_request) -.form-actions +- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?) +.gray-content-block{class: (is_footer ? "footer-block" : "middle-block")} - if !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) && !issuable.persisted? %p Please review the -- cgit v1.2.1 From 3ad1d320ef3f3aa5a0896a9b30a2fba791696f58 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:48:51 +0100 Subject: Remove duplicated styling for center top menus --- app/assets/stylesheets/pages/commit.scss | 13 ------------ app/assets/stylesheets/pages/merge_requests.scss | 23 ++-------------------- app/views/projects/commit/_ci_menu.html.haml | 2 +- app/views/projects/diffs/_diffs.html.haml | 2 +- .../projects/merge_requests/_discussion.html.haml | 2 +- .../projects/merge_requests/_new_submit.html.haml | 5 ++--- app/views/projects/merge_requests/_show.html.haml | 2 +- .../merge_requests/show/_commits.html.haml | 2 +- 8 files changed, 9 insertions(+), 42 deletions(-) diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index a0e5f7554ed..74bbe0880f7 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -108,16 +108,3 @@ z-index: 2; } } - -.commit-ci-menu { - padding: 0; - margin: 0; - list-style: none; - margin-top: 5px; - height: 56px; - margin: -16px; - padding: 16px; - text-align: center; - margin-top: 0px; - margin-bottom: 2px; -} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 177cf6ca45b..017a86bcd9a 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -4,7 +4,6 @@ */ .mr-state-widget { background: #F7F8FA; - margin-bottom: 20px; color: $gl-gray; border: 1px solid #dce0e6; @include border-radius(2px); @@ -116,26 +115,8 @@ } } -.merge-request .merge-request-tabs { - @include nav-menu; - margin: -$gl-padding; - padding: $gl-padding; - text-align: center; - margin-bottom: 1px; -} - -// Mobile -@media (max-width: 480px) { - .merge-request .merge-request-tabs { - margin: 0; - padding: 0; - - li { - a { - padding: 0; - } - } - } +.merge-request-details { + margin-bottom: $gl-padding; } .mr_source_commit, diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml index c73ba74f5ef..76dc87a8824 100644 --- a/app/views/projects/commit/_ci_menu.html.haml +++ b/app/views/projects/commit/_ci_menu.html.haml @@ -1,4 +1,4 @@ -%ul.center-top-menu.commit-ci-menu +%ul.center-top-menu.no-top.no-bottom.commit-ci-menu = nav_link(path: 'commit#show') do = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do Changes diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 416fb4da071..f9d661d59d2 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -3,7 +3,7 @@ - diff_files = safe_diff_files(diffs) -.gray-content-block.second-block.oneline-block +.gray-content-block.middle-block.oneline-block .inline-parallel-buttons .btn-group = inline_diff_btn diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 2b3c3eff5e4..4a192aeb2cd 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -7,7 +7,7 @@ = render 'shared/show_aside' -.gray-content-block.second-block.oneline-block +.gray-content-block.middle-block.oneline-block .row .col-md-9 .votes-holder.pull-right diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 6244d3ba0b4..72132344c88 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -19,7 +19,7 @@ = f.hidden_field :target_branch .mr-compare.merge-request - %ul.merge-request-tabs + %ul.merge-request-tabs.center-top-menu.no-top.no-bottom %li.commits-tab = link_to url_for(params), data: {target: '#commits', action: 'commits', toggle: 'tab'} do Commits @@ -31,7 +31,7 @@ .tab-content #commits.commits.tab-pane - = render "projects/commits/commits", project: @project + = render "projects/merge_requests/show/commits" #diffs.diffs.tab-pane.active - if @diffs.present? = render "projects/diffs/diffs", diffs: @diffs, project: @project @@ -57,4 +57,3 @@ diffs_loaded: true, commits_loaded: true }); - diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 94bd154aebb..e7eb0066594 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -40,7 +40,7 @@ = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" - if @commits.present? - %ul.merge-request-tabs + %ul.merge-request-tabs.center-top-menu.no-top.no-bottom %li.notes-tab = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#notes', action: 'notes', toggle: 'tab'} do Discussion diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml index 478054db517..7f904ec42a0 100644 --- a/app/views/projects/merge_requests/show/_commits.html.haml +++ b/app/views/projects/merge_requests/show/_commits.html.haml @@ -1,4 +1,4 @@ -.gray-content-block.second-block.oneline-block +.gray-content-block.middle-block.oneline-block = icon("sort-amount-desc") Most recent commits displayed first -- cgit v1.2.1 From 1c595681e5b60cfdf402abc6fd43c282897b6260 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:49:29 +0100 Subject: Remove double border between gray blocks --- app/assets/stylesheets/framework/blocks.scss | 4 ++++ app/views/projects/commit/show.html.haml | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 1635df9c97b..dec50a0e0f6 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -114,3 +114,7 @@ right: 10px; } } + +.block-connector { + margin-top: -1px; +} diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 85e203cbe57..069b8b1f169 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,6 +1,9 @@ - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" = render "projects/commits/header_title" = render "commit_box" -= render "ci_menu" if @ci_commit +- if @ci_commit + = render "ci_menu" +- else + %div.block-connector = render "projects/diffs/diffs", diffs: @diffs, project: @project = render "projects/notes/notes_with_form" -- cgit v1.2.1 From 9ea72430921efaa4a1a79ea21c54763bda8bba78 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:49:46 +0100 Subject: Fix padding of no readme and empty repo messages --- app/views/projects/_readme.html.haml | 25 +++++++++++++------------ app/views/projects/empty.html.haml | 13 ++++++------- app/views/projects/show.html.haml | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml index b5ef0aca540..d1191928d4f 100644 --- a/app/views/projects/_readme.html.haml +++ b/app/views/projects/_readme.html.haml @@ -7,15 +7,16 @@ = cache(readme_cache_key) do = render_readme(readme) - else - %h3.page-title - This project does not have README yet - - if can?(current_user, :push_code, @project) - %p.slead - A - %code README - file contains information about other files in a repository and is commonly - distributed with computer software, forming part of its documentation. - %br - We recommend you to - = link_to "add README", new_readme_path, class: 'underlined-link' - file to the repository and GitLab will render it here instead of this message. + .gray-content-block.second-block.center + %h3.page-title + This project does not have README yet + - if can?(current_user, :push_code, @project) + %p + A + %code README + file contains information about other files in a repository and is commonly + distributed with computer software, forming part of its documentation. + %p + We recommend you to + = link_to "add README", new_readme_path, class: 'underlined-link' + file to the repository and GitLab will render it here instead of this message. diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index c3858e78cad..950ab33825e 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -5,17 +5,16 @@ = render "home_panel" -.gray-content-block.center +.gray-content-block.second-block.center %h3.page-title The repository for this project is empty - - if can?(current_user, :download_code, @project) + - if can?(current_user, :push_code, @project) %p If you already have files you can push them using command line instructions below. - %br - - if can?(current_user, :push_code, @project) - Otherwise you can start with - = link_to "adding README", new_readme_path, class: 'underlined-link' - file to this project. + %p + Otherwise you can start with + = link_to "adding README", new_readme_path, class: 'underlined-link' + file to this project. - if can?(current_user, :download_code, @project) .prepend-top-20 diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 585caf674c9..9c7a5584da9 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -11,7 +11,7 @@ = render "home_panel" -.project-stats.gray-content-block +.project-stats.gray-content-block.second-block %ul.nav.nav-pills %li = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do -- cgit v1.2.1 From ba4bf27fe486af1790844ac40bc188c6462720b8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:49:59 +0100 Subject: Use table-holder where appropriate --- app/views/profiles/applications.html.haml | 84 ++++++++++++++-------------- app/views/profiles/keys/_key_table.html.haml | 16 +++--- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml index 2342936a5d5..0436c2213da 100644 --- a/app/views/profiles/applications.html.haml +++ b/app/views/profiles/applications.html.haml @@ -15,24 +15,25 @@ .pull-right = link_to 'New Application', new_oauth_application_path, class: 'btn btn-success' - if @applications.any? - %table.table.table-striped - %thead - %tr - %th Name - %th Callback URL - %th Clients - %th - %th - %tbody - - @applications.each do |application| - %tr{:id => "application_#{application.id}"} - %td= link_to application.name, oauth_application_path(application) - %td - - application.redirect_uri.split.each do |uri| - %div= uri - %td= application.access_tokens.count - %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-sm' - %td= render 'doorkeeper/applications/delete_form', application: application + .table-holder + %table.table.table-striped + %thead + %tr + %th Name + %th Callback URL + %th Clients + %th + %th + %tbody + - @applications.each do |application| + %tr{:id => "application_#{application.id}"} + %td= link_to application.name, oauth_application_path(application) + %td + - application.redirect_uri.split.each do |uri| + %div= uri + %td= application.access_tokens.count + %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-sm' + %td= render 'doorkeeper/applications/delete_form', application: application .oauth-authorized-applications.prepend-top-20 - if user_oauth_applications? @@ -40,29 +41,30 @@ Authorized applications - if @authorized_tokens.any? - %table.table.table-striped - %thead - %tr - %th Name - %th Authorized At - %th Scope - %th - %tbody - - @authorized_apps.each do |app| - - token = app.authorized_tokens.order('created_at desc').first - %tr{:id => "application_#{app.id}"} - %td= app.name - %td= token.created_at - %td= token.scopes - %td= render 'doorkeeper/authorized_applications/delete_form', application: app - - @authorized_anonymous_tokens.each do |token| + .table-holder + %table.table.table-striped + %thead %tr - %td - Anonymous - %div.help-block - %em Authorization was granted by entering your username and password in the application. - %td= token.created_at - %td= token.scopes - %td= render 'doorkeeper/authorized_applications/delete_form', token: token + %th Name + %th Authorized At + %th Scope + %th + %tbody + - @authorized_apps.each do |app| + - token = app.authorized_tokens.order('created_at desc').first + %tr{:id => "application_#{app.id}"} + %td= app.name + %td= token.created_at + %td= token.scopes + %td= render 'doorkeeper/authorized_applications/delete_form', application: app + - @authorized_anonymous_tokens.each do |token| + %tr + %td + Anonymous + %div.help-block + %em Authorization was granted by entering your username and password in the application. + %td= token.created_at + %td= token.scopes + %td= render 'doorkeeper/authorized_applications/delete_form', token: token - else %p.light You don't have any authorized applications diff --git a/app/views/profiles/keys/_key_table.html.haml b/app/views/profiles/keys/_key_table.html.haml index ef0075aad3b..8c9d546af4c 100644 --- a/app/views/profiles/keys/_key_table.html.haml +++ b/app/views/profiles/keys/_key_table.html.haml @@ -1,6 +1,6 @@ - is_admin = defined?(admin) ? true : false -.panel.panel-default - - if @keys.any? +- if @keys.any? + .table-holder %table.table %thead.panel-heading %tr @@ -11,9 +11,9 @@ %tbody - @keys.each do |key| = render 'profiles/keys/key', key: key, is_admin: is_admin - - else - .nothing-here-block - - if is_admin - User has no ssh keys - - else - There are no SSH keys with access to your account. +- else + .nothing-here-block + - if is_admin + User has no ssh keys + - else + There are no SSH keys with access to your account. -- cgit v1.2.1 From d5fd9b26b13678a9e09a56890af10babe46362f0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:50:12 +0100 Subject: Remove style duplication between snippet and issue detail pages --- app/assets/stylesheets/pages/snippets.scss | 44 ++++------------------------- app/views/shared/snippets/_header.html.haml | 7 +++-- 2 files changed, 9 insertions(+), 42 deletions(-) diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index 242783a7b7e..bb74e50151d 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -27,56 +27,22 @@ } .snippet-holder { - .snippet-details { - .page-title { - margin-top: -15px; - padding: 10px 0; - margin-bottom: 0; - color: #5c5d5e; - font-size: 16px; - - .author { - color: #5c5d5e; - } - - .snippet-id { - color: #5c5d5e; - } - } - - .snippet-title { - margin: 0; - font-size: 23px; - color: #313236; - } - - @media (max-width: $screen-md-max) { - .new-snippet-link { - display: none; - } - } - - @media (max-width: $screen-sm-max) { - .creator, - .page-title .btn-close { - display: none; - } - } - } + margin-bottom: -$gl-padding; .file-holder { border-top: 0; } } - .snippet-box { @include border-radius(2px); - display: inline-block; - padding: 10px $gl-padding; + display: block; + float: left; + padding: 0 $gl-padding; font-weight: normal; margin-right: 10px; font-size: $gl-font-size; border: 1px solid; + line-height: 40px; } diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 0a4a790ec5e..35241029288 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,9 +1,9 @@ -.snippet-details +.issuable-details .page-title .snippet-box{class: visibility_level_color(@snippet.visibility_level)} = visibility_level_icon(@snippet.visibility_level) = visibility_level_label(@snippet.visibility_level) - %span.snippet-id Snippet ##{@snippet.id} + Snippet ##{@snippet.id} %span.creator · created by #{link_to_member(@project, @snippet.author, size: 24)} · @@ -19,6 +19,7 @@ = render "projects/snippets/actions" - else = render "snippets/actions" + .gray-content-block.middle-block - %h2.snippet-title + %h2.issue-title = gfm escape_once(@snippet.title) -- cgit v1.2.1 From 09396b2eaa529a7c9049e482142c3885c4510b90 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:50:41 +0100 Subject: Render all form controls at the same height --- app/assets/stylesheets/framework/forms.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index ddb522bb638..80283a56ec3 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -74,6 +74,8 @@ label { .form-control { @include box-shadow(none); + height: 42px; + padding: 8px $gl-padding; } .wiki-content { -- cgit v1.2.1 From 8196d269ebcc15925bf532a9edcccf75248bfffb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:50:50 +0100 Subject: No bottom margin for form help blocks --- app/assets/stylesheets/framework/forms.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 80283a56ec3..cc92966c458 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -95,3 +95,7 @@ label { background-color: #f7f8fa; } } + +.help-block { + margin-bottom: 0; +} -- cgit v1.2.1 From ac389c6a3ec7f54174edb243b54b279a4916c30a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:51:10 +0100 Subject: Consistent styling for dashboard groups gray block --- app/views/dashboard/groups/index.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml index f3f3f58111e..d5b7e729e7b 100644 --- a/app/views/dashboard/groups/index.html.haml +++ b/app/views/dashboard/groups/index.html.haml @@ -8,8 +8,8 @@ = link_to new_group_path, class: "btn btn-new" do %i.fa.fa-plus New Group - .title Welcome to the groups! - Group members have access to all group projects. + .oneline + Group members have access to all group projects. %ul.content-list - @group_members.each do |group_member| -- cgit v1.2.1 From 6ec9999a8e49e5374fe9e10dc60dc98a7ede1075 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:51:25 +0100 Subject: Use consistent border color --- app/assets/stylesheets/pages/note_form.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 02db44c8eb0..e1a72af0013 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -79,8 +79,8 @@ padding: $gl-padding; margin-left: -$gl-padding; margin-right: -$gl-padding; - border-right: 1px solid #ECEEF1; - border-top: 1px solid #ECEEF1; + border-right: 1px solid $border-color; + border-top: 1px solid $border-color; margin-bottom: -$gl-padding; } -- cgit v1.2.1 From aec4b290fd154ac382b49aefed4e34386785630f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:51:37 +0100 Subject: Remove unused style --- app/assets/stylesheets/pages/projects.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index b3054e36247..ac2d50f9c18 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -18,10 +18,6 @@ } } -.project-edit-content { - padding: 7px; -} - .project-name-holder { .help-inline { vertical-align: top; -- cgit v1.2.1 From 9517b6211782619ba4d4f9f1322230bd833c0a2c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:20:11 +0100 Subject: Link MR list item branch name to branch --- app/assets/stylesheets/framework/common.scss | 4 ---- app/views/projects/merge_requests/_merge_request.html.haml | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 61689aff57e..1c7c31b02a9 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -337,10 +337,6 @@ table { text-align: center; } -.task-status { - margin-left: 10px; -} - #nprogress .spinner { top: 15px !important; right: 10px !important; diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 8246a432c77..60dd4f4f87c 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -44,8 +44,8 @@ = link_to_label(label, project: merge_request.project) - if merge_request.target_project.default_branch != merge_request.target_branch   - %span - %i.fa.fa-code-fork + = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do + = icon('code-fork') = merge_request.target_branch - if merge_request.tasks?   -- cgit v1.2.1 From 080a0c026bbd2f651eab68c816991e31e57e11ef Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:52:36 +0100 Subject: Use same text color for commit box as issue title --- app/assets/stylesheets/pages/commit.scss | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index a0e5f7554ed..39cde517c16 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -2,10 +2,6 @@ display: block; } -.commit-title{ - margin-bottom: 10px; -} - .commit-author, .commit-committer{ display: block; color: #999; @@ -41,6 +37,8 @@ .commit-box { .commit-title { margin: 0; + font-size: 23px; + color: #313236; } .commit-description { -- cgit v1.2.1 From 67ff47b39cfa47a6904bd5e4e84499a098a158cf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:53:11 +0100 Subject: Capitalize tab titles --- app/helpers/blob_helper.rb | 2 +- app/views/projects/blob/edit.html.haml | 6 +++--- features/steps/project/source/browse_files.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 77d99140c43..df5f5fae23c 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -60,7 +60,7 @@ module BlobHelper if Gitlab::MarkupHelper.previewable?(filename) 'Preview' else - 'Preview changes' + 'Preview Changes' end end diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 56745165251..a47fe7ede80 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -5,12 +5,12 @@ %ul.center-top-menu.no-bottom.js-edit-mode %li.active = link_to '#editor' do - %i.fa.fa-edit - Edit file + = icon('edit') + Edit File %li = link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do - %i.fa.fa-eye + = icon('eye') = editing_preview_title(@blob.name) = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input js-edit-blob-form') do diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index f40e0f0d528..bee31506779 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -87,7 +87,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I click link "Diff"' do - click_link 'Preview changes' + click_link 'Preview Changes' end step 'I click on "Commit Changes"' do -- cgit v1.2.1 From e9e9f537e84c1b3a1df7b4920d3b696029ba6dcc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:53:20 +0100 Subject: Truncate submodule commit SHAs to 7 characters --- app/helpers/diff_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index bfd3622a6a9..24134310fc5 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -146,9 +146,9 @@ module DiffHelper def submodule_link(blob, ref, repository = @repository) tree, commit = submodule_links(blob, ref, repository) commit_id = if commit.nil? - blob.id[0..10] + Commit.truncate_sha(blob.id) else - link_to "#{blob.id[0..10]}", commit + link_to Commit.truncate_sha(blob.id), commit end [ -- cgit v1.2.1 From b8a926fab0acc85dd22456366390a93f4c4b6ad9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:53:28 +0100 Subject: Don't use success state for explore projects search button --- app/views/explore/projects/_filter.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml index 2761272aa8a..28b12c8dca8 100644 --- a/app/views/explore/projects/_filter.html.haml +++ b/app/views/explore/projects/_filter.html.haml @@ -3,7 +3,7 @@ .form-group = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search", spellcheck: false .form-group - = button_tag 'Search', class: "btn btn-success" + = button_tag 'Search', class: "btn" .pull-right.hidden-sm.hidden-xs - if current_user -- cgit v1.2.1 From 12dd4c7c8a393cea2f264832e38df4286591f48c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:53:56 +0100 Subject: Use "Delete" in milestone and label delete buttons instead of "Remove" --- app/views/projects/labels/_label.html.haml | 2 +- app/views/projects/milestones/_milestone.html.haml | 2 +- features/steps/admin/labels.rb | 2 +- features/steps/project/issues/labels.rb | 2 +- features/steps/project/issues/milestones.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index c6ebfa281a1..b70a9fc9fe5 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -7,4 +7,4 @@ - if can? current_user, :admin_label, @project = link_to 'Edit', edit_namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm' - = link_to 'Remove', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"} + = link_to 'Delete', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"} diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml index 5e93d55b1fb..334172b976f 100644 --- a/app/views/projects/milestones/_milestone.html.haml +++ b/app/views/projects/milestones/_milestone.html.haml @@ -31,4 +31,4 @@ = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close" = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove" do %i.fa.fa-trash-o - Remove + Delete diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb index b45d98658bc..2d5db8f739e 100644 --- a/features/steps/admin/labels.rb +++ b/features/steps/admin/labels.rb @@ -17,7 +17,7 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps step 'I remove label \'bug\'' do page.within "#label_#{bug_label.id}" do - click_link 'Remove' + click_link 'Delete' end end diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index d656acf4220..047cf701bb0 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -9,7 +9,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I remove label \'bug\'' do page.within "#label_#{bug_label.id}" do - click_link 'Remove' + click_link 'Delete' end end diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb index c8708572ec6..e2eda511497 100644 --- a/features/steps/project/issues/milestones.rb +++ b/features/steps/project/issues/milestones.rb @@ -63,7 +63,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps end step 'I click link to remove milestone' do - click_link 'Remove' + click_link 'Delete' end step 'I should see no milestones' do -- cgit v1.2.1 From f8e577591b29b4cde211f275db6f870f98f58eb3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:54:12 +0100 Subject: Don't use success state for "Download zip" button --- app/views/projects/repositories/_download_archive.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index 07c24950ee2..b9486a9b492 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -3,10 +3,10 @@ - split_button = split_button || false - if split_button == true %span.btn-group{class: btn_class} - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn btn-success col-xs-10', rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn col-xs-10', rel: 'nofollow' do %i.fa.fa-download %span Download zip - %a.col-xs-2.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' } + %a.col-xs-2.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } %span.caret %span.sr-only Select Archive Format -- cgit v1.2.1 From 40760d3f7bc1b5fbed17bff2f185e92538590484 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:54:24 +0100 Subject: Add header title to import pages --- app/views/import/bitbucket/status.html.haml | 1 + app/views/import/fogbugz/new.html.haml | 1 + app/views/import/fogbugz/new_user_map.html.haml | 1 + app/views/import/fogbugz/status.html.haml | 1 + app/views/import/github/status.html.haml | 1 + app/views/import/gitlab/status.html.haml | 1 + app/views/import/gitorious/status.html.haml | 1 + app/views/import/google_code/new.html.haml | 3 ++- app/views/import/google_code/new_user_map.html.haml | 17 +++++++++-------- app/views/import/google_code/status.html.haml | 1 + 10 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index 1f09a27e2d6..aec2e836c9f 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -1,4 +1,5 @@ - page_title "Bitbucket import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-bitbucket Import projects from Bitbucket diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml index e1bb88ca4ed..5515fad6f48 100644 --- a/app/views/import/fogbugz/new.html.haml +++ b/app/views/import/fogbugz/new.html.haml @@ -1,4 +1,5 @@ - page_title "FogBugz Import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-bug Import projects from FogBugz diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml index bc3c90294e3..07338736bac 100644 --- a/app/views/import/fogbugz/new_user_map.html.haml +++ b/app/views/import/fogbugz/new_user_map.html.haml @@ -1,4 +1,5 @@ - page_title 'User map', 'FogBugz import' +- header_title "Projects", root_path %h3.page-title %i.fa.fa-bug Import projects from FogBugz diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index b902006597b..6ee16c8be4b 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -1,4 +1,5 @@ - page_title "FogBugz import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-bug Import projects from FogBugz diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 0699321c8c0..1416ee5bd5a 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -1,4 +1,5 @@ - page_title "GitHub import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-github Import projects from GitHub diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index f4a2b33af21..911a55eb85d 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -1,4 +1,5 @@ - page_title "GitLab.com import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-heart Import projects from GitLab.com diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml index 71752d21efa..6b0fa1edf8c 100644 --- a/app/views/import/gitorious/status.html.haml +++ b/app/views/import/gitorious/status.html.haml @@ -1,4 +1,5 @@ - page_title "Gitorious import" +- header_title "Projects", root_path %h3.page-title %i.icon-gitorious.icon-gitorious-big Import projects from Gitorious.org diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml index 9c64e0a009f..5d2f149cd5f 100644 --- a/app/views/import/google_code/new.html.haml +++ b/app/views/import/google_code/new.html.haml @@ -1,4 +1,5 @@ - page_title "Google Code import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-google Import projects from Google Code @@ -6,7 +7,7 @@ = form_tag callback_import_google_code_path, class: 'form-horizontal', multipart: true do %p - Follow the steps below to export your Google Code project data. + Follow the steps below to export your Google Code project data. In the next step, you'll be able to select the projects you want to import. %ol %li diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml index e53ebda7dc1..0738b3db1eb 100644 --- a/app/views/import/google_code/new_user_map.html.haml +++ b/app/views/import/google_code/new_user_map.html.haml @@ -1,4 +1,5 @@ - page_title "User map", "Google Code import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-google Import projects from Google Code @@ -8,31 +9,31 @@ %p Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import. - %p + %p The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of :. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side. %ul %li %strong Default: Directly import the Google Code email address or username %p - "johnsmith@example.com": "johnsm...@example.com" - will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com. + "johnsmith@example.com": "johnsm...@example.com" + will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy. %li %strong Map a Google Code user to a GitLab user %p - "johnsmith@example.com": "@johnsmith" - will add "By @johnsmith" to all issues and comments originally created by johnsmith@example.com, + "johnsmith@example.com": "@johnsmith" + will add "By @johnsmith" to all issues and comments originally created by johnsmith@example.com, and will set @johnsmith as the assignee on all issues originally assigned to johnsmith@example.com. %li %strong Map a Google Code user to a full name %p - "johnsmith@example.com": "John Smith" + "johnsmith@example.com": "John Smith" will add "By John Smith" to all issues and comments originally created by johnsmith@example.com. %li %strong Map a Google Code user to a full email address %p - "johnsmith@example.com": "johnsmith@example.com" - will add "By johnsmith@example.com" to all issues and comments originally created by johnsmith@example.com. + "johnsmith@example.com": "johnsmith@example.com" + will add "By johnsmith@example.com" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address. .form-group diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index 8c64fd27e60..175ef6921cd 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -1,4 +1,5 @@ - page_title "Google Code import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-google Import projects from Google Code -- cgit v1.2.1 From 0ec0c93824096fc6b26de369e170ffc729967bd3 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 2 Dec 2015 15:54:44 +0200 Subject: Also update gitlab-workhorse in patch updates [ci skip] --- doc/update/patch_versions.md | 45 +++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index 593722eb01f..957354decb7 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -6,7 +6,8 @@ For example from 7.14.0 to 7.14.3, also see the [semantic versioning specificati ### 0. Backup It's useful to make a backup just in case things go south: -(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version) +(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab +user on the database version) ```bash cd /home/git/gitlab @@ -15,19 +16,23 @@ 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 for the stable branch +In the commands below, replace `LATEST_TAG` with the latest GitLab tag you want +to update to, for example `v8.0.3`. Use `git tag -l 'v*.[0-9]' --sort='v:refname'` +to see a list of all tags. Make sure to update patch versions only (check your +current version with `cat VERSION`). + ```bash cd /home/git/gitlab sudo -u git -H git fetch --all sudo -u git -H git checkout -- Gemfile.lock db/schema.rb sudo -u git -H git checkout LATEST_TAG -b LATEST_TAG ``` -Replace `LATEST_TAG` with the latest GitLab tag you want to update to, for example `v8.0.3`. -Use `git tag -l 'v*.[0-9]' --sort='v:refname'` to see a list of all tags. -Make sure to update patch versions only (check your current version with `cat VERSION`) ### 3. Update gitlab-shell to the corresponding version @@ -37,12 +42,20 @@ sudo -u git -H git fetch sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` ``` -### 4. Install libs, migrations, etc. +### 4. Update gitlab-workhorse to the corresponding version + +```bash +cd /home/git/gitlab-workhorse +sudo -u git -H git fetch +sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` +``` + +### 5. Install libs, migrations, etc. ```bash cd /home/git/gitlab -#PostgreSQL +# PostgreSQL sudo -u git -H bundle install --without development test mysql --deployment # MySQL @@ -52,19 +65,25 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` -### 5. Start application +### 6. Start application - sudo service gitlab start - sudo service nginx restart +```bash +sudo service gitlab start +sudo service nginx restart +``` -### 6. Check application status +### 7. Check application status Check if GitLab and its environment are configured correctly: - sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production +```bash +sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production +``` To make sure you didn't miss anything run a more thorough check with: - sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production +```bash +sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production +``` If all items are green, then congratulations upgrade complete! -- cgit v1.2.1 From 90df3701e753257a7b71d97c8eec590cc148f409 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:54:50 +0100 Subject: Add plus icon to all "Add X" buttons --- app/views/profiles/keys/index.html.haml | 4 +++- app/views/projects/branches/index.html.haml | 2 +- app/views/projects/labels/index.html.haml | 1 + app/views/projects/tags/index.html.haml | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml index 14adba1c797..17a4195030e 100644 --- a/app/views/profiles/keys/index.html.haml +++ b/app/views/profiles/keys/index.html.haml @@ -3,7 +3,9 @@ .gray-content-block.top-block .pull-right - = link_to "Add SSH Key", new_profile_key_path, class: "btn btn-new" + = link_to new_profile_key_path, class: "btn btn-new" do + = icon('plus') + Add SSH Key .oneline Before you can add an SSH key you need to = link_to "generate it.", help_page_path("ssh", "README") diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 03ade02a0c8..204def60794 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -5,7 +5,7 @@ .pull-right - if can? current_user, :push_code, @project = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do - %i.fa.fa-add-sign + = icon('plus') New branch   .dropdown.inline diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index fb784ee5f4f..9081bcfe9b3 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -4,6 +4,7 @@ .gray-content-block.top-block - if can? current_user, :admin_label, @project = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do + = icon('plus') New label .oneline Labels can be applied to issues and merge requests. diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 85d76eae3b5..760347de0a9 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -6,7 +6,7 @@ - if can? current_user, :push_code, @project .pull-right = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do - %i.fa.fa-add-sign + = icon('plus') New tag .oneline Tags give the ability to mark specific points in history as being important -- cgit v1.2.1 From a3da3d8e3b60f8eeca716ce231ab5670a637e3eb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:54:58 +0100 Subject: Use monospace font for commit SHA in branch list --- app/views/projects/branches/_commit.html.haml | 2 +- app/views/projects/tree/_tree_content.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml index 22d77dda938..9fe65cbb104 100644 --- a/app/views/projects/branches/_commit.html.haml +++ b/app/views/projects/branches/_commit.html.haml @@ -1,5 +1,5 @@ .branch-commit - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id" + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id monospace" · %span.str-truncated = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index c64e684df26..1bc90edd8f0 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -12,7 +12,7 @@ %i.fa.fa-angle-right   %small.light - = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit) + = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" – = truncate(@commit.title, length: 50) = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right' -- cgit v1.2.1 From 59c3ec4e67f598e1a67df233f2d12fbec3d033dc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:55:36 +0100 Subject: Use file-type specific file icon in blame view and diff item --- app/views/projects/blame/show.html.haml | 3 +-- app/views/projects/diffs/_file.html.haml | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index 6518c4173e1..8d9ec068a43 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -6,7 +6,7 @@ #tree-holder.tree-holder .file-holder .file-title - %i.fa.fa-file + = blob_icon @blob.mode, @blob.name %strong = @path %small= number_to_human_size @blob.size @@ -43,4 +43,3 @@ - blame_group[:lines].each do |line| :erb <%= highlight(@blob.name, line, nowrap: true, continue: true).html_safe %> - diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index c745b4e69bf..b3392d00e01 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -2,19 +2,27 @@ .diff-header{id: "file-path-#{hexdigest(diff_file.file_path)}"} - if diff_file.diff.submodule? %span + = icon('archive fw') - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path) - = submodule_link(submodule_item, @commit.id, project.repository) + %strong + = submodule_link(submodule_item, @commit.id, project.repository) - else %span + = blob_icon blob.mode, blob.name + = link_to "#diff-#{i}" do + %strong + = diff_file.new_path + - if diff_file.deleted_file - = "#{diff_file.old_path} deleted" + deleted - elsif diff_file.renamed_file - = "#{diff_file.old_path} renamed to #{diff_file.new_path}" - - else - = diff_file.new_path + renamed from + %strong + = diff_file.old_path - if diff_file.mode_changed? - %span.file-mode= "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" + %small + = "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" .diff-controls - if blob.text? -- cgit v1.2.1 From 854deae4129c002faf97f615c17e742e8eff9166 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:56:17 +0100 Subject: Brefer "Directory" over "Dir", "Files" over "Code --- app/helpers/commits_helper.rb | 4 ++-- app/views/projects/commit/_commit_box.html.haml | 5 +++-- features/steps/project/source/browse_files.rb | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 9df20c9fce5..590d20ac7b3 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -109,7 +109,7 @@ module CommitsHelper ) elsif @path.present? return link_to( - "Browse Dir »", + "Browse Directory »", namespace_project_tree_path(project.namespace, project, tree_join(commit.id, @path)), class: "pull-right" @@ -117,7 +117,7 @@ module CommitsHelper end end link_to( - "Browse Code »", + "Browse Files »", namespace_project_tree_path(project.namespace, project, commit), class: "pull-right" ) diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 776768537d0..d8bfe6a07ac 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -13,8 +13,9 @@ - unless @commit.parents.length > 1 %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch) %li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff) - = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-primary btn-grouped" do - %span Browse Code » + = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-grouped" do + = icon('files-o') + Browse Files %div %p diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index bee31506779..ceab23b9a4d 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -192,7 +192,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I see Browse dir link' do - expect(page).to have_link 'Browse Dir »' + expect(page).to have_link 'Browse Directory »' expect(page).not_to have_link 'Browse Code »' end @@ -204,13 +204,13 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps step 'I see Browse file link' do expect(page).to have_link 'Browse File »' - expect(page).not_to have_link 'Browse Code »' + expect(page).not_to have_link 'Browse Files »' end step 'I see Browse code link' do - expect(page).to have_link 'Browse Code »' + expect(page).to have_link 'Browse Files »' expect(page).not_to have_link 'Browse File »' - expect(page).not_to have_link 'Browse Dir »' + expect(page).not_to have_link 'Browse Directory »' end step 'I click on Permalink' do -- cgit v1.2.1 From e3fe255c057e86d841a68f9a96b1a62b07036781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Rosen=C3=B6gger?= <123haynes@gmail.com> Date: Wed, 2 Dec 2015 15:21:02 +0100 Subject: fixed the documentation of the Guest role in permission.md --- doc/permissions/permissions.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md index 8d4c2ceab7d..bcd00cfc6bf 100644 --- a/doc/permissions/permissions.md +++ b/doc/permissions/permissions.md @@ -6,6 +6,9 @@ If a user is both in a project group and in the project itself, the highest perm If a user is a GitLab administrator they receive all permissions. +On public projects the Guest role is not enforced. +All users will be able to create issues, leave comments, and pull or download the project code. + To add or import a user, you can follow the [project users and members documentation](doc/workflow/add-user/add-user.md). @@ -15,8 +18,8 @@ documentation](doc/workflow/add-user/add-user.md). |---------------------------------------|---------|------------|-------------|----------|--------| | Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ | | Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ | -| Pull project code | ✓ | ✓ | ✓ | ✓ | ✓ | -| Download project | ✓ | ✓ | ✓ | ✓ | ✓ | +| Pull project code | | ✓ | ✓ | ✓ | ✓ | +| Download project | | ✓ | ✓ | ✓ | ✓ | | Create code snippets | | ✓ | ✓ | ✓ | ✓ | | Manage issue tracker | | ✓ | ✓ | ✓ | ✓ | | Manage labels | | ✓ | ✓ | ✓ | ✓ | -- cgit v1.2.1 From 1fb3c0996acbe9b8e44376e72e24c867a588f4f0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:32:45 +0100 Subject: Fix styling of empty repo message --- app/assets/stylesheets/framework/blocks.scss | 4 ++++ app/assets/stylesheets/pages/projects.scss | 7 ------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index dec50a0e0f6..8836c8b666b 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -68,6 +68,10 @@ .oneline { line-height: 42px; } + + > p:last-child { + margin-bottom: 0; + } } .cover-block { diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index ac2d50f9c18..9d5b62c75d3 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -26,12 +26,6 @@ } .project-home-panel { - text-align: center; - background: #f7f8fa; - margin: -$gl-padding; - padding: $gl-padding; - padding: 44px 0 17px 0; - .project-identicon-holder { margin-bottom: 16px; @@ -101,7 +95,6 @@ display: inline-table; position: relative; top: 17px; - margin-bottom: 44px; } .project-repo-buttons { -- cgit v1.2.1 From 845f26d07b97a281057a09facae28b2bbdb0168b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:04:29 +0100 Subject: Remove minimum containe height --- app/assets/stylesheets/framework/layout.scss | 7 ++++--- app/assets/stylesheets/framework/sidebar.scss | 5 ++--- app/assets/stylesheets/pages/login.scss | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index b91c15d8910..a60940a8bee 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -2,10 +2,10 @@ html { overflow-y: scroll; &.touch .tooltip { display: none !important; } +} - body { - padding-top: $header-height; - } +body { + background-color: #EAEBEC !important; } .container { @@ -18,6 +18,7 @@ html { } .navless-container { + padding-top: $header-height; margin-top: 30px; } diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index c1b0129c866..81cda68b94c 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -1,4 +1,6 @@ .page-with-sidebar { + padding-top: $header-height; + .sidebar-wrapper { position: fixed; top: 0; @@ -18,15 +20,12 @@ } .content-wrapper { - min-height: 100vh; width: 100%; padding: 20px; - background: #EAEBEC; .container-fluid { background: #FFF; padding: $gl-padding; - min-height: 90vh; &.container-blank { background: none; diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 83b866c3a64..edd51705136 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -1,5 +1,7 @@ /* Login Page */ .login-page { + background-color: white; + .container { max-width: 960px; } -- cgit v1.2.1 From acc35a58a7455a9107d6ebf2035f88350b1fb1ce Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:52:33 +0100 Subject: Revert unrelated changes --- app/assets/stylesheets/framework/sidebar.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 458af76cb75..fa463975805 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -20,12 +20,15 @@ } .content-wrapper { + min-height: 100vh; width: 100%; padding: 20px; + background: #EAEBEC; .container-fluid { background: #FFF; padding: $gl-padding; + min-height: 90vh; &.container-blank { background: none; -- cgit v1.2.1 From 74a81cbfe3e7b6fb6c3b7a620b07905f19971a86 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 17:30:50 +0100 Subject: Fix branch rendering --- app/helpers/merge_requests_helper.rb | 6 ++++-- app/views/projects/merge_requests/_show.html.haml | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index cc4243e1559..7f3a61a5e38 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -56,12 +56,14 @@ module MergeRequestsHelper end def source_branch_with_namespace(merge_request) + branch = link_to(merge_request.source_branch, namespace_project_commits_path(merge_request.source_project.namespace, merge_request.source_project, merge_request.source_branch)) + if merge_request.for_fork? namespace = link_to(merge_request.source_project_namespace, project_path(merge_request.source_project)) - namespace + ":#{merge_request.source_branch}" + namespace + ":" + branch else - merge_request.source_branch + branch end end diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 09b228b7c0c..e1992504232 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -26,7 +26,8 @@ %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) .normal %span Request to merge - = link_to source_branch_with_namespace(@merge_request), namespace_project_commits_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), class: "label-branch" + %span.label-branch + = source_branch_with_namespace(@merge_request) %span into = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do = @merge_request.target_branch -- cgit v1.2.1 From f10bd7df90cd509848dfcc9a574d7e4569a0428e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 17:37:06 +0100 Subject: Tweak merge request list item --- app/views/projects/merge_requests/_merge_request.html.haml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 60dd4f4f87c..1d4c9b66c42 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -33,7 +33,12 @@ \##{merge_request.iid} · opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)} - - if merge_request.milestone_id? + - if merge_request.target_project.default_branch != merge_request.target_branch +   + = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do + = icon('code-fork') + = merge_request.target_branch + - if merge_request.milestone   = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do = icon('clock-o') @@ -42,11 +47,6 @@   - merge_request.labels.each do |label| = link_to_label(label, project: merge_request.project) - - if merge_request.target_project.default_branch != merge_request.target_branch -   - = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do - = icon('code-fork') - = merge_request.target_branch - if merge_request.tasks?   %span.task-status -- cgit v1.2.1 From 044e0e33dce9371430a3c91e63f9f687b536d35b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 18:48:39 +0100 Subject: Allow invalid URLs in closing pattern --- lib/gitlab/closing_issue_extractor.rb | 4 +- spec/lib/gitlab/closing_issue_extractor_spec.rb | 49 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb index 0cf4c918736..9bef9037ad6 100644 --- a/lib/gitlab/closing_issue_extractor.rb +++ b/lib/gitlab/closing_issue_extractor.rb @@ -1,8 +1,10 @@ module Gitlab class ClosingIssueExtractor ISSUE_CLOSING_REGEX = begin + link_pattern = URI.regexp(%w(http https)) + pattern = Gitlab.config.gitlab.issue_closing_pattern - pattern = pattern.sub('%{issue_ref}', "(?:(?:#{Issue.link_reference_pattern})|(?:#{Issue.reference_pattern}))") + pattern = pattern.sub('%{issue_ref}', "(?:(?:#{link_pattern})|(?:#{Issue.reference_pattern}))") Regexp.new(pattern).freeze end diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 21254f778d3..e9d7e8c1111 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -2,11 +2,18 @@ require 'spec_helper' describe Gitlab::ClosingIssueExtractor do let(:project) { create(:project) } + let(:project2) { create(:project) } let(:issue) { create(:issue, project: project) } + let(:issue2) { create(:issue, project: project2) } let(:reference) { issue.to_reference } + let(:cross_reference) { issue2.to_reference(project) } subject { described_class.new(project, project.creator) } + before do + project2.team << [project.creator, :master] + end + describe "#closed_by_message" do context 'with a single reference' do it do @@ -130,6 +137,27 @@ describe Gitlab::ClosingIssueExtractor do end end + context "with a cross-project reference" do + it do + message = "Closes #{cross_reference}" + expect(subject.closed_by_message(message)).to eq([issue2]) + end + end + + context "with a cross-project URL" do + it do + message = "Closes #{Gitlab.config.gitlab.url}/#{project2.to_reference}/issues/#{issue2.iid}" + expect(subject.closed_by_message(message)).to eq([issue2]) + end + end + + context "with an invalid URL" do + it do + message = "Closes https://google.com/#{project2.to_reference}/issues/#{issue2.iid}" + expect(subject.closed_by_message(message)).to eq([]) + end + end + context 'with multiple references' do let(:other_issue) { create(:issue, project: project) } let(:third_issue) { create(:issue, project: project) } @@ -171,6 +199,27 @@ describe Gitlab::ClosingIssueExtractor do expect(subject.closed_by_message(message)). to match_array([issue, other_issue, third_issue]) end + + it "fetches cross-project references" do + message = "Closes #{reference} and #{cross_reference}" + + expect(subject.closed_by_message(message)). + to match_array([issue, issue2]) + end + + it "fetches cross-project URL references" do + message = "Closes #{Gitlab.config.gitlab.url}/#{project2.to_reference}/issues/#{issue2.iid} and #{reference}" + + expect(subject.closed_by_message(message)). + to match_array([issue, issue2]) + end + + it "ignores invalid cross-project URL references" do + message = "Closes https://google.com/#{project2.to_reference}/issues/#{issue2.iid} and #{reference}" + + expect(subject.closed_by_message(message)). + to match_array([issue]) + end end end end -- cgit v1.2.1 From ec754d221368fad2b765fa60c665a461b2b29c78 Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Wed, 2 Dec 2015 13:21:07 -0500 Subject: Be more explicit with the impersonate return URL --- app/controllers/admin/impersonation_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/impersonation_controller.rb b/app/controllers/admin/impersonation_controller.rb index 102dd437402..bf98af78615 100644 --- a/app/controllers/admin/impersonation_controller.rb +++ b/app/controllers/admin/impersonation_controller.rb @@ -11,7 +11,7 @@ class Admin::ImpersonationController < Admin::ApplicationController redirect_to admin_user_path(@user) else session[:impersonator_id] = current_user.username - session[:impersonator_return_to] = request.env['HTTP_REFERER'] + session[:impersonator_return_to] = admin_user_path(@user) warden.set_user(user, scope: 'user') -- cgit v1.2.1 From f94afdbdb232418da08658516bc469cf8429dadc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 2 Dec 2015 14:40:43 -0500 Subject: Fix broken spec related to MySQL and fractional seconds support. --- spec/models/project_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f80fada45e9..06a02c13bf1 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -444,7 +444,9 @@ describe Project do before do 2.times do - create(:note_on_commit, project: project2, created_at: date) + # Little fix for special issue related to Fractional Seconds support for MySQL. + # See: https://github.com/rails/rails/pull/14359/files + create(:note_on_commit, project: project2, created_at: date + 1) end end -- cgit v1.2.1 From 162cd0099c6c1f4ee0051e35562636468e88ee0c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 10:33:38 +0200 Subject: fix deprecation messages in tests --- app/models/project_services/buildkite_service.rb | 2 +- app/models/project_services/drone_ci_service.rb | 2 +- app/models/user.rb | 2 +- app/views/ci/notify/build_fail_email.html.haml | 2 +- lib/gitlab/lfs/response.rb | 2 +- spec/mailers/notify_spec.rb | 12 +++++++----- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index 40058b53df5..199ee3a9d0d 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -37,7 +37,7 @@ class BuildkiteService < CiService def compose_service_hook hook = service_hook || build_service_hook hook.url = webhook_url - hook.enable_ssl_verification = enable_ssl_verification + hook.enable_ssl_verification = !!enable_ssl_verification hook.save end diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 127684bd274..06c3922593c 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -34,7 +34,7 @@ class DroneCiService < CiService hook = service_hook || build_service_hook # If using a service template, project may not be available hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project - hook.enable_ssl_verification = enable_ssl_verification + hook.enable_ssl_verification = !!enable_ssl_verification hook.save end diff --git a/app/models/user.rb b/app/models/user.rb index 15aab315649..719b49b16fe 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -690,7 +690,7 @@ class User < ActiveRecord::Base end def starred?(project) - starred_projects.exists?(project) + starred_projects.exists?(project.id) end def toggle_star(project) diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml index b0aaea89075..de6291aa914 100644 --- a/app/views/ci/notify/build_fail_email.html.haml +++ b/app/views/ci/notify/build_fail_email.html.haml @@ -7,7 +7,7 @@ = @project.name %p - Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index c18dfbd485d..9be9a65671b 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -260,7 +260,7 @@ module Gitlab end def link_to_project(object) - if object && !object.projects.exists?(@project) + if object && !object.projects.exists?(@project.id) object.projects << @project object.save end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index d6796b07a5b..27e509933b2 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -247,7 +247,7 @@ describe Notify do end describe 'that have been reassigned' do - subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user) } + subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user.id) } it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'issue' @@ -278,7 +278,7 @@ describe Notify do describe 'status changed' do let(:status) { 'closed' } - subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } + subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) } it_behaves_like 'an answer to an existing thread', 'issue' it_behaves_like 'it should show Gmail Actions View Issue link' @@ -382,7 +382,7 @@ describe Notify do describe 'status changed' do let(:status) { 'reopened' } - subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user) } + subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) } it_behaves_like 'an answer to an existing thread', 'merge_request' it_behaves_like 'it should show Gmail Actions View Merge request link' @@ -597,8 +597,10 @@ describe Notify do let(:user) { create(:user, email: 'old-email@mail.com') } before do - user.email = "new-email@mail.com" - user.save + perform_enqueued_jobs do + user.email = "new-email@mail.com" + user.save + end end subject { ActionMailer::Base.deliveries.last } -- cgit v1.2.1 From 54b74aa99885b4f35f580bbb2cd923a381562c39 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 10:49:02 +0200 Subject: fix Celluloid warnings --- Gemfile.lock | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index dcb4a74e239..cd1855758b2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,23 +116,8 @@ GEM activemodel (>= 3.2.0) activesupport (>= 3.2.0) json (>= 1.7) - celluloid (0.17.2) - celluloid-essentials - celluloid-extras - celluloid-fsm - celluloid-pool - celluloid-supervision - timers (>= 4.1.1) - celluloid-essentials (0.20.5) - timers (>= 4.1.1) - celluloid-extras (0.20.5) - timers (>= 4.1.1) - celluloid-fsm (0.20.5) - timers (>= 4.1.1) - celluloid-pool (0.20.5) - timers (>= 4.1.1) - celluloid-supervision (0.20.5) - timers (>= 4.1.1) + celluloid (0.16.0) + timers (~> 4.0.0) charlock_holmes (0.7.3) chunky_png (1.3.5) cliver (0.3.2) @@ -757,7 +742,7 @@ GEM thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) - timers (4.1.1) + timers (4.0.4) hitimes timfel-krb5-auth (0.8.3) tinder (1.10.1) -- cgit v1.2.1 From f21bbd4444f9466d0dc622b0de286deace642598 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 10:53:58 +0200 Subject: Rails deprecation warning about log_level --- config/environments/production.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index 317b113e100..909526605a1 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -32,7 +32,7 @@ Rails.application.configure do # config.force_ssl = true # See everything in the log (default is :info) - # config.log_level = :debug + config.log_level = :info # Suppress 'Rendered template ...' messages in the log # source: http://stackoverflow.com/a/16369363 -- cgit v1.2.1 From b871c38bf2ca23c915fd5ce55ecfb2245b53bbe3 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 11:49:11 +0200 Subject: Fix failures in master --- app/views/admin/labels/_label.html.haml | 2 +- app/views/projects/milestones/show.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml index 596e06243dd..e3ccbf6c3a8 100644 --- a/app/views/admin/labels/_label.html.haml +++ b/app/views/admin/labels/_label.html.haml @@ -2,4 +2,4 @@ = render_colored_label(label) .pull-right = link_to 'Edit', edit_admin_label_path(label), class: 'btn btn-sm' - = link_to 'Remove', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"} + = link_to 'Delete', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Delete this label? Are you sure?"} diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 3a898dfbcfd..eab5333ca29 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -23,7 +23,7 @@ = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do %i.fa.fa-trash-o - Remove + Delete %hr - if @milestone.issues.any? && @milestone.can_be_closed? -- cgit v1.2.1 From 129ad8425e8fcbb1d7025e247d29772831d76b06 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 13:17:04 +0100 Subject: Tweak tag form wording --- app/views/projects/tags/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 9c9bfa3f55f..3a2f75fecaa 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -32,7 +32,7 @@ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render 'projects/zen', attr: :release_description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' - .help-block Optionally, you can add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page. + .help-block Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page. .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' -- cgit v1.2.1 From f905fd239c79f391ce5a7cc2c5c6648b3d6380e4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 14:00:00 +0100 Subject: Use URL helpers in specs --- lib/gitlab/markdown/label_reference_filter.rb | 4 ++-- spec/fixtures/markdown.md.erb | 20 ++++++++++---------- spec/lib/gitlab/closing_issue_extractor_spec.rb | 12 ++++++++---- .../gitlab/markdown/label_reference_filter_spec.rb | 6 +++--- spec/support/markdown_feature.rb | 4 ++++ 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index a23aa0adeec..ec2b179b7de 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -65,8 +65,8 @@ module Gitlab def url_for_label(project, label) h = Gitlab::Application.routes.url_helpers - h.namespace_project_issues_path(project.namespace, project, - label_name: label.name) + h.namespace_project_issues_url( project.namespace, project, label_name: label.name, + only_path: context[:only_path]) end def render_colored_label(label) diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 76e733165ca..e8dfc5c0eb1 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -161,9 +161,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Issue in another project: <%= xissue.to_reference(project) %> - Ignored in code: `<%= issue.to_reference %>` - Ignored in links: [Link to <%= issue.to_reference %>](#issue-link) -- Issue by URL: <%= Gitlab.config.gitlab.url %>/<%= issue.project.to_reference %>/issues/<%= issue.iid %> +- Issue by URL: <%= urls.namespace_project_issue_url(issue.project.namespace, issue.project, issue) %> - Link to issue by reference: [Issue](<%= issue.to_reference %>) -- Link to issue by URL: [Issue](<%= Gitlab.config.gitlab.url %>/<%= issue.project.to_reference %>/issues/<%= issue.iid %>) +- Link to issue by URL: [Issue](<%= urls.namespace_project_issue_url(issue.project.namespace, issue.project, issue) %>) #### MergeRequestReferenceFilter @@ -171,9 +171,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Merge request in another project: <%= xmerge_request.to_reference(project) %> - Ignored in code: `<%= merge_request.to_reference %>` - Ignored in links: [Link to <%= merge_request.to_reference %>](#merge-request-link) -- Merge request by URL: <%= Gitlab.config.gitlab.url %>/<%= merge_request.project.to_reference %>/merge_requests/<%= merge_request.iid %> +- Merge request by URL: <%= urls.namespace_project_merge_request_url(merge_request.project.namespace, merge_request.project, merge_request) %> - Link to merge request by reference: [Merge request](<%= merge_request.to_reference %>) -- Link to merge request by URL: [Merge request](<%= Gitlab.config.gitlab.url %>/<%= merge_request.project.to_reference %>/merge_requests/<%= merge_request.iid %>) +- Link to merge request by URL: [Merge request](<%= urls.namespace_project_merge_request_url(merge_request.project.namespace, merge_request.project, merge_request) %>) #### SnippetReferenceFilter @@ -181,9 +181,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Snippet in another project: <%= xsnippet.to_reference(project) %> - Ignored in code: `<%= snippet.to_reference %>` - Ignored in links: [Link to <%= snippet.to_reference %>](#snippet-link) -- Snippet by URL: <%= Gitlab.config.gitlab.url %>/<%= snippet.project.to_reference %>/snippets/<%= snippet.id %> +- Snippet by URL: <%= urls.namespace_project_snippet_url(snippet.project.namespace, snippet.project, snippet) %> - Link to snippet by reference: [Snippet](<%= snippet.to_reference %>) -- Link to snippet by URL: [Snippet](<%= Gitlab.config.gitlab.url %>/<%= snippet.project.to_reference %>/snippets/<%= snippet.id %>) +- Link to snippet by URL: [Snippet](<%= urls.namespace_project_snippet_url(snippet.project.namespace, snippet.project, snippet) %>) #### CommitRangeReferenceFilter @@ -191,9 +191,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Range in another project: <%= xcommit_range.to_reference(project) %> - Ignored in code: `<%= commit_range.to_reference %>` - Ignored in links: [Link to <%= commit_range.to_reference %>](#commit-range-link) -- Range by URL: <%= Gitlab.config.gitlab.url %>/<%= commit_range.project.to_reference %>/compare/<%= commit_range.id %> +- Range by URL: <%= urls.namespace_project_compare_url(commit_range.project.namespace, commit_range.project, commit_range.to_param) %> - Link to range by reference: [Range](<%= commit_range.to_reference %>) -- Link to range by URL: [Range](<%= Gitlab.config.gitlab.url %>/<%= commit_range.project.to_reference %>/compare/<%= commit_range.id %>) +- Link to range by URL: [Range](<%= urls.namespace_project_compare_url(commit_range.project.namespace, commit_range.project, commit_range.to_param) %>) #### CommitReferenceFilter @@ -201,9 +201,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Commit in another project: <%= xcommit.to_reference(project) %> - Ignored in code: `<%= commit.to_reference %>` - Ignored in links: [Link to <%= commit.to_reference %>](#commit-link) -- Commit by URL: <%= Gitlab.config.gitlab.url %>/<%= commit.project.to_reference %>/commit/<%= commit.id %> +- Commit by URL: <%= urls.namespace_project_commit_url(commit.project.namespace, commit.project, commit) %> - Link to commit by reference: [Commit](<%= commit.to_reference %>) -- Link to commit by URL: [Commit](<%= Gitlab.config.gitlab.url %>/<%= commit.project.to_reference %>/commit/<%= commit.id %>) +- Link to commit by URL: [Commit](<%= urls.namespace_project_commit_url(commit.project.namespace, commit.project, commit) %>) #### LabelReferenceFilter diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index e9d7e8c1111..fe1b94a484e 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -146,14 +146,14 @@ describe Gitlab::ClosingIssueExtractor do context "with a cross-project URL" do it do - message = "Closes #{Gitlab.config.gitlab.url}/#{project2.to_reference}/issues/#{issue2.iid}" + message = "Closes #{urls.namespace_project_issue_url(issue2.project.namespace, issue2.project, issue2)}" expect(subject.closed_by_message(message)).to eq([issue2]) end end context "with an invalid URL" do it do - message = "Closes https://google.com/#{project2.to_reference}/issues/#{issue2.iid}" + message = "Closes https://google.com#{urls.namespace_project_issue_path(issue2.project.namespace, issue2.project, issue2)}" expect(subject.closed_by_message(message)).to eq([]) end end @@ -208,18 +208,22 @@ describe Gitlab::ClosingIssueExtractor do end it "fetches cross-project URL references" do - message = "Closes #{Gitlab.config.gitlab.url}/#{project2.to_reference}/issues/#{issue2.iid} and #{reference}" + message = "Closes #{urls.namespace_project_issue_url(issue2.project.namespace, issue2.project, issue2)} and #{reference}" expect(subject.closed_by_message(message)). to match_array([issue, issue2]) end it "ignores invalid cross-project URL references" do - message = "Closes https://google.com/#{project2.to_reference}/issues/#{issue2.iid} and #{reference}" + message = "Closes https://google.com#{urls.namespace_project_issue_path(issue2.project.namespace, issue2.project, issue2)} and #{reference}" expect(subject.closed_by_message(message)). to match_array([issue]) end end end + + def urls + Gitlab::Application.routes.url_helpers + end end diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index f5a6d67f772..ef6dd524aba 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -71,7 +71,7 @@ module Gitlab::Markdown doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_path(project.namespace, project, label_name: label.name) + namespace_project_issues_url(project.namespace, project, label_name: label.name) end it 'links with adjacent text' do @@ -94,7 +94,7 @@ module Gitlab::Markdown doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_path(project.namespace, project, label_name: label.name) + namespace_project_issues_url(project.namespace, project, label_name: label.name) expect(doc.text).to eq 'See gfm' end @@ -118,7 +118,7 @@ module Gitlab::Markdown doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_path(project.namespace, project, label_name: label.name) + namespace_project_issues_url(project.namespace, project, label_name: label.name) expect(doc.text).to eq 'See gfm references' end diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb index bedc1a7f1db..d6d3062a197 100644 --- a/spec/support/markdown_feature.rb +++ b/spec/support/markdown_feature.rb @@ -93,6 +93,10 @@ class MarkdownFeature end end + def urls + Gitlab::Application.routes.url_helpers + end + def raw_markdown markdown = File.read(Rails.root.join('spec/fixtures/markdown.md.erb')) ERB.new(markdown).result(binding) -- cgit v1.2.1 From 0de8e260662a62603dbee5efb133cd4f1e0f0ed7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 14:00:41 +0100 Subject: Pass original text along with label reference filter. --- lib/gitlab/markdown/label_reference_filter.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index ec2b179b7de..36cf7a8f4ce 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -51,7 +51,11 @@ module Gitlab if label = project.labels.find_by(params) url = url_for_label(project, label) klass = reference_class(:label) - data = data_attribute(project: project.id, label: label.id) + data = data_attribute( + original: link_text || match, + project: project.id, + label: label.id + ) text = link_text || render_colored_label(label) -- cgit v1.2.1 From 9ce8c867eeb5da53eb76bc01ef7eaef5c798243c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 15:05:43 +0200 Subject: Fix mailer queue --- Procfile | 2 +- bin/background_jobs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Procfile b/Procfile index fd5f7ecb94b..eaf79ccc2ea 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,3 @@ web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} -worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q mailers -q default +worker: bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default # mail_room: bundle exec mail_room -q -c config/mail_room.yml diff --git a/bin/background_jobs b/bin/background_jobs index d4578f6a222..5c85fb339e6 100755 --- a/bin/background_jobs +++ b/bin/background_jobs @@ -37,7 +37,7 @@ start_no_deamonize() start_sidekiq() { - bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1 + bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1 } load_ok() -- cgit v1.2.1 From 90899b3208035f5c74feb439da5fbe689dd1d2d7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 14:54:14 +0100 Subject: Fix specs --- features/steps/project/forked_merge_requests.rb | 2 +- features/steps/project/issues/labels.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 024dc5e72d3..de1dbfdfa93 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -46,7 +46,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps end step 'I submit the merge request' do - click_button "Submit new merge request" + click_button "Submit merge request" end step 'I follow the target commit link' do diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index 047cf701bb0..f749ea2eca8 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -31,19 +31,19 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I submit new label \'support\'' do fill_in 'Title', with: 'support' - fill_in 'Background Color', with: '#F95610' + fill_in 'Background color', with: '#F95610' click_button 'Save' end step 'I submit new label \'bug\'' do fill_in 'Title', with: 'bug' - fill_in 'Background Color', with: '#F95610' + fill_in 'Background color', with: '#F95610' click_button 'Save' end step 'I submit new label with invalid color' do fill_in 'Title', with: 'support' - fill_in 'Background Color', with: '#12' + fill_in 'Background color', with: '#12' click_button 'Save' end @@ -85,7 +85,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I change label \'bug\' to \'fix\'' do fill_in 'Title', with: 'fix' - fill_in 'Background Color', with: '#F15610' + fill_in 'Background color', with: '#F15610' click_button 'Save' end -- cgit v1.2.1 From 0cbb7717df8d243d7812041099d99f731de7f82b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:32:29 +0100 Subject: Fix spec --- features/steps/project/issues/labels.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index f749ea2eca8..e273bb391b3 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -32,19 +32,19 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I submit new label \'support\'' do fill_in 'Title', with: 'support' fill_in 'Background color', with: '#F95610' - click_button 'Save' + click_button 'Create Label' end step 'I submit new label \'bug\'' do fill_in 'Title', with: 'bug' fill_in 'Background color', with: '#F95610' - click_button 'Save' + click_button 'Create Label' end step 'I submit new label with invalid color' do fill_in 'Title', with: 'support' fill_in 'Background color', with: '#12' - click_button 'Save' + click_button 'Create Label' end step 'I should see label label exist error message' do @@ -86,7 +86,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I change label \'bug\' to \'fix\'' do fill_in 'Title', with: 'fix' fill_in 'Background color', with: '#F15610' - click_button 'Save' + click_button 'Save changes' end step 'I should see label \'fix\'' do -- cgit v1.2.1 From 5ab1b11e901712ab462b10f030971610ba140257 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:42:37 +0100 Subject: Fix specs --- features/steps/project/source/markdown_render.rb | 4 ++-- features/steps/project/wiki.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb index ec88c8c20c8..3a4f7a6e01c 100644 --- a/features/steps/project/source/markdown_render.rb +++ b/features/steps/project/source/markdown_render.rb @@ -252,7 +252,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps step 'I see Gitlab API document' do expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "api") - expect(page).to have_content "Editing" + expect(page).to have_content "Edit Page api" end step 'I click on Rake tasks link' do @@ -261,7 +261,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps step 'I see Rake tasks directory' do expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "raketasks") - expect(page).to have_content "Editing" + expect(page).to have_content "Edit Page raketasks" end step 'I go directory which contains README file' do diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index 935c1ca8a2e..91d227fadbf 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -156,7 +156,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I should see the Editing page' do - expect(page).to have_content('Editing') + expect(page).to have_content('Edit Page') end step 'I view the page history of a Wiki page that has a path' do -- cgit v1.2.1 From 1dd7c978862b900b92bde355f99ce5e869a98328 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:51:44 +0100 Subject: Fix background and padding of login and error pages --- app/assets/stylesheets/framework/layout.scss | 8 ++++++-- app/assets/stylesheets/pages/login.scss | 3 +-- app/views/layouts/devise.html.haml | 4 ++-- app/views/layouts/errors.html.haml | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index a60940a8bee..aa5acb93cc5 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -6,6 +6,10 @@ html { body { background-color: #EAEBEC !important; + + &.navless { + background-color: white !important; + } } .container { @@ -18,8 +22,8 @@ body { } .navless-container { - padding-top: $header-height; - margin-top: 30px; + margin-top: $header-height; + padding-top: $gl-padding * 2; } .container-limited { diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index edd51705136..f9c6f1b39f9 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -1,7 +1,5 @@ /* Login Page */ .login-page { - background-color: white; - .container { max-width: 960px; } @@ -21,6 +19,7 @@ h1:first-child { font-weight: normal; margin-bottom: 30px; + margin-top: 0; } img { diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 95e077c339f..f08cb0a5428 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -1,13 +1,13 @@ !!! 5 %html{ lang: "en"} = render "layouts/head" - %body.ui_charcoal.login-page.application + %body.ui_charcoal.login-page.application.navless = render "layouts/header/empty" = render "layouts/broadcast" .container.navless-container .content = render "layouts/flash" - .row.prepend-top-20 + .row .col-sm-5.pull-right = yield .col-sm-7.brand-holder.pull-left diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml index 2af265a2296..915acc4612e 100644 --- a/app/views/layouts/errors.html.haml +++ b/app/views/layouts/errors.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head" - %body{class: "#{user_application_theme} application"} + %body{class: "#{user_application_theme} application navless"} = render "layouts/header/empty" .container.navless-container = render "layouts/flash" -- cgit v1.2.1 From a89d6d1428d61bd2ae6f530acfc5a34d5a9c46e8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:53:17 +0100 Subject: Add authorization to new branch/tag pages. --- app/controllers/projects/branches_controller.rb | 2 +- app/controllers/projects/tags_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 3ac0a75fa70..3c2849a7601 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -3,7 +3,7 @@ class Projects::BranchesController < Projects::ApplicationController # Authorize before_action :require_non_empty_project before_action :authorize_download_code! - before_action :authorize_push_code!, only: [:create, :destroy] + before_action :authorize_push_code!, only: [:new, :create, :destroy] def index @sort = params[:sort] || 'name' diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index cb39c2b8782..280fe12cc7c 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -2,7 +2,7 @@ class Projects::TagsController < Projects::ApplicationController # Authorize before_action :require_non_empty_project before_action :authorize_download_code! - before_action :authorize_push_code!, only: [:create] + before_action :authorize_push_code!, only: [:new, :create] before_action :authorize_admin_project!, only: [:destroy] def index -- cgit v1.2.1 From 494ebad39b465ca6a70888ca376db0dd962a618f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:56:43 +0100 Subject: Fix spec --- features/steps/project/issues/issues.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index af2da41badb..9683bebd563 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -65,7 +65,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps step 'I see current user as the first user' do expect(page).to have_selector('.user-result', visible: true, count: 4) users = page.all('.user-name') - expect(users[0].text).to eq 'Any' + expect(users[0].text).to eq 'Any Assignee' expect(users[1].text).to eq 'Unassigned' expect(users[2].text).to eq current_user.name end -- cgit v1.2.1 From e41a0c60f448057f69c2952f15d88f9d8191c2fe Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:59:03 +0100 Subject: Add missing milestone ID --- app/models/milestone.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/milestone.rb b/app/models/milestone.rb index c2642b75b8a..58acc5d6ccc 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -16,9 +16,9 @@ class Milestone < ActiveRecord::Base # Represents a "No Milestone" state used for filtering Issues and Merge # Requests that have no milestone assigned. - MilestoneStruct = Struct.new(:title, :name) - None = MilestoneStruct.new('No Milestone', 'No Milestone') - Any = MilestoneStruct.new('Any', '') + MilestoneStruct = Struct.new(:title, :name, :id) + None = MilestoneStruct.new('No Milestone', 'No Milestone', 0) + Any = MilestoneStruct.new('Any', '', -1) include InternalId include Sortable -- cgit v1.2.1 From f9d954fae71d40a314a5812c1a7eec5a601d1575 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 19:02:56 +0100 Subject: Satisfy rubocop --- lib/gitlab/markdown/label_reference_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index 36cf7a8f4ce..a2026eecaeb 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -70,7 +70,7 @@ module Gitlab def url_for_label(project, label) h = Gitlab::Application.routes.url_helpers h.namespace_project_issues_url( project.namespace, project, label_name: label.name, - only_path: context[:only_path]) + only_path: context[:only_path]) end def render_colored_label(label) -- cgit v1.2.1 From 82e916b0f2cbd500d14f545095ac82464c0a3889 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Tue, 1 Dec 2015 18:11:12 -0200 Subject: Touch project when toggling stars to update cache --- app/models/users_star_project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb index 3d49cb05949..413f3f485a8 100644 --- a/app/models/users_star_project.rb +++ b/app/models/users_star_project.rb @@ -10,7 +10,7 @@ # class UsersStarProject < ActiveRecord::Base - belongs_to :project, counter_cache: :star_count + belongs_to :project, counter_cache: :star_count, touch: true belongs_to :user validates :user, presence: true -- cgit v1.2.1 From e5c865eebf95d58ed33ef3d86f7582aa2db9bb09 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 20:01:35 +0100 Subject: Fix specs --- app/assets/stylesheets/pages/issuable.scss | 11 +++++++++++ app/assets/stylesheets/pages/issues.scss | 11 ----------- app/assets/stylesheets/pages/merge_requests.scss | 11 ----------- app/views/shared/issuable/_context.html.haml | 2 +- features/steps/project/forked_merge_requests.rb | 2 +- features/steps/project/issues/issues.rb | 2 +- 6 files changed, 14 insertions(+), 25 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 51d8e5b4657..b5f449ff695 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -90,6 +90,17 @@ } } +.issuable-show-labels { + a { + margin-right: 5px; + margin-bottom: 5px; + display: inline-block; + .color-label { + padding: 6px 10px; + } + } +} + .cross-project-reference { text-align: center; width: 100%; diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 41c069f0ad3..f5548c5b88b 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -56,17 +56,6 @@ } } -.issue-show-labels { - a { - margin-right: 5px; - margin-bottom: 5px; - display: inline-block; - .color-label { - padding: 6px 10px; - } - } -} - form.edit-issue { margin: 0; } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 017a86bcd9a..af6c6830fab 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -183,17 +183,6 @@ display: none; } -.merge-request-show-labels { - a { - margin-right: 5px; - margin-bottom: 5px; - display: inline-block; - .color-label { - padding: 6px 10px; - } - } -} - .merge-request-form .select2-container { width: 250px !important; } diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index 521364ac858..f44b439a843 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -33,7 +33,7 @@ %div.prepend-top-default.clearfix .issuable-context-title %label Labels - .merge-request-show-labels + .issuable-show-labels - issuable.labels.each do |label| = link_to_label(label) diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 789befc9df2..34b435919ac 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -115,7 +115,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps expect(find(:select, "merge_request_source_project_id", {}).value).to eq @forked_project.id.to_s expect(find(:select, "merge_request_target_project_id", {}).value).to eq @project.id.to_s expect(find(:select, "merge_request_source_branch", {}).value).to eq "" - expect(find(:select, "merge_request_target_branch", {}).value).to eq "" + expect(find(:select, "merge_request_target_branch", {}).value).to eq "master" click_button "Compare branches" end diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index af2da41badb..264408e85ec 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -86,7 +86,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps end step 'I should see label \'bug\' with issue' do - page.within '.issue-show-labels' do + page.within '.issuable-show-labels' do expect(page).to have_content 'bug' end end -- cgit v1.2.1 From c5b6b31c847d5d2f467453d8292e13ca735ee630 Mon Sep 17 00:00:00 2001 From: Anton Baklanov Date: Wed, 18 Nov 2015 20:08:07 +0200 Subject: Fixed invalid link on starred projects dashboard. Fixes #3468 --- CHANGELOG | 1 + app/views/dashboard/projects/index.html.haml | 2 +- app/views/dashboard/projects/starred.html.haml | 2 +- app/views/explore/projects/index.html.haml | 2 +- app/views/explore/projects/starred.html.haml | 2 +- app/views/explore/projects/trending.html.haml | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index db812796b69..438cae617a7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.2.2 - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) - Fix: Raw private snippets access workflow - Prevent "413 Request entity too large" errors when pushing large files with LFS + - Fix invalid links within projects dashboard header v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index 7a16b811f6b..53abf274bdb 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -3,7 +3,7 @@ = auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity") - page_title "Projects" -- header_title "Projects", root_path +- header_title "Projects", dashboard_projects_path = render 'dashboard/projects_head' diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml index f75f2e0a32a..70705923d42 100644 --- a/app/views/dashboard/projects/starred.html.haml +++ b/app/views/dashboard/projects/starred.html.haml @@ -1,5 +1,5 @@ - page_title "Starred Projects" -- header_title "Projects", projects_path +- header_title "Projects", dashboard_projects_path = render 'dashboard/projects_head' diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index 67e38ca3127..76bdd68fd76 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -1,5 +1,5 @@ - page_title "Projects" -- header_title "Projects", root_path +- header_title "Projects", dashboard_projects_path - if current_user = render 'dashboard/projects_head' diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml index 596cb0a96cd..e30c3633223 100644 --- a/app/views/explore/projects/starred.html.haml +++ b/app/views/explore/projects/starred.html.haml @@ -1,5 +1,5 @@ - page_title "Projects" -- header_title "Projects", root_path +- header_title "Projects", dashboard_projects_path - if current_user = render 'dashboard/projects_head' diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml index 5ea6d81c5b9..1412b19acde 100644 --- a/app/views/explore/projects/trending.html.haml +++ b/app/views/explore/projects/trending.html.haml @@ -1,5 +1,5 @@ - page_title "Projects" -- header_title "Projects", root_path +- header_title "Projects", dashboard_projects_path - if current_user = render 'dashboard/projects_head' -- cgit v1.2.1 From 83e6d8294f9e1c4ee199a3577e5fc810df33def6 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 3 Dec 2015 23:14:19 +0100 Subject: Update .ruby-version to 2.1.7 --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index 399088bf465..04b10b4f150 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.1.6 +2.1.7 -- cgit v1.2.1 From 387e5656a1e158eaa1c010f22d332d758b336179 Mon Sep 17 00:00:00 2001 From: Kevin Pankonen Date: Thu, 3 Dec 2015 15:40:08 -0700 Subject: fixes #3263 slashes are replaced with two underscores --- doc/ci/docker/using_docker_images.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index ef8a7ec1e86..64e52eba3a2 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -60,11 +60,11 @@ This is image that have fully preconfigured `wordpress` and have `MySQL` server ``` Next time when you run your application the `tutum/wordpress` will be started -and you will have access to it from your build container under hostname: `tutum_wordpress`. +and you will have access to it from your build container under hostname: `tutum__wordpress`. Alias hostname for the service is made from the image name: 1. Everything after `:` is stripped, -2. '/' is replaced to `_`. +2. '/' is replaced with `__`. ### Configuring services Many services accept environment variables, which allow you to easily change database names or set account names depending on the environment. -- cgit v1.2.1 From 0ccd7de7f369d98615833867abe84142bcc25193 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Dec 2015 10:55:36 +0100 Subject: Fix wrong doc in merge request API Signed-off-by: Dmitriy Zaporozhets --- doc/api/merge_requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 0cef09d5b27..3a1fc406fd1 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -159,7 +159,7 @@ Parameters: "updated_at": "2015-02-02T19:49:26.013Z", "due_date": null }, - "files": [ + "changes": [ { "old_path": "VERSION", "new_path": "VERSION", -- cgit v1.2.1 From 0b68a0e79e1cbe12c44beb6c23e79d48b71f219e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Dec 2015 11:08:10 +0100 Subject: Add API endpoint to fetch merge request commits list Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + doc/api/merge_requests.md | 39 ++++++++++++++++++++++++++++++++ lib/api/merge_requests.rb | 16 +++++++++++++ spec/requests/api/merge_requests_spec.rb | 17 ++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 2f310a4b028..2876e36f9ef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Add ignore whitespace change option to commit view - Fire update hook from GitLab - Don't show project fork event as "imported" + - Add API endpoint to fetch merge request commits list v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 3a1fc406fd1..2b1498c85a0 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -101,6 +101,45 @@ Parameters: } ``` +## Get single MR commits + +Get a list of repository commits in a merge request. + +``` +GET /projects/:id/merge_request/:merge_request_id/commits +``` + +Parameters: + +- `id` (required) - The ID of a project +- `merge_request_id` (required) - The ID of MR + + +```json +[ + { + "id": "ed899a2f4b50b4370feeea94676502b42383c746", + "short_id": "ed899a2f4b5", + "title": "Replace sanitize with escape once", + "author_name": "Dmitriy Zaporozhets", + "author_email": "dzaporozhets@sphereconsultinginc.com", + "created_at": "2012-09-20T11:50:22+03:00", + "message": "Replace sanitize with escape once", + "allow_failure": false + }, + { + "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", + "short_id": "6104942438c", + "title": "Sanitize for network graph", + "author_name": "randx", + "author_email": "dmitriy.zaporozhets@gmail.com", + "created_at": "2012-09-20T09:06:12+03:00", + "message": "Sanitize for network graph", + "allow_failure": false + } +] +``` + ## Get single MR changes Shows information about the merge request including its files and changes. diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 6eb84baf9cb..e7c5f808aea 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -76,6 +76,22 @@ module API present merge_request, with: Entities::MergeRequest end + # Show MR commits + # + # Parameters: + # id (required) - The ID of a project + # merge_request_id (required) - The ID of MR + # + # Example: + # GET /projects/:id/merge_request/:merge_request_id/commits + # + get ':id/merge_request/:merge_request_id/commits' do + merge_request = user_project.merge_requests. + find(params[:merge_request_id]) + authorize! :read_merge_request, merge_request + present merge_request.commits, with: Entities::RepoCommit + end + # Show MR changes # # Parameters: diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index a68c7b1e461..c6d3aef0af9 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -131,6 +131,23 @@ describe API::API, api: true do end end + describe 'GET /projects/:id/merge_request/:merge_request_id/commits' do + context 'valid merge request' do + before { get api("/projects/#{project.id}/merge_request/#{merge_request.id}/commits", user) } + let(:commit) { merge_request.commits.first } + + it { expect(response.status).to eq 200 } + it { expect(json_response.size).to eq(merge_request.commits.size) } + it { expect(json_response.first['id']).to eq(commit.id) } + it { expect(json_response.first['title']).to eq(commit.title) } + end + + it 'returns a 404 when merge_request_id not found' do + get api("/projects/#{project.id}/merge_request/999/commits", user) + expect(response.status).to eq(404) + end + end + describe 'GET /projects/:id/merge_request/:merge_request_id/changes' do it 'should return the change information of the merge_request' do get api("/projects/#{project.id}/merge_request/#{merge_request.id}/changes", user) -- cgit v1.2.1 From c366e81da7fb6c9ef921cbd57e5ac662999f0bc0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Dec 2015 11:26:35 +0100 Subject: Improve docs Signed-off-by: Dmitriy Zaporozhets --- doc/api/merge_requests.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 2b1498c85a0..82f2cef969f 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -103,7 +103,7 @@ Parameters: ## Get single MR commits -Get a list of repository commits in a merge request. +Get a list of merge request commits. ``` GET /projects/:id/merge_request/:merge_request_id/commits @@ -124,8 +124,7 @@ Parameters: "author_name": "Dmitriy Zaporozhets", "author_email": "dzaporozhets@sphereconsultinginc.com", "created_at": "2012-09-20T11:50:22+03:00", - "message": "Replace sanitize with escape once", - "allow_failure": false + "message": "Replace sanitize with escape once" }, { "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", @@ -134,8 +133,7 @@ Parameters: "author_name": "randx", "author_email": "dmitriy.zaporozhets@gmail.com", "created_at": "2012-09-20T09:06:12+03:00", - "message": "Sanitize for network graph", - "allow_failure": false + "message": "Sanitize for network graph" } ] ``` -- cgit v1.2.1 From 3227a5ead22c90873794b0c0e4c788436283fb3e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Dec 2015 12:21:06 +0100 Subject: Extent Event and Note API * add note to Events API * add author section to Events API * add noteable_id and noteable_type to Notes API Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + doc/api/notes.md | 15 ++++++-- doc/api/projects.md | 71 +++++++++++++++++++++++++++++++++++--- lib/api/entities.rb | 3 ++ spec/requests/api/projects_spec.rb | 30 ++++++++++++---- 5 files changed, 106 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2f310a4b028..002a132b9cd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Add ignore whitespace change option to commit view - Fire update hook from GitLab - Don't show project fork event as "imported" + - Expose events API with comment information and author info v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/doc/api/notes.md b/doc/api/notes.md index e7f299c0994..4d7ef288df8 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -35,7 +35,9 @@ Parameters: "created_at": "2013-10-02T09:22:45Z", "system": true, "upvote": false, - "downvote": false + "downvote": false, + "noteable_id": 377, + "noteable_type": "Issue" }, { "id": 305, @@ -52,7 +54,9 @@ Parameters: "created_at": "2013-10-02T09:56:03Z", "system": true, "upvote": false, - "downvote": false + "downvote": false, + "noteable_id": 121, + "noteable_type": "Issue" } ] ``` @@ -219,7 +223,12 @@ Parameters: "state": "active", "created_at": "2013-09-30T13:46:01Z" }, - "created_at": "2013-10-02T08:57:14Z" + "created_at": "2013-10-02T08:57:14Z", + "system": false, + "upvote": false, + "downvote": false, + "noteable_id": 2, + "noteable_type": "MergeRequest" } ``` diff --git a/doc/api/projects.md b/doc/api/projects.md index 755cc6525c2..42919a312ae 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -245,9 +245,17 @@ Parameters: "target_id": 830, "target_type": "Issue", "author_id": 1, - "author_username": "john", "data": null, - "target_title": "Public project search field" + "target_title": "Public project search field", + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, + "author_username": "root" }, { "title": null, @@ -256,6 +264,14 @@ Parameters: "target_id": null, "target_type": null, "author_id": 1, + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, "author_username": "john", "data": { "before": "50d4420237a9de7be1304607147aec22e4a14af7", @@ -292,9 +308,56 @@ Parameters: "target_id": 840, "target_type": "Issue", "author_id": 1, - "author_username": "john", "data": null, - "target_title": "Finish & merge Code search PR" + "target_title": "Finish & merge Code search PR", + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, + "author_username": "root" + }, + { + "title": null, + "project_id": 15, + "action_name": "commented on", + "target_id": 1312, + "target_type": "Note", + "author_id": 1, + "data": null, + "target_title": null, + "created_at": "2015-12-04T10:33:58.089Z", + "note": { + "id": 1312, + "body": "What an awesome day!", + "attachment": null, + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, + "created_at": "2015-12-04T10:33:56.698Z", + "system": false, + "upvote": false, + "downvote": false, + "noteable_id": 377, + "noteable_type": "Issue" + }, + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, + "author_username": "root" } ] ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 9f337bc3cc6..96b73df6af9 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -194,6 +194,7 @@ module API expose :author, using: Entities::UserBasic expose :created_at expose :system?, as: :system + expose :noteable_id, :noteable_type # upvote? and downvote? are deprecated, always return false expose :upvote?, as: :upvote expose :downvote?, as: :downvote @@ -224,6 +225,8 @@ module API expose :target_id, :target_type, :author_id expose :data, :target_title expose :created_at + expose :note, using: Entities::Note, if: ->(event, options) { event.note? } + expose :author, using: Entities::UserBasic, if: ->(event, options) { event.author } expose :author_username do |event, options| if event.author diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 9fc294118ae..c59ee7af8ab 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -389,14 +389,30 @@ describe API::API, api: true do describe 'GET /projects/:id/events' do before { project_member2 } - it 'should return a project events' do - get api("/projects/#{project.id}/events", user) - expect(response.status).to eq(200) - json_event = json_response.first + context 'valid request' do + before do + note = create(:note_on_issue, note: 'What an awesome day!', project: project) + EventCreateService.new.leave_note(note, note.author) + get api("/projects/#{project.id}/events", user) + end + + it { expect(response.status).to eq(200) } + + context 'joined event' do + let(:json_event) { json_response[1] } - expect(json_event['action_name']).to eq('joined') - expect(json_event['project_id'].to_i).to eq(project.id) - expect(json_event['author_username']).to eq(user3.username) + it { expect(json_event['action_name']).to eq('joined') } + it { expect(json_event['project_id'].to_i).to eq(project.id) } + it { expect(json_event['author_username']).to eq(user3.username) } + it { expect(json_event['author']['name']).to eq(user3.name) } + end + + context 'comment event' do + let(:json_event) { json_response.first } + + it { expect(json_event['action_name']).to eq('commented on') } + it { expect(json_event['note']['body']).to eq('What an awesome day!') } + end end it 'should return a 404 error if not found' do -- cgit v1.2.1 From 32b45493b89b35b7b7d3f086e8996d1ed7b8d0f3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 3 Dec 2015 00:09:10 -0800 Subject: Fix application settings cache not expiring after changes cache_key is an instance method that relies on updated_at. When changes were made, the time-dependent key was being used instead of X.application_setting.last. Closes #3609 --- CHANGELOG | 1 + app/models/application_setting.rb | 12 +++++------- app/models/ci/application_setting.rb | 11 ++++------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 58a0a3c8944..9ea091c3ce5 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.3.0 (unreleased) + - Fix application settings cache not expiring after changes (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 3df8135acf1..5ddcf3d9a0b 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -30,6 +30,8 @@ # class ApplicationSetting < ActiveRecord::Base + CACHE_KEY = 'application_setting.last' + serialize :restricted_visibility_levels serialize :import_sources serialize :restricted_signup_domains, Array @@ -73,21 +75,17 @@ class ApplicationSetting < ActiveRecord::Base end after_commit do - Rails.cache.write(cache_key, self) + Rails.cache.write(CACHE_KEY, self) end def self.current - Rails.cache.fetch(cache_key) do + Rails.cache.fetch(CACHE_KEY) do ApplicationSetting.last end end def self.expire - Rails.cache.delete(cache_key) - end - - def self.cache_key - 'application_setting.last' + Rails.cache.delete(CACHE_KEY) end def self.create_from_defaults diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb index 4e512d290ee..7f5df8ce6c4 100644 --- a/app/models/ci/application_setting.rb +++ b/app/models/ci/application_setting.rb @@ -12,17 +12,18 @@ module Ci class ApplicationSetting < ActiveRecord::Base extend Ci::Model + CACHE_KEY = 'ci_application_setting.last' after_commit do - Rails.cache.write(cache_key, self) + Rails.cache.write(CACHE_KEY, self) end def self.expire - Rails.cache.delete(cache_key) + Rails.cache.delete(CACHE_KEY) end def self.current - Rails.cache.fetch(cache_key) do + Rails.cache.fetch(CACHE_KEY) do Ci::ApplicationSetting.last end end @@ -33,9 +34,5 @@ module Ci add_pusher: Settings.gitlab_ci['add_pusher'], ) end - - def self.cache_key - 'ci_application_setting.last' - end end end -- cgit v1.2.1 From f1fd4880d9bbb7c34e910b357bc52874d2e6188e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 2 Dec 2015 20:11:58 +0000 Subject: Check GitLab Workhorse status in init.d script when reporting all components are up and running Closes https://github.com/gitlabhq/gitlabhq/issues/9869 --- lib/support/init.d/gitlab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index f0a6c2b30e9..43fda6fa92e 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -327,7 +327,7 @@ print_status() { printf "The GitLab MailRoom email processor is \033[31mnot running\033[0m.\n" fi fi - if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then + if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then printf "GitLab and all its components are \033[32mup and running\033[0m.\n" fi } -- cgit v1.2.1 From 253301bb47d14494ea45230aeb682a9b7dffd5bb Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 16:38:23 -0800 Subject: Make current user the first user in assignee dropdown in issues detail page Closes #3679 --- CHANGELOG | 1 + app/views/shared/issuable/_context.html.haml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 58a0a3c8944..93d59966873 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 8.2.2 - Fix: Raw private snippets access workflow - Prevent "413 Request entity too large" errors when pushing large files with LFS - Fix invalid links within projects dashboard header + - Make current user the first user in assignee dropdown in issues detail page (Stan Hu) v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index f44b439a843..2aa46662613 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -9,7 +9,7 @@ none .issuable-context-selectbox - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true) + = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) %div.prepend-top-default.clearfix .issuable-context-title -- cgit v1.2.1 From aa1ba0093632a66c9c9c0eac710d63d7513ad358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 24 Nov 2015 22:41:36 -0500 Subject: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 --- CHANGELOG | 1 + .../javascripts/merge_request_widget.js.coffee | 9 ++++-- app/helpers/gitlab_routing_helper.rb | 2 +- app/views/projects/merge_requests/merge.js.haml | 2 +- .../merge_requests/widget/_merged.html.haml | 2 +- features/project/merge_requests/accept.feature | 17 +++++++++++ .../steps/project/merge_requests/acceptance.rb | 35 ++++++++++++++++++++++ 7 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 features/project/merge_requests/accept.feature create mode 100644 features/steps/project/merge_requests/acceptance.rb diff --git a/CHANGELOG b/CHANGELOG index 58a0a3c8944..f21129c6d61 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ v 8.3.0 (unreleased) - Don't show project fork event as "imported" - Add API endpoint to fetch merge request commits list - Expose events API with comment information and author info + - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index 3176e5a8965..c4b63966fe7 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -10,17 +10,20 @@ class @MergeRequestWidget constructor: (@opts) -> modal = $('#modal_merge_info').modal(show: false) - mergeInProgress: -> + mergeInProgress: (deleteSourceBranch = false)-> $.ajax type: 'GET' url: $('.merge-request').data('url') success: (data) => if data.state == "merged" - location.reload() + urlSuffix = if deleteSourceBranch then '?delete_source=true' else '' + + window.location.href = window.location.href + urlSuffix else if data.merge_error $('.mr-widget-body').html("

" + data.merge_error + "

") else - setTimeout(merge_request_widget.mergeInProgress, 2000) + callback = -> merge_request_widget.mergeInProgress(deleteSourceBranch) + setTimeout(callback, 2000) dataType: 'json' getMergeStatus: -> diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index b0b536d4649..f3fddef01cb 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -6,7 +6,7 @@ # # For example instead of this: # -# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request) +# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request) # # We can simply use shortcut: # diff --git a/app/views/projects/merge_requests/merge.js.haml b/app/views/projects/merge_requests/merge.js.haml index 33321651e32..518ecb9f00f 100644 --- a/app/views/projects/merge_requests/merge.js.haml +++ b/app/views/projects/merge_requests/merge.js.haml @@ -1,6 +1,6 @@ - if @status :plain - merge_request_widget.mergeInProgress(); + merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'}); - else :plain $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}"); diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index ac08e0b498a..5c6fece8c5c 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -7,7 +7,7 @@ by #{link_to_member(@project, @merge_request.merge_event.author, avatar: true)} #{time_ago_with_tooltip(@merge_request.merge_event.created_at)} %div - - if !@merge_request.source_branch_exists? + - if !@merge_request.source_branch_exists? || (params[:delete_source] == 'true') = succeed '.' do The changes were merged into = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do diff --git a/features/project/merge_requests/accept.feature b/features/project/merge_requests/accept.feature new file mode 100644 index 00000000000..3e6e59a3808 --- /dev/null +++ b/features/project/merge_requests/accept.feature @@ -0,0 +1,17 @@ +Feature: Project Merge Requests Acceptance + Background: + Given There is an open Merge Request + And I am signed in as a developer of the project + + @javascript + Scenario: Accepting the Merge Request and removing the source branch + Given I am on the Merge Request detail page + When I click on "Remove source branch" option + And I click on Accept Merge Request + Then I should not see the Remove Source Branch button + + @javascript + Scenario: Accepting the Merge Request without removing the source branch + Given I am on the Merge Request detail page + When I click on Accept Merge Request + Then I should see the Remove Source Branch button diff --git a/features/steps/project/merge_requests/acceptance.rb b/features/steps/project/merge_requests/acceptance.rb new file mode 100644 index 00000000000..6adecaa8385 --- /dev/null +++ b/features/steps/project/merge_requests/acceptance.rb @@ -0,0 +1,35 @@ +class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps + include LoginHelpers + include GitlabRoutingHelper + + step 'I am on the Merge Request detail page' do + visit merge_request_path(@merge_request) + end + + step 'I click on "Remove source branch" option' do + check('Remove source branch') + end + + step 'I click on Accept Merge Request' do + click_button('Accept Merge Request') + end + + step 'I should see the Remove Source Branch button' do + expect(page).to have_link('Remove Source Branch') + end + + step 'I should not see the Remove Source Branch button' do + expect(page).not_to have_link('Remove Source Branch') + end + + step 'There is an open Merge Request' do + @user = create(:user) + @project = create(:project, :public) + @project_member = create(:project_member, user: @user, project: @project, access_level: ProjectMember::DEVELOPER) + @merge_request = create(:merge_request, :with_diffs, :simple, source_project: @project) + end + + step 'I am signed in as a developer of the project' do + login_as(@user) + end +end -- cgit v1.2.1 From 3c8051776b25add2e2845344a328328db33d1671 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 4 Dec 2015 14:35:29 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 6b61cbc11e9..ad15ed43b74 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,6 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - - Fix application settings cache not expiring after changes (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) @@ -12,6 +11,9 @@ v 8.3.0 (unreleased) - Expose events API with comment information and author info - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 +v 8.2.3 + - Fix application settings cache not expiring after changes (Stan Hu) + v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) - Ensure cached application settings are refreshed at startup (Stan Hu) -- cgit v1.2.1 From 1c53dc28b505f2853750ed4ea8b954385c5bf598 Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Wed, 2 Dec 2015 19:02:15 -0500 Subject: Notify user if they cannot create projects --- app/assets/javascripts/user.js.coffee | 6 ++++++ app/assets/stylesheets/pages/projects.scss | 2 +- app/controllers/profiles_controller.rb | 1 + app/views/dashboard/_projects_head.html.haml | 3 +++ app/views/shared/_project_limit.html.haml | 8 ++++++++ db/migrate/20151203162133_add_hide_project_limit_to_users.rb | 5 +++++ db/schema.rb | 3 ++- 7 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 app/views/shared/_project_limit.html.haml create mode 100644 db/migrate/20151203162133_add_hide_project_limit_to_users.rb diff --git a/app/assets/javascripts/user.js.coffee b/app/assets/javascripts/user.js.coffee index d0d81f96921..ec4271b092c 100644 --- a/app/assets/javascripts/user.js.coffee +++ b/app/assets/javascripts/user.js.coffee @@ -2,3 +2,9 @@ class @User constructor: -> $('.profile-groups-avatars').tooltip("placement": "top") new ProjectsList() + + $('.hide-project-limit-message').on 'click', (e) -> + path = '/' + $.cookie('hide_project_limit_message', 'false', { path: path }) + $(@).parents('.project-limit-message').remove() + e.preventDefault() diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 352f0ba2781..2ded32dba12 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -5,7 +5,7 @@ font-weight: normal; } } -.no-ssh-key-message { +.no-ssh-key-message, .project-limit-message { background-color: #f28d35; margin-bottom: 16px; } diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 8da7b4d50ea..28803164fcf 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -70,6 +70,7 @@ class ProfilesController < Profiles::ApplicationController :email, :hide_no_password, :hide_no_ssh_key, + :hide_project_limit, :linkedin, :location, :name, diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index ed480b8caf8..991e67b1cd3 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -1,3 +1,6 @@ += content_for :flash_message do + = render 'shared/project_limit' + %ul.center-top-menu = nav_link(path: ['projects#index', 'root#index']) do = link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do diff --git a/app/views/shared/_project_limit.html.haml b/app/views/shared/_project_limit.html.haml new file mode 100644 index 00000000000..960ff00b49d --- /dev/null +++ b/app/views/shared/_project_limit.html.haml @@ -0,0 +1,8 @@ +- if cookies[:hide_project_limit_message].blank? && !current_user.hide_project_limit && !current_user.can_create_project? + .project-limit-message.alert.alert-warning.hidden-xs + You won't be able to create new projects because you have reached your project limit. + + .pull-right + = link_to "Don't show again", profile_path(user: {hide_project_limit: true}), method: :put, class: 'alert-link' + | + = link_to 'Remind later', '#', class: 'hide-project-limit-message alert-link' diff --git a/db/migrate/20151203162133_add_hide_project_limit_to_users.rb b/db/migrate/20151203162133_add_hide_project_limit_to_users.rb new file mode 100644 index 00000000000..6ffadfa1894 --- /dev/null +++ b/db/migrate/20151203162133_add_hide_project_limit_to_users.rb @@ -0,0 +1,5 @@ +class AddHideProjectLimitToUsers < ActiveRecord::Migration + def change + add_column :users, :hide_project_limit, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index fbcb711e569..fb59e187625 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: 20151118162244) do +ActiveRecord::Schema.define(version: 20151203162133) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -814,6 +814,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.integer "project_view", default: 0 t.integer "consumed_timestep" t.integer "layout", default: 0 + t.boolean "hide_project_limit", default: false end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree -- cgit v1.2.1 From caa6851bf5a65e454b702104a2895e63e368a21a Mon Sep 17 00:00:00 2001 From: Anton Baklanov Date: Sun, 29 Nov 2015 22:42:54 +0200 Subject: Fixed duplicated issue note email notifications. Fixes #2560 --- CHANGELOG | 1 + app/services/notification_service.rb | 1 + spec/services/notification_service_spec.rb | 9 ++++++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 7b2f1528656..99c5fdd4d07 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,7 @@ v 8.2.2 - Prevent "413 Request entity too large" errors when pushing large files with LFS - Fix invalid links within projects dashboard header - Make current user the first user in assignee dropdown in issues detail page (Stan Hu) + - Fix: duplicate email notifications on issue comments v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 388a4defb26..bdf7b3ad2bb 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -145,6 +145,7 @@ class NotificationService recipients = reject_unsubscribed_users(recipients, note.noteable) recipients.delete(note.author) + recipients = recipients.uniq # build notify method like 'note_commit_email' notify_method = "note_#{note.noteable_type.underscore}_email".to_sym diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index a4e2b2953cc..35fa412ed80 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -45,6 +45,7 @@ describe NotificationService do project.team << [issue.author, :master] project.team << [issue.assignee, :master] project.team << [note.author, :master] + create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@subscribed_participant cc this guy') end describe :new_note do @@ -60,6 +61,7 @@ describe NotificationService do should_email(note.noteable.assignee) should_email(@u_mentioned) should_email(@subscriber) + should_email(@subscribed_participant) should_not_email(note.author) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -381,18 +383,19 @@ describe NotificationService do def add_users_with_subscription(project, issuable) @subscriber = create :user @unsubscriber = create :user + @subscribed_participant = create(:user, username: 'subscribed_participant', notification_level: Notification::N_PARTICIPATING) + project.team << [@subscribed_participant, :master] project.team << [@subscriber, :master] project.team << [@unsubscriber, :master] issuable.subscriptions.create(user: @subscriber, subscribed: true) + issuable.subscriptions.create(user: @subscribed_participant, subscribed: true) issuable.subscriptions.create(user: @unsubscriber, subscribed: false) end def sent_to_user?(user) - ActionMailer::Base.deliveries.any? do |message| - message.to.include?(user.email) - end + ActionMailer::Base.deliveries.map(&:to).flatten.count(user.email) == 1 end def should_email(user) -- cgit v1.2.1