From a6ba8647f919cca5f37f663502186d8b6b7642ec Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 19 Apr 2016 16:00:45 -0400 Subject: [PATCH 001/138] Improve uniqueness of field names on the signup form Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/15075 --- app/controllers/registrations_controller.rb | 7 ++++++ app/views/devise/shared/_signup_box.html.haml | 4 ++-- spec/features/signup_spec.rb | 24 +++++++++---------- spec/features/users_spec.rb | 16 ++++++------- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index c48175a4c5..b441b34d0b 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -8,6 +8,13 @@ class RegistrationsController < Devise::RegistrationsController def create if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha + # To avoid duplicate form fields on the login page, the registration form + # names fields using `new_user`, but Devise still wants the params in + # `user`. + if params["new_#{resource_name}"].present? && params[resource_name].blank? + params[resource_name] = params.delete(:"new_#{resource_name}") + end + super else flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code." diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index e5607dacd0..510215bb8c 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -6,7 +6,7 @@ .login-heading %h3 Create an account .login-body - = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| + = form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name)) do |f| .devise-errors = devise_error_messages! %div @@ -16,7 +16,7 @@ %div = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true .form-group.append-bottom-20#password-strength - = f.password_field :password, class: "form-control bottom", id: "user_password_sign_up", placeholder: "Password", required: true + = f.password_field :password, class: "form-control bottom", placeholder: "Password", required: true %div - if current_application_settings.recaptcha_enabled = recaptcha_tags diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb index 01472743b2..a9b8173389 100644 --- a/spec/features/signup_spec.rb +++ b/spec/features/signup_spec.rb @@ -7,10 +7,10 @@ feature 'Signup', feature: true do visit root_path - fill_in 'user_name', with: user.name - fill_in 'user_username', with: user.username - fill_in 'user_email', with: user.email - fill_in 'user_password_sign_up', with: user.password + fill_in 'new_user_name', with: user.name + fill_in 'new_user_username', with: user.username + fill_in 'new_user_email', with: user.email + fill_in 'new_user_password', with: user.password click_button "Sign up" expect(current_path).to eq user_session_path @@ -25,10 +25,10 @@ feature 'Signup', feature: true do visit root_path - fill_in 'user_name', with: user.name - fill_in 'user_username', with: user.username - fill_in 'user_email', with: existing_user.email - fill_in 'user_password_sign_up', with: user.password + fill_in 'new_user_name', with: user.name + fill_in 'new_user_username', with: user.username + fill_in 'new_user_email', with: existing_user.email + fill_in 'new_user_password', with: user.password click_button "Sign up" expect(current_path).to eq user_registration_path @@ -42,10 +42,10 @@ feature 'Signup', feature: true do visit root_path - fill_in 'user_name', with: user.name - fill_in 'user_username', with: user.username - fill_in 'user_email', with: existing_user.email - fill_in 'user_password_sign_up', with: user.password + fill_in 'new_user_name', with: user.name + fill_in 'new_user_username', with: user.username + fill_in 'new_user_email', with: existing_user.email + fill_in 'new_user_password', with: user.password click_button "Sign up" expect(current_path).to eq user_registration_path diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index c124816203..cf11604039 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -5,10 +5,10 @@ feature 'Users', feature: true do scenario 'GET /users/sign_in creates a new user account' do visit new_user_session_path - fill_in 'user_name', with: 'Name Surname' - fill_in 'user_username', with: 'Great' - fill_in 'user_email', with: 'name@mail.com' - fill_in 'user_password_sign_up', with: 'password1234' + fill_in 'new_user_name', with: 'Name Surname' + fill_in 'new_user_username', with: 'Great' + fill_in 'new_user_email', with: 'name@mail.com' + fill_in 'new_user_password', with: 'password1234' expect { click_button 'Sign up' }.to change { User.count }.by(1) end @@ -31,10 +31,10 @@ feature 'Users', feature: true do scenario 'Should show one error if email is already taken' do visit new_user_session_path - fill_in 'user_name', with: 'Another user name' - fill_in 'user_username', with: 'anotheruser' - fill_in 'user_email', with: user.email - fill_in 'user_password_sign_up', with: '12341234' + fill_in 'new_user_name', with: 'Another user name' + fill_in 'new_user_username', with: 'anotheruser' + fill_in 'new_user_email', with: user.email + fill_in 'new_user_password', with: '12341234' expect { click_button 'Sign up' }.to change { User.count }.by(0) expect(page).to have_text('Email has already been taken') expect(number_of_errors_on_page(page)).to be(1), 'errors on page:\n #{errors_on_page page}' From d3462e711c0b3cc17ef47e1ffffa6f40f98b5e98 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 22 Apr 2016 23:19:55 +0200 Subject: [PATCH 002/138] Fix issue with impersonation --- .../admin/application_controller.rb | 8 +- .../admin/impersonation_controller.rb | 38 -------- .../admin/impersonations_controller.rb | 28 ++++++ app/controllers/admin/users_controller.rb | 16 ++++ app/views/layouts/header/_default.html.haml | 2 +- config/routes.rb | 6 +- .../admin/impersonation_controller_spec.rb | 19 ---- .../admin/impersonations_controller_spec.rb | 95 +++++++++++++++++++ .../admin/users_controller_spec.rb | 49 +++++++++- 9 files changed, 192 insertions(+), 69 deletions(-) delete mode 100644 app/controllers/admin/impersonation_controller.rb create mode 100644 app/controllers/admin/impersonations_controller.rb delete mode 100644 spec/controllers/admin/impersonation_controller_spec.rb create mode 100644 spec/controllers/admin/impersonations_controller_spec.rb diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb index 9083bfb41c..cf795d977c 100644 --- a/app/controllers/admin/application_controller.rb +++ b/app/controllers/admin/application_controller.rb @@ -6,12 +6,6 @@ class Admin::ApplicationController < ApplicationController layout 'admin' def authenticate_admin! - return render_404 unless current_user.is_admin? - end - - def authorize_impersonator! - if session[:impersonator_id] - User.find_by!(username: session[:impersonator_id]).admin? - end + render_404 unless current_user.is_admin? end end diff --git a/app/controllers/admin/impersonation_controller.rb b/app/controllers/admin/impersonation_controller.rb deleted file mode 100644 index bf98af7861..0000000000 --- a/app/controllers/admin/impersonation_controller.rb +++ /dev/null @@ -1,38 +0,0 @@ -class Admin::ImpersonationController < Admin::ApplicationController - skip_before_action :authenticate_admin!, only: :destroy - - before_action :user - before_action :authorize_impersonator! - - def create - if @user.blocked? - flash[:alert] = "You cannot impersonate a blocked user" - - redirect_to admin_user_path(@user) - else - session[:impersonator_id] = current_user.username - session[:impersonator_return_to] = admin_user_path(@user) - - warden.set_user(user, scope: 'user') - - flash[:alert] = "You are impersonating #{user.username}." - - redirect_to root_path - end - end - - def destroy - redirect = session[:impersonator_return_to] - - warden.set_user(user, scope: 'user') - - session[:impersonator_return_to] = nil - session[:impersonator_id] = nil - - redirect_to redirect || root_path - end - - def user - @user ||= User.find_by!(username: params[:id] || session[:impersonator_id]) - end -end diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb new file mode 100644 index 0000000000..1ca3dd8228 --- /dev/null +++ b/app/controllers/admin/impersonations_controller.rb @@ -0,0 +1,28 @@ +class Admin::ImpersonationsController < Admin::ApplicationController + skip_before_action :authenticate_admin! + before_action :authenticate_impersonator! + + def destroy + redirect_path = admin_user_path(current_user) + + warden.set_user(impersonator, scope: :user) + + session[:impersonator_id] = nil + + redirect_to redirect_path + end + + private + + def user + @user ||= User.find(params[:id]) + end + + def impersonator + @impersonator ||= User.find(session[:impersonator_id]) if session[:impersonator_id] + end + + def authenticate_impersonator! + render_404 unless impersonator && impersonator.is_admin? && !impersonator.blocked? + end +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 9abf08d0e1..b8976fa09a 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -31,6 +31,22 @@ class Admin::UsersController < Admin::ApplicationController user end + def impersonate + if user.blocked? + flash[:alert] = "You cannot impersonate a blocked user" + + redirect_to admin_user_path(user) + else + session[:impersonator_id] = current_user.id + + warden.set_user(user, scope: :user) + + flash[:alert] = "You are now impersonating #{user.username}" + + redirect_to root_path + end + end + def block if user.block redirect_back_or_admin_user(notice: "Successfully blocked") diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 3beb8ff7c0..cde9e1b918 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -15,7 +15,7 @@ - if current_user - if session[:impersonator_id] %li.impersonation - = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do + = link_to admin_impersonation_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = icon('user-secret fw') - if current_user.is_admin? %li diff --git a/config/routes.rb b/config/routes.rb index 2f820aafed..9c6fb682fc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -212,8 +212,6 @@ Rails.application.routes.draw do resources :keys, only: [:show, :destroy] resources :identities, except: [:show] - delete 'stop_impersonation' => 'impersonation#destroy', on: :collection - member do get :projects get :keys @@ -223,12 +221,14 @@ Rails.application.routes.draw do put :unblock put :unlock put :confirm - post 'impersonate' => 'impersonation#create' + post :impersonate patch :disable_two_factor delete 'remove/:email_id', action: 'remove_email', as: 'remove_email' end end + resource :impersonation, only: :destroy + resources :abuse_reports, only: [:index, :destroy] resources :spam_logs, only: [:index, :destroy] diff --git a/spec/controllers/admin/impersonation_controller_spec.rb b/spec/controllers/admin/impersonation_controller_spec.rb deleted file mode 100644 index d7a7ba1c5b..0000000000 --- a/spec/controllers/admin/impersonation_controller_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -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/controllers/admin/impersonations_controller_spec.rb b/spec/controllers/admin/impersonations_controller_spec.rb new file mode 100644 index 0000000000..8b48c6b6ea --- /dev/null +++ b/spec/controllers/admin/impersonations_controller_spec.rb @@ -0,0 +1,95 @@ +require 'spec_helper' + +describe Admin::ImpersonationsController do + let(:impersonator) { create(:admin) } + let(:user) { create(:user) } + + describe "DELETE destroy" do + context "when not signed in" do + it "redirects to the sign in page" do + delete :destroy + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when not impersonating" do + it "responds with status 404" do + delete :destroy + + expect(response.status).to eq(404) + end + + it "doesn't sign us in" do + delete :destroy + + expect(warden.user).to eq(user) + end + end + + context "when impersonating" do + before do + session[:impersonator_id] = impersonator.id + end + + context "when the impersonator is not impersonator (anymore)" do + before do + impersonator.admin = false + impersonator.save + end + + it "responds with status 404" do + delete :destroy + + expect(response.status).to eq(404) + end + + it "doesn't sign us in as the impersonator" do + delete :destroy + + expect(warden.user).to eq(user) + end + end + + context "when the impersonator is admin" do + context "when the impersonator is blocked" do + before do + impersonator.block! + end + + it "responds with status 404" do + delete :destroy + + expect(response.status).to eq(404) + end + + it "doesn't sign us in as the impersonator" do + delete :destroy + + expect(warden.user).to eq(user) + end + end + + context "when the impersonator is not blocked" do + it "redirects to the impersonated user's page" do + delete :destroy + + expect(response).to redirect_to(admin_user_path(user)) + end + + it "signs us in as the impersonator" do + delete :destroy + + expect(warden.user).to eq(impersonator) + end + end + end + end + end + end +end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 9ef8ba1b09..ce2a62ae1f 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -2,9 +2,10 @@ require 'spec_helper' describe Admin::UsersController do let(:user) { create(:user) } + let(:admin) { create(:admin) } before do - sign_in(create(:admin)) + sign_in(admin) end describe 'DELETE #user with projects' do @@ -112,4 +113,50 @@ describe Admin::UsersController do patch :disable_two_factor, id: user.to_param end end + + describe "POST impersonate" do + context "when the user is blocked" do + before do + user.block! + end + + it "shows a notice" do + post :impersonate, id: user.username + + expect(flash[:alert]).to eq("You cannot impersonate a blocked user") + end + + it "doesn't sign us in as the user" do + post :impersonate, id: user.username + + expect(warden.user).to eq(admin) + end + end + + context "when the user is not blocked" do + it "stores the impersonator in the session" do + post :impersonate, id: user.username + + expect(session[:impersonator_id]).to eq(admin.id) + end + + it "signs us in as the user" do + post :impersonate, id: user.username + + expect(warden.user).to eq(user) + end + + it "redirects to root" do + post :impersonate, id: user.username + + expect(response).to redirect_to(root_path) + end + + it "shows a notice" do + post :impersonate, id: user.username + + expect(flash[:alert]).to eq("You are now impersonating #{user.username}") + end + end + end end From b992e2520cc93b3161f6e7c46dde66f10fe13a12 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 22 Apr 2016 21:46:47 +0000 Subject: [PATCH 003/138] Fix typo --- spec/controllers/admin/impersonations_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/admin/impersonations_controller_spec.rb b/spec/controllers/admin/impersonations_controller_spec.rb index 8b48c6b6ea..eb82476b17 100644 --- a/spec/controllers/admin/impersonations_controller_spec.rb +++ b/spec/controllers/admin/impersonations_controller_spec.rb @@ -37,7 +37,7 @@ describe Admin::ImpersonationsController do session[:impersonator_id] = impersonator.id end - context "when the impersonator is not impersonator (anymore)" do + context "when the impersonator is not admin (anymore)" do before do impersonator.admin = false impersonator.save From 0ab98a8a407cb9764c9c28554c8f077906a9eb9b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 22 Apr 2016 21:55:49 +0000 Subject: [PATCH 004/138] Remove unused method --- app/controllers/admin/impersonations_controller.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb index 1ca3dd8228..2d64923c47 100644 --- a/app/controllers/admin/impersonations_controller.rb +++ b/app/controllers/admin/impersonations_controller.rb @@ -14,10 +14,6 @@ class Admin::ImpersonationsController < Admin::ApplicationController private - def user - @user ||= User.find(params[:id]) - end - def impersonator @impersonator ||= User.find(session[:impersonator_id]) if session[:impersonator_id] end From c6c985bc690c88f2819787824a50b22cc86cacf4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 22 Apr 2016 21:58:09 +0000 Subject: [PATCH 005/138] Store original user in variable --- app/controllers/admin/impersonations_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb index 2d64923c47..2db824c87e 100644 --- a/app/controllers/admin/impersonations_controller.rb +++ b/app/controllers/admin/impersonations_controller.rb @@ -3,13 +3,13 @@ class Admin::ImpersonationsController < Admin::ApplicationController before_action :authenticate_impersonator! def destroy - redirect_path = admin_user_path(current_user) + original_user = current_user warden.set_user(impersonator, scope: :user) session[:impersonator_id] = nil - redirect_to redirect_path + redirect_to admin_user_path(original_user) end private From 03ae2cdbff49d4f72d32529963a2173c7308da40 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 24 Apr 2016 20:07:59 -0700 Subject: [PATCH 006/138] Filter confidential issues from milestones API if user does not have access Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/15579 --- CHANGELOG | 2 ++ lib/api/milestones.rb | 10 ++++++- spec/requests/api/milestones_spec.rb | 40 +++++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2d1c561fb8..4ce16b4449 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,8 @@ v 8.7.1 (unreleased) - Fix license detection to detect all license files, not only known licenses. !3878 - Use the `can?` helper instead of `current_user.can?`. !3882 - Prevent users from deleting Webhooks via API they do not own + - Use the `can?` helper instead of `current_user.can?` + - Filter confidential issues from milestones API if user does not have access v 8.7.0 - Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index 84b4d4cdd6..132043cf3f 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -105,7 +105,15 @@ module API authorize! :read_milestone, user_project @milestone = user_project.milestones.find(params[:milestone_id]) - present paginate(@milestone.issues), with: Entities::Issue, current_user: current_user + + finder_params = { + project_id: user_project.id, + milestone_title: @milestone.title, + state: 'all' + } + + issues = IssuesFinder.new(current_user, finder_params).execute + present paginate(issues), with: Entities::Issue, current_user: current_user end end diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index 344f0fe0b7..cb9c3dde5e 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -127,7 +127,7 @@ describe API::API, api: true do describe 'GET /projects/:id/milestones/:milestone_id/issues' do before do - milestone.issues << create(:issue) + milestone.issues << create(:issue, project: project) end it 'should return project issues for a particular milestone' do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) @@ -141,4 +141,42 @@ describe API::API, api: true do expect(response.status).to eq(401) end end + + describe 'confidential issues' do + it 'should return confidential issues to team members' do + public_project = create(:project, :public) + user = create(:user) + milestone = create(:milestone, project: public_project) + issue = create(:issue, project: public_project) + confidential_issue = create(:issue, confidential: true, project: public_project) + public_project.team << [user, :developer] + milestone.issues << issue + milestone.issues << confidential_issue + + get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(2) + expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) + end + + it 'should not return confidential issues to regular users' do + public_project = create(:project, :public) + normal_user = create(:user) + milestone = create(:milestone, project: public_project) + issue = create(:issue, project: public_project) + confidential_issue = create(:issue, confidential: true, project: public_project) + public_project.team << [user, :developer] + milestone.issues << issue + milestone.issues << confidential_issue + + get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", normal_user) + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(1) + expect(json_response.map { |issue| issue['id'] }).to include(issue.id) + end + end end From 97bd349146bfb3acef77ec413cd0def552d00472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 25 Apr 2016 11:49:52 +0200 Subject: [PATCH 007/138] Improve Milestones API specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- CHANGELOG | 2 - spec/requests/api/milestones_spec.rb | 55 ++++++++++++---------------- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4ce16b4449..2d1c561fb8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,8 +8,6 @@ v 8.7.1 (unreleased) - Fix license detection to detect all license files, not only known licenses. !3878 - Use the `can?` helper instead of `current_user.can?`. !3882 - Prevent users from deleting Webhooks via API they do not own - - Use the `can?` helper instead of `current_user.can?` - - Filter confidential issues from milestones API if user does not have access v 8.7.0 - Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index cb9c3dde5e..241995041b 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -140,43 +140,34 @@ describe API::API, api: true do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues") expect(response.status).to eq(401) end - end - describe 'confidential issues' do - it 'should return confidential issues to team members' do - public_project = create(:project, :public) - user = create(:user) - milestone = create(:milestone, project: public_project) - issue = create(:issue, project: public_project) - confidential_issue = create(:issue, confidential: true, project: public_project) - public_project.team << [user, :developer] - milestone.issues << issue - milestone.issues << confidential_issue + describe 'confidential issues' do + let(:public_project) { create(:project, :public) } + let(:milestone) { create(:milestone, project: public_project) } + let(:issue) { create(:issue, project: public_project) } + let(:confidential_issue) { create(:issue, confidential: true, project: public_project) } + before do + public_project.team << [user, :developer] + milestone.issues << issue << confidential_issue + end - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) + it 'returns confidential issues to team members' do + get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) - expect(response.status).to eq(200) - expect(json_response).to be_an Array - expect(json_response.size).to eq(2) - expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) - end + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(2) + expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) + end - it 'should not return confidential issues to regular users' do - public_project = create(:project, :public) - normal_user = create(:user) - milestone = create(:milestone, project: public_project) - issue = create(:issue, project: public_project) - confidential_issue = create(:issue, confidential: true, project: public_project) - public_project.team << [user, :developer] - milestone.issues << issue - milestone.issues << confidential_issue + it 'does not return confidential issues to regular users' do + get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user)) - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", normal_user) - - expect(response.status).to eq(200) - expect(json_response).to be_an Array - expect(json_response.size).to eq(1) - expect(json_response.map { |issue| issue['id'] }).to include(issue.id) + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(1) + expect(json_response.map { |issue| issue['id'] }).to include(issue.id) + end end end end From d5267dfd0dac8e4cab4919bf8aca611de3a5497b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 24 Apr 2016 21:45:26 -0700 Subject: [PATCH 008/138] Prevent private snippets in public/internal projects from being leaked via API Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/15580 --- app/finders/snippets_finder.rb | 2 +- lib/api/project_snippets.rb | 15 ++-- spec/requests/api/project_snippets_spec.rb | 87 ++++++++++++++++++++++ spec/requests/api/projects_spec.rb | 2 +- 4 files changed, 99 insertions(+), 7 deletions(-) diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb index a41172816b..01cbf91c65 100644 --- a/app/finders/snippets_finder.rb +++ b/app/finders/snippets_finder.rb @@ -51,7 +51,7 @@ class SnippetsFinder snippets = project.snippets.fresh if current_user - if project.team.member?(current_user.id) + if project.team.member?(current_user.id) || current_user.admin? snippets else snippets.public_and_internal diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb index 22ce3c6a06..ce1bf0d26d 100644 --- a/lib/api/project_snippets.rb +++ b/lib/api/project_snippets.rb @@ -11,6 +11,11 @@ module API end not_found! end + + def snippets_for_current_user + finder_params = { filter: :by_project, project: user_project } + SnippetsFinder.new.execute(current_user, finder_params) + end end # Get a project snippets @@ -20,7 +25,7 @@ module API # Example Request: # GET /projects/:id/snippets get ":id/snippets" do - present paginate(user_project.snippets), with: Entities::ProjectSnippet + present paginate(snippets_for_current_user), with: Entities::ProjectSnippet end # Get a project snippet @@ -31,7 +36,7 @@ module API # Example Request: # GET /projects/:id/snippets/:snippet_id get ":id/snippets/:snippet_id" do - @snippet = user_project.snippets.find(params[:snippet_id]) + @snippet = snippets_for_current_user.find(params[:snippet_id]) present @snippet, with: Entities::ProjectSnippet end @@ -73,7 +78,7 @@ module API # Example Request: # PUT /projects/:id/snippets/:snippet_id put ":id/snippets/:snippet_id" do - @snippet = user_project.snippets.find(params[:snippet_id]) + @snippet = snippets_for_current_user.find(params[:snippet_id]) authorize! :update_project_snippet, @snippet attrs = attributes_for_keys [:title, :file_name, :visibility_level] @@ -97,7 +102,7 @@ module API # DELETE /projects/:id/snippets/:snippet_id delete ":id/snippets/:snippet_id" do begin - @snippet = user_project.snippets.find(params[:snippet_id]) + @snippet = snippets_for_current_user.find(params[:snippet_id]) authorize! :update_project_snippet, @snippet @snippet.destroy rescue @@ -113,7 +118,7 @@ module API # Example Request: # GET /projects/:id/snippets/:snippet_id/raw get ":id/snippets/:snippet_id/raw" do - @snippet = user_project.snippets.find(params[:snippet_id]) + @snippet = snippets_for_current_user.find(params[:snippet_id]) env['api.format'] = :txt content_type 'text/plain' diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index 3722ddf5a3..9706d060cf 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -15,4 +15,91 @@ describe API::API, api: true do expect(json_response['expires_at']).to be_nil end end + + describe 'GET /projects/:project_id/snippets/' do + it 'all snippets available to team member' do + project = create(:project, :public) + user = create(:user) + project.team << [user, :developer] + public_snippet = create(:project_snippet, :public, project: project) + internal_snippet = create(:project_snippet, :internal, project: project) + private_snippet = create(:project_snippet, :private, project: project) + + get api("/projects/#{project.id}/snippets/", user) + + expect(response.status).to eq(200) + expect(json_response.size).to eq(3) + expect(json_response.map{ |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id) + end + + it 'hides private snippets from regular user' do + project = create(:project, :public) + user = create(:user) + create(:project_snippet, :private, project: project) + + get api("/projects/#{project.id}/snippets/", user) + expect(response.status).to eq(200) + expect(json_response.size).to eq(0) + end + end + + describe 'POST /projects/:project_id/snippets/' do + it 'creates a new snippet' do + admin = create(:admin) + project = create(:project) + params = { + title: 'Test Title', + file_name: 'test.rb', + code: 'puts "hello world"', + visibility_level: Gitlab::VisibilityLevel::PUBLIC + } + + post api("/projects/#{project.id}/snippets/", admin), params + + expect(response.status).to eq(201) + snippet = ProjectSnippet.find(json_response['id']) + expect(snippet.content).to eq(params[:code]) + expect(snippet.title).to eq(params[:title]) + expect(snippet.file_name).to eq(params[:file_name]) + expect(snippet.visibility_level).to eq(params[:visibility_level]) + end + end + + describe 'PUT /projects/:project_id/snippets/:id/' do + it 'updates snippet' do + admin = create(:admin) + snippet = create(:project_snippet, author: admin) + new_content = 'New content' + + put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content + + expect(response.status).to eq(200) + snippet.reload + expect(snippet.content).to eq(new_content) + end + end + + describe 'DELETE /projects/:project_id/snippets/:id/' do + it 'deletes snippet' do + admin = create(:admin) + snippet = create(:project_snippet, author: admin) + + delete api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin) + + expect(response.status).to eq(200) + end + end + + describe 'GET /projects/:project_id/snippets/:id/raw' do + it 'returns raw text' do + admin = create(:admin) + snippet = create(:project_snippet, author: admin) + + get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin) + + expect(response.status).to eq(200) + expect(response.content_type).to eq 'text/plain' + expect(response.body).to eq(snippet.content) + end + end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index fccd08bd6d..66193eac05 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -11,7 +11,7 @@ describe API::API, api: true do let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) } let(:project3) { create(:project, path: 'project3', creator_id: user.id, namespace: user.namespace) } - let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') } + let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') } let(:project_member) { create(:project_member, :master, user: user, project: project) } let(:project_member2) { create(:project_member, :developer, user: user3, project: project) } let(:user4) { create(:user) } From ef340f6e777875e1bbb38752e64ba7bea3ab2f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 21 Apr 2016 17:13:14 +0200 Subject: [PATCH 009/138] Ensure URL in all Service subclasses are valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/helpers/issues_helper.rb | 48 +++++--- .../project_services/buildkite_service.rb | 4 +- .../project_services/issue_tracker_service.rb | 2 +- app/models/project_services/jira_service.rb | 2 + app/models/project_services/slack_service.rb | 2 +- spec/helpers/issues_helper_spec.rb | 36 ++++++ .../project_services/bamboo_service_spec.rb | 109 ++++++------------ .../buildkite_service_spec.rb | 17 +++ .../builds_email_service_spec.rb | 81 ++++++------- .../project_services/campfire_service_spec.rb | 42 +++++++ .../custom_issue_tracker_service_spec.rb | 49 ++++++++ .../project_services/drone_ci_service_spec.rb | 13 +-- .../emails_on_push_service_spec.rb | 17 +++ .../external_wiki_service_spec.rb | 17 ++- .../project_services/flowdock_service_spec.rb | 14 +++ .../gemnasium_service_spec.rb | 16 +++ .../gitlab_issue_tracker_service_spec.rb | 14 +++ .../project_services/hipchat_service_spec.rb | 14 +++ .../project_services/irker_service_spec.rb | 14 ++- .../project_services/jira_service_spec.rb | 26 ++++- .../pivotaltracker_service_spec.rb | 42 +++++++ .../project_services/pushover_service_spec.rb | 20 ++-- .../project_services/redmine_service_spec.rb | 49 ++++++++ .../project_services/slack_service_spec.rb | 17 ++- .../project_services/teamcity_service_spec.rb | 109 ++++++------------ .../issue_tracker_service_shared_example.rb | 7 ++ 26 files changed, 539 insertions(+), 242 deletions(-) create mode 100644 spec/models/project_services/campfire_service_spec.rb create mode 100644 spec/models/project_services/custom_issue_tracker_service_spec.rb create mode 100644 spec/models/project_services/emails_on_push_service_spec.rb rename spec/models/{ => project_services}/external_wiki_service_spec.rb (78%) create mode 100644 spec/models/project_services/pivotaltracker_service_spec.rb create mode 100644 spec/models/project_services/redmine_service_spec.rb create mode 100644 spec/support/issue_tracker_service_shared_example.rb diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index afe1e11a0d..198d39455d 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -16,31 +16,49 @@ module IssuesHelper def url_for_project_issues(project = @project, options = {}) return '' if project.nil? - if options[:only_path] - project.issues_tracker.project_path - else - project.issues_tracker.project_url - end + url = + if options[:only_path] + project.issues_tracker.project_path + else + project.issues_tracker.project_url + end + + # Ensure we return a valid URL to prevent possible XSS. + URI.parse(url).to_s + rescue URI::InvalidURIError + '' end def url_for_new_issue(project = @project, options = {}) return '' if project.nil? - if options[:only_path] - project.issues_tracker.new_issue_path - else - project.issues_tracker.new_issue_url - end + url = + if options[:only_path] + project.issues_tracker.new_issue_path + else + project.issues_tracker.new_issue_url + end + + # Ensure we return a valid URL to prevent possible XSS. + URI.parse(url).to_s + rescue URI::InvalidURIError + '' end def url_for_issue(issue_iid, project = @project, options = {}) return '' if project.nil? - if options[:only_path] - project.issues_tracker.issue_path(issue_iid) - else - project.issues_tracker.issue_url(issue_iid) - end + url = + if options[:only_path] + project.issues_tracker.issue_path(issue_iid) + else + project.issues_tracker.issue_url(issue_iid) + end + + # Ensure we return a valid URL to prevent possible XSS. + URI.parse(url).to_s + rescue URI::InvalidURIError + '' end def bulk_update_milestone_options diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index 3efbfd2eec..861cc974ec 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -26,7 +26,7 @@ class BuildkiteService < CiService prop_accessor :project_url, :token, :enable_ssl_verification - validates :project_url, presence: true, if: :activated? + validates :project_url, presence: true, url: true, if: :activated? validates :token, presence: true, if: :activated? after_save :compose_service_hook, if: :activated? @@ -91,7 +91,7 @@ class BuildkiteService < CiService { type: 'text', name: 'project_url', placeholder: "#{ENDPOINT}/example/project" }, - + { type: 'checkbox', name: 'enable_ssl_verification', title: "Enable SSL verification" } diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 25045224ce..c5501e0641 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -21,7 +21,7 @@ class IssueTrackerService < Service - validates :project_url, :issues_url, :new_issue_url, presence: true, if: :activated? + validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated? default_value_for :category, 'issue_tracker' diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 1ed42c4f3e..b4418ba928 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -28,6 +28,8 @@ class JiraService < IssueTrackerService prop_accessor :username, :password, :api_url, :jira_issue_transition_id, :title, :description, :project_url, :issues_url, :new_issue_url + validates :api_url, presence: true, url: true, if: :activated? + before_validation :set_api_url, :set_jira_issue_transition_id before_update :reset_password diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index fd65027f08..7092b75754 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -22,7 +22,7 @@ class SlackService < Service prop_accessor :webhook, :username, :channel boolean_accessor :notify_only_broken_builds - validates :webhook, presence: true, if: :activated? + validates :webhook, presence: true, url: true, if: :activated? def initialize_properties if properties.nil? diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 543593cf38..bffe2c18b6 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -30,6 +30,18 @@ describe IssuesHelper do expect(url_for_project_issues).to eq "" end + it 'returns an empty string if project_url is invalid' do + expect(project).to receive_message_chain('issues_tracker.project_url') { 'javascript:alert("foo");' } + + expect(url_for_project_issues(project)).to eq '' + end + + it 'returns an empty string if project_path is invalid' do + expect(project).to receive_message_chain('issues_tracker.project_path') { 'javascript:alert("foo");' } + + expect(url_for_project_issues(project, only_path: true)).to eq '' + end + describe "when external tracker was enabled and then config removed" do before do @project = ext_project @@ -68,6 +80,18 @@ describe IssuesHelper do expect(url_for_issue(issue.iid)).to eq "" end + it 'returns an empty string if issue_url is invalid' do + expect(project).to receive_message_chain('issues_tracker.issue_url') { 'javascript:alert("foo");' } + + expect(url_for_issue(issue.iid, project)).to eq '' + end + + it 'returns an empty string if issue_path is invalid' do + expect(project).to receive_message_chain('issues_tracker.issue_path') { 'javascript:alert("foo");' } + + expect(url_for_issue(issue.iid, project, only_path: true)).to eq '' + end + describe "when external tracker was enabled and then config removed" do before do @project = ext_project @@ -105,6 +129,18 @@ describe IssuesHelper do expect(url_for_new_issue).to eq "" end + it 'returns an empty string if issue_url is invalid' do + expect(project).to receive_message_chain('issues_tracker.new_issue_url') { 'javascript:alert("foo");' } + + expect(url_for_new_issue(project)).to eq '' + end + + it 'returns an empty string if issue_path is invalid' do + expect(project).to receive_message_chain('issues_tracker.new_issue_path') { 'javascript:alert("foo");' } + + expect(url_for_new_issue(project, only_path: true)).to eq '' + end + describe "when external tracker was enabled and then config removed" do before do @project = ext_project diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index 31b2c90122..e771f35811 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -27,86 +27,51 @@ describe BambooService, models: true do end describe 'Validations' do - describe '#bamboo_url' do - it 'does not validate the presence of bamboo_url if service is not active' do - bamboo_service = service - bamboo_service.active = false + subject { service } - expect(bamboo_service).not_to validate_presence_of(:bamboo_url) + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:build_key) } + it { is_expected.to validate_presence_of(:bamboo_url) } + it_behaves_like 'issue tracker service URL attribute', :bamboo_url + + describe '#username' do + it 'does not validate the presence of username if password is nil' do + subject.password = nil + + expect(subject).not_to validate_presence_of(:username) + end + + it 'validates the presence of username if password is present' do + subject.password = 'secret' + + expect(subject).to validate_presence_of(:username) + end end - it 'validates the presence of bamboo_url if service is active' do - bamboo_service = service - bamboo_service.active = true + describe '#password' do + it 'does not validate the presence of password if username is nil' do + subject.username = nil - expect(bamboo_service).to validate_presence_of(:bamboo_url) + expect(subject).not_to validate_presence_of(:password) + end + + it 'validates the presence of password if username is present' do + subject.username = 'john' + + expect(subject).to validate_presence_of(:password) + end end end - describe '#build_key' do - it 'does not validate the presence of build_key if service is not active' do - bamboo_service = service - bamboo_service.active = false + context 'when service is inactive' do + before { subject.active = false } - expect(bamboo_service).not_to validate_presence_of(:build_key) - end - - it 'validates the presence of build_key if service is active' do - bamboo_service = service - bamboo_service.active = true - - expect(bamboo_service).to validate_presence_of(:build_key) - end - end - - describe '#username' do - it 'does not validate the presence of username if service is not active' do - bamboo_service = service - bamboo_service.active = false - - expect(bamboo_service).not_to validate_presence_of(:username) - end - - it 'does not validate the presence of username if username is nil' do - bamboo_service = service - bamboo_service.active = true - bamboo_service.password = nil - - expect(bamboo_service).not_to validate_presence_of(:username) - end - - it 'validates the presence of username if service is active and username is present' do - bamboo_service = service - bamboo_service.active = true - bamboo_service.password = 'secret' - - expect(bamboo_service).to validate_presence_of(:username) - end - end - - describe '#password' do - it 'does not validate the presence of password if service is not active' do - bamboo_service = service - bamboo_service.active = false - - expect(bamboo_service).not_to validate_presence_of(:password) - end - - it 'does not validate the presence of password if username is nil' do - bamboo_service = service - bamboo_service.active = true - bamboo_service.username = nil - - expect(bamboo_service).not_to validate_presence_of(:password) - end - - it 'validates the presence of password if service is active and username is present' do - bamboo_service = service - bamboo_service.active = true - bamboo_service.username = 'john' - - expect(bamboo_service).to validate_presence_of(:password) - end + it { is_expected.not_to validate_presence_of(:build_key) } + it { is_expected.not_to validate_presence_of(:bamboo_url) } + it { is_expected.not_to validate_presence_of(:username) } + it { is_expected.not_to validate_presence_of(:password) } end end diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index 88cd624877..60364df201 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -26,6 +26,23 @@ describe BuildkiteService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:token) } + it_behaves_like 'issue tracker service URL attribute', :project_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:token) } + end + end + describe 'commits methods' do before do @project = Project.new diff --git a/spec/models/project_services/builds_email_service_spec.rb b/spec/models/project_services/builds_email_service_spec.rb index 7c23c2efcc..236df8f047 100644 --- a/spec/models/project_services/builds_email_service_spec.rb +++ b/spec/models/project_services/builds_email_service_spec.rb @@ -1,76 +1,71 @@ require 'spec_helper' describe BuildsEmailService do - let(:build) { create(:ci_build) } - let(:data) { Gitlab::BuildDataBuilder.build(build) } - let!(:project) { create(:project, :public, ci_id: 1) } - let(:service) { described_class.new(project: project, active: true) } + let(:data) { Gitlab::BuildDataBuilder.build(create(:ci_build)) } + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:recipients) } + + context 'when pusher is added' do + before { subject.add_pusher = true } + + it { is_expected.not_to validate_presence_of(:recipients) } + end + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:recipients) } + end + end describe '#execute' do it 'sends email' do - service.recipients = 'test@gitlab.com' + subject.recipients = 'test@gitlab.com' data[:build_status] = 'failed' + expect(BuildEmailWorker).to receive(:perform_async) - service.execute(data) + + subject.execute(data) end it 'does not send email with succeeded build and notify_only_broken_builds on' do - expect(service).to receive(:notify_only_broken_builds).and_return(true) + expect(subject).to receive(:notify_only_broken_builds).and_return(true) data[:build_status] = 'success' + expect(BuildEmailWorker).not_to receive(:perform_async) - service.execute(data) + + subject.execute(data) end it 'does not send email with failed build and build_allow_failure is true' do data[:build_status] = 'failed' data[:build_allow_failure] = true + expect(BuildEmailWorker).not_to receive(:perform_async) - service.execute(data) + + subject.execute(data) end it 'does not send email with unknown build status' do data[:build_status] = 'foo' + expect(BuildEmailWorker).not_to receive(:perform_async) - service.execute(data) + + subject.execute(data) end it 'does not send email when recipients list is empty' do - service.recipients = ' ,, ' + subject.recipients = ' ,, ' data[:build_status] = 'failed' + expect(BuildEmailWorker).not_to receive(:perform_async) - service.execute(data) - end - end - describe 'validations' do - - context 'when pusher is not added' do - before { service.add_pusher = false } - - it 'does not allow empty recipient input' do - service.recipients = '' - expect(service.valid?).to be false - end - - it 'does allow non-empty recipient input' do - service.recipients = 'test@example.com' - expect(service.valid?).to be true - end - - end - - context 'when pusher is added' do - before { service.add_pusher = true } - - it 'does allow empty recipient input' do - service.recipients = '' - expect(service.valid?).to be true - end - - it 'does allow non-empty recipient input' do - service.recipients = 'test@example.com' - expect(service.valid?).to be true - end + subject.execute(data) end end end diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb new file mode 100644 index 0000000000..3e6da42803 --- /dev/null +++ b/spec/models/project_services/campfire_service_spec.rb @@ -0,0 +1,42 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe CampfireService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + end + end +end diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb new file mode 100644 index 0000000000..ff976f8ec5 --- /dev/null +++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb @@ -0,0 +1,49 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe CustomIssueTrackerService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:issues_url) } + it { is_expected.to validate_presence_of(:new_issue_url) } + it_behaves_like 'issue tracker service URL attribute', :project_url + it_behaves_like 'issue tracker service URL attribute', :issues_url + it_behaves_like 'issue tracker service URL attribute', :new_issue_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:issues_url) } + it { is_expected.not_to validate_presence_of(:new_issue_url) } + end + end +end diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index a2cf68a9e3..3a8e67438f 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -28,25 +28,18 @@ describe DroneCiService, models: true do describe 'validations' do context 'active' do - before { allow(subject).to receive(:activated?).and_return(true) } + before { subject.active = true } it { is_expected.to validate_presence_of(:token) } it { is_expected.to validate_presence_of(:drone_url) } - it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } - it { is_expected.to allow_value('http://ci.example.com').for(:drone_url) } - it { is_expected.not_to allow_value('this is not url').for(:drone_url) } - it { is_expected.not_to allow_value('http//noturl').for(:drone_url) } - it { is_expected.not_to allow_value('ftp://ci.example.com').for(:drone_url) } + it_behaves_like 'issue tracker service URL attribute', :drone_url end context 'inactive' do - before { allow(subject).to receive(:activated?).and_return(false) } + before { subject.active = false } it { is_expected.not_to validate_presence_of(:token) } it { is_expected.not_to validate_presence_of(:drone_url) } - it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } - it { is_expected.to allow_value('http://drone.example.com').for(:drone_url) } - it { is_expected.to allow_value('ftp://drone.example.com').for(:drone_url) } end end diff --git a/spec/models/project_services/emails_on_push_service_spec.rb b/spec/models/project_services/emails_on_push_service_spec.rb new file mode 100644 index 0000000000..e6f78898c8 --- /dev/null +++ b/spec/models/project_services/emails_on_push_service_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe EmailsOnPushService do + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:recipients) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:recipients) } + end + end +end diff --git a/spec/models/external_wiki_service_spec.rb b/spec/models/project_services/external_wiki_service_spec.rb similarity index 78% rename from spec/models/external_wiki_service_spec.rb rename to spec/models/project_services/external_wiki_service_spec.rb index d37978720b..5fe5ea7d2d 100644 --- a/spec/models/external_wiki_service_spec.rb +++ b/spec/models/project_services/external_wiki_service_spec.rb @@ -28,13 +28,18 @@ describe ExternalWikiService, models: true do it { should have_one :service_hook } end - describe "Validations" do - context "active" do - before do - subject.active = true - end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } - it { should validate_presence_of :external_wiki_url } + it { is_expected.to validate_presence_of(:external_wiki_url) } + it_behaves_like 'issue tracker service URL attribute', :external_wiki_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:external_wiki_url) } end end diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb index ff7fbcaa00..b7e627e651 100644 --- a/spec/models/project_services/flowdock_service_spec.rb +++ b/spec/models/project_services/flowdock_service_spec.rb @@ -26,6 +26,20 @@ describe FlowdockService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + end + end + describe "Execute" do let(:user) { create(:user) } let(:project) { create(:project) } diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb index ecb3ccb167..a08f1ac229 100644 --- a/spec/models/project_services/gemnasium_service_spec.rb +++ b/spec/models/project_services/gemnasium_service_spec.rb @@ -26,6 +26,22 @@ describe GemnasiumService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + it { is_expected.to validate_presence_of(:api_key) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + it { is_expected.not_to validate_presence_of(:api_key) } + end + end + describe "Execute" do let(:user) { create(:user) } let(:project) { create(:project) } diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb index 3518dbd172..7a1f106d6e 100644 --- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb +++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb @@ -26,6 +26,20 @@ describe GitlabIssueTrackerService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + subject { described_class.new(project: create(:project), active: true) } + + it { is_expected.to validate_presence_of(:issues_url) } + it_behaves_like 'issue tracker service URL attribute', :issues_url + end + + context 'when service is inactive' do + subject { described_class.new(project: create(:project), active: false) } + + it { is_expected.not_to validate_presence_of(:issues_url) } + end + end describe 'project and issue urls' do let(:project) { create(:project) } diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index d878162a22..6fb5cad501 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -26,6 +26,20 @@ describe HipchatService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + end + end + describe "Execute" do let(:hipchat) { HipchatService.new } let(:user) { create(:user, username: 'username') } diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb index b783b1a576..4ee022a517 100644 --- a/spec/models/project_services/irker_service_spec.rb +++ b/spec/models/project_services/irker_service_spec.rb @@ -29,14 +29,16 @@ describe IrkerService, models: true do end describe 'Validations' do - before do - subject.active = true - subject.properties['recipients'] = _recipients + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:recipients) } end - context 'active' do - let(:_recipients) { nil } - it { should validate_presence_of :recipients } + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:recipients) } end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 2f8193170a..5309cfb99f 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -26,6 +26,30 @@ describe JiraService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:api_url) } + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:issues_url) } + it { is_expected.to validate_presence_of(:new_issue_url) } + it_behaves_like 'issue tracker service URL attribute', :api_url + it_behaves_like 'issue tracker service URL attribute', :project_url + it_behaves_like 'issue tracker service URL attribute', :issues_url + it_behaves_like 'issue tracker service URL attribute', :new_issue_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:api_url) } + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:issues_url) } + it { is_expected.not_to validate_presence_of(:new_issue_url) } + end + end + describe "Execute" do let(:user) { create(:user) } let(:project) { create(:project) } @@ -72,7 +96,7 @@ describe JiraService, models: true do context "when a password was previously set" do before do - @jira_service = JiraService.create( + @jira_service = JiraService.create!( project: create(:project), properties: { api_url: 'http://jira.example.com/rest/api/2', diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/project_services/pivotaltracker_service_spec.rb new file mode 100644 index 0000000000..f37edd4d97 --- /dev/null +++ b/spec/models/project_services/pivotaltracker_service_spec.rb @@ -0,0 +1,42 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe PivotaltrackerService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + end + end +end diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb index 96039f9491..555d9757b4 100644 --- a/spec/models/project_services/pushover_service_spec.rb +++ b/spec/models/project_services/pushover_service_spec.rb @@ -27,14 +27,20 @@ describe PushoverService, models: true do end describe 'Validations' do - context 'active' do - before do - subject.active = true - end + context 'when service is active' do + before { subject.active = true } - it { is_expected.to validate_presence_of :api_key } - it { is_expected.to validate_presence_of :user_key } - it { is_expected.to validate_presence_of :priority } + it { is_expected.to validate_presence_of(:api_key) } + it { is_expected.to validate_presence_of(:user_key) } + it { is_expected.to validate_presence_of(:priority) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:api_key) } + it { is_expected.not_to validate_presence_of(:user_key) } + it { is_expected.not_to validate_presence_of(:priority) } end end diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb new file mode 100644 index 0000000000..7d14f6e828 --- /dev/null +++ b/spec/models/project_services/redmine_service_spec.rb @@ -0,0 +1,49 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe RedmineService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:issues_url) } + it { is_expected.to validate_presence_of(:new_issue_url) } + it_behaves_like 'issue tracker service URL attribute', :project_url + it_behaves_like 'issue tracker service URL attribute', :issues_url + it_behaves_like 'issue tracker service URL attribute', :new_issue_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:issues_url) } + it { is_expected.not_to validate_presence_of(:new_issue_url) } + end + end +end diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index 478d59be08..a97b756013 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -26,13 +26,18 @@ describe SlackService, models: true do it { is_expected.to have_one :service_hook } end - describe "Validations" do - context "active" do - before do - subject.active = true - end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } - it { is_expected.to validate_presence_of :webhook } + it { is_expected.to validate_presence_of(:webhook) } + it_behaves_like 'issue tracker service URL attribute', :webhook + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:webhook) } end end diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index bc7423cee6..ad24b89517 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -27,86 +27,51 @@ describe TeamcityService, models: true do end describe 'Validations' do - describe '#teamcity_url' do - it 'does not validate the presence of teamcity_url if service is not active' do - teamcity_service = service - teamcity_service.active = false + subject { service } - expect(teamcity_service).not_to validate_presence_of(:teamcity_url) + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:build_type) } + it { is_expected.to validate_presence_of(:teamcity_url) } + it_behaves_like 'issue tracker service URL attribute', :teamcity_url + + describe '#username' do + it 'does not validate the presence of username if password is nil' do + subject.password = nil + + expect(subject).not_to validate_presence_of(:username) + end + + it 'validates the presence of username if password is present' do + subject.password = 'secret' + + expect(subject).to validate_presence_of(:username) + end end - it 'validates the presence of teamcity_url if service is active' do - teamcity_service = service - teamcity_service.active = true + describe '#password' do + it 'does not validate the presence of password if username is nil' do + subject.username = nil - expect(teamcity_service).to validate_presence_of(:teamcity_url) + expect(subject).not_to validate_presence_of(:password) + end + + it 'validates the presence of password if username is present' do + subject.username = 'john' + + expect(subject).to validate_presence_of(:password) + end end end - describe '#build_type' do - it 'does not validate the presence of build_type if service is not active' do - teamcity_service = service - teamcity_service.active = false + context 'when service is inactive' do + before { subject.active = false } - expect(teamcity_service).not_to validate_presence_of(:build_type) - end - - it 'validates the presence of build_type if service is active' do - teamcity_service = service - teamcity_service.active = true - - expect(teamcity_service).to validate_presence_of(:build_type) - end - end - - describe '#username' do - it 'does not validate the presence of username if service is not active' do - teamcity_service = service - teamcity_service.active = false - - expect(teamcity_service).not_to validate_presence_of(:username) - end - - it 'does not validate the presence of username if username is nil' do - teamcity_service = service - teamcity_service.active = true - teamcity_service.password = nil - - expect(teamcity_service).not_to validate_presence_of(:username) - end - - it 'validates the presence of username if service is active and username is present' do - teamcity_service = service - teamcity_service.active = true - teamcity_service.password = 'secret' - - expect(teamcity_service).to validate_presence_of(:username) - end - end - - describe '#password' do - it 'does not validate the presence of password if service is not active' do - teamcity_service = service - teamcity_service.active = false - - expect(teamcity_service).not_to validate_presence_of(:password) - end - - it 'does not validate the presence of password if username is nil' do - teamcity_service = service - teamcity_service.active = true - teamcity_service.username = nil - - expect(teamcity_service).not_to validate_presence_of(:password) - end - - it 'validates the presence of password if service is active and username is present' do - teamcity_service = service - teamcity_service.active = true - teamcity_service.username = 'john' - - expect(teamcity_service).to validate_presence_of(:password) - end + it { is_expected.not_to validate_presence_of(:build_type) } + it { is_expected.not_to validate_presence_of(:teamcity_url) } + it { is_expected.not_to validate_presence_of(:username) } + it { is_expected.not_to validate_presence_of(:password) } end end diff --git a/spec/support/issue_tracker_service_shared_example.rb b/spec/support/issue_tracker_service_shared_example.rb new file mode 100644 index 0000000000..b6d7436c36 --- /dev/null +++ b/spec/support/issue_tracker_service_shared_example.rb @@ -0,0 +1,7 @@ +RSpec.shared_examples 'issue tracker service URL attribute' do |url_attr| + it { is_expected.to allow_value('https://example.com').for(url_attr) } + + it { is_expected.not_to allow_value('example.com').for(url_attr) } + it { is_expected.not_to allow_value('ftp://example.com').for(url_attr) } + it { is_expected.not_to allow_value('herp-and-derp').for(url_attr) } +end From cd0750e0457f26f8165be301ad628e1830bd1e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 25 Apr 2016 15:46:15 +0200 Subject: [PATCH 010/138] Prevent private project name and namespace from leaking in the new MR view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #15591. Signed-off-by: Rémy Coutable --- app/services/merge_requests/build_service.rb | 3 +++ spec/features/merge_requests/create_new_mr_spec.rb | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index fa34753c4f..3544752d47 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -7,6 +7,9 @@ module MergeRequests merge_request.can_be_created = false merge_request.compare_commits = [] merge_request.source_project = project unless merge_request.source_project + + merge_request.target_project = nil unless can?(current_user, :read_project, merge_request.target_project) + merge_request.target_project ||= (project.forked_from_project || project) merge_request.target_branch ||= merge_request.target_project.default_branch diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb index 00b60bd0e7..e296078bad 100644 --- a/spec/features/merge_requests/create_new_mr_spec.rb +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -30,4 +30,14 @@ feature 'Create New Merge Request', feature: true, js: true do expect(page).to have_content 'git checkout -b orphaned-branch origin/orphaned-branch' end + + context 'when target project cannot be viewed by the current user' do + it 'does not leak the private project name & namespace' do + private_project = create(:project, :private) + + visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_project_id: private_project.id }) + + expect(page).not_to have_content private_project.to_reference + end + end end From 180c45b37ee16c24ddcb5c26a631dbca4ac76335 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 22 Apr 2016 13:25:32 +0300 Subject: [PATCH 011/138] AwardsHandler follows code style conventions --- app/assets/javascripts/awards_handler.coffee | 66 +++++++++++--------- app/assets/javascripts/notes.js.coffee | 8 +-- app/views/votes/_votes_block.html.haml | 18 +++--- 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index fcba981872..21bd19ef07 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -1,5 +1,5 @@ class @AwardsHandler - constructor: (@get_emojis_url, @post_emoji_url, @noteable_type, @noteable_id, @unicodes) -> + constructor: (@getEmojisUrl, @postEmojiUrl, @noteableType, @noteableId, @unicodes) -> $(".js-add-award").on "click", (event) => event.stopPropagation() event.preventDefault() @@ -23,13 +23,13 @@ class @AwardsHandler .find(".icon") .data "emoji" - if emoji is "thumbsup" and awards_handler.didUserClickEmoji $(this), "thumbsdown" - awards_handler.addAward "thumbsdown" + if emoji is "thumbsup" and awardsHandler.didUserClickEmoji $(this), "thumbsdown" + awardsHandler.addAward "thumbsdown" - else if emoji is "thumbsdown" and awards_handler.didUserClickEmoji $(this), "thumbsup" - awards_handler.addAward "thumbsup" + else if emoji is "thumbsdown" and awardsHandler.didUserClickEmoji $(this), "thumbsup" + awardsHandler.addAward "thumbsup" - awards_handler.addAward emoji + awardsHandler.addAward emoji $(this).trigger 'blur' @@ -47,7 +47,7 @@ class @AwardsHandler $("#emoji_search").focus() else $('.js-add-award').addClass "is-loading" - $.get @get_emojis_url, (response) => + $.get @getEmojisUrl, (response) => $('.js-add-award').removeClass "is-loading" $(".js-award-holder").append response setTimeout => @@ -99,25 +99,25 @@ class @AwardsHandler emojiIcon.remove() removeMeFromAuthorList: (emoji) -> - award_block = @findEmojiIcon(emoji).parent() - authors = award_block + awardBlock = @findEmojiIcon(emoji).parent() + authors = awardBlock .attr("data-original-title") .split(", ") authors.splice(authors.indexOf("me"),1) - award_block + awardBlock .closest(".js-emoji-btn") .attr("data-original-title", authors.join(", ")) - @resetTooltip(award_block) + @resetTooltip(awardBlock) addMeToAuthorList: (emoji) -> - award_block = @findEmojiIcon(emoji).parent() - origTitle = award_block.attr("data-original-title").trim() + awardBlock = @findEmojiIcon(emoji).parent() + origTitle = awardBlock.attr("data-original-title").trim() authors = [] if origTitle authors = origTitle.split(', ') authors.push("me") - award_block.attr("data-original-title", authors.join(", ")) - @resetTooltip(award_block) + awardBlock.attr("data-original-title", authors.join(", ")) + @resetTooltip(awardBlock) resetTooltip: (award) -> award.tooltip("destroy") @@ -139,20 +139,28 @@ class @AwardsHandler "" ) - emoji_node = $(nodes.join("\n")) + $(nodes.join("\n")) .insertBefore(".js-award-holder") .find(".emoji-icon") .data("emoji", emoji) $('.award-control').tooltip() resolveNameToCssClass: (emoji) -> - "emoji-#{@unicodes[emoji]}" + emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']") + + if emoji_icon.length > 0 + unicodeName = emoji_icon.data('unicode-name') + else + # Find by alias + unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data('unicode-name') + + "emoji-#{unicodeName}" postEmoji: (emoji, callback) -> - $.post @post_emoji_url, { note: { + $.post @postEmojiUrl, { note: { note: ":#{emoji}:" - noteable_type: @noteable_type - noteable_id: @noteable_id + noteable_type: @noteableType + noteable_id: @noteableId }},(data) -> if data.ok callback.call() @@ -166,21 +174,21 @@ class @AwardsHandler }, 200) addEmojiToFrequentlyUsedList: (emoji) -> - frequently_used_emojis = @getFrequentlyUsedEmojis() - frequently_used_emojis.push(emoji) - $.cookie('frequently_used_emojis', frequently_used_emojis.join(","), { expires: 365 }) + frequentlyUsedEmojis = @getFrequentlyUsedEmojis() + frequentlyUsedEmojis.push(emoji) + $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(","), { expires: 365 }) getFrequentlyUsedEmojis: -> - frequently_used_emojis = ($.cookie('frequently_used_emojis') || "").split(",") - _.compact(_.uniq(frequently_used_emojis)) + frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || "").split(",") + _.compact(_.uniq(frequentlyUsedEmojis)) renderFrequentlyUsedBlock: -> if $.cookie('frequently_used_emojis') - frequently_used_emojis = @getFrequentlyUsedEmojis() + frequentlyUsedEmojis = @getFrequentlyUsedEmojis() ul = $("
    ") - for emoji in frequently_used_emojis + for emoji in frequentlyUsedEmojis do (emoji) -> $(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul) @@ -196,8 +204,8 @@ class @AwardsHandler if term # Generate a search result block h5 = $("
    ").text("Search results").addClass("emoji-search") - found_emojis = @searchEmojis(term).show() - ul = $("
      ").addClass("emoji-menu-list emoji-menu-search").append(found_emojis) + foundEmojis = @searchEmojis(term).show() + ul = $("
        ").addClass("emoji-menu-list emoji-menu-search").append(foundEmojis) $(".emoji-menu-content ul, .emoji-menu-content h5").hide() $(".emoji-menu-content").append(h5).append(ul) else diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 82e210fed7..efb3e8e219 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -167,8 +167,8 @@ class @Notes return if note.award - awards_handler.addAwardToEmojiBar(note.note) - awards_handler.scrollToAwards() + awardsHandler.addAwardToEmojiBar(note.note) + awardsHandler.scrollToAwards() # render note if it not present in loaded list # or skip if rendered @@ -373,11 +373,11 @@ class @Notes new GLForm form if scrollTo? and myLastNote? - # scroll to the bottom + # scroll to the bottom # so the open of the last element doesn't make a jump $('html, body').scrollTop($(document).height()); $('html, body').animate({ - scrollTop: myLastNote.offset().top - 150 + scrollTop: myLastNote.offset().top - 150 }, 500, -> $noteText = form.find(".js-note-text") $noteText.focus() diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 59e1279869..4beb874644 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -15,16 +15,16 @@ - if current_user :javascript - var get_emojis_url = "#{emojis_path}"; - var post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}"; - var noteable_type = "#{votable.class.name.underscore}"; - var noteable_id = "#{votable.id}"; + var getEmojisUrl = "#{emojis_path}"; + var postEmojiUrl = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}"; + var noteableType = "#{votable.class.name.underscore}"; + var noteableId = "#{votable.id}"; var unicodes = #{AwardEmoji.unicode.to_json}; - window.awards_handler = new AwardsHandler( - get_emojis_url, - post_emoji_url, - noteable_type, - noteable_id, + window.awardsHandler = new AwardsHandler( + getEmojisUrl, + postEmojiUrl, + noteableType, + noteableId, unicodes ); From 311c859d11f2ca6bcfc0bba04e778535dc4e0f83 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 25 Apr 2016 22:37:25 +0300 Subject: [PATCH 012/138] awards_handler: single quotes --- app/assets/javascripts/awards_handler.coffee | 128 +++++++++---------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 21bd19ef07..bf95e06b4e 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -1,58 +1,58 @@ class @AwardsHandler constructor: (@getEmojisUrl, @postEmojiUrl, @noteableType, @noteableId, @unicodes) -> - $(".js-add-award").on "click", (event) => + $('.js-add-award').on 'click', (event) => event.stopPropagation() event.preventDefault() @showEmojiMenu() - $("html").on 'click', (event) -> - if !$(event.target).closest(".emoji-menu").length - if $(".emoji-menu").is(":visible") - $(".emoji-menu").removeClass "is-visible" + $('html').on 'click', (event) -> + if !$(event.target).closest('.emoji-menu').length + if $('.emoji-menu').is(':visible') + $('.emoji-menu').removeClass 'is-visible' - $(".awards") - .off "click" - .on "click", ".js-emoji-btn", @handleClick + $('.awards') + .off 'click' + .on 'click', '.js-emoji-btn', @handleClick @renderFrequentlyUsedBlock() handleClick: (e) -> e.preventDefault() emoji = $(this) - .find(".icon") - .data "emoji" + .find('.icon') + .data 'emoji' - if emoji is "thumbsup" and awardsHandler.didUserClickEmoji $(this), "thumbsdown" - awardsHandler.addAward "thumbsdown" + if emoji is 'thumbsup' and awardsHandler.didUserClickEmoji $(this), 'thumbsdown' + awardsHandler.addAward 'thumbsdown' - else if emoji is "thumbsdown" and awardsHandler.didUserClickEmoji $(this), "thumbsup" - awardsHandler.addAward "thumbsup" + else if emoji is 'thumbsdown' and awardsHandler.didUserClickEmoji $(this), 'thumbsup' + awardsHandler.addAward 'thumbsup' awardsHandler.addAward emoji $(this).trigger 'blur' didUserClickEmoji: (that, emoji) -> - if $(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title") - $(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title").indexOf('me') > -1 + if $(that).siblings("button:has([data-emoji=#{emoji}])").attr('data-original-title') + $(that).siblings("button:has([data-emoji=#{emoji}])").attr('data-original-title').indexOf('me') > -1 showEmojiMenu: -> - if $(".emoji-menu").length - if $(".emoji-menu").is ".is-visible" - $(".emoji-menu").removeClass "is-visible" - $("#emoji_search").blur() + if $('.emoji-menu').length + if $('.emoji-menu').is '.is-visible' + $('.emoji-menu').removeClass 'is-visible' + $('#emoji_search').blur() else - $(".emoji-menu").addClass "is-visible" - $("#emoji_search").focus() + $('.emoji-menu').addClass 'is-visible' + $('#emoji_search').focus() else - $('.js-add-award').addClass "is-loading" + $('.js-add-award').addClass 'is-loading' $.get @getEmojisUrl, (response) => - $('.js-add-award').removeClass "is-loading" - $(".js-award-holder").append response + $('.js-add-award').removeClass 'is-loading' + $('.js-award-holder').append response setTimeout => - $(".emoji-menu").addClass "is-visible" - $("#emoji_search").focus() + $('.emoji-menu').addClass 'is-visible' + $('#emoji_search').focus() @setupSearch() , 200 @@ -60,7 +60,7 @@ class @AwardsHandler @postEmoji emoji, => @addAwardToEmojiBar(emoji) - $(".emoji-menu").removeClass "is-visible" + $('.emoji-menu').removeClass 'is-visible' addAwardToEmojiBar: (emoji) -> @addEmojiToFrequentlyUsedList(emoji) @@ -69,9 +69,9 @@ class @AwardsHandler if @isActive(emoji) @decrementCounter(emoji) else - counter = @findEmojiIcon(emoji).siblings(".js-counter") + counter = @findEmojiIcon(emoji).siblings('.js-counter') counter.text(parseInt(counter.text()) + 1) - counter.parent().addClass("active") + counter.parent().addClass('active') @addMeToAuthorList(emoji) else @createEmoji(emoji) @@ -80,47 +80,47 @@ class @AwardsHandler @findEmojiIcon(emoji).length > 0 isActive: (emoji) -> - @findEmojiIcon(emoji).parent().hasClass("active") + @findEmojiIcon(emoji).parent().hasClass('active') decrementCounter: (emoji) -> - counter = @findEmojiIcon(emoji).siblings(".js-counter") + counter = @findEmojiIcon(emoji).siblings('.js-counter') emojiIcon = counter.parent() if parseInt(counter.text()) > 1 counter.text(parseInt(counter.text()) - 1) - emojiIcon.removeClass("active") + emojiIcon.removeClass('active') @removeMeFromAuthorList(emoji) - else if emoji == "thumbsup" || emoji == "thumbsdown" - emojiIcon.tooltip("destroy") + else if emoji == 'thumbsup' || emoji == 'thumbsdown' + emojiIcon.tooltip('destroy') counter.text(0) - emojiIcon.removeClass("active") + emojiIcon.removeClass('active') @removeMeFromAuthorList(emoji) else - emojiIcon.tooltip("destroy") + emojiIcon.tooltip('destroy') emojiIcon.remove() removeMeFromAuthorList: (emoji) -> awardBlock = @findEmojiIcon(emoji).parent() authors = awardBlock - .attr("data-original-title") - .split(", ") - authors.splice(authors.indexOf("me"),1) + .attr('data-original-title') + .split(', ') + authors.splice(authors.indexOf('me'),1) awardBlock - .closest(".js-emoji-btn") - .attr("data-original-title", authors.join(", ")) + .closest('.js-emoji-btn') + .attr('data-original-title', authors.join(', ')) @resetTooltip(awardBlock) addMeToAuthorList: (emoji) -> awardBlock = @findEmojiIcon(emoji).parent() - origTitle = awardBlock.attr("data-original-title").trim() + origTitle = awardBlock.attr('data-original-title').trim() authors = [] if origTitle authors = origTitle.split(', ') - authors.push("me") - awardBlock.attr("data-original-title", authors.join(", ")) + authors.push('me') + awardBlock.attr('data-original-title', authors.join(', ')) @resetTooltip(awardBlock) resetTooltip: (award) -> - award.tooltip("destroy") + award.tooltip('destroy') # "destroy" call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout. setTimeout (-> @@ -140,16 +140,16 @@ class @AwardsHandler ) $(nodes.join("\n")) - .insertBefore(".js-award-holder") - .find(".emoji-icon") - .data("emoji", emoji) + .insertBefore('.js-award-holder') + .find('.emoji-icon') + .data('emoji', emoji) $('.award-control').tooltip() resolveNameToCssClass: (emoji) -> - emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']") + emojiIcon = $(".emoji-menu-content [data-emoji='#{emoji}']") - if emoji_icon.length > 0 - unicodeName = emoji_icon.data('unicode-name') + if emojiIcon.length > 0 + unicodeName = emojiIcon.data('unicode-name') else # Find by alias unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data('unicode-name') @@ -176,40 +176,40 @@ class @AwardsHandler addEmojiToFrequentlyUsedList: (emoji) -> frequentlyUsedEmojis = @getFrequentlyUsedEmojis() frequentlyUsedEmojis.push(emoji) - $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(","), { expires: 365 }) + $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }) getFrequentlyUsedEmojis: -> - frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || "").split(",") + frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(',') _.compact(_.uniq(frequentlyUsedEmojis)) renderFrequentlyUsedBlock: -> if $.cookie('frequently_used_emojis') frequentlyUsedEmojis = @getFrequentlyUsedEmojis() - ul = $("
          ") + ul = $('
            ') for emoji in frequentlyUsedEmojis do (emoji) -> - $(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul) + $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul) - $("input.emoji-search").after(ul).after($("
            ").text("Frequently used")) + $('input.emoji-search').after(ul).after($('
            ').text('Frequently used')) setupSearch: -> - $("input.emoji-search").keyup (ev) => + $('input.emoji-search').keyup (ev) => term = $(ev.target).val() # Clean previous search results - $("ul.emoji-menu-search, h5.emoji-search").remove() + $('ul.emoji-menu-search, h5.emoji-search').remove() if term # Generate a search result block - h5 = $("
            ").text("Search results").addClass("emoji-search") + h5 = $('
            ').text('Search results').addClass('emoji-search') foundEmojis = @searchEmojis(term).show() - ul = $("
              ").addClass("emoji-menu-list emoji-menu-search").append(foundEmojis) - $(".emoji-menu-content ul, .emoji-menu-content h5").hide() - $(".emoji-menu-content").append(h5).append(ul) + ul = $('
                ').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis) + $('.emoji-menu-content ul, .emoji-menu-content h5').hide() + $('.emoji-menu-content').append(h5).append(ul) else - $(".emoji-menu-content").children().show() + $('.emoji-menu-content').children().show() searchEmojis: (term)-> $(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone() From 9f85b7bc58485831a61da0f9acc530511ea7474a Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 26 Apr 2016 16:56:14 +0200 Subject: [PATCH 013/138] Initialize wikis on legacy projects during check --- .../single_repository_worker.rb | 34 +++++++++++++------ .../single_repository_worker_spec.rb | 33 +++++++++++++++--- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb index a76729e3c7..f2d12ba5a7 100644 --- a/app/workers/repository_check/single_repository_worker.rb +++ b/app/workers/repository_check/single_repository_worker.rb @@ -1,9 +1,9 @@ module RepositoryCheck class SingleRepositoryWorker include Sidekiq::Worker - + sidekiq_options retry: false - + def perform(project_id) project = Project.find(project_id) project.update_columns( @@ -11,20 +11,32 @@ module RepositoryCheck last_repository_check_at: Time.now, ) end - + private - + def check(project) - repositories = [project.repository] - repositories << project.wiki.repository if project.wiki_enabled? - # Use 'map do', not 'all? do', to prevent short-circuiting - repositories.map { |repository| git_fsck(repository.path_to_repo) }.all? + if !git_fsck(project.repository) + false + elsif project.wiki_enabled? + # Historically some projects never had their wiki repos initialized; + # this happens on project creation now. Let's initialize an empty repo + # if it is not already there. + begin + project.create_wiki + rescue Rugged::RepositoryError + end + + git_fsck(project.wiki.repository) + else + true + end end - - def git_fsck(path) + + def git_fsck(repository) + path = repository.path_to_repo cmd = %W(nice git --git-dir=#{path} fsck) output, status = Gitlab::Popen.popen(cmd) - + if status.zero? true else diff --git a/spec/workers/repository_check/single_repository_worker_spec.rb b/spec/workers/repository_check/single_repository_worker_spec.rb index 087e4c667d..5a03bb77eb 100644 --- a/spec/workers/repository_check/single_repository_worker_spec.rb +++ b/spec/workers/repository_check/single_repository_worker_spec.rb @@ -12,7 +12,7 @@ describe RepositoryCheck::SingleRepositoryWorker do subject.perform(project.id) expect(project.reload.last_repository_check_failed).to eq(false) - destroy_wiki(project) + break_wiki(project) subject.perform(project.id) expect(project.reload.last_repository_check_failed).to eq(true) @@ -20,15 +20,38 @@ describe RepositoryCheck::SingleRepositoryWorker do it 'skips wikis when disabled' do project = create(:project_empty_repo, wiki_enabled: false) - # Make sure the test would fail if it checked the wiki repo - destroy_wiki(project) + # Make sure the test would fail if the wiki repo was checked + break_wiki(project) subject.perform(project.id) expect(project.reload.last_repository_check_failed).to eq(false) end - def destroy_wiki(project) - FileUtils.rm_rf(project.wiki.repository.path_to_repo) + it 'creates missing wikis' do + project = create(:project_empty_repo, wiki_enabled: true) + FileUtils.rm_rf(wiki_path(project)) + + subject.perform(project.id) + + expect(project.reload.last_repository_check_failed).to eq(false) + end + + it 'does not create a wiki if the main repo does not exist at all' do + project = create(:project_empty_repo) + FileUtils.rm_rf(project.repository.path_to_repo) + FileUtils.rm_rf(wiki_path(project)) + + subject.perform(project.id) + + expect(File.exist?(wiki_path(project))).to eq(false) + end + + def break_wiki(project) + FileUtils.rm_rf(wiki_path(project) + '/objects') + end + + def wiki_path(project) + project.wiki.repository.path_to_repo end end From 247ae960552acc8cd3be299dbb10ed61d8dafe75 Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Tue, 26 Apr 2016 23:20:19 +0700 Subject: [PATCH 014/138] Allow to assign labels and milestone to target project when moving issue --- app/services/issues/move_service.rb | 11 ++++++++++- spec/services/issues/move_service_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index 82e7090f1e..8d41ea5df5 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -41,7 +41,8 @@ module Issues private def create_new_issue - new_params = { id: nil, iid: nil, label_ids: [], milestone: nil, + new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids, + milestone: cloneable_milestone_id, project: @new_project, author: @old_issue.author, description: rewrite_content(@old_issue.description) } @@ -49,6 +50,14 @@ module Issues CreateService.new(@new_project, @current_user, new_params).execute end + def cloneable_label_ids + @new_project.labels.where(title: @old_issue.labels.pluck(:title)).pluck(:id) + end + + def cloneable_milestone_id + @new_project.milestones.find_by(title: @old_issue.milestone.try(:title)) + end + def rewrite_notes @old_issue.notes.find_each do |note| new_note = note.dup diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 2a5e4ac3ec..c98e60d76b 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -21,6 +21,15 @@ describe Issues::MoveService, services: true do before do old_project.team << [user, :reporter] new_project.team << [user, :reporter] + + create(:milestone, project_id: old_project.id, title: 'v1.0') + create(:milestone, project_id: new_project.id, title: 'v1.0') + + old_issue.labels << create(:label, project_id: old_project.id, title: 'label1') + old_issue.labels << create(:label, project_id: old_project.id, title: 'label2') + + new_project.labels << create(:label, title: 'label1') + new_project.labels << create(:label, title: 'label2') end end @@ -39,6 +48,17 @@ describe Issues::MoveService, services: true do expect(new_issue.project).to eq new_project end + it 'assigns milestone to new issue' do + expect(new_issue.reload.milestone.title).to eq 'v1.0' + end + + it 'assign labels to new issue' do + expected_label_titles = new_issue.reload.labels.map(&:title) + expect(expected_label_titles).to include 'label1' + expect(expected_label_titles).to include 'label2' + expect(expected_label_titles.size).to eq 2 + end + it 'rewrites issue title' do expect(new_issue.title).to eq title end From d6d1daf430bbadef953b40975a503e0bb64044fc Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Wed, 27 Apr 2016 00:03:09 +0700 Subject: [PATCH 015/138] Update specs --- spec/services/issues/move_service_spec.rb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index c98e60d76b..7654b25569 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -7,10 +7,11 @@ describe Issues::MoveService, services: true do let(:description) { 'Some issue description' } let(:old_project) { create(:project) } let(:new_project) { create(:project) } + let!(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') } let(:old_issue) do create(:issue, title: title, description: description, - project: old_project, author: author) + project: old_project, author: author, milestone: milestone1) end let(:move_service) do @@ -22,8 +23,7 @@ describe Issues::MoveService, services: true do old_project.team << [user, :reporter] new_project.team << [user, :reporter] - create(:milestone, project_id: old_project.id, title: 'v1.0') - create(:milestone, project_id: new_project.id, title: 'v1.0') + create(:milestone, project_id: new_project.id, title: 'v9.0') old_issue.labels << create(:label, project_id: old_project.id, title: 'label1') old_issue.labels << create(:label, project_id: old_project.id, title: 'label2') @@ -49,7 +49,7 @@ describe Issues::MoveService, services: true do end it 'assigns milestone to new issue' do - expect(new_issue.reload.milestone.title).to eq 'v1.0' + expect(new_issue.reload.milestone.title).to eq 'v9.0' end it 'assign labels to new issue' do @@ -92,11 +92,6 @@ describe Issues::MoveService, services: true do expect(new_issue.author).to eq author end - it 'removes data that is invalid in new context' do - expect(new_issue.milestone).to be_nil - expect(new_issue.labels).to be_empty - end - it 'creates a new internal id for issue' do expect(new_issue.iid).to be 1 end From be67a4843cc37790402404650cb96a6f02552b54 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 26 Apr 2016 12:55:38 -0400 Subject: [PATCH 016/138] Prevent privilege escalation via notes API Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/15577 --- app/services/notes/create_service.rb | 11 ++++++++ spec/requests/api/notes_spec.rb | 41 +++++++++++++++++++++------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 2bb312bb25..0158699481 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -5,6 +5,8 @@ module Notes note.author = current_user note.system = false + return unless valid_project?(note) + if note.save # Finish the harder work in the background NewNoteWorker.perform_in(2.seconds, note.id, params) @@ -13,5 +15,14 @@ module Notes note end + + private + + def valid_project?(note) + return false unless project + return true if note.for_commit? + + note.noteable.try(:project) == project + end end end diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index ec9eda0a2e..49091fc0f4 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe API::API, api: true do include ApiHelpers let(:user) { create(:user) } - let!(:project) { create(:project, namespace: user.namespace ) } + let!(:project) { create(:project, namespace: user.namespace) } let!(:issue) { create(:issue, project: project, author: user) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) } let!(:snippet) { create(:project_snippet, project: project, author: user) } @@ -45,7 +45,7 @@ describe API::API, api: true do end it "should return a 404 error when issue id not found" do - get api("/projects/#{project.id}/issues/123/notes", user) + get api("/projects/#{project.id}/issues/12345/notes", user) expect(response.status).to eq(404) end @@ -106,7 +106,7 @@ describe API::API, api: true do end it "should return a 404 error if issue note not found" do - get api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user) + get api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) expect(response.status).to eq(404) end @@ -134,7 +134,7 @@ describe API::API, api: true do end it "should return a 404 error if snippet note not found" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/123", user) + get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user) expect(response.status).to eq(404) end end @@ -191,6 +191,27 @@ describe API::API, api: true do expect(response.status).to eq(401) end end + + context 'when user does not have access to create noteable' do + let(:private_issue) { create(:issue, project: create(:project, :private)) } + + ## + # We are posting to project user has access to, but we use issue id + # from a different project, see #15577 + # + before do + post api("/projects/#{project.id}/issues/#{private_issue.id}/notes", user), + body: 'Hi!' + end + + it 'responds with 500' do + expect(response.status).to eq 500 + end + + it 'does not create new note' do + expect(private_issue.notes.reload).to be_empty + end + end end describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do @@ -211,7 +232,7 @@ describe API::API, api: true do end it 'should return a 404 error when note id not found' do - put api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user), + put api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user), body: 'Hello!' expect(response.status).to eq(404) end @@ -233,7 +254,7 @@ describe API::API, api: true do it 'should return a 404 error when note id not found' do put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ - "notes/123", user), body: "Hello!" + "notes/12345", user), body: "Hello!" expect(response.status).to eq(404) end end @@ -248,7 +269,7 @@ describe API::API, api: true do it 'should return a 404 error when note id not found' do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ - "notes/123", user), body: "Hello!" + "notes/12345", user), body: "Hello!" expect(response.status).to eq(404) end end @@ -268,7 +289,7 @@ describe API::API, api: true do end it 'returns a 404 error when note id not found' do - delete api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user) + delete api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) expect(response.status).to eq(404) end @@ -288,7 +309,7 @@ describe API::API, api: true do it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ - "notes/123", user) + "notes/12345", user) expect(response.status).to eq(404) end @@ -308,7 +329,7 @@ describe API::API, api: true do it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/merge_requests/"\ - "#{merge_request.id}/notes/123", user) + "#{merge_request.id}/notes/12345", user) expect(response.status).to eq(404) end From 0ace42cdb90161ee7daf69b7a6a7af6ce4195208 Mon Sep 17 00:00:00 2001 From: Jason Roehm Date: Tue, 26 Apr 2016 21:41:52 -0400 Subject: [PATCH 017/138] fix: in recent versions of Docker, the /.dockerinit file doesn't exist; use /.dockerenv instead [ci skip] Signed-off-by: Jason Roehm --- doc/ci/ssh_keys/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md index 7f825e6a06..7c0fb225da 100644 --- a/doc/ci/ssh_keys/README.md +++ b/doc/ci/ssh_keys/README.md @@ -57,7 +57,7 @@ before_script: # WARNING: Use this only with the Docker executor, if you use it with shell # you will overwrite your user's SSH config. - mkdir -p ~/.ssh - - '[[ -f /.dockerinit ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' + - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' ``` As a final step, add the _public_ key from the one you created earlier to the From ce9310d63f6ddf55187d16b882d59f26f7b384a9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 27 Apr 2016 09:28:08 +0200 Subject: [PATCH 018/138] fix var error --- lib/gitlab/bitbucket_import/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/bitbucket_import/client.rb b/lib/gitlab/bitbucket_import/client.rb index 9bb507b5ed..9b83292ef3 100644 --- a/lib/gitlab/bitbucket_import/client.rb +++ b/lib/gitlab/bitbucket_import/client.rb @@ -12,7 +12,7 @@ module Gitlab token_secret = import_data_credentials[:bb_session][:bitbucket_access_token_secret] new(token, token_secret) else - raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{project.id}" end end From 6b2489f2007e3ffe890f707d824a915f97ed7e61 Mon Sep 17 00:00:00 2001 From: Karlo Soriano Date: Wed, 27 Apr 2016 17:45:50 +0800 Subject: [PATCH 019/138] Fix typo in user steps feature [ci skip] --- features/steps/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/user.rb b/features/steps/user.rb index 3230234cb6..39bbb59343 100644 --- a/features/steps/user.rb +++ b/features/steps/user.rb @@ -12,7 +12,7 @@ class Spinach::Features::User < Spinach::FeatureSteps user = User.find_by(name: 'John Doe') project = contributed_project - # Issue controbution + # Issue contribution issue_params = { title: 'Bug in old browser' } Issues::CreateService.new(project, user, issue_params).execute From c8187738a217166cbf03c26adc4a09fcf43a3b94 Mon Sep 17 00:00:00 2001 From: Karlo Soriano Date: Wed, 27 Apr 2016 19:36:04 +0800 Subject: [PATCH 020/138] Remove unused .contributed-projects class While working on #13401 and trying to add a new tab to the user profile page, I came across this. I noticed that the `contributed-projects` class was only being used in order to select the div in the tests. For consistency with the other tabs, I decided to remove this class and use the div's id for the selector. --- app/views/users/show.html.haml | 2 +- features/steps/user.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 3028491e5b..5554f736e9 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -98,7 +98,7 @@ #groups.tab-pane - # This tab is always loaded via AJAX - #contributed.contributed-projects.tab-pane + #contributed.tab-pane - # This tab is always loaded via AJAX #projects.tab-pane diff --git a/features/steps/user.rb b/features/steps/user.rb index 3230234cb6..18fd8e5c61 100644 --- a/features/steps/user.rb +++ b/features/steps/user.rb @@ -28,7 +28,7 @@ class Spinach::Features::User < Spinach::FeatureSteps end step 'I should see contributed projects' do - page.within '.contributed-projects' do + page.within '#contributed' do expect(page).to have_content(@contributed_project.name) end end From b677b0e33a04ead7edaad5ee528c915980b1c283 Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 27 Apr 2016 16:03:20 -0600 Subject: [PATCH 021/138] Add explicit --with statement for postgres and mysql gem groups as necessary. When I followed these instructions, the first command did not install the postgres group and it had to be made explicit. --- doc/update/patch_versions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index f446ed0a35..bca570b28b 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -57,10 +57,10 @@ sudo -u git -H make cd /home/git/gitlab # PostgreSQL -sudo -u git -H bundle install --without development test mysql --deployment +sudo -u git -H bundle install --without development test mysql --with postgres --deployment # MySQL -sudo -u git -H bundle install --without development test postgres --deployment +sudo -u git -H bundle install --without development test postgres --with mysql --deployment # Optional: clean up old gems sudo -u git -H bundle clean From 267dd23311fe6ad6e9c9af15d683dc1a1bf608fe Mon Sep 17 00:00:00 2001 From: Connor Shea Date: Wed, 27 Apr 2016 18:05:57 -0600 Subject: [PATCH 022/138] Enable LstripRstrip cop This requires no code changes since it doesn't actually change anything in the codebase, just preventative. --- .rubocop.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 83ed6c3867..9f179efa3c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -953,10 +953,9 @@ Performance/DoubleStartEndWith: Performance/EndWith: Enabled: false -# TODO: Enable LstripRstrip Cop. # Use `strip` instead of `lstrip.rstrip`. Performance/LstripRstrip: - Enabled: false + Enabled: true # TODO: Enable RangeInclude Cop. # Use `Range#cover?` instead of `Range#include?`. From 8793025dd04b7a6c8dd6dd04a52eabbfc612745d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 28 Apr 2016 10:26:44 +0200 Subject: [PATCH 023/138] added spec testing exception raised --- .../gitlab/bitbucket_import/client_spec.rb | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb index aa0699f2eb..cd31894eda 100644 --- a/spec/lib/gitlab/bitbucket_import/client_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb @@ -34,18 +34,33 @@ describe Gitlab::BitbucketImport::Client, lib: true do it 'retrieves issues over a number of pages' do stub_request(:get, "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=0"). - to_return(status: 200, - body: first_sample_data.to_json, - headers: {}) + to_return(status: 200, + body: first_sample_data.to_json, + headers: {}) stub_request(:get, "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=50"). - to_return(status: 200, - body: second_sample_data.to_json, - headers: {}) + to_return(status: 200, + body: second_sample_data.to_json, + headers: {}) issues = client.issues(project_id) expect(issues.count).to eq(95) end end + + context 'project import' do + it 'calls .from_project with no errors' do + project = create(:empty_project) + project.create_or_update_import_data(credentials: + { :user => "git", + :password => nil, + :bb_session => + { :bitbucket_access_token => "test", + :bitbucket_access_token_secret => "test" } }) + project.import_url = "ssh://git@bitbucket.org/test/test.git" + + expect { described_class.from_project(project) }.to_not raise_error + end + end end From f7844a11be6a5f6aa7011bd96f59bf218c4788ea Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Thu, 28 Apr 2016 15:52:23 +0700 Subject: [PATCH 024/138] Code refactor and fix broken spec --- app/services/issues/move_service.rb | 8 +++++--- spec/services/issues/move_service_spec.rb | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index 8d41ea5df5..fe5df8f18c 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -42,7 +42,7 @@ module Issues def create_new_issue new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids, - milestone: cloneable_milestone_id, + milestone_id: cloneable_milestone_id, project: @new_project, author: @old_issue.author, description: rewrite_content(@old_issue.description) } @@ -51,11 +51,13 @@ module Issues end def cloneable_label_ids - @new_project.labels.where(title: @old_issue.labels.pluck(:title)).pluck(:id) + @new_project.labels + .where(title: @old_issue.labels.pluck(:title)).pluck(:id) end def cloneable_milestone_id - @new_project.milestones.find_by(title: @old_issue.milestone.try(:title)) + @new_project.milestones + .find_by(title: @old_issue.milestone.try(:title)).try(:id) end def rewrite_notes diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 7654b25569..62a5e93037 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -7,7 +7,9 @@ describe Issues::MoveService, services: true do let(:description) { 'Some issue description' } let(:old_project) { create(:project) } let(:new_project) { create(:project) } - let!(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') } + let!(:milestone1) do + create(:milestone, project_id: old_project.id, title: 'v9.0') + end let(:old_issue) do create(:issue, title: title, description: description, @@ -23,10 +25,12 @@ describe Issues::MoveService, services: true do old_project.team << [user, :reporter] new_project.team << [user, :reporter] - create(:milestone, project_id: new_project.id, title: 'v9.0') - - old_issue.labels << create(:label, project_id: old_project.id, title: 'label1') - old_issue.labels << create(:label, project_id: old_project.id, title: 'label2') + ['label1', 'label2'].each do |label| + old_issue.labels << create(:label, + project_id: old_project.id, + title: label + ) + end new_project.labels << create(:label, title: 'label1') new_project.labels << create(:label, title: 'label2') @@ -35,6 +39,10 @@ describe Issues::MoveService, services: true do describe '#execute' do shared_context 'issue move executed' do + let!(:milestone2) do + create(:milestone, project_id: new_project.id, title: 'v9.0') + end + let!(:new_issue) { move_service.execute(old_issue, new_project) } end From 81467d2e04562d63249063113bdd61d4ad16da7d Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Thu, 28 Apr 2016 16:21:41 +0700 Subject: [PATCH 025/138] Fix rubocop --- spec/services/issues/move_service_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 62a5e93037..7399003bd7 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -28,8 +28,7 @@ describe Issues::MoveService, services: true do ['label1', 'label2'].each do |label| old_issue.labels << create(:label, project_id: old_project.id, - title: label - ) + title: label) end new_project.labels << create(:label, title: 'label1') From 9a9681772c3c8a6523bb94e758655e5c555cea91 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 28 Apr 2016 11:40:23 +0200 Subject: [PATCH 026/138] fix failing spec --- spec/lib/gitlab/bitbucket_import/client_spec.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb index cd31894eda..af839f4242 100644 --- a/spec/lib/gitlab/bitbucket_import/client_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb @@ -53,11 +53,10 @@ describe Gitlab::BitbucketImport::Client, lib: true do it 'calls .from_project with no errors' do project = create(:empty_project) project.create_or_update_import_data(credentials: - { :user => "git", - :password => nil, - :bb_session => - { :bitbucket_access_token => "test", - :bitbucket_access_token_secret => "test" } }) + { user: "git", + password: nil, + bb_session: { bitbucket_access_token: "test", + bitbucket_access_token_secret: "test" } }) project.import_url = "ssh://git@bitbucket.org/test/test.git" expect { described_class.from_project(project) }.to_not raise_error From ced6563121851d95cd156ee486476469acdd6469 Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Thu, 28 Apr 2016 16:41:35 +0700 Subject: [PATCH 027/138] Add changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 558897ad89..c6c96c096c 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.8.0 (unreleased) + - Assign labels and milestone to target project when moving issue - Remove future dates from contribution calendar graph. - Fix error when visiting commit builds page before build was updated - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project From f43044cc8abb23f391f635e145b52cf5a54a0058 Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Thu, 28 Apr 2016 16:43:21 +0700 Subject: [PATCH 028/138] Update changelog --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index c6c96c096c..2d3a9e051e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.8.0 (unreleased) - - Assign labels and milestone to target project when moving issue + - Assign labels and milestone to target project when moving issue. !3934 - Remove future dates from contribution calendar graph. - Fix error when visiting commit builds page before build was updated - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project From 8a6776caa9f45c4c16347ad662bd83519032687d Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 28 Apr 2016 12:11:08 +0200 Subject: [PATCH 029/138] Use 'exec' in Unicorn and Sidekiq launch scripts When running Unicorn or Sidekiq in the foreground this change removes an intermediate /bin/sh process. This makes process supervision in the GitLab Development Kit more reliable. This change does not affect Omnibus-GitLab (because there we do not use these launch scripts). Installations from source do use the launch scripts but for the standard GitLab init script this change will not make a difference. Custom installations using Upstart or Systemd may be affected however, because under certain configurations these systems count exactly how many forks happen during process startup, and we are reducing that number by one here. --- bin/background_jobs | 2 +- bin/web | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/background_jobs b/bin/background_jobs index 1f67d73294..25a578a1c4 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 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 "$@" + exec 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 "$@" } load_ok() diff --git a/bin/web b/bin/web index 03fe7a6354..ecd0bbd10b 100755 --- a/bin/web +++ b/bin/web @@ -19,12 +19,12 @@ get_unicorn_pid() start() { - $unicorn_cmd -D + exec $unicorn_cmd -D } start_foreground() { - $unicorn_cmd + exec $unicorn_cmd } stop() From 4fcf2fb9e779cbc8ce872a15c5c7657471d1ddba Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 28 Apr 2016 11:35:46 +0100 Subject: [PATCH 030/138] Updated spacing between notification label and button Closes #16552 --- app/views/profiles/emails/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml index 57527361eb..6f7fefdb46 100644 --- a/app/views/profiles/emails/index.html.haml +++ b/app/views/profiles/emails/index.html.haml @@ -45,4 +45,4 @@ %span.label.label-info Public Email - if email.email === current_user.notification_email %span.label.label-info Notification Email - = link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove pull-right' + = link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-warning prepend-left-10' From 52a6b976a77d9ede9f1b50d55e33824ed05acb5f Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Thu, 28 Apr 2016 14:13:39 +0200 Subject: [PATCH 031/138] feature proposal issue template in contributing guide --- CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24cd586453..a1ded9b984 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -141,6 +141,16 @@ members to add the label `feature proposal` to the issue. Please keep feature proposals as small and simple as possible, complex ones might be edited to make them small and simple. +You are encouraged to use the template below for feature proposals. + +``` +## Description including problem, use cases, benefits, and/or goals + +## Proposal + +## Links / references +``` + For changes in the interface, it can be helpful to create a mockup first. If you want to create something yourself, consider opening an issue first to discuss whether it is interesting to include this in GitLab. From e9eeeaa0d0c8820f66a9581d8b4d03f02ac705d0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 28 Apr 2016 15:35:40 +0200 Subject: [PATCH 032/138] Redesign navigation for group pages Signed-off-by: Dmitriy Zaporozhets --- app/views/groups/activity.html.haml | 1 - app/views/groups/edit.html.haml | 2 - .../groups/group_members/index.html.haml | 1 - app/views/groups/issues.html.haml | 1 - app/views/groups/merge_requests.html.haml | 1 - app/views/groups/milestones/index.html.haml | 1 - app/views/groups/projects.html.haml | 1 - app/views/groups/show.html.haml | 6 --- app/views/layouts/group.html.haml | 3 +- app/views/layouts/group_settings.html.haml | 4 +- app/views/layouts/nav/_dashboard.html.haml | 4 +- app/views/layouts/nav/_group.html.haml | 40 ++++++++++++------- 12 files changed, 31 insertions(+), 34 deletions(-) diff --git a/app/views/groups/activity.html.haml b/app/views/groups/activity.html.haml index f73e1d9e86..aaad265b3e 100644 --- a/app/views/groups/activity.html.haml +++ b/app/views/groups/activity.html.haml @@ -3,7 +3,6 @@ = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") - page_title "Activity" -- header_title group_title(@group, "Activity", activity_group_path(@group)) %section.activities = render 'activities' diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index a698cbbe9d..92cd4c553d 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,5 +1,3 @@ -- header_title group_title(@group, "Settings", edit_group_path(@group)) - .panel.panel-default.prepend-top-default .panel-heading Group settings diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index 6b7fd5746d..0eb6bbd442 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,5 +1,4 @@ - page_title "Members" -- header_title group_title(@group, "Members", group_group_members_path(@group)) .group-members-page.prepend-top-default - if current_user && current_user.can?(:admin_group_member, @group) diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index aea35c5086..76160232e0 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,5 +1,4 @@ - page_title "Issues" -- header_title group_title(@group, "Issues", issues_group_path(@group)) = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues") diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index e1c9dd931e..87090352e2 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,5 +1,4 @@ - page_title "Merge Requests" -- header_title group_title(@group, "Merge Requests", merge_requests_group_path(@group)) .top-area = render 'shared/issuable/nav', type: :merge_requests diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index ab307708b7..8bca00eb71 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -1,5 +1,4 @@ - page_title "Milestones" -- header_title group_title(@group, "Milestones", group_milestones_path(@group)) .top-area = render 'shared/milestones_filter' diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index dd75766121..c2f2d9912f 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -1,5 +1,4 @@ - page_title "Projects" -- header_title group_title(@group, "Projects", projects_group_path(@group)) .panel.panel-default.prepend-top-default .panel-heading diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 3d16ecb097..c71070e6c9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -5,12 +5,6 @@ = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") .cover-block - .cover-controls - - if @group && can?(current_user, :admin_group, @group) - = link_to icon('pencil'), edit_group_path(@group), class: 'btn' - - if current_user - = link_to icon('rss'), group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' - .avatar-holder = link_to group_icon(@group), target: '_blank' do = image_tag group_icon(@group), class: "avatar group-avatar s90" diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index 2e483b7148..7bee7e172e 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -1,6 +1,7 @@ - page_title @group.name - page_description @group.description unless page_description - header_title group_title(@group) unless header_title -- sidebar "group" unless sidebar +- sidebar "dashboard" unless sidebar +- nav "group" = render template: "layouts/application" diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml index a1a1fc2f85..1ff53ce2a9 100644 --- a/app/views/layouts/group_settings.html.haml +++ b/app/views/layouts/group_settings.html.haml @@ -1,5 +1,5 @@ - page_title "Settings" -- header_title group_title(@group, "Settings", edit_group_path(@group)) -- sidebar "group_settings" +- sidebar "dashboard" unless sidebar +- nav "group" = render template: "layouts/group" diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index ca49c313ff..fad4224e94 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -15,12 +15,12 @@ = icon('dashboard fw') %span Activity - = nav_link(controller: :groups) do + = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do = link_to dashboard_groups_path, title: 'Groups' do = icon('group fw') %span Groups - = nav_link(controller: :milestones) do + = nav_link(controller: 'dashboard/milestones') do = link_to dashboard_milestones_path, title: 'Milestones' do = icon('clock-o fw') %span diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 55940741dc..bc5ce7052e 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,12 +1,28 @@ -%ul.nav.nav-sidebar - = nav_link do - = link_to root_path, title: 'Go to dashboard', class: 'back-link' do - = icon('caret-square-o-left fw') - %span - Go to dashboard +- if current_user + .controls + - if current_path?('groups#show') + = link_to icon('rss'), group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn btn-gray rss-btn' - %li.separate-item + %span.dropdown.group-settings-dropdown + %a.dropdown-new.btn.btn-gray#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} + = icon('cog') + = icon('angle-down') + %ul.dropdown-menu.dropdown-menu-align-right + = nav_link(path: 'groups#projects') do + = link_to projects_group_path(@group), title: 'Projects' do + Projects + %li.divider + - if @group && can?(current_user, :admin_group, @group) + %li + = link_to edit_group_path(@group) do + Edit Group + - if access = @group.users.find(current_user) + %li + = link_to leave_group_group_members_path(@group), + data: { confirm: leave_group_message(@group.name) }, method: :delete, title: 'Leave group' do + Leave Group +%ul.nav-links = nav_link(path: 'groups#show', html_options: {class: 'home'}) do = link_to group_path(@group), title: 'Home' do = icon('group fw') @@ -28,22 +44,16 @@ %span Issues - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.count= number_with_delimiter(issues.count) + %span.badge.count= number_with_delimiter(issues.count) = nav_link(path: 'groups#merge_requests') do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do = icon('tasks fw') %span Merge Requests - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.count= number_with_delimiter(merge_requests.count) + %span.badge.count= number_with_delimiter(merge_requests.count) = nav_link(controller: [:group_members]) 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' do - = icon ('cogs fw') - %span - Settings From a94b71c301c8fa0741ba7b4958c30d04f7f10341 Mon Sep 17 00:00:00 2001 From: connorshea Date: Sun, 10 Apr 2016 14:47:01 -0600 Subject: [PATCH 033/138] Upgrade Doorkeeper from 2.2.2 to 3.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I’d rather upgrade one major version at a time, so Doorkeeper needs to be upgraded to 3.x before it can be upgraded to 4.x (which includes Rails 5 support). Changelog: https://github.com/doorkeeper-gem/doorkeeper/blob/master/NEWS.md#310 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 7882e467f8..25c13fda57 100644 --- a/Gemfile +++ b/Gemfile @@ -19,7 +19,7 @@ gem "pg", '~> 0.18.2', group: :postgres # Authentication libraries gem 'devise', '~> 3.5.4' -gem 'doorkeeper', '~> 2.2.0' +gem 'doorkeeper', '~> 3.1' gem 'omniauth', '~> 1.3.1' gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-azure-oauth2', '~> 0.0.6' diff --git a/Gemfile.lock b/Gemfile.lock index 91d89b4875..9c97426cb5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -173,7 +173,7 @@ GEM diff-lcs (1.2.5) diffy (3.0.7) docile (1.1.5) - doorkeeper (2.2.2) + doorkeeper (3.1.0) railties (>= 3.2) dropzonejs-rails (0.7.2) rails (> 3.1) @@ -922,7 +922,7 @@ DEPENDENCIES devise (~> 3.5.4) devise-two-factor (~> 2.0.0) diffy (~> 3.0.3) - doorkeeper (~> 2.2.0) + doorkeeper (~> 3.1) dropzonejs-rails (~> 0.7.1) email_reply_parser (~> 0.5.8) email_spec (~> 1.6.0) From 519a791ef9a90541a1bb3825790bd46fd57756f9 Mon Sep 17 00:00:00 2001 From: Connor Shea Date: Thu, 28 Apr 2016 15:08:23 -0600 Subject: [PATCH 034/138] Prevent Rails filtered parameters from leaking to Sentry. As described in their Docs: https://docs.getsentry.com/on-premise/clients/ruby/integrations/rails/ --- config/initializers/sentry.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index e87899b2d5..74fef7cadf 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -15,6 +15,9 @@ if Rails.env.production? Raven.configure do |config| config.dsn = current_application_settings.sentry_dsn config.release = Gitlab::REVISION + + # Sanitize fields based on those sanitized from Rails. + config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s) end end end From c50ce3ba2905ab9f3431467c0118d3cf16fbfea2 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Thu, 28 Apr 2016 21:13:21 -0700 Subject: [PATCH 035/138] Fix the GitHub Omniauth instructions --- doc/integration/github.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/integration/github.md b/doc/integration/github.md index e1f9242fd0..e7497e475c 100644 --- a/doc/integration/github.md +++ b/doc/integration/github.md @@ -9,7 +9,9 @@ GitHub will generate an application ID and secret key for you to use. 1. Navigate to your individual user settings or an organization's settings, depending on how you want the application registered. It does not matter if the application is registered as an individual or an organization - that is entirely up to you. -1. Select "Applications" in the left menu. +1. Select "OAuth applications" in the left menu. + +1. If you already have applications listed, switch to the "Developer applications" tab. 1. Select "Register new application". From c4b9bd041321df25764ad1de90f89b1f0dda9f33 Mon Sep 17 00:00:00 2001 From: Paco Guzman Date: Fri, 22 Apr 2016 14:07:25 +0200 Subject: [PATCH 036/138] API support for the 'since' and 'until' operators on commit requests - Parameter validation as ISO8601 format --- CHANGELOG | 1 + Gemfile.lock | 2 +- .../projects/commits_controller.rb | 2 +- app/controllers/projects/graphs_controller.rb | 4 +-- app/models/repository.rb | 6 ++-- doc/api/commits.md | 2 ++ lib/api/commits.rb | 8 ++++- lib/api/helpers.rb | 16 +++++++++ lib/gitlab/push_data_builder.rb | 2 +- spec/models/repository_spec.rb | 2 +- spec/requests/api/commits_spec.rb | 35 +++++++++++++++++++ 11 files changed, 71 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e52e52691c..c921595437 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,7 @@ v 8.8.0 (unreleased) - Replace Devise Async with Devise ActiveJob integration. !3902 (Connor Shea) - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea) - Added button to toggle whitespaces changes on diff view + - API support for the 'since' and 'until' operators on commit requests (Paco Guzman) v 8.7.1 (unreleased) - Throttle the update of `project.last_activity_at` to 1 minute. !3848 diff --git a/Gemfile.lock b/Gemfile.lock index 91d89b4875..02ccbdd8fc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -351,7 +351,7 @@ GEM posix-spawn (~> 0.3) gitlab_emoji (0.3.1) gemojione (~> 2.2, >= 2.2.1) - gitlab_git (10.0.0) + gitlab_git (10.0.2) activesupport (~> 4.0) charlock_holmes (~> 0.7.3) github-linguist (~> 4.7.0) diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 1420b96840..a52c614b25 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -15,7 +15,7 @@ class Projects::CommitsController < Projects::ApplicationController if search.present? @repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact else - @repository.commits(@ref, @path, @limit, @offset) + @repository.commits(@ref, path: @path, limit: @limit, offset: @offset) end @note_counts = project.notes.where(commit_id: @commits.map(&:id)). diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index d13ea9f34b..092ef32e6e 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -17,7 +17,7 @@ class Projects::GraphsController < Projects::ApplicationController end def commits - @commits = @project.repository.commits(@ref, nil, 2000, 0, true) + @commits = @project.repository.commits(@ref, limit: 2000, skip_merges: true) @commits_graph = Gitlab::Graphs::Commits.new(@commits) @commits_per_week_days = @commits_graph.commits_per_week_days @commits_per_time = @commits_graph.commits_per_time @@ -55,7 +55,7 @@ class Projects::GraphsController < Projects::ApplicationController private def fetch_graph - @commits = @project.repository.commits(@ref, nil, 6000, 0, true) + @commits = @project.repository.commits(@ref, limit: 6000, skip_merges: true) @log = [] @commits.each do |commit| diff --git a/app/models/repository.rb b/app/models/repository.rb index d495c8d18f..292acf9044 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -87,13 +87,15 @@ class Repository nil end - def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false) + def commits(ref, path: nil, limit: nil, offset: nil, skip_merges: false, after: nil, before: nil) options = { repo: raw_repository, ref: ref, path: path, limit: limit, offset: offset, + after: after, + before: before, # --follow doesn't play well with --skip. See: # https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520 follow: false, @@ -575,7 +577,7 @@ class Repository end def contributors - commits = self.commits(nil, nil, 2000, 0, true) + commits = self.commits(nil, limit: 2000, offset: 0, skip_merges: true) commits.group_by(&:author_email).map do |email, commits| contributor = Gitlab::Contributor.new diff --git a/doc/api/commits.md b/doc/api/commits.md index 6341440c58..57c2e1d9b8 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -12,6 +12,8 @@ GET /projects/:id/repository/commits | --------- | ---- | -------- | ----------- | | `id` | integer | yes | The ID of a project | | `ref_name` | string | no | The name of a repository branch or tag or if not given the default branch | +| `since` | string | no | Only commits after or in this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ | +| `until` | string | no | Only commits before or in this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ | ```bash curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/commits" diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 4544a41b1e..93a3a5ce08 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -12,14 +12,20 @@ module API # Parameters: # id (required) - The ID of a project # ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used + # since (optional) - Only commits after or in this date will be returned + # until (optional) - Only commits before or in this date will be returned # Example Request: # GET /projects/:id/repository/commits get ":id/repository/commits" do + datetime_attributes! :since, :until + page = (params[:page] || 0).to_i per_page = (params[:per_page] || 20).to_i ref = params[:ref_name] || user_project.try(:default_branch) || 'master' + after = params[:since] + before = params[:until] - commits = user_project.repository.commits(ref, nil, per_page, page * per_page) + commits = user_project.repository.commits(ref, limit: per_page, offset: page * per_page, after: after, before: before) present commits, with: Entities::RepoCommit end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 5bbf721321..40c967453f 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -183,6 +183,22 @@ module API Gitlab::Access.options_with_owner.values.include? level.to_i end + # Checks the occurrences of datetime attributes, each attribute if present in the params hash must be in ISO 8601 + # format (YYYY-MM-DDTHH:MM:SSZ) or a Bad Request error is invoked. + # + # Parameters: + # keys (required) - An array consisting of elements that must be parseable as dates from the params hash + def datetime_attributes!(*keys) + keys.each do |key| + begin + params[key] = Time.xmlschema(params[key]) if params[key].present? + rescue ArgumentError + message = "\"" + key.to_s + "\" must be a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ" + render_api_error!(message, 400) + end + end + end + def issuable_order_by if params["order_by"] == 'updated_at' 'updated_at' diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index 67622f321a..c8f1257711 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -66,7 +66,7 @@ module Gitlab # This method provide a sample data generated with # existing project and commits to test webhooks def build_sample(project, user) - commits = project.repository.commits(project.default_branch, nil, 3) + commits = project.repository.commits(project.default_branch, limit: 3) ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}" build(project, user, commits.last.id, commits.first.id, ref, commits) end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index a306cc4aef..802b4e579e 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -561,7 +561,7 @@ describe Repository, models: true do end describe :skip_merged_commit do - subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", nil, 100, 0, true).map{ |k| k.id } } + subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", limit: 100, skip_merges: true).map{ |k| k.id } } it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') } end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index e28998d51b..cb82ca7802 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -32,6 +32,41 @@ describe API::API, api: true do expect(response.status).to eq(401) end end + + context "since optional parameter" do + it "should return project commits since provided parameter" do + commits = project.repository.commits("master") + since = commits.second.created_at + + get api("/projects/#{project.id}/repository/commits?since=#{since.utc.iso8601}", user) + + expect(json_response.size).to eq 2 + expect(json_response.first["id"]).to eq(commits.first.id) + expect(json_response.second["id"]).to eq(commits.second.id) + end + end + + context "until optional parameter" do + it "should return project commits until provided parameter" do + commits = project.repository.commits("master") + before = commits.second.created_at + + get api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user) + + expect(json_response.size).to eq(commits.size - 1) + expect(json_response.first["id"]).to eq(commits.second.id) + expect(json_response.second["id"]).to eq(commits.third.id) + end + end + + context "invalid xmlschema date parameters" do + it "should return an invalid parameter error message" do + get api("/projects/#{project.id}/repository/commits?since=invalid-date", user) + + expect(response.status).to eq(400) + expect(json_response['message']).to include "\"since\" must be a timestamp in ISO 8601 format" + end + end end describe "GET /projects:id/repository/commits/:sha" do From 3811eb0ba1e8c0f318060696853b1cab7f99dfa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 26 Apr 2016 12:54:49 +0200 Subject: [PATCH 037/138] Fix error when trying to create a wiki page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #15527. Signed-off-by: Rémy Coutable --- CHANGELOG | 3 +- app/controllers/projects/wikis_controller.rb | 4 +- app/services/wiki_pages/create_service.rb | 3 +- .../wiki/user_creates_wiki_page_spec.rb | 87 +++++++++++++++++++ .../wiki/user_updates_wiki_page_spec.rb | 46 ++++++++++ 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 spec/features/projects/wiki/user_creates_wiki_page_spec.rb create mode 100644 spec/features/projects/wiki/user_updates_wiki_page_spec.rb diff --git a/CHANGELOG b/CHANGELOG index ed151df9e2..bcbcdfc3fc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,8 +15,9 @@ v 8.8.0 (unreleased) - Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718 - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes) -v 8.7.2 +v 8.7.2 (unreleased) - The "New Branch" button is now loaded asynchronously + - Fix error 500 when trying to create a wiki page v 8.7.1 - Throttle the update of `project.last_activity_at` to 1 minute. !3848 diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index c02bc28ace..0d6c32fabd 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -40,10 +40,10 @@ class Projects::WikisController < Projects::ApplicationController end def update - @page = @project_wiki.find_page(params[:id]) - return render('empty') unless can?(current_user, :create_wiki, @project) + @page = @project_wiki.find_page(params[:id]) + if @page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page) redirect_to( namespace_project_wiki_path(@project.namespace, @project, @page), diff --git a/app/services/wiki_pages/create_service.rb b/app/services/wiki_pages/create_service.rb index 988c663b9d..24a817c06c 100644 --- a/app/services/wiki_pages/create_service.rb +++ b/app/services/wiki_pages/create_service.rb @@ -1,7 +1,8 @@ module WikiPages class CreateService < WikiPages::BaseService def execute - page = WikiPage.new(@project.wiki) + project_wiki = ProjectWiki.new(@project, current_user) + page = WikiPage.new(project_wiki) if page.create(@params) execute_hooks(page, 'create') diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb new file mode 100644 index 0000000000..d709fe8046 --- /dev/null +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +feature 'Projects > Wiki > User creates wiki page', feature: true do + let(:user) { create(:user) } + + background do + project.team << [user, :master] + login_as(user) + + visit namespace_project_path(project.namespace, project) + click_link 'Wiki' + end + + context 'wiki project is in the user namespace' do + let(:project) { create(:project, namespace: user.namespace) } + + context 'when wiki is empty' do + scenario 'user can create a new wiki page from the wiki home page' do + expect(page).to have_content('Home · Edit Page') + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Create page' + + expect(page).to have_content("Home · last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + + context 'when wiki is not empty' do + before do + WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute + end + + scenario 'user can create a new wiki page', js: true do + click_link 'New Page' + + fill_in :new_wiki_path, with: 'foo' + click_button 'Create Page' + + expect(page).to have_content('Foo · Edit Page') + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Create page' + + expect(page).to have_content("Foo · last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + end + + context 'wiki project is in the user namespace' do + let(:project) { create(:project, namespace: create(:group, :public)) } + + context 'when wiki is empty' do + scenario 'user can create a new wiki page from the wiki home page' do + expect(page).to have_content('Home · Edit Page') + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Create page' + + expect(page).to have_content("Home · last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + + context 'when wiki is not empty' do + before do + WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute + end + + scenario 'user can create a new wiki page', js: true do + click_link 'New Page' + + fill_in :new_wiki_path, with: 'foo' + click_button 'Create Page' + + expect(page).to have_content('Foo · Edit Page') + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Create page' + + expect(page).to have_content("Foo · last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + end +end diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb new file mode 100644 index 0000000000..b69d6b46ac --- /dev/null +++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +feature 'Projects > Wiki > User updates wiki page', feature: true do + let(:user) { create(:user) } + + background do + project.team << [user, :master] + login_as(user) + + visit namespace_project_path(project.namespace, project) + WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute + click_link 'Wiki' + end + + context 'wiki project is in the user namespace' do + let(:project) { create(:project, namespace: user.namespace) } + + scenario 'user can update the wiki home page' do + click_link 'Edit' + + expect(page).to have_content('Home · Edit Page') + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Save changes' + + expect(page).to have_content("Home · last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + + context 'wiki project is in the user namespace' do + let(:project) { create(:project, namespace: create(:group, :public)) } + + scenario 'user can update the wiki home page' do + click_link 'Edit' + + expect(page).to have_content('Home · Edit Page') + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Save changes' + + expect(page).to have_content("Home · last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end +end From b2ec176539dd7caf8ae463776175e00c80199470 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 28 Apr 2016 10:32:32 +0100 Subject: [PATCH 038/138] Removes duplicates from the label dropdown It concats the duplicate labels into a single label in the dropdown with the color being a gradient of the differnet colors being concatted. Closes #16555 --- .../javascripts/labels_select.js.coffee | 40 ++++++++++++++++++- spec/features/dashboard/label_filter_spec.rb | 29 ++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 spec/features/dashboard/label_filter_spec.rb diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee index 7d61cd9bf7..995fd76860 100644 --- a/app/assets/javascripts/labels_select.js.coffee +++ b/app/assets/javascripts/labels_select.js.coffee @@ -163,6 +163,21 @@ class @LabelsSelect $.ajax( url: labelUrl ).done (data) -> + data = _.chain data + .groupBy (label) -> + label.title + .map (label) -> + color = _.map label, (dup) -> + dup.color + + return { + id: label[0].id + title: label[0].title + color: color + duplicate: color.length > 1 + } + .value() + if $dropdown.hasClass 'js-extra-options' if showNo data.unshift( @@ -178,6 +193,7 @@ class @LabelsSelect if data.length > 2 data.splice 2, 0, 'divider' + callback data renderRow: (label) -> @@ -192,11 +208,31 @@ class @LabelsSelect if $dropdown.hasClass('js-multiselect') and removesAll selectedClass.push 'dropdown-clear-active' - color = if label.color? then "" else "" + if label.duplicate + spacing = 100 / label.color.length + + # Reduce the colors to 4 + label.color = label.color.filter (color, i) -> + i < 4 + + color = _.map(label.color, (color, i) -> + percentFirst = Math.floor(spacing * i) + percentSecond = Math.floor(spacing * (i + 1)) + "#{color} #{percentFirst}%,#{color} #{percentSecond}% " + ).join(',') + color = "linear-gradient(#{color})" + else + if label.color? + color = label.color[0] + + if color + colorEl = "" + else + colorEl = '' "
              • - #{color} + #{colorEl} #{_.escape(label.title)}
              • " diff --git a/spec/features/dashboard/label_filter_spec.rb b/spec/features/dashboard/label_filter_spec.rb new file mode 100644 index 0000000000..24e83d4401 --- /dev/null +++ b/spec/features/dashboard/label_filter_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe 'Dashboard > label filter', feature: true, js: true do + let(:user) { create(:user) } + let(:project) { create(:project, name: 'test', namespace: user.namespace) } + let(:project2) { create(:project, name: 'test2', path: 'test2', namespace: user.namespace) } + let(:label) { create(:label, title: 'bug', color: '#ff0000') } + let(:label2) { create(:label, title: 'bug') } + + before do + project.labels << label + project2.labels << label2 + + login_as(user) + visit issues_dashboard_path + end + + context 'duplicate labels' do + it 'should remove duplicate labels' do + page.within('.labels-filter') do + click_button 'Label' + end + + page.within('.dropdown-menu-labels') do + expect(page).to have_selector('.dropdown-content a', text: 'bug', count: 1) + end + end + end +end From 6e5f0fbad47c28d3cf33fd52296e278ed4625d5a Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Fri, 29 Apr 2016 12:08:53 -0400 Subject: [PATCH 039/138] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index ed151df9e2..5bd897126b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.8.0 (unreleased) - Backport GitLab Enterprise support from EE - Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718 - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes) + - Added multiple colors for labels in dropdowns when dups happen. v 8.7.2 - The "New Branch" button is now loaded asynchronously From f3f820d0bd579bdaba19cf6043aa5fadd4216b98 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 29 Apr 2016 19:02:30 +0200 Subject: [PATCH 040/138] Move modal css to separate file and fix danger text for confirmation modal Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework.scss | 1 + app/assets/stylesheets/framework/modal.scss | 22 +++++++++++++++++++++ app/assets/stylesheets/pages/help.scss | 19 ------------------ app/views/shared/_confirm_modal.html.haml | 2 +- 4 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 app/assets/stylesheets/framework/modal.scss diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index c85ab9148d..560de9fc0b 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -25,6 +25,7 @@ @import "framework/lists.scss"; @import "framework/markdown_area.scss"; @import "framework/mobile.scss"; +@import "framework/modal.scss"; @import "framework/nav.scss"; @import "framework/pagination.scss"; @import "framework/progress.scss"; diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss new file mode 100644 index 0000000000..26ad2870aa --- /dev/null +++ b/app/assets/stylesheets/framework/modal.scss @@ -0,0 +1,22 @@ +.modal-body { + position: relative; + overflow-y: auto; + padding: 15px; + + .form-actions { + margin: -$gl-padding+1; + margin-top: 15px; + } + + .text-danger { + font-weight: bold; + } +} + +body.modal-open { + overflow: hidden; +} + +.modal .modal-dialog { + width: 860px; +} diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss index ee95bdf488..4a95b7b852 100644 --- a/app/assets/stylesheets/pages/help.scss +++ b/app/assets/stylesheets/pages/help.scss @@ -55,25 +55,6 @@ } } -.modal-body { - position: relative; - overflow-y: auto; - padding: 15px; - - .form-actions { - margin: -$gl-padding+1; - margin-top: 15px; - } -} - -body.modal-open { - overflow: hidden; -} - -.modal .modal-dialog { - width: 860px; -} - .documentation { padding: 7px; } diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml index 34241cd8aa..b0fc60573f 100644 --- a/app/views/shared/_confirm_modal.html.haml +++ b/app/views/shared/_confirm_modal.html.haml @@ -7,7 +7,7 @@ Confirmation required .modal-body - %p.cred.lead.js-confirm-text + %p.text-danger.js-confirm-text %p This action can lead to data loss. From f49d86cd5c680ba233e4cb46c51748d83e2f562e Mon Sep 17 00:00:00 2001 From: Connor Shea Date: Fri, 29 Apr 2016 13:11:23 -0600 Subject: [PATCH 041/138] Fix some broken links in the documentation [ci skip] --- doc/monitoring/performance/gitlab_configuration.md | 2 +- doc/monitoring/performance/influxdb_configuration.md | 2 +- doc/monitoring/performance/influxdb_schema.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/monitoring/performance/gitlab_configuration.md b/doc/monitoring/performance/gitlab_configuration.md index 90e9930221..771584268d 100644 --- a/doc/monitoring/performance/gitlab_configuration.md +++ b/doc/monitoring/performance/gitlab_configuration.md @@ -37,4 +37,4 @@ Read more on: - [Introduction to GitLab Performance Monitoring](introduction.md) - [InfluxDB Configuration](influxdb_configuration.md) - [InfluxDB Schema](influxdb_schema.md) -- [Grafana Install/Configuration](grafana_configuration.md +- [Grafana Install/Configuration](grafana_configuration.md) diff --git a/doc/monitoring/performance/influxdb_configuration.md b/doc/monitoring/performance/influxdb_configuration.md index 63aa03985e..c30cd2950d 100644 --- a/doc/monitoring/performance/influxdb_configuration.md +++ b/doc/monitoring/performance/influxdb_configuration.md @@ -181,7 +181,7 @@ Read more on: - [Introduction to GitLab Performance Monitoring](introduction.md) - [GitLab Configuration](gitlab_configuration.md) - [InfluxDB Schema](influxdb_schema.md) -- [Grafana Install/Configuration](grafana_configuration.md +- [Grafana Install/Configuration](grafana_configuration.md) [influxdb-retention]: https://docs.influxdata.com/influxdb/v0.9/query_language/database_management/#retention-policy-management [influxdb documentation]: https://docs.influxdata.com/influxdb/v0.9/ diff --git a/doc/monitoring/performance/influxdb_schema.md b/doc/monitoring/performance/influxdb_schema.md index d31b3788f3..41861860b6 100644 --- a/doc/monitoring/performance/influxdb_schema.md +++ b/doc/monitoring/performance/influxdb_schema.md @@ -85,4 +85,4 @@ Read more on: - [Introduction to GitLab Performance Monitoring](introduction.md) - [GitLab Configuration](gitlab_configuration.md) - [InfluxDB Configuration](influxdb_configuration.md) -- [Grafana Install/Configuration](grafana_configuration.md +- [Grafana Install/Configuration](grafana_configuration.md) From e28d1fa3cc2e8c2f696f6740c8a8441100542470 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 29 Apr 2016 22:21:06 +0200 Subject: [PATCH 042/138] Use a query in Project#protected_branch? This changes Project#protected_branch? to use a query to check if a branch is protected, instead of loading all ProtectedBranch records into memory just to check if the list of names includes a given branch name. --- app/models/project.rb | 2 +- spec/models/project_spec.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 5c6c36e6b3..bbc929e9bd 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -764,7 +764,7 @@ class Project < ActiveRecord::Base # Check if current branch name is marked as protected in the system def protected_branch?(branch_name) - protected_branches_names.include?(branch_name) + protected_branches.where(name: branch_name).any? end def developers_can_push_to_protected_branch?(branch_name) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index e33c7d62ff..5b1cf71337 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -798,4 +798,18 @@ describe Project, models: true do end end end + + describe '#protected_branch?' do + let(:project) { create(:empty_project) } + + it 'returns true when a branch is a protected branch' do + project.protected_branches.create!(name: 'foo') + + expect(project.protected_branch?('foo')).to eq(true) + end + + it 'returns false when a branch is not a protected branch' do + expect(project.protected_branch?('foo')).to eq(false) + end + end end From b6a18f1093c88e82d2320d4d3f3c15c549b861be Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 29 Apr 2016 22:33:56 +0200 Subject: [PATCH 043/138] Tweak checking branches in Project#open_branches This changes 4 things: 1. Project#protected_branches_names has been renamed to Project#protected_branch_names. 2. Project#open_branches uses a Set for the branch names as checking values in a Set is faster than checking values in a (large) Array. 3. Some redundant code in Project#open_branches has been removed. 4. Project#protected_branch_names now uses #pluck instead of #map, removing the need for loading entire DB records into memory. --- CHANGELOG | 1 + app/models/project.rb | 16 +++++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 5bd897126b..121b03be32 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.8.0 (unreleased) + - Project#open_branches has been cleaned up and no longer loads entire records into memory. - Make build status canceled if any of the jobs was canceled and none failed - Remove future dates from contribution calendar graph. - Use ActionDispatch Remote IP for Akismet checking diff --git a/app/models/project.rb b/app/models/project.rb index bbc929e9bd..af62e8ecd9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -735,19 +735,17 @@ class Project < ActiveRecord::Base end def open_branches - all_branches = repository.branches + # We're using a Set here as checking values in a large Set is faster than + # checking values in a large Array. + protected_set = Set.new(protected_branch_names) - if protected_branches.present? - all_branches.reject! do |branch| - protected_branches_names.include?(branch.name) - end + repository.branches.reject do |branch| + protected_set.include?(branch.name) end - - all_branches end - def protected_branches_names - @protected_branches_names ||= protected_branches.map(&:name) + def protected_branch_names + @protected_branch_names ||= protected_branches.pluck(:name) end def root_ref?(branch) From b47a84b4be5781c91bf1072dcfa765b6782f0a35 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 28 Apr 2016 21:44:21 +0200 Subject: [PATCH 044/138] Added performance guidelines Fixes gitlab-org/gitlab-ce#15254 gitlab-org/gitlab-ce#14277 [ci skip] --- doc/development/README.md | 1 + doc/development/performance.md | 258 +++++++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 doc/development/performance.md diff --git a/doc/development/README.md b/doc/development/README.md index 3f3ef068f9..aa7d54c01d 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -8,6 +8,7 @@ - [How to dump production data to staging](db_dump.md) - [Instrumentation](instrumentation.md) - [Migration Style Guide](migration_style_guide.md) for creating safe migrations +- [Performance guidelines](performance.md) - [Rake tasks](rake_tasks.md) for development - [Shell commands](shell_commands.md) in the GitLab codebase - [Sidekiq debugging](sidekiq_debugging.md) diff --git a/doc/development/performance.md b/doc/development/performance.md new file mode 100644 index 0000000000..c74198650e --- /dev/null +++ b/doc/development/performance.md @@ -0,0 +1,258 @@ +# Performance Guidelines + +This document describes various guidelines to follow to ensure good and +consistent performance of GitLab. + +## Workflow + +The process of solving performance problems is roughly as follows: + +1. Make sure there's an issue open somewhere (e.g. on the GitLab CE issue + tracker), create one if there isn't. See [#15607][#15607] for an example. +2. Measure the performance of the code in a production environment such as + GitLab.com (see the [Tooling](#tooling) section below). Performance should be + measured over a period of _at least_ 24 hours. +3. Add your findings based on the measurement period (screenshots of graphs, + timings, etc) to the issue mentioned in step 1. +4. Solve the problem. +5. Create a merge request, assign the "performance" label and ping the right + people (e.g. [@yorickpeterse][@yorickpeterse] and [@joshfng][@joshfng]). +6. Once a change has been deployed make sure to _again_ measure for at least 24 + hours to see if your changes have any impact on the production environment. +7. Repeat until you're done. + +When providing timings make sure to provide: + +* The 95th percentile +* The 99th percentile +* The mean + +When providing screenshots of graphs make sure that both the X and Y axes and +the legend are clearly visible. If you happen to have access to GitLab.com's own +monitoring tools you should also provide a link to any relevant +graphs/dashboards. + +## Tooling + +GitLab provides two built-in tools to aid the process of improving performance: + +* [Sherlock](doc/development/profiling.md#sherlock) +* [GitLab Performance Monitoring](doc/monitoring/performance) + +GitLab employees can use GitLab.com's performance monitoring systems located at +, this requires you to log in using your +`@gitlab.com` Email address. Non-GitLab employees are advised to set up their +own InfluxDB + Grafana stack. + +## Benchmarks + +Benchmarks are almost always useless. Benchmarks usually only test small bits of +code in isolation and often only measure the best case scenario. On top of that, +benchmarks for libraries (e.g. a Gem) tend to be biased in favour of the +library. After all there's little benefit to an author publishing a benchmark +that shows they perform worse than their competitors. + +Benchmarks are only really useful when you need a rough (emphasis on "rough") +understanding of the impact of your changes. For example, if a certain method is +slow a benchmark can be used to see if the changes you're making have any impact +on the method's performance. However, even when a benchmark shows your changes +improve performance there's no guarantee the performance also improves in a +production environment. + +When writing benchmarks you should almost always use +[benchmark-ips](https://github.com/evanphx/benchmark-ips). Ruby's `Benchmark` +module that comes with the standard library is rarely useful as it runs either a +single iteration (when using `Benchmark.bm`) or two iterations (when using +`Benchmark.bmbm`). Running this few iterations means external factors (e.g. a +video streaming in the background) can very easily skew the benchmark +statistics. + +Another problem with the `Benchmark` module is that it displays timings, not +iterations. This means that if a piece of code completes in a very short period +of time it can be very difficult to compare the timings before and after a +certain change. This in turn leads to patterns such as the following: + +```ruby +Benchmark.bmbm(10) do |bench| + bench.report 'do something' do + 100.times do + ... work here ... + end + end +end +``` + +This however leads to the question: how many iterations should we run to get +meaningful statistics? + +The benchmark-ips Gem basically takes care of all this and much more, and as a +result of this should be used instead of the `Benchmark` module. + +In short: + +1. Don't trust benchmarks you find on the internet. +2. Never make claims based on just benchmarks, always measure in production to + confirm your findings. +3. X being N times faster than Y is meaningless if you don't know what impact it + will actually have on your production environment. +4. A production environment is the _only_ benchmark that always tells the truth + (unless your performance monitoring systems are not set up correctly). +5. If you must write a benchmark use the benchmark-ips Gem instead of Ruby's + `Benchmark` module. + +## Importance of Changes + +When working on performance improvements it's important to always ask yourself +the question "How important is it to improve the performance of this piece of +code?". Not every piece of code is equally important and it would be a waste to +spend a week trying to improve something that only impacts a tiny fraction of +our users. For example, spending a week trying to squeeze 10 milliseconds out of +a method is a waste of time when you could have spent a week squeezing out 10 +seconds elsewhere. + +There is no clear set of steps that you can follow to determine if a certain +piece of code is worth optimizing. The only two things you can do are: + +1. Think about what the code does, how it's used, how many times it's called and + how much time is spent in it relative to the total execution time (e.g. the + total time spent in a web request). +2. Ask others (preferably in the form of an issue). + +Some examples of changes that aren't really important/worth the effort: + +* Replacing double quotes with single quotes. +* Replacing usage of Array with Set when the list of values is very small. +* Replacing library A with library B when both only take up 0.1% of the total + execution time. +* Calling `freeze` on every string (see [String Freezing](#string-freezing)). + +## Slow Operations & Sidekiq + +Slow operations (e.g. merging branches) or operations that are prone to errors +(using external APIs) should be performed in a Sidekiq worker instead of +directly in a web request as much as possible. This has numerous benefits such +as: + +1. An error won't prevent the request from completing. +2. The process being slow won't affect the loading time of a page. +3. In case of a failure it's easy to re-try the process (Sidekiq takes care of + this automatically). +4. By isolating the code from a web request it will hopefully be easier to test + and maintain. + +It's especially important to use Sidekiq as much as possible when dealing with +Git operations as these operations can take quite some time to complete +depending on the performance of the underlying storage system. + +## Git Operations + +Care should be taken to not run unnecessary Git operations. For example, +retrieving the list of branch names using `Repository#branch_names` can be done +without an explicit check if a repository exists or not. In other words, instead +of this: + +```ruby +if repository.exists? + repository.branch_names.each do |name| + ... + end +end +``` + +You can instead just write: + +```ruby +repository.branch_names.each do |name| + ... +end +``` + +## Caching + +Operations that will often return the same result should be cached using Redis, +in particular Git operations. When caching data in Redis make sure the cache is +flushed whenever needed. For example, a cache for the list of tags should be +flushed whenever a new tag is pushed or a tag is removed. + +When adding cache expiration code for repositories this code should be placed in +one of the before/after hooks residing in the Repository class. For example, if +a cache should be flushed after importing a repository this code should be added +to `Repository#after_import`. This ensures the cache logic stays within the +Repository class instead of leaking into other classes. + +When caching data make sure to also memoize the result in an instance variable. +While retrieving data from Redis is much faster than raw Git operations it still +has overhead. By caching the result in an instance variable repeated calls to +the same method won't end up retrieving data from Redis upon every call. When +memoizing cached data in an instance variable make sure to also reset the +instance variable when flushing the cache. An example: + + +```ruby +def first_branch + @first_branch ||= cache.fetch(:first_branch) { branches.first } +end + +def expire_first_branch_cache + cache.expire(:first_branch) + @first_branch = nil +end +``` + +## Anti-Patterns + +This is a collection of [anti-patterns][anti-pattern] that should be avoided +unless these changes have a measurable, significant and positive impact on +production environments. + +### String Freezing + +In recent Ruby versions calling `freeze` on a String leads to it being allocated +only once and re-used. For example, on Ruby 2.3 this will only allocate the +"foo" String once: + +```ruby +10.times do + 'foo'.freeze +end +``` + +Blindly adding a `.freeze` call to every String is an anti-pattern that should +be avoided unless one can prove (using production data) the call actually has a +positive impact on performance. + +This feature of Ruby wasn't really meant to make things faster directly, instead +it was meant to reduce the number of allocations. Depending on the size of the +String and how frequently it would be allocated (before the `.freeze` call was +added) this _may_ make things faster, but there's no guarantee it will. + +Another common flavour of this is to not only freeze a String but also assign it +to a constant, for example: + +```ruby +SOME_CONSTANT = 'foo'.freeze + +9000.times do + SOME_CONSTANT +end +``` + +The only reason you should be doing this is to prevent somebody from mutating +the global String. However, since you can just re-assign constants in Ruby +there's nothing stopping somebody from doing this elsewhere in the code: + +```ruby +SOME_CONSTANT = 'bar' +``` + +### Moving Allocations to Constants + +Storing an object as a constant so you only allocate it once _may_ improve +performance but there's no guarantee this will. Looking up constants has an +impact on runtime performance and as such using a constant instead of +referencing an object directly may even slow code down. + +[#15607]: https://gitlab.com/gitlab-org/gitlab-ce/issues/15607 +[@yorickpeterse]: https://gitlab.com/u/yorickpeterse +[@joshfng]: https://gitlab.com/u/joshfng +[anti-pattern]: https://en.wikipedia.org/wiki/Anti-pattern From 8d3debe4cc964d830a2f21a7e950f143cc9008ca Mon Sep 17 00:00:00 2001 From: Connor Shea Date: Thu, 28 Apr 2016 12:12:03 -0600 Subject: [PATCH 045/138] Add more parameters to the filter_parameters config. Adds Sentry DSN, Webhooks, Deploy Keys, etc. Alphabetized the parameters and included line breaks between each parameter. Easier to merge into EE if there are any differences. This also seems to be the more popular syntax for adding new parameters, from what I can find. --- config/application.rb | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/config/application.rb b/config/application.rb index 2e2ed48db0..b602e2b616 100644 --- a/config/application.rb +++ b/config/application.rb @@ -32,7 +32,30 @@ module Gitlab config.encoding = "utf-8" # Configure sensitive parameters which will be filtered from the log file. - config.filter_parameters.push(:password, :password_confirmation, :private_token, :otp_attempt, :variables, :import_url) + # + # Parameters filtered: + # - Password (:password, :password_confirmation) + # - Private tokens (:private_token) + # - Two-factor tokens (:otp_attempt) + # - Repo/Project Import URLs (:import_url) + # - Build variables (:variables) + # - GitLab Pages SSL cert/key info (:certificate, :encrypted_key) + # - Webhook URLs (:hook) + # - Sentry DSN (:sentry_dsn) + # - Deploy keys (:key) + config.filter_parameters += %i( + certificate + encrypted_key + hook + import_url + key + otp_attempt + password + password_confirmation + private_token + sentry_dsn + variables + ) # Enable escaping HTML in JSON. config.active_support.escape_html_entities_in_json = true From 0cd5edf35cfaca74344dd389aadd65f0f179d395 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Wed, 27 Apr 2016 04:24:49 -0300 Subject: [PATCH 046/138] Backported minimal safewebhook implementation to GitLab CE --- app/models/hooks/project_hook.rb | 1 + app/models/hooks/service_hook.rb | 1 + app/models/hooks/system_hook.rb | 1 + app/models/hooks/web_hook.rb | 24 ++++++---- .../20160413115152_add_token_to_web_hooks.rb | 5 ++ db/schema.rb | 1 + spec/factories/project_hooks.rb | 4 ++ spec/models/hooks/web_hook_spec.rb | 46 ++++++++++++------- 8 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 db/migrate/20160413115152_add_token_to_web_hooks.rb diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index bc6e0f98c3..d149511b86 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -16,6 +16,7 @@ # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null +# token :string # class ProjectHook < WebHook diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb index 80962264ba..f45145eeb3 100644 --- a/app/models/hooks/service_hook.rb +++ b/app/models/hooks/service_hook.rb @@ -16,6 +16,7 @@ # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null +# token :string # class ServiceHook < WebHook diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index 15dddcc244..012cc8ec00 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -16,6 +16,7 @@ # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null +# token :string # class SystemHook < WebHook diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 3a2e4f546f..1e3b481559 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -16,6 +16,7 @@ # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null +# token :string # class WebHook < ActiveRecord::Base @@ -43,23 +44,17 @@ class WebHook < ActiveRecord::Base if parsed_url.userinfo.blank? response = WebHook.post(url, body: data.to_json, - headers: { - "Content-Type" => "application/json", - "X-Gitlab-Event" => hook_name.singularize.titleize - }, + headers: build_headers(hook_name), verify: enable_ssl_verification) else - post_url = url.gsub("#{parsed_url.userinfo}@", "") + post_url = url.gsub("#{parsed_url.userinfo}@", '') auth = { username: CGI.unescape(parsed_url.user), password: CGI.unescape(parsed_url.password), } response = WebHook.post(post_url, body: data.to_json, - headers: { - "Content-Type" => "application/json", - "X-Gitlab-Event" => hook_name.singularize.titleize - }, + headers: build_headers(hook_name), verify: enable_ssl_verification, basic_auth: auth) end @@ -73,4 +68,15 @@ class WebHook < ActiveRecord::Base def async_execute(data, hook_name) Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data, hook_name) end + + private + + def build_headers(hook_name) + headers = { + 'Content-Type' => 'application/json', + 'X-Gitlab-Event' => hook_name.singularize.titleize + } + headers['X-Gitlab-Token'] = token if token.present? + headers + end end diff --git a/db/migrate/20160413115152_add_token_to_web_hooks.rb b/db/migrate/20160413115152_add_token_to_web_hooks.rb new file mode 100644 index 0000000000..f04225068c --- /dev/null +++ b/db/migrate/20160413115152_add_token_to_web_hooks.rb @@ -0,0 +1,5 @@ +class AddTokenToWebHooks < ActiveRecord::Migration + def change + add_column :web_hooks, :token, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 42457d9235..04aee737e4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1025,6 +1025,7 @@ ActiveRecord::Schema.define(version: 20160421130527) do t.boolean "enable_ssl_verification", default: true t.boolean "build_events", default: false, null: false t.boolean "wiki_page_events", default: false, null: false + t.string "token" end add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb index 94dd935a03..3195fb3ddc 100644 --- a/spec/factories/project_hooks.rb +++ b/spec/factories/project_hooks.rb @@ -1,5 +1,9 @@ FactoryGirl.define do factory :project_hook do url { FFaker::Internet.uri('http') } + + trait :token do + token { SecureRandom.hex(10) } + end end end diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 04bc2dcfb1..37a27d73aa 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -43,51 +43,65 @@ describe WebHook, models: true do end describe "execute" do + let(:project) { create(:project) } + let(:project_hook) { create(:project_hook) } + before(:each) do - @project_hook = create(:project_hook) - @project = create(:project) - @project.hooks << [@project_hook] + project.hooks << [project_hook] @data = { before: 'oldrev', after: 'newrev', ref: 'ref' } - WebMock.stub_request(:post, @project_hook.url) + WebMock.stub_request(:post, project_hook.url) + end + + context 'when token is defined' do + let(:project_hook) { create(:project_hook, :token) } + + it 'POSTs to the webhook URL' do + project_hook.execute(@data, 'push_hooks') + expect(WebMock).to have_requested(:post, project_hook.url).with( + headers: { 'Content-Type' => 'application/json', + 'X-Gitlab-Event' => 'Push Hook', + 'X-Gitlab-Token' => project_hook.token } + ).once + end end it "POSTs to the webhook URL" do - @project_hook.execute(@data, 'push_hooks') - expect(WebMock).to have_requested(:post, @project_hook.url).with( - headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook' } + project_hook.execute(@data, 'push_hooks') + expect(WebMock).to have_requested(:post, project_hook.url).with( + headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Push Hook' } ).once end it "POSTs the data as JSON" do - @project_hook.execute(@data, 'push_hooks') - expect(WebMock).to have_requested(:post, @project_hook.url).with( - headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook' } + project_hook.execute(@data, 'push_hooks') + expect(WebMock).to have_requested(:post, project_hook.url).with( + headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Push Hook' } ).once end it "catches exceptions" do expect(WebHook).to receive(:post).and_raise("Some HTTP Post error") - expect { @project_hook.execute(@data, 'push_hooks') }.to raise_error(RuntimeError) + expect { project_hook.execute(@data, 'push_hooks') }.to raise_error(RuntimeError) end it "handles SSL exceptions" do expect(WebHook).to receive(:post).and_raise(OpenSSL::SSL::SSLError.new('SSL error')) - expect(@project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error']) + expect(project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error']) end it "handles 200 status code" do - WebMock.stub_request(:post, @project_hook.url).to_return(status: 200, body: "Success") + WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: "Success") - expect(@project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) + expect(project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) end it "handles 2xx status codes" do - WebMock.stub_request(:post, @project_hook.url).to_return(status: 201, body: "Success") + WebMock.stub_request(:post, project_hook.url).to_return(status: 201, body: "Success") - expect(@project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) + expect(project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) end end end From c9577711cee5f9eec699711d39196480f400a746 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 30 Apr 2016 21:14:40 +0200 Subject: [PATCH 047/138] Handle issue move access instead of raising error Closes #15533 --- app/controllers/projects/issues_controller.rb | 2 + .../projects/issues_controller_spec.rb | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 9face235ba..016f5dd000 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -96,6 +96,8 @@ class Projects::IssuesController < Projects::ApplicationController if params[:move_to_project_id].to_i > 0 new_project = Project.find(params[:move_to_project_id]) + return render_404 unless issue.can_move?(current_user, new_project) + move_service = Issues::MoveService.new(project, current_user) @issue = move_service.execute(@issue, new_project) end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index d6e4cd71ce..2b2ad3b941 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -40,6 +40,45 @@ describe Projects::IssuesController do end end + describe 'PUT #update' do + context 'when moving issue to another private project' do + let(:another_project) { create(:project, :private) } + + before do + sign_in(user) + project.team << [user, :developer] + end + + context 'when user has access to move issue' do + before { another_project.team << [user, :reporter] } + + it 'moves issue to another project' do + move_issue + + expect(response).to have_http_status :found + expect(another_project.issues).to_not be_empty + end + end + + context 'when user does not have access to move issue' do + it 'responds with 404' do + move_issue + + expect(response).to have_http_status :not_found + end + end + + def move_issue + put :update, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: issue.iid, + issue: { title: 'New title' }, + move_to_project_id: another_project.id + end + end + end + describe 'Confidential Issues' do let(:project) { create(:project_empty_repo, :public) } let(:assignee) { create(:assignee) } From 6243a834115c9c815fe9a1224824f02f9fb3857f Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 30 Apr 2016 22:36:26 -0700 Subject: [PATCH 048/138] Bump mail_room to 0.7.0 to fix stuck IDLE connections Closes #13357 --- CHANGELOG | 1 + Gemfile | 2 +- Gemfile.lock | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 121b03be32..7b34095af9 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.8.0 (unreleased) - Project#open_branches has been cleaned up and no longer loads entire records into memory. - Make build status canceled if any of the jobs was canceled and none failed + - Bump mail_room to 0.7.0 to fix stuck IDLE connections - Remove future dates from contribution calendar graph. - Use ActionDispatch Remote IP for Akismet checking - Fix error when visiting commit builds page before build was updated diff --git a/Gemfile b/Gemfile index 25c13fda57..8210f4cb3d 100644 --- a/Gemfile +++ b/Gemfile @@ -319,7 +319,7 @@ gem "newrelic_rpm", '~> 3.14' gem 'octokit', '~> 4.3.0' -gem "mail_room", "~> 0.6.1" +gem "mail_room", "~> 0.7" gem 'email_reply_parser', '~> 0.5.8' diff --git a/Gemfile.lock b/Gemfile.lock index b1e954e088..5ea4347112 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -465,7 +465,7 @@ GEM systemu (~> 2.6.2) mail (2.6.4) mime-types (>= 1.16, < 4) - mail_room (0.6.1) + mail_room (0.7.0) method_source (0.8.2) mime-types (2.99.1) mimemagic (0.3.0) @@ -961,7 +961,7 @@ DEPENDENCIES letter_opener_web (~> 1.3.0) licensee (~> 8.0.0) loofah (~> 2.0.3) - mail_room (~> 0.6.1) + mail_room (~> 0.7) method_source (~> 0.8) minitest (~> 5.7.0) mousetrap-rails (~> 1.4.6) From 41df76b028aa663711b8126b2aabd7f36f254153 Mon Sep 17 00:00:00 2001 From: Karlo Soriano Date: Sun, 1 May 2016 14:43:52 +0800 Subject: [PATCH 049/138] Remove unnecessary method call in events view Since `User#to_param` already returns `User#username`, we don't need to pass in the user's username. Changing this also helps us obey LoD. --- app/views/events/_event.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index 5d62258208..e4629bae0e 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -5,7 +5,7 @@ = cache [event, current_application_settings, "v2.2"] do - if event.author - = link_to user_path(event.author.username) do + = link_to user_path(event.author) do = image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:'' - else = image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:'' From 66d7acfe4ddec5d5f16bc6a7d758343ff8573fd9 Mon Sep 17 00:00:00 2001 From: Connor Shea Date: Sun, 1 May 2016 13:44:50 -0600 Subject: [PATCH 050/138] Update rspec-rails from 3.3.3 to 3.4.2. This allows the removal of the monkey patch from this commit: 47ff1c56089b3df9c36b77c02f0f3db54fea1d54 It'll also make it slightly easier to upgrade to 3.5.0 later. Changelog: https://github.com/rspec/rspec-rails/blob/master/Changelog.md#340--2015-11-11 --- Gemfile | 2 +- Gemfile.lock | 36 ++++++++++++++++++------------------ spec/spec_helper.rb | 6 ------ 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/Gemfile b/Gemfile index 25c13fda57..c10fe0a1c7 100644 --- a/Gemfile +++ b/Gemfile @@ -269,7 +269,7 @@ group :development, :test do gem 'database_cleaner', '~> 1.4.0' gem 'factory_girl_rails', '~> 4.6.0' - gem 'rspec-rails', '~> 3.3.0' + gem 'rspec-rails', '~> 3.4.0' gem 'rspec-retry' gem 'spinach-rails', '~> 0.2.1' gem 'spinach-rerun-reporter', '~> 0.0.2' diff --git a/Gemfile.lock b/Gemfile.lock index b1e954e088..b19dc5b6d2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -662,29 +662,29 @@ GEM chunky_png rqrcode-rails3 (0.1.7) rqrcode (>= 0.4.2) - rspec (3.3.0) - rspec-core (~> 3.3.0) - rspec-expectations (~> 3.3.0) - rspec-mocks (~> 3.3.0) - rspec-core (3.3.2) - rspec-support (~> 3.3.0) - rspec-expectations (3.3.1) + rspec (3.4.0) + rspec-core (~> 3.4.0) + rspec-expectations (~> 3.4.0) + rspec-mocks (~> 3.4.0) + rspec-core (3.4.4) + rspec-support (~> 3.4.0) + rspec-expectations (3.4.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) - rspec-mocks (3.3.2) + rspec-support (~> 3.4.0) + rspec-mocks (3.4.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) - rspec-rails (3.3.3) + rspec-support (~> 3.4.0) + rspec-rails (3.4.2) actionpack (>= 3.0, < 4.3) activesupport (>= 3.0, < 4.3) railties (>= 3.0, < 4.3) - rspec-core (~> 3.3.0) - rspec-expectations (~> 3.3.0) - rspec-mocks (~> 3.3.0) - rspec-support (~> 3.3.0) + rspec-core (~> 3.4.0) + rspec-expectations (~> 3.4.0) + rspec-mocks (~> 3.4.0) + rspec-support (~> 3.4.0) rspec-retry (0.4.5) rspec-core - rspec-support (3.3.0) + rspec-support (3.4.1) rubocop (0.38.0) parser (>= 2.3.0.6, < 3.0) powerpack (~> 0.1) @@ -1011,7 +1011,7 @@ DEPENDENCIES responders (~> 2.0) rouge (~> 1.10.1) rqrcode-rails3 (~> 0.1.7) - rspec-rails (~> 3.3.0) + rspec-rails (~> 3.4.0) rspec-retry rubocop (~> 0.38.0) ruby-fogbugz (~> 0.2.1) @@ -1058,4 +1058,4 @@ DEPENDENCIES wikicloth (= 0.8.1) BUNDLED WITH - 1.11.2 + 1.12.1 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 596d607f2a..576d16e7ea 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -51,10 +51,4 @@ FactoryGirl::SyntaxRunner.class_eval do include RSpec::Mocks::ExampleMethods end -# Work around a Rails 4.2.5.1 issue -# See https://github.com/rspec/rspec-rails/issues/1532 -RSpec::Rails::ViewRendering::EmptyTemplatePathSetDecorator.class_eval do - alias_method :find_all_anywhere, :find_all -end - ActiveRecord::Migration.maintain_test_schema! From 00ced598ea65f1b957c43576bc1564ed3f67d749 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Sat, 30 Apr 2016 05:34:31 -0300 Subject: [PATCH 051/138] Added UI to define secret_token for webhook and systemhook Codestyle changes to easy EE merge --- app/controllers/admin/hooks_controller.rb | 8 +++++++- app/controllers/projects/hooks_controller.rb | 14 +++++++++++--- app/views/admin/hooks/index.html.haml | 10 ++++++++-- app/views/projects/hooks/index.html.haml | 5 +++++ 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 93c4894ea0..4e85b6b4cf 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -39,6 +39,12 @@ class Admin::HooksController < Admin::ApplicationController end def hook_params - params.require(:hook).permit(:url, :enable_ssl_verification, :push_events, :tag_push_events) + params.require(:hook).permit( + :enable_ssl_verification, + :push_events, + :tag_push_events, + :token, + :url + ) end end diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 5fd4f855de..dfa9bd259e 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -52,8 +52,16 @@ class Projects::HooksController < Projects::ApplicationController end def hook_params - params.require(:hook).permit(:url, :push_events, :issues_events, - :merge_requests_events, :tag_push_events, :note_events, - :build_events, :enable_ssl_verification) + params.require(:hook).permit( + :build_events, + :enable_ssl_verification, + :issues_events, + :merge_requests_events, + :note_events, + :push_events, + :tag_push_events, + :token, + :url + ) end end diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index 67d23c8023..7b388cf786 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -13,9 +13,15 @@ = form_errors(@hook) .form-group - = f.label :url, "URL:", class: 'control-label' + = f.label :url, 'URL', class: 'control-label' .col-sm-10 - = f.text_field :url, class: "form-control" + = f.text_field :url, class: 'form-control' + .form-group + = f.label :token, 'Secret Token', class: 'control-label' + .col-sm-10 + = f.text_field :token, class: 'form-control' + %p.help-block + Use this token to validate received payloads .form-group = f.label :url, "Trigger", class: 'control-label' .col-sm-10.prepend-top-10 diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 6f1ee20943..36c1d69f06 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -15,6 +15,11 @@ .form-group = f.label :url, "URL", class: "label-light" = f.text_field :url, class: "form-control", placeholder: "http://example.com/trigger-ci.json" + .form-group + = f.label :token, "Secret Token", class: 'label-light' + = f.text_field :token, class: "form-control", placeholder: '' + %p.help-block + Use this token to validate received payloads .form-group = f.label :url, "Trigger", class: "label-light" %div From 1dfd051ec58e5c1a324d3b5493552289a44f1aa0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 2 May 2016 14:32:26 +0000 Subject: [PATCH 052/138] Remove duplicate entry in the CHANGELOG --- CHANGELOG | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 121b03be32..f1f93a3cb0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -140,7 +140,6 @@ v 8.7.0 - Import GitHub labels - Add option to filter by "Owned projects" on dashboard page - Import GitHub milestones - - Fix emoji catgories in the emoji picker - Execute system web hooks on push to the project - Allow enable/disable push events for system hooks - Fix GitHub project's link in the import page when provider has a custom URL From dc0ff9a17472c71d2e2f5510797976f9ce7cea23 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 2 May 2016 17:22:03 +0200 Subject: [PATCH 053/138] Fix "remember me" sign in option --- config/initializers/session_store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 88cb859871..599dabb9e5 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -22,7 +22,7 @@ else key: '_gitlab_session', secure: Gitlab.config.gitlab.https, httponly: true, - expire_after: Settings.gitlab['session_expire_delay'] * 60, + expires_in: Settings.gitlab['session_expire_delay'] * 60, path: (Rails.application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root ) end From f1e74de47cd9fcf334617023aaa737c83c98a7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 2 May 2016 17:49:51 +0200 Subject: [PATCH 054/138] Simplify specs by not over-expecting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- CHANGELOG | 2 +- .../wiki/user_creates_wiki_page_spec.rb | 32 ++++++++----------- .../wiki/user_updates_wiki_page_spec.rb | 18 +++++------ 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bcbcdfc3fc..efaa5f43d5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,7 +15,7 @@ v 8.8.0 (unreleased) - Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718 - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes) -v 8.7.2 (unreleased) +v 8.7.2 - The "New Branch" button is now loaded asynchronously - Fix error 500 when trying to create a wiki page diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb index d709fe8046..7e6eef6587 100644 --- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -11,17 +11,16 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do click_link 'Wiki' end - context 'wiki project is in the user namespace' do + context 'in the user namespace' do let(:project) { create(:project, namespace: user.namespace) } context 'when wiki is empty' do - scenario 'user can create a new wiki page from the wiki home page' do - expect(page).to have_content('Home · Edit Page') - + scenario 'directly from the wiki home page' do fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' - expect(page).to have_content("Home · last edited by #{user.name}") + expect(page).to have_content('Home') + expect(page).to have_content("last edited by #{user.name}") expect(page).to have_content('My awesome wiki!') end end @@ -31,34 +30,32 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute end - scenario 'user can create a new wiki page', js: true do + scenario 'via the "new wiki page" page', js: true do click_link 'New Page' fill_in :new_wiki_path, with: 'foo' click_button 'Create Page' - expect(page).to have_content('Foo · Edit Page') - fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' - expect(page).to have_content("Foo · last edited by #{user.name}") + expect(page).to have_content('Foo') + expect(page).to have_content("last edited by #{user.name}") expect(page).to have_content('My awesome wiki!') end end end - context 'wiki project is in the user namespace' do + context 'in a group namespace' do let(:project) { create(:project, namespace: create(:group, :public)) } context 'when wiki is empty' do - scenario 'user can create a new wiki page from the wiki home page' do - expect(page).to have_content('Home · Edit Page') - + scenario 'directly from the wiki home page' do fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' - expect(page).to have_content("Home · last edited by #{user.name}") + expect(page).to have_content('Home') + expect(page).to have_content("last edited by #{user.name}") expect(page).to have_content('My awesome wiki!') end end @@ -68,18 +65,17 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute end - scenario 'user can create a new wiki page', js: true do + scenario 'via the "new wiki page" page', js: true do click_link 'New Page' fill_in :new_wiki_path, with: 'foo' click_button 'Create Page' - expect(page).to have_content('Foo · Edit Page') - fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Create page' - expect(page).to have_content("Foo · last edited by #{user.name}") + expect(page).to have_content('Foo') + expect(page).to have_content("last edited by #{user.name}") expect(page).to have_content('My awesome wiki!') end end diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb index b69d6b46ac..ef82d2375d 100644 --- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb @@ -12,34 +12,32 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do click_link 'Wiki' end - context 'wiki project is in the user namespace' do + context 'in the user namespace' do let(:project) { create(:project, namespace: user.namespace) } - scenario 'user can update the wiki home page' do + scenario 'the home page' do click_link 'Edit' - expect(page).to have_content('Home · Edit Page') - fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Save changes' - expect(page).to have_content("Home · last edited by #{user.name}") + expect(page).to have_content('Home') + expect(page).to have_content("last edited by #{user.name}") expect(page).to have_content('My awesome wiki!') end end - context 'wiki project is in the user namespace' do + context 'in a group namespace' do let(:project) { create(:project, namespace: create(:group, :public)) } - scenario 'user can update the wiki home page' do + scenario 'the home page' do click_link 'Edit' - expect(page).to have_content('Home · Edit Page') - fill_in :wiki_content, with: 'My awesome wiki!' click_button 'Save changes' - expect(page).to have_content("Home · last edited by #{user.name}") + expect(page).to have_content('Home') + expect(page).to have_content("last edited by #{user.name}") expect(page).to have_content('My awesome wiki!') end end From e31b471efc7e9e21011b80a0bce9433a3d899eda Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 2 May 2016 19:22:48 +0200 Subject: [PATCH 055/138] Make files list more compact by reducing row height from 47px to 44px Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/tree.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 25b5e95583..a84fc2e031 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -16,7 +16,7 @@ tr { > td, > th { - line-height: 26px; + line-height: 23px; } &:hover { From b0cfd2a9c55e134711079a64a50c00d6fcdd766a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 2 May 2016 19:37:22 +0200 Subject: [PATCH 056/138] Use correct border color between table rows Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/tables.scss | 2 +- app/assets/stylesheets/framework/variables.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 75b770ae5a..4765a3de51 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -37,8 +37,8 @@ table { } td { + border-bottom: 1px solid; border-color: $table-border-color; - border-bottom: 1px solid $border-color; } } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index b8ed7e8a74..92ecfe5048 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -12,7 +12,7 @@ $gutter_inner_width: 258px; */ $border-color: #e5e5e5; $focus-border-color: #3aabf0; -$table-border-color: #eef0f2; +$table-border-color: #ececec; $background-color: #fafafa; /* From 606e09c141d39ccc3bcfe47ac200eef6df3861a0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 2 May 2016 19:52:46 +0200 Subject: [PATCH 057/138] Fix table borders Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/tables.scss | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 4765a3de51..b42075c98d 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -32,12 +32,10 @@ table { th { background-color: $background-color; font-weight: normal; - font-size: 15px; - border-bottom: 1px solid $border-color; + border-bottom: none; } td { - border-bottom: 1px solid; border-color: $table-border-color; } } From f64b82e1da47e0f0b4e0f6e91746ed95a98105dd Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 29 Apr 2016 16:02:28 -0700 Subject: [PATCH 058/138] Support e-mail notifications for comments on project snippets Closes #2334 --- CHANGELOG | 1 + app/mailers/emails/notes.rb | 8 +++ app/models/project_snippet.rb | 2 + app/views/notify/note_snippet_email.html.haml | 1 + app/views/notify/note_snippet_email.text.erb | 8 +++ spec/services/notification_service_spec.rb | 65 ++++++++++++++----- 6 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 app/views/notify/note_snippet_email.html.haml create mode 100644 app/views/notify/note_snippet_email.text.erb diff --git a/CHANGELOG b/CHANGELOG index 87aafb939e..3dfa074eae 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.8.0 (unreleased) - Project#open_branches has been cleaned up and no longer loads entire records into memory. - Make build status canceled if any of the jobs was canceled and none failed - Remove future dates from contribution calendar graph. + - Support e-mail notifications for comments on project snippets - Use ActionDispatch Remote IP for Akismet checking - Fix error when visiting commit builds page before build was updated - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index cdc40b81ee..96116e916d 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -28,6 +28,14 @@ module Emails mail_answer_thread(@merge_request, note_thread_options(recipient_id)) end + def note_snippet_email(recipient_id, note_id) + setup_note_mail(note_id, recipient_id) + + @snippet = @note.noteable + @target_url = namespace_project_snippet_url(*note_target_url_options) + mail_answer_thread(@snippet, note_thread_options(recipient_id)) + end + private def note_target_url_options diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb index 1f7d85a5f3..d48f054615 100644 --- a/app/models/project_snippet.rb +++ b/app/models/project_snippet.rb @@ -22,4 +22,6 @@ class ProjectSnippet < Snippet # Scopes scope :fresh, -> { order("created_at DESC") } + + participant :author, :notes end diff --git a/app/views/notify/note_snippet_email.html.haml b/app/views/notify/note_snippet_email.html.haml new file mode 100644 index 0000000000..2fa2f78466 --- /dev/null +++ b/app/views/notify/note_snippet_email.html.haml @@ -0,0 +1 @@ += render 'note_message' diff --git a/app/views/notify/note_snippet_email.text.erb b/app/views/notify/note_snippet_email.text.erb new file mode 100644 index 0000000000..4d5a406f4b --- /dev/null +++ b/app/views/notify/note_snippet_email.text.erb @@ -0,0 +1,8 @@ +New comment for Snippet <%= @snippet.id %> + +<%= url_for(namespace_project_snippet_url(@snippet.project.namespace, @snippet.project, @snippet, anchor: "note_#{@note.id}")) %> + + +Author: <%= @note.author_name %> + +<%= @note.note %> diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index d7c72dc081..4bbc4ddc3a 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -10,7 +10,7 @@ describe NotificationService, services: true do end describe 'Keys' do - describe :new_key do + describe '#new_key' do let!(:key) { create(:personal_key) } it { expect(notification.new_key(key)).to be_truthy } @@ -22,7 +22,7 @@ describe NotificationService, services: true do end describe 'Email' do - describe :new_email do + describe '#new_email' do let!(:email) { create(:email) } it { expect(notification.new_email(email)).to be_truthy } @@ -147,8 +147,8 @@ describe NotificationService, services: true do ActionMailer::Base.deliveries.clear end - describe :new_note do - it do + describe '#new_note' do + it 'notifies the team members' do notification.new_note(note) # Notify all team members @@ -177,6 +177,39 @@ describe NotificationService, services: true do end end + context 'project snippet note' do + let(:project) { create(:empty_project, :public) } + let(:snippet) { create(:project_snippet, project: project, author: create(:user)) } + let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: snippet.project.id, note: '@all mentioned') } + + before do + build_team(note.project) + note.project.team << [note.author, :master] + ActionMailer::Base.deliveries.clear + end + + describe '#new_note' do + it 'notifies the team members' 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 + # Author should not be notified + next if member.id == note.author.id + should_email(member) + end + + should_email(note.noteable.author) + should_not_email(note.author) + should_email(@u_mentioned) + should_not_email(@u_disabled) + should_email(@u_not_mentioned) + end + end + end + context 'commit note' do let(:project) { create(:project, :public) } let(:note) { create(:note_on_commit, project: project) } @@ -187,7 +220,7 @@ describe NotificationService, services: true do allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer) end - describe :new_note, :perform_enqueued_jobs do + describe '#new_note, #perform_enqueued_jobs' do it do notification.new_note(note) @@ -230,7 +263,7 @@ describe NotificationService, services: true do ActionMailer::Base.deliveries.clear end - describe :new_issue do + describe '#new_issue' do it do notification.new_issue(issue, @u_disabled) @@ -289,7 +322,7 @@ describe NotificationService, services: true do end end - describe :reassigned_issue do + describe '#reassigned_issue' do it 'emails new assignee' do notification.reassigned_issue(issue, @u_disabled) @@ -419,7 +452,7 @@ describe NotificationService, services: true do end end - describe :close_issue do + describe '#close_issue' do it 'should sent email to issue assignee and issue author' do notification.close_issue(issue, @u_disabled) @@ -435,7 +468,7 @@ describe NotificationService, services: true do end end - describe :reopen_issue do + describe '#reopen_issue' do it 'should send email to issue assignee and issue author' do notification.reopen_issue(issue, @u_disabled) @@ -461,7 +494,7 @@ describe NotificationService, services: true do ActionMailer::Base.deliveries.clear end - describe :new_merge_request do + describe '#new_merge_request' do it do notification.new_merge_request(merge_request, @u_disabled) @@ -483,7 +516,7 @@ describe NotificationService, services: true do end end - describe :reassigned_merge_request do + describe '#reassigned_merge_request' do it do notification.reassigned_merge_request(merge_request, merge_request.author) @@ -498,7 +531,7 @@ describe NotificationService, services: true do end end - describe :relabel_merge_request do + describe '#relabel_merge_request' do let(:label) { create(:label, merge_requests: [merge_request]) } let(:label2) { create(:label) } let!(:subscriber_to_label) { create(:user).tap { |u| label.toggle_subscription(u) } } @@ -527,7 +560,7 @@ describe NotificationService, services: true do end end - describe :closed_merge_request do + describe '#closed_merge_request' do it do notification.close_mr(merge_request, @u_disabled) @@ -542,7 +575,7 @@ describe NotificationService, services: true do end end - describe :merged_merge_request do + describe '#merged_merge_request' do it do notification.merge_mr(merge_request, @u_disabled) @@ -557,7 +590,7 @@ describe NotificationService, services: true do end end - describe :reopen_merge_request do + describe '#reopen_merge_request' do it do notification.reopen_mr(merge_request, @u_disabled) @@ -581,7 +614,7 @@ describe NotificationService, services: true do ActionMailer::Base.deliveries.clear end - describe :project_was_moved do + describe '#project_was_moved' do it do notification.project_was_moved(project, "gitlab/gitlab") From f0c4f727359a5848d12e2097bad6a6a3190943ef Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 2 May 2016 21:47:11 -0400 Subject: [PATCH 059/138] Update CHANGELOG for 8.6.8, 8.5.12, et al. [ci skip] --- CHANGELOG | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3dfa074eae..b6527780bb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,7 +18,7 @@ v 8.8.0 (unreleased) - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes) - Added multiple colors for labels in dropdowns when dups happen. -v 8.7.2 +v 8.7.2 (unreleased) - The "New Branch" button is now loaded asynchronously - Fix error 500 when trying to create a wiki page @@ -148,6 +148,19 @@ v 8.7.0 - Add RAW build trace output and button on build page - Add incremental build trace update into CI API +v 8.6.8 + - Prevent privilege escalation via "impersonate" feature + - Prevent privilege escalation via notes API + - Prevent privilege escalation via project webhook API + - Prevent XSS via Git branch and tag names + - Prevent XSS via custom issue tracker URL + - Prevent XSS via `window.opener` + - Prevent XSS via label drop-down + - Prevent information disclosure via milestone API + - Prevent information disclosure via snippet API + - Prevent information disclosure via project labels + - Prevent information disclosure via new merge request page + v 8.6.7 - Fix persistent XSS vulnerability in `commit_person_link` helper - Fix persistent XSS vulnerability in Label and Milestone dropdowns @@ -289,6 +302,17 @@ v 8.6.0 - Trigger a todo for mentions on commits page - Let project owners and admins soft delete issues and merge requests +v 8.5.12 + - Prevent privilege escalation via "impersonate" feature + - Prevent privilege escalation via notes API + - Prevent privilege escalation via project webhook API + - Prevent XSS via Git branch and tag names + - Prevent XSS via custom issue tracker URL + - Prevent XSS via `window.opener` + - Prevent information disclosure via snippet API + - Prevent information disclosure via project labels + - Prevent information disclosure via new merge request page + v 8.5.11 - Fix persistent XSS vulnerability in `commit_person_link` helper @@ -439,6 +463,17 @@ v 8.5.0 - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul) - Add Todos +v 8.4.10 + - Prevent privilege escalation via "impersonate" feature + - Prevent privilege escalation via notes API + - Prevent privilege escalation via project webhook API + - Prevent XSS via Git branch and tag names + - Prevent XSS via custom issue tracker URL + - Prevent XSS via `window.opener` + - Prevent information disclosure via snippet API + - Prevent information disclosure via project labels + - Prevent information disclosure via new merge request page + v 8.4.9 - Fix persistent XSS vulnerability in `commit_person_link` helper @@ -564,6 +599,15 @@ v 8.4.0 - Add IP check against DNSBLs at account sign-up - Added cache:key to .gitlab-ci.yml allowing to fine tune the caching +v 8.3.9 + - Prevent privilege escalation via "impersonate" feature + - Prevent privilege escalation via notes API + - Prevent privilege escalation via project webhook API + - Prevent XSS via custom issue tracker URL + - Prevent XSS via `window.opener` + - Prevent information disclosure via project labels + - Prevent information disclosure via new merge request page + v 8.3.8 - Fix persistent XSS vulnerability in `commit_person_link` helper @@ -673,6 +717,17 @@ v 8.3.0 - Expose Git's version in the admin area - Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye) +v 8.2.5 + - Prevent privilege escalation via "impersonate" feature + - Prevent privilege escalation via notes API + - Prevent privilege escalation via project webhook API + - Prevent XSS via `window.opener` + - Prevent information disclosure via project labels + - Prevent information disclosure via new merge request page + +v 8.2.4 + - Bump Git version requirement to 2.7.4 + v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) - Fix Error 500s when creating global milestones with Unicode characters (Stan Hu) From 01aed0c84863ee0271cfb27ab3120ffdf2be7739 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 May 2016 12:31:50 +0200 Subject: [PATCH 060/138] Dont specify sidebar for group and group settings layouts Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/group.html.haml | 1 - app/views/layouts/group_settings.html.haml | 1 - 2 files changed, 2 deletions(-) diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index 7bee7e172e..f06acc98ca 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -1,7 +1,6 @@ - page_title @group.name - page_description @group.description unless page_description - header_title group_title(@group) unless header_title -- sidebar "dashboard" unless sidebar - nav "group" = render template: "layouts/application" diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml index 1ff53ce2a9..66b115e36d 100644 --- a/app/views/layouts/group_settings.html.haml +++ b/app/views/layouts/group_settings.html.haml @@ -1,5 +1,4 @@ - page_title "Settings" -- sidebar "dashboard" unless sidebar - nav "group" = render template: "layouts/group" From d8440b3035667623c87fe84b5a58c68da4ff936f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 May 2016 12:37:07 +0200 Subject: [PATCH 061/138] Fix active state for group pages if user not logged in Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/nav/_explore.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml index f08c5edf99..3b40006a0c 100644 --- a/app/views/layouts/nav/_explore.html.haml +++ b/app/views/layouts/nav/_explore.html.haml @@ -4,7 +4,7 @@ = icon('bookmark fw') %span Projects - = nav_link(controller: :groups) do + = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do = link_to explore_groups_path, title: 'Groups' do = icon('group fw') %span From 774af93027a5e1758adac00eb61178ecd85567ed Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 May 2016 13:04:47 +0200 Subject: [PATCH 062/138] Remove go to dashboard test suite for group page and get rid of AR warning Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/nav/_group.html.haml | 2 +- features/groups.feature | 4 ---- features/steps/group/milestones.rb | 6 ++++-- features/steps/groups.rb | 4 ---- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index bc5ce7052e..87ee765b8f 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -16,7 +16,7 @@ %li = link_to edit_group_path(@group) do Edit Group - - if access = @group.users.find(current_user) + - if access = @group.users.find(current_user.id) %li = link_to leave_group_group_members_path(@group), data: { confirm: leave_group_message(@group.name) }, method: :delete, title: 'Leave group' do diff --git a/features/groups.feature b/features/groups.feature index 419a5d3963..49e939807b 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -7,10 +7,6 @@ Feature: Groups When I visit group "NonExistentGroup" page Then page status code should be 404 - Scenario: I should have back to group button - When I visit group "Owned" page - Then I should see back to dashboard button - @javascript Scenario: I should see group "Owned" dashboard list When I visit group "Owned" page diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index a167d25983..f5fddab357 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -5,7 +5,9 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps include SharedUser step 'I click on group milestones' do - click_link 'Milestones' + page.within('.layout-nav') do + click_link 'Milestones' + end end step 'I should see group milestones index page has no milestones' do @@ -84,7 +86,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps end step 'I click on the "Labels" tab' do - page.within('.nav-links') do + page.within('.content .nav-links') do page.find(:xpath, "//a[@href='#tab-labels']").click end end diff --git a/features/steps/groups.rb b/features/steps/groups.rb index e5b7db4c5e..483370f41c 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -4,10 +4,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps include SharedGroup include SharedUser - step 'I should see back to dashboard button' do - expect(page).to have_content 'Go to dashboard' - end - step 'I should see group "Owned"' do expect(page).to have_content '@owned' end From 268868bcf0e1d09448c2212fb5275d1264d21b1b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 May 2016 13:21:09 +0200 Subject: [PATCH 063/138] Improve milestone page UI Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/milestone.scss | 4 ++-- app/views/projects/milestones/show.html.haml | 10 +++++----- app/views/shared/milestones/_top.html.haml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index d0e72a4422..b94f524b51 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -28,7 +28,7 @@ li.milestone { // Issue title span a { - color: rgba(0,0,0,0.64); + color: $gl-text-color; } } } @@ -51,7 +51,7 @@ li.milestone { margin-top: 7px; .issuable-number { - color: rgba(0,0,0,0.44); + color: $gl-placeholder-color; margin-right: 5px; } .avatar { diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 56543ccd06..6ec8466015 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -24,15 +24,15 @@ - else = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped" - = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-nr" do - = icon('trash-o') - Delete - = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped btn-nr" do = icon('pencil-square-o') Edit -.detail-page-description.milestone-detail.second-block + = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do + = icon('trash-o') + Delete + +.detail-page-description.milestone-detail %h2.title = markdown escape_once(@milestone.title), pipeline: :single_line %div diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index cab8743a07..7ff947a51d 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -24,7 +24,7 @@ - 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" -.detail-page-description.gray-content-block.second-block +.detail-page-description.milestone-detail %h2.title = markdown escape_once(milestone.title), pipeline: :single_line From bdda1eeddeb8a49484d221ae7217a9983bf984c4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 May 2016 14:30:42 +0200 Subject: [PATCH 064/138] Fix 404 on group page if user is not member of page Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/nav/_group.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 87ee765b8f..0971bccfcd 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -12,11 +12,11 @@ = link_to projects_group_path(@group), title: 'Projects' do Projects %li.divider - - if @group && can?(current_user, :admin_group, @group) + - if can?(current_user, :admin_group, @group) %li = link_to edit_group_path(@group) do Edit Group - - if access = @group.users.find(current_user.id) + - if access = @group.users.find_by(id: current_user.id) %li = link_to leave_group_group_members_path(@group), data: { confirm: leave_group_message(@group.name) }, method: :delete, title: 'Leave group' do From 1fddfa8f2a7b6bd6e71f7e997f152a2f8baa15d4 Mon Sep 17 00:00:00 2001 From: Karlo Soriano Date: Tue, 3 May 2016 21:17:39 +0800 Subject: [PATCH 065/138] Link to gitlab code review guide in contributing guidelines I noticed that the contributing guidelines still link to the thoughtbot code review guidelines even though we already have our own. [ci skip] --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24cd586453..783d6947ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -349,7 +349,7 @@ on your merge request feel free to mention one of the Merge Marshalls in the Please ensure that your merge request meets the contribution acceptance criteria. When having your code reviewed and when reviewing merge requests please take the -[Thoughtbot code review guide] into account. +[code review guidelines](doc/development/code_review.md) into account. ### Merge request description format @@ -523,4 +523,3 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design [free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12 [`gitlab1.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/gitlab1.atype/ -[Thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review From 50d18a1e1d945297de4162b2712fa6bbcee0e42e Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Tue, 3 May 2016 09:29:15 -0500 Subject: [PATCH 066/138] Rake drop tables with cascade --- lib/tasks/gitlab/db.rake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index 4921c6e0bc..1c706dc11b 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -29,7 +29,10 @@ namespace :gitlab do tables.delete 'schema_migrations' # Truncate schema_migrations to ensure migrations re-run connection.execute('TRUNCATE schema_migrations') - tables.each { |t| connection.execute("DROP TABLE #{t}") } + # Drop tables with cascade to avoid dependent table errors + # PG: http://www.postgresql.org/docs/current/static/ddl-depend.html + # MySQL: http://dev.mysql.com/doc/refman/5.7/en/drop-table.html + tables.each { |t| connection.execute("DROP TABLE #{t} CASCADE") } end end end From 94df8d5fc87a6a3a43a23b8f484de9e24af837d1 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 3 May 2016 16:39:08 +0200 Subject: [PATCH 067/138] Updated CHANGELOG for 8.7.2 [ci skip] --- CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index b6527780bb..11b1768c8e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,9 +18,10 @@ v 8.8.0 (unreleased) - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes) - Added multiple colors for labels in dropdowns when dups happen. -v 8.7.2 (unreleased) +v 8.7.2 - The "New Branch" button is now loaded asynchronously - Fix error 500 when trying to create a wiki page + - Updated spacing between notification label and button v 8.7.1 - Throttle the update of `project.last_activity_at` to 1 minute. !3848 From b7641dbb51102b24e692efa5ba01d52a88bce855 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 3 May 2016 11:24:30 -0400 Subject: [PATCH 068/138] Update link to release docs in README [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index afa60116eb..36f4bb12df 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ There are a lot of [third-party applications integrating with GitLab](https://ab ## GitLab release cycle -For more information about the release process see the [release documentation](http://doc.gitlab.com/ce/release/). +For more information about the release process see the [release documentation](https://gitlab.com/gitlab-org/release-tools/blob/master/README.md). ## Upgrading From f53557a4faa02c4596d16819c4b5c7783829677a Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Tue, 3 May 2016 11:44:55 -0500 Subject: [PATCH 069/138] Group navigation design update --- app/assets/stylesheets/framework/blocks.scss | 7 +++++++ app/assets/stylesheets/framework/header.scss | 4 ++++ app/assets/stylesheets/framework/nav.scss | 10 +++++++--- .../stylesheets/framework/variables.scss | 2 ++ app/helpers/nav_helper.rb | 13 +++++++----- app/views/groups/show.html.haml | 20 +++++++------------ app/views/layouts/nav/_group.html.haml | 6 ++++-- 7 files changed, 39 insertions(+), 23 deletions(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index e72e4aa47e..57f722c481 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -150,6 +150,13 @@ right: auto; } } + + &.groups-cover-block { + background: $white-light; + border-bottom: 1px solid $border-color; + text-align: left; + padding: 24px 0; + } } .block-connector { diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 5fa10d29a8..97f9d58200 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -30,6 +30,10 @@ header { border: none; border-bottom: 1px solid $border-color; + &.with-horizontal-nav { + border-bottom: none; + } + .container-fluid { width: 100% !important; filter: none; diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 5fe687dcec..40b24e96fa 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -26,8 +26,8 @@ } &.active a { - color: #000; - border-bottom: 2px solid #4688f1; + border-bottom: 2px solid $link-underline-blue; + color: $black; } .badge { @@ -193,7 +193,7 @@ .controls { float: right; position: relative; - top: 10px; + top: 0; .dropdown { margin-left: 7px; @@ -202,5 +202,9 @@ .nav-links { border-bottom: none; + + a { + padding-top: 2px; + } } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index b8ed7e8a74..3b69e50aca 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -74,6 +74,7 @@ $btn-transparent-color: #8f8f8f; $settings-icon-size: 18px; $provider-btn-group-border: #e5e5e5; $provider-btn-not-active-color: #4688f1; +$link-underline-blue: #4a8bee; /* * Color schema @@ -108,6 +109,7 @@ $red-light: #e52c5a; $red-normal: #d22852; $red-dark: darken($red-normal, 5%); +$black: #000000; $black-transparent: rgba(0, 0, 0, 0.3); $border-white-light: #f1f2f4; diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 5d86bd490a..3aa4103045 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -34,10 +34,13 @@ module NavHelper end def nav_header_class - if nav_menu_collapsed? - "header-collapsed" - else - "header-expanded" - end + class_name = + if nav_menu_collapsed? + "header-collapsed" + else + "header-expanded" + end + class_name += " with-horizontal-nav" if defined?(nav) && nav + class_name end end diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index c71070e6c9..ffce7b1fc4 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -4,22 +4,16 @@ - if current_user = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") -.cover-block - .avatar-holder +.cover-block.groups-cover-block + .container-fluid = link_to group_icon(@group), target: '_blank' do = image_tag group_icon(@group), class: "avatar group-avatar s90" - .cover-title - %h1 - = @group.name - %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) } - = visibility_level_icon(@group.visibility_level, fw: false) + .cover-title + %h1 @#{@group.path} - .cover-desc.username - @#{@group.path} - - - if @group.description.present? - .cover-desc.description - = markdown(@group.description, pipeline: :description) + - if @group.description.present? + .cover-desc.description + = markdown(@group.description, pipeline: :description) %div{ class: container_class } .top-area diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 0971bccfcd..705682aeb2 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -44,14 +44,16 @@ %span Issues - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.badge.count= number_with_delimiter(issues.count) + %span.count + (#{number_with_delimiter(issues.count)}) = nav_link(path: 'groups#merge_requests') do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do = icon('tasks fw') %span Merge Requests - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.badge.count= number_with_delimiter(merge_requests.count) + %span.count + (#{number_with_delimiter(merge_requests.count)}) = nav_link(controller: [:group_members]) do = link_to group_group_members_path(@group), title: 'Members' do = icon('users fw') From 8bd793c9c58c701882447fba055cd93e55f34af5 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 3 May 2016 20:46:14 +0300 Subject: [PATCH 070/138] Copyedit performance document [ci skip] --- doc/development/performance.md | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/doc/development/performance.md b/doc/development/performance.md index c74198650e..41f415812a 100644 --- a/doc/development/performance.md +++ b/doc/development/performance.md @@ -7,7 +7,7 @@ consistent performance of GitLab. The process of solving performance problems is roughly as follows: -1. Make sure there's an issue open somewhere (e.g. on the GitLab CE issue +1. Make sure there's an issue open somewhere (e.g., on the GitLab CE issue tracker), create one if there isn't. See [#15607][#15607] for an example. 2. Measure the performance of the code in a production environment such as GitLab.com (see the [Tooling](#tooling) section below). Performance should be @@ -27,7 +27,7 @@ When providing timings make sure to provide: * The 99th percentile * The mean -When providing screenshots of graphs make sure that both the X and Y axes and +When providing screenshots of graphs, make sure that both the X and Y axes and the legend are clearly visible. If you happen to have access to GitLab.com's own monitoring tools you should also provide a link to any relevant graphs/dashboards. @@ -37,7 +37,7 @@ graphs/dashboards. GitLab provides two built-in tools to aid the process of improving performance: * [Sherlock](doc/development/profiling.md#sherlock) -* [GitLab Performance Monitoring](doc/monitoring/performance) +* [GitLab Performance Monitoring](doc/monitoring/performance/monitoring.md) GitLab employees can use GitLab.com's performance monitoring systems located at , this requires you to log in using your @@ -48,7 +48,7 @@ own InfluxDB + Grafana stack. Benchmarks are almost always useless. Benchmarks usually only test small bits of code in isolation and often only measure the best case scenario. On top of that, -benchmarks for libraries (e.g. a Gem) tend to be biased in favour of the +benchmarks for libraries (e.g., a Gem) tend to be biased in favour of the library. After all there's little benefit to an author publishing a benchmark that shows they perform worse than their competitors. @@ -102,7 +102,7 @@ In short: ## Importance of Changes -When working on performance improvements it's important to always ask yourself +When working on performance improvements, it's important to always ask yourself the question "How important is it to improve the performance of this piece of code?". Not every piece of code is equally important and it would be a waste to spend a week trying to improve something that only impacts a tiny fraction of @@ -114,7 +114,7 @@ There is no clear set of steps that you can follow to determine if a certain piece of code is worth optimizing. The only two things you can do are: 1. Think about what the code does, how it's used, how many times it's called and - how much time is spent in it relative to the total execution time (e.g. the + how much time is spent in it relative to the total execution time (e.g., the total time spent in a web request). 2. Ask others (preferably in the form of an issue). @@ -159,7 +159,7 @@ if repository.exists? end ``` -You can instead just write: +You can just write: ```ruby repository.branch_names.each do |name| @@ -170,21 +170,21 @@ end ## Caching Operations that will often return the same result should be cached using Redis, -in particular Git operations. When caching data in Redis make sure the cache is +in particular Git operations. When caching data in Redis, make sure the cache is flushed whenever needed. For example, a cache for the list of tags should be flushed whenever a new tag is pushed or a tag is removed. -When adding cache expiration code for repositories this code should be placed in -one of the before/after hooks residing in the Repository class. For example, if -a cache should be flushed after importing a repository this code should be added -to `Repository#after_import`. This ensures the cache logic stays within the -Repository class instead of leaking into other classes. +When adding cache expiration code for repositories, this code should be placed +in one of the before/after hooks residing in the Repository class. For example, +if a cache should be flushed after importing a repository this code should be +added to `Repository#after_import`. This ensures the cache logic stays within +the Repository class instead of leaking into other classes. -When caching data make sure to also memoize the result in an instance variable. -While retrieving data from Redis is much faster than raw Git operations it still -has overhead. By caching the result in an instance variable repeated calls to +When caching data, make sure to also memoize the result in an instance variable. +While retrieving data from Redis is much faster than raw Git operations, it still +has overhead. By caching the result in an instance variable, repeated calls to the same method won't end up retrieving data from Redis upon every call. When -memoizing cached data in an instance variable make sure to also reset the +memoizing cached data in an instance variable, make sure to also reset the instance variable when flushing the cache. An example: @@ -224,10 +224,10 @@ positive impact on performance. This feature of Ruby wasn't really meant to make things faster directly, instead it was meant to reduce the number of allocations. Depending on the size of the String and how frequently it would be allocated (before the `.freeze` call was -added) this _may_ make things faster, but there's no guarantee it will. +added), this _may_ make things faster, but there's no guarantee it will. -Another common flavour of this is to not only freeze a String but also assign it -to a constant, for example: +Another common flavour of this is to not only freeze a String, but also assign +it to a constant, for example: ```ruby SOME_CONSTANT = 'foo'.freeze @@ -248,8 +248,8 @@ SOME_CONSTANT = 'bar' ### Moving Allocations to Constants Storing an object as a constant so you only allocate it once _may_ improve -performance but there's no guarantee this will. Looking up constants has an -impact on runtime performance and as such using a constant instead of +performance, but there's no guarantee this will. Looking up constants has an +impact on runtime performance, and as such, using a constant instead of referencing an object directly may even slow code down. [#15607]: https://gitlab.com/gitlab-org/gitlab-ce/issues/15607 From 473e653e2408e818d0d61732332aeba53e5ca499 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 3 May 2016 21:53:54 +0300 Subject: [PATCH 071/138] Add clear instructions on installing the pg_trgm extension [ci skip] --- doc/install/installation.md | 56 ++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index e721e70a59..e3af302226 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -157,22 +157,64 @@ Create a `git` user for GitLab: ## 5. Database -We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](database_mysql.md). *Note*: because we need to make use of extensions you need at least pgsql 9.1. +We recommend using a PostgreSQL database. For MySQL check the +[MySQL setup guide](database_mysql.md). - # Install the database packages - sudo apt-get install -y postgresql postgresql-client libpq-dev +> **Note**: because we need to make use of extensions you need at least pgsql 9.1. - # Create a user for GitLab +1. Install the database packages: + + ```bash + sudo apt-get install -y postgresql postgresql-client libpq-dev postgresql-contrib + ``` + +1. Create a database user for GitLab: + + ```bash sudo -u postgres psql -d template1 -c "CREATE USER git CREATEDB;" + ``` - # Create the GitLab production database & grant all privileges on database +1. Create the GitLab production database and grant all privileges on database: + + ```bash sudo -u postgres psql -d template1 -c "CREATE DATABASE gitlabhq_production OWNER git;" + ``` - # Try connecting to the new database with the new user +1. Create the `pg_trgm` extension (required for GitLab 8.6+): + + ```bash + sudo -u postgres psql -d template1 -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" + ``` + +1. Try connecting to the new database with the new user: + + ```bash sudo -u git -H psql -d gitlabhq_production + ``` - # Quit the database session +1. Check if the `pg_trgm` extension is enabled: + + ```bash + SELECT true AS enabled + FROM pg_available_extensions + WHERE name = 'pg_trgm' + AND installed_version IS NOT NULL; + ``` + + If the extension is enabled this will produce the following output: + + ``` + enabled + --------- + t + (1 row) + ``` + +1. Quit the database session: + + ```bash gitlabhq_production> \q + ``` ## 6. Redis From b898810c8d94b51514a7b5582921ab9ace4e40fb Mon Sep 17 00:00:00 2001 From: connorshea Date: Thu, 31 Mar 2016 19:54:03 -0600 Subject: [PATCH 072/138] Improve the Two-factor Authentication sign-in text [ci skip] Resolves #14543. --- CHANGELOG | 1 + app/views/devise/sessions/two_factor.html.haml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 11b1768c8e..4c68c83608 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 8.8.0 (unreleased) - Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718 - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes) - Added multiple colors for labels in dropdowns when dups happen. + - Improve description for the Two-factor Authentication sign-in screen. (Connor Shea) v 8.7.2 - The "New Branch" button is now loaded asynchronously diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index 22b2c1a186..c9d1e454a5 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -4,7 +4,7 @@ %h3 Two-factor Authentication .login-body = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| - = f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-factor authentication code', required: true, autofocus: true - %p.help-block.hint If you've lost your phone, you may enter one of your recovery codes. + = f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-factor Authentication code', required: true, autofocus: true + %p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes. .prepend-top-20 = f.submit "Verify code", class: "btn btn-save" From d6c2d6bab9f00c288df3318424c4c1bbbca614dc Mon Sep 17 00:00:00 2001 From: Artem Sidorenko Date: Sat, 30 Apr 2016 17:38:39 +0200 Subject: [PATCH 073/138] Use sign out path only if not empty --- app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1c53b0b21a..17b3f49aed 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -117,7 +117,7 @@ class ApplicationController < ActionController::Base end def after_sign_out_path_for(resource) - current_application_settings.after_sign_out_path || new_user_session_path + current_application_settings.after_sign_out_path.presence || new_user_session_path end def abilities From 21ccf89685bceee2b502a9e6a9fbb55310e02ebc Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Tue, 3 May 2016 15:29:11 -0500 Subject: [PATCH 074/138] Redesign profile page and group page mobile navigation --- app/assets/stylesheets/framework/blocks.scss | 20 ++++++++++++ app/assets/stylesheets/framework/mobile.scss | 2 +- app/assets/stylesheets/framework/nav.scss | 32 ++++++++++++++++--- .../stylesheets/framework/variables.scss | 2 +- app/assets/stylesheets/pages/profile.scss | 30 +++++++++++++++++ app/views/groups/show.html.haml | 14 +++++--- 6 files changed, 88 insertions(+), 12 deletions(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 57f722c481..a78b1f30e6 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -156,6 +156,26 @@ border-bottom: 1px solid $border-color; text-align: left; padding: 24px 0; + + .group-info { + p { + margin-bottom: 0; + } + } + + @media (max-width: $screen-xs-max) { + text-align: center; + + .avatar { + float: none; + } + } + } + + .group-info { + h1 { + display: inline; + } } } diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index 7eb451c124..33cbee8598 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -30,7 +30,7 @@ } .rss-btn { - display: none !important; + display: none; } .project-home-links { diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 40b24e96fa..2ada2b9f2f 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -198,13 +198,35 @@ .dropdown { margin-left: 7px; } - } - .nav-links { - border-bottom: none; + @media (max-width: $screen-md-max) { + float: none; + margin-bottom: 10px; - a { - padding-top: 2px; + .btn { + width: 20%; + } + } + + @media (max-width: $screen-xs-max) { + text-align: center; + + .rss-btn, + .dropdown-new { + display: inline-block; + width: 48%; + } } } } + +.nav-links { + border-bottom: none; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; + + a { + padding-top: 2px; + } +} diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 3b69e50aca..7c695ba8f4 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -109,7 +109,7 @@ $red-light: #e52c5a; $red-normal: #d22852; $red-dark: darken($red-normal, 5%); -$black: #000000; +$black: #000; $black-transparent: rgba(0, 0, 0, 0.3); $border-white-light: #f1f2f4; diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 01f9847962..7a3a2ddd36 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -205,3 +205,33 @@ text-align: center; } } + +.user-profile { + @media (max-width: $screen-xs-max) { + .cover-block { + padding-top: 20px; + } + + .cover-controls { + position: static; + margin-bottom: 20px; + + .btn { + display: inline-block; + width: 48%; + } + } + } + + .cover-controls { + @media (max-width: $screen-xs-max) { + position: static; + margin-bottom: 20px; + + .btn { + display: inline-block; + width: 48%; + } + } + } +} diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index ffce7b1fc4..8c23a4ed1a 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -8,12 +8,16 @@ .container-fluid = link_to group_icon(@group), target: '_blank' do = image_tag group_icon(@group), class: "avatar group-avatar s90" - .cover-title - %h1 @#{@group.path} + .group-info + .cover-title + %h1 + @#{@group.path} + %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) } + = visibility_level_icon(@group.visibility_level, fw: false) - - if @group.description.present? - .cover-desc.description - = markdown(@group.description, pipeline: :description) + - if @group.description.present? + .cover-desc.description + = markdown(@group.description, pipeline: :description) %div{ class: container_class } .top-area From 3004386b55fede6381e5e0794c12625e2b83de25 Mon Sep 17 00:00:00 2001 From: Connor Shea Date: Tue, 3 May 2016 15:37:59 -0600 Subject: [PATCH 075/138] Revert "Remove the Devise Async gem." This reverts commit 1cc614f2bdd30b4fce35ee9e680f9272b9012978. It was causing the ActiveJob integration to fail, so unfortunately we'll have to add the gem again. --- CHANGELOG | 1 - Gemfile | 1 + Gemfile.lock | 3 +++ app/models/user.rb | 2 +- config/initializers/devise_async.rb | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 config/initializers/devise_async.rb diff --git a/CHANGELOG b/CHANGELOG index 11b1768c8e..5e64e776f7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,7 +10,6 @@ v 8.8.0 (unreleased) - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project - Updated search UI - Display informative message when new milestone is created - - Replace Devise Async with Devise ActiveJob integration. !3902 (Connor Shea) - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea) - Added button to toggle whitespaces changes on diff view - Backport GitLab Enterprise support from EE diff --git a/Gemfile b/Gemfile index 25c13fda57..890fcbc131 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,7 @@ gem "pg", '~> 0.18.2', group: :postgres # Authentication libraries gem 'devise', '~> 3.5.4' gem 'doorkeeper', '~> 3.1' +gem 'devise-async', '~> 0.9.0' gem 'omniauth', '~> 1.3.1' gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-azure-oauth2', '~> 0.0.6' diff --git a/Gemfile.lock b/Gemfile.lock index b1e954e088..ae91b0fb6d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -164,6 +164,8 @@ GEM responders thread_safe (~> 0.1) warden (~> 1.2.3) + devise-async (0.9.0) + devise (~> 3.2) devise-two-factor (2.0.1) activesupport attr_encrypted (~> 1.3.2) @@ -920,6 +922,7 @@ DEPENDENCIES database_cleaner (~> 1.4.0) default_value_for (~> 3.0.0) devise (~> 3.5.4) + devise-async (~> 0.9.0) devise-two-factor (~> 2.0.0) diffy (~> 3.0.3) doorkeeper (~> 3.1) diff --git a/app/models/user.rb b/app/models/user.rb index b6f405c698..ab48f8f196 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -91,7 +91,7 @@ class User < ActiveRecord::Base devise :two_factor_backupable, otp_number_of_backup_codes: 10 serialize :otp_backup_codes, JSON - devise :lockable, :recoverable, :rememberable, :trackable, + devise :lockable, :async, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :confirmable, :registerable attr_accessor :force_random_password diff --git a/config/initializers/devise_async.rb b/config/initializers/devise_async.rb new file mode 100644 index 0000000000..05a1852cdb --- /dev/null +++ b/config/initializers/devise_async.rb @@ -0,0 +1 @@ +Devise::Async.backend = :sidekiq From 82b5bb91179438bb74f0b6ce31f6fd3cebac6645 Mon Sep 17 00:00:00 2001 From: Aral Balkan Date: Wed, 4 May 2016 06:58:40 +0000 Subject: [PATCH 076/138] Made it clearer that issue_id means iid, not id. --- doc/api/notes.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/notes.md b/doc/api/notes.md index 7aa1c2155b..a6b5b1787f 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -15,7 +15,7 @@ GET /projects/:id/issues/:issue_id/notes Parameters: - `id` (required) - The ID of a project -- `issue_id` (required) - The ID of an issue +- `issue_id` (required) - The IID of an issue (not ID) ```json [ @@ -73,7 +73,7 @@ GET /projects/:id/issues/:issue_id/notes/:note_id Parameters: - `id` (required) - The ID of a project -- `issue_id` (required) - The ID of a project issue +- `issue_id` (required) - The IID of a project issue (not ID) - `note_id` (required) - The ID of an issue note ### Create new issue note @@ -87,7 +87,7 @@ POST /projects/:id/issues/:issue_id/notes Parameters: - `id` (required) - The ID of a project -- `issue_id` (required) - The ID of an issue +- `issue_id` (required) - The IID of an issue (not ID) - `body` (required) - The content of a note - `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z @@ -102,7 +102,7 @@ PUT /projects/:id/issues/:issue_id/notes/:note_id Parameters: - `id` (required) - The ID of a project -- `issue_id` (required) - The ID of an issue +- `issue_id` (required) - The IID of an issue (not ID) - `note_id` (required) - The ID of a note - `body` (required) - The content of a note @@ -120,7 +120,7 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `id` | integer | yes | The ID of a project | -| `issue_id` | integer | yes | The ID of an issue | +| `issue_id` | integer | yes | The IID of an issue | | `note_id` | integer | yes | The ID of a note | ```bash From 5f89c9642e6fa48d0487e40ea2dc6977676855dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 4 May 2016 12:17:12 +0200 Subject: [PATCH 077/138] Fix a spec that was failing due to !3483 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec were skipped in this MR so that tests started to fail in master instead of in this MR! Signed-off-by: Rémy Coutable --- spec/features/login_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index 4433ef2d6f..8c38dd5b12 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -37,7 +37,7 @@ feature 'Login', feature: true do end def enter_code(code) - fill_in 'Two-factor authentication code', with: code + fill_in 'Two-factor Authentication code', with: code click_button 'Verify code' end From 70551217a6cc250dc2e9a61720fc447df74e38c7 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 4 May 2016 13:17:43 +0200 Subject: [PATCH 078/138] Fixed username links in the performance guide These would end up being rendered as: @yorickpeterse @yorickpeterse [ci skip] --- doc/development/performance.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/development/performance.md b/doc/development/performance.md index 41f415812a..fb37b3a889 100644 --- a/doc/development/performance.md +++ b/doc/development/performance.md @@ -16,7 +16,7 @@ The process of solving performance problems is roughly as follows: timings, etc) to the issue mentioned in step 1. 4. Solve the problem. 5. Create a merge request, assign the "performance" label and ping the right - people (e.g. [@yorickpeterse][@yorickpeterse] and [@joshfng][@joshfng]). + people (e.g. [@yorickpeterse][yorickpeterse] and [@joshfng][joshfng]). 6. Once a change has been deployed make sure to _again_ measure for at least 24 hours to see if your changes have any impact on the production environment. 7. Repeat until you're done. @@ -253,6 +253,6 @@ impact on runtime performance, and as such, using a constant instead of referencing an object directly may even slow code down. [#15607]: https://gitlab.com/gitlab-org/gitlab-ce/issues/15607 -[@yorickpeterse]: https://gitlab.com/u/yorickpeterse -[@joshfng]: https://gitlab.com/u/joshfng +[yorickpeterse]: https://gitlab.com/u/yorickpeterse +[joshfng]: https://gitlab.com/u/joshfng [anti-pattern]: https://en.wikipedia.org/wiki/Anti-pattern From 9bd5ac50b4c0677a2999ccee4f13e2c8e20a2def Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Wed, 4 May 2016 21:33:55 +0900 Subject: [PATCH 079/138] Use new build badge URLs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36f4bb12df..c1a29c3bb1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # GitLab -[![build status](https://ci.gitlab.com/projects/1/status.svg?ref=master)](https://ci.gitlab.com/projects/1?ref=master) +[![build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) [![Build Status](https://semaphoreci.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/400484/shields_badge.svg)](https://semaphoreci.com/gitlabhq/gitlabhq) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master) From f660b2173e408a0e527b9852c92b2d57982885b5 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Wed, 4 May 2016 12:54:11 +0100 Subject: [PATCH 080/138] Instrument methods used in email diffs Make all of the nested constant instrumentation for core app code work the same way, add mailer instrumentation, and add instrumentation to the premailer gem. --- CHANGELOG | 3 +++ config/initializers/metrics.rb | 35 ++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4c68c83608..268c7a8110 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,9 @@ v 8.8.0 (unreleased) - Added multiple colors for labels in dropdowns when dups happen. - Improve description for the Two-factor Authentication sign-in screen. (Connor Shea) +v 8.7.3 + - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented + v 8.7.2 - The "New Branch" button is now loaded asynchronously - Fix error 500 when trying to create a wiki page diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb index 283936d0ef..b2d08d87ba 100644 --- a/config/initializers/metrics.rb +++ b/config/initializers/metrics.rb @@ -61,12 +61,30 @@ if Gitlab::Metrics.enabled? config.instrument_instance_methods(const) end - Dir[Rails.root.join('app', 'finders', '*.rb')].each do |path| - const = File.basename(path, '.rb').camelize.constantize + # Path to search => prefix to strip from constant + paths_to_instrument = { + ['app', 'finders'] => ['app', 'finders'], + ['app', 'mailers', 'emails'] => ['app', 'mailers'], + ['app', 'services', '**'] => ['app', 'services'], + ['lib', 'gitlab', 'diff'] => ['lib'], + ['lib', 'gitlab', 'email', 'message'] => ['lib'] + } - config.instrument_instance_methods(const) + paths_to_instrument.each do |(path, prefix)| + prefix = Rails.root.join(*prefix) + + Dir[Rails.root.join(*path + ['*.rb'])].each do |file_path| + path = Pathname.new(file_path).relative_path_from(prefix) + const = path.to_s.sub('.rb', '').camelize.constantize + + config.instrument_methods(const) + config.instrument_instance_methods(const) + end end + config.instrument_methods(Premailer::Adapter::Nokogiri) + config.instrument_instance_methods(Premailer::Adapter::Nokogiri) + [ :Blame, :Branch, :BranchCollection, :Blob, :Commit, :Diff, :Repository, :Tag, :TagCollection, :Tree @@ -97,17 +115,6 @@ if Gitlab::Metrics.enabled? config.instrument_methods(Gitlab::ReferenceExtractor) config.instrument_instance_methods(Gitlab::ReferenceExtractor) - # Instrument all service classes - services = Rails.root.join('app', 'services') - - Dir[services.join('**', '*.rb')].each do |file_path| - path = Pathname.new(file_path).relative_path_from(services) - const = path.to_s.sub('.rb', '').camelize.constantize - - config.instrument_methods(const) - config.instrument_instance_methods(const) - end - # Instrument the classes used for checking if somebody has push access. config.instrument_instance_methods(Gitlab::GitAccess) config.instrument_instance_methods(Gitlab::GitAccessWiki) From 44f89eafc08a7967544429a3f930354a5f9bbbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 15 Apr 2016 12:15:52 +0200 Subject: [PATCH 081/138] Use Rugged's TagCollection#create instead of gitlab-shell's Repository#add_tag for better performance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- CHANGELOG | 1 + app/models/repository.rb | 11 +++++-- app/services/create_tag_service.rb | 44 ++++++++++++-------------- features/steps/project/commits/tags.rb | 4 +-- lib/gitlab/backend/shell.rb | 18 ----------- spec/models/repository_spec.rb | 11 ++++--- spec/requests/api/tags_spec.rb | 4 +-- 7 files changed, 41 insertions(+), 52 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4c5c2b42df..577a3643d9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.8.0 (unreleased) - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea) - Added button to toggle whitespaces changes on diff view - Backport GitLab Enterprise support from EE + - Create tags using Rugged for performance reasons. !3745 - Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718 - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes) - Added multiple colors for labels in dropdowns when dups happen. diff --git a/app/models/repository.rb b/app/models/repository.rb index b4319297e4..c4e38980a8 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -146,10 +146,17 @@ class Repository find_branch(branch_name) end - def add_tag(tag_name, ref, message = nil) + def add_tag(user, tag_name, ref, message = nil) before_push_tag - gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message) + options = { message: message, tagger: user_to_committer(user) } if message + + tag = rugged.tags.create(tag_name, ref, options) + if tag.annotated? + Gitlab::Git::Tag.new(tag_name, ref, tag.annotation.message) + else + Gitlab::Git::Tag.new(tag_name, ref) + end end def rm_branch(user, branch_name) diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb index 55985380d3..775f9db2a4 100644 --- a/app/services/create_tag_service.rb +++ b/app/services/create_tag_service.rb @@ -8,32 +8,28 @@ class CreateTagService < BaseService end repository = project.repository - existing_tag = repository.find_tag(tag_name) - if existing_tag - return error('Tag already exists') - end - message.strip! if message - - repository.add_tag(tag_name, ref, message) - new_tag = repository.find_tag(tag_name) - - if new_tag - 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) - CreateCommitBuildsService.new.execute(project, current_user, push_data) - - if release_description - CreateReleaseService.new(@project, @current_user). - execute(tag_name, release_description) - end - - success(new_tag) - else - error('Invalid reference name') + begin + new_tag = repository.add_tag(current_user, tag_name, ref, message) + rescue Rugged::TagError + return error("Tag #{tag_name} already exists") + rescue Rugged::ReferenceError + return error("Target #{ref} is invalid") 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) + CreateCommitBuildsService.new.execute(project, current_user, push_data) + + if release_description + CreateReleaseService.new(@project, @current_user). + execute(tag_name, release_description) + end + + success(new_tag) end def success(branch) diff --git a/features/steps/project/commits/tags.rb b/features/steps/project/commits/tags.rb index eff4234a44..912ba580ef 100644 --- a/features/steps/project/commits/tags.rb +++ b/features/steps/project/commits/tags.rb @@ -57,11 +57,11 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps end step 'I should see new an error that tag ref is invalid' do - expect(page).to have_content 'Invalid reference name' + expect(page).to have_content 'Target foo is invalid' end step 'I should see new an error that tag already exists' do - expect(page).to have_content 'Tag already exists' + expect(page).to have_content 'Tag v1.0.0 already exists' end step "I visit tag 'v1.1.0' page" do diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index 5e2fb863a8..132f9cd196 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -79,24 +79,6 @@ module Gitlab 'rm-project', "#{name}.git"]) end - # Add repository tag from passed ref - # - # path - project path with namespace - # tag_name - new tag name - # ref - HEAD for new tag - # message - optional message for tag (annotated tag) - # - # Ex. - # add_tag("gitlab/gitlab-ci", "v4.0", "master") - # add_tag("gitlab/gitlab-ci", "v4.0", "master", "message") - # - def add_tag(path, tag_name, ref, message = nil) - cmd = %W(#{gitlab_shell_path}/bin/gitlab-projects create-tag #{path}.git - #{tag_name} #{ref}) - cmd << message unless message.nil? || message.empty? - Gitlab::Utils.system_silent(cmd) - end - # Gc repository # # path - project path with namespace diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 397bb5a802..5cdf644a0e 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -859,12 +859,15 @@ describe Repository, models: true do describe '#add_tag' do it 'adds a tag' do + user = build_stubbed(:user) expect(repository).to receive(:before_push_tag) + expect(repository.rugged.tags).to receive(:create). + with('8.5', 'master', + hash_including(message: 'foo', + tagger: hash_including(name: user.name, email: user.email))). + and_call_original - expect_any_instance_of(Gitlab::Shell).to receive(:add_tag). - with(repository.path_with_namespace, '8.5', 'master', 'foo') - - repository.add_tag('8.5', 'master', 'foo') + repository.add_tag(user, '8.5', 'master', 'foo') end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index edcb2bedbf..12e170b232 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -147,7 +147,7 @@ describe API::API, api: true do tag_name: 'v8.0.0', ref: 'master' expect(response.status).to eq(400) - expect(json_response['message']).to eq('Tag already exists') + expect(json_response['message']).to eq('Tag v8.0.0 already exists') end it 'should return 400 if ref name is invalid' do @@ -155,7 +155,7 @@ describe API::API, api: true do tag_name: 'mytag', ref: 'foo' expect(response.status).to eq(400) - expect(json_response['message']).to eq('Invalid reference name') + expect(json_response['message']).to eq('Target foo is invalid') end end From 209f2f1e6fd861dd7bb6a73389400b4bb266d26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 25 Apr 2016 16:31:45 +0200 Subject: [PATCH 082/138] Use a similar approach to branch creation for tag creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/models/repository.rb | 17 ++++---- app/services/create_branch_service.rb | 5 --- app/services/create_tag_service.rb | 46 +++++++------------- spec/models/repository_spec.rb | 32 ++++++++++---- spec/services/create_tag_service_spec.rb | 53 ++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 52 deletions(-) create mode 100644 spec/services/create_tag_service_spec.rb diff --git a/app/models/repository.rb b/app/models/repository.rb index c4e38980a8..d5d70f41fd 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -146,17 +146,20 @@ class Repository find_branch(branch_name) end - def add_tag(user, tag_name, ref, message = nil) - before_push_tag + def add_tag(user, tag_name, target, message = nil) + oldrev = Gitlab::Git::BLANK_SHA + ref = Gitlab::Git::TAG_REF_PREFIX + tag_name + target = commit(target).try(:id) + + return false unless target options = { message: message, tagger: user_to_committer(user) } if message - tag = rugged.tags.create(tag_name, ref, options) - if tag.annotated? - Gitlab::Git::Tag.new(tag_name, ref, tag.annotation.message) - else - Gitlab::Git::Tag.new(tag_name, ref) + GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do + rugged.tags.create(tag_name, target, options) end + + find_tag(tag_name) end def rm_branch(user, branch_name) diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb index 707c2f7ff8..9f4481a815 100644 --- a/app/services/create_branch_service.rb +++ b/app/services/create_branch_service.rb @@ -43,9 +43,4 @@ class CreateBranchService < BaseService out[:branch] = branch out end - - def build_push_data(project, user, branch) - Gitlab::PushDataBuilder. - build(project, user, Gitlab::Git::BLANK_SHA, branch.target, "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}", []) - end end diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb index 775f9db2a4..91ed0e354d 100644 --- a/app/services/create_tag_service.rb +++ b/app/services/create_tag_service.rb @@ -1,46 +1,30 @@ require_relative 'base_service' class CreateTagService < BaseService - def execute(tag_name, ref, message, release_description = nil) + def execute(tag_name, target, message, release_description = nil) valid_tag = Gitlab::GitRefValidator.validate(tag_name) - if valid_tag == false - return error('Tag name invalid') - end + return error('Tag name invalid') unless valid_tag repository = project.repository message.strip! if message + + new_tag = nil begin - new_tag = repository.add_tag(current_user, tag_name, ref, message) + new_tag = repository.add_tag(current_user, tag_name, target, message) rescue Rugged::TagError return error("Tag #{tag_name} already exists") - rescue Rugged::ReferenceError - return error("Target #{ref} is invalid") + rescue GitHooksService::PreReceiveError + return error('Tag creation was rejected by Git hook') 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) - CreateCommitBuildsService.new.execute(project, current_user, push_data) - - if release_description - CreateReleaseService.new(@project, @current_user). - execute(tag_name, release_description) + if new_tag + if release_description + CreateReleaseService.new(@project, @current_user). + execute(tag_name, release_description) + end + success.merge(tag: new_tag) + else + error("Target #{target} is invalid") end - - success(new_tag) - end - - def success(branch) - out = super() - out[:tag] = branch - out - end - - def create_push_data(project, user, tag) - commits = [project.commit(tag.target)].compact - Gitlab::PushDataBuilder. - build(project, user, Gitlab::Git::BLANK_SHA, tag.target, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", commits, tag.message) end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 5cdf644a0e..b3359a4223 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -858,16 +858,30 @@ describe Repository, models: true do end describe '#add_tag' do - it 'adds a tag' do - user = build_stubbed(:user) - expect(repository).to receive(:before_push_tag) - expect(repository.rugged.tags).to receive(:create). - with('8.5', 'master', - hash_including(message: 'foo', - tagger: hash_including(name: user.name, email: user.email))). - and_call_original + context 'with a valid target' do + let(:user) { build_stubbed(:user) } - repository.add_tag(user, '8.5', 'master', 'foo') + it 'creates the tag using rugged' do + expect(repository.rugged.tags).to receive(:create). + with('8.5', repository.commit('master').id, + hash_including(message: 'foo', + tagger: hash_including(name: user.name, email: user.email))). + and_call_original + + repository.add_tag(user, '8.5', 'master', 'foo') + end + + it 'returns a Gitlab::Git::Tag object' do + tag = repository.add_tag(user, '8.5', 'master', 'foo') + + expect(tag).to be_a(Gitlab::Git::Tag) + end + end + + context 'with an invalid target' do + it 'returns false' do + expect(repository.add_tag(user, '8.5', 'bar', 'foo')).to be false + end end end diff --git a/spec/services/create_tag_service_spec.rb b/spec/services/create_tag_service_spec.rb new file mode 100644 index 0000000000..91f9e663b6 --- /dev/null +++ b/spec/services/create_tag_service_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe CreateTagService, services: true do + let(:project) { create(:project) } + let(:repository) { project.repository } + let(:user) { create(:user) } + let(:service) { described_class.new(project, user) } + + describe '#execute' do + it 'creates the tag and returns success' do + response = service.execute('v42.42.42', 'master', 'Foo') + + expect(response[:status]).to eq(:success) + expect(response[:tag]).to be_a Gitlab::Git::Tag + expect(response[:tag].name).to eq('v42.42.42') + end + + context 'when target is invalid' do + it 'returns an error' do + response = service.execute('v1.1.0', 'foo', 'Foo') + + expect(response).to eq(status: :error, + message: 'Target foo is invalid') + end + end + + context 'when tag already exists' do + it 'returns an error' do + expect(repository).to receive(:add_tag). + with(user, 'v1.1.0', 'master', 'Foo'). + and_raise(Rugged::TagError) + + response = service.execute('v1.1.0', 'master', 'Foo') + + expect(response).to eq(status: :error, + message: 'Tag v1.1.0 already exists') + end + end + + context 'when pre-receive hook fails' do + it 'returns an error' do + expect(repository).to receive(:add_tag). + with(user, 'v1.1.0', 'master', 'Foo'). + and_raise(GitHooksService::PreReceiveError) + + response = service.execute('v1.1.0', 'master', 'Foo') + + expect(response).to eq(status: :error, + message: 'Tag creation was rejected by Git hook') + end + end + end +end From fb5682e7cd587e7e6c871588240fc49b325b042f Mon Sep 17 00:00:00 2001 From: Artem Sidorenko Date: Sat, 30 Apr 2016 16:35:10 +0200 Subject: [PATCH 083/138] Use the new admin settings for gravatar --- CHANGELOG | 1 + app/views/profiles/show.html.haml | 4 ++-- config/gitlab.yml.example | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index cc6588c6cb..4505b42498 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,7 @@ v 8.8.0 (unreleased) - Added multiple colors for labels in dropdowns when dups happen. - Improve description for the Two-factor Authentication sign-in screen. (Connor Shea) - API support for the 'since' and 'until' operators on commit requests (Paco Guzman) + - Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko) v 8.7.3 - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index f59d27f7ed..eef50d887c 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -8,11 +8,11 @@ %p - if @user.avatar? You can change your avatar here - - if Gitlab.config.gravatar.enabled + - if gravatar_enabled? or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host} - else You can upload an avatar here - - if Gitlab.config.gravatar.enabled + - if gravatar_enabled? or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host} .col-lg-9 .clearfix.avatar-image.append-bottom-default diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 07ce4b6d71..e682bcb976 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -152,7 +152,6 @@ production: &base ## Gravatar ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html gravatar: - enabled: true # Use user avatar image from Gravatar.com (default: true) # gravatar urls: possible placeholders: %{hash} %{size} %{email} # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon From 731b231357f3b0b71f60a3bb1f81f8cd78f3e318 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 3 May 2016 20:55:35 -0700 Subject: [PATCH 084/138] Log to application.log when an admin starts and stops impersonating a user Closes gitlab-org/gitlab-ee#536 --- CHANGELOG | 1 + app/controllers/admin/impersonations_controller.rb | 2 ++ app/controllers/admin/users_controller.rb | 2 ++ 3 files changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index b2f5c283a1..5f1793d472 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.8.0 (unreleased) - Project#open_branches has been cleaned up and no longer loads entire records into memory. + - Log to application.log when an admin starts and stops impersonating a user - Make build status canceled if any of the jobs was canceled and none failed - Remove future dates from contribution calendar graph. - Support e-mail notifications for comments on project snippets diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb index 2db824c87e..8be35f00a7 100644 --- a/app/controllers/admin/impersonations_controller.rb +++ b/app/controllers/admin/impersonations_controller.rb @@ -7,6 +7,8 @@ class Admin::ImpersonationsController < Admin::ApplicationController warden.set_user(impersonator, scope: :user) + Gitlab::AppLogger.info("User #{original_user.username} has stopped impersonating #{impersonator.username}") + session[:impersonator_id] = nil redirect_to admin_user_path(original_user) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index b8976fa09a..f2f654c7bc 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -41,6 +41,8 @@ class Admin::UsersController < Admin::ApplicationController warden.set_user(user, scope: :user) + Gitlab::AppLogger.info("User #{current_user.username} has started impersonating #{user.username}") + flash[:alert] = "You are now impersonating #{user.username}" redirect_to root_path From b9672d4b38514a14cc39773d30f55c9629973f63 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 4 May 2016 00:59:20 -0700 Subject: [PATCH 085/138] Limit the number of merge requests per project to avoid long seeds This step was taking a long time because seed_fu creates N / 2 merge requests for each repo, where N is the number of branches for that repo. At the time of this writing, there are 234 branches on the gitlab-ce repo, leading to 117 merge requests. --- db/fixtures/development/10_merge_requests.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb index 0825776ffa..87fb8e3300 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/10_merge_requests.rb @@ -1,6 +1,9 @@ Gitlab::Seeder.quiet do + # Limit the number of merge requests per project to avoid long seeds + MAX_NUM_MERGE_REQUESTS = 10 + Project.all.reject(&:empty_repo?).each do |project| - branches = project.repository.branch_names + branches = project.repository.branch_names.sample(MAX_NUM_MERGE_REQUESTS * 2) branches.each do |branch_name| break if branches.size < 2 From 003b36b45f38ca3116af5d9179fff4adb26a4eb1 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 4 May 2016 11:43:47 -0500 Subject: [PATCH 086/138] Fixed layout nav, revert back to badge styles, general enhancements --- app/assets/stylesheets/framework/nav.scss | 36 +++++++++++++------ app/assets/stylesheets/framework/sidebar.scss | 8 +++++ app/views/groups/show.html.haml | 2 +- app/views/layouts/_page.html.haml | 2 +- app/views/layouts/nav/_group.html.haml | 6 ++-- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 2ada2b9f2f..c79de099d3 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -140,6 +140,12 @@ } } + .project-filter-form { + input { + background-color: $background-color; + } + } + @media (max-width: $screen-xs-max) { padding-bottom: 0; @@ -187,13 +193,17 @@ } .layout-nav { + position: fixed; + top: 58px; + width: 100%; + z-index: 1; background: $background-color; border-bottom: 1px solid $border-color; + transition-duration: .3s; .controls { float: right; - position: relative; - top: 0; + padding: 12px 5px 0 0; .dropdown { margin-left: 7px; @@ -218,15 +228,21 @@ } } } -} -.nav-links { - border-bottom: none; - white-space: nowrap; - overflow-x: auto; - overflow-y: hidden; + .nav-links { + border-bottom: none; - a { - padding-top: 2px; + li { + + .badge { + color: $gl-icon-color; + } + + } } + +} + +.page-with-layout-nav { + margin-top: 56px; } diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 18189e985c..32d948e4c5 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -254,6 +254,10 @@ } } } + + .layout-nav { + padding-right: $sidebar_collapsed_width; + } } .page-sidebar-expanded { @@ -280,6 +284,10 @@ } } } + + .layout-nav { + padding-right: $sidebar_width; + } } .right-sidebar-collapsed { diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 8c23a4ed1a..089de798bd 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -5,7 +5,7 @@ = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") .cover-block.groups-cover-block - .container-fluid + .container-fluid.container-limited = link_to group_icon(@group), target: '_blank' do = image_tag group_icon(@group), class: "avatar group-avatar s90" .group-info diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index ad8a2e1e6c..3c3bc41bf0 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -26,7 +26,7 @@ .layout-nav .container-fluid = render "layouts/nav/#{nav}" - .content-wrapper + .content-wrapper{ class: ('page-with-layout-nav' if defined?(nav) && nav) } = render "layouts/flash" = yield :flash_message %div{ class: (container_class unless @no_container) } diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 705682aeb2..0971bccfcd 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -44,16 +44,14 @@ %span Issues - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.count - (#{number_with_delimiter(issues.count)}) + %span.badge.count= number_with_delimiter(issues.count) = nav_link(path: 'groups#merge_requests') do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do = icon('tasks fw') %span Merge Requests - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.count - (#{number_with_delimiter(merge_requests.count)}) + %span.badge.count= number_with_delimiter(merge_requests.count) = nav_link(controller: [:group_members]) do = link_to group_group_members_path(@group), title: 'Members' do = icon('users fw') From 4ed9e5f5bebc5d3e11c21a929fdf57b8b014a31b Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 4 May 2016 13:11:38 -0500 Subject: [PATCH 087/138] Update font colors, font sizes, caret icon, avatar size --- app/assets/stylesheets/framework/avatar.scss | 1 + app/assets/stylesheets/framework/blocks.scss | 8 +++++ app/assets/stylesheets/framework/nav.scss | 33 +++++++++++++++++-- .../stylesheets/framework/variables.scss | 1 + app/views/groups/show.html.haml | 2 +- app/views/layouts/nav/_group.html.haml | 2 +- 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index 5aa425dab6..f5ce70b606 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -28,6 +28,7 @@ &.s46 { width: 46px; height: 46px; margin-right: 15px; } &.s48 { width: 48px; height: 48px; margin-right: 10px; } &.s60 { width: 60px; height: 60px; margin-right: 12px; } + &.s70 { width: 70px; height: 70px; margin-right: 14px; } &.s90 { width: 90px; height: 90px; margin-right: 15px; } &.s110 { width: 110px; height: 110px; margin-right: 15px; } &.s140 { width: 140px; height: 140px; margin-right: 20px; } diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index a78b1f30e6..e85eac585f 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -158,6 +158,10 @@ padding: 24px 0; .group-info { + .cover-title { + margin-top: 9px; + } + p { margin-bottom: 0; } @@ -173,8 +177,12 @@ } .group-info { + h1 { display: inline; + font-weight: normal; + font-size: 24px; + color: $gl-title-color; } } } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index c79de099d3..5c205ab724 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -203,7 +203,21 @@ .controls { float: right; - padding: 12px 5px 0 0; + padding: 7px 5px 0 0; + + i { + color: $layout-link-gray; + } + + .fa-rss, + .fa-cog { + font-size: 16px; + } + + .fa-caret-down { + margin-left: 5px; + color: $gl-icon-color; + } .dropdown { margin-left: 7px; @@ -234,15 +248,28 @@ li { + a { + padding-top: 10px; + } + + a, i { + color: $layout-link-gray; + } + + &.active { + a, i { + color: $black; + } + } + .badge { color: $gl-icon-color; } - } } } .page-with-layout-nav { - margin-top: 56px; + margin-top: 52px; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 7c695ba8f4..6c8cdcded1 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -75,6 +75,7 @@ $settings-icon-size: 18px; $provider-btn-group-border: #e5e5e5; $provider-btn-not-active-color: #4688f1; $link-underline-blue: #4a8bee; +$layout-link-gray: #7e7c7c; /* * Color schema diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 089de798bd..77c297255b 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -7,7 +7,7 @@ .cover-block.groups-cover-block .container-fluid.container-limited = link_to group_icon(@group), target: '_blank' do - = image_tag group_icon(@group), class: "avatar group-avatar s90" + = image_tag group_icon(@group), class: "avatar group-avatar s70" .group-info .cover-title %h1 diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 0971bccfcd..b4b528c614 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -6,7 +6,7 @@ %span.dropdown.group-settings-dropdown %a.dropdown-new.btn.btn-gray#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} = icon('cog') - = icon('angle-down') + = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right = nav_link(path: 'groups#projects') do = link_to projects_group_path(@group), title: 'Projects' do From 525e05b6536123a3117b5ff53fd46a96382d5f9d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Apr 2016 16:48:37 -0700 Subject: [PATCH 088/138] Expire repository exists? and has_visible_content? caches after a push if necessary Closes #17012 --- CHANGELOG | 1 + app/services/git_push_service.rb | 1 + app/services/git_tag_push_service.rb | 1 + 3 files changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 25d26535b7..925740e5d9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,7 @@ v 8.8.0 (unreleased) - Improve description for the Two-factor Authentication sign-in screen. (Connor Shea) - API support for the 'since' and 'until' operators on commit requests (Paco Guzman) - Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko) + - Expire repository exists? and has_visible_content? caches after a push if necessary v 8.7.3 - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index b7af80055b..66136b6261 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -17,6 +17,7 @@ class GitPushService < BaseService # 6. Checks if the project's main language has changed # def execute + @project.repository.after_create if @project.empty_repo? @project.repository.after_push_commit(branch_name, params[:newrev]) if push_remove_branch? diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index 64271d8bc5..7410442609 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -2,6 +2,7 @@ class GitTagPushService < BaseService attr_accessor :push_data def execute + project.repository.after_create if project.empty_repo? project.repository.before_push_tag @push_data = build_push_data From 6b4e255aa86864571b7d8dbc338986d4017b4215 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 4 May 2016 15:00:33 -0500 Subject: [PATCH 089/138] Remove break-all from links --- app/assets/stylesheets/pages/notes.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 9619d65db8..50ca755bcb 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -114,10 +114,6 @@ ul.notes { word-break: keep-all; } } - - a { - word-break: break-all; - } } .note-header { From ccd9c4be00a1e783b6d8c821aaec2b975e4a3176 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 4 May 2016 14:19:12 -0500 Subject: [PATCH 090/138] Reduce height of nav to 50px --- app/assets/stylesheets/framework/nav.scss | 4 ++-- app/assets/stylesheets/framework/sidebar.scss | 5 +++-- app/assets/stylesheets/framework/variables.scss | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 5c205ab724..bf09b6dadc 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -194,7 +194,7 @@ .layout-nav { position: fixed; - top: 58px; + top: $header-height; width: 100%; z-index: 1; background: $background-color; @@ -271,5 +271,5 @@ } .page-with-layout-nav { - margin-top: 52px; + margin-top: 50px; } diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 32d948e4c5..e940fd7286 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -3,6 +3,7 @@ position: absolute; width: 58px; cursor: pointer; + margin-top: 8px; } .page-with-sidebar { @@ -62,7 +63,7 @@ float: left; height: $header-height; width: 100%; - padding: 11px 0 11px 22px; + padding-left: 22px; overflow: hidden; outline: none; transition-duration: .3s; @@ -85,7 +86,7 @@ margin: 0; margin-left: 50px; font-size: 19px; - line-height: 41px; + line-height: 50px; font-weight: normal; } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 6c8cdcded1..42772ff5c3 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -65,7 +65,7 @@ $gl-padding-top: 10px; $row-hover: #f4f8fe; $progress-color: #c0392b; $avatar_radius: 50%; -$header-height: 58px; +$header-height: 50px; $fixed-layout-width: 1280px; $gl-avatar-size: 40px; $error-exclamation-point: #e62958; From 75523d1df91cc871847a00cad4e5e1d44278a647 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 3 May 2016 21:32:49 -0700 Subject: [PATCH 091/138] Sanitize repo paths in new project error message Closes #17243 --- CHANGELOG | 1 + app/helpers/projects_helper.rb | 6 ++++++ app/views/projects/imports/new.html.haml | 2 +- spec/helpers/projects_helper_spec.rb | 9 +++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 25d26535b7..e5dc5e5d4a 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.8.0 (unreleased) - Project#open_branches has been cleaned up and no longer loads entire records into memory. - Make build status canceled if any of the jobs was canceled and none failed + - Sanitize repo paths in new project error message - Remove future dates from contribution calendar graph. - Support e-mail notifications for comments on project snippets - Use ActionDispatch Remote IP for Akismet checking diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 3d5e61d2c1..3720b0085e 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -341,4 +341,10 @@ module ProjectsHelper ) end end + + def sanitize_repo_path(message) + return '' unless message.present? + + message.strip.gsub(Gitlab.config.gitlab_shell.repos_path.chomp('/'), "[REPOS PATH]") + end end diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index 6027fb2336..a8a8caf728 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -10,7 +10,7 @@ .panel-body %pre :preserve - #{@project.import_error.try(:strip)} + #{sanitize_repo_path(@project.import_error)} = 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 diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 62389188d2..29bcb8c589 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -131,4 +131,13 @@ describe ProjectsHelper do end end end + + describe '#sanitized_import_error' do + it 'removes the repo path' do + repo = File.join(Gitlab.config.gitlab_shell.repos_path, '/namespace/test.git') + import_error = "Could not clone #{repo}\n" + + expect(sanitize_repo_path(import_error)).to eq('Could not clone [REPOS PATH]/namespace/test.git') + end + end end From 7878eb9fa69123981281c2d71a2964dca1b459ee Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Mon, 25 Apr 2016 15:02:15 -0500 Subject: [PATCH 092/138] Start builds redesign --- app/assets/stylesheets/framework/blocks.scss | 9 +++++-- app/assets/stylesheets/framework/buttons.scss | 2 +- .../stylesheets/framework/tw_bootstrap.scss | 2 +- .../stylesheets/framework/typography.scss | 4 ++-- .../stylesheets/framework/variables.scss | 3 ++- app/assets/stylesheets/pages/commit.scss | 12 ++++++++-- app/assets/stylesheets/pages/detail_page.scss | 2 +- .../stylesheets/pages/merge_requests.scss | 20 ++++++++++++++-- app/assets/stylesheets/pages/projects.scss | 2 +- app/views/admin/builds/_build.html.haml | 20 ++++++++-------- app/views/admin/builds/index.html.haml | 3 ++- app/views/admin/logs/show.html.haml | 2 +- app/views/admin/users/index.html.haml | 2 +- app/views/dashboard/todos/index.html.haml | 2 +- app/views/events/_event_last_push.html.haml | 2 +- app/views/explore/groups/index.html.haml | 2 +- app/views/explore/snippets/index.html.haml | 2 +- app/views/groups/issues.html.haml | 2 +- app/views/groups/merge_requests.html.haml | 2 +- app/views/groups/milestones/index.html.haml | 2 +- app/views/help/ui.html.haml | 6 ++--- app/views/projects/_last_push.html.haml | 2 +- app/views/projects/_readme.html.haml | 2 +- app/views/projects/artifacts/browse.html.haml | 2 +- app/views/projects/branches/index.html.haml | 2 +- app/views/projects/builds/index.html.haml | 3 ++- app/views/projects/builds/show.html.haml | 4 ++-- app/views/projects/ci/builds/_build.html.haml | 3 ++- .../projects/commit/_ci_commit.html.haml | 6 +++-- .../projects/commit/_commit_box.html.haml | 24 +++++++++---------- app/views/projects/commits/show.html.haml | 2 +- app/views/projects/compare/index.html.haml | 2 +- app/views/projects/compare/show.html.haml | 2 +- app/views/projects/empty.html.haml | 2 +- app/views/projects/graphs/ci.html.haml | 2 +- app/views/projects/graphs/commits.html.haml | 2 +- app/views/projects/graphs/languages.html.haml | 2 +- app/views/projects/graphs/show.html.haml | 2 +- app/views/projects/network/_head.html.haml | 2 +- app/views/projects/releases/edit.html.haml | 2 +- app/views/projects/show.html.haml | 2 +- app/views/projects/snippets/index.html.haml | 2 +- app/views/projects/tags/index.html.haml | 2 +- app/views/projects/tags/show.html.haml | 2 +- app/views/projects/wikis/git_access.html.haml | 2 +- app/views/search/_results.html.haml | 2 +- app/views/shared/issuable/_filter.html.haml | 4 ++-- app/views/shared/issuable/_form.html.haml | 2 +- app/views/shared/milestones/_top.html.haml | 2 +- app/views/shared/snippets/_header.html.haml | 2 +- .../sherlock/file_samples/show.html.haml | 2 +- app/views/sherlock/queries/show.html.haml | 2 +- .../sherlock/transactions/index.html.haml | 2 +- .../sherlock/transactions/show.html.haml | 2 +- app/views/users/show.html.haml | 2 +- 55 files changed, 118 insertions(+), 85 deletions(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index e72e4aa47e..af6f22da28 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -18,7 +18,7 @@ line-height: 36px; } -.gray-content-block { +.row-content-block { margin-top: 0; margin-bottom: -$gl-padding; background-color: $background-color; @@ -81,6 +81,11 @@ margin-left: 10px; } } + + &.build-content { + background-color: $white-light; + border-top: none; + } } .cover-block { @@ -113,7 +118,7 @@ line-height: 1.1; h1 { - color: #313236; + color: $gl-gray-dark; margin-bottom: 6px; font-size: 23px; } diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 062da397b6..edb5ee71f9 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -59,7 +59,7 @@ } @mixin btn-gray { - @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, #313236); + @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, $gl-gray-dark); } @mixin btn-white { diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index 96bab7880c..6a45c34ccb 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -81,7 +81,7 @@ // Labels .label { - padding: 2px 4px; + padding: 4px 5px; font-size: 13px; font-style: normal; font-weight: normal; diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index b2535ddf4b..2779cd5678 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -42,14 +42,14 @@ margin: 24px 0 12px; padding: 0 0 10px; border-bottom: 1px solid #e7e9ed; - color: #313236; + color: $gl-gray-dark; } h2 { font-size: 1.2em; font-weight: 600; margin: 24px 0 12px; - color: #313236; + color: $gl-gray-dark; } h3 { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 92ecfe5048..4227839eed 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -20,7 +20,7 @@ $background-color: #fafafa; */ $gl-font-size: 15px; $gl-title-color: #333; -$gl-text-color: #555; +$gl-text-color: #5c5c5c; $gl-text-green: #4a2; $gl-text-red: #d12f19; $gl-text-orange: #d90; @@ -30,6 +30,7 @@ $gl-placeholder-color: #8f8f8f; $gl-icon-color: $gl-placeholder-color; $gl-grayish-blue: #7f8fa4; $gl-gray: $gl-text-color; +$gl-gray-dark: #313236; $gl-header-color: $gl-title-color; /* diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index 358d2f4ab9..5c945e2d4c 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -31,9 +31,17 @@ } .commit-committer-link, .commit-author-link { - color: #444; + color: $gl-gray; font-weight: bold; } + + .ci-status { + margin-right: 10px; + } + + .fa-clipboard { + color: $dropdown-title-btn-color; + } } .commit-box { @@ -42,7 +50,7 @@ .commit-title { margin: 0; font-size: 23px; - color: #313236; + color: $gl-gray-dark; } .commit-description { diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss index 3438dbe495..5e61e61d85 100644 --- a/app/assets/stylesheets/pages/detail_page.scss +++ b/app/assets/stylesheets/pages/detail_page.scss @@ -22,7 +22,7 @@ .title { margin: 0; font-size: 23px; - color: #313236; + color: $gl-gray-dark; } .description { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 4ef548ffbe..c4005ba1e6 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -104,7 +104,7 @@ font-weight: 600; font-size: 17px; margin: 5px 0; - color: #313236; + color: $gl-gray-dark; } p:last-child { @@ -136,7 +136,7 @@ } .label-branch { - color: #313236; + color: $gl-gray-dark; font-family: $monospace_font; font-weight: bold; overflow: hidden; @@ -272,3 +272,19 @@ display: inline-block; width: 250px; } + +.table-holder { + .builds { + + th { + background-color: $white-light; + color: $gl-placeholder-color; + } + + th, + td { + padding: 16px; + } + } +} + diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 99108e9bfc..c20f04653f 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -178,7 +178,7 @@ .option-title { font-weight: normal; display: inline-block; - color: #313236; + color: $gl-gray-dark; } .option-descr { diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml index 3571eefd57..84b78a6f36 100644 --- a/app/views/admin/builds/_build.html.haml +++ b/app/views/admin/builds/_build.html.haml @@ -35,15 +35,15 @@ %td #{build.stage} / #{build.name} - .pull-right - - if build.tags.any? - - build.tags.each do |tag| - %span.label.label-primary - = tag - - if build.try(:trigger_request) - %span.label.label-info triggered - - if build.try(:allow_failure) - %span.label.label-danger allowed to fail + %td + - if build.tags.any? + - build.tags.each do |tag| + %span.label.label-primary + = tag + - if build.try(:trigger_request) + %span.label.label-info triggered + - if build.try(:allow_failure) + %span.label.label-danger allowed to fail %td.duration - if build.duration @@ -69,4 +69,4 @@ %i.fa.fa-remove.cred - elsif defined?(allow_retry) && allow_retry && build.retryable? = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry' do - %i.fa.fa-repeat + %i.fa.fa-refresh diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml index 5931efdefe..804d7851bd 100644 --- a/app/views/admin/builds/index.html.haml +++ b/app/views/admin/builds/index.html.haml @@ -19,7 +19,7 @@ - if @all_builds.running_or_pending.any? = link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post -.gray-content-block.second-block +.row-content-block.second-block #{(@scope || 'running').capitalize} builds %ul.content-list @@ -38,6 +38,7 @@ %th Ref %th Runner %th Name + %th Tags %th Duration %th Finished at %th diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index 4b475a4d8f..698feb571a 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -7,7 +7,7 @@ %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') } = link_to klass::file_name, "##{klass::file_name_noext}", 'data-toggle' => 'tab' -.gray-content-block +.row-content-block To prevent performance issues admin logs output the last 2000 lines .tab-content - loggers.each do |klass| diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 0ee8dc962b..d6743081c8 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -32,7 +32,7 @@ Without projects %small.badge= number_with_delimiter(User.without_projects.count) - .gray-content-block.second-block + .row-content-block.second-block .pull-right .dropdown.inline %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 49ab8aad1d..e27fefc0af 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -25,7 +25,7 @@ = icon('spinner spin') .todos-filters - .gray-content-block.second-block + .row-content-block.second-block = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form' do .filter-item.inline = select_tag('project_id', todo_projects_options, diff --git a/app/views/events/_event_last_push.html.haml b/app/views/events/_event_last_push.html.haml index 5753158c24..a1a282178e 100644 --- a/app/views/events/_event_last_push.html.haml +++ b/app/views/events/_event_last_push.html.haml @@ -1,5 +1,5 @@ - if show_last_push_widget?(event) - .gray-content-block.clear-block.last-push-widget + .row-content-block.clear-block.last-push-widget .event-last-push .event-last-push-text %span You pushed to diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index 8ffca96bb4..57f6e7e061 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -6,7 +6,7 @@ - else = render 'explore/head' -.gray-content-block.clearfix +.row-content-block.clearfix .pull-left = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f| = hidden_field_tag :sort, @sort diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml index 0f100c39ff..9b838b9f3b 100644 --- a/app/views/explore/snippets/index.html.haml +++ b/app/views/explore/snippets/index.html.haml @@ -6,7 +6,7 @@ - else = render 'explore/head' -.gray-content-block +.row-content-block - if current_user .pull-right = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index aea35c5086..df6db8c23d 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -16,7 +16,7 @@ = render 'shared/issuable/filter', type: :issues -.gray-content-block.second-block +.row-content-block.second-block Only issues from %strong #{@group.name} group are listed here. diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index e1c9dd931e..0cc2305b7d 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -8,7 +8,7 @@ = render 'shared/issuable/filter', type: :merge_requests -.gray-content-block.second-block +.row-content-block.second-block Only merge requests from %strong #{@group.name} group are listed here. diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index ab307708b7..ddb8be0d49 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -10,7 +10,7 @@ = icon('plus') New Milestone -.gray-content-block +.row-content-block Only milestones from %strong #{@group.name} group are listed here. diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index f12df5c8ff..d676bc28c8 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -48,14 +48,14 @@ .lead Gray content block with side padding using - %code .gray-content-block + %code .row-content-block .example - .gray-content-block + .row-content-block %h4 Normal block inside content = lorem - .gray-content-block.second-block + .row-content-block.second-block %h4 Second block = lorem diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml index f0a3e416db..7c2b8d0150 100644 --- a/app/views/projects/_last_push.html.haml +++ b/app/views/projects/_last_push.html.haml @@ -1,7 +1,7 @@ - if event = last_push_event - if show_last_push_widget?(event) - .gray-content-block.top-block.clear-block.hidden-xs + .row-content-block.top-block.clear-block.hidden-xs .event-last-push .event-last-push-text %span You pushed to diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml index a9908eaecc..369a847e7d 100644 --- a/app/views/projects/_readme.html.haml +++ b/app/views/projects/_readme.html.haml @@ -7,7 +7,7 @@ = cache(readme_cache_key) do = render_readme(readme) - else - .gray-content-block.second-block.center + .row-content-block.second-block.center %h3.page-title This project does not have a README yet - if can?(current_user, :push_code, @project) diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml index 84034c8bf1..49f95ff37d 100644 --- a/app/views/projects/artifacts/browse.html.haml +++ b/app/views/projects/artifacts/browse.html.haml @@ -1,7 +1,7 @@ - page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds' = render 'projects/builds/header_title' -.top-block.gray-content-block.clearfix +.top-block.row-content-block.clearfix .pull-right = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-default download' do diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 88266e2123..ac7790421a 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -1,7 +1,7 @@ - page_title "Branches" = render "projects/commits/header_title" = render "projects/commits/head" -.gray-content-block +.row-content-block .pull-right - if can? current_user, :push_code, @project = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index 0406fc21d7..d842dd54e6 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -34,7 +34,7 @@ = icon('wrench') %span CI Lint -.gray-content-block +.row-content-block #{(@scope || 'running').capitalize} builds from this project %ul.content-list @@ -52,6 +52,7 @@ %th Ref %th Stage %th Name + %th Tags %th Duration %th Finished at - if @project.build_coverage_enabled? diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 99d72aa793..c0f7a7686f 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -2,7 +2,7 @@ = render "header_title" .build-page - .gray-content-block.top-block + .row-content-block.top-block Build ##{@build.id} for commit %strong.monospace= link_to @build.commit.short_sha, ci_status_path(@build.commit) from @@ -34,7 +34,7 @@ %i.fa.fa-warning This build was retried. - .gray-content-block.middle-block + .row-content-block.middle-block .build-head .clearfix = ci_status_with_icon(@build.status) diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index e123eb1cc7..e5a5efb315 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -40,6 +40,7 @@ %td = build.name + %td .label-container - if build.tags.any? - build.tags.each do |tag| @@ -76,4 +77,4 @@ %i.fa.fa-remove.cred - elsif defined?(allow_retry) && allow_retry && build.retryable? = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry' do - %i.fa.fa-repeat + %i.fa.fa-refresh diff --git a/app/views/projects/commit/_ci_commit.html.haml b/app/views/projects/commit/_ci_commit.html.haml index d3acd33116..e849aefb18 100644 --- a/app/views/projects/commit/_ci_commit.html.haml +++ b/app/views/projects/commit/_ci_commit.html.haml @@ -1,4 +1,4 @@ -.gray-content-block.middle-block +.row-content-block.build-content.middle-block .pull-right - if can?(current_user, :update_build, @project) - if ci_commit.builds.latest.failed.any?(&:retryable?) @@ -40,6 +40,7 @@ %th Build ID %th Stage %th Name + %th Tags %th Duration %th Finished at - if @project.build_coverage_enabled? @@ -49,7 +50,7 @@ = render builds, coverage: @project.build_coverage_enabled?, stage: true, ref: false, allow_retry: true - if ci_commit.retried.any? - .gray-content-block.second-block + .row-content-block.second-block Retried builds .table-holder @@ -61,6 +62,7 @@ %th Ref %th Stage %th Name + %th Tags %th Duration %th Finished at - if @project.build_coverage_enabled? diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 3d7c18a5f5..b73cbf3f1d 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -22,10 +22,12 @@ %div %p - %span.light Commit - = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" - = clipboard_button(clipboard_text: @commit.id) .commit-info-row + - if @commit.status + = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status ci-#{@commit.status}" do + = ci_icon_for_status(@commit.status) + build: + = ci_label_for_status(@commit.status) %span.light Authored by %strong = commit_author_link(@commit, avatar: true, size: 24) @@ -39,19 +41,15 @@ #{time_ago_with_tooltip(@commit.committed_date)} .commit-info-row + %span.light Commit + = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" + = clipboard_button(clipboard_text: @commit.id) %span.cgray= pluralize(@commit.parents.count, "parent") - @commit.parents.each do |parent| = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace" -- if @commit.status - .pull-right - = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status ci-#{@commit.status}" do - = ci_icon_for_status(@commit.status) - build: - = ci_label_for_status(@commit.status) - -.commit-info-row.branches - %i.fa.fa-spinner.fa-spin + %span.commit-info.branches + %i.fa.fa-spinner.fa-spin .commit-box.content-block %h3.commit-title @@ -61,4 +59,4 @@ = preserve(markdown(escape_once(@commit.description), pipeline: :single_line)) :javascript - $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}"); + $(".commit-info.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}"); diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index bcdb09208a..088eaa2801 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -6,7 +6,7 @@ = render "head" -.gray-content-block.second-block +.row-content-block.second-block .tree-ref-holder = render 'shared/ref_switcher', destination: 'commits' diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index 02be5a2d07..5e188dd0f3 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -2,7 +2,7 @@ = render "projects/commits/header_title" = render "projects/commits/head" -.gray-content-block +.row-content-block Compare branches, tags or commit ranges. %br Fill input field with commit id like diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index da731f28bb..6252516823 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -3,7 +3,7 @@ = render "projects/commits/head" -.gray-content-block +.row-content-block = render "form" - if @commits.present? diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 52d093871b..1a2e59752f 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -7,7 +7,7 @@ = render "home_panel" -.gray-content-block.second-block.center +.row-content-block.second-block.center %h3.page-title The repository for this project is empty - if can?(current_user, :push_code, @project) diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml index 6fa77cc10c..9f05be9982 100644 --- a/app/views/projects/graphs/ci.html.haml +++ b/app/views/projects/graphs/ci.html.haml @@ -1,7 +1,7 @@ - page_title "Continuous Integration", "Graphs" = render "header_title" = render 'head' -.gray-content-block.append-bottom-default +.row-content-block.append-bottom-default .oneline A collection of graphs for Continuous Integration diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index fc465ab273..da9f648cc9 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -2,7 +2,7 @@ = render "header_title" = render 'head' -.gray-content-block.append-bottom-default +.row-content-block.append-bottom-default .tree-ref-holder = render 'shared/ref_switcher', destination: 'graphs_commits' %ul.breadcrumb.repo-breadcrumb diff --git a/app/views/projects/graphs/languages.html.haml b/app/views/projects/graphs/languages.html.haml index a7fab5b6d7..ebecab1dbf 100644 --- a/app/views/projects/graphs/languages.html.haml +++ b/app/views/projects/graphs/languages.html.haml @@ -2,7 +2,7 @@ = render "header_title" = render 'head' -.gray-content-block.append-bottom-default +.row-content-block.append-bottom-default .oneline Programming languages used in this repository diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index 882e7d6b6e..ad4a932d39 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -2,7 +2,7 @@ = render "header_title" = render 'head' -.gray-content-block.append-bottom-default +.row-content-block.append-bottom-default .tree-ref-holder = render 'shared/ref_switcher', destination: 'graphs' %ul.breadcrumb.repo-breadcrumb diff --git a/app/views/projects/network/_head.html.haml b/app/views/projects/network/_head.html.haml index 28a617538b..c609c505de 100644 --- a/app/views/projects/network/_head.html.haml +++ b/app/views/projects/network/_head.html.haml @@ -1,4 +1,4 @@ -.gray-content-block.append-bottom-default +.row-content-block.append-bottom-default .tree-ref-holder = render partial: 'shared/ref_switcher', locals: {destination: 'graph'} diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index 6f0b32aa16..0d59cec322 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -2,7 +2,7 @@ = render "projects/commits/header_title" = render "projects/commits/head" -.gray-content-block +.row-content-block .oneline .title Release notes for tag diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index d854ac2172..74feb9e328 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -12,7 +12,7 @@ = render 'projects/last_push' = render "home_panel" -.project-stats.gray-content-block.second-block +.project-stats.row-content-block.second-block %ul.nav %li = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index 4af963e14d..103ff44746 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -1,7 +1,7 @@ - page_title "Snippets" = render "header_title" -.gray-content-block.top-block +.row-content-block.top-block .pull-right = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do = icon('plus') diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 760347de0a..dc6ece30dd 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -2,7 +2,7 @@ = render "projects/commits/header_title" = render "projects/commits/head" -.gray-content-block +.row-content-block - 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 diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 1dc9b799a9..9c916fd02d 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -2,7 +2,7 @@ = render "projects/commits/header_title" = render "projects/commits/head" -.gray-content-block +.row-content-block .pull-right - if can?(current_user, :push_code, @project) = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has-tooltip', title: 'Edit release notes' do diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index dd27ea2b11..ba3f2cadc4 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -2,7 +2,7 @@ = render "header_title" = render 'nav' -.gray-content-block +.row-content-block %span.oneline Git access for %strong= @project_wiki.path_with_namespace diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml index 711337f308..252c37532e 100644 --- a/app/views/search/_results.html.haml +++ b/app/views/search/_results.html.haml @@ -1,7 +1,7 @@ - if @search_objects.empty? = render partial: "search/results/empty" - else - .gray-content-block + .row-content-block = search_entries_info(@search_objects, @scope, @search_term) - unless @show_snippets - if @project diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index fc1101646f..9474462cbd 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -1,5 +1,5 @@ .issues-filters - .issues-details-filters.gray-content-block.second-block + .issues-details-filters.row-content-block.second-block = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name]), method: :get, class: 'filter-form' do - if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project) .check-all-holder @@ -48,7 +48,7 @@ = button_tag "Update issues", class: "btn update_selected_issues btn-save" - if !@labels.nil? - .gray-content-block.second-block.filtered-labels{ class: ("hidden" if !@labels.any?) } + .row-content-block.second-block.filtered-labels{ class: ("hidden" if !@labels.any?) } - if @labels.any? = render "shared/labels_row", labels: @labels diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 18b091df39..5c52cc6d1d 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -116,7 +116,7 @@ = link_to 'Change branches', mr_change_branches_path(@merge_request) - is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?) -.gray-content-block{class: (is_footer ? "footer-block" : "middle-block")} +.row-content-block{class: (is_footer ? "footer-block" : "middle-block")} - if issuable.new_record? = f.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' - else diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 7ff947a51d..8bca971bcf 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -24,7 +24,7 @@ - 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" -.detail-page-description.milestone-detail +.detail-page-description.milestone-detailrow-content-block.second-block %h2.title = markdown escape_once(milestone.title), pipeline: :single_line diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 3c445f6723..e65b181487 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -20,6 +20,6 @@ - else = render "snippets/actions" -.detail-page-description.gray-content-block.second-block +.detail-page-description.row-content-block.second-block %h2.title = markdown escape_once(@snippet.title), pipeline: :single_line diff --git a/app/views/sherlock/file_samples/show.html.haml b/app/views/sherlock/file_samples/show.html.haml index cfd11e45b6..94d4dd4fa7 100644 --- a/app/views/sherlock/file_samples/show.html.haml +++ b/app/views/sherlock/file_samples/show.html.haml @@ -3,7 +3,7 @@ - header_title t('sherlock.title'), sherlock_transactions_path -.gray-content-block +.row-content-block .pull-right = link_to(sherlock_transaction_path(@transaction), class: 'btn') do %i.fa.fa-arrow-left diff --git a/app/views/sherlock/queries/show.html.haml b/app/views/sherlock/queries/show.html.haml index 83f61ce4b0..fc2863dca8 100644 --- a/app/views/sherlock/queries/show.html.haml +++ b/app/views/sherlock/queries/show.html.haml @@ -9,7 +9,7 @@ %a(href="#tab-backtrace" data-toggle="tab") = t('sherlock.backtrace') -.gray-content-block +.row-content-block .pull-right = link_to(sherlock_transaction_path(@transaction), class: 'btn') do %i.fa.fa-arrow-left diff --git a/app/views/sherlock/transactions/index.html.haml b/app/views/sherlock/transactions/index.html.haml index 010e1a2a90..da969c0276 100644 --- a/app/views/sherlock/transactions/index.html.haml +++ b/app/views/sherlock/transactions/index.html.haml @@ -1,7 +1,7 @@ - page_title t('sherlock.title') - header_title t('sherlock.title'), sherlock_transactions_path -.gray-content-block +.row-content-block .pull-right = link_to(destroy_all_sherlock_transactions_path, class: 'btn btn-danger', diff --git a/app/views/sherlock/transactions/show.html.haml b/app/views/sherlock/transactions/show.html.haml index 9d4b0b2724..8aa6b437d9 100644 --- a/app/views/sherlock/transactions/show.html.haml +++ b/app/views/sherlock/transactions/show.html.haml @@ -16,7 +16,7 @@ %span.badge #{@transaction.file_samples.length} -.gray-content-block +.row-content-block .pull-right = link_to(sherlock_transactions_path, class: 'btn') do %i.fa.fa-arrow-left diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 03511b0654..3c0b89c674 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -85,7 +85,7 @@ %div{ class: container_class } .tab-content #activity.tab-pane - .gray-content-block.calender-block.white.second-block.hidden-xs + .row-content-block.calender-block.white.second-block.hidden-xs %div{ class: container_class } .user-calendar{data: {href: user_calendar_path}} %h4.center.light From fc20639c8d76b367dc0e0b66ef94033697b28dc7 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Tue, 26 Apr 2016 13:20:58 -0500 Subject: [PATCH 093/138] Builds page redesign --- app/assets/stylesheets/framework/buttons.scss | 6 ++++++ app/assets/stylesheets/pages/builds.scss | 13 +++++++++++++ app/assets/stylesheets/pages/commit.scss | 18 ++++++++++++++++-- app/assets/stylesheets/pages/status.scss | 2 +- app/helpers/application_helper.rb | 3 +-- app/views/admin/builds/_build.html.haml | 2 +- app/views/dashboard/snippets/index.html.haml | 16 ++++++++-------- app/views/dashboard/todos/index.html.haml | 8 ++++---- app/views/projects/builds/index.html.haml | 13 +++++++------ app/views/projects/ci/builds/_build.html.haml | 2 +- app/views/projects/commit/_ci_menu.html.haml | 4 ++-- .../projects/commit/_commit_box.html.haml | 2 +- app/views/projects/commit/branches.html.haml | 1 - app/views/projects/commits/_head.html.haml | 9 ++++++--- .../projects/merge_requests/_show.html.haml | 12 ++++++++---- 15 files changed, 75 insertions(+), 36 deletions(-) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index edb5ee71f9..1f2f9f7754 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -251,3 +251,9 @@ .btn-file-option { background: linear-gradient(180deg, $white-light 25%, $gray-light 100%); } + +.btn-refresh { + i { + color: $gl-icon-color; + } +} diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 201f3e5ca4..3340cee964 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -83,3 +83,16 @@ } } } + +table.builds { + + .build-link { + a { + color: $gl-dark-link-color; + } + } + + .btn-refresh { + margin-left: 10px; + } +} diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index 5c945e2d4c..c2cd227571 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -35,13 +35,19 @@ font-weight: bold; } - .ci-status { - margin-right: 10px; + .time_ago { + margin-left: 8px; } .fa-clipboard { color: $dropdown-title-btn-color; } + + .commit-info { + &.branches { + margin-left: 8px; + } + } } .commit-box { @@ -91,6 +97,14 @@ } } +.commit-action-buttons { + i { + color: $gl-icon-color; + font-size: 13px; + margin-right: 3px; + } +} + /* * Commit message textarea for web editor and * custom merge request message diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index dbb6daf0d7..2370d35924 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -1,7 +1,7 @@ .container-fluid { .ci-status { padding: 2px 7px; - margin-right: 5px; + margin-right: 10px; border: 1px solid #eee; white-space: nowrap; @include border-radius(4px); diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3e0074da39..313a8fd56c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -316,8 +316,7 @@ module ApplicationHelper html = content_tag :span, entity_title if count.present? - html += " " - html += content_tag :span, number_with_delimiter(count), class: 'badge' + html += content_tag :span, " (#{number_with_delimiter(count)})" end html.html_safe diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml index 84b78a6f36..2b99f360d6 100644 --- a/app/views/admin/builds/_build.html.haml +++ b/app/views/admin/builds/_build.html.haml @@ -68,5 +68,5 @@ = link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel' do %i.fa.fa-remove.cred - elsif defined?(allow_retry) && allow_retry && build.retryable? - = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry' do + = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-refresh' do %i.fa.fa-refresh diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index d4e7862981..53f1094ec0 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -13,26 +13,26 @@ %li{ class: ("active" unless params[:scope]) } = link_to dashboard_snippets_path do All - %span.badge - = current_user.snippets.count + %span + (#{current_user.snippets.count}) %li{ class: ("active" if params[:scope] == "are_private") } = link_to dashboard_snippets_path(scope: 'are_private') do Private - %span.badge - = current_user.snippets.are_private.count + %span + (#{current_user.snippets.are_private.count}) %li{ class: ("active" if params[:scope] == "are_internal") } = link_to dashboard_snippets_path(scope: 'are_internal') do Internal - %span.badge - = current_user.snippets.are_internal.count + %span + (#{current_user.snippets.are_internal.count}) %li{ class: ("active" if params[:scope] == "are_public") } = link_to dashboard_snippets_path(scope: 'are_public') do Public - %span.badge - = current_user.snippets.are_public.count + %span + (#{current_user.snippets.are_public.count}) = render 'snippets/snippets' diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index e27fefc0af..a87b796c6f 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -8,15 +8,15 @@ = link_to todos_filter_path(state: 'pending') do %span To do - %span{class: 'badge'} - = todos_pending_count + %span + (#{todos_pending_count}) - todo_done_active = ('active' if params[:state] == 'done') %li{class: "todos-done #{todo_done_active}"} = link_to todos_filter_path(state: 'done') do %span Done - %span{class: 'badge'} - = todos_done_count + %span + (#{todos_done_count}) .nav-controls - if @todos.any?(&:pending?) diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index d842dd54e6..fcb1e656d7 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -6,20 +6,21 @@ %li{class: ('active' if @scope.nil?)} = link_to project_builds_path(@project) do All - %span.badge.js-totalbuilds-count - = number_with_delimiter(@all_builds.count(:id)) + %span.js-totalbuilds-count + (#{number_with_delimiter(@all_builds.count(:id))}) + %li{class: ('active' if @scope == 'running')} = link_to project_builds_path(@project, scope: :running) do Running - %span.badge.js-running-count - = number_with_delimiter(@all_builds.running_or_pending.count(:id)) + %span.js-running-count + (#{number_with_delimiter(@all_builds.running_or_pending.count(:id))}) %li{class: ('active' if @scope == 'finished')} = link_to project_builds_path(@project, scope: :finished) do Finished - %span.badge.js-running-count - = number_with_delimiter(@all_builds.finished.count(:id)) + %span.js-running-count + (#{number_with_delimiter(@all_builds.finished.count(:id))}) .nav-controls - if can?(current_user, :update_build, @project) diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index e5a5efb315..1b382e4836 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -76,5 +76,5 @@ = link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel' do %i.fa.fa-remove.cred - elsif defined?(allow_retry) && allow_retry && build.retryable? - = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry' do + = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-refresh' do %i.fa.fa-refresh diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml index ea33aa472a..b2d3db0bdc 100644 --- a/app/views/projects/commit/_ci_menu.html.haml +++ b/app/views/projects/commit/_ci_menu.html.haml @@ -2,8 +2,8 @@ = nav_link(path: 'commit#show') do = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do Changes - %span.badge= @diffs.count + %span (#{@diffs.count}) = nav_link(path: 'commit#builds') do = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do Builds - %span.badge= @statuses.count + %span (#{@statuses.count}) diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index b73cbf3f1d..01163e526b 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,4 +1,4 @@ -.pull-right +.pull-right.commit-action-buttons %div - if @notes_count > 0 %span.btn.disabled.btn-grouped diff --git a/app/views/projects/commit/branches.html.haml b/app/views/projects/commit/branches.html.haml index 82aac1fbd1..2b0c9a4b4d 100644 --- a/app/views/projects/commit/branches.html.haml +++ b/app/views/projects/commit/branches.html.haml @@ -3,7 +3,6 @@ - branch = commit_default_branch(@project, @branches) = link_to(namespace_project_tree_path(@project.namespace, @project, branch)) do %span.label.label-gray - %i.fa.fa-code-fork = branch - if @branches.any? || @tags.any? = link_to("#", class: "js-details-expand") do diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index 7a5b0d993d..2c8fecae76 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -2,7 +2,8 @@ = nav_link(controller: [:commit, :commits]) do = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do Commits - %span.badge= number_with_delimiter(@repository.commit_count) + %span + (#{number_with_delimiter(@repository.commit_count)}) = nav_link(controller: %w(network)) do = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do @@ -15,9 +16,11 @@ = nav_link(html_options: {class: branches_tab_class}) do = link_to namespace_project_branches_path(@project.namespace, @project) do Branches - %span.badge.js-totalbranch-count= @repository.branch_count + %span.js-totalbranch-count + (#{@repository.branch_count}) = nav_link(controller: [:tags, :releases]) do = link_to namespace_project_tags_path(@project.namespace, @project) do Tags - %span.badge.js-totaltags-count= @repository.tag_count + %span.js-totaltags-count + (#{@repository.tag_count}) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 290753d57c..0c97a29847 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -50,20 +50,24 @@ %li.notes-tab = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do Discussion - %span.badge= @merge_request.mr_and_commit_notes.user.nonawards.count + %span + (#{@merge_request.mr_and_commit_notes.user.nonawards.count}) %li.commits-tab = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do Commits - %span.badge= @commits.size + %span + (#{@commits.size}) - if @ci_commit %li.builds-tab = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do Builds - %span.badge= @statuses.size + %span + (#{@statuses.size}) %li.diffs-tab = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do Changes - %span.badge= @merge_request.diff_size + %span + (#{@merge_request.diff_size}) .tab-content #notes.notes.tab-pane.voting_notes From 3a3f0bf436007b3eecc0b85d0fc696951245ab6a Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 27 Apr 2016 09:46:31 -0500 Subject: [PATCH 094/138] Change build icons into buttons; update tests --- app/assets/stylesheets/framework/buttons.scss | 3 ++- app/assets/stylesheets/pages/builds.scss | 4 ---- app/views/admin/builds/_build.html.haml | 6 +++--- app/views/projects/ci/builds/_build.html.haml | 6 +++--- features/project/merge_requests.feature | 8 ++++---- features/steps/dashboard/todos.rb | 8 ++++---- features/steps/project/merge_requests.rb | 10 +++++----- spec/features/notes_on_merge_requests_spec.rb | 2 +- 8 files changed, 22 insertions(+), 25 deletions(-) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 1f2f9f7754..eaf85bb17c 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -252,7 +252,8 @@ background: linear-gradient(180deg, $white-light 25%, $gray-light 100%); } -.btn-refresh { +.btn-build { + margin-left: 10px; i { color: $gl-icon-color; } diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 3340cee964..aa41565f81 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -91,8 +91,4 @@ table.builds { color: $gl-dark-link-color; } } - - .btn-refresh { - margin-left: 10px; - } } diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml index 2b99f360d6..967151bc33 100644 --- a/app/views/admin/builds/_build.html.haml +++ b/app/views/admin/builds/_build.html.haml @@ -61,12 +61,12 @@ %td .pull-right - if can?(current_user, :read_build, project) && build.artifacts? - = link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), title: 'Download artifacts' do + = link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), title: 'Download artifacts', class: 'btn btn-build' do %i.fa.fa-download - if can?(current_user, :update_build, build.project) - if build.active? - = link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel' do + = link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do %i.fa.fa-remove.cred - elsif defined?(allow_retry) && allow_retry && build.retryable? - = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-refresh' do + = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do %i.fa.fa-refresh diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 1b382e4836..8e95f04027 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -69,12 +69,12 @@ %td .pull-right - if can?(current_user, :read_build, build) && build.artifacts? - = link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), title: 'Download artifacts' do + = link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), title: 'Download artifacts', class: 'btn btn-build' do %i.fa.fa-download - if can?(current_user, :update_build, build) - if build.active? - = link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel' do + = link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do %i.fa.fa-remove.cred - elsif defined?(allow_retry) && allow_retry && build.retryable? - = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-refresh' do + = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do %i.fa.fa-refresh diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index ecda4ea824..aec590f7d9 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -135,7 +135,7 @@ Feature: Project Merge Requests And I leave a comment like "Line is wrong" on diff And I switch to the merge request's comments tab Then I should see a discussion has started on diff - And I should see a badge of "1" next to the discussion link + And I should see a badge of "(1)" next to the discussion link @javascript Scenario: I see a new comment on merge request diff from another user in the discussion tab @@ -143,7 +143,7 @@ Feature: Project Merge Requests And I visit merge request page "Bug NS-05" And user "John Doe" leaves a comment like "Line is wrong" on diff Then I should see a discussion by user "John Doe" has started on diff - And I should see a badge of "1" next to the discussion link + And I should see a badge of "(1)" next to the discussion link @javascript Scenario: I edit a comment on a merge request diff @@ -161,11 +161,11 @@ Feature: Project Merge Requests And I visit merge request page "Bug NS-05" And I click on the Changes tab And I leave a comment like "Line is wrong" on diff - And I should see a badge of "1" next to the discussion link + And I should see a badge of "(1)" next to the discussion link And I delete the comment "Line is wrong" on diff And I click on the Discussion tab Then I should not see any discussion - And I should see a badge of "0" next to the discussion link + And I should see a badge of "(0)" next to the discussion link @javascript Scenario: I comment on a line of a commit in merge request diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb index 2b23df6764..da380250cd 100644 --- a/features/steps/dashboard/todos.rb +++ b/features/steps/dashboard/todos.rb @@ -27,8 +27,8 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps step 'I should see todos assigned to me' do page.within('.nav-sidebar') { expect(page).to have_content 'Todos 4' } - expect(page).to have_content 'To do 4' - expect(page).to have_content 'Done 0' + expect(page).to have_content 'To do (4)' + expect(page).to have_content 'Done (0)' expect(page).to have_link project.name_with_namespace should_see_todo(1, "John Doe assigned you merge request #{merge_request.to_reference}", merge_request.title) @@ -43,8 +43,8 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps end page.within('.nav-sidebar') { expect(page).to have_content 'Todos 3' } - expect(page).to have_content 'To do 3' - expect(page).to have_content 'Done 1' + expect(page).to have_content 'To do (3)' + expect(page).to have_content 'Done (1)' should_not_see_todo "John Doe assigned you merge request #{merge_request.to_reference}" end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 3b1a00f628..463235742f 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -344,12 +344,12 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end end - step 'I should see a badge of "1" next to the discussion link' do - expect_discussion_badge_to_have_counter("1") + step 'I should see a badge of "(1)" next to the discussion link' do + expect_discussion_badge_to_have_counter("(1)") end - step 'I should see a badge of "0" next to the discussion link' do - expect_discussion_badge_to_have_counter("0") + step 'I should see a badge of "(0)" next to the discussion link' do + expect_discussion_badge_to_have_counter("(0)") end step 'I should see a discussion has started on commit diff' do @@ -572,7 +572,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end def expect_discussion_badge_to_have_counter(value) - page.within(".notes-tab .badge") do + page.within(".notes-tab span") do page.should have_content value end end diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index 389812ff7e..0aef4cba23 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -151,7 +151,7 @@ describe 'Comments', feature: true do visit namespace_project_merge_request_path(project.namespace, project, merge_request) expect(merge_request.mr_and_commit_notes.count).to eq 2 - expect(find('.notes-tab span.badge').text).to eq "1" + expect(find('.notes-tab span').text).to eq "(1)" end end end From 8d19955c05f18675fc16dee6ca0f4da24013e816 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 4 May 2016 16:25:10 -0500 Subject: [PATCH 095/138] Revert counter parentheses back to badges --- app/helpers/application_helper.rb | 3 ++- app/views/dashboard/snippets/index.html.haml | 16 ++++++++-------- app/views/dashboard/todos/index.html.haml | 8 ++++---- app/views/projects/builds/index.html.haml | 12 ++++++------ app/views/projects/commit/_ci_menu.html.haml | 4 ++-- app/views/projects/commits/_head.html.haml | 10 ++++------ .../projects/merge_requests/_show.html.haml | 12 ++++-------- app/views/shared/milestones/_top.html.haml | 2 +- features/project/merge_requests.feature | 8 ++++---- features/steps/dashboard/todos.rb | 8 ++++---- features/steps/project/merge_requests.rb | 10 +++++----- spec/features/notes_on_merge_requests_spec.rb | 2 +- 12 files changed, 45 insertions(+), 50 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 313a8fd56c..3e0074da39 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -316,7 +316,8 @@ module ApplicationHelper html = content_tag :span, entity_title if count.present? - html += content_tag :span, " (#{number_with_delimiter(count)})" + html += " " + html += content_tag :span, number_with_delimiter(count), class: 'badge' end html.html_safe diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index 53f1094ec0..d4e7862981 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -13,26 +13,26 @@ %li{ class: ("active" unless params[:scope]) } = link_to dashboard_snippets_path do All - %span - (#{current_user.snippets.count}) + %span.badge + = current_user.snippets.count %li{ class: ("active" if params[:scope] == "are_private") } = link_to dashboard_snippets_path(scope: 'are_private') do Private - %span - (#{current_user.snippets.are_private.count}) + %span.badge + = current_user.snippets.are_private.count %li{ class: ("active" if params[:scope] == "are_internal") } = link_to dashboard_snippets_path(scope: 'are_internal') do Internal - %span - (#{current_user.snippets.are_internal.count}) + %span.badge + = current_user.snippets.are_internal.count %li{ class: ("active" if params[:scope] == "are_public") } = link_to dashboard_snippets_path(scope: 'are_public') do Public - %span - (#{current_user.snippets.are_public.count}) + %span.badge + = current_user.snippets.are_public.count = render 'snippets/snippets' diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index a87b796c6f..fc42e5dcc6 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -8,15 +8,15 @@ = link_to todos_filter_path(state: 'pending') do %span To do - %span - (#{todos_pending_count}) + %span.badge + = todos_pending_count - todo_done_active = ('active' if params[:state] == 'done') %li{class: "todos-done #{todo_done_active}"} = link_to todos_filter_path(state: 'done') do %span Done - %span - (#{todos_done_count}) + %span.badge + = todos_done_count .nav-controls - if @todos.any?(&:pending?) diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index fcb1e656d7..2e8015d119 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -6,21 +6,21 @@ %li{class: ('active' if @scope.nil?)} = link_to project_builds_path(@project) do All - %span.js-totalbuilds-count - (#{number_with_delimiter(@all_builds.count(:id))}) + %span.badge.js-totalbuilds-count + = number_with_delimiter(@all_builds.count(:id)) %li{class: ('active' if @scope == 'running')} = link_to project_builds_path(@project, scope: :running) do Running - %span.js-running-count - (#{number_with_delimiter(@all_builds.running_or_pending.count(:id))}) + %span.badge.js-running-count + = number_with_delimiter(@all_builds.running_or_pending.count(:id)) %li{class: ('active' if @scope == 'finished')} = link_to project_builds_path(@project, scope: :finished) do Finished - %span.js-running-count - (#{number_with_delimiter(@all_builds.finished.count(:id))}) + %span.badge.js-running-count + = number_with_delimiter(@all_builds.finished.count(:id)) .nav-controls - if can?(current_user, :update_build, @project) diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml index b2d3db0bdc..ea33aa472a 100644 --- a/app/views/projects/commit/_ci_menu.html.haml +++ b/app/views/projects/commit/_ci_menu.html.haml @@ -2,8 +2,8 @@ = nav_link(path: 'commit#show') do = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do Changes - %span (#{@diffs.count}) + %span.badge= @diffs.count = nav_link(path: 'commit#builds') do = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do Builds - %span (#{@statuses.count}) + %span.badge= @statuses.count diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index 2c8fecae76..d1bd76ab52 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -2,8 +2,8 @@ = nav_link(controller: [:commit, :commits]) do = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do Commits - %span - (#{number_with_delimiter(@repository.commit_count)}) + %span.badge + = number_with_delimiter(@repository.commit_count) = nav_link(controller: %w(network)) do = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do @@ -16,11 +16,9 @@ = nav_link(html_options: {class: branches_tab_class}) do = link_to namespace_project_branches_path(@project.namespace, @project) do Branches - %span.js-totalbranch-count - (#{@repository.branch_count}) + %span.badge.js-totalbranch-count= @repository.branch_count = nav_link(controller: [:tags, :releases]) do = link_to namespace_project_tags_path(@project.namespace, @project) do Tags - %span.js-totaltags-count - (#{@repository.tag_count}) + %span.badge.js-totaltags-count= @repository.tag_count diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 0c97a29847..290753d57c 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -50,24 +50,20 @@ %li.notes-tab = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do Discussion - %span - (#{@merge_request.mr_and_commit_notes.user.nonawards.count}) + %span.badge= @merge_request.mr_and_commit_notes.user.nonawards.count %li.commits-tab = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do Commits - %span - (#{@commits.size}) + %span.badge= @commits.size - if @ci_commit %li.builds-tab = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do Builds - %span - (#{@statuses.size}) + %span.badge= @statuses.size %li.diffs-tab = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do Changes - %span - (#{@merge_request.diff_size}) + %span.badge= @merge_request.diff_size .tab-content #notes.notes.tab-pane.voting_notes diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 8bca971bcf..7ff947a51d 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -24,7 +24,7 @@ - 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" -.detail-page-description.milestone-detailrow-content-block.second-block +.detail-page-description.milestone-detail %h2.title = markdown escape_once(milestone.title), pipeline: :single_line diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index aec590f7d9..ecda4ea824 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -135,7 +135,7 @@ Feature: Project Merge Requests And I leave a comment like "Line is wrong" on diff And I switch to the merge request's comments tab Then I should see a discussion has started on diff - And I should see a badge of "(1)" next to the discussion link + And I should see a badge of "1" next to the discussion link @javascript Scenario: I see a new comment on merge request diff from another user in the discussion tab @@ -143,7 +143,7 @@ Feature: Project Merge Requests And I visit merge request page "Bug NS-05" And user "John Doe" leaves a comment like "Line is wrong" on diff Then I should see a discussion by user "John Doe" has started on diff - And I should see a badge of "(1)" next to the discussion link + And I should see a badge of "1" next to the discussion link @javascript Scenario: I edit a comment on a merge request diff @@ -161,11 +161,11 @@ Feature: Project Merge Requests And I visit merge request page "Bug NS-05" And I click on the Changes tab And I leave a comment like "Line is wrong" on diff - And I should see a badge of "(1)" next to the discussion link + And I should see a badge of "1" next to the discussion link And I delete the comment "Line is wrong" on diff And I click on the Discussion tab Then I should not see any discussion - And I should see a badge of "(0)" next to the discussion link + And I should see a badge of "0" next to the discussion link @javascript Scenario: I comment on a line of a commit in merge request diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb index da380250cd..2b23df6764 100644 --- a/features/steps/dashboard/todos.rb +++ b/features/steps/dashboard/todos.rb @@ -27,8 +27,8 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps step 'I should see todos assigned to me' do page.within('.nav-sidebar') { expect(page).to have_content 'Todos 4' } - expect(page).to have_content 'To do (4)' - expect(page).to have_content 'Done (0)' + expect(page).to have_content 'To do 4' + expect(page).to have_content 'Done 0' expect(page).to have_link project.name_with_namespace should_see_todo(1, "John Doe assigned you merge request #{merge_request.to_reference}", merge_request.title) @@ -43,8 +43,8 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps end page.within('.nav-sidebar') { expect(page).to have_content 'Todos 3' } - expect(page).to have_content 'To do (3)' - expect(page).to have_content 'Done (1)' + expect(page).to have_content 'To do 3' + expect(page).to have_content 'Done 1' should_not_see_todo "John Doe assigned you merge request #{merge_request.to_reference}" end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 463235742f..3b1a00f628 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -344,12 +344,12 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end end - step 'I should see a badge of "(1)" next to the discussion link' do - expect_discussion_badge_to_have_counter("(1)") + step 'I should see a badge of "1" next to the discussion link' do + expect_discussion_badge_to_have_counter("1") end - step 'I should see a badge of "(0)" next to the discussion link' do - expect_discussion_badge_to_have_counter("(0)") + step 'I should see a badge of "0" next to the discussion link' do + expect_discussion_badge_to_have_counter("0") end step 'I should see a discussion has started on commit diff' do @@ -572,7 +572,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end def expect_discussion_badge_to_have_counter(value) - page.within(".notes-tab span") do + page.within(".notes-tab .badge") do page.should have_content value end end diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index 0aef4cba23..389812ff7e 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -151,7 +151,7 @@ describe 'Comments', feature: true do visit namespace_project_merge_request_path(project.namespace, project, merge_request) expect(merge_request.mr_and_commit_notes.count).to eq 2 - expect(find('.notes-tab span').text).to eq "(1)" + expect(find('.notes-tab span.badge').text).to eq "1" end end end From 655b2640ae9cbe2737369401969e99caeddf192d Mon Sep 17 00:00:00 2001 From: Benedikt Huss Date: Wed, 4 May 2016 09:34:25 +0200 Subject: [PATCH 096/138] Merge request widget displays TeamCity build state and code coverage correctly again --- .../merge_request_widget.js.coffee | 12 +++---- .../merge_request_widget_spec.js.coffee | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 spec/javascripts/merge_request_widget_spec.js.coffee diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index 065626beeb..1abda3530c 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -68,13 +68,13 @@ class @MergeRequestWidget $.getJSON @opts.ci_status_url, (data) => @readyForCICheck = true - if @firstCICheck - @firstCICheck = false + if @firstCICheck || @opts.ci_status is '' + if @firstCICheck + @firstCICheck = false @opts.ci_status = data.status - - if @opts.ci_status is '' - @opts.ci_status = data.status - return + @showCIStatus data.status + if data.coverage + @showCICoverage data.coverage if data.status isnt @opts.ci_status and data.status? @showCIStatus data.status diff --git a/spec/javascripts/merge_request_widget_spec.js.coffee b/spec/javascripts/merge_request_widget_spec.js.coffee new file mode 100644 index 0000000000..e1fb610654 --- /dev/null +++ b/spec/javascripts/merge_request_widget_spec.js.coffee @@ -0,0 +1,35 @@ +#= require merge_request_widget + +describe 'MergeRequestWidget', -> + + beforeEach -> + window.notifyPermissions = () -> + @opts = {ci_status_url:"http://sampledomain.local/ci/getstatus",ci_status:""} + @class = new MergeRequestWidget(@opts) + @ciStatusData = {"title":"Sample MR title","sha":"12a34bc5","status":"success","coverage":98} + + describe 'getCIStatus', -> + beforeEach -> + spyOn(jQuery, 'getJSON').and.callFake (req, cb) => + cb(@ciStatusData) + + it 'should call showCIStatus even if a notification should not be displayed', -> + spy = spyOn(@class, 'showCIStatus').and.stub() + @class.getCIStatus(false) + expect(spy).toHaveBeenCalledWith(@ciStatusData.status) + + it 'should call showCIStatus when a notification should be displayed', -> + spy = spyOn(@class, 'showCIStatus').and.stub() + @class.getCIStatus(true) + expect(spy).toHaveBeenCalledWith(@ciStatusData.status) + + it 'should call showCICoverage when the coverage rate is set', -> + spy = spyOn(@class, 'showCICoverage').and.stub() + @class.getCIStatus(true) + expect(spy).toHaveBeenCalledWith(@ciStatusData.coverage) + + it 'should not call showCICoverage when the coverage rate is not set', -> + @ciStatusData.coverage = null + spy = spyOn(@class, 'showCICoverage').and.stub() + @class.getCIStatus(true) + expect(spy).not.toHaveBeenCalled() From 92c25f42009ce42c439efa4236d14ac1440711e4 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 4 May 2016 17:33:58 -0400 Subject: [PATCH 097/138] Use `number_to_human_size` helper to show repository size This will intelligently format large repository sizes in GBs (or, shudder, TBs). --- app/helpers/projects_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 3d5e61d2c1..058233c88a 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -200,7 +200,8 @@ module ProjectsHelper end def repository_size(project = @project) - "#{project.repository_size} MB" + size_in_bytes = project.repository_size * 1.megabyte + number_to_human_size(size_in_bytes, delimiter: ',', precision: 2) rescue # In order to prevent 500 error # when application cannot allocate memory From 5448970950008fb49092fcfbdb155161d707e10f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 4 May 2016 17:39:06 -0400 Subject: [PATCH 098/138] Remove `rescue` clause from `repository_size` helper The repository size has since become calculated (and cached) more intelligently, and this should no longer be necessary. --- app/helpers/projects_helper.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 058233c88a..20e47f77b4 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -202,11 +202,6 @@ module ProjectsHelper def repository_size(project = @project) size_in_bytes = project.repository_size * 1.megabyte number_to_human_size(size_in_bytes, delimiter: ',', precision: 2) - rescue - # In order to prevent 500 error - # when application cannot allocate memory - # to calculate repo size - just show 'Unknown' - 'unknown' end def default_url_to_repo(project = @project) From 0e9c2e721b08e244e63f1d26ac3771a8d858cd76 Mon Sep 17 00:00:00 2001 From: Benedikt Huss Date: Wed, 4 May 2016 20:45:12 +0200 Subject: [PATCH 099/138] Feedback from stanhu --- CHANGELOG | 1 + .../merge_request_widget.js.coffee | 19 ++++++++---------- .../merge_request_widget_spec.js.coffee | 20 ++++++++++++++++--- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0e6e2505ec..d2ff4c32f2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -22,6 +22,7 @@ v 8.8.0 (unreleased) - API support for the 'since' and 'until' operators on commit requests (Paco Guzman) - Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko) - Expire repository exists? and has_visible_content? caches after a push if necessary + - Merge request widget displays TeamCity build state and code coverage correctly again. v 8.7.3 - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index 1abda3530c..17a5a057a9 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -68,20 +68,18 @@ class @MergeRequestWidget $.getJSON @opts.ci_status_url, (data) => @readyForCICheck = true - if @firstCICheck || @opts.ci_status is '' - if @firstCICheck - @firstCICheck = false + if data.status is '' + return + + if @firstCiCheck || data.status isnt @opts.ci_status and data.status? @opts.ci_status = data.status @showCIStatus data.status if data.coverage @showCICoverage data.coverage - if data.status isnt @opts.ci_status and data.status? - @showCIStatus data.status - if data.coverage - @showCICoverage data.coverage - - if showNotification + # The first check should only update the UI, a notification + # should only be displayed on status changes + if showNotification and not @firstCiCheck status = @ciLabelForStatus(data.status) if status is "preparing" @@ -104,8 +102,7 @@ class @MergeRequestWidget @close() Turbolinks.visit _this.opts.builds_path ) - - @opts.ci_status = data.status + @firstCiCheck = false showCIStatus: (state) -> $('.ci_widget').hide() diff --git a/spec/javascripts/merge_request_widget_spec.js.coffee b/spec/javascripts/merge_request_widget_spec.js.coffee index e1fb610654..c0bd8a29e4 100644 --- a/spec/javascripts/merge_request_widget_spec.js.coffee +++ b/spec/javascripts/merge_request_widget_spec.js.coffee @@ -4,7 +4,21 @@ describe 'MergeRequestWidget', -> beforeEach -> window.notifyPermissions = () -> - @opts = {ci_status_url:"http://sampledomain.local/ci/getstatus",ci_status:""} + window.notify = () -> + @opts = { + ci_status_url:"http://sampledomain.local/ci/getstatus", + ci_status:"", + ci_message: { + normal: "Build {{status}} for \"{{title}}\"", + preparing: "{{status}} build for \"{{title}}\"" + }, + ci_title: { + preparing: "{{status}} build", + normal: "Build {{status}}" + }, + gitlab_icon:"gitlab_logo.png", + builds_path:"http://sampledomain.local/sampleBuildsPath" + } @class = new MergeRequestWidget(@opts) @ciStatusData = {"title":"Sample MR title","sha":"12a34bc5","status":"success","coverage":98} @@ -25,11 +39,11 @@ describe 'MergeRequestWidget', -> it 'should call showCICoverage when the coverage rate is set', -> spy = spyOn(@class, 'showCICoverage').and.stub() - @class.getCIStatus(true) + @class.getCIStatus(false) expect(spy).toHaveBeenCalledWith(@ciStatusData.coverage) it 'should not call showCICoverage when the coverage rate is not set', -> @ciStatusData.coverage = null spy = spyOn(@class, 'showCICoverage').and.stub() - @class.getCIStatus(true) + @class.getCIStatus(false) expect(spy).not.toHaveBeenCalled() From 91f693c0c4f1ebb62d78e6c82b833f8a19c4dc62 Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Thu, 5 May 2016 13:59:09 +0700 Subject: [PATCH 100/138] Update changelog, improve specs --- CHANGELOG | 2 +- app/services/issues/move_service.rb | 2 +- spec/services/issues/move_service_spec.rb | 10 +++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2d3a9e051e..27c89a7c1e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.8.0 (unreleased) - - Assign labels and milestone to target project when moving issue. !3934 + - Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen) - Remove future dates from contribution calendar graph. - Fix error when visiting commit builds page before build was updated - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index fe5df8f18c..e61628086f 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -46,7 +46,7 @@ module Issues project: @new_project, author: @old_issue.author, description: rewrite_content(@old_issue.description) } - new_params = @old_issue.serializable_hash.merge(new_params) + new_params = @old_issue.serializable_hash.symbolize_keys.merge(new_params) CreateService.new(@new_project, @current_user, new_params).execute end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 7399003bd7..c15e26189a 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -7,9 +7,7 @@ describe Issues::MoveService, services: true do let(:description) { 'Some issue description' } let(:old_project) { create(:project) } let(:new_project) { create(:project) } - let!(:milestone1) do - create(:milestone, project_id: old_project.id, title: 'v9.0') - end + let(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') } let(:old_issue) do create(:issue, title: title, description: description, @@ -57,6 +55,7 @@ describe Issues::MoveService, services: true do it 'assigns milestone to new issue' do expect(new_issue.reload.milestone.title).to eq 'v9.0' + expect(new_issue.reload.milestone).to eq(milestone2) end it 'assign labels to new issue' do @@ -64,6 +63,11 @@ describe Issues::MoveService, services: true do expect(expected_label_titles).to include 'label1' expect(expected_label_titles).to include 'label2' expect(expected_label_titles.size).to eq 2 + + new_issue.labels.each do |label| + expect(new_project.labels).to include(label) + expect(old_project.labels).not_to include(label) + end end it 'rewrites issue title' do From 92ed02c54896d395160e9801f286ba16eb8e3ea6 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 5 May 2016 10:09:26 +0200 Subject: [PATCH 101/138] Moved TeamCity changelog entry to 8.7.3 [ci skip] --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ecd8b7c347..e989e622b9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,10 +23,10 @@ v 8.8.0 (unreleased) - API support for the 'since' and 'until' operators on commit requests (Paco Guzman) - Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko) - Expire repository exists? and has_visible_content? caches after a push if necessary - - Merge request widget displays TeamCity build state and code coverage correctly again. v 8.7.3 - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented + - Merge request widget displays TeamCity build state and code coverage correctly again. v 8.7.2 - The "New Branch" button is now loaded asynchronously From 6fbf6b2936e8fa4aecae1bb798d38ec82d2989d2 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 3 May 2016 02:45:46 -0300 Subject: [PATCH 102/138] Fix the line code when importing PR review comments from GitHub Pull Request Review Comments are comments on a portion of the unified diff. --- lib/gitlab/github_import/comment_formatter.rb | 21 ++++++++++--- .../github_import/comment_formatter_spec.rb | 30 ++++++++----------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/lib/gitlab/github_import/comment_formatter.rb b/lib/gitlab/github_import/comment_formatter.rb index 7d58e53991..7d679eaec6 100644 --- a/lib/gitlab/github_import/comment_formatter.rb +++ b/lib/gitlab/github_import/comment_formatter.rb @@ -28,13 +28,26 @@ module Gitlab end def line_code - if on_diff? - Gitlab::Diff::LineCode.generate(raw_data.path, raw_data.position, 0) - end + return unless on_diff? + + parsed_lines = Gitlab::Diff::Parser.new.parse(diff_hunk.lines) + generate_line_code(parsed_lines.to_a.last) + end + + def generate_line_code(line) + Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos) end def on_diff? - raw_data.path && raw_data.position + diff_hunk.present? + end + + def diff_hunk + raw_data.diff_hunk + end + + def file_path + raw_data.path end def note diff --git a/spec/lib/gitlab/github_import/comment_formatter_spec.rb b/spec/lib/gitlab/github_import/comment_formatter_spec.rb index a324a82e69..55e86d4cea 100644 --- a/spec/lib/gitlab/github_import/comment_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/comment_formatter_spec.rb @@ -2,23 +2,25 @@ require 'spec_helper' describe Gitlab::GithubImport::CommentFormatter, lib: true do let(:project) { create(:project) } - let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:octocat) { double(id: 123456, login: 'octocat') } let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') } let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') } - let(:base_data) do + let(:base) do { body: "I'm having a problem with this.", user: octocat, + commit_id: nil, + diff_hunk: nil, created_at: created_at, updated_at: updated_at } end - subject(:comment) { described_class.new(project, raw_data)} + subject(:comment) { described_class.new(project, raw)} describe '#attributes' do context 'when do not reference a portion of the diff' do - let(:raw_data) { OpenStruct.new(base_data) } + let(:raw) { double(base) } it 'returns formatted attributes' do expected = { @@ -36,24 +38,23 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do end context 'when on a portion of the diff' do - let(:diff_data) do + let(:diff) do { body: 'Great stuff', commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', - diff_hunk: '@@ -16,33 +16,40 @@ public class Connection : IConnection...', - path: 'file1.txt', - position: 1 + diff_hunk: "@@ -1,5 +1,9 @@\n class User\n def name\n- 'John Doe'\n+ 'Jane Doe'", + path: 'file1.txt' } end - let(:raw_data) { OpenStruct.new(base_data.merge(diff_data)) } + let(:raw) { double(base.merge(diff)) } it 'returns formatted attributes' do expected = { project: project, note: "*Created by: octocat*\n\nGreat stuff", commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', - line_code: 'ce1be0ff4065a6e9415095c95f25f47a633cef2b_0_1', + line_code: 'ce1be0ff4065a6e9415095c95f25f47a633cef2b_4_3', author_id: project.creator_id, created_at: created_at, updated_at: updated_at @@ -64,15 +65,10 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do end context 'when author is a GitLab user' do - let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + let(:raw) { double(base.merge(user: octocat)) } - it 'returns project#creator_id as author_id when is not a GitLab user' do - expect(comment.attributes.fetch(:author_id)).to eq project.creator_id - end - - it 'returns GitLab user id as author_id when is a GitLab user' do + it 'returns GitLab user id as author_id' do gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') - expect(comment.attributes.fetch(:author_id)).to eq gl_user.id end end From 3bf43e7c2710f150720392e28757862bc4e021b3 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 5 May 2016 10:47:08 -0300 Subject: [PATCH 103/138] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 0020e580b8..bcb3ecec26 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -28,6 +28,7 @@ v 8.8.0 (unreleased) v 8.7.3 - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented - Merge request widget displays TeamCity build state and code coverage correctly again. + - Fix the line code when importing PR review comments from GitHub. !4010 v 8.7.2 - The "New Branch" button is now loaded asynchronously From c24958365fa739357e7a090e10c233860008dcd3 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Thu, 5 May 2016 09:00:29 -0500 Subject: [PATCH 104/138] Remove mobile styles; horizontally scroll layout nav links --- app/assets/stylesheets/framework/nav.scss | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index bf09b6dadc..7c18e93a26 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -222,29 +222,13 @@ .dropdown { margin-left: 7px; } - - @media (max-width: $screen-md-max) { - float: none; - margin-bottom: 10px; - - .btn { - width: 20%; - } - } - - @media (max-width: $screen-xs-max) { - text-align: center; - - .rss-btn, - .dropdown-new { - display: inline-block; - width: 48%; - } - } } .nav-links { border-bottom: none; + height: 51px; + white-space: nowrap; + overflow-x: auto; li { From 4bef939e27d7a6b2df9984f445485207f3dacd43 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 May 2016 16:31:19 +0200 Subject: [PATCH 105/138] Remove group rss icon since it depends on context and duplciates on other pages Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/nav/_group.html.haml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index b4b528c614..5c11d3c36c 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,8 +1,5 @@ - if current_user .controls - - if current_path?('groups#show') - = link_to icon('rss'), group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn btn-gray rss-btn' - %span.dropdown.group-settings-dropdown %a.dropdown-new.btn.btn-gray#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} = icon('cog') From 5a8b8ba6c76a74dcad401a627b271b10bb7a1088 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 May 2016 16:37:41 +0200 Subject: [PATCH 106/138] Fix group settings dropdown for group members and visitors Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/nav/_group.html.haml | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 5c11d3c36c..dff188e0b1 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,19 +1,19 @@ - if current_user - .controls - %span.dropdown.group-settings-dropdown - %a.dropdown-new.btn.btn-gray#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} - = icon('cog') - = icon('caret-down') - %ul.dropdown-menu.dropdown-menu-align-right - = nav_link(path: 'groups#projects') do - = link_to projects_group_path(@group), title: 'Projects' do - Projects - %li.divider - - if can?(current_user, :admin_group, @group) - %li - = link_to edit_group_path(@group) do - Edit Group - - if access = @group.users.find_by(id: current_user.id) + - if access = @group.users.find_by(id: current_user.id) + .controls + %span.dropdown.group-settings-dropdown + %a.dropdown-new.btn.btn-gray#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} + = icon('cog') + = icon('caret-down') + %ul.dropdown-menu.dropdown-menu-align-right + - if can?(current_user, :admin_group, @group) + = nav_link(path: 'groups#projects') do + = link_to projects_group_path(@group), title: 'Projects' do + Projects + %li.divider + %li + = link_to edit_group_path(@group) do + Edit Group %li = link_to leave_group_group_members_path(@group), data: { confirm: leave_group_message(@group.name) }, method: :delete, title: 'Leave group' do From 280ea4f600f9084d16d84fdaf57bf54830787fe3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 5 May 2016 09:17:46 -0700 Subject: [PATCH 107/138] Upgrade Sidekiq to 4.1.2 --- CHANGELOG | 1 + Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bcb3ecec26..8bc554efbe 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.8.0 (unreleased) - Project#open_branches has been cleaned up and no longer loads entire records into memory. - Log to application.log when an admin starts and stops impersonating a user - Make build status canceled if any of the jobs was canceled and none failed + - Upgrade Sidekiq to 4.1.2 - Sanitize repo paths in new project error message - Bump mail_room to 0.7.0 to fix stuck IDLE connections - Remove future dates from contribution calendar graph. diff --git a/Gemfile.lock b/Gemfile.lock index 41d2922f63..a8f7bfe43b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -134,7 +134,7 @@ GEM execjs coffee-script-source (1.10.0) colorize (0.7.7) - concurrent-ruby (1.0.1) + concurrent-ruby (1.0.2) connection_pool (2.2.0) coveralls (0.8.13) json (~> 1.8) @@ -740,7 +740,7 @@ GEM rack shoulda-matchers (2.8.0) activesupport (>= 3.0.0) - sidekiq (4.1.1) + sidekiq (4.1.2) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) redis (~> 3.2, >= 3.2.1) From 8159555491d0f1f9524ee818236b4bc552012bfa Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 5 May 2016 19:31:10 +0200 Subject: [PATCH 108/138] Added CHANGELOG entry for merge request !3931 [ci skip] --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 8bc554efbe..d8dfaaf7d0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -30,6 +30,7 @@ v 8.7.3 - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented - Merge request widget displays TeamCity build state and code coverage correctly again. - Fix the line code when importing PR review comments from GitHub. !4010 + - Wikis are now initialized on legacy projects when checking repositories v 8.7.2 - The "New Branch" button is now loaded asynchronously From d04146b0e0450f6363193fb703d90321df9920e5 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 5 May 2016 19:33:05 +0200 Subject: [PATCH 109/138] Added CHANGELOG entry for merge request !3880 [ci skip] --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index d8dfaaf7d0..975157c683 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -31,6 +31,7 @@ v 8.7.3 - Merge request widget displays TeamCity build state and code coverage correctly again. - Fix the line code when importing PR review comments from GitHub. !4010 - Wikis are now initialized on legacy projects when checking repositories + - Label titles in filters are now escaped properly v 8.7.2 - The "New Branch" button is now loaded asynchronously From 0cf59281cda96e970a525b939d9db5f5ca7a3be4 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 5 May 2016 19:40:08 +0200 Subject: [PATCH 110/138] Moved entry for !3880 to 8.7.2 Apparently this was already released in 8.7.2 but no changelog entry was added. [ci skip] --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 975157c683..5469723bec 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -31,12 +31,12 @@ v 8.7.3 - Merge request widget displays TeamCity build state and code coverage correctly again. - Fix the line code when importing PR review comments from GitHub. !4010 - Wikis are now initialized on legacy projects when checking repositories - - Label titles in filters are now escaped properly v 8.7.2 - The "New Branch" button is now loaded asynchronously - Fix error 500 when trying to create a wiki page - Updated spacing between notification label and button + - Label titles in filters are now escaped properly v 8.7.1 - Throttle the update of `project.last_activity_at` to 1 minute. !3848 From 6e47a1924b71c44b989769e6af41459b0f5da6d2 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 4 May 2016 10:44:43 +0200 Subject: [PATCH 111/138] Updated gitlab_git to 10.1.0 --- CHANGELOG | 2 ++ Gemfile.lock | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 5469723bec..769ffb2476 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.8.0 (unreleased) - Project#open_branches has been cleaned up and no longer loads entire records into memory. - Log to application.log when an admin starts and stops impersonating a user + - Updated gitlab_git to 10.1.0 + - GitAccess#protected_tag? no longer loads all tags just to check if a single one exists - Make build status canceled if any of the jobs was canceled and none failed - Upgrade Sidekiq to 4.1.2 - Sanitize repo paths in new project error message diff --git a/Gemfile.lock b/Gemfile.lock index a8f7bfe43b..fe083c2b56 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -353,7 +353,7 @@ GEM posix-spawn (~> 0.3) gitlab_emoji (0.3.1) gemojione (~> 2.2, >= 2.2.1) - gitlab_git (10.0.2) + gitlab_git (10.1.0) activesupport (~> 4.0) charlock_holmes (~> 0.7.3) github-linguist (~> 4.7.0) From 93ce229665f875efcd7ee25b006834300c2e37be Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 29 Apr 2016 21:41:39 +0200 Subject: [PATCH 112/138] Use tag_exists? in GitAccess#protected_tag? This removes the need for retrieving the entire list of tags just to check if a specific one exists. --- lib/gitlab/git_access.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 3ed1eec517..ad2da2b81c 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -176,7 +176,7 @@ module Gitlab end def protected_tag?(tag_name) - project.repository.tag_names.include?(tag_name) + project.repository.tag_exists?(tag_name) end def user_allowed? From 003671207db67eee5f3ceb605a061346dfad945d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 4 May 2016 12:06:40 +0200 Subject: [PATCH 113/138] Fix passing nil to protected_tag? Previously this method would directly receive the output of tag_name(). This method could either return a String or nil. In the previous setup this would somehow magically work but because Rugged::TagCollection#[] doesn't accept nil values it started to fail. To work around this the elsif in change_access_check() assigns the result of tag_name() to a local and then _only_ calls protected_tag?() if the tag name is not nil. The extra parenthesis are put in place to ensure that things are parsed correctly, without these the code would be parsed as follows: elsif tag_ref = (tag_name(ref) && protected_tag(tag_ref)) During runtime this would basically resolve to: elsif tag_ref = (tag_name(ref) && protected_tag(nil)) This is because when you refer to the variable you're assigning _in_ the assignment Ruby returns nil instead of raising an error. --- lib/gitlab/git_access.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index ad2da2b81c..6cb4123987 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -128,7 +128,7 @@ module Gitlab action = if project.protected_branch?(branch_name(ref)) protected_branch_action(oldrev, newrev, branch_name(ref)) - elsif protected_tag?(tag_name(ref)) + elsif (tag_ref = tag_name(ref)) && protected_tag?(tag_ref) # Prevent any changes to existing git tag unless user has permissions :admin_project else From 00b3eedf7c1a4c94bd82ef513c5aecc21d1296d0 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Thu, 5 May 2016 12:55:01 -0500 Subject: [PATCH 114/138] Remove duplicate cover-control mobile styles --- app/assets/stylesheets/pages/profile.scss | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 7a3a2ddd36..abc5a0e987 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -222,16 +222,4 @@ } } } - - .cover-controls { - @media (max-width: $screen-xs-max) { - position: static; - margin-bottom: 20px; - - .btn { - display: inline-block; - width: 48%; - } - } - } } From d6f67dba0144bdae20bd86051f3eb98ce201439e Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Thu, 5 May 2016 13:46:58 -0700 Subject: [PATCH 115/138] Add note to requirements doc on swap recommendation, and ensuring enough available memory. --- doc/install/requirements.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 58f409746c..df8e8bdc47 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -64,7 +64,10 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim ### Memory You need at least 2GB of addressable memory (RAM + swap) to install and use GitLab! -With less memory GitLab will give strange errors during the reconfigure run and 500 errors during usage. +The operating system and any other running applications will also be using memory +so keep in mind that you need at least 2GB available before running GitLab. With +less memory GitLab will give strange errors during the reconfigure run and 500 +errors during usage. - 512MB RAM + 1.5GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice. - 1GB RAM + 1GB swap supports up to 100 users but it will be very slow @@ -77,6 +80,10 @@ With less memory GitLab will give strange errors during the reconfigure run and - 128GB RAM supports up to 32,000 users - More users? Run it on [multiple application servers](https://about.gitlab.com/high-availability/) +We recommend having at least 1GB of swap on your server, even if you currently have +enough available RAM. Having swap will help reduce the chance of errors occuring +if your available memory changes. + Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about many you need of those. ## Gitlab Runner From df9a7c6b4acf0a662366b2e31d897cda370c2216 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 5 May 2016 17:22:18 -0400 Subject: [PATCH 116/138] Add a note to testing docs about the `:each` argument to RSpec hooks [ci skip] --- doc/development/testing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/development/testing.md b/doc/development/testing.md index 672e3fb464..33eed29ba5 100644 --- a/doc/development/testing.md +++ b/doc/development/testing.md @@ -64,6 +64,7 @@ the command line via `bundle exec teaspoon`, or via a web browser at methods. - Use `context` to test branching logic. - Don't `describe` symbols (see [Gotchas](gotchas.md#dont-describe-symbols)). +- Don't supply the `:each` argument to hooks since it's the default. - Prefer `not_to` to `to_not`. - Try to match the ordering of tests to the ordering within the class. - Try to follow the [Four-Phase Test][four-phase-test] pattern, using newlines From 80b30500e44b621c0ecd3c062349b34ddbb0a0fb Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Fri, 6 May 2016 00:42:13 +0000 Subject: [PATCH 117/138] blost -> blog --- doc/ci/examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index cc059dc437..3a152c9116 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -10,6 +10,6 @@ ## Outside the documentation -- [Blost post about using GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) +- [Blog post about using GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) - [Repo's with examples for various languages](https://gitlab.com/groups/gitlab-examples) - [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml) From 47da013cf83b4a42fc9f4b049d8ba41dc3f325d2 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 4 May 2016 19:42:10 +0200 Subject: [PATCH 118/138] Annotate the models --- app/models/appearance.rb | 13 ++++ app/models/application_setting.rb | 16 ++-- app/models/audit_event.rb | 4 +- app/models/broadcast_message.rb | 4 +- app/models/ci/build.rb | 14 ++-- app/models/ci/commit.rb | 10 ++- app/models/ci/runner.rb | 14 ++-- app/models/ci/trigger.rb | 2 +- app/models/ci/variable.rb | 6 +- app/models/commit_status.rb | 17 +++-- app/models/deploy_key.rb | 6 +- app/models/email.rb | 2 +- app/models/event.rb | 4 +- app/models/generic_commit_status.rb | 17 +++-- app/models/group.rb | 21 +++--- app/models/hooks/project_hook.rb | 2 +- app/models/hooks/service_hook.rb | 2 +- app/models/hooks/system_hook.rb | 2 +- app/models/hooks/web_hook.rb | 2 +- app/models/identity.rb | 4 +- app/models/issue.rb | 9 ++- app/models/key.rb | 6 +- app/models/label.rb | 16 ++-- app/models/label_link.rb | 2 +- app/models/lfs_object.rb | 4 +- app/models/member.rb | 8 +- app/models/members/group_member.rb | 8 +- app/models/members/project_member.rb | 8 +- app/models/merge_request.rb | 13 ++-- app/models/merge_request_diff.rb | 4 +- app/models/milestone.rb | 4 +- app/models/namespace.rb | 20 ++--- app/models/note.rb | 8 +- app/models/notification_setting.rb | 13 ++++ app/models/personal_snippet.rb | 6 +- app/models/project.rb | 75 ++++++++++--------- app/models/project_group_link.rb | 12 +++ app/models/project_import_data.rb | 9 ++- app/models/project_services/asana_service.rb | 13 ++-- .../project_services/assembla_service.rb | 13 ++-- app/models/project_services/bamboo_service.rb | 13 ++-- .../project_services/buildkite_service.rb | 13 ++-- .../project_services/builds_email_service.rb | 13 ++-- .../project_services/campfire_service.rb | 13 ++-- app/models/project_services/ci_service.rb | 13 ++-- .../custom_issue_tracker_service.rb | 13 ++-- .../project_services/drone_ci_service.rb | 13 ++-- .../emails_on_push_service.rb | 13 ++-- .../project_services/external_wiki_service.rb | 13 ++-- .../project_services/flowdock_service.rb | 13 ++-- .../project_services/gemnasium_service.rb | 13 ++-- .../project_services/gitlab_ci_service.rb | 13 ++-- .../gitlab_issue_tracker_service.rb | 13 ++-- .../project_services/hipchat_service.rb | 13 ++-- app/models/project_services/irker_service.rb | 13 ++-- .../project_services/issue_tracker_service.rb | 13 ++-- app/models/project_services/jira_service.rb | 13 ++-- .../pivotaltracker_service.rb | 13 ++-- .../project_services/pushover_service.rb | 13 ++-- .../project_services/redmine_service.rb | 13 ++-- app/models/project_services/slack_service.rb | 13 ++-- .../project_services/teamcity_service.rb | 13 ++-- app/models/project_snippet.rb | 6 +- app/models/protected_branch.rb | 2 +- app/models/release.rb | 2 +- app/models/security_event.rb | 4 +- app/models/sent_notification.rb | 8 +- app/models/service.rb | 13 ++-- app/models/snippet.rb | 6 +- app/models/spam_log.rb | 17 +++++ app/models/subscription.rb | 2 +- app/models/user.rb | 49 ++++++------ 72 files changed, 485 insertions(+), 323 deletions(-) diff --git a/app/models/appearance.rb b/app/models/appearance.rb index 4cf8dd9a8c..4528760fef 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -1,3 +1,16 @@ +# == Schema Information +# +# Table name: appearances +# +# id :integer not null, primary key +# title :string +# description :text +# header_logo :string +# logo :string +# created_at :datetime not null +# updated_at :datetime not null +# + class Appearance < ActiveRecord::Base validates :title, presence: true validates :description, presence: true diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 36f8815423..72ec91d290 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -10,21 +10,20 @@ # sign_in_text :text # created_at :datetime # updated_at :datetime -# home_page_url :string(255) +# home_page_url :string # default_branch_protection :integer default(2) # restricted_visibility_levels :text # version_check_enabled :boolean default(TRUE) # max_attachment_size :integer default(10), not null # default_project_visibility :integer # default_snippet_visibility :integer -# default_group_visibility :integer # restricted_signup_domains :text # user_oauth_applications :boolean default(TRUE) -# after_sign_out_path :string(255) +# after_sign_out_path :string # session_expire_delay :integer default(10080), not null # import_sources :text # help_page_text :text -# admin_notification_email :string(255) +# admin_notification_email :string # shared_runners_enabled :boolean default(TRUE), not null # max_artifacts_size :integer default(100), not null # runners_registration_token :string @@ -32,8 +31,6 @@ # two_factor_grace_period :integer default(48) # metrics_enabled :boolean default(FALSE) # metrics_host :string default("localhost") -# metrics_username :string -# metrics_password :string # metrics_pool_size :integer default(16) # metrics_timeout :integer default(10) # metrics_method_call_threshold :integer default(10) @@ -41,9 +38,16 @@ # recaptcha_site_key :string # recaptcha_private_key :string # metrics_port :integer default(8089) +# metrics_sample_interval :integer default(15) # sentry_enabled :boolean default(FALSE) # sentry_dsn :string +# akismet_enabled :boolean default(FALSE) +# akismet_api_key :string # email_author_in_body :boolean default(FALSE) +# default_group_visibility :integer +# repository_checks_enabled :boolean default(FALSE) +# metrics_packet_size :integer default(1) +# shared_runners_text :text # class ApplicationSetting < ActiveRecord::Base diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb index 0ed0dd98a5..44b090260e 100644 --- a/app/models/audit_event.rb +++ b/app/models/audit_event.rb @@ -4,9 +4,9 @@ # # id :integer not null, primary key # author_id :integer not null -# type :string(255) not null +# type :string not null # entity_id :integer not null -# entity_type :string(255) not null +# entity_type :string not null # details :text # created_at :datetime # updated_at :datetime diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index 8a0a8a4c2a..075ac733bf 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -8,8 +8,8 @@ # ends_at :datetime # created_at :datetime # updated_at :datetime -# color :string(255) -# font :string(255) +# color :string +# font :string # class BroadcastMessage < ActiveRecord::Base diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 553cd44797..4bc3a225e2 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -4,7 +4,7 @@ # # id :integer not null, primary key # project_id :integer -# status :string(255) +# status :string # finished_at :datetime # trace :text # created_at :datetime @@ -15,19 +15,19 @@ # commit_id :integer # commands :text # job_id :integer -# name :string(255) +# name :string # deploy :boolean default(FALSE) # options :text # allow_failure :boolean default(FALSE), not null -# stage :string(255) +# stage :string # trigger_request_id :integer # stage_idx :integer # tag :boolean -# ref :string(255) +# ref :string # user_id :integer -# type :string(255) -# target_url :string(255) -# description :string(255) +# type :string +# target_url :string +# description :string # artifacts_file :text # gl_project_id :integer # artifacts_metadata :text diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index f2667e5476..4ac4e0fb8b 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -4,9 +4,9 @@ # # id :integer not null, primary key # project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) +# ref :string +# sha :string +# before_sha :string # push_data :text # created_at :datetime # updated_at :datetime @@ -14,6 +14,10 @@ # yaml_errors :text # committed_at :datetime # gl_project_id :integer +# status :string +# started_at :datetime +# finished_at :datetime +# duration :integer # module Ci diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 90349a0759..add59a0889 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -3,18 +3,18 @@ # Table name: ci_runners # # id :integer not null, primary key -# token :string(255) +# token :string # created_at :datetime # updated_at :datetime -# description :string(255) +# description :string # contacted_at :datetime # active :boolean default(TRUE), not null # is_shared :boolean default(FALSE) -# name :string(255) -# version :string(255) -# revision :string(255) -# platform :string(255) -# architecture :string(255) +# name :string +# version :string +# revision :string +# platform :string +# architecture :string # module Ci diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 2b9a457c8a..4f3f4d79fa 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -3,7 +3,7 @@ # Table name: ci_triggers # # id :integer not null, primary key -# token :string(255) +# token :string # project_id :integer # deleted_at :datetime # created_at :datetime diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index e786bd7dd9..4229fe085a 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -4,11 +4,11 @@ # # id :integer not null, primary key # project_id :integer -# key :string(255) +# key :string # value :text # encrypted_value :text -# encrypted_value_salt :string(255) -# encrypted_value_iv :string(255) +# encrypted_value_salt :string +# encrypted_value_iv :string # gl_project_id :integer # diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index aa56314aa1..1260c448de 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -4,7 +4,7 @@ # # id :integer not null, primary key # project_id :integer -# status :string(255) +# status :string # finished_at :datetime # trace :text # created_at :datetime @@ -15,21 +15,24 @@ # commit_id :integer # commands :text # job_id :integer -# name :string(255) +# name :string # deploy :boolean default(FALSE) # options :text # allow_failure :boolean default(FALSE), not null -# stage :string(255) +# stage :string # trigger_request_id :integer # stage_idx :integer # tag :boolean -# ref :string(255) +# ref :string # user_id :integer -# type :string(255) -# target_url :string(255) -# description :string(255) +# type :string +# target_url :string +# description :string # artifacts_file :text # gl_project_id :integer +# artifacts_metadata :text +# erased_by_id :integer +# erased_at :datetime # class CommitStatus < ActiveRecord::Base diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index 9ab663c04a..43cf625f77 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -7,9 +7,9 @@ # created_at :datetime # updated_at :datetime # key :text -# title :string(255) -# type :string(255) -# fingerprint :string(255) +# title :string +# type :string +# fingerprint :string # public :boolean default(FALSE), not null # diff --git a/app/models/email.rb b/app/models/email.rb index b323d1edd1..eae2472f33 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -4,7 +4,7 @@ # # id :integer not null, primary key # user_id :integer not null -# email :string(255) not null +# email :string not null # created_at :datetime # updated_at :datetime # diff --git a/app/models/event.rb b/app/models/event.rb index 897518aadc..25c7c3e6dc 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -3,9 +3,9 @@ # Table name: events # # id :integer not null, primary key -# target_type :string(255) +# target_type :string # target_id :integer -# title :string(255) +# title :string # data :text # project_id :integer # created_at :datetime diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb index 97f4f03a9a..d4afd8cbe8 100644 --- a/app/models/generic_commit_status.rb +++ b/app/models/generic_commit_status.rb @@ -4,7 +4,7 @@ # # id :integer not null, primary key # project_id :integer -# status :string(255) +# status :string # finished_at :datetime # trace :text # created_at :datetime @@ -15,21 +15,24 @@ # commit_id :integer # commands :text # job_id :integer -# name :string(255) +# name :string # deploy :boolean default(FALSE) # options :text # allow_failure :boolean default(FALSE), not null -# stage :string(255) +# stage :string # trigger_request_id :integer # stage_idx :integer # tag :boolean -# ref :string(255) +# ref :string # user_id :integer -# type :string(255) -# target_url :string(255) -# description :string(255) +# type :string +# target_url :string +# description :string # artifacts_file :text # gl_project_id :integer +# artifacts_metadata :text +# erased_by_id :integer +# erased_at :datetime # class GenericCommitStatus < CommitStatus diff --git a/app/models/group.rb b/app/models/group.rb index 1f8432e332..cff7687795 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -2,16 +2,17 @@ # # Table name: namespaces # -# id :integer not null, primary key -# name :string(255) not null -# path :string(255) not null -# owner_id :integer -# visibility_level :integer default(20), not null -# created_at :datetime -# updated_at :datetime -# type :string(255) -# description :string(255) default(""), not null -# avatar :string(255) +# id :integer not null, primary key +# name :string not null +# path :string not null +# owner_id :integer +# created_at :datetime +# updated_at :datetime +# type :string +# description :string default(""), not null +# avatar :string +# share_with_group_lock :boolean default(FALSE) +# visibility_level :integer default(20), not null # require 'carrierwave/orm/activerecord' diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index d149511b86..64f570b12a 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -16,7 +16,7 @@ # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null -# token :string +# wiki_page_events :boolean default(FALSE), not null # class ProjectHook < WebHook diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb index f45145eeb3..23f6468ee5 100644 --- a/app/models/hooks/service_hook.rb +++ b/app/models/hooks/service_hook.rb @@ -16,7 +16,7 @@ # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null -# token :string +# wiki_page_events :boolean default(FALSE), not null # class ServiceHook < WebHook diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index 012cc8ec00..5e9046f362 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -16,7 +16,7 @@ # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null -# token :string +# wiki_page_events :boolean default(FALSE), not null # class SystemHook < WebHook diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 1e3b481559..a8f17ac16a 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -16,7 +16,7 @@ # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null -# token :string +# wiki_page_events :boolean default(FALSE), not null # class WebHook < ActiveRecord::Base diff --git a/app/models/identity.rb b/app/models/identity.rb index e1915b079d..ef4d5f9909 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -3,8 +3,8 @@ # Table name: identities # # id :integer not null, primary key -# extern_uid :string(255) -# provider :string(255) +# extern_uid :string +# provider :string # user_id :integer # created_at :datetime # updated_at :datetime diff --git a/app/models/issue.rb b/app/models/issue.rb index ea1bfb776e..abaa509707 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -3,20 +3,23 @@ # Table name: issues # # id :integer not null, primary key -# title :string(255) +# title :string # assignee_id :integer # author_id :integer # project_id :integer # created_at :datetime # updated_at :datetime # position :integer default(0) -# branch_name :string(255) +# branch_name :string # description :text # milestone_id :integer -# state :string(255) +# state :string # iid :integer # updated_by_id :integer # moved_to_id :integer +# confidential :boolean default(FALSE) +# deleted_at :datetime +# due_date :date # require 'carrierwave/orm/activerecord' diff --git a/app/models/key.rb b/app/models/key.rb index 0282ad1813..b2b57849f8 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -7,9 +7,9 @@ # created_at :datetime # updated_at :datetime # key :text -# title :string(255) -# type :string(255) -# fingerprint :string(255) +# title :string +# type :string +# fingerprint :string # public :boolean default(FALSE), not null # diff --git a/app/models/label.rb b/app/models/label.rb index 60bdce3295..9a22398d95 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -2,14 +2,14 @@ # # Table name: labels # -# id :integer not null, primary key -# title :string(255) -# color :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# template :boolean default(FALSE) -# description :string(255) +# id :integer not null, primary key +# title :string +# color :string +# project_id :integer +# created_at :datetime +# updated_at :datetime +# template :boolean default(FALSE) +# description :string # class Label < ActiveRecord::Base diff --git a/app/models/label_link.rb b/app/models/label_link.rb index b94c9c777a..7b8e872b6d 100644 --- a/app/models/label_link.rb +++ b/app/models/label_link.rb @@ -5,7 +5,7 @@ # id :integer not null, primary key # label_id :integer # target_id :integer -# target_type :string(255) +# target_type :string # created_at :datetime # updated_at :datetime # diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 86b1b7e2f9..927e764af9 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -3,11 +3,11 @@ # Table name: lfs_objects # # id :integer not null, primary key -# oid :string(255) not null +# oid :string not null # size :integer not null # created_at :datetime # updated_at :datetime -# file :string(255) +# file :string # class LfsObject < ActiveRecord::Base diff --git a/app/models/member.rb b/app/models/member.rb index 60efafef21..cca82da89f 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -5,15 +5,15 @@ # id :integer not null, primary key # access_level :integer not null # source_id :integer not null -# source_type :string(255) not null +# source_type :string not null # user_id :integer # notification_level :integer not null -# type :string(255) +# type :string # created_at :datetime # updated_at :datetime # created_by_id :integer -# invite_email :string(255) -# invite_token :string(255) +# invite_email :string +# invite_token :string # invite_accepted_at :datetime # diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index 9fb474a1a9..a48c1943e6 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -5,15 +5,15 @@ # id :integer not null, primary key # access_level :integer not null # source_id :integer not null -# source_type :string(255) not null +# source_type :string not null # user_id :integer # notification_level :integer not null -# type :string(255) +# type :string # created_at :datetime # updated_at :datetime # created_by_id :integer -# invite_email :string(255) -# invite_token :string(255) +# invite_email :string +# invite_token :string # invite_accepted_at :datetime # diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb index 07ddb02ae9..143350a0b5 100644 --- a/app/models/members/project_member.rb +++ b/app/models/members/project_member.rb @@ -5,15 +5,15 @@ # id :integer not null, primary key # access_level :integer not null # source_id :integer not null -# source_type :string(255) not null +# source_type :string not null # user_id :integer # notification_level :integer not null -# type :string(255) +# type :string # created_at :datetime # updated_at :datetime # created_by_id :integer -# invite_email :string(255) -# invite_token :string(255) +# invite_email :string +# invite_token :string # invite_accepted_at :datetime # diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index d00919c3b0..4175e1e5fb 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -3,28 +3,29 @@ # Table name: merge_requests # # id :integer not null, primary key -# target_branch :string(255) not null -# source_branch :string(255) not null +# target_branch :string not null +# source_branch :string not null # source_project_id :integer not null # author_id :integer # assignee_id :integer -# title :string(255) +# title :string # created_at :datetime # updated_at :datetime # milestone_id :integer -# state :string(255) -# merge_status :string(255) +# state :string +# merge_status :string # target_project_id :integer not null # iid :integer # description :text # position :integer default(0) # locked_at :datetime # updated_by_id :integer -# merge_error :string(255) +# merge_error :string # merge_params :text # merge_when_build_succeeds :boolean default(FALSE), not null # merge_user_id :integer # merge_commit_sha :string +# deleted_at :datetime # class MergeRequest < ActiveRecord::Base diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 0580cafdd1..8951e92a0b 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -3,12 +3,14 @@ # Table name: merge_request_diffs # # id :integer not null, primary key -# state :string(255) +# state :string # st_commits :text # st_diffs :text # merge_request_id :integer not null # created_at :datetime # updated_at :datetime +# base_commit_sha :string +# real_size :string # class MergeRequestDiff < ActiveRecord::Base diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 986184dd30..5ee8a965ad 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -3,13 +3,13 @@ # Table name: milestones # # id :integer not null, primary key -# title :string(255) not null +# title :string not null # project_id :integer not null # description :text # due_date :date # created_at :datetime # updated_at :datetime -# state :string(255) +# state :string # iid :integer # diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 55842df1e2..741e912171 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -2,15 +2,17 @@ # # Table name: namespaces # -# id :integer not null, primary key -# name :string(255) not null -# path :string(255) not null -# owner_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) -# description :string(255) default(""), not null -# avatar :string(255) +# id :integer not null, primary key +# name :string not null +# path :string not null +# owner_id :integer +# created_at :datetime +# updated_at :datetime +# type :string +# description :string default(""), not null +# avatar :string +# share_with_group_lock :boolean default(FALSE) +# visibility_level :integer default(20), not null # class Namespace < ActiveRecord::Base diff --git a/app/models/note.rb b/app/models/note.rb index 71b4293d57..deee2b9e88 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -4,14 +4,14 @@ # # id :integer not null, primary key # note :text -# noteable_type :string(255) +# noteable_type :string # author_id :integer # created_at :datetime # updated_at :datetime # project_id :integer -# attachment :string(255) -# line_code :string(255) -# commit_id :string(255) +# attachment :string +# line_code :string +# commit_id :string # noteable_id :integer # system :boolean default(FALSE), not null # st_diff :text diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb index 5001738f41..846773752a 100644 --- a/app/models/notification_setting.rb +++ b/app/models/notification_setting.rb @@ -1,3 +1,16 @@ +# == Schema Information +# +# Table name: notification_settings +# +# id :integer not null, primary key +# user_id :integer not null +# source_id :integer not null +# source_type :string not null +# level :integer default(0), not null +# created_at :datetime not null +# updated_at :datetime not null +# + class NotificationSetting < ActiveRecord::Base enum level: { disabled: 0, participating: 1, watch: 2, global: 3, mention: 4 } diff --git a/app/models/personal_snippet.rb b/app/models/personal_snippet.rb index 452f3913ee..1d5f4c5025 100644 --- a/app/models/personal_snippet.rb +++ b/app/models/personal_snippet.rb @@ -3,14 +3,14 @@ # Table name: snippets # # id :integer not null, primary key -# title :string(255) +# title :string # content :text # author_id :integer not null # project_id :integer # created_at :datetime # updated_at :datetime -# file_name :string(255) -# type :string(255) +# file_name :string +# type :string # visibility_level :integer default(0), not null # diff --git a/app/models/project.rb b/app/models/project.rb index af62e8ecd9..9403acf775 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2,41 +2,46 @@ # # Table name: projects # -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# description :text -# created_at :datetime -# updated_at :datetime -# creator_id :integer -# issues_enabled :boolean default(TRUE), not null -# wall_enabled :boolean default(TRUE), not null -# merge_requests_enabled :boolean default(TRUE), not null -# wiki_enabled :boolean default(TRUE), not null -# namespace_id :integer -# issues_tracker :string(255) default("gitlab"), not null -# issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null -# last_activity_at :datetime -# import_url :string(255) -# visibility_level :integer default(0), not null -# archived :boolean default(FALSE), not null -# avatar :string(255) -# import_status :string(255) -# repository_size :float default(0.0) -# star_count :integer default(0), not null -# import_type :string(255) -# import_source :string(255) -# commit_count :integer default(0) -# import_error :text -# ci_id :integer -# builds_enabled :boolean default(TRUE), not null -# shared_runners_enabled :boolean default(TRUE), not null -# runners_token :string -# build_coverage_regex :string -# build_allow_git_fetch :boolean default(TRUE), not null -# build_timeout :integer default(3600), not null -# pending_delete :boolean +# id :integer not null, primary key +# name :string +# path :string +# description :text +# created_at :datetime +# updated_at :datetime +# creator_id :integer +# issues_enabled :boolean default(TRUE), not null +# wall_enabled :boolean default(TRUE), not null +# merge_requests_enabled :boolean default(TRUE), not null +# wiki_enabled :boolean default(TRUE), not null +# namespace_id :integer +# issues_tracker :string default("gitlab"), not null +# issues_tracker_id :string +# snippets_enabled :boolean default(TRUE), not null +# last_activity_at :datetime +# import_url :string +# visibility_level :integer default(0), not null +# archived :boolean default(FALSE), not null +# avatar :string +# import_status :string +# repository_size :float default(0.0) +# star_count :integer default(0), not null +# import_type :string +# import_source :string +# commit_count :integer default(0) +# import_error :text +# ci_id :integer +# builds_enabled :boolean default(TRUE), not null +# shared_runners_enabled :boolean default(TRUE), not null +# runners_token :string +# build_coverage_regex :string +# build_allow_git_fetch :boolean default(TRUE), not null +# build_timeout :integer default(3600), not null +# pending_delete :boolean default(FALSE) +# public_builds :boolean default(TRUE), not null +# main_language :string +# pushes_since_gc :integer default(0) +# last_repository_check_failed :boolean +# last_repository_check_at :datetime # require 'carrierwave/orm/activerecord' diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb index e52a6bd7c8..66f5a609bf 100644 --- a/app/models/project_group_link.rb +++ b/app/models/project_group_link.rb @@ -1,3 +1,15 @@ +# == Schema Information +# +# Table name: project_group_links +# +# id :integer not null, primary key +# project_id :integer not null +# group_id :integer not null +# created_at :datetime +# updated_at :datetime +# group_access :integer default(30), not null +# + class ProjectGroupLink < ActiveRecord::Base GUEST = 10 REPORTER = 20 diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 2c0ae312f1..3f0080205d 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -2,9 +2,12 @@ # # Table name: project_import_data # -# id :integer not null, primary key -# project_id :integer -# data :text +# id :integer not null, primary key +# project_id :integer +# data :text +# encrypted_credentials :text +# encrypted_credentials_iv :text +# encrypted_credentials_salt :text # require 'carrierwave/orm/activerecord' diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index 792ad80457..368485a060 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # require 'asana' diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb index 29d841faed..ffb7455b01 100644 --- a/app/models/project_services/assembla_service.rb +++ b/app/models/project_services/assembla_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class AssemblaService < Service diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 060062aaf7..c36ee95e37 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class BambooService < CiService diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index 861cc974ec..f9f4897a06 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # require "addressable/uri" diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index 6ab6d7417b..20cdfcaffb 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class BuildsEmailService < Service diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb index 6e8f084252..28c969fe57 100644 --- a/app/models/project_services/campfire_service.rb +++ b/app/models/project_services/campfire_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class CampfireService < Service diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb index d9f0849d14..9bc8f982da 100644 --- a/app/models/project_services/ci_service.rb +++ b/app/models/project_services/ci_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # # Base class for CI services diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index 88a3e9218c..4d1319eb6f 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class CustomIssueTrackerService < IssueTrackerService diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index b4724bb647..d8e00e018c 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class DroneCiService < CiService diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb index b831577cd9..2dbd29062d 100644 --- a/app/models/project_services/emails_on_push_service.rb +++ b/app/models/project_services/emails_on_push_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class EmailsOnPushService < Service diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb index b402b68665..5469049bb5 100644 --- a/app/models/project_services/external_wiki_service.rb +++ b/app/models/project_services/external_wiki_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class ExternalWikiService < Service diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 8605ce66e4..3dc1e0fbe8 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # require "flowdock-git-hook" diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb index 61babe9cfe..b4c311cf66 100644 --- a/app/models/project_services/gemnasium_service.rb +++ b/app/models/project_services/gemnasium_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # require "gemnasium/gitlab_service" diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 33f0d7ea01..a92f722608 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # # TODO(ayufan): The GitLabCiService is deprecated and the type should be removed when the database entries are removed diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index eaa5654b9c..1adaeeb3b2 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class GitlabIssueTrackerService < IssueTrackerService diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 064ef8e867..f9ddf58872 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class HipchatService < Service diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb index 04c714bfaa..b9a592d709 100644 --- a/app/models/project_services/irker_service.rb +++ b/app/models/project_services/irker_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # require 'uri' diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index c5501e0641..98a3a7c6b8 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class IssueTrackerService < Service diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index b4418ba928..ba68658f0b 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class JiraService < IssueTrackerService diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb index c9a890c7e3..acaa0c3936 100644 --- a/app/models/project_services/pivotaltracker_service.rb +++ b/app/models/project_services/pivotaltracker_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class PivotaltrackerService < Service diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb index e76d9eca2a..a640c8cb44 100644 --- a/app/models/project_services/pushover_service.rb +++ b/app/models/project_services/pushover_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class PushoverService < Service diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb index de974354c7..e2137e92c6 100644 --- a/app/models/project_services/redmine_service.rb +++ b/app/models/project_services/redmine_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class RedmineService < IssueTrackerService diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 7092b75754..83ffa53a40 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class SlackService < Service diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index 8dceee5e2c..4015da3150 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # class TeamcityService < CiService diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb index d48f054615..b4b2807eba 100644 --- a/app/models/project_snippet.rb +++ b/app/models/project_snippet.rb @@ -3,14 +3,14 @@ # Table name: snippets # # id :integer not null, primary key -# title :string(255) +# title :string # content :text # author_id :integer not null # project_id :integer # created_at :datetime # updated_at :datetime -# file_name :string(255) -# type :string(255) +# file_name :string +# type :string # visibility_level :integer default(0), not null # diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index 8ebd790a89..3d2052c892 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -4,7 +4,7 @@ # # id :integer not null, primary key # project_id :integer not null -# name :string(255) not null +# name :string not null # created_at :datetime # updated_at :datetime # developers_can_push :boolean default(FALSE), not null diff --git a/app/models/release.rb b/app/models/release.rb index 89f70278af..dc700d1ea5 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -3,7 +3,7 @@ # Table name: releases # # id :integer not null, primary key -# tag :string(255) +# tag :string # description :text # project_id :integer # created_at :datetime diff --git a/app/models/security_event.rb b/app/models/security_event.rb index 68c00adad5..0bee03974f 100644 --- a/app/models/security_event.rb +++ b/app/models/security_event.rb @@ -4,9 +4,9 @@ # # id :integer not null, primary key # author_id :integer not null -# type :string(255) not null +# type :string not null # entity_id :integer not null -# entity_type :string(255) not null +# entity_type :string not null # details :text # created_at :datetime # updated_at :datetime diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index 77115597d7..99279a2e08 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -5,11 +5,11 @@ # id :integer not null, primary key # project_id :integer # noteable_id :integer -# noteable_type :string(255) +# noteable_type :string # recipient_id :integer -# commit_id :string(255) -# line_code :string(255) -# reply_key :string(255) not null +# commit_id :string +# reply_key :string not null +# line_code :string # class SentNotification < ActiveRecord::Base diff --git a/app/models/service.rb b/app/models/service.rb index 2645b8321d..bf16a54530 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -3,12 +3,12 @@ # Table name: services # # id :integer not null, primary key -# type :string(255) -# title :string(255) +# type :string +# title :string # project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# active :boolean not null # properties :text # template :boolean default(FALSE) # push_events :boolean default(TRUE) @@ -17,6 +17,9 @@ # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null # build_events :boolean default(FALSE), not null +# category :string default("common"), not null +# default :boolean default(FALSE) +# wiki_page_events :boolean default(TRUE) # # To add new service you should build a class inherited from Service diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 0fd0806192..2f905a9094 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -3,14 +3,14 @@ # Table name: snippets # # id :integer not null, primary key -# title :string(255) +# title :string # content :text # author_id :integer not null # project_id :integer # created_at :datetime # updated_at :datetime -# file_name :string(255) -# type :string(255) +# file_name :string +# type :string # visibility_level :integer default(0), not null # diff --git a/app/models/spam_log.rb b/app/models/spam_log.rb index 12df68ef83..f49eb7d88e 100644 --- a/app/models/spam_log.rb +++ b/app/models/spam_log.rb @@ -1,3 +1,20 @@ +# == Schema Information +# +# Table name: spam_logs +# +# id :integer not null, primary key +# user_id :integer +# source_ip :string +# user_agent :string +# via_api :boolean +# project_id :integer +# noteable_type :string +# title :string +# description :text +# created_at :datetime not null +# updated_at :datetime not null +# + class SpamLog < ActiveRecord::Base belongs_to :user diff --git a/app/models/subscription.rb b/app/models/subscription.rb index dd800ce110..242faa7d32 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -5,7 +5,7 @@ # id :integer not null, primary key # user_id :integer # subscribable_id :integer -# subscribable_type :string(255) +# subscribable_type :string # subscribed :boolean # created_at :datetime # updated_at :datetime diff --git a/app/models/user.rb b/app/models/user.rb index ab48f8f196..959b1f9375 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,55 +3,55 @@ # Table name: users # # id :integer not null, primary key -# email :string(255) default(""), not null -# encrypted_password :string(255) default(""), not null -# reset_password_token :string(255) +# email :string default(""), not null +# encrypted_password :string default(""), not null +# reset_password_token :string # reset_password_sent_at :datetime # remember_created_at :datetime # sign_in_count :integer default(0) # current_sign_in_at :datetime # last_sign_in_at :datetime -# current_sign_in_ip :string(255) -# last_sign_in_ip :string(255) +# current_sign_in_ip :string +# last_sign_in_ip :string # created_at :datetime # updated_at :datetime -# name :string(255) +# name :string # admin :boolean default(FALSE), not null # projects_limit :integer default(10) -# skype :string(255) default(""), not null -# linkedin :string(255) default(""), not null -# twitter :string(255) default(""), not null -# authentication_token :string(255) +# skype :string default(""), not null +# linkedin :string default(""), not null +# twitter :string default(""), not null +# authentication_token :string # theme_id :integer default(1), not null -# bio :string(255) +# bio :string # failed_attempts :integer default(0) # locked_at :datetime -# username :string(255) +# username :string # can_create_group :boolean default(TRUE), not null # can_create_team :boolean default(TRUE), not null -# state :string(255) +# state :string # color_scheme_id :integer default(1), not null # notification_level :integer default(1), not null # password_expires_at :datetime # created_by_id :integer # last_credential_check_at :datetime -# avatar :string(255) -# confirmation_token :string(255) +# avatar :string +# confirmation_token :string # confirmed_at :datetime # confirmation_sent_at :datetime -# unconfirmed_email :string(255) +# unconfirmed_email :string # hide_no_ssh_key :boolean default(FALSE) -# website_url :string(255) default(""), not null -# notification_email :string(255) +# website_url :string default(""), not null +# notification_email :string # hide_no_password :boolean default(FALSE) # password_automatically_set :boolean default(FALSE) -# location :string(255) -# encrypted_otp_secret :string(255) -# encrypted_otp_secret_iv :string(255) -# encrypted_otp_secret_salt :string(255) +# location :string +# encrypted_otp_secret :string +# encrypted_otp_secret_iv :string +# encrypted_otp_secret_salt :string # otp_required_for_login :boolean default(FALSE), not null # otp_backup_codes :text -# public_email :string(255) default(""), not null +# public_email :string default(""), not null # dashboard :integer default(0) # project_view :integer default(0) # consumed_timestep :integer @@ -59,7 +59,8 @@ # hide_project_limit :boolean default(FALSE) # unlock_token :string # otp_grace_period_started_at :datetime -# external :boolean default(FALSE) +# ldap_email :boolean default(FALSE), not null +# external :boolean default(FALSE) # require 'carrierwave/orm/activerecord' From 8278b763d96ef10c6494409b18b7eb541463af29 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Fri, 6 May 2016 08:27:40 +0200 Subject: [PATCH 119/138] Auto annotate models on migration [ci skip] --- lib/tasks/auto_annotate_models.rake | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 lib/tasks/auto_annotate_models.rake diff --git a/lib/tasks/auto_annotate_models.rake b/lib/tasks/auto_annotate_models.rake new file mode 100644 index 0000000000..16bad4bd2b --- /dev/null +++ b/lib/tasks/auto_annotate_models.rake @@ -0,0 +1,44 @@ +if Rails.env.development? + task :set_annotation_options do + # You can override any of these by setting an environment variable of the + # same name. + Annotate.set_defaults( + 'routes' => 'false', + 'position_in_routes' => 'before', + 'position_in_class' => 'before', + 'position_in_test' => 'before', + 'position_in_fixture' => 'before', + 'position_in_factory' => 'before', + 'position_in_serializer' => 'before', + 'show_foreign_keys' => 'true', + 'show_indexes' => 'false', + 'simple_indexes' => 'false', + 'model_dir' => 'app/models', + 'root_dir' => '', + 'include_version' => 'false', + 'require' => '', + 'exclude_tests' => 'true', + 'exclude_fixtures' => 'true', + 'exclude_factories' => 'true', + 'exclude_serializers' => 'true', + 'exclude_scaffolds' => 'true', + 'exclude_controllers' => 'true', + 'exclude_helpers' => 'true', + 'ignore_model_sub_dir' => 'false', + 'ignore_columns' => nil, + 'ignore_unknown_models' => 'false', + 'hide_limit_column_types' => 'integer,boolean', + 'skip_on_db_migrate' => 'false', + 'format_bare' => 'true', + 'format_rdoc' => 'false', + 'format_markdown' => 'false', + 'sort' => 'false', + 'force' => 'false', + 'trace' => 'false', + 'wrapper_open' => nil, + 'wrapper_close' => nil, + ) + end + + Annotate.load_tasks +end From b8b54252fde4dc636db9d41330fa910af803284e Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Fri, 6 May 2016 07:54:00 +0000 Subject: [PATCH 120/138] clarify changelog entry on GitHub Enterprise import support --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 769ffb2476..99e294c9ae 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,7 +18,7 @@ v 8.8.0 (unreleased) - Display informative message when new milestone is created - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea) - Added button to toggle whitespaces changes on diff view - - Backport GitLab Enterprise support from EE + - Backport GitHub Enterprise import support from EE - Create tags using Rugged for performance reasons. !3745 - Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718 - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes) From 76f43d5ce2e784067a04b64ea1838e1de459287d Mon Sep 17 00:00:00 2001 From: Fabio Huser Date: Sun, 1 May 2016 22:48:26 +0200 Subject: [PATCH 121/138] docs(api): adapted user API documentation to match with latest API The user API documentation and the actual implementation were out of sync, missing certain newly introduced fields and beeing inconsistent between certain API definitions. The documentation was changed according the actual latest implementation. Signed-off-by: Fabio Huser --- doc/api/users.md | 80 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/doc/api/users.md b/doc/api/users.md index 7d2b4897cf..7e848586db 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -20,6 +20,7 @@ GET /users "name": "John Smith", "state": "active", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", + "web_url": "http://localhost:3000/u/john_smith" }, { "id": 2, @@ -27,6 +28,7 @@ GET /users "name": "Jack Smith", "state": "blocked", "avatar_url": "http://gravatar.com/../e32131cd8.jpeg", + "web_url": "http://localhost:3000/u/jack_smith" } ] ``` @@ -45,21 +47,31 @@ GET /users "email": "john@example.com", "name": "John Smith", "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", + "web_url": "http://localhost:3000/u/john_smith", "created_at": "2012-05-23T08:00:58Z", + "is_admin": false, "bio": null, + "location": null, "skype": "", "linkedin": "", "twitter": "", "website_url": "", - "extern_uid": "john.smith", - "provider": "provider_name", + "last_sign_in_at": "2012-06-01T11:41:01Z", + "confirmed_at": "2012-05-23T09:05:22Z", "theme_id": 1, "color_scheme_id": 2, - "is_admin": false, - "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", + "projects_limit": 100, + "current_sign_in_at": "2012-06-02T06:36:55Z", + "identities": [ + {"provider": "github", "extern_uid": "2435223452345"}, + {"provider": "bitbucket", "extern_uid": "john.smith"}, + {"provider": "google_oauth2", "extern_uid": "8776128412476123468721346"} + ], "can_create_group": true, - "current_sign_in_at": "2014-03-19T13:12:15Z", - "two_factor_enabled": true + "can_create_project": true, + "two_factor_enabled": true, + "external": false }, { "id": 2, @@ -67,24 +79,27 @@ GET /users "email": "jack@example.com", "name": "Jack Smith", "state": "blocked", + "avatar_url": "http://localhost:3000/uploads/user/avatar/2/index.jpg", + "web_url": "http://localhost:3000/u/jack_smith", "created_at": "2012-05-23T08:01:01Z", + "is_admin": false, "bio": null, "location": null, "skype": "", "linkedin": "", "twitter": "", "website_url": "", - "extern_uid": "jack.smith", - "provider": "provider_name", + "last_sign_in_at": null, + "confirmed_at": "2012-05-30T16:53:06.148Z", "theme_id": 1, "color_scheme_id": 3, - "is_admin": false, - "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", - "can_create_group": true, - "can_create_project": true, "projects_limit": 100, "current_sign_in_at": "2014-03-19T17:54:13Z", - "two_factor_enabled": false + "identities": [], + "can_create_group": true, + "can_create_project": true, + "two_factor_enabled": true, + "external": false } ] ``` @@ -124,6 +139,7 @@ Parameters: "name": "John Smith", "state": "active", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", + "web_url": "http://localhost:3000/u/john_smith", "created_at": "2012-05-23T08:00:58Z", "is_admin": false, "bio": null, @@ -152,23 +168,31 @@ Parameters: "email": "john@example.com", "name": "John Smith", "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", + "web_url": "http://localhost:3000/u/john_smith", "created_at": "2012-05-23T08:00:58Z", - "confirmed_at": "2012-05-23T08:00:58Z", - "last_sign_in_at": "2015-03-23T08:00:58Z", + "is_admin": false, "bio": null, "location": null, "skype": "", "linkedin": "", "twitter": "", "website_url": "", - "extern_uid": "john.smith", - "provider": "provider_name", + "last_sign_in_at": "2012-06-01T11:41:01Z", + "confirmed_at": "2012-05-23T09:05:22Z", "theme_id": 1, "color_scheme_id": 2, - "is_admin": false, + "projects_limit": 100, + "current_sign_in_at": "2012-06-02T06:36:55Z", + "identities": [ + {"provider": "github", "extern_uid": "2435223452345"}, + {"provider": "bitbucket", "extern_uid": "john.smith"}, + {"provider": "google_oauth2", "extern_uid": "8776128412476123468721346"} + ], "can_create_group": true, "can_create_project": true, - "projects_limit": 100 + "two_factor_enabled": true, + "external": false } ``` @@ -261,21 +285,33 @@ GET /user "username": "john_smith", "email": "john@example.com", "name": "John Smith", - "private_token": "dd34asd13as", "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", + "web_url": "http://localhost:3000/u/john_smith", "created_at": "2012-05-23T08:00:58Z", + "is_admin": false, "bio": null, "location": null, "skype": "", "linkedin": "", "twitter": "", "website_url": "", + "last_sign_in_at": "2012-06-01T11:41:01Z", + "confirmed_at": "2012-05-23T09:05:22Z", "theme_id": 1, "color_scheme_id": 2, - "is_admin": false, + "projects_limit": 100, + "current_sign_in_at": "2012-06-02T06:36:55Z", + "identities": [ + {"provider": "github", "extern_uid": "2435223452345"}, + {"provider": "bitbucket", "extern_uid": "john_smith"}, + {"provider": "google_oauth2", "extern_uid": "8776128412476123468721346"} + ], "can_create_group": true, "can_create_project": true, - "projects_limit": 100 + "two_factor_enabled": true, + "external": false, + "private_token": "dd34asd13as" } ``` From 4ab49fab94284f3f7e3804309ba8825ed28fc24c Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Fri, 22 Apr 2016 19:23:21 +0900 Subject: [PATCH 122/138] Use outer join for issues ordering by milestones due. --- app/models/concerns/issuable.rb | 5 +++-- spec/features/issues_spec.rb | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index d5166e8147..2e4efc4e8d 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -35,13 +35,14 @@ module Issuable scope :only_opened, -> { with_state(:opened) } scope :only_reopened, -> { with_state(:reopened) } scope :closed, -> { with_state(:closed) } - 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 :order_milestone_due_desc, -> { outer_join_milestone.reorder('milestones.due_date IS NULL ASC, milestones.due_date DESC, milestones.id DESC') } + scope :order_milestone_due_asc, -> { outer_join_milestone.reorder('milestones.due_date IS NULL ASC, milestones.due_date ASC, milestones.id ASC') } scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :join_project, -> { joins(:project) } scope :references_project, -> { references(:project) } scope :non_archived, -> { join_project.where(projects: { archived: false }) } + scope :outer_join_milestone, -> { joins("LEFT OUTER JOIN milestones ON milestones.id = #{table_name}.milestone_id") } delegate :name, :email, diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index b57131f68d..d5755c293c 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -264,12 +264,14 @@ describe 'Issues', feature: true do visit namespace_project_issues_path(project.namespace, project, sort: sort_value_milestone_soon) expect(first_issue).to include('foo') + expect(last_issue).to include('baz') end it 'sorts by least recently due milestone' do visit namespace_project_issues_path(project.namespace, project, sort: sort_value_milestone_later) expect(first_issue).to include('bar') + expect(last_issue).to include('baz') end end From 0a38eac16e1d86d094c98c12a38702ffe88cd527 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Wed, 4 May 2016 21:07:07 +0900 Subject: [PATCH 123/138] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 5b907fa217..78d6c1ec2b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -28,6 +28,7 @@ v 8.8.0 (unreleased) - API support for the 'since' and 'until' operators on commit requests (Paco Guzman) - Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko) - Expire repository exists? and has_visible_content? caches after a push if necessary + - Fix unintentional filtering bug in issues sorted by milestone due (Takuya Noguchi) v 8.7.3 - Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented From 13d4d3c8b0df93dd51228db79265179cb978ff29 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Thu, 28 Apr 2016 10:45:45 +0100 Subject: [PATCH 124/138] Add specs for MergeRequests::BuildService --- .../merge_requests/build_service_spec.rb | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 spec/services/merge_requests/build_service_spec.rb diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb new file mode 100644 index 0000000000..7ad13478dd --- /dev/null +++ b/spec/services/merge_requests/build_service_spec.rb @@ -0,0 +1,146 @@ +require 'spec_helper' + +describe MergeRequests::BuildService, services: true do + include RepoHelpers + + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:issue) { create(:issue, project: project) } + let(:description) { nil } + let(:source_branch) { 'feature-branch' } + let(:target_branch) { 'master' } + let(:merge_request) { service.execute } + let(:compare) { double(:compare, commits: commits) } + let(:commit_1) { double(:commit_1, safe_message: "Initial commit\n\nCreate the app") } + let(:commit_2) { double(:commit_2, safe_message: 'This is a bad commit message!') } + let(:commits) { nil } + + let(:service) do + MergeRequests::BuildService.new(project, user, + description: description, + source_branch: source_branch, + target_branch: target_branch) + end + + before do + allow(CompareService).to receive_message_chain(:new, :execute).and_return(compare) + end + + describe 'execute' do + context 'missing source branch' do + let(:source_branch) { '' } + + it 'forbids the merge request from being created' do + expect(merge_request.can_be_created).to eq(false) + end + + it 'adds an error message to the merge request' do + expect(merge_request.errors).to contain_exactly('You must select source and target branch') + end + end + + context 'missing target branch' do + let(:target_branch) { '' } + + it 'forbids the merge request from being created' do + expect(merge_request.can_be_created).to eq(false) + end + + it 'adds an error message to the merge request' do + expect(merge_request.errors).to contain_exactly('You must select source and target branch') + end + end + + context 'no commits in the diff' do + let(:commits) { [] } + + it 'forbids the merge request from being created' do + expect(merge_request.can_be_created).to eq(false) + end + end + + context 'one commit in the diff' do + let(:commits) { [commit_1] } + + it 'allows the merge request to be created' do + expect(merge_request.can_be_created).to eq(true) + end + + it 'uses the title of the commit as the title of the merge request' do + expect(merge_request.title).to eq(commit_1.safe_message.split("\n").first) + end + + it 'uses the description of the commit as the description of the merge request' do + expect(merge_request.description).to eq(commit_1.safe_message.split(/\n+/, 2).last) + end + + context 'merge request already has a description set' do + let(:description) { 'Merge request description' } + + it 'keeps the description from the initial params' do + expect(merge_request.description).to eq(description) + end + end + + context 'commit has no description' do + let(:commits) { [commit_2] } + + it 'uses the title of the commit as the title of the merge request' do + expect(merge_request.title).to eq(commit_2.safe_message) + end + + it 'sets the description to nil' do + expect(merge_request.description).to be_nil + end + end + + context 'branch starts with issue IID followed by a hyphen' do + let(:source_branch) { "#{issue.iid}-fix-issue" } + + it 'appends "Closes #$issue-iid" to the description' do + expect(merge_request.description).to eq("#{commit_1.safe_message.split(/\n+/, 2).last}\nCloses ##{issue.iid}") + end + + context 'merge request already has a description set' do + let(:description) { 'Merge request description' } + + it 'appends "Closes #$issue-iid" to the description' do + expect(merge_request.description).to eq("#{description}\nCloses ##{issue.iid}") + end + end + + context 'commit has no description' do + let(:commits) { [commit_2] } + + it 'sets the description to "Closes #$issue-iid"' do + expect(merge_request.description).to eq("Closes ##{issue.iid}") + end + end + end + end + + context 'more than one commit in the diff' do + let(:commits) { [commit_1, commit_2] } + + it 'allows the merge request to be created' do + expect(merge_request.can_be_created).to eq(true) + end + + it 'uses the title of the branch as the merge request title' do + expect(merge_request.title).to eq('Feature branch') + end + + it 'does not add a description' do + expect(merge_request.description).to be_nil + end + + context 'merge request already has a description set' do + let(:description) { 'Merge request description' } + + it 'keeps the description from the initial params' do + expect(merge_request.description).to eq(description) + end + end + end + end +end From e76f339dcd83799a63d206007750077b7af753f4 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Thu, 28 Apr 2016 11:36:37 +0100 Subject: [PATCH 125/138] Auto-set title for branches created from issues If a branch starts with an issue's IID, followed by a hyphen, the description will be updated to say that is closes the issue. This also updates the title of the merge request to 'Resolves "$issue-title"', as long as: - There is more than one commit in the merge request (if there is only one commit, the commit's title will be used as before) - The issue's IID is valid for the project --- app/services/merge_requests/build_service.rb | 36 +++++++++++++++---- .../merge_requests/build_service_spec.rb | 28 ++++++++++++++- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index 3544752d47..4add1d66ea 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -41,21 +41,45 @@ module MergeRequests merge_request.can_be_created = false end + set_title_and_description(merge_request) + end + + private + + # When your branch name starts with an iid followed by a dash this pattern will be + # interpreted as the user wants to close that issue on this project. + # + # For example: + # - Issue 112 exists, title: Emoji don't show up in commit title + # - Source branch is: 112-fix-mep-mep + # + # Will lead to: + # - Appending `Closes #112` to the description + # - Setting the title as 'Resolves "Emoji don't show up in commit title"' if there is + # more than one commit in the MR + # + def set_title_and_description(merge_request) + if match = merge_request.source_branch.match(/\A(\d+)-/) + iid = match[1] + end + commits = merge_request.compare_commits if commits && commits.count == 1 commit = commits.first merge_request.title = commit.title merge_request.description ||= commit.description.try(:strip) + elsif iid && (issue = merge_request.target_project.get_issue(iid)) + case issue + when Issue + merge_request.title = "Resolve \"#{issue.title}\"" + when ExternalIssue + merge_request.title = "Resolve #{issue.title}" + end else merge_request.title = merge_request.source_branch.titleize.humanize end - # When your branch name starts with an iid followed by a dash this pattern will - # be interpreted as the use wants to close that issue on this project - # Pattern example: 112-fix-mep-mep - # Will lead to appending `Closes #112` to the description - if match = merge_request.source_branch.match(/\A(\d+)-/) - iid = match[1] + if iid closes_issue = "Closes ##{iid}" if merge_request.description.present? diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index 7ad13478dd..f8b768a1a9 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -5,7 +5,7 @@ describe MergeRequests::BuildService, services: true do let(:project) { create(:project) } let(:user) { create(:user) } - let(:issue) { create(:issue, project: project) } + let(:issue) { create(:issue, project: project, title: 'A bug') } let(:description) { nil } let(:source_branch) { 'feature-branch' } let(:target_branch) { 'master' } @@ -141,6 +141,32 @@ describe MergeRequests::BuildService, services: true do expect(merge_request.description).to eq(description) end end + + context 'branch starts with GitLab issue IID followed by a hyphen' do + let(:source_branch) { "#{issue.iid}-fix-issue" } + + it 'sets the title to: Resolves "$issue-title"' do + expect(merge_request.title).to eq("Resolve \"#{issue.title}\"") + end + + context 'issue does not exist' do + let(:source_branch) { "#{issue.iid.succ}-fix-issue" } + + it 'uses the title of the branch as the merge request title' do + expect(merge_request.title).to eq("#{issue.iid.succ} fix issue") + end + end + end + + context 'branch starts with external issue IID followed by a hyphen' do + let(:source_branch) { '12345-fix-issue' } + + before { allow(project).to receive(:default_issues_tracker?).and_return(false) } + + it 'sets the title to: Resolves External Issue $issue-iid' do + expect(merge_request.title).to eq('Resolve External Issue 12345') + end + end end end end From 09209725cee58b3d9645b8cf58ec955b566f5b5b Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Thu, 28 Apr 2016 12:12:03 +0100 Subject: [PATCH 126/138] Don't auto-set MR title for confidential issues --- app/services/merge_requests/build_service.rb | 2 +- spec/services/merge_requests/build_service_spec.rb | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index 4add1d66ea..cd4230aa5e 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -68,7 +68,7 @@ module MergeRequests commit = commits.first merge_request.title = commit.title merge_request.description ||= commit.description.try(:strip) - elsif iid && (issue = merge_request.target_project.get_issue(iid)) + elsif iid && (issue = merge_request.target_project.get_issue(iid)) && !issue.try(:confidential?) case issue when Issue merge_request.title = "Resolve \"#{issue.title}\"" diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index f8b768a1a9..782d74ec5e 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -5,7 +5,8 @@ describe MergeRequests::BuildService, services: true do let(:project) { create(:project) } let(:user) { create(:user) } - let(:issue) { create(:issue, project: project, title: 'A bug') } + let(:issue_confidential) { false } + let(:issue) { create(:issue, project: project, title: 'A bug', confidential: issue_confidential) } let(:description) { nil } let(:source_branch) { 'feature-branch' } let(:target_branch) { 'master' } @@ -156,6 +157,14 @@ describe MergeRequests::BuildService, services: true do expect(merge_request.title).to eq("#{issue.iid.succ} fix issue") end end + + context 'issue is confidential' do + let(:issue_confidential) { true } + + it 'uses the title of the branch as the merge request title' do + expect(merge_request.title).to eq("#{issue.iid} fix issue") + end + end end context 'branch starts with external issue IID followed by a hyphen' do From 2dcd3f29ddf6d54468f3e712166d9504aa356f6f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 May 2016 16:13:35 +0200 Subject: [PATCH 127/138] Annotate models Signed-off-by: Dmitriy Zaporozhets --- app/models/hooks/project_hook.rb | 1 + app/models/hooks/service_hook.rb | 1 + app/models/hooks/system_hook.rb | 1 + app/models/hooks/web_hook.rb | 1 + app/models/project_import_data.rb | 4 ++-- 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index 64f570b12a..2b8f34a056 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -17,6 +17,7 @@ # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null # wiki_page_events :boolean default(FALSE), not null +# token :string # class ProjectHook < WebHook diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb index 23f6468ee5..0e176de5ef 100644 --- a/app/models/hooks/service_hook.rb +++ b/app/models/hooks/service_hook.rb @@ -17,6 +17,7 @@ # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null # wiki_page_events :boolean default(FALSE), not null +# token :string # class ServiceHook < WebHook diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index 5e9046f362..ad508cbbcb 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -17,6 +17,7 @@ # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null # wiki_page_events :boolean default(FALSE), not null +# token :string # class SystemHook < WebHook diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index a8f17ac16a..8e58c9583a 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -17,6 +17,7 @@ # enable_ssl_verification :boolean default(TRUE) # build_events :boolean default(FALSE), not null # wiki_page_events :boolean default(FALSE), not null +# token :string # class WebHook < ActiveRecord::Base diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 3f0080205d..7830f764ed 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -6,8 +6,8 @@ # project_id :integer # data :text # encrypted_credentials :text -# encrypted_credentials_iv :text -# encrypted_credentials_salt :text +# encrypted_credentials_iv :string +# encrypted_credentials_salt :string # require 'carrierwave/orm/activerecord' From 0aba37cfd192234eba518cadf002608e15edd1e0 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Fri, 6 May 2016 09:21:04 -0500 Subject: [PATCH 128/138] Align code within links in comments --- app/assets/stylesheets/pages/notes.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 50ca755bcb..624c8249f7 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -168,6 +168,11 @@ ul.notes { .notes { background-color: $white-light; } + + a code { + top: 0; + margin-right: 0; + } } } } From 235d70da73c5538b48278deb971ecda8667af68f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 May 2016 16:32:11 +0200 Subject: [PATCH 129/138] Update db/schema.rb after running db:migrate:reset Signed-off-by: Dmitriy Zaporozhets --- db/schema.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 04aee737e4..7683aad968 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -70,16 +70,16 @@ ActiveRecord::Schema.define(version: 20160421130527) do t.string "recaptcha_site_key" t.string "recaptcha_private_key" t.integer "metrics_port", default: 8089 + t.boolean "akismet_enabled", default: false + t.string "akismet_api_key" t.integer "metrics_sample_interval", default: 15 t.boolean "sentry_enabled", default: false t.string "sentry_dsn" - t.boolean "akismet_enabled", default: false - t.string "akismet_api_key" t.boolean "email_author_in_body", default: false t.integer "default_group_visibility" t.boolean "repository_checks_enabled", default: false - t.integer "metrics_packet_size", default: 1 t.text "shared_runners_text" + t.integer "metrics_packet_size", default: 1 end create_table "audit_events", force: :cascade do |t| @@ -426,10 +426,10 @@ ActiveRecord::Schema.define(version: 20160421130527) do t.string "state" t.integer "iid" t.integer "updated_by_id" - t.integer "moved_to_id" t.boolean "confidential", default: false t.datetime "deleted_at" t.date "due_date" + t.integer "moved_to_id" end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree @@ -716,8 +716,8 @@ ActiveRecord::Schema.define(version: 20160421130527) do t.integer "project_id" t.text "data" t.text "encrypted_credentials" - t.text "encrypted_credentials_iv" - t.text "encrypted_credentials_salt" + t.string "encrypted_credentials_iv" + t.string "encrypted_credentials_salt" end create_table "projects", force: :cascade do |t| @@ -816,9 +816,9 @@ ActiveRecord::Schema.define(version: 20160421130527) do t.string "type" t.string "title" t.integer "project_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "active", null: false + t.datetime "created_at" + t.datetime "updated_at" + t.boolean "active", default: false, null: false t.text "properties" t.boolean "template", default: false t.boolean "push_events", default: true From 64ef2e0834fe9320191286052f03689f9080c2ac Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 May 2016 17:33:32 +0200 Subject: [PATCH 130/138] Move group settings navigation to own partial Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/nav/_group.html.haml | 21 +--------- .../layouts/nav/_group_settings.html.haml | 40 +++++++++---------- 2 files changed, 21 insertions(+), 40 deletions(-) diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index dff188e0b1..3438005863 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,23 +1,4 @@ -- if current_user - - if access = @group.users.find_by(id: current_user.id) - .controls - %span.dropdown.group-settings-dropdown - %a.dropdown-new.btn.btn-gray#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} - = icon('cog') - = icon('caret-down') - %ul.dropdown-menu.dropdown-menu-align-right - - if can?(current_user, :admin_group, @group) - = nav_link(path: 'groups#projects') do - = link_to projects_group_path(@group), title: 'Projects' do - Projects - %li.divider - %li - = link_to edit_group_path(@group) do - Edit Group - %li - = link_to leave_group_group_members_path(@group), - data: { confirm: leave_group_message(@group.name) }, method: :delete, title: 'Leave group' do - Leave Group += render 'layouts/nav/group_settings' %ul.nav-links = nav_link(path: 'groups#show', html_options: {class: 'home'}) do diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml index 56a92fe910..f6ba1b75a5 100644 --- a/app/views/layouts/nav/_group_settings.html.haml +++ b/app/views/layouts/nav/_group_settings.html.haml @@ -1,20 +1,20 @@ -%ul.nav.nav-sidebar - = nav_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 - - %li.separate-item - - %ul.sidebar-subnav - = nav_link(path: 'groups#edit') 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' do - = icon('folder fw') - %span - Projects +- if current_user + - if access = @group.users.find_by(id: current_user.id) + .controls + %span.dropdown.group-settings-dropdown + %a.dropdown-new.btn.btn-gray#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} + = icon('cog') + = icon('caret-down') + %ul.dropdown-menu.dropdown-menu-align-right + - if can?(current_user, :admin_group, @group) + = nav_link(path: 'groups#projects') do + = link_to projects_group_path(@group), title: 'Projects' do + Projects + %li.divider + %li + = link_to edit_group_path(@group) do + Edit Group + %li + = link_to leave_group_group_members_path(@group), + data: { confirm: leave_group_message(@group.name) }, method: :delete, title: 'Leave group' do + Leave Group From 510b10583c5b40d544c878adde16b43a9060c0b4 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Fri, 6 May 2016 11:47:43 -0400 Subject: [PATCH 131/138] Add nice new Todo bell --- app/assets/stylesheets/framework/variables.scss | 1 + app/assets/stylesheets/pages/todos.scss | 9 ++++++++- app/views/layouts/header/_default.html.haml | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index e4a7339491..ccb4e5381b 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -77,6 +77,7 @@ $provider-btn-group-border: #e5e5e5; $provider-btn-not-active-color: #4688f1; $link-underline-blue: #4a8bee; $layout-link-gray: #7e7c7c; +$todo-alert-blue: #428bca; /* * Color schema diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index 75f78569e3..e51c3491da 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -6,9 +6,16 @@ .navbar-nav { li { .badge.todos-pending-count { - background-color: $gl-icon-color; margin-top: -5px; font-weight: normal; + background: $todo-alert-blue; + margin-left: -17px; + font-size: 11px; + color: white; + padding: 3px; + padding-top: 1px; + padding-bottom: 1px; + border-radius: 3px; } } } diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index cde9e1b918..86930d4eaa 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -23,6 +23,7 @@ = icon('wrench fw') %li = link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = icon('bell fw') %span.badge.todos-pending-count = todos_pending_count - if current_user.can_create_project? From dd03cfd1367c6876ac6b4d38cbc8e607e7cd9bf0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 May 2016 21:05:57 +0200 Subject: [PATCH 132/138] Make group settings button white instead of gray for better visibility Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/nav/_group_settings.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml index f6ba1b75a5..e391ec7f2b 100644 --- a/app/views/layouts/nav/_group_settings.html.haml +++ b/app/views/layouts/nav/_group_settings.html.haml @@ -2,7 +2,7 @@ - if access = @group.users.find_by(id: current_user.id) .controls %span.dropdown.group-settings-dropdown - %a.dropdown-new.btn.btn-gray#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} + %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} = icon('cog') = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right From f4bde77529169ab8a48f16ec9c1dd4dcc817e578 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Fri, 6 May 2016 21:52:29 +0200 Subject: [PATCH 133/138] We're hiring. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c1a29c3bb1..5e41665a6b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ To see how GitLab looks please see the [features page on our website](https://ab - Completely free and open source (MIT Expat license) - Powered by [Ruby on Rails](https://github.com/rails/rails) +## Hiring + +We're hiring developers, support people, and production engineers all the time, please see our [jobs page](https://about.gitlab.com/jobs/). + ## Editions There are two editions of GitLab: From dd4fe8e9bbdd1f9874f20fbbd481bf56158f4f0c Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sat, 7 May 2016 13:09:27 +0900 Subject: [PATCH 134/138] Update CI example docs. --- doc/ci/examples/README.md | 2 +- .../test-and-deploy-python-application-to-heroku.md | 10 +++++----- .../test-and-deploy-ruby-application-to-heroku.md | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index cc059dc437..31760d872b 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -4,7 +4,7 @@ - [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md) - [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md) - [Test a Clojure application](test-clojure-application.md) -- [Using `dpl` as deployment tool](deployment/README.md) +- [Using `dpl` as deployment tool](../deployment/README.md) - Help your favorite programming language and GitLab by sending a merge request with a guide for that language. diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md index a236da53fe..e4d3970dea 100644 --- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md +++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md @@ -8,7 +8,7 @@ This is what the `.gitlab-ci.yml` file looks like for this project: ```yaml test: script: - # this configures django application to use attached postgres database that is run on `postgres` host + # this configures Django application to use attached postgres database that is run on `postgres` host - export DATABASE_URL=postgres://postgres:@postgres:5432/python-test-app - apt-get update -qy - apt-get install -y python-dev python-pip @@ -37,7 +37,7 @@ production: ``` This project has three jobs: -1. `test` - used to test rails application, +1. `test` - used to test Django application, 2. `staging` - used to automatically deploy staging environment every push to `master` branch 3. `production` - used to automatically deploy production environmnet for every created tag @@ -61,12 +61,12 @@ gitlab-ci-multi-runner register \ --non-interactive \ --url "https://gitlab.com/ci/" \ --registration-token "PROJECT_REGISTRATION_TOKEN" \ - --description "python-3.2" \ + --description "python-3.5" \ --executor "docker" \ - --docker-image python:3.2 \ + --docker-image python:3.5 \ --docker-postgres latest ``` -With the command above, you create a runner that uses [python:3.2](https://registry.hub.docker.com/u/library/python/) image and uses [postgres](https://registry.hub.docker.com/u/library/postgres/) database. +With the command above, you create a runner that uses [python:3.5](https://hub.docker.com/r/_/python/) image and uses [postgres](https://hub.docker.com/r/_/postgres/) database. To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password. diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md index f5645d586a..08c10d391e 100644 --- a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md +++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md @@ -1,5 +1,5 @@ ## Test and Deploy a ruby application -This example will guide you how to run tests in your Ruby application and deploy it automatically as Heroku application. +This example will guide you how to run tests in your Ruby on Rails application and deploy it automatically as Heroku application. You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://gitlab.com/ayufan/ruby-getting-started/builds?scope=all). @@ -32,7 +32,7 @@ production: ``` This project has three jobs: -1. `test` - used to test rails application, +1. `test` - used to test Rails application, 2. `staging` - used to automatically deploy staging environment every push to `master` branch 3. `production` - used to automatically deploy production environmnet for every created tag @@ -62,6 +62,6 @@ gitlab-ci-multi-runner register \ --docker-postgres latest ``` -With the command above, you create a runner that uses [ruby:2.2](https://registry.hub.docker.com/u/library/ruby/) image and uses [postgres](https://registry.hub.docker.com/u/library/postgres/) database. +With the command above, you create a runner that uses [ruby:2.2](https://hub.docker.com/r/_/ruby/) image and uses [postgres](https://hub.docker.com/r/_/postgres/) database. To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password. From 95221312599a88b2f7898a22b2fa5b47d3b61f8c Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sat, 7 May 2016 13:29:24 +0900 Subject: [PATCH 135/138] Update Docker Hub links. --- doc/ci/docker/using_docker_images.md | 6 +++--- doc/ci/examples/php.md | 6 +++--- doc/ci/services/mysql.md | 4 ++-- doc/ci/services/postgres.md | 2 +- doc/ci/services/redis.md | 2 +- docker/README.md | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 84212fb3c6..ab7369ce97 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -273,7 +273,7 @@ creation. [Docker Fundamentals]: https://docs.docker.com/engine/understanding-docker/ [hub]: https://hub.docker.com/ [linking-containers]: https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/ -[tutum/wordpress]: https://registry.hub.docker.com/u/tutum/wordpress/ -[postgres-hub]: https://registry.hub.docker.com/u/library/postgres/ -[mysql-hub]: https://registry.hub.docker.com/u/library/mysql/ +[tutum/wordpress]: https://hub.docker.com/r/tutum/wordpress/ +[postgres-hub]: https://hub.docker.com/r/_/postgres/ +[mysql-hub]: https://hub.docker.com/r/_/mysql/ [runner-priv-reg]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/configuration/advanced-configuration.md#using-a-private-docker-registry diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md index db07792712..2695301450 100644 --- a/doc/ci/examples/php.md +++ b/doc/ci/examples/php.md @@ -60,7 +60,7 @@ docker-php-ext-install pdo_mysql You might wonder what `docker-php-ext-install` is. In short, it is a script provided by the official php docker image that you can use to easilly install extensions. For more information read the the documentation at -. +. Now that we created the script that contains all prerequisites for our build environment, let's add it in `.gitlab-ci.yml`: @@ -92,7 +92,7 @@ Finally, commit your files and push them to GitLab to see your build succeeding The final `.gitlab-ci.yml` should look similar to this: ```yaml -# Select image from https://hub.docker.com/_/php/ +# Select image from https://hub.docker.com/r/_/php/ image: php:5.6 before_script: @@ -278,7 +278,7 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available Want to hack on it? Simply fork it, commit and push your changes. Within a few moments the changes will be picked by a public runner and the build will begin. -[php-hub]: https://hub.docker.com/_/php/ +[php-hub]: https://hub.docker.com/r/_/php/ [phpenv]: https://github.com/phpenv/phpenv [phpenv-installation]: https://github.com/phpenv/phpenv#installation [php-example-repo]: https://gitlab.com/gitlab-examples/php diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md index c66d77122b..aaf3aa7783 100644 --- a/doc/ci/services/mysql.md +++ b/doc/ci/services/mysql.md @@ -16,7 +16,7 @@ services: - mysql:latest variables: - # Configure mysql environment variables (https://hub.docker.com/_/mysql/) + # Configure mysql environment variables (https://hub.docker.com/r/_/mysql/) MYSQL_DATABASE: el_duderino MYSQL_ROOT_PASSWORD: mysql_strong_password ``` @@ -114,5 +114,5 @@ available [shared runners](../runners/README.md). Want to hack on it? Simply fork it, commit and push your changes. Within a few moments the changes will be picked by a public runner and the build will begin. -[hub-mysql]: https://hub.docker.com/_/mysql/ +[hub-mysql]: https://hub.docker.com/r/_/mysql/ [mysql-example-repo]: https://gitlab.com/gitlab-examples/mysql diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md index 17d21dbda1..f787cc0a12 100644 --- a/doc/ci/services/postgres.md +++ b/doc/ci/services/postgres.md @@ -110,5 +110,5 @@ available [shared runners](../runners/README.md). Want to hack on it? Simply fork it, commit and push your changes. Within a few moments the changes will be picked by a public runner and the build will begin. -[hub-pg]: https://hub.docker.com/_/postgres/ +[hub-pg]: https://hub.docker.com/r/_/postgres/ [postgres-example-repo]: https://gitlab.com/gitlab-examples/postgres diff --git a/doc/ci/services/redis.md b/doc/ci/services/redis.md index b281e8f9f6..80705024d2 100644 --- a/doc/ci/services/redis.md +++ b/doc/ci/services/redis.md @@ -65,5 +65,5 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available Want to hack on it? Simply fork it, commit and push your changes. Within a few moments the changes will be picked by a public runner and the build will begin. -[hub-redis]: https://hub.docker.com/_/redis/ +[hub-redis]: https://hub.docker.com/r/_/redis/ [redis-example-repo]: https://gitlab.com/gitlab-examples/redis diff --git a/docker/README.md b/docker/README.md index 7514d610ae..ee1f32adc2 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,7 +1,7 @@ # GitLab Docker images -* The official GitLab Community Edition Docker image is [available on Docker Hub](https://registry.hub.docker.com/u/gitlab/gitlab-ce/). -* The official GitLab Enterprise Edition Docker image is [available on Docker Hub](https://registry.hub.docker.com/u/gitlab/gitlab-ee/). +* The official GitLab Community Edition Docker image is [available on Docker Hub](https://hub.docker.com/r/gitlab/gitlab-ce/). +* The official GitLab Enterprise Edition Docker image is [available on Docker Hub](https://hub.docker.com/r/gitlab/gitlab-ee/). * The complete usage guide can be found in [Using GitLab Docker images](http://doc.gitlab.com/omnibus/docker/) * The Dockerfile used for building public images is in [Omnibus Repository](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master/docker) -* Check the guide for [creating Omnibus-based Docker Image](http://doc.gitlab.com/omnibus/build/README.html#Build-Docker-image) +* Check the guide for [creating Omnibus-based Docker Image](http://doc.gitlab.com/omnibus/build/README.html#build-docker-image) From 3c8265559a6965411c5c155a15fae4f2a822a532 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sat, 7 May 2016 14:54:36 +0900 Subject: [PATCH 136/138] Rename Docker with Docker Engine. --- doc/ci/docker/using_docker_build.md | 6 +++--- doc/ci/docker/using_docker_images.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 5fb086b1dd..ca52a483a5 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -41,9 +41,9 @@ GitLab Runner then executes build scripts as `gitlab-runner` user. --description "My Runner" ``` -2. Install Docker on server. +2. Install Docker Engine on server. - For more information how to install Docker on different systems checkout the [Supported installations](https://docs.docker.com/installation/). + For more information how to install Docker Engine on different systems checkout the [Supported installations](https://docs.docker.com/engine/installation/). 3. Add `gitlab-runner` user to `docker` group: @@ -151,4 +151,4 @@ In order to do that follow the steps: An example project using this approach can be found here: https://gitlab.com/gitlab-examples/docker. [docker-in-docker]: https://blog.docker.com/2013/09/docker-can-now-run-within-docker/ -[docker-cap]: https://docs.docker.com/reference/run/#runtime-privilege-and-linux-capabilities +[docker-cap]: https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index ab7369ce97..56ac2195c4 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -64,7 +64,7 @@ You can see some widely used services examples in the relevant documentation of ### How is service linked to the build To better understand how the container linking works, read -[Linking containers together](https://docs.docker.com/userguide/dockerlinks/). +[Linking containers together][linking-containers]. To summarize, if you add `mysql` as service to your application, the image will then be used to create a container that is linked to the build container. From 21d89d0286e385d6d0a4debdbf7c801939c3e279 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 24 Mar 2016 22:39:58 -0700 Subject: [PATCH 137/138] Update SVG sanitizer to conform to SVG 1.1 Use a custom Loofah scrubber since sanitize 2.x transformers are inadequate to handle case-sensitive SVG attributes. sanitize parses documents as HTML instead of XML, which causes all SVG attribute names (e.g. viewBox) to be downcased. * SVG element list: https://www.w3.org/TR/SVG/eltindex.html * SVG attribute list: https://www.w3.org/TR/SVG/attindex.html Closes #14555 --- CHANGELOG | 1 + app/helpers/blob_helper.rb | 2 +- lib/gitlab/sanitizers/svg.rb | 37 +++++++++ lib/gitlab/sanitizers/svg/whitelist.rb | 107 +++++++++++++++++++++++++ spec/fixtures/sanitized.svg | 50 ++++++++++++ spec/fixtures/unsanitized.svg | 50 ++++++++++++ spec/helpers/blob_helper_spec.rb | 12 +++ 7 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/sanitizers/svg.rb create mode 100644 lib/gitlab/sanitizers/svg/whitelist.rb create mode 100644 spec/fixtures/sanitized.svg create mode 100644 spec/fixtures/unsanitized.svg diff --git a/CHANGELOG b/CHANGELOG index 78d6c1ec2b..55de253324 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ v 8.8.0 (unreleased) - Use ActionDispatch Remote IP for Akismet checking - Fix error when visiting commit builds page before build was updated - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project + - Update SVG sanitizer to conform to SVG 1.1 - Updated search UI - Display informative message when new milestone is created - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 474c6f2737..93241b3afb 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -131,7 +131,7 @@ module BlobHelper # elements and attributes. Note that this whitelist is by no means complete # and may omit some elements. def sanitize_svg(blob) - blob.data = Loofah.scrub_fragment(blob.data, :strip).to_xml + blob.data = Gitlab::Sanitizers::SVG.clean(blob.data) blob end diff --git a/lib/gitlab/sanitizers/svg.rb b/lib/gitlab/sanitizers/svg.rb new file mode 100644 index 0000000000..b98589dff8 --- /dev/null +++ b/lib/gitlab/sanitizers/svg.rb @@ -0,0 +1,37 @@ +require_relative "svg/whitelist" + +module Gitlab + module Sanitizers + module SVG + def self.clean(data) + Loofah.xml_document(data).scrub!(Scrubber.new).to_s + end + + class Scrubber < Loofah::Scrubber + # http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#embedding-custom-non-visible-data-with-the-data-*-attributes + DATA_ATTR_PATTERN = /\Adata-(?!xml)[a-z_][\w.\u00E0-\u00F6\u00F8-\u017F\u01DD-\u02AF-]*\z/u + + def scrub(node) + unless ALLOWED_ELEMENTS.include?(node.name) + node.unlink + else + node.attributes.each do |attr_name, attr| + valid_attributes = ALLOWED_ATTRIBUTES[node.name] + + unless valid_attributes && valid_attributes.include?(attr_name) + if ALLOWED_DATA_ATTRIBUTES_IN_ELEMENTS.include?(node.name) && + attr_name.start_with?('data-') + # Arbitrary data attributes are allowed. Verify that the attribute + # is a valid data attribute. + attr.unlink unless attr_name =~ DATA_ATTR_PATTERN + else + attr.unlink + end + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/sanitizers/svg/whitelist.rb b/lib/gitlab/sanitizers/svg/whitelist.rb new file mode 100644 index 0000000000..917e795b29 --- /dev/null +++ b/lib/gitlab/sanitizers/svg/whitelist.rb @@ -0,0 +1,107 @@ +# Generated from: +# SVG element list: https://www.w3.org/TR/SVG/eltindex.html +# SVG Attribute list: https://www.w3.org/TR/SVG/attindex.html +module Gitlab + module Sanitizers + module SVG + ALLOWED_ELEMENTS = %w[ + a altGlyph altGlyphDef altGlyphItem animate + animateColor animateMotion animateTransform circle clipPath color-profile + cursor defs desc ellipse feBlend feColorMatrix feComponentTransfer + feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap + feDistantLight feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur + feImage feMerge feMergeNode feMorphology feOffset fePointLight + feSpecularLighting feSpotLight feTile feTurbulence filter font font-face + font-face-format font-face-name font-face-src font-face-uri foreignObject + g glyph glyphRef hkern image line linearGradient marker mask metadata + missing-glyph mpath path pattern polygon polyline radialGradient rect + script set stop style svg switch symbol text textPath title tref tspan use + view vkern].freeze + + ALLOWED_DATA_ATTRIBUTES_IN_ELEMENTS = %w[svg].freeze + + ALLOWED_ATTRIBUTES = { + 'a' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage target text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'altGlyph' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline dx dy enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight format glyph-orientation-horizontal glyph-orientation-vertical glyphRef id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures rotate shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering unicode-bidi visibility word-spacing writing-mode x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y], + 'altGlyphDef' => %w[id xml:base xml:lang xml:space], + 'altGlyphItem' => %w[id xml:base xml:lang xml:space], + 'animate' => %w[accumulate additive alignment-baseline attributeName attributeType baseline-shift begin by calcMode clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline dur enable-background end externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight from glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning keySplines keyTimes letter-spacing lighting-color marker-end marker-mid marker-start mask max min onbegin onend onload onrepeat opacity overflow pointer-events repeatCount repeatDur requiredExtensions requiredFeatures restart shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width systemLanguage text-anchor text-decoration text-rendering to unicode-bidi values visibility word-spacing writing-mode xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'animateColor' => %w[accumulate additive alignment-baseline attributeName attributeType baseline-shift begin by calcMode clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline dur enable-background end externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight from glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning keySplines keyTimes letter-spacing lighting-color marker-end marker-mid marker-start mask max min onbegin onend onload onrepeat opacity overflow pointer-events repeatCount repeatDur requiredExtensions requiredFeatures restart shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width systemLanguage text-anchor text-decoration text-rendering to unicode-bidi values visibility word-spacing writing-mode xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'animateMotion' => %w[accumulate additive begin by calcMode dur end externalResourcesRequired fill from id keyPoints keySplines keyTimes max min onbegin onend onload onrepeat origin path repeatCount repeatDur requiredExtensions requiredFeatures restart rotate systemLanguage to values xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'animateTransform' => %w[accumulate additive attributeName attributeType begin by calcMode dur end externalResourcesRequired fill from id keySplines keyTimes max min onbegin onend onload onrepeat repeatCount repeatDur requiredExtensions requiredFeatures restart systemLanguage to type values xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'circle' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor cx cy direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events r requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'clipPath' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule clipPathUnits color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'color-profile' => %w[id local name rendering-intent xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'cursor' => %w[externalResourcesRequired id requiredExtensions requiredFeatures systemLanguage x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y], + 'defs' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'desc' => %w[class id style xml:base xml:lang xml:space], + 'ellipse' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor cx cy direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures rx ry shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'feBlend' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in in2 kerning letter-spacing lighting-color marker-end marker-mid marker-start mask mode opacity overflow pointer-events result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feColorMatrix' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering type unicode-bidi values visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feComponentTransfer' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feComposite' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in in2 k1 k2 k3 k4 kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity operator overflow pointer-events result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feConvolveMatrix' => %w[alignment-baseline baseline-shift bias class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display divisor dominant-baseline edgeMode enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in kernelMatrix kernelUnitLength kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity order overflow pointer-events preserveAlpha result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style targetX targetY text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feDiffuseLighting' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor diffuseConstant direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in kernelUnitLength kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style surfaceScale text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feDisplacementMap' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in in2 kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result scale shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xChannelSelector xml:base xml:lang xml:space y yChannelSelector], + 'feDistantLight' => %w[azimuth elevation id xml:base xml:lang xml:space], + 'feFlood' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feFuncA' => %w[amplitude exponent id intercept offset slope tableValues type xml:base xml:lang xml:space], + 'feFuncB' => %w[amplitude exponent id intercept offset slope tableValues type xml:base xml:lang xml:space], + 'feFuncG' => %w[amplitude exponent id intercept offset slope tableValues type xml:base xml:lang xml:space], + 'feFuncR' => %w[amplitude exponent id intercept offset slope tableValues type xml:base xml:lang xml:space], + 'feGaussianBlur' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result shape-rendering stdDeviation stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feImage' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events preserveAspectRatio result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y], + 'feMerge' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feMergeNode' => %w[id xml:base xml:lang xml:space], + 'feMorphology' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity operator overflow pointer-events radius result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feOffset' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline dx dy enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'fePointLight' => %w[id x xml:base xml:lang xml:space y z], + 'feSpecularLighting' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in kernelUnitLength kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result shape-rendering specularConstant specularExponent stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style surfaceScale text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feSpotLight' => %w[id limitingConeAngle pointsAtX pointsAtY pointsAtZ specularExponent x xml:base xml:lang xml:space y z], + 'feTile' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering in kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events result shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'feTurbulence' => %w[alignment-baseline baseFrequency baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask numOctaves opacity overflow pointer-events result seed shape-rendering stitchTiles stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering type unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'filter' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter filterRes filterUnits flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events primitiveUnits shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y], + 'font' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x horiz-origin-y id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi vert-adv-y vert-origin-x vert-origin-y visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'font-face' => %w[accent-height alphabetic ascent bbox cap-height descent font-family font-size font-stretch font-style font-variant font-weight hanging id ideographic mathematical overline-position overline-thickness panose-1 slope stemh stemv strikethrough-position strikethrough-thickness underline-position underline-thickness unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical widths x-height xml:base xml:lang xml:space], + 'font-face-format' => %w[id string xml:base xml:lang xml:space], + 'font-face-name' => %w[id name xml:base xml:lang xml:space], + 'font-face-src' => %w[id xml:base xml:lang xml:space], + 'font-face-uri' => %w[id xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'foreignObject' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'g' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'glyph' => %w[alignment-baseline arabic-form baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor d direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x id image-rendering kerning lang letter-spacing lighting-color marker-end marker-mid marker-start mask opacity orientation overflow pointer-events shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode unicode-bidi vert-adv-y vert-origin-x vert-origin-y visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'glyphRef' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline dx dy enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight format glyph-orientation-horizontal glyph-orientation-vertical glyphRef id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility word-spacing writing-mode x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y], + 'hkern' => %w[g1 g2 id k u1 u2 xml:base xml:lang xml:space], + 'image' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events preserveAspectRatio requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility width word-spacing writing-mode x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y], + 'line' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode x1 x2 xml:base xml:lang xml:space y1 y2], + 'linearGradient' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical gradientTransform gradientUnits id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events shape-rendering spreadMethod stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility word-spacing writing-mode x1 x2 xlink:arcrole xlink:href xlink:role xlink:title xlink:type xml:base xml:lang xml:space y1 y2], + 'marker' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start markerHeight markerUnits markerWidth mask opacity orient overflow pointer-events preserveAspectRatio refX refY shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi viewBox visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'mask' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask maskContentUnits maskUnits opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'metadata' => %w[id xml:base xml:lang xml:space], + 'missing-glyph' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor d direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi vert-adv-y vert-origin-x vert-origin-y visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'mpath' => %w[externalResourcesRequired id xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'path' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor d direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pathLength pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'pattern' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow patternContentUnits patternTransform patternUnits pointer-events preserveAspectRatio requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering unicode-bidi viewBox visibility width word-spacing writing-mode x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y], + 'polygon' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events points requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'polyline' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events points requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'radialGradient' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor cx cy direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight fx fy glyph-orientation-horizontal glyph-orientation-vertical gradientTransform gradientUnits id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events r shape-rendering spreadMethod stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility word-spacing writing-mode xlink:arcrole xlink:href xlink:role xlink:title xlink:type xml:base xml:lang xml:space], + 'rect' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures rx ry shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility width word-spacing writing-mode x xml:base xml:lang xml:space y], + 'script' => %w[externalResourcesRequired id type xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'set' => %w[attributeName attributeType begin dur end externalResourcesRequired fill id max min onbegin onend onload onrepeat repeatCount repeatDur requiredExtensions requiredFeatures restart systemLanguage to xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space], + 'stop' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask offset opacity overflow pointer-events shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'style' => %w[id media title type xml:base xml:lang xml:space], + 'svg' => %w[alignment-baseline baseProfile baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering contentScriptType contentStyleType cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onabort onactivate onclick onerror onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup onresize onscroll onunload onzoom opacity overflow pointer-events preserveAspectRatio requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering unicode-bidi version viewBox visibility width word-spacing writing-mode x xml:base xml:lang xml:space xmlns y zoomAndPan], + 'switch' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'symbol' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events preserveAspectRatio shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style text-anchor text-decoration text-rendering unicode-bidi viewBox visibility word-spacing writing-mode xml:base xml:lang xml:space], + 'text' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline dx dy enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning lengthAdjust letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures rotate shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering textLength transform unicode-bidi visibility word-spacing writing-mode x xml:base xml:lang xml:space y], + 'textPath' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning lengthAdjust letter-spacing lighting-color marker-end marker-mid marker-start mask method onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering spacing startOffset stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering textLength unicode-bidi visibility word-spacing writing-mode xlink:arcrole xlink:href xlink:role xlink:title xlink:type xml:base xml:lang xml:space], + 'title' => %w[class id style xml:base xml:lang xml:space], + 'tref' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline dx dy enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning lengthAdjust letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures rotate shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering textLength unicode-bidi visibility word-spacing writing-mode x xlink:arcrole xlink:href xlink:role xlink:title xlink:type xml:base xml:lang xml:space y], + 'tspan' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline dx dy enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical id image-rendering kerning lengthAdjust letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures rotate shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering textLength unicode-bidi visibility word-spacing writing-mode x xml:base xml:lang xml:space y], + 'use' => %w[alignment-baseline baseline-shift class clip clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering cursor direction display dominant-baseline enable-background externalResourcesRequired fill fill-opacity fill-rule filter flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical height id image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup opacity overflow pointer-events requiredExtensions requiredFeatures shape-rendering stop-color stop-opacity stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style systemLanguage text-anchor text-decoration text-rendering transform unicode-bidi visibility width word-spacing writing-mode x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y], + 'view' => %w[externalResourcesRequired id preserveAspectRatio viewBox viewTarget xml:base xml:lang xml:space zoomAndPan], + 'vkern' => %w[g1 g2 id k u1 u2 xml:base xml:lang xml:space] + }.freeze + end + end +end diff --git a/spec/fixtures/sanitized.svg b/spec/fixtures/sanitized.svg new file mode 100644 index 0000000000..8f84b8f5e2 --- /dev/null +++ b/spec/fixtures/sanitized.svg @@ -0,0 +1,50 @@ + + + + + + + stacked_wm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/fixtures/unsanitized.svg b/spec/fixtures/unsanitized.svg new file mode 100644 index 0000000000..3957557334 --- /dev/null +++ b/spec/fixtures/unsanitized.svg @@ -0,0 +1,50 @@ + + + + + + + stacked_wm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 87849230db..6d1c02db29 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -67,4 +67,16 @@ describe BlobHelper do expect(result).to eq(expected) end end + + describe "#sanitize_svg" do + let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') } + let(:data) { open(input_svg_path).read } + let(:expected_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') } + let(:expected) { open(expected_svg_path).read } + + it 'should retain essential elements' do + blob = OpenStruct.new(data: data) + expect(sanitize_svg(blob).data).to eq(expected) + end + end end From a65de9c2c1b8ea2d7dca3132ff0d72775f04bb78 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 1 May 2016 00:38:53 -0700 Subject: [PATCH 138/138] Reduce delay in destroying a project from 1-minute to immediately Run ProjectDestroyWorker after pending_delete attribute has been committed to DB --- CHANGELOG | 1 + app/models/project.rb | 7 +++++++ app/services/projects/destroy_service.rb | 4 +--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 78d6c1ec2b..e9b3e7b097 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.8.0 (unreleased) - Log to application.log when an admin starts and stops impersonating a user - Updated gitlab_git to 10.1.0 - GitAccess#protected_tag? no longer loads all tags just to check if a single one exists + - Reduce delay in destroying a project from 1-minute to immediately - Make build status canceled if any of the jobs was canceled and none failed - Upgrade Sidekiq to 4.1.2 - Sanitize repo paths in new project error message diff --git a/app/models/project.rb b/app/models/project.rb index 9403acf775..dfd1e54ecf 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1039,4 +1039,11 @@ class Project < ActiveRecord::Base def wiki @wiki ||= ProjectWiki.new(self, self.owner) end + + def schedule_delete!(user_id, params) + # Queue this task for after the commit, so once we mark pending_delete it will run + run_after_commit { ProjectDestroyWorker.perform_async(id, user_id, params) } + + update_attribute(:pending_delete, true) + end end diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index df5054f08d..19aab999e0 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -7,9 +7,7 @@ module Projects DELETED_FLAG = '+deleted' def pending_delete! - project.update_attribute(:pending_delete, true) - - ProjectDestroyWorker.perform_in(1.minute, project.id, current_user.id, params) + project.schedule_delete!(current_user.id, params) end def execute