From df27b3a1742048358674733d9cb3392348d5e964 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 9 Feb 2016 12:02:52 +0100 Subject: Issuable can be assigned to author Closes #9014 The only difference with #9014 is that I thoughed the author should also be able to assign the issue. If this is unwanted behavior Ill revert it. --- CHANGELOG | 1 + app/assets/javascripts/users_select.js.coffee | 4 +++- app/controllers/autocomplete_controller.rb | 8 +++++++- app/controllers/projects/issues_controller.rb | 4 ++-- app/helpers/selects_helper.rb | 16 +++++++++------- app/models/concerns/issuable.rb | 5 +++++ app/services/issuable_base_service.rb | 11 ++++++++--- app/services/issues/base_service.rb | 4 ++-- app/services/merge_requests/base_service.rb | 4 ++-- app/views/shared/issuable/_sidebar.html.haml | 7 +++++-- spec/controllers/autocomplete_controller_spec.rb | 13 +++++++++++++ spec/models/concerns/issuable_spec.rb | 14 ++++++++++++++ 12 files changed, 71 insertions(+), 20 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 77a21e757c4..e93979fd468 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -78,6 +78,7 @@ v 8.5.0 (unreleased) - Add label description (Nuttanart Pornprasitsakul) - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul) - Add Todos + - Allow issues and merge requests to be assigned to the author(Zeger-Jan van de Weg) v 8.4.4 - Update omniauth-saml gem to 1.4.2 diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 9467011799f..93c0c7adfee 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -7,11 +7,12 @@ class @UsersSelect @projectId = $(select).data('project-id') @groupId = $(select).data('group-id') @showCurrentUser = $(select).data('current-user') + @authorId = $(select).data('author-id') showNullUser = $(select).data('null-user') showAnyUser = $(select).data('any-user') showEmailUser = $(select).data('email-user') firstUser = $(select).data('first-user') - + $(select).select2 placeholder: "Search for a user" multiple: $(select).hasClass('multiselect') @@ -112,6 +113,7 @@ class @UsersSelect project_id: @projectId group_id: @groupId current_user: @showCurrentUser + author_id: @authorId dataType: "json" ).done (users) -> callback(users) diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 77c8dafc012..23f2ab45ff4 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -12,8 +12,14 @@ class AutocompleteController < ApplicationController if params[:search].blank? # Include current user if available to filter by "Me" if params[:current_user] && current_user - @users = [*@users, current_user].uniq + @users = [*@users, current_user] end + + if params[:author_id] + @users = [User.find(params[:author_id]), *@users] + end + + @users.uniq! end render json: @users, only: [:name, :username, :id], methods: [:avatar_url] diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 67faa1e4437..8aa85e448c4 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -59,8 +59,8 @@ class Projects::IssuesController < Projects::ApplicationController end def show - @note = @project.notes.new(noteable: @issue) - @notes = @issue.notes.nonawards.with_associations.fresh + @note = @project.notes.new(noteable: @issue) + @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue @merge_requests = @issue.referenced_merge_requests(current_user) diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index 05386d790ca..368762b8265 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -6,12 +6,13 @@ module SelectsHelper value = opts[:selected] || '' placeholder = opts[:placeholder] || 'Search for a user' - null_user = opts[:null_user] || false - any_user = opts[:any_user] || false - email_user = opts[:email_user] || false - first_user = opts[:first_user] && current_user ? current_user.username : false - current_user = opts[:current_user] || false - project = opts[:project] || @project + null_user = opts[:null_user] || false + any_user = opts[:any_user] || false + email_user = opts[:email_user] || false + first_user = opts[:first_user] && current_user ? current_user.username : false + current_user = opts[:current_user] || false + author_id = opts[:author_id] || false + project = opts[:project] || @project html = { class: css_class, @@ -21,7 +22,8 @@ module SelectsHelper any_user: any_user, email_user: email_user, first_user: first_user, - current_user: current_user + current_user: current_user, + author_id: author_id } } diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index e5f089fb8a0..f9314a241b2 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -116,6 +116,11 @@ module Issuable assignee_id_changed? end + def can_assign_user?(current_user) + author == current_user || + Ability.abilities.allowed?(current_user, :"admin_#{to_ability_name}", project) + end + def open? opened? || reopened? end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index ca87dca4a70..99a28b8c637 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -33,7 +33,7 @@ class IssuableBaseService < BaseService end end - def filter_params(issuable_ability_name = :issue) + def filter_params(issuable_ability_name, issuable) params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE @@ -42,13 +42,18 @@ class IssuableBaseService < BaseService unless can?(current_user, ability, project) params.delete(:milestone_id) params.delete(:label_ids) - params.delete(:assignee_id) + + # The author of an issue can be assigned, to signal the ball being in his/her + # court. This allow him/her to reassign the issue back to the reviewer. + if issuable && !(issuable.author == current_user) + params.delete(:assignee_id) + end end end def update(issuable) change_state(issuable) - filter_params + filter_params(issuable) old_labels = issuable.labels.to_a if params.present? && issuable.update_attributes(params.merge(updated_by: current_user)) diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 770f32de944..4527d1c74e0 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -10,8 +10,8 @@ module Issues private - def filter_params - super(:issue) + def filter_params(issuable = nil) + super(:issue, issuable) end def execute_hooks(issue, action = 'open') diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 7b306a8a531..2afbdfae664 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -23,8 +23,8 @@ module MergeRequests private - def filter_params - super(:merge_request) + def filter_params(issueable = nil) + super(:merge_request, issueable) end end end diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index a45775f36b5..1df44eaa64f 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -30,7 +30,7 @@ .title %label Assignee - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + - if issuable.can_assign_user?(current_user) .pull-right = link_to 'Edit', '#', class: 'edit-link' .value @@ -43,7 +43,10 @@ .light None .selectbox - = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) + = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", + placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', + selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, + first_user: true, author_id: issuable.author_id) .block.milestone .sidebar-collapsed-icon diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 85379a8e984..0754a2889fc 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -143,4 +143,17 @@ describe AutocompleteController do it { expect(body.size).to eq 0 } end end + + context 'author of issuable included' do + before do + sign_in(user) + get(:users, author_id: non_member.id) + end + + let(:body) { JSON.parse(response.body) } + + it 'should also return the author' do + expect(body.first["username"]).to eq non_member.username + end + end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 600089802b2..16fc1a6069a 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -111,6 +111,20 @@ describe Issue, "Issuable" do end end + describe "#can_assign_user?" do + let(:author) { build(:user) } + let(:issue) { build(:issue, author: author)} + + it "Allows the author to change the assignee" do + expect(issue.can_assign_user?(author)).to be_truthy + end + + it "Doesn't allow others, non-team members" do + other_user = build(:user) + expect(issue.can_assign_user?(other_user)).to be_falsey + end + end + describe "votes" do before do author = create :user -- cgit v1.2.1 From 829830ae9df9e777716e03fa393a328d3ff882b0 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 9 Feb 2016 14:36:12 +0100 Subject: Fix loading data when no author_id is passed --- app/controllers/autocomplete_controller.rb | 2 +- app/services/merge_requests/base_service.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 23f2ab45ff4..5d81a996fba 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -15,7 +15,7 @@ class AutocompleteController < ApplicationController @users = [*@users, current_user] end - if params[:author_id] + unless params[:author_id] == "false" @users = [User.find(params[:author_id]), *@users] end diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 2afbdfae664..e472381ba36 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -23,8 +23,8 @@ module MergeRequests private - def filter_params(issueable = nil) - super(:merge_request, issueable) + def filter_params(issuable = nil) + super(:merge_request, issuable) end end end -- cgit v1.2.1 From 15a6633999c81387245cabf129dd2fbb04650c95 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 17 Feb 2016 12:49:38 +0100 Subject: Revert authors ability to assign anyone --- app/controllers/autocomplete_controller.rb | 2 +- app/models/concerns/issuable.rb | 5 ----- app/services/issuable_base_service.rb | 11 +++-------- app/services/issues/base_service.rb | 4 ++-- app/services/merge_requests/base_service.rb | 4 ++-- app/views/shared/issuable/_sidebar.html.haml | 2 +- spec/controllers/autocomplete_controller_spec.rb | 4 ++-- spec/models/concerns/issuable_spec.rb | 14 -------------- 8 files changed, 11 insertions(+), 35 deletions(-) diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 5d81a996fba..1e4fc612a3c 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -15,7 +15,7 @@ class AutocompleteController < ApplicationController @users = [*@users, current_user] end - unless params[:author_id] == "false" + if params[:author_id] && params[:author_id] != "false" @users = [User.find(params[:author_id]), *@users] end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index f9314a241b2..e5f089fb8a0 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -116,11 +116,6 @@ module Issuable assignee_id_changed? end - def can_assign_user?(current_user) - author == current_user || - Ability.abilities.allowed?(current_user, :"admin_#{to_ability_name}", project) - end - def open? opened? || reopened? end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 99a28b8c637..ca87dca4a70 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -33,7 +33,7 @@ class IssuableBaseService < BaseService end end - def filter_params(issuable_ability_name, issuable) + def filter_params(issuable_ability_name = :issue) params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE @@ -42,18 +42,13 @@ class IssuableBaseService < BaseService unless can?(current_user, ability, project) params.delete(:milestone_id) params.delete(:label_ids) - - # The author of an issue can be assigned, to signal the ball being in his/her - # court. This allow him/her to reassign the issue back to the reviewer. - if issuable && !(issuable.author == current_user) - params.delete(:assignee_id) - end + params.delete(:assignee_id) end end def update(issuable) change_state(issuable) - filter_params(issuable) + filter_params old_labels = issuable.labels.to_a if params.present? && issuable.update_attributes(params.merge(updated_by: current_user)) diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 4527d1c74e0..770f32de944 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -10,8 +10,8 @@ module Issues private - def filter_params(issuable = nil) - super(:issue, issuable) + def filter_params + super(:issue) end def execute_hooks(issue, action = 'open') diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index e472381ba36..7b306a8a531 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -23,8 +23,8 @@ module MergeRequests private - def filter_params(issuable = nil) - super(:merge_request, issuable) + def filter_params + super(:merge_request) end end end diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 1df44eaa64f..ef351fe8093 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -30,7 +30,7 @@ .title %label Assignee - - if issuable.can_assign_user?(current_user) + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) .pull-right = link_to 'Edit', '#', class: 'edit-link' .value diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 0754a2889fc..612e344c411 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -12,13 +12,13 @@ describe AutocompleteController do project.team << [user, :master] end - let(:body) { JSON.parse(response.body) } - describe 'GET #users with project ID' do before do get(:users, project_id: project.id) end + let(:body) { JSON.parse(response.body) } + it { expect(body).to be_kind_of(Array) } it { expect(body.size).to eq 1 } it { expect(body.first["username"]).to eq user.username } diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 16fc1a6069a..600089802b2 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -111,20 +111,6 @@ describe Issue, "Issuable" do end end - describe "#can_assign_user?" do - let(:author) { build(:user) } - let(:issue) { build(:issue, author: author)} - - it "Allows the author to change the assignee" do - expect(issue.can_assign_user?(author)).to be_truthy - end - - it "Doesn't allow others, non-team members" do - other_user = build(:user) - expect(issue.can_assign_user?(other_user)).to be_falsey - end - end - describe "votes" do before do author = create :user -- cgit v1.2.1 From 18411645505f4bf4bb877743cb4dc027d422414b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 2 Mar 2016 17:31:17 +0100 Subject: add initial migrations --- app/models/project.rb | 1 + ...20160302151724_add_import_credentials_to_projects.rb | 6 ++++++ ...60302152808_remove_wrong_import_url_from_projects.rb | 17 +++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 db/migrate/20160302151724_add_import_credentials_to_projects.rb create mode 100644 db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb diff --git a/app/models/project.rb b/app/models/project.rb index 6f5d592755a..36b11366f3f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -91,6 +91,7 @@ class Project < ActiveRecord::Base attr_accessor :new_default_branch attr_accessor :old_path_with_namespace + attr_encrypted :import_credentials, key: Gitlab::Application.secrets.db_key_base # Relations belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' diff --git a/db/migrate/20160302151724_add_import_credentials_to_projects.rb b/db/migrate/20160302151724_add_import_credentials_to_projects.rb new file mode 100644 index 00000000000..3cfe2bbd50a --- /dev/null +++ b/db/migrate/20160302151724_add_import_credentials_to_projects.rb @@ -0,0 +1,6 @@ +class AddImportCredentialsToProjects < ActiveRecord::Migration + def change + add_column :projects, :encrypted_import_credentials, :text + add_column :projects, :encrypted_import_credentials_iv, :text + end +end diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb new file mode 100644 index 00000000000..3f1b65aff14 --- /dev/null +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -0,0 +1,17 @@ +class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration + def up + projects_with_wrong_import_url.each do |project| + project.update_columns(import_url: nil) # TODO Check really nil? + # TODO: migrate current credentials to import_credentials? + # TODO: Notify user ? + end + end + + private + + + def projects_with_dot_atom + # TODO Check live with #operations for possible false positives. Also, consider regex? But may have issues MySQL/PSQL + select_all("SELECT p.id from projects p WHERE p.import_url LIKE '%//%:%@%' or p.import_url like '#{"_"*40}@github.com%'") + end +end -- cgit v1.2.1 From cefefb2adea23c81c5e6254992da975eca71b559 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 3 Mar 2016 18:10:06 +0100 Subject: WIP - refactored migration and updated project_import_data with encrypted att --- app/models/project.rb | 1 - app/models/project_import_data.rb | 3 +- ...dd_import_credentials_to_project_import_data.rb | 6 ++++ ...302151724_add_import_credentials_to_projects.rb | 6 ---- ...152808_remove_wrong_import_url_from_projects.rb | 36 +++++++++++++++++----- 5 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb delete mode 100644 db/migrate/20160302151724_add_import_credentials_to_projects.rb diff --git a/app/models/project.rb b/app/models/project.rb index 36b11366f3f..6f5d592755a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -91,7 +91,6 @@ class Project < ActiveRecord::Base attr_accessor :new_default_branch attr_accessor :old_path_with_namespace - attr_encrypted :import_credentials, key: Gitlab::Application.secrets.db_key_base # Relations belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index cd3319f077e..2900b86d643 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -12,7 +12,8 @@ require 'file_size_validator' class ProjectImportData < ActiveRecord::Base belongs_to :project - + attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base + serialize :data, JSON validates :project, presence: true diff --git a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb new file mode 100644 index 00000000000..ff3d8b466dc --- /dev/null +++ b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb @@ -0,0 +1,6 @@ +class AddImportCredentialsToProjectImportData < ActiveRecord::Migration + def change + add_column :project_import_data, :encrypted_credentials, :text + add_column :project_import_data, :encrypted_credentials_iv, :text + end +end diff --git a/db/migrate/20160302151724_add_import_credentials_to_projects.rb b/db/migrate/20160302151724_add_import_credentials_to_projects.rb deleted file mode 100644 index 3cfe2bbd50a..00000000000 --- a/db/migrate/20160302151724_add_import_credentials_to_projects.rb +++ /dev/null @@ -1,6 +0,0 @@ -class AddImportCredentialsToProjects < ActiveRecord::Migration - def change - add_column :projects, :encrypted_import_credentials, :text - add_column :projects, :encrypted_import_credentials_iv, :text - end -end diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 3f1b65aff14..dda7648fb87 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -1,16 +1,38 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration + + class ImportUrlSanitizer + def initialize(url) + @url = url + end + + def sanitized_url + @sanitized_url ||= @url[regex_extractor, 1] + @url[regex_extractor, 3] + end + + def credentials + @credentials ||= @url[regex_extractor, 2] + end + + private + + # Regex matches 1 , 2 , + # 3 + def regex_extractor + /(.*\/\/)(.*)(\@.*)/ + end + end + def up projects_with_wrong_import_url.each do |project| - project.update_columns(import_url: nil) # TODO Check really nil? - # TODO: migrate current credentials to import_credentials? - # TODO: Notify user ? + sanitizer = ImportUrlSanitizer.new(project.import_urls) + project.update_columns(import_url: sanitizer.sanitized_url) + if project.import_data + project.import_data.update_columns(credentials: sanitizer.credentials) + end end end - private - - - def projects_with_dot_atom + def projects_with_wrong_import_url # TODO Check live with #operations for possible false positives. Also, consider regex? But may have issues MySQL/PSQL select_all("SELECT p.id from projects p WHERE p.import_url LIKE '%//%:%@%' or p.import_url like '#{"_"*40}@github.com%'") end -- cgit v1.2.1 From 06b36c00d55df38cd2aaa4d5251185485c8abe5c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Mar 2016 12:21:53 +0100 Subject: some refactoring in the migration. Also fixed github import issue and updated spec --- ...0160302152808_remove_wrong_import_url_from_projects.rb | 15 ++++++++++----- db/schema.rb | 4 +++- lib/gitlab/github_import/project_creator.rb | 12 ++++++++++-- spec/lib/gitlab/github_import/project_creator_spec.rb | 3 ++- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index dda7648fb87..dfa9f2d4dee 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -23,11 +23,16 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def up - projects_with_wrong_import_url.each do |project| - sanitizer = ImportUrlSanitizer.new(project.import_urls) - project.update_columns(import_url: sanitizer.sanitized_url) - if project.import_data - project.import_data.update_columns(credentials: sanitizer.credentials) + projects_with_wrong_import_url.each do |project_id| + project = Project.find(project_id["id"]) + sanitizer = ImportUrlSanitizer.new(project.import_url) + + ActiveRecord::Base.transaction do + project.update_columns(import_url: sanitizer.sanitized_url) + if project.import_data + project.import_data.credentials = sanitizer.credentials + project.save! + end end end end diff --git a/db/schema.rb b/db/schema.rb index 53a941d30de..05c19fc7a2e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160222153918) do +ActiveRecord::Schema.define(version: 20160302152808) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -658,6 +658,8 @@ ActiveRecord::Schema.define(version: 20160222153918) do create_table "project_import_data", force: :cascade do |t| t.integer "project_id" t.text "data" + t.text "encrypted_credentials" + t.text "encrypted_credentials_iv" end create_table "projects", force: :cascade do |t| diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb index 474927069a5..d6cab3c2d24 100644 --- a/lib/gitlab/github_import/project_creator.rb +++ b/lib/gitlab/github_import/project_creator.rb @@ -20,13 +20,21 @@ module Gitlab visibility_level: repo.private ? Gitlab::VisibilityLevel::PRIVATE : Gitlab::VisibilityLevel::PUBLIC, import_type: "github", import_source: repo.full_name, - import_url: repo.clone_url.sub("https://", "https://#{@session_data[:github_access_token]}@"), + import_url: repo.clone_url, wiki_enabled: !repo.has_wiki? # If repo has wiki we'll import it later ).execute - project.create_import_data(data: { "github_session" => session_data } ) + create_import_data(project) project end + + private + + def create_import_data(project) + project.create_import_data( + credentials: session_data.delete(:github_access_token), + data: { "github_session" => session_data }) + end end end end diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb index c93a3ebdaec..36abe87f527 100644 --- a/spec/lib/gitlab/github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/github_import/project_creator_spec.rb @@ -26,7 +26,8 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do project_creator = Gitlab::GithubImport::ProjectCreator.new(repo, namespace, user, access_params) project = project_creator.execute - expect(project.import_url).to eq("https://asdffg@gitlab.com/asd/vim.git") + expect(project.import_url).to eq("https://gitlab.com/asd/vim.git") + expect(project.import_data.credentials).to eq("asdffg") expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) end end -- cgit v1.2.1 From c2b33d3b71215858892debeb45d93a69d530fd8e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Mar 2016 16:23:19 +0100 Subject: added import url exposer to construct URL withunencrypted credentials --- app/models/project_import_data.rb | 1 + lib/gitlab/github_import/importer.rb | 3 +-- lib/gitlab/github_import/project_creator.rb | 4 ++-- lib/gitlab/github_import/wiki_formatter.rb | 4 +++- lib/gitlab/import_url_exposer.rb | 17 +++++++++++++++++ 5 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 lib/gitlab/import_url_exposer.rb diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 2900b86d643..493c82a94fa 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -13,6 +13,7 @@ require 'file_size_validator' class ProjectImportData < ActiveRecord::Base belongs_to :project attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base + serialize :credentials, JSON serialize :data, JSON diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index e2a85f29825..515fd4720d5 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -7,8 +7,7 @@ module Gitlab def initialize(project) @project = project - import_data = project.import_data.try(:data) - github_session = import_data["github_session"] if import_data + github_session = project.import_data.credentials if import_data @client = Client.new(github_session["github_access_token"]) @formatter = Gitlab::ImportFormatter.new end diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb index d6cab3c2d24..b5ed32e5b1e 100644 --- a/lib/gitlab/github_import/project_creator.rb +++ b/lib/gitlab/github_import/project_creator.rb @@ -32,8 +32,8 @@ module Gitlab def create_import_data(project) project.create_import_data( - credentials: session_data.delete(:github_access_token), - data: { "github_session" => session_data }) + credentials: { github_access_token: session_data.delete(:github_access_token) }, + data: { github_session: session_data }) end end end diff --git a/lib/gitlab/github_import/wiki_formatter.rb b/lib/gitlab/github_import/wiki_formatter.rb index 6c592ff469c..8be82924107 100644 --- a/lib/gitlab/github_import/wiki_formatter.rb +++ b/lib/gitlab/github_import/wiki_formatter.rb @@ -12,7 +12,9 @@ module Gitlab end def import_url - project.import_url.sub(/\.git\z/, ".wiki.git") + import_url = Gitlab::ImportUrlExposer.expose(import_url: project.import_url, + credentials: project.import_data.credentials) + import_url.sub(/\.git\z/, ".wiki.git") end end end diff --git a/lib/gitlab/import_url_exposer.rb b/lib/gitlab/import_url_exposer.rb new file mode 100644 index 00000000000..6b4af0bf265 --- /dev/null +++ b/lib/gitlab/import_url_exposer.rb @@ -0,0 +1,17 @@ +module Gitlab + # Exposes an import URL that includes the credentials unencrypted. + # Extracted to its own class to prevent unintended use. + module ImportUrlExposer + extend self + + def expose(import_url:, credentials: ) + import_url.sub("//", "//#{parsed_credentials(credentials)}@") + end + + private + + def parsed_credentials(credentials) + credentials.values.join(":") + end + end +end \ No newline at end of file -- cgit v1.2.1 From 7085850c50a6dd7072bd2c80f092b0c20f74d1dc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Mar 2016 18:37:00 +0100 Subject: fix specs --- app/models/project_import_data.rb | 3 +-- lib/gitlab/import_url_exposer.rb | 2 +- spec/factories/project_import_data.rb | 5 +++++ spec/lib/gitlab/github_import/project_creator_spec.rb | 4 ++-- spec/lib/gitlab/github_import/wiki_formatter_spec.rb | 9 +++++++-- 5 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 spec/factories/project_import_data.rb diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 493c82a94fa..23f4e97b8aa 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -12,8 +12,7 @@ require 'file_size_validator' class ProjectImportData < ActiveRecord::Base belongs_to :project - attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base - serialize :credentials, JSON + attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true serialize :data, JSON diff --git a/lib/gitlab/import_url_exposer.rb b/lib/gitlab/import_url_exposer.rb index 6b4af0bf265..f1919dffa8a 100644 --- a/lib/gitlab/import_url_exposer.rb +++ b/lib/gitlab/import_url_exposer.rb @@ -14,4 +14,4 @@ module Gitlab credentials.values.join(":") end end -end \ No newline at end of file +end diff --git a/spec/factories/project_import_data.rb b/spec/factories/project_import_data.rb new file mode 100644 index 00000000000..18393cdda98 --- /dev/null +++ b/spec/factories/project_import_data.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :project_import_data, class: ProjectImportData do + data "test" + end +end \ No newline at end of file diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb index 36abe87f527..290c855642a 100644 --- a/spec/lib/gitlab/github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/github_import/project_creator_spec.rb @@ -12,7 +12,7 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do owner: OpenStruct.new(login: "john") ) end - let(:namespace){ create(:group, owner: user) } + let(:namespace) { create(:group, owner: user) } let(:token) { "asdffg" } let(:access_params) { { github_access_token: token } } @@ -27,7 +27,7 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do project = project_creator.execute expect(project.import_url).to eq("https://gitlab.com/asd/vim.git") - expect(project.import_data.credentials).to eq("asdffg") + expect(project.import_data.credentials).to eq(:github_access_token => "asdffg") expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) end end diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb index aed2aa39e3a..46d5c2f3296 100644 --- a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb @@ -3,10 +3,15 @@ require 'spec_helper' describe Gitlab::GithubImport::WikiFormatter, lib: true do let(:project) do create(:project, namespace: create(:namespace, path: 'gitlabhq'), - import_url: 'https://xxx@github.com/gitlabhq/sample.gitlabhq.git') + import_url: 'https://github.com/gitlabhq/sample.gitlabhq.git') end - subject(:wiki) { described_class.new(project)} + let!(:project_import_data) do + create(:project_import_data, credentials: { github_access_token: 'xxx' }, + project: project) + end + + subject(:wiki) { described_class.new(project) } describe '#path_with_namespace' do it 'appends .wiki to project path' do -- cgit v1.2.1 From 735563329d1f86ee4d72b37cd22eed1168935e8e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 7 Mar 2016 12:50:35 +0100 Subject: refactored a bunch of stuff based on MR feedback --- app/models/project_import_data.rb | 2 +- ...152808_remove_wrong_import_url_from_projects.rb | 38 +++++++++++++--------- lib/gitlab/github_import/importer.rb | 4 +-- lib/gitlab/github_import/project_creator.rb | 3 +- lib/gitlab/import_url_exposer.rb | 14 +++----- spec/lib/gitlab/import_url_exposer_spec.rb | 13 ++++++++ 6 files changed, 44 insertions(+), 30 deletions(-) create mode 100644 spec/lib/gitlab/import_url_exposer_spec.rb diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 23f4e97b8aa..f3b9daa0d1a 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -12,7 +12,7 @@ require 'file_size_validator' class ProjectImportData < ActiveRecord::Base belongs_to :project - attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true + attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true serialize :data, JSON diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index dfa9f2d4dee..881af783c61 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -2,43 +2,49 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration class ImportUrlSanitizer def initialize(url) - @url = url + @url = URI.parse(url) end def sanitized_url - @sanitized_url ||= @url[regex_extractor, 1] + @url[regex_extractor, 3] + @sanitized_url ||= safe_url end def credentials - @credentials ||= @url[regex_extractor, 2] + @credentials ||= { user: @url.user, password: @url.password } end private - # Regex matches 1 , 2 , - # 3 - def regex_extractor - /(.*\/\/)(.*)(\@.*)/ + def safe_url + safe_url = @url.dup + safe_url.password = nil + safe_url.user = nil + safe_url end + + end + + class FakeProjectImportData + extend AttrEncrypted + attr_accessor :credentials + attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true end def up - projects_with_wrong_import_url.each do |project_id| - project = Project.find(project_id["id"]) - sanitizer = ImportUrlSanitizer.new(project.import_url) + projects_with_wrong_import_url.each do |project| + sanitizer = ImportUrlSanitizer.new(project["import_url"]) ActiveRecord::Base.transaction do - project.update_columns(import_url: sanitizer.sanitized_url) - if project.import_data - project.import_data.credentials = sanitizer.credentials - project.save! - end + execute("UPDATE projects SET import_url = '#{sanitizer.sanitized_url}' WHERE id = #{project['id']}") + fake_import_data = FakeProjectImportData.new + fake_import_data.credentials = sanitizer.credentials + execute("UPDATE project_import_data SET encrypted_credentials = '#{fake_import_data.encrypted_credentials}' WHERE project_id = #{project['id']}") end end end def projects_with_wrong_import_url # TODO Check live with #operations for possible false positives. Also, consider regex? But may have issues MySQL/PSQL - select_all("SELECT p.id from projects p WHERE p.import_url LIKE '%//%:%@%' or p.import_url like '#{"_"*40}@github.com%'") + select_all("SELECT p.id, p.import_url from projects p WHERE p.import_url LIKE '%//%:%@%' or p.import_url like '#{"_"*40}@github.com%'") end end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 515fd4720d5..d478d3b5398 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -7,8 +7,8 @@ module Gitlab def initialize(project) @project = project - github_session = project.import_data.credentials if import_data - @client = Client.new(github_session["github_access_token"]) + credentials = project.import_data.credentials if import_data + @client = Client.new(credentials["github_access_token"]) @formatter = Gitlab::ImportFormatter.new end diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb index b5ed32e5b1e..52aba93a51d 100644 --- a/lib/gitlab/github_import/project_creator.rb +++ b/lib/gitlab/github_import/project_creator.rb @@ -32,8 +32,7 @@ module Gitlab def create_import_data(project) project.create_import_data( - credentials: { github_access_token: session_data.delete(:github_access_token) }, - data: { github_session: session_data }) + credentials: { github_access_token: session_data.delete(:github_access_token) }) end end end diff --git a/lib/gitlab/import_url_exposer.rb b/lib/gitlab/import_url_exposer.rb index f1919dffa8a..bf03f5a6daf 100644 --- a/lib/gitlab/import_url_exposer.rb +++ b/lib/gitlab/import_url_exposer.rb @@ -2,16 +2,12 @@ module Gitlab # Exposes an import URL that includes the credentials unencrypted. # Extracted to its own class to prevent unintended use. module ImportUrlExposer - extend self - def expose(import_url:, credentials: ) - import_url.sub("//", "//#{parsed_credentials(credentials)}@") - end - - private - - def parsed_credentials(credentials) - credentials.values.join(":") + def self.expose(import_url:, credentials: ) + uri = URI.parse(import_url) + uri.user = credentials[:user] + uri.password = credentials[:password] + uri end end end diff --git a/spec/lib/gitlab/import_url_exposer_spec.rb b/spec/lib/gitlab/import_url_exposer_spec.rb new file mode 100644 index 00000000000..878947caea1 --- /dev/null +++ b/spec/lib/gitlab/import_url_exposer_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe 'Gitlab::ImportUrlExposer' do + + describe :expose do + let(:credentials) do + Gitlab::ImportUrlExposer.expose(import_url: "https://github.com/me/project.git", credentials: {user: 'blah', password: 'password'}) + end + + it { expect(credentials).to be_a(URI) } + it { expect(credentials.to_s).to eq("https://blah:password@github.com/me/project.git") } + end +end -- cgit v1.2.1 From 383cc8404741f65bd52fbe80eec6c2dae2578fce Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 13:15:51 +0100 Subject: some refactoring based on feedback --- app/models/project.rb | 12 +++++ ...152808_remove_wrong_import_url_from_projects.rb | 52 ++++++++-------------- db/schema.rb | 4 +- lib/gitlab/github_import/importer.rb | 2 +- lib/gitlab/github_import/project_creator.rb | 14 +----- lib/gitlab/github_import/wiki_formatter.rb | 4 +- lib/gitlab/import_url_sanitizer.rb | 24 ++++++++++ 7 files changed, 62 insertions(+), 50 deletions(-) create mode 100644 lib/gitlab/import_url_sanitizer.rb diff --git a/app/models/project.rb b/app/models/project.rb index 412c6c6732d..ab4afd4159e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -404,6 +404,18 @@ class Project < ActiveRecord::Base self.import_data.destroy if self.import_data end + def import_url=(value) + sanitizer = Gitlab::ImportUrlSanitizer.new(value) + self[:import_url] = sanitizer.sanitized_url + create_import_data(credentials: sanitizer.credentials) + end + + def import_url + if import_data + Gitlab::ImportUrlExposer.expose(import_url: self[:import_url], credentials: import_data.credentials) + end + end + def import? external_import? || forked? end diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 881af783c61..f98ec925ac4 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -1,29 +1,5 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration - class ImportUrlSanitizer - def initialize(url) - @url = URI.parse(url) - end - - def sanitized_url - @sanitized_url ||= safe_url - end - - def credentials - @credentials ||= { user: @url.user, password: @url.password } - end - - private - - def safe_url - safe_url = @url.dup - safe_url.password = nil - safe_url.user = nil - safe_url - end - - end - class FakeProjectImportData extend AttrEncrypted attr_accessor :credentials @@ -31,20 +7,30 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def up - projects_with_wrong_import_url.each do |project| - sanitizer = ImportUrlSanitizer.new(project["import_url"]) - - ActiveRecord::Base.transaction do - execute("UPDATE projects SET import_url = '#{sanitizer.sanitized_url}' WHERE id = #{project['id']}") - fake_import_data = FakeProjectImportData.new - fake_import_data.credentials = sanitizer.credentials - execute("UPDATE project_import_data SET encrypted_credentials = '#{fake_import_data.encrypted_credentials}' WHERE project_id = #{project['id']}") + projects_with_wrong_import_url.find_in_batches do |project_batch| + project_batch.each do |project| + sanitizer = Gitlab::ImportUrlSanitizer.new(project["import_url"]) + + ActiveRecord::Base.transaction do + execute("UPDATE projects SET import_url = '#{quote(sanitizer.sanitized_url)}' WHERE id = #{project['id']}") + fake_import_data = FakeProjectImportData.new + fake_import_data.credentials = sanitizer.credentials + execute("UPDATE project_import_data SET encrypted_credentials = '#{quote(fake_import_data.encrypted_credentials)}' WHERE project_id = #{project['id']}") + end end end end + def down + + end + def projects_with_wrong_import_url # TODO Check live with #operations for possible false positives. Also, consider regex? But may have issues MySQL/PSQL - select_all("SELECT p.id, p.import_url from projects p WHERE p.import_url LIKE '%//%:%@%' or p.import_url like '#{"_"*40}@github.com%'") + select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND (p.import_url LIKE '%//%:%@%' OR p.import_url LIKE '#{"_"*40}@github.com%')") + end + + def quote(value) + ActiveRecord::Base.connection.quote(value) end end diff --git a/db/schema.rb b/db/schema.rb index 5b2f5aa3ddd..72a3ec2fb10 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -416,7 +416,7 @@ ActiveRecord::Schema.define(version: 20160316204731) do t.string "state" t.integer "iid" t.integer "updated_by_id" - t.boolean "confidential", default: false + t.boolean "confidential", default: false end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree @@ -684,6 +684,8 @@ ActiveRecord::Schema.define(version: 20160316204731) do create_table "project_import_data", force: :cascade do |t| t.integer "project_id" t.text "data" + t.text "encrypted_credentials" + t.text "encrypted_credentials_iv" end create_table "projects", force: :cascade do |t| diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index a96cfa8af9d..d407be5dcf4 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -8,7 +8,7 @@ module Gitlab def initialize(project) @project = project credentials = project.import_data.credentials if import_data - @client = Client.new(credentials["github_access_token"]) + @client = Client.new(credentials[:user]) @formatter = Gitlab::ImportFormatter.new end diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb index 52aba93a51d..f4221003db5 100644 --- a/lib/gitlab/github_import/project_creator.rb +++ b/lib/gitlab/github_import/project_creator.rb @@ -11,7 +11,7 @@ module Gitlab end def execute - project = ::Projects::CreateService.new( + ::Projects::CreateService.new( current_user, name: repo.name, path: repo.name, @@ -20,19 +20,9 @@ module Gitlab visibility_level: repo.private ? Gitlab::VisibilityLevel::PRIVATE : Gitlab::VisibilityLevel::PUBLIC, import_type: "github", import_source: repo.full_name, - import_url: repo.clone_url, + import_url: repo.clone_url.sub("https://", "https://#{@session_data[:github_access_token]}@"), wiki_enabled: !repo.has_wiki? # If repo has wiki we'll import it later ).execute - - create_import_data(project) - project - end - - private - - def create_import_data(project) - project.create_import_data( - credentials: { github_access_token: session_data.delete(:github_access_token) }) end end end diff --git a/lib/gitlab/github_import/wiki_formatter.rb b/lib/gitlab/github_import/wiki_formatter.rb index 8be82924107..db2c49a497a 100644 --- a/lib/gitlab/github_import/wiki_formatter.rb +++ b/lib/gitlab/github_import/wiki_formatter.rb @@ -12,9 +12,7 @@ module Gitlab end def import_url - import_url = Gitlab::ImportUrlExposer.expose(import_url: project.import_url, - credentials: project.import_data.credentials) - import_url.sub(/\.git\z/, ".wiki.git") + project.import_url.import_url.sub(/\.git\z/, ".wiki.git") end end end diff --git a/lib/gitlab/import_url_sanitizer.rb b/lib/gitlab/import_url_sanitizer.rb new file mode 100644 index 00000000000..dfbc4f8303c --- /dev/null +++ b/lib/gitlab/import_url_sanitizer.rb @@ -0,0 +1,24 @@ +module Gitlab + class ImportUrlSanitizer + def initialize(url) + @url = URI.parse(url) + end + + def sanitized_url + @sanitized_url ||= safe_url.to_s + end + + def credentials + @credentials ||= { user: @url.user, password: @url.password } + end + + private + + def safe_url + safe_url = @url.dup + safe_url.password = nil + safe_url.user = nil + safe_url + end + end +end \ No newline at end of file -- cgit v1.2.1 From 8d7d9c8daa61d58a17fea648771a1bb6c9341304 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 13:23:35 +0100 Subject: added missing column for current att_encrypted version --- .../20160302151724_add_import_credentials_to_project_import_data.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb index ff3d8b466dc..dd2b3613983 100644 --- a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb +++ b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb @@ -2,5 +2,6 @@ class AddImportCredentialsToProjectImportData < ActiveRecord::Migration def change add_column :project_import_data, :encrypted_credentials, :text add_column :project_import_data, :encrypted_credentials_iv, :text + add_column :project_import_data, :encrypted_credentials_salt, :text end end -- cgit v1.2.1 From 030b13944534be505dc97667ce2094ed6c588f12 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 15:11:05 +0100 Subject: more refactoring --- app/models/project.rb | 12 +++++--- ...152808_remove_wrong_import_url_from_projects.rb | 23 ++++++-------- db/schema.rb | 11 +++---- lib/gitlab/github_import/importer.rb | 13 ++++++-- lib/gitlab/github_import/wiki_formatter.rb | 2 +- lib/gitlab/import_url.rb | 36 ++++++++++++++++++++++ lib/gitlab/import_url_exposer.rb | 13 -------- lib/gitlab/import_url_sanitizer.rb | 24 --------------- .../gitlab/github_import/project_creator_spec.rb | 5 +-- .../gitlab/github_import/wiki_formatter_spec.rb | 7 +---- spec/lib/gitlab/import_url_exposer_spec.rb | 13 -------- spec/lib/gitlab/import_url_spec.rb | 21 +++++++++++++ 12 files changed, 94 insertions(+), 86 deletions(-) create mode 100644 lib/gitlab/import_url.rb delete mode 100644 lib/gitlab/import_url_exposer.rb delete mode 100644 lib/gitlab/import_url_sanitizer.rb delete mode 100644 spec/lib/gitlab/import_url_exposer_spec.rb create mode 100644 spec/lib/gitlab/import_url_spec.rb diff --git a/app/models/project.rb b/app/models/project.rb index ab4afd4159e..4e5fa8821ea 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -405,14 +405,17 @@ class Project < ActiveRecord::Base end def import_url=(value) - sanitizer = Gitlab::ImportUrlSanitizer.new(value) - self[:import_url] = sanitizer.sanitized_url - create_import_data(credentials: sanitizer.credentials) + import_url = Gitlab::ImportUrl.new(value) + create_import_data(credentials: import_url.credentials) + super(import_url.sanitized_url) end def import_url if import_data - Gitlab::ImportUrlExposer.expose(import_url: self[:import_url], credentials: import_data.credentials) + import_url = Gitlab::ImportUrl.new(super, credentials: import_data.credentials) + import_url.full_url + else + super end end @@ -447,6 +450,7 @@ class Project < ActiveRecord::Base def safe_import_url result = URI.parse(self.import_url) result.password = '*****' unless result.password.nil? + result.user = '*****' unless result.user.nil? #tokens or other data may be saved as user result.to_s rescue self.import_url diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index f98ec925ac4..fd718ef3974 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -1,3 +1,6 @@ +# Loops through old importer projects that kept a token/password in the import URL +# and encrypts the credentials into a separate field in project#import_data +# #down method not supported class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration class FakeProjectImportData @@ -7,24 +10,18 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def up - projects_with_wrong_import_url.find_in_batches do |project_batch| - project_batch.each do |project| - sanitizer = Gitlab::ImportUrlSanitizer.new(project["import_url"]) + projects_with_wrong_import_url do |project| + import_url = Gitlab::ImportUrl.new(project["import_url"]) - ActiveRecord::Base.transaction do - execute("UPDATE projects SET import_url = '#{quote(sanitizer.sanitized_url)}' WHERE id = #{project['id']}") - fake_import_data = FakeProjectImportData.new - fake_import_data.credentials = sanitizer.credentials - execute("UPDATE project_import_data SET encrypted_credentials = '#{quote(fake_import_data.encrypted_credentials)}' WHERE project_id = #{project['id']}") - end + ActiveRecord::Base.transaction do + execute("UPDATE projects SET import_url = '#{quote(import_url.sanitized_url)}' WHERE id = #{project['id']}") + fake_import_data = FakeProjectImportData.new + fake_import_data.credentials = import_url.credentials + execute("UPDATE project_import_data SET encrypted_credentials = '#{quote(fake_import_data.encrypted_credentials)}' WHERE project_id = #{project['id']}") end end end - def down - - end - def projects_with_wrong_import_url # TODO Check live with #operations for possible false positives. Also, consider regex? But may have issues MySQL/PSQL select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND (p.import_url LIKE '%//%:%@%' OR p.import_url LIKE '#{"_"*40}@github.com%')") diff --git a/db/schema.rb b/db/schema.rb index 72a3ec2fb10..02300c028d4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160316204731) do +ActiveRecord::Schema.define(version: 20160302151724) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -260,7 +260,6 @@ ActiveRecord::Schema.define(version: 20160316204731) do end add_index "ci_runners", ["description"], name: "index_ci_runners_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} - add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree add_index "ci_runners", ["token"], name: "index_ci_runners_on_token_trigram", using: :gin, opclasses: {"token"=>"gin_trgm_ops"} create_table "ci_services", force: :cascade do |t| @@ -686,6 +685,7 @@ ActiveRecord::Schema.define(version: 20160316204731) do t.text "data" t.text "encrypted_credentials" t.text "encrypted_credentials_iv" + t.text "encrypted_credentials_salt" end create_table "projects", force: :cascade do |t| @@ -725,7 +725,6 @@ ActiveRecord::Schema.define(version: 20160316204731) do t.boolean "pending_delete", default: false t.boolean "public_builds", default: true, null: false t.string "main_language" - t.integer "pushes_since_gc", default: 0 end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree @@ -811,6 +810,7 @@ ActiveRecord::Schema.define(version: 20160316204731) do t.string "file_name" t.string "type" t.integer "visibility_level", default: 0, null: false + t.datetime "expires_at" end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree @@ -869,7 +869,7 @@ ActiveRecord::Schema.define(version: 20160316204731) do create_table "todos", force: :cascade do |t| t.integer "user_id", null: false t.integer "project_id", null: false - t.integer "target_id" + t.integer "target_id", null: false t.string "target_type", null: false t.integer "author_id" t.integer "action", null: false @@ -877,11 +877,9 @@ ActiveRecord::Schema.define(version: 20160316204731) do t.datetime "created_at" t.datetime "updated_at" t.integer "note_id" - t.string "commit_id" end add_index "todos", ["author_id"], name: "index_todos_on_author_id", using: :btree - add_index "todos", ["commit_id"], name: "index_todos_on_commit_id", using: :btree add_index "todos", ["note_id"], name: "index_todos_on_note_id", using: :btree add_index "todos", ["project_id"], name: "index_todos_on_project_id", using: :btree add_index "todos", ["state"], name: "index_todos_on_state", using: :btree @@ -946,7 +944,6 @@ ActiveRecord::Schema.define(version: 20160316204731) do t.string "unlock_token" t.datetime "otp_grace_period_started_at" t.boolean "ldap_email", default: false, null: false - t.boolean "external", default: false end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index d407be5dcf4..0b1ed510229 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -7,9 +7,12 @@ module Gitlab def initialize(project) @project = project - credentials = project.import_data.credentials if import_data - @client = Client.new(credentials[:user]) - @formatter = Gitlab::ImportFormatter.new + if import_data_credentials + @client = Client.new(import_data_credentials[:user]) + @formatter = Gitlab::ImportFormatter.new + else + raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + end end def execute @@ -18,6 +21,10 @@ module Gitlab private + def import_data_credentials + @import_data_credentials ||= project.import_data.credentials if project.import_data + end + def import_issues client.list_issues(project.import_source, state: :all, sort: :created, diff --git a/lib/gitlab/github_import/wiki_formatter.rb b/lib/gitlab/github_import/wiki_formatter.rb index db2c49a497a..6c592ff469c 100644 --- a/lib/gitlab/github_import/wiki_formatter.rb +++ b/lib/gitlab/github_import/wiki_formatter.rb @@ -12,7 +12,7 @@ module Gitlab end def import_url - project.import_url.import_url.sub(/\.git\z/, ".wiki.git") + project.import_url.sub(/\.git\z/, ".wiki.git") end end end diff --git a/lib/gitlab/import_url.rb b/lib/gitlab/import_url.rb new file mode 100644 index 00000000000..7358edac2ee --- /dev/null +++ b/lib/gitlab/import_url.rb @@ -0,0 +1,36 @@ +module Gitlab + class ImportUrl + def initialize(url, credentials: nil) + @url = URI.parse(url) + @credentials = credentials + end + + def sanitized_url + @sanitized_url ||= safe_url.to_s + end + + def credentials + @credentials ||= { user: @url.user, password: @url.password } + end + + def full_url + @full_url ||= generate_full_url.to_s + end + + private + + def generate_full_url + @full_url = @url.dup + @full_url.user = @credentials[:user] + @full_url.password = @credentials[:password] + @full_url + end + + def safe_url + safe_url = @url.dup + safe_url.password = nil + safe_url.user = nil + safe_url + end + end +end \ No newline at end of file diff --git a/lib/gitlab/import_url_exposer.rb b/lib/gitlab/import_url_exposer.rb deleted file mode 100644 index bf03f5a6daf..00000000000 --- a/lib/gitlab/import_url_exposer.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Gitlab - # Exposes an import URL that includes the credentials unencrypted. - # Extracted to its own class to prevent unintended use. - module ImportUrlExposer - - def self.expose(import_url:, credentials: ) - uri = URI.parse(import_url) - uri.user = credentials[:user] - uri.password = credentials[:password] - uri - end - end -end diff --git a/lib/gitlab/import_url_sanitizer.rb b/lib/gitlab/import_url_sanitizer.rb deleted file mode 100644 index dfbc4f8303c..00000000000 --- a/lib/gitlab/import_url_sanitizer.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Gitlab - class ImportUrlSanitizer - def initialize(url) - @url = URI.parse(url) - end - - def sanitized_url - @sanitized_url ||= safe_url.to_s - end - - def credentials - @credentials ||= { user: @url.user, password: @url.password } - end - - private - - def safe_url - safe_url = @url.dup - safe_url.password = nil - safe_url.user = nil - safe_url - end - end -end \ No newline at end of file diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb index 290c855642a..2092c1b9584 100644 --- a/spec/lib/gitlab/github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/github_import/project_creator_spec.rb @@ -26,8 +26,9 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do project_creator = Gitlab::GithubImport::ProjectCreator.new(repo, namespace, user, access_params) project = project_creator.execute - expect(project.import_url).to eq("https://gitlab.com/asd/vim.git") - expect(project.import_data.credentials).to eq(:github_access_token => "asdffg") + expect(project.import_url).to eq("https://asdffg@gitlab.com/asd/vim.git") + expect(project.safe_import_url).to eq("https://*****@gitlab.com/asd/vim.git") + expect(project.import_data.credentials).to eq(:user => "asdffg", :password => nil) expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) end end diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb index 46d5c2f3296..91cd370987d 100644 --- a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb @@ -3,12 +3,7 @@ require 'spec_helper' describe Gitlab::GithubImport::WikiFormatter, lib: true do let(:project) do create(:project, namespace: create(:namespace, path: 'gitlabhq'), - import_url: 'https://github.com/gitlabhq/sample.gitlabhq.git') - end - - let!(:project_import_data) do - create(:project_import_data, credentials: { github_access_token: 'xxx' }, - project: project) + import_url: 'https://xxx@github.com/gitlabhq/sample.gitlabhq.git') end subject(:wiki) { described_class.new(project) } diff --git a/spec/lib/gitlab/import_url_exposer_spec.rb b/spec/lib/gitlab/import_url_exposer_spec.rb deleted file mode 100644 index 878947caea1..00000000000 --- a/spec/lib/gitlab/import_url_exposer_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'spec_helper' - -describe 'Gitlab::ImportUrlExposer' do - - describe :expose do - let(:credentials) do - Gitlab::ImportUrlExposer.expose(import_url: "https://github.com/me/project.git", credentials: {user: 'blah', password: 'password'}) - end - - it { expect(credentials).to be_a(URI) } - it { expect(credentials.to_s).to eq("https://blah:password@github.com/me/project.git") } - end -end diff --git a/spec/lib/gitlab/import_url_spec.rb b/spec/lib/gitlab/import_url_spec.rb new file mode 100644 index 00000000000..f758cb8693c --- /dev/null +++ b/spec/lib/gitlab/import_url_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Gitlab::ImportUrl do + + let(:credentials) { { user: 'blah', password: 'password' } } + let(:import_url) do + Gitlab::ImportUrl.new("https://github.com/me/project.git", credentials: credentials) + end + + describe :full_url do + it { expect(import_url.full_url).to eq("https://blah:password@github.com/me/project.git") } + end + + describe :sanitized_url do + it { expect(import_url.sanitized_url).to eq("https://github.com/me/project.git") } + end + + describe :credentials do + it { expect(import_url.credentials).to eq(credentials) } + end +end -- cgit v1.2.1 From bd8a77674f7226aafa4bd9befa8ed7482c372dc1 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 17:16:27 +0100 Subject: fixed few issues with the migration --- app/models/project.rb | 3 ++- app/models/project_import_data.rb | 2 +- ...152808_remove_wrong_import_url_from_projects.rb | 26 ++++++++++++++++++---- lib/gitlab/import_url.rb | 1 + 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 4e5fa8821ea..242ad19b115 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -406,6 +406,7 @@ class Project < ActiveRecord::Base def import_url=(value) import_url = Gitlab::ImportUrl.new(value) + # deletes any existing import_data create_import_data(credentials: import_url.credentials) super(import_url.sanitized_url) end @@ -450,7 +451,7 @@ class Project < ActiveRecord::Base def safe_import_url result = URI.parse(self.import_url) result.password = '*****' unless result.password.nil? - result.user = '*****' unless result.user.nil? #tokens or other data may be saved as user + result.user = '*****' unless result.user.nil? || result.user == "git" #tokens or other data may be saved as user result.to_s rescue self.import_url diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index f3b9daa0d1a..420c01f9960 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -12,7 +12,7 @@ require 'file_size_validator' class ProjectImportData < ActiveRecord::Base belongs_to :project - attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true + attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true, :mode => :per_attribute_iv_and_salt serialize :data, JSON diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index fd718ef3974..0f7da3103b8 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -6,25 +6,43 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration class FakeProjectImportData extend AttrEncrypted attr_accessor :credentials - attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true + attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true, :mode => :per_attribute_iv_and_salt end def up + byebug projects_with_wrong_import_url do |project| import_url = Gitlab::ImportUrl.new(project["import_url"]) ActiveRecord::Base.transaction do - execute("UPDATE projects SET import_url = '#{quote(import_url.sanitized_url)}' WHERE id = #{project['id']}") + execute("UPDATE projects SET import_url = #{quote(import_url.sanitized_url)} WHERE id = #{project['id']}") fake_import_data = FakeProjectImportData.new fake_import_data.credentials = import_url.credentials - execute("UPDATE project_import_data SET encrypted_credentials = '#{quote(fake_import_data.encrypted_credentials)}' WHERE project_id = #{project['id']}") + project_import_data = project_import_data(project['id']) + if project_import_data + execute(update_import_data_sql(project_import_data['id'], fake_import_data)) + else + execute(insert_import_data_sql(project['id'], fake_import_data)) + end end end end + def insert_import_data_sql(project_id, fake_import_data) + %( INSERT into project_import_data (encrypted_credentials, project_id, encrypted_credentials_iv, encrypted_credentials_salt) VALUES ( #{quote(fake_import_data.encrypted_credentials)}, '#{project_id}', #{quote(fake_import_data.encrypted_credentials_iv)}, #{quote(fake_import_data.encrypted_credentials_salt)})) + end + + def update_import_data_sql(id, fake_import_data) + %( UPDATE project_import_data SET encrypted_credentials = #{quote(fake_import_data.encrypted_credentials)}, encrypted_credentials_iv = #{quote(fake_import_data.encrypted_credentials_iv)}, encrypted_credentials_salt = #{quote(fake_import_data.encrypted_credentials_salt)} WHERE id = '#{id}') + end + def projects_with_wrong_import_url # TODO Check live with #operations for possible false positives. Also, consider regex? But may have issues MySQL/PSQL - select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND (p.import_url LIKE '%//%:%@%' OR p.import_url LIKE '#{"_"*40}@github.com%')") + select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND (p.import_url LIKE '%//%:%@%' OR p.import_url LIKE 'https___#{"_"*40}@github.com%')") + end + + def project_import_data(project_id) + select_one("SELECT id FROM project_import_data WHERE project_id = '#{project_id}'") end def quote(value) diff --git a/lib/gitlab/import_url.rb b/lib/gitlab/import_url.rb index 7358edac2ee..aa430920252 100644 --- a/lib/gitlab/import_url.rb +++ b/lib/gitlab/import_url.rb @@ -20,6 +20,7 @@ module Gitlab private def generate_full_url + return @url unless @credentials @full_url = @url.dup @full_url.user = @credentials[:user] @full_url.password = @credentials[:password] -- cgit v1.2.1 From dff4050f1d3f00815c095ec2645bd935f14e51a7 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 17:29:19 +0100 Subject: fixed some rubocop warnings --- app/models/project_import_data.rb | 2 +- lib/gitlab/import_url.rb | 2 +- spec/factories/project_import_data.rb | 2 +- spec/lib/gitlab/github_import/project_creator_spec.rb | 2 +- spec/lib/gitlab/github_import/wiki_formatter_spec.rb | 3 ++- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 420c01f9960..ba4334055d6 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -12,7 +12,7 @@ require 'file_size_validator' class ProjectImportData < ActiveRecord::Base belongs_to :project - attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true, :mode => :per_attribute_iv_and_salt + attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true, mode: :per_attribute_iv_and_salt serialize :data, JSON diff --git a/lib/gitlab/import_url.rb b/lib/gitlab/import_url.rb index aa430920252..adcbcc43d24 100644 --- a/lib/gitlab/import_url.rb +++ b/lib/gitlab/import_url.rb @@ -34,4 +34,4 @@ module Gitlab safe_url end end -end \ No newline at end of file +end diff --git a/spec/factories/project_import_data.rb b/spec/factories/project_import_data.rb index 18393cdda98..a799af9996c 100644 --- a/spec/factories/project_import_data.rb +++ b/spec/factories/project_import_data.rb @@ -2,4 +2,4 @@ FactoryGirl.define do factory :project_import_data, class: ProjectImportData do data "test" end -end \ No newline at end of file +end diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb index 2092c1b9584..0f363b8b0aa 100644 --- a/spec/lib/gitlab/github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/github_import/project_creator_spec.rb @@ -28,7 +28,7 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do expect(project.import_url).to eq("https://asdffg@gitlab.com/asd/vim.git") expect(project.safe_import_url).to eq("https://*****@gitlab.com/asd/vim.git") - expect(project.import_data.credentials).to eq(:user => "asdffg", :password => nil) + expect(project.import_data.credentials).to eq(user: "asdffg", password: nil) expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) end end diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb index 91cd370987d..1bd29b8a563 100644 --- a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' describe Gitlab::GithubImport::WikiFormatter, lib: true do let(:project) do - create(:project, namespace: create(:namespace, path: 'gitlabhq'), + create(:project, + namespace: create(:namespace, path: 'gitlabhq'), import_url: 'https://xxx@github.com/gitlabhq/sample.gitlabhq.git') end -- cgit v1.2.1 From 6dfb5d7cad3e3cefd766e30a4c25a9549fa2b041 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 17:34:20 +0100 Subject: remove byebug --- db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 0f7da3103b8..6b4b6726fc5 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -10,7 +10,6 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def up - byebug projects_with_wrong_import_url do |project| import_url = Gitlab::ImportUrl.new(project["import_url"]) -- cgit v1.2.1 From ced56641bfc42c8380af1760fae93ba37bf2e785 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 18:09:47 +0100 Subject: refactored code based on feedback --- app/models/project.rb | 10 ++++++++-- lib/gitlab/import_url.rb | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 242ad19b115..a1aa1d5fdbc 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -406,8 +406,7 @@ class Project < ActiveRecord::Base def import_url=(value) import_url = Gitlab::ImportUrl.new(value) - # deletes any existing import_data - create_import_data(credentials: import_url.credentials) + create_or_update_import_data(import_url.credentials) super(import_url.sanitized_url) end @@ -420,6 +419,13 @@ class Project < ActiveRecord::Base end end + def create_or_update_import_data(credentials) + project_import_data = import_data || ProjectImportData.new + project_import_data.credentials = credentials + project_import_data.project_id = id + project_import_data.save + end + def import? external_import? || forked? end diff --git a/lib/gitlab/import_url.rb b/lib/gitlab/import_url.rb index adcbcc43d24..5b18d67ddc3 100644 --- a/lib/gitlab/import_url.rb +++ b/lib/gitlab/import_url.rb @@ -20,10 +20,10 @@ module Gitlab private def generate_full_url - return @url unless @credentials + return @url unless credentials @full_url = @url.dup - @full_url.user = @credentials[:user] - @full_url.password = @credentials[:password] + @full_url.user = credentials[:user] + @full_url.password = credentials[:password] @full_url end -- cgit v1.2.1 From 19bfdcf4689e9c994f5a630da9cff75e5baa0e55 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 18:20:25 +0100 Subject: fix import data creation --- app/models/project.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index a1aa1d5fdbc..2a530715a9e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -420,9 +420,8 @@ class Project < ActiveRecord::Base end def create_or_update_import_data(credentials) - project_import_data = import_data || ProjectImportData.new + project_import_data = import_data || build_import_data project_import_data.credentials = credentials - project_import_data.project_id = id project_import_data.save end -- cgit v1.2.1 From 868e4918f93021430162ec097d3362f313a4bb80 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 18:38:06 +0100 Subject: updated migration --- .../20160302152808_remove_wrong_import_url_from_projects.rb | 3 +-- db/schema.rb | 10 +++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 6b4b6726fc5..cf6b983323e 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -36,8 +36,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def projects_with_wrong_import_url - # TODO Check live with #operations for possible false positives. Also, consider regex? But may have issues MySQL/PSQL - select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND (p.import_url LIKE '%//%:%@%' OR p.import_url LIKE 'https___#{"_"*40}@github.com%')") + select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND p.import_type = 'github' AND p.import_url LIKE '%//%:%@%'") end def project_import_data(project_id) diff --git a/db/schema.rb b/db/schema.rb index 02300c028d4..71f1e1e496e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160302151724) do +ActiveRecord::Schema.define(version: 20160316204731) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -260,6 +260,7 @@ ActiveRecord::Schema.define(version: 20160302151724) do end add_index "ci_runners", ["description"], name: "index_ci_runners_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} + add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree add_index "ci_runners", ["token"], name: "index_ci_runners_on_token_trigram", using: :gin, opclasses: {"token"=>"gin_trgm_ops"} create_table "ci_services", force: :cascade do |t| @@ -725,6 +726,7 @@ ActiveRecord::Schema.define(version: 20160302151724) do t.boolean "pending_delete", default: false t.boolean "public_builds", default: true, null: false t.string "main_language" + t.integer "pushes_since_gc", default: 0 end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree @@ -810,7 +812,6 @@ ActiveRecord::Schema.define(version: 20160302151724) do t.string "file_name" t.string "type" t.integer "visibility_level", default: 0, null: false - t.datetime "expires_at" end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree @@ -869,7 +870,7 @@ ActiveRecord::Schema.define(version: 20160302151724) do create_table "todos", force: :cascade do |t| t.integer "user_id", null: false t.integer "project_id", null: false - t.integer "target_id", null: false + t.integer "target_id" t.string "target_type", null: false t.integer "author_id" t.integer "action", null: false @@ -877,9 +878,11 @@ ActiveRecord::Schema.define(version: 20160302151724) do t.datetime "created_at" t.datetime "updated_at" t.integer "note_id" + t.string "commit_id" end add_index "todos", ["author_id"], name: "index_todos_on_author_id", using: :btree + add_index "todos", ["commit_id"], name: "index_todos_on_commit_id", using: :btree add_index "todos", ["note_id"], name: "index_todos_on_note_id", using: :btree add_index "todos", ["project_id"], name: "index_todos_on_project_id", using: :btree add_index "todos", ["state"], name: "index_todos_on_state", using: :btree @@ -944,6 +947,7 @@ ActiveRecord::Schema.define(version: 20160302151724) do t.string "unlock_token" t.datetime "otp_grace_period_started_at" t.boolean "ldap_email", default: false, null: false + t.boolean "external", default: false end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree -- cgit v1.2.1 From 1b8d995492baca8984bde950e0449dad6342befc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 11:32:15 +0100 Subject: refactoring migration to add bitbucket stuff --- ...152808_remove_wrong_import_url_from_projects.rb | 55 ++++++++++++++++++---- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index cf6b983323e..8ba68e61c74 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -10,23 +10,52 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def up + process_projects_with_wrong_url + process_bitbucket_projects + end + + def process_projects_with_wrong_url projects_with_wrong_import_url do |project| import_url = Gitlab::ImportUrl.new(project["import_url"]) ActiveRecord::Base.transaction do - execute("UPDATE projects SET import_url = #{quote(import_url.sanitized_url)} WHERE id = #{project['id']}") - fake_import_data = FakeProjectImportData.new - fake_import_data.credentials = import_url.credentials - project_import_data = project_import_data(project['id']) - if project_import_data - execute(update_import_data_sql(project_import_data['id'], fake_import_data)) - else - execute(insert_import_data_sql(project['id'], fake_import_data)) - end + update_import_url(import_url, project) + update_import_data(import_url, project) + end + end + end + + def process_bitbucket_projects + bitbucket_projects_with_wrong_import_url do |bitbucket_data| + data = bitbucket_data['data'] + data_hash = YAML::load(data) + if data_hash && data_hash['bb_session'] + update_import_data_for_bitbucket(data_hash, bitbucket_data['id']) end end end + def update_import_data(import_url, project) + fake_import_data = FakeProjectImportData.new + fake_import_data.credentials = import_url.credentials + project_import_data = project_import_data(project['id']) + if project_import_data + execute(update_import_data_sql(project_import_data['id'], fake_import_data)) + else + execute(insert_import_data_sql(project['id'], fake_import_data)) + end + end + + def update_import_data_for_bitbucket(data_hash, import_data_id) + fake_import_data = FakeProjectImportData.new + fake_import_data.credentials = data_hash + execute(update_import_data_sql(import_data_id, fake_import_data)) + end + + def update_import_url(import_url, project) + execute("UPDATE projects SET import_url = #{quote(import_url.sanitized_url)} WHERE id = #{project['id']}") + end + def insert_import_data_sql(project_id, fake_import_data) %( INSERT into project_import_data (encrypted_credentials, project_id, encrypted_credentials_iv, encrypted_credentials_salt) VALUES ( #{quote(fake_import_data.encrypted_credentials)}, '#{project_id}', #{quote(fake_import_data.encrypted_credentials_iv)}, #{quote(fake_import_data.encrypted_credentials_salt)})) end @@ -35,8 +64,14 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration %( UPDATE project_import_data SET encrypted_credentials = #{quote(fake_import_data.encrypted_credentials)}, encrypted_credentials_iv = #{quote(fake_import_data.encrypted_credentials_iv)}, encrypted_credentials_salt = #{quote(fake_import_data.encrypted_credentials_salt)} WHERE id = '#{id}') end + #Github projects with token, and any user:password@ based URL def projects_with_wrong_import_url - select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND p.import_type = 'github' AND p.import_url LIKE '%//%:%@%'") + select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND (p.import_url LIKE '%//%:%@%' OR p.import_url LIKE 'https___#{"_"*40}@github.com%')") + end + + # All bitbucket imports + def bitbucket_projects_with_wrong_import_url + select_all("SELECT p.id, p.import_urlselect_all("SELECT i.id, p.import_url, i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_type = 'bitbucket' "), i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_type = 'bitbucket' ") end def project_import_data(project_id) -- cgit v1.2.1 From 23146fca1878c9f6e90deed6856d3da2c731d513 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 12:25:28 +0100 Subject: update bitbucket importer --- ...60302152808_remove_wrong_import_url_from_projects.rb | 2 +- lib/gitlab/bitbucket_import/importer.rb | 17 ++++++++++++----- lib/gitlab/github_import/importer.rb | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 8ba68e61c74..4ca245035d3 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -71,7 +71,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration # All bitbucket imports def bitbucket_projects_with_wrong_import_url - select_all("SELECT p.id, p.import_urlselect_all("SELECT i.id, p.import_url, i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_type = 'bitbucket' "), i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_type = 'bitbucket' ") + select_all("SELECT i.id, p.import_url, i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_type = 'bitbucket' ") end def project_import_data(project_id) diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 46e51a4bf6d..36110962e0c 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -5,11 +5,14 @@ module Gitlab def initialize(project) @project = project - import_data = project.import_data.try(:data) - bb_session = import_data["bb_session"] if import_data - @client = Client.new(bb_session["bitbucket_access_token"], - bb_session["bitbucket_access_token_secret"]) - @formatter = Gitlab::ImportFormatter.new + if import_data_credentials && import_data_credentials['bb_session'] + token = import_data_credentials['bb_session']['bitbucket_access_token'] + token_secret = import_data_credentials['bb_session']['bitbucket_access_token_secret'] + @client = Client.new(token, token_secret) + @formatter = Gitlab::ImportFormatter.new + else + raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + end end def execute @@ -24,6 +27,10 @@ module Gitlab private + def import_data_credentials + @import_data_credentials ||= project.import_data.credentials if project.import_data + end + def gl_user_id(project, bitbucket_id) if bitbucket_id user = User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", bitbucket_id.to_s) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 0b1ed510229..a5d3ab5fcf1 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -8,7 +8,7 @@ module Gitlab def initialize(project) @project = project if import_data_credentials - @client = Client.new(import_data_credentials[:user]) + @client = Client.new(import_data_credentials['user']) @formatter = Gitlab::ImportFormatter.new else raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" -- cgit v1.2.1 From 3b78885eac025b5cf90e1a370b7bbbbd88cc9727 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 12:26:50 +0100 Subject: encrypt credentials in project_creator for bitbucket by default --- lib/gitlab/bitbucket_import/project_creator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index 03aac1a025a..c9ccecef0c3 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -23,7 +23,7 @@ module Gitlab import_url: "ssh://git@bitbucket.org/#{repo["owner"]}/#{repo["slug"]}.git", ).execute - project.create_import_data(data: { "bb_session" => session_data } ) + project.create_import_data(credentials: { "bb_session" => session_data } ) project end end -- cgit v1.2.1 From 8aafe685837d12b623f70eec86cae6e7cef9a849 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 17:53:53 +0100 Subject: first round of fixes and spec fixes --- app/models/project.rb | 5 +++-- lib/gitlab/bitbucket_import/importer.rb | 19 +--------------- lib/gitlab/bitbucket_import/importer_init.rb | 27 +++++++++++++++++++++++ lib/gitlab/bitbucket_import/key_deleter.rb | 13 ++--------- lib/gitlab/bitbucket_import/project_creator.rb | 4 ++-- spec/lib/gitlab/bitbucket_import/importer_spec.rb | 8 +++---- 6 files changed, 39 insertions(+), 37 deletions(-) create mode 100644 lib/gitlab/bitbucket_import/importer_init.rb diff --git a/app/models/project.rb b/app/models/project.rb index c4287d314ea..b4643563260 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -407,7 +407,7 @@ class Project < ActiveRecord::Base end def import_url - if import_data + if import_data && super import_url = Gitlab::ImportUrl.new(super, credentials: import_data.credentials) import_url.full_url else @@ -417,7 +417,8 @@ class Project < ActiveRecord::Base def create_or_update_import_data(credentials) project_import_data = import_data || build_import_data - project_import_data.credentials = credentials + project_import_data.credentials ||= {} + project_import_data.credentials.merge!(credentials) project_import_data.save end diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 36110962e0c..f80410641a9 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -1,19 +1,6 @@ module Gitlab module BitbucketImport - class Importer - attr_reader :project, :client - - def initialize(project) - @project = project - if import_data_credentials && import_data_credentials['bb_session'] - token = import_data_credentials['bb_session']['bitbucket_access_token'] - token_secret = import_data_credentials['bb_session']['bitbucket_access_token_secret'] - @client = Client.new(token, token_secret) - @formatter = Gitlab::ImportFormatter.new - else - raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" - end - end + class Importer < ImporterInit def execute import_issues if has_issues? @@ -27,10 +14,6 @@ module Gitlab private - def import_data_credentials - @import_data_credentials ||= project.import_data.credentials if project.import_data - end - def gl_user_id(project, bitbucket_id) if bitbucket_id user = User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", bitbucket_id.to_s) diff --git a/lib/gitlab/bitbucket_import/importer_init.rb b/lib/gitlab/bitbucket_import/importer_init.rb new file mode 100644 index 00000000000..6d81811d36b --- /dev/null +++ b/lib/gitlab/bitbucket_import/importer_init.rb @@ -0,0 +1,27 @@ +module Gitlab + module BitbucketImport + class ImporterInit + attr_reader :project, :client + + def initialize(project) + @project = project + if import_data_credentials && import_data_credentials['bb_session'] + token = import_data_credentials['bb_session']['bitbucket_access_token'] + token_secret = import_data_credentials['bb_session']['bitbucket_access_token_secret'] + @client = Client.new(token, token_secret) + @formatter = Gitlab::ImportFormatter.new + else + raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + end + end + + private + + def import_data_credentials + @import_data_credentials ||= project.import_data.credentials if project.import_data + end + end + end +end + + diff --git a/lib/gitlab/bitbucket_import/key_deleter.rb b/lib/gitlab/bitbucket_import/key_deleter.rb index f4dd393ad29..312ed581500 100644 --- a/lib/gitlab/bitbucket_import/key_deleter.rb +++ b/lib/gitlab/bitbucket_import/key_deleter.rb @@ -1,16 +1,7 @@ module Gitlab module BitbucketImport - class KeyDeleter - attr_reader :project, :current_user, :client - - def initialize(project) - @project = project - @current_user = project.creator - import_data = project.import_data.try(:data) - bb_session = import_data["bb_session"] if import_data - @client = Client.new(bb_session["bitbucket_access_token"], - bb_session["bitbucket_access_token_secret"]) - end + class KeyDeleter < ImporterInit + attr_reader :current_user def execute return false unless BitbucketImport.public_key.present? diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index c9ccecef0c3..0e6f66de321 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -22,8 +22,8 @@ module Gitlab import_source: "#{repo["owner"]}/#{repo["slug"]}", import_url: "ssh://git@bitbucket.org/#{repo["owner"]}/#{repo["slug"]}.git", ).execute - - project.create_import_data(credentials: { "bb_session" => session_data } ) + import_data = project.import_data + import_data.credentials.merge!("bb_session" => session_data) project end end diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index c413132abe5..1a833f255a5 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -34,9 +34,9 @@ describe Gitlab::BitbucketImport::Importer, lib: true do let(:project_identifier) { 'namespace/repo' } let(:data) do { - bb_session: { - bitbucket_access_token: "123456", - bitbucket_access_token_secret: "secret" + 'bb_session' => { + 'bitbucket_access_token' => "123456", + 'bitbucket_access_token_secret' => "secret" } } end @@ -44,7 +44,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do create( :project, import_source: project_identifier, - import_data: ProjectImportData.new(data: data) + import_data: ProjectImportData.new(credentials: data) ) end let(:importer) { Gitlab::BitbucketImport::Importer.new(project) } -- cgit v1.2.1 From c136edbbe343cbb54f135928db5d901c1cb65c4f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 18:03:54 +0100 Subject: fix gitlab import and spec --- lib/gitlab/gitlab_import/importer.rb | 11 +++++++---- lib/gitlab/gitlab_import/project_creator.rb | 1 - 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb index 850b73244c6..afc06d01ebd 100644 --- a/lib/gitlab/gitlab_import/importer.rb +++ b/lib/gitlab/gitlab_import/importer.rb @@ -5,10 +5,13 @@ module Gitlab def initialize(project) @project = project - import_data = project.import_data.try(:data) - gitlab_session = import_data["gitlab_session"] if import_data - @client = Client.new(gitlab_session["gitlab_access_token"]) - @formatter = Gitlab::ImportFormatter.new + credentials = import_data.credentials + if credentials && credentials["password"] + @client = Client.new(credentials["password"]) + @formatter = Gitlab::ImportFormatter.new + else + raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + end end def execute diff --git a/lib/gitlab/gitlab_import/project_creator.rb b/lib/gitlab/gitlab_import/project_creator.rb index 7baaadb813c..77c33db4b59 100644 --- a/lib/gitlab/gitlab_import/project_creator.rb +++ b/lib/gitlab/gitlab_import/project_creator.rb @@ -23,7 +23,6 @@ module Gitlab import_url: repo["http_url_to_repo"].sub("://", "://oauth2:#{@session_data[:gitlab_access_token]}@") ).execute - project.create_import_data(data: { "gitlab_session" => session_data } ) project end end -- cgit v1.2.1 From 46346caf5ba5af7ad9af6913e479f301a3ff2d2d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 18:14:13 +0100 Subject: fix rubocop warning --- lib/gitlab/bitbucket_import/importer_init.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/gitlab/bitbucket_import/importer_init.rb b/lib/gitlab/bitbucket_import/importer_init.rb index 6d81811d36b..08b710003e4 100644 --- a/lib/gitlab/bitbucket_import/importer_init.rb +++ b/lib/gitlab/bitbucket_import/importer_init.rb @@ -23,5 +23,3 @@ module Gitlab end end end - - -- cgit v1.2.1 From 8da04f6b64884c642e8e4b630a30e6400e47b09e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 23 Mar 2016 11:55:43 +0100 Subject: refactored migration a bit and fixed a few problems. Also added some logging --- ...152808_remove_wrong_import_url_from_projects.rb | 36 ++++++++++++++-------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 4ca245035d3..3d97a66c0ff 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -10,31 +10,41 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def up - process_projects_with_wrong_url - process_bitbucket_projects + say("Encrypting and migrating project import credentials...") + + say("Projects and Github projects with a wrong URL") + in_transaction { process_projects_with_wrong_url } + + say("Migrating bitbucket credentials...") + in_transaction { process_bitbucket_projects } end def process_projects_with_wrong_url - projects_with_wrong_import_url do |project| + projects_with_wrong_import_url.each do |project| import_url = Gitlab::ImportUrl.new(project["import_url"]) - ActiveRecord::Base.transaction do - update_import_url(import_url, project) - update_import_data(import_url, project) - end + update_import_url(import_url, project) + update_import_data(import_url, project) end end def process_bitbucket_projects - bitbucket_projects_with_wrong_import_url do |bitbucket_data| - data = bitbucket_data['data'] - data_hash = YAML::load(data) - if data_hash && data_hash['bb_session'] + bitbucket_projects_with_wrong_import_url.each do |bitbucket_data| + data_hash = YAML::load(bitbucket_data['data']) if bitbucket_data['data'] + if defined?(data_hash) && data_hash && data_hash['bb_session'] update_import_data_for_bitbucket(data_hash, bitbucket_data['id']) end end end + def in_transaction + say_with_time("Processing new transaction...") do + ActiveRecord::Base.transaction do + yield + end + end + end + def update_import_data(import_url, project) fake_import_data = FakeProjectImportData.new fake_import_data.credentials = import_url.credentials @@ -60,8 +70,8 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration %( INSERT into project_import_data (encrypted_credentials, project_id, encrypted_credentials_iv, encrypted_credentials_salt) VALUES ( #{quote(fake_import_data.encrypted_credentials)}, '#{project_id}', #{quote(fake_import_data.encrypted_credentials_iv)}, #{quote(fake_import_data.encrypted_credentials_salt)})) end - def update_import_data_sql(id, fake_import_data) - %( UPDATE project_import_data SET encrypted_credentials = #{quote(fake_import_data.encrypted_credentials)}, encrypted_credentials_iv = #{quote(fake_import_data.encrypted_credentials_iv)}, encrypted_credentials_salt = #{quote(fake_import_data.encrypted_credentials_salt)} WHERE id = '#{id}') + def update_import_data_sql(id, fake_import_data, data = 'NULL') + %( UPDATE project_import_data SET encrypted_credentials = #{quote(fake_import_data.encrypted_credentials)}, encrypted_credentials_iv = #{quote(fake_import_data.encrypted_credentials_iv)}, encrypted_credentials_salt = #{quote(fake_import_data.encrypted_credentials_salt)}, data = #{data} WHERE id = '#{id}') end #Github projects with token, and any user:password@ based URL -- cgit v1.2.1 From 6967871fc567cbadd63f26f1ef87c4008cc6387b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 23 Mar 2016 12:52:50 +0100 Subject: fogbugz importer, also refactored migration again to make it easier to add new importers --- ...152808_remove_wrong_import_url_from_projects.rb | 31 +++++++++++++--------- lib/gitlab/fogbugz_import/importer.rb | 13 ++++++--- lib/gitlab/fogbugz_import/project_creator.rb | 2 +- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 3d97a66c0ff..f3a4fc26be9 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -12,11 +12,14 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration def up say("Encrypting and migrating project import credentials...") - say("Projects and Github projects with a wrong URL") + say("Projects and Github projects with a wrong URL. Also, migrating Gitlab projects credentials.") in_transaction { process_projects_with_wrong_url } say("Migrating bitbucket credentials...") - in_transaction { process_bitbucket_projects } + in_transaction { process_project(import_type: 'bitbucket') } + + say("Migrating fogbugz credentials...") + in_transaction { process_project(import_type: 'fogbugz') } end def process_projects_with_wrong_url @@ -28,12 +31,16 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end end - def process_bitbucket_projects - bitbucket_projects_with_wrong_import_url.each do |bitbucket_data| - data_hash = YAML::load(bitbucket_data['data']) if bitbucket_data['data'] - if defined?(data_hash) && data_hash && data_hash['bb_session'] - update_import_data_for_bitbucket(data_hash, bitbucket_data['id']) - end + def process_project(import_type: ) + unencrypted_import_data(import_type: import_type).each do |data| + replace_data_credentials(data) + end + end + + def replace_data_credentials(data) + data_hash = YAML::load(data['data']) if data['data'] + if defined?(data_hash) && data_hash + update_with_encrypted_data(data_hash, data['id']) end end @@ -56,7 +63,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end end - def update_import_data_for_bitbucket(data_hash, import_data_id) + def update_with_encrypted_data(data_hash, import_data_id) fake_import_data = FakeProjectImportData.new fake_import_data.credentials = data_hash execute(update_import_data_sql(import_data_id, fake_import_data)) @@ -79,9 +86,9 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND (p.import_url LIKE '%//%:%@%' OR p.import_url LIKE 'https___#{"_"*40}@github.com%')") end - # All bitbucket imports - def bitbucket_projects_with_wrong_import_url - select_all("SELECT i.id, p.import_url, i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_type = 'bitbucket' ") + # All imports with data for import_type + def unencrypted_import_data(import_type: ) + select_all("SELECT i.id, p.import_url, i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_type = '#{import_type}' ") end def project_import_data(project_id) diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index db580b5e578..c33b3541dd8 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -8,9 +8,12 @@ module Gitlab import_data = project.import_data.try(:data) repo_data = import_data['repo'] if import_data - @repo = FogbugzImport::Repository.new(repo_data) - - @known_labels = Set.new + if import_data_credentials && import_data_credentials['repo'] + @repo = FogbugzImport::Repository.new(repo_data) + @known_labels = Set.new + else + raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + end end def execute @@ -30,6 +33,10 @@ module Gitlab private + def import_data_credentials + @import_data_credentials ||= project.import_data.credentials if project.import_data + end + def user_map @user_map ||= begin user_map = Hash.new diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb index e0163499e30..73d6272720a 100644 --- a/lib/gitlab/fogbugz_import/project_creator.rb +++ b/lib/gitlab/fogbugz_import/project_creator.rb @@ -25,7 +25,7 @@ module Gitlab ).execute project.create_import_data( - data: { + credentials: { 'repo' => repo.raw_data, 'user_map' => user_map, 'fb_session' => fb_session -- cgit v1.2.1 From cc4d04f97f891479c4d033196c6868e19528c51c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 23 Mar 2016 17:57:10 +0100 Subject: added rest of importers, fixed specs and some issues with the migration --- ...02152808_remove_wrong_import_url_from_projects.rb | 10 +++++++--- db/schema.rb | 4 ++-- lib/gitlab/google_code_import/importer.rb | 20 ++++++++++++-------- lib/gitlab/google_code_import/project_creator.rb | 2 +- spec/lib/gitlab/google_code_import/importer_spec.rb | 2 +- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index f3a4fc26be9..a55ab6a42b4 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -12,7 +12,8 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration def up say("Encrypting and migrating project import credentials...") - say("Projects and Github projects with a wrong URL. Also, migrating Gitlab projects credentials.") + # This should cover Github, Gitlab, Bitbucket user:password, token@domain, and other similar URLs. + say("Projects and Github projects with a wrong URL. It also migrates Gitlab project credentials.") in_transaction { process_projects_with_wrong_url } say("Migrating bitbucket credentials...") @@ -20,6 +21,9 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration say("Migrating fogbugz credentials...") in_transaction { process_project(import_type: 'fogbugz') } + + say("Migrating google code credentials...") + in_transaction { process_project(import_type: 'google_code') } end def process_projects_with_wrong_url @@ -39,7 +43,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration def replace_data_credentials(data) data_hash = YAML::load(data['data']) if data['data'] - if defined?(data_hash) && data_hash + if defined?(data_hash) && !data_hash.blank? update_with_encrypted_data(data_hash, data['id']) end end @@ -83,7 +87,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration #Github projects with token, and any user:password@ based URL def projects_with_wrong_import_url - select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND (p.import_url LIKE '%//%:%@%' OR p.import_url LIKE 'https___#{"_"*40}@github.com%')") + select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND p.import_url LIKE '%//%@%'") end # All imports with data for import_type diff --git a/db/schema.rb b/db/schema.rb index 53d12aa35dd..75509c35b05 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -417,9 +417,9 @@ ActiveRecord::Schema.define(version: 20160320204112) do t.string "state" t.integer "iid" t.integer "updated_by_id" - t.integer "moved_to_id" - t.boolean "confidential", default: false + t.boolean "confidential", default: false t.datetime "deleted_at" + t.integer "moved_to_id" end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb index 62da327931f..6b0715d1492 100644 --- a/lib/gitlab/google_code_import/importer.rb +++ b/lib/gitlab/google_code_import/importer.rb @@ -6,12 +6,13 @@ module Gitlab def initialize(project) @project = project - import_data = project.import_data.try(:data) - repo_data = import_data["repo"] if import_data - @repo = GoogleCodeImport::Repository.new(repo_data) - - @closed_statuses = [] - @known_labels = Set.new + if import_data_credentials && import_data_credentials['repo'] + @repo = GoogleCodeImport::Repository.new(import_data_credentials['repo']) + @closed_statuses = [] + @known_labels = Set.new + else + raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + end end def execute @@ -28,6 +29,10 @@ module Gitlab private + def import_data_credentials + @import_data_credentials ||= project.import_data.credentials if project.import_data + end + def user_map @user_map ||= begin user_map = Hash.new do |hash, user| @@ -35,8 +40,7 @@ module Gitlab Client.mask_email(user).sub("...", "\\.\\.\\.") end - import_data = project.import_data.try(:data) - stored_user_map = import_data["user_map"] if import_data + stored_user_map = import_data_credentials["user_map"] user_map.update(stored_user_map) if stored_user_map user_map diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb index 87821c23460..acd3a832d59 100644 --- a/lib/gitlab/google_code_import/project_creator.rb +++ b/lib/gitlab/google_code_import/project_creator.rb @@ -25,7 +25,7 @@ module Gitlab ).execute project.create_import_data( - data: { + credentials: { "repo" => repo.raw_data, "user_map" => user_map } diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb index 647631271e0..6ecf3d7182f 100644 --- a/spec/lib/gitlab/google_code_import/importer_spec.rb +++ b/spec/lib/gitlab/google_code_import/importer_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Importer, lib: true do subject { described_class.new(project) } before do - project.create_import_data(data: import_data) + project.create_import_data(credentials: import_data) end describe "#execute" do -- cgit v1.2.1 From 459ad34493c57b40fd431b18750fef85884d51e1 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 28 Mar 2016 16:35:03 +0200 Subject: refactored code based on feedback plus fixed a couple of other issues --- ...152808_remove_wrong_import_url_from_projects.rb | 66 ++++++++++++++-------- lib/gitlab/bitbucket_import/client.rb | 11 ++++ lib/gitlab/bitbucket_import/importer.rb | 9 ++- lib/gitlab/bitbucket_import/importer_init.rb | 25 -------- lib/gitlab/bitbucket_import/key_deleter.rb | 10 +++- lib/gitlab/google_code_import/importer.rb | 20 +++---- lib/gitlab/google_code_import/project_creator.rb | 2 +- .../lib/gitlab/google_code_import/importer_spec.rb | 2 +- 8 files changed, 81 insertions(+), 64 deletions(-) delete mode 100644 lib/gitlab/bitbucket_import/importer_init.rb diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index a55ab6a42b4..fb5d8591c09 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -16,14 +16,12 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration say("Projects and Github projects with a wrong URL. It also migrates Gitlab project credentials.") in_transaction { process_projects_with_wrong_url } - say("Migrating bitbucket credentials...") - in_transaction { process_project(import_type: 'bitbucket') } + say("Migrating bitbucket credentials...")# TODO remove last param + in_transaction { process_project(import_type: 'bitbucket', unencrypted_data: ['repo', 'user_map']) } say("Migrating fogbugz credentials...") - in_transaction { process_project(import_type: 'fogbugz') } + in_transaction { process_project(import_type: 'fogbugz', unencrypted_data: ['repo', 'user_map']) } - say("Migrating google code credentials...") - in_transaction { process_project(import_type: 'google_code') } end def process_projects_with_wrong_url @@ -35,19 +33,29 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end end - def process_project(import_type: ) + def process_project(import_type: , unencrypted_data: []) unencrypted_import_data(import_type: import_type).each do |data| - replace_data_credentials(data) + replace_data_credentials(data, unencrypted_data) end end - def replace_data_credentials(data) - data_hash = YAML::load(data['data']) if data['data'] + def replace_data_credentials(data, unencrypted_data) + data_hash = JSON.load(data['data']) if data['data'] if defined?(data_hash) && !data_hash.blank? - update_with_encrypted_data(data_hash, data['id']) + unencrypted_data_hash = encrypted_data_hash(data_hash, unencrypted_data) + update_with_encrypted_data(data_hash, data['id'], unencrypted_data_hash) end end + def encrypted_data_hash(data_hash, unencrypted_data) + return 'NULL' if unencrypted_data.empty? + new_data_hash = {} + unencrypted_data.each do |key| + new_data_hash[key] = data_hash.delete(key) if data_hash[key] + end + quote(new_data_hash.to_json) + end + def in_transaction say_with_time("Processing new transaction...") do ActiveRecord::Base.transaction do @@ -59,18 +67,18 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration def update_import_data(import_url, project) fake_import_data = FakeProjectImportData.new fake_import_data.credentials = import_url.credentials - project_import_data = project_import_data(project['id']) - if project_import_data - execute(update_import_data_sql(project_import_data['id'], fake_import_data)) + import_data_id = project['import_data_id'] + if import_data_id + execute(update_import_data_sql(import_data_id, fake_import_data)) else execute(insert_import_data_sql(project['id'], fake_import_data)) end end - def update_with_encrypted_data(data_hash, import_data_id) + def update_with_encrypted_data(data_hash, import_data_id, data_array = nil) fake_import_data = FakeProjectImportData.new fake_import_data.credentials = data_hash - execute(update_import_data_sql(import_data_id, fake_import_data)) + execute(update_import_data_sql(import_data_id, fake_import_data, data_array)) end def update_import_url(import_url, project) @@ -78,16 +86,34 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def insert_import_data_sql(project_id, fake_import_data) - %( INSERT into project_import_data (encrypted_credentials, project_id, encrypted_credentials_iv, encrypted_credentials_salt) VALUES ( #{quote(fake_import_data.encrypted_credentials)}, '#{project_id}', #{quote(fake_import_data.encrypted_credentials_iv)}, #{quote(fake_import_data.encrypted_credentials_salt)})) + %( + INSERT INTO project_import_data + (encrypted_credentials, + project_id, + encrypted_credentials_iv, + encrypted_credentials_salt) + VALUES ( #{quote(fake_import_data.encrypted_credentials)}, + '#{project_id}', + #{quote(fake_import_data.encrypted_credentials_iv)}, + #{quote(fake_import_data.encrypted_credentials_salt)}) + ).squish end def update_import_data_sql(id, fake_import_data, data = 'NULL') - %( UPDATE project_import_data SET encrypted_credentials = #{quote(fake_import_data.encrypted_credentials)}, encrypted_credentials_iv = #{quote(fake_import_data.encrypted_credentials_iv)}, encrypted_credentials_salt = #{quote(fake_import_data.encrypted_credentials_salt)}, data = #{data} WHERE id = '#{id}') + %( + UPDATE project_import_data + SET encrypted_credentials = #{quote(fake_import_data.encrypted_credentials)}, + encrypted_credentials_iv = #{quote(fake_import_data.encrypted_credentials_iv)}, + encrypted_credentials_salt = #{quote(fake_import_data.encrypted_credentials_salt)}, + data = #{data} + WHERE id = '#{id}' + ).squish end #Github projects with token, and any user:password@ based URL + #TODO: may need to add import_type != list def projects_with_wrong_import_url - select_all("SELECT p.id, p.import_url FROM projects p WHERE p.import_url IS NOT NULL AND p.import_url LIKE '%//%@%'") + select_all("SELECT p.id, p.import_url, i.id as import_data_id FROM projects p LEFT JOIN project_import_data i on p.id = i.id WHERE p.import_url IS NOT NULL AND p.import_url LIKE '%//%@%'") end # All imports with data for import_type @@ -95,10 +121,6 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration select_all("SELECT i.id, p.import_url, i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_type = '#{import_type}' ") end - def project_import_data(project_id) - select_one("SELECT id FROM project_import_data WHERE project_id = '#{project_id}'") - end - def quote(value) ActiveRecord::Base.connection.quote(value) end diff --git a/lib/gitlab/bitbucket_import/client.rb b/lib/gitlab/bitbucket_import/client.rb index d88a6eaac6b..1d1bd5e3216 100644 --- a/lib/gitlab/bitbucket_import/client.rb +++ b/lib/gitlab/bitbucket_import/client.rb @@ -5,6 +5,17 @@ module Gitlab attr_reader :consumer, :api + def self.from_project(project) + credentials = project.import_data.credentials if project.import_data + if defined?(credentials) && credentials['bb_session'] + token = credentials['bb_session']['bitbucket_access_token'] + token_secret = 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}" + end + end + def initialize(access_token = nil, access_token_secret = nil) @consumer = ::OAuth::Consumer.new( config.app_id, diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index f80410641a9..7beaecd1cf0 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -1,6 +1,13 @@ module Gitlab module BitbucketImport - class Importer < ImporterInit + class Importer + attr_reader :project, :client + + def initialize(project) + @project = project + @client = Client.from_project(@project) + @formatter = Gitlab::ImportFormatter.new + end def execute import_issues if has_issues? diff --git a/lib/gitlab/bitbucket_import/importer_init.rb b/lib/gitlab/bitbucket_import/importer_init.rb deleted file mode 100644 index 08b710003e4..00000000000 --- a/lib/gitlab/bitbucket_import/importer_init.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Gitlab - module BitbucketImport - class ImporterInit - attr_reader :project, :client - - def initialize(project) - @project = project - if import_data_credentials && import_data_credentials['bb_session'] - token = import_data_credentials['bb_session']['bitbucket_access_token'] - token_secret = import_data_credentials['bb_session']['bitbucket_access_token_secret'] - @client = Client.new(token, token_secret) - @formatter = Gitlab::ImportFormatter.new - else - raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" - end - end - - private - - def import_data_credentials - @import_data_credentials ||= project.import_data.credentials if project.import_data - end - end - end -end diff --git a/lib/gitlab/bitbucket_import/key_deleter.rb b/lib/gitlab/bitbucket_import/key_deleter.rb index 312ed581500..e03c3155b3e 100644 --- a/lib/gitlab/bitbucket_import/key_deleter.rb +++ b/lib/gitlab/bitbucket_import/key_deleter.rb @@ -1,7 +1,13 @@ module Gitlab module BitbucketImport - class KeyDeleter < ImporterInit - attr_reader :current_user + class KeyDeleter + attr_reader :project, :current_user, :client + + def initialize(project) + @project = project + @current_user = project.creator + @client = Client.from_project(@project) + end def execute return false unless BitbucketImport.public_key.present? diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb index 6b0715d1492..62da327931f 100644 --- a/lib/gitlab/google_code_import/importer.rb +++ b/lib/gitlab/google_code_import/importer.rb @@ -6,13 +6,12 @@ module Gitlab def initialize(project) @project = project - if import_data_credentials && import_data_credentials['repo'] - @repo = GoogleCodeImport::Repository.new(import_data_credentials['repo']) - @closed_statuses = [] - @known_labels = Set.new - else - raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" - end + import_data = project.import_data.try(:data) + repo_data = import_data["repo"] if import_data + @repo = GoogleCodeImport::Repository.new(repo_data) + + @closed_statuses = [] + @known_labels = Set.new end def execute @@ -29,10 +28,6 @@ module Gitlab private - def import_data_credentials - @import_data_credentials ||= project.import_data.credentials if project.import_data - end - def user_map @user_map ||= begin user_map = Hash.new do |hash, user| @@ -40,7 +35,8 @@ module Gitlab Client.mask_email(user).sub("...", "\\.\\.\\.") end - stored_user_map = import_data_credentials["user_map"] + import_data = project.import_data.try(:data) + stored_user_map = import_data["user_map"] if import_data user_map.update(stored_user_map) if stored_user_map user_map diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb index acd3a832d59..87821c23460 100644 --- a/lib/gitlab/google_code_import/project_creator.rb +++ b/lib/gitlab/google_code_import/project_creator.rb @@ -25,7 +25,7 @@ module Gitlab ).execute project.create_import_data( - credentials: { + data: { "repo" => repo.raw_data, "user_map" => user_map } diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb index 6ecf3d7182f..647631271e0 100644 --- a/spec/lib/gitlab/google_code_import/importer_spec.rb +++ b/spec/lib/gitlab/google_code_import/importer_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Importer, lib: true do subject { described_class.new(project) } before do - project.create_import_data(credentials: import_data) + project.create_import_data(data: import_data) end describe "#execute" do -- cgit v1.2.1 From 28df200c66f88348e0c995fc8023b6dd78b9bf9b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 29 Mar 2016 15:23:32 +0200 Subject: fixed failing specs --- lib/gitlab/import_url.rb | 6 +++++- spec/factories/project_import_data.rb | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/import_url.rb b/lib/gitlab/import_url.rb index 5b18d67ddc3..3cfbc17b89b 100644 --- a/lib/gitlab/import_url.rb +++ b/lib/gitlab/import_url.rb @@ -20,7 +20,7 @@ module Gitlab private def generate_full_url - return @url unless credentials + return @url unless valid_credentials? @full_url = @url.dup @full_url.user = credentials[:user] @full_url.password = credentials[:password] @@ -33,5 +33,9 @@ module Gitlab safe_url.user = nil safe_url end + + def valid_credentials? + credentials && credentials.is_a?(Hash) && credentials.any? + end end end diff --git a/spec/factories/project_import_data.rb b/spec/factories/project_import_data.rb index a799af9996c..9e08d5a22e9 100644 --- a/spec/factories/project_import_data.rb +++ b/spec/factories/project_import_data.rb @@ -1,5 +1,6 @@ FactoryGirl.define do factory :project_import_data, class: ProjectImportData do data "test" + project end end -- cgit v1.2.1 From c93570d8fb439a8a60a204bcc41e5e8a720730e4 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 29 Mar 2016 16:10:35 +0200 Subject: fixing a few issues after testing imports --- lib/gitlab/bitbucket_import/project_creator.rb | 3 +++ lib/gitlab/fogbugz_import/project_creator.rb | 11 ++++------- lib/gitlab/google_code_import/project_creator.rb | 9 +++------ 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index 0e6f66de321..7dc3e01753a 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -22,8 +22,11 @@ module Gitlab import_source: "#{repo["owner"]}/#{repo["slug"]}", import_url: "ssh://git@bitbucket.org/#{repo["owner"]}/#{repo["slug"]}.git", ).execute + import_data = project.import_data import_data.credentials.merge!("bb_session" => session_data) + import_data.save + project end end diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb index 73d6272720a..15914aea3b9 100644 --- a/lib/gitlab/fogbugz_import/project_creator.rb +++ b/lib/gitlab/fogbugz_import/project_creator.rb @@ -24,13 +24,10 @@ module Gitlab import_url: Project::UNKNOWN_IMPORT_URL ).execute - project.create_import_data( - credentials: { - 'repo' => repo.raw_data, - 'user_map' => user_map, - 'fb_session' => fb_session - } - ) + import_data = project.import_data + import_data.credentials.merge!('fb_session' => fb_session) + import_data.data = { 'repo' => repo.raw_data, 'user_map' => user_map } + import_data.save project end diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb index 87821c23460..49d6013af28 100644 --- a/lib/gitlab/google_code_import/project_creator.rb +++ b/lib/gitlab/google_code_import/project_creator.rb @@ -24,12 +24,9 @@ module Gitlab import_url: repo.import_url ).execute - project.create_import_data( - data: { - "repo" => repo.raw_data, - "user_map" => user_map - } - ) + import_data = project.import_data + import_data.data = { 'repo' => repo.raw_data, 'user_map' => user_map } + import_data.save project end -- cgit v1.2.1 From 9e5a11f4b679f4a11aab31ca82c347ba6b025b68 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 29 Mar 2016 17:27:56 +0200 Subject: Target right release --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 1adfa49ad16..b0ddf6c3a94 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ v 8.7.0 (unreleased) - Add links to CI setup documentation from project settings and builds pages - Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.) - Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.) + - Allow issues and merge requests to be assigned to the author v 8.6.2 (unreleased) - Comments on confidential issues don't show up in activity feed to non-members @@ -233,7 +234,6 @@ v 8.5.0 - Add label description (Nuttanart Pornprasitsakul) - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul) - Add Todos - - Allow issues and merge requests to be assigned to the author(Zeger-Jan van de Weg) v 8.4.5 - No CE-specific changes -- cgit v1.2.1 From 075b56aae2e045e930580985234276edb353747f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 29 Mar 2016 18:43:23 +0200 Subject: more fixes after doing more manual testing on importing --- lib/gitlab/bitbucket_import/project_creator.rb | 3 ++- lib/gitlab/fogbugz_import/project_creator.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index 7dc3e01753a..cc7f2017142 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -24,7 +24,8 @@ module Gitlab ).execute import_data = project.import_data - import_data.credentials.merge!("bb_session" => session_data) + # merge! with a bang doesn't work here + import_data.credentials = import_data.credentials.merge("bb_session" => session_data) import_data.save project diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb index 15914aea3b9..939cb96c101 100644 --- a/lib/gitlab/fogbugz_import/project_creator.rb +++ b/lib/gitlab/fogbugz_import/project_creator.rb @@ -25,7 +25,8 @@ module Gitlab ).execute import_data = project.import_data - import_data.credentials.merge!('fb_session' => fb_session) + # merge! with a bang doesn't work here + import_data.credentials = import_data.credentials.merge('fb_session' => fb_session) import_data.data = { 'repo' => repo.raw_data, 'user_map' => user_map } import_data.save -- cgit v1.2.1 From 6d12d79d29e035c7238aa7112db1429711e61a65 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 1 Apr 2016 12:04:41 +0200 Subject: fix fogbugz import --- app/models/project_import_data.rb | 4 ++++ lib/gitlab/fogbugz_import/importer.rb | 14 ++++++-------- lib/gitlab/fogbugz_import/project_creator.rb | 3 ++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index ba4334055d6..fa6055ff64d 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -17,4 +17,8 @@ class ProjectImportData < ActiveRecord::Base serialize :data, JSON validates :project, presence: true + + def stringified_credentials + JSON[credentials.to_json] + end end diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index c33b3541dd8..c88a44573a7 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -8,7 +8,7 @@ module Gitlab import_data = project.import_data.try(:data) repo_data = import_data['repo'] if import_data - if import_data_credentials && import_data_credentials['repo'] + if defined?(repo_data) @repo = FogbugzImport::Repository.new(repo_data) @known_labels = Set.new else @@ -18,10 +18,8 @@ module Gitlab def execute return true unless repo.valid? - - data = project.import_data.try(:data) - - client = Gitlab::FogbugzImport::Client.new(token: data['fb_session']['token'], uri: data['fb_session']['uri']) + Rails.logger.error import_data_credentials.inspect + client = Gitlab::FogbugzImport::Client.new(token: import_data_credentials['fb_session']['token'], uri: import_data_credentials['fb_session']['uri']) @cases = client.cases(@repo.id.to_i) @categories = client.categories @@ -34,7 +32,7 @@ module Gitlab private def import_data_credentials - @import_data_credentials ||= project.import_data.credentials if project.import_data + @import_data_credentials ||= project.import_data.stringified_credentials if project.import_data end def user_map @@ -244,8 +242,8 @@ module Gitlab def build_attachment_url(rel_url) data = project.import_data.try(:data) - uri = data['fb_session']['uri'] - token = data['fb_session']['token'] + uri = import_data_credentials['fb_session']['uri'] + token = import_data_credentials['fb_session']['token'] "#{uri}/#{rel_url}&token=#{token}" end diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb index 939cb96c101..0a87b406c56 100644 --- a/lib/gitlab/fogbugz_import/project_creator.rb +++ b/lib/gitlab/fogbugz_import/project_creator.rb @@ -25,9 +25,10 @@ module Gitlab ).execute import_data = project.import_data + import_data.data = { 'repo' => repo.raw_data, 'user_map' => user_map } + # merge! with a bang doesn't work here import_data.credentials = import_data.credentials.merge('fb_session' => fb_session) - import_data.data = { 'repo' => repo.raw_data, 'user_map' => user_map } import_data.save project -- cgit v1.2.1 From 6aeb753ba81c857c23d29f9d4c5e22aaeed737dc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 1 Apr 2016 15:51:54 +0200 Subject: fix github import issues --- app/models/project.rb | 2 +- lib/gitlab/github_import/importer.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 52f70256be3..941e444a4f8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -418,7 +418,7 @@ class Project < ActiveRecord::Base def create_or_update_import_data(credentials) project_import_data = import_data || build_import_data project_import_data.credentials ||= {} - project_import_data.credentials.merge!(credentials) + project_import_data.credentials = project_import_data.credentials.merge(credentials) project_import_data.save end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index a5d3ab5fcf1..0b1ed510229 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -8,7 +8,7 @@ module Gitlab def initialize(project) @project = project if import_data_credentials - @client = Client.new(import_data_credentials['user']) + @client = Client.new(import_data_credentials[:user]) @formatter = Gitlab::ImportFormatter.new else raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" -- cgit v1.2.1 From 255cd31652f1afda5cd1c075526bbe3ee56a708e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 1 Apr 2016 17:27:07 +0200 Subject: fixes after more import testing --- lib/gitlab/bitbucket_import/client.rb | 2 +- lib/gitlab/gitlab_import/importer.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/bitbucket_import/client.rb b/lib/gitlab/bitbucket_import/client.rb index 1d1bd5e3216..acd0f298b3d 100644 --- a/lib/gitlab/bitbucket_import/client.rb +++ b/lib/gitlab/bitbucket_import/client.rb @@ -6,7 +6,7 @@ module Gitlab attr_reader :consumer, :api def self.from_project(project) - credentials = project.import_data.credentials if project.import_data + credentials = project.import_data.stringified_credentials if project.import_data if defined?(credentials) && credentials['bb_session'] token = credentials['bb_session']['bitbucket_access_token'] token_secret = credentials['bb_session']['bitbucket_access_token_secret'] diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb index afc06d01ebd..19dc79462c0 100644 --- a/lib/gitlab/gitlab_import/importer.rb +++ b/lib/gitlab/gitlab_import/importer.rb @@ -5,7 +5,7 @@ module Gitlab def initialize(project) @project = project - credentials = import_data.credentials + credentials = import_data.stringified_credentials if credentials && credentials["password"] @client = Client.new(credentials["password"]) @formatter = Gitlab::ImportFormatter.new -- cgit v1.2.1 From 43ee65e173cbb4f846c118c531f635dc0f8602ac Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 1 Apr 2016 17:50:50 +0200 Subject: remove useless var --- lib/gitlab/fogbugz_import/importer.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index c88a44573a7..fffeb66ce26 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -241,7 +241,6 @@ module Gitlab end def build_attachment_url(rel_url) - data = project.import_data.try(:data) uri = import_data_credentials['fb_session']['uri'] token = import_data_credentials['fb_session']['token'] "#{uri}/#{rel_url}&token=#{token}" -- cgit v1.2.1 From ef85c510fa24a0deec6f680f9226dccd937a7bbe Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 4 Apr 2016 09:57:52 +0200 Subject: corrected a couple of based on MR review --- db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb | 4 ++-- lib/gitlab/fogbugz_import/importer.rb | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index fb5d8591c09..60f86c74df0 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -16,8 +16,8 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration say("Projects and Github projects with a wrong URL. It also migrates Gitlab project credentials.") in_transaction { process_projects_with_wrong_url } - say("Migrating bitbucket credentials...")# TODO remove last param - in_transaction { process_project(import_type: 'bitbucket', unencrypted_data: ['repo', 'user_map']) } + say("Migrating bitbucket credentials...") + in_transaction { process_project(import_type: 'bitbucket') } say("Migrating fogbugz credentials...") in_transaction { process_project(import_type: 'fogbugz', unencrypted_data: ['repo', 'user_map']) } diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index fffeb66ce26..5c1c1c4865a 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -8,7 +8,7 @@ module Gitlab import_data = project.import_data.try(:data) repo_data = import_data['repo'] if import_data - if defined?(repo_data) + if repo_data @repo = FogbugzImport::Repository.new(repo_data) @known_labels = Set.new else @@ -18,7 +18,6 @@ module Gitlab def execute return true unless repo.valid? - Rails.logger.error import_data_credentials.inspect client = Gitlab::FogbugzImport::Client.new(token: import_data_credentials['fb_session']['token'], uri: import_data_credentials['fb_session']['uri']) @cases = client.cases(@repo.id.to_i) -- cgit v1.2.1 From bf9526739b5c90790907c1d8b9410dd339e3d395 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 4 Apr 2016 17:23:43 +0200 Subject: Rebase repo check MR --- app/controllers/admin/projects_controller.rb | 24 ++++++++++- app/mailers/repo_check_mailer.rb | 16 ++++++++ app/views/admin/logs/show.html.haml | 3 +- app/views/admin/projects/index.html.haml | 15 ++++++- app/views/admin/projects/show.html.haml | 32 +++++++++++++++ app/views/repo_check_mailer/notify.html.haml | 5 +++ app/views/repo_check_mailer/notify.text.haml | 3 ++ app/workers/admin_email_worker.rb | 12 ++++++ app/workers/repo_check_worker.rb | 46 ++++++++++++++++++++++ app/workers/single_repo_check_worker.rb | 34 ++++++++++++++++ config/gitlab.yml.example | 7 ++++ config/initializers/1_settings.rb | 7 +++- config/routes.rb | 5 +++ .../20160315135439_project_add_repo_check.rb | 6 +++ db/schema.rb | 2 + doc/administration/repo_checks.md | 39 ++++++++++++++++++ lib/gitlab/repo_check_logger.rb | 7 ++++ spec/features/admin/admin_projects_spec.rb | 37 ++++++++++++++++- spec/mailers/repo_check_mailer_spec.rb | 21 ++++++++++ spec/workers/repo_check_worker_spec.rb | 31 +++++++++++++++ 20 files changed, 346 insertions(+), 6 deletions(-) create mode 100644 app/mailers/repo_check_mailer.rb create mode 100644 app/views/repo_check_mailer/notify.html.haml create mode 100644 app/views/repo_check_mailer/notify.text.haml create mode 100644 app/workers/admin_email_worker.rb create mode 100644 app/workers/repo_check_worker.rb create mode 100644 app/workers/single_repo_check_worker.rb create mode 100644 db/migrate/20160315135439_project_add_repo_check.rb create mode 100644 doc/administration/repo_checks.md create mode 100644 lib/gitlab/repo_check_logger.rb create mode 100644 spec/mailers/repo_check_mailer_spec.rb create mode 100644 spec/workers/repo_check_worker_spec.rb diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 4089091d569..b8c276fb1bb 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -1,5 +1,5 @@ class Admin::ProjectsController < Admin::ApplicationController - before_action :project, only: [:show, :transfer] + before_action :project, only: [:show, :transfer, :repo_check] before_action :group, only: [:show, :transfer] def index @@ -8,6 +8,7 @@ class Admin::ProjectsController < Admin::ApplicationController @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present? @projects = @projects.with_push if params[:with_push].present? @projects = @projects.abandoned if params[:abandoned].present? + @projects = @projects.where(last_repo_check_failed: true) if params[:last_repo_check_failed].present? @projects = @projects.non_archived unless params[:with_archived].present? @projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.sort(@sort = params[:sort]) @@ -30,6 +31,27 @@ class Admin::ProjectsController < Admin::ApplicationController redirect_to admin_namespace_project_path(@project.namespace, @project) end + def repo_check + SingleRepoCheckWorker.perform_async(@project.id) + + redirect_to( + admin_namespace_project_path(@project.namespace, @project), + notice: 'Repo check was triggered' + ) + end + + def clear_repo_check_states + Project.update_all( + last_repo_check_failed: false, + last_repo_check_at: nil + ) + + redirect_to( + admin_namespaces_projects_path, + notice: 'All project repo check states were cleared' + ) + end + protected def project diff --git a/app/mailers/repo_check_mailer.rb b/app/mailers/repo_check_mailer.rb new file mode 100644 index 00000000000..d98533f120d --- /dev/null +++ b/app/mailers/repo_check_mailer.rb @@ -0,0 +1,16 @@ +class RepoCheckMailer < BaseMailer + include ActionView::Helpers::TextHelper + + def notify(failed_count) + if failed_count == 1 + @message = "One project failed its last repository check" + else + @message = "#{failed_count} projects failed their last repository check" + end + + mail( + to: User.admins.pluck(:email), + subject: @message + ) + end +end diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index af9fdeb0734..abcc93f4046 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -1,6 +1,7 @@ - page_title "Logs" - loggers = [Gitlab::GitLogger, Gitlab::AppLogger, - Gitlab::ProductionLogger, Gitlab::SidekiqLogger] + Gitlab::ProductionLogger, Gitlab::SidekiqLogger, + Gitlab::RepoCheckLogger] %ul.nav-links.log-tabs - loggers.each do |klass| %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') } diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index d39c0f44031..ed360f2f012 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -3,7 +3,7 @@ .row.prepend-top-default %aside.col-md-3 - .admin-filter + .panel.admin-filter = form_tag admin_namespaces_projects_path, method: :get, class: '' do .form-group = label_tag :name, 'Name:' @@ -38,11 +38,22 @@ %span.descr = visibility_level_icon(level) = label - %hr + %fieldset + %strong Problems + .checkbox + = label_tag :last_repo_check_failed do + = check_box_tag :last_repo_check_failed, 1, params[:last_repo_check_failed] + %span Last repo check failed + = hidden_field_tag :sort, params[:sort] = button_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel" + .panel.panel-default.repo-check-states + .panel-heading + Repo check states + .panel-body + = link_to 'Clear all', clear_repo_check_states_admin_namespace_projects_path(0), data: { confirm: 'This will clear repo check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" %section.col-md-9 .panel.panel-default .panel-heading diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index c638c32a654..a7a3f6349ef 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -5,6 +5,14 @@ %i.fa.fa-pencil-square-o Edit %hr +- if @project.last_repo_check_failed? + .row + .col-md-12 + .panel + .panel-heading.alert.alert-danger + Last repo check failed. See + = link_to 'repocheck.log', admin_logs_path + for error messages. .row .col-md-6 .panel.panel-default @@ -95,6 +103,30 @@ .col-sm-offset-2.col-sm-10 = f.submit 'Transfer', class: 'btn btn-primary' + .panel.panel-default.repo-check + .panel-heading + Repo check + .panel-body + = form_for @project, url: repo_check_admin_namespace_project_path(@project.namespace, @project), method: :post do |f| + .form-group + - if @project.last_repo_check_at.nil? + This repository has never been checked. + - else + This repository was last checked + = @project.last_repo_check_at.to_s(:medium) + '.' + The check + - if @project.last_repo_check_failed? + = succeed '.' do + %strong.cred failed + See + = link_to 'repocheck.log', admin_logs_path + for error messages. + - else + passed. + + .form-group + = f.submit 'Trigger repo check', class: 'btn btn-primary' + .col-md-6 - if @group .panel.panel-default diff --git a/app/views/repo_check_mailer/notify.html.haml b/app/views/repo_check_mailer/notify.html.haml new file mode 100644 index 00000000000..ef0016f9d3e --- /dev/null +++ b/app/views/repo_check_mailer/notify.html.haml @@ -0,0 +1,5 @@ +%p + #{@message}. + +%p + = link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repo_check_failed: 1) diff --git a/app/views/repo_check_mailer/notify.text.haml b/app/views/repo_check_mailer/notify.text.haml new file mode 100644 index 00000000000..bdf8c2ad675 --- /dev/null +++ b/app/views/repo_check_mailer/notify.text.haml @@ -0,0 +1,3 @@ +#{@message}. +\ +View details: #{admin_namespaces_projects_url(last_repo_check_failed: 1)} diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb new file mode 100644 index 00000000000..fcccb9ea669 --- /dev/null +++ b/app/workers/admin_email_worker.rb @@ -0,0 +1,12 @@ +class AdminEmailWorker + include Sidekiq::Worker + + sidekiq_options retry: false # this job auto-repeats via sidekiq-cron + + def perform + repo_check_failed_count = Project.where(last_repo_check_failed: true).count + return if repo_check_failed_count.zero? + + RepoCheckMailer.notify(repo_check_failed_count).deliver_now + end +end diff --git a/app/workers/repo_check_worker.rb b/app/workers/repo_check_worker.rb new file mode 100644 index 00000000000..9be795309e0 --- /dev/null +++ b/app/workers/repo_check_worker.rb @@ -0,0 +1,46 @@ +class RepoCheckWorker + include Sidekiq::Worker + + RUN_TIME = 3600 + + sidekiq_options retry: false + + def perform + start = Time.now + + # This loop will break after a little more than one hour ('a little + # more' because `git fsck` may take a few minutes), or if it runs out of + # projects to check. By default sidekiq-cron will start a new + # RepoCheckWorker each hour so that as long as there are repositories to + # check, only one (or two) will be checked at a time. + project_ids.each do |project_id| + break if Time.now - start >= RUN_TIME + + next if !try_obtain_lease(project_id) + + SingleRepoCheckWorker.new.perform(project_id) + end + end + + private + + # In an ideal world we would use Project.where(...).find_each. + # Unfortunately, calling 'find_each' drops the 'where', so we must build + # an array of IDs instead. + def project_ids + limit = 10_000 + never_checked_projects = Project.where('last_repo_check_at IS NULL').limit(limit). + pluck(:id) + old_check_projects = Project.where('last_repo_check_at < ?', 1.week.ago). + reorder('last_repo_check_at ASC').limit(limit).pluck(:id) + never_checked_projects + old_check_projects + end + + def try_obtain_lease(id) + lease = Gitlab::ExclusiveLease.new( + "project_repo_check:#{id}", + timeout: RUN_TIME + ) + lease.try_obtain + end +end diff --git a/app/workers/single_repo_check_worker.rb b/app/workers/single_repo_check_worker.rb new file mode 100644 index 00000000000..f8b245247c5 --- /dev/null +++ b/app/workers/single_repo_check_worker.rb @@ -0,0 +1,34 @@ +class SingleRepoCheckWorker + include Sidekiq::Worker + + sidekiq_options retry: false + + def perform(project_id) + project = Project.find(project_id) + update(project, success: check(project)) + end + + private + + def check(project) + [project.repository.path_to_repo, project.wiki.wiki.path].all? do |path| + git_fsck(path) + end + end + + def git_fsck(path) + cmd = %W(nice git --git-dir=#{path} fsck) + output, status = Gitlab::Popen.popen(cmd) + return true if status.zero? + + Gitlab::RepoCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") + false + end + + def update(project, success:) + project.update_columns( + last_repo_check_failed: !success, + last_repo_check_at: Time.now, + ) + end +end diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index fb1c3476f65..cf20fc9c63a 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -155,6 +155,13 @@ production: &base # Flag stuck CI builds as failed stuck_ci_builds_worker: cron: "0 0 * * *" + # Periodically run 'git fsck' on all repositories. If started more than + # once per hour you will have concurrent 'git fsck' jobs. + repo_check_worker: + cron: "20 * * * *" + # Send admin emails once a day + admin_email_worker: + cron: "0 0 * * *" # diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 2b989015279..8240978ef06 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -239,7 +239,12 @@ Settings['cron_jobs'] ||= Settingslogic.new({}) Settings.cron_jobs['stuck_ci_builds_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['stuck_ci_builds_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['stuck_ci_builds_worker']['job_class'] = 'StuckCiBuildsWorker' - +Settings.cron_jobs['repo_check_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['repo_check_worker']['cron'] ||= '20 * * * *' +Settings.cron_jobs['repo_check_worker']['job_class'] = 'RepoCheckWorker' +Settings.cron_jobs['admin_email_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['admin_email_worker']['cron'] ||= '0 0 * * *' +Settings.cron_jobs['admin_email_worker']['job_class'] = 'AdminEmailWorker' # # GitLab Shell diff --git a/config/routes.rb b/config/routes.rb index 6bf22fb4456..fad8600b77d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -264,6 +264,11 @@ Rails.application.routes.draw do member do put :transfer + post :repo_check + end + + collection do + put :clear_repo_check_states end resources :runner_projects diff --git a/db/migrate/20160315135439_project_add_repo_check.rb b/db/migrate/20160315135439_project_add_repo_check.rb new file mode 100644 index 00000000000..ba7ddc9ecb4 --- /dev/null +++ b/db/migrate/20160315135439_project_add_repo_check.rb @@ -0,0 +1,6 @@ +class ProjectAddRepoCheck < ActiveRecord::Migration + def change + add_column :projects, :last_repo_check_failed, :boolean, default: false + add_column :projects, :last_repo_check_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index e63e22ce864..d2c183f968b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -732,6 +732,8 @@ ActiveRecord::Schema.define(version: 20160331133914) do t.boolean "public_builds", default: true, null: false t.string "main_language" t.integer "pushes_since_gc", default: 0 + t.boolean "last_repo_check_failed", default: false + t.datetime "last_repo_check_at" end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree diff --git a/doc/administration/repo_checks.md b/doc/administration/repo_checks.md new file mode 100644 index 00000000000..9c2c01594e8 --- /dev/null +++ b/doc/administration/repo_checks.md @@ -0,0 +1,39 @@ +# Repo checks + +_**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ + +--- + +Git has a built-in mechanism [git fsck][git-fsck] to verify the +integrity of all data commited to a repository. GitLab administrators can +trigger such a check for a project via the admin panel. The checks run +asynchronously so it may take a few minutes before the check result is +visible on the project admin page. If the checks failed you can see their +output on the admin log page under 'repocheck.log'. + +## Periodical checks + +GitLab periodically runs a repo check on all project repositories and +wiki repositories in order to detect data corruption problems. A +project will be checked no more than once per week. If any projects +fail their repo checks all GitLab administrators will receive an email +notification of the situation. This notification is sent out no more +than once a day. + + +## What to do if a check failed + +If the repo check fails for some repository you should look up the error +in repocheck.log (in the admin panel or on disk; see +`/var/log/gitlab/gitlab-rails` for Omnibus installations or +`/home/git/gitlab/log` for installations from source). Once you have +resolved the issue use the admin panel to trigger a new repo check on +the project. This will clear the 'check failed' state. + +If for some reason the periodical repo check caused a lot of false +alarms you can choose to clear ALL repo check states from the admin +project index page. + +--- +[ce-3232]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3232 "Auto git fsck" +[git-fsck]: https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html "git fsck documentation" \ No newline at end of file diff --git a/lib/gitlab/repo_check_logger.rb b/lib/gitlab/repo_check_logger.rb new file mode 100644 index 00000000000..9409f68722c --- /dev/null +++ b/lib/gitlab/repo_check_logger.rb @@ -0,0 +1,7 @@ +module Gitlab + class RepoCheckLogger < Gitlab::Logger + def self.file_name_noext + 'repocheck' + end + end +end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 101d955d693..e3991d48ed6 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' +require 'rails_helper' -describe "Admin::Projects", feature: true do +describe "Admin Projects", feature: true do before do @project = create(:project) login_as :admin @@ -31,4 +32,38 @@ describe "Admin::Projects", feature: true do expect(page).to have_content(@project.name) end end + + feature 'repo checks' do + scenario 'trigger repo check' do + visit_admin_project_page + + page.within('.repo-check') do + click_button 'Trigger repo check' + end + + expect(page).to have_content('Repo check was triggered') + end + + scenario 'see failed repo check' do + @project.update_column(:last_repo_check_failed, true) + visit_admin_project_page + + expect(page).to have_content('Last repo check failed') + end + + scenario 'clear repo checks', js: true do + @project.update_column(:last_repo_check_failed, true) + visit admin_namespaces_projects_path + + page.within('.repo-check-states') do + click_link 'Clear all' # pop-up should be auto confirmed + end + + expect(@project.reload.last_repo_check_failed).to eq(false) + end + end + + def visit_admin_project_page + visit admin_namespace_project_path(@project.namespace, @project) + end end diff --git a/spec/mailers/repo_check_mailer_spec.rb b/spec/mailers/repo_check_mailer_spec.rb new file mode 100644 index 00000000000..d49a6ae0c05 --- /dev/null +++ b/spec/mailers/repo_check_mailer_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +describe RepoCheckMailer do + include EmailSpec::Matchers + + describe '.notify' do + it 'emails all admins' do + admins = 3.times.map { create(:admin) } + + mail = described_class.notify(1) + + expect(mail).to deliver_to admins.map(&:email) + end + + it 'mentions the number of failed checks' do + mail = described_class.notify(3) + + expect(mail).to have_subject '3 projects failed their last repository check' + end + end +end diff --git a/spec/workers/repo_check_worker_spec.rb b/spec/workers/repo_check_worker_spec.rb new file mode 100644 index 00000000000..7ef3eba9ac5 --- /dev/null +++ b/spec/workers/repo_check_worker_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe RepoCheckWorker do + subject { RepoCheckWorker.new } + + it 'prefers projects that have never been checked' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repo_check_at, 1.month.ago) + projects[2].update_column(:last_repo_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) + end + + it 'sorts projects by last_repo_check_at' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repo_check_at, 2.weeks.ago) + projects[1].update_column(:last_repo_check_at, 1.month.ago) + projects[2].update_column(:last_repo_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) + end + + it 'excludes projects that were checked recently' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repo_check_at, 2.days.ago) + projects[1].update_column(:last_repo_check_at, 1.month.ago) + projects[2].update_column(:last_repo_check_at, 3.days.ago) + + expect(subject.perform).to eq([projects[1].id]) + end +end -- cgit v1.2.1 From 288d8e669883abe995fe27ad577189da42ef78fb Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 4 Apr 2016 18:03:55 +0200 Subject: fixes based on MR review --- ...152808_remove_wrong_import_url_from_projects.rb | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 60f86c74df0..708dde8eca8 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -17,10 +17,10 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration in_transaction { process_projects_with_wrong_url } say("Migrating bitbucket credentials...") - in_transaction { process_project(import_type: 'bitbucket') } + in_transaction { process_project(import_type: 'bitbucket', credentials_keys: ['bb_session']) } say("Migrating fogbugz credentials...") - in_transaction { process_project(import_type: 'fogbugz', unencrypted_data: ['repo', 'user_map']) } + in_transaction { process_project(import_type: 'fogbugz', credentials_keys: ['fb_session']) } end @@ -33,27 +33,26 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end end - def process_project(import_type: , unencrypted_data: []) + def process_project(import_type: , credentials_keys: []) unencrypted_import_data(import_type: import_type).each do |data| - replace_data_credentials(data, unencrypted_data) + replace_data_credentials(data, credentials_keys) end end - def replace_data_credentials(data, unencrypted_data) + def replace_data_credentials(data, credentials_keys) data_hash = JSON.load(data['data']) if data['data'] - if defined?(data_hash) && !data_hash.blank? - unencrypted_data_hash = encrypted_data_hash(data_hash, unencrypted_data) - update_with_encrypted_data(data_hash, data['id'], unencrypted_data_hash) + unless data_hash.blank? + encrypted_data_hash = encrypt_data(data_hash, credentials_keys) + unencrypted_data = data_hash.empty? ? ' NULL ' : quote(data_hash.to_json) + update_with_encrypted_data(encrypted_data_hash, data['id'], unencrypted_data) end end - def encrypted_data_hash(data_hash, unencrypted_data) - return 'NULL' if unencrypted_data.empty? + def encrypt_data(data_hash, credentials_keys) new_data_hash = {} - unencrypted_data.each do |key| + credentials_keys.each do |key| new_data_hash[key] = data_hash.delete(key) if data_hash[key] end - quote(new_data_hash.to_json) end def in_transaction @@ -75,10 +74,10 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end end - def update_with_encrypted_data(data_hash, import_data_id, data_array = nil) + def update_with_encrypted_data(data_hash, import_data_id, unencrypted_data = ' NULL ') fake_import_data = FakeProjectImportData.new fake_import_data.credentials = data_hash - execute(update_import_data_sql(import_data_id, fake_import_data, data_array)) + execute(update_import_data_sql(import_data_id, fake_import_data, unencrypted_data)) end def update_import_url(import_url, project) -- cgit v1.2.1 From 172d37a2385424eb5f4c157fa691fd47b9f5a850 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 4 Apr 2016 19:45:53 +0200 Subject: fix wording --- .../20160302152808_remove_wrong_import_url_from_projects.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 708dde8eca8..0a7ba65acc4 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -12,14 +12,14 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration def up say("Encrypting and migrating project import credentials...") - # This should cover Github, Gitlab, Bitbucket user:password, token@domain, and other similar URLs. - say("Projects and Github projects with a wrong URL. It also migrates Gitlab project credentials.") + # This should cover GitHub, GitLab, Bitbucket user:password, token@domain, and other similar URLs. + say("Projects and GitHub projects with a wrong URL. It also migrates GitLab project credentials.") in_transaction { process_projects_with_wrong_url } - say("Migrating bitbucket credentials...") + say("Migrating Bitbucket credentials...") in_transaction { process_project(import_type: 'bitbucket', credentials_keys: ['bb_session']) } - say("Migrating fogbugz credentials...") + say("Migrating FogBugz credentials...") in_transaction { process_project(import_type: 'fogbugz', credentials_keys: ['fb_session']) } end @@ -109,7 +109,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration ).squish end - #Github projects with token, and any user:password@ based URL + #GitHub projects with token, and any user:password@ based URL #TODO: may need to add import_type != list def projects_with_wrong_import_url select_all("SELECT p.id, p.import_url, i.id as import_data_id FROM projects p LEFT JOIN project_import_data i on p.id = i.id WHERE p.import_url IS NOT NULL AND p.import_url LIKE '%//%@%'") -- cgit v1.2.1 From 4835e680a4624ab8de3316b367b8375bb5a270a0 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 4 Apr 2016 19:55:13 +0200 Subject: fix migration issue --- db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 0a7ba65acc4..8fef93233ef 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -53,6 +53,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration credentials_keys.each do |key| new_data_hash[key] = data_hash.delete(key) if data_hash[key] end + new_data_hash end def in_transaction -- cgit v1.2.1 From 5e51fce4dcd62997f372aed44badc844f98851e9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 5 Apr 2016 15:41:15 +0200 Subject: some refactoring to symbolise keys across importers and left a TODO --- app/models/project_import_data.rb | 8 ++++++-- .../20160302152808_remove_wrong_import_url_from_projects.rb | 4 ++-- db/schema.rb | 5 ++--- lib/gitlab/bitbucket_import/client.rb | 8 ++++---- lib/gitlab/bitbucket_import/project_creator.rb | 2 +- lib/gitlab/fogbugz_import/importer.rb | 8 ++++---- lib/gitlab/fogbugz_import/project_creator.rb | 2 +- lib/gitlab/gitlab_import/importer.rb | 6 +++--- 8 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index fa6055ff64d..225cbda15b1 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -18,7 +18,11 @@ class ProjectImportData < ActiveRecord::Base validates :project, presence: true - def stringified_credentials - JSON[credentials.to_json] + # TODO: This doesnt play well with attr_encrypted. Perhaps consider extending Marshall and specify a different Marshaller + before_validation :symbolize_credentials + + def symbolize_credentials + return if credentials.blank? + credentials.deep_symbolize_keys! end end diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 8fef93233ef..93e040fce28 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -51,9 +51,9 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration def encrypt_data(data_hash, credentials_keys) new_data_hash = {} credentials_keys.each do |key| - new_data_hash[key] = data_hash.delete(key) if data_hash[key] + new_data_hash[key.to_sym] = data_hash.delete(key) if data_hash[key] end - new_data_hash + new_data_hash.deep_symbolize_keys end def in_transaction diff --git a/db/schema.rb b/db/schema.rb index df4c65a0625..5d87dbfe41f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160331133914) do +ActiveRecord::Schema.define(version: 20160331223143) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -44,7 +44,6 @@ ActiveRecord::Schema.define(version: 20160331133914) do t.datetime "updated_at" t.string "home_page_url" t.integer "default_branch_protection", default: 2 - t.boolean "twitter_sharing_enabled", default: true t.text "restricted_visibility_levels" t.boolean "version_check_enabled", default: true t.integer "max_attachment_size", default: 10, null: false @@ -417,9 +416,9 @@ ActiveRecord::Schema.define(version: 20160331133914) 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.integer "moved_to_id" end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree diff --git a/lib/gitlab/bitbucket_import/client.rb b/lib/gitlab/bitbucket_import/client.rb index acd0f298b3d..49f86ab5edf 100644 --- a/lib/gitlab/bitbucket_import/client.rb +++ b/lib/gitlab/bitbucket_import/client.rb @@ -6,10 +6,10 @@ module Gitlab attr_reader :consumer, :api def self.from_project(project) - credentials = project.import_data.stringified_credentials if project.import_data - if defined?(credentials) && credentials['bb_session'] - token = credentials['bb_session']['bitbucket_access_token'] - token_secret = credentials['bb_session']['bitbucket_access_token_secret'] + credentials = project.import_data if project.import_data + if credentials && credentials[:bb_session] + token = credentials[:bb_session][:bitbucket_access_token] + token_secret = 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}" diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index cc7f2017142..109010cb962 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -25,7 +25,7 @@ module Gitlab import_data = project.import_data # merge! with a bang doesn't work here - import_data.credentials = import_data.credentials.merge("bb_session" => session_data) + import_data.credentials = import_data.credentials.merge(bb_session: session_data) import_data.save project diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index 5c1c1c4865a..249c5b48b1c 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -18,7 +18,7 @@ module Gitlab def execute return true unless repo.valid? - client = Gitlab::FogbugzImport::Client.new(token: import_data_credentials['fb_session']['token'], uri: import_data_credentials['fb_session']['uri']) + client = Gitlab::FogbugzImport::Client.new(token: import_data_credentials[:fb_session][:token], uri: import_data_credentials[:fb_session][:uri]) @cases = client.cases(@repo.id.to_i) @categories = client.categories @@ -31,7 +31,7 @@ module Gitlab private def import_data_credentials - @import_data_credentials ||= project.import_data.stringified_credentials if project.import_data + @import_data_credentials ||= project.import_data if project.import_data end def user_map @@ -240,8 +240,8 @@ module Gitlab end def build_attachment_url(rel_url) - uri = import_data_credentials['fb_session']['uri'] - token = import_data_credentials['fb_session']['token'] + uri = import_data_credentials[:fb_session][:uri] + token = import_data_credentials[:fb_session][:token] "#{uri}/#{rel_url}&token=#{token}" end diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb index 0a87b406c56..e9fac8968e6 100644 --- a/lib/gitlab/fogbugz_import/project_creator.rb +++ b/lib/gitlab/fogbugz_import/project_creator.rb @@ -28,7 +28,7 @@ module Gitlab import_data.data = { 'repo' => repo.raw_data, 'user_map' => user_map } # merge! with a bang doesn't work here - import_data.credentials = import_data.credentials.merge('fb_session' => fb_session) + import_data.credentials = import_data.credentials.merge(fb_session: fb_session) import_data.save project diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb index 19dc79462c0..96717b42bae 100644 --- a/lib/gitlab/gitlab_import/importer.rb +++ b/lib/gitlab/gitlab_import/importer.rb @@ -5,9 +5,9 @@ module Gitlab def initialize(project) @project = project - credentials = import_data.stringified_credentials - if credentials && credentials["password"] - @client = Client.new(credentials["password"]) + credentials = import_data + if credentials && credentials[:password] + @client = Client.new(credentials[:password]) @formatter = Gitlab::ImportFormatter.new else raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" -- cgit v1.2.1 From 77a24965cafba42b596039f9058e2e11a7ebc0dd Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 5 Apr 2016 17:08:45 +0200 Subject: Add author id to the right selector --- app/assets/javascripts/users_select.js.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 33d3357a1f8..a7e934936e9 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -12,6 +12,7 @@ class @UsersSelect showNullUser = $dropdown.data('null-user') showAnyUser = $dropdown.data('any-user') firstUser = $dropdown.data('first-user') + @authorId = $dropdown.data('author-id') selectedId = $dropdown.data('selected') defaultLabel = $dropdown.data('default-label') issueURL = $dropdown.data('issueUpdate') @@ -212,7 +213,7 @@ class @UsersSelect showAnyUser = $(select).data('any-user') showEmailUser = $(select).data('email-user') firstUser = $(select).data('first-user') - + $(select).select2 placeholder: "Search for a user" multiple: $(select).hasClass('multiselect') -- cgit v1.2.1 From b97654393e326095c7d95ccc1eb9f583a3b23da9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 6 Apr 2016 10:36:30 +0200 Subject: fix some issues with credentials --- app/models/project_import_data.rb | 4 ++-- lib/gitlab/bitbucket_import/client.rb | 12 ++++++------ lib/gitlab/fogbugz_import/importer.rb | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index 225cbda15b1..e984832685b 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -22,7 +22,7 @@ class ProjectImportData < ActiveRecord::Base before_validation :symbolize_credentials def symbolize_credentials - return if credentials.blank? - credentials.deep_symbolize_keys! + # bang doesn't work here + self.credentials = self.credentials.deep_symbolize_keys unless self.credentials.blank? end end diff --git a/lib/gitlab/bitbucket_import/client.rb b/lib/gitlab/bitbucket_import/client.rb index 49f86ab5edf..9bb507b5edd 100644 --- a/lib/gitlab/bitbucket_import/client.rb +++ b/lib/gitlab/bitbucket_import/client.rb @@ -6,10 +6,10 @@ module Gitlab attr_reader :consumer, :api def self.from_project(project) - credentials = project.import_data if project.import_data - if credentials && credentials[:bb_session] - token = credentials[:bb_session][:bitbucket_access_token] - token_secret = credentials[:bb_session][:bitbucket_access_token_secret] + import_data_credentials = project.import_data.credentials if project.import_data + if import_data_credentials && import_data_credentials[:bb_session] + token = import_data_credentials[:bb_session][:bitbucket_access_token] + 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}" @@ -65,7 +65,7 @@ module Gitlab def issues(project_identifier) all_issues = [] offset = 0 - per_page = 50 # Maximum number allowed by Bitbucket + per_page = 50 # Maximum number allowed by Bitbucket index = 0 begin @@ -131,7 +131,7 @@ module Gitlab end def config - Gitlab.config.omniauth.providers.find { |provider| provider.name == "bitbucket"} + Gitlab.config.omniauth.providers.find { |provider| provider.name == "bitbucket" } end def bitbucket_options diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index 249c5b48b1c..42f9b6eab84 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -31,7 +31,7 @@ module Gitlab private def import_data_credentials - @import_data_credentials ||= project.import_data if project.import_data + @import_data_credentials ||= project.import_data.credentials if project.import_data end def user_map -- cgit v1.2.1 From 05be0c306ea8663461ee73023f162aeaf77a4325 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 6 Apr 2016 11:13:19 +0200 Subject: removed TODO [ci skip] --- app/models/project_import_data.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index e984832685b..a0994312003 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -18,7 +18,6 @@ class ProjectImportData < ActiveRecord::Base validates :project, presence: true - # TODO: This doesnt play well with attr_encrypted. Perhaps consider extending Marshall and specify a different Marshaller before_validation :symbolize_credentials def symbolize_credentials -- cgit v1.2.1 From e3558ed67e7e829fe5148c3fb2fe80ed045fe1b4 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 6 Apr 2016 12:26:29 +0200 Subject: Document how to disable repo checks --- doc/administration/repo_checks.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/administration/repo_checks.md b/doc/administration/repo_checks.md index 9c2c01594e8..81087f3ac1c 100644 --- a/doc/administration/repo_checks.md +++ b/doc/administration/repo_checks.md @@ -20,6 +20,22 @@ fail their repo checks all GitLab administrators will receive an email notification of the situation. This notification is sent out no more than once a day. +## Disabling periodic checks + +You can disable the periodic checks by giving them an empty cron +schedule in gitlab.yml. + +``` +# For omnibus installations, in /etc/gitlab/gitlab.rb: +gitlab_rails['cron_jobs_repo_check_worker_cron'] = '' +``` + +``` +# For installations from source, in config/gitlab.yml: + cron_jobs: + repo_check_worker: + cron: "" +``` ## What to do if a check failed -- cgit v1.2.1 From 5cf56e56470e695b10db02dff70d0f0b50060518 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 6 Apr 2016 13:47:05 +0200 Subject: Rename almost all the things --- app/controllers/admin/projects_controller.rb | 18 +++---- app/mailers/repo_check_mailer.rb | 16 ------- app/mailers/repository_check_mailer.rb | 16 +++++++ app/views/admin/logs/show.html.haml | 2 +- app/views/admin/projects/index.html.haml | 12 ++--- app/views/admin/projects/show.html.haml | 18 +++---- app/views/repo_check_mailer/notify.html.haml | 5 -- app/views/repo_check_mailer/notify.text.haml | 3 -- app/views/repository_check_mailer/notify.html.haml | 5 ++ app/views/repository_check_mailer/notify.text.haml | 3 ++ app/workers/admin_email_worker.rb | 6 +-- app/workers/repo_check_worker.rb | 46 ------------------ app/workers/repository_check_worker.rb | 46 ++++++++++++++++++ app/workers/single_repo_check_worker.rb | 34 ------------- app/workers/single_repository_check_worker.rb | 34 +++++++++++++ config/gitlab.yml.example | 2 +- config/initializers/1_settings.rb | 6 +-- config/routes.rb | 4 +- .../20160315135439_project_add_repo_check.rb | 6 --- .../20160315135439_project_add_repository_check.rb | 6 +++ db/schema.rb | 4 +- doc/administration/repo_checks.md | 55 ---------------------- doc/administration/repository_checks.md | 55 ++++++++++++++++++++++ lib/gitlab/repo_check_logger.rb | 7 --- lib/gitlab/repository_check_logger.rb | 7 +++ spec/features/admin/admin_projects_spec.rb | 24 +++++----- spec/mailers/repo_check_mailer_spec.rb | 21 --------- spec/mailers/repository_check_mailer_spec.rb | 21 +++++++++ spec/workers/repo_check_worker_spec.rb | 31 ------------ spec/workers/repository_check_worker_spec.rb | 31 ++++++++++++ 30 files changed, 272 insertions(+), 272 deletions(-) delete mode 100644 app/mailers/repo_check_mailer.rb create mode 100644 app/mailers/repository_check_mailer.rb delete mode 100644 app/views/repo_check_mailer/notify.html.haml delete mode 100644 app/views/repo_check_mailer/notify.text.haml create mode 100644 app/views/repository_check_mailer/notify.html.haml create mode 100644 app/views/repository_check_mailer/notify.text.haml delete mode 100644 app/workers/repo_check_worker.rb create mode 100644 app/workers/repository_check_worker.rb delete mode 100644 app/workers/single_repo_check_worker.rb create mode 100644 app/workers/single_repository_check_worker.rb delete mode 100644 db/migrate/20160315135439_project_add_repo_check.rb create mode 100644 db/migrate/20160315135439_project_add_repository_check.rb delete mode 100644 doc/administration/repo_checks.md create mode 100644 doc/administration/repository_checks.md delete mode 100644 lib/gitlab/repo_check_logger.rb create mode 100644 lib/gitlab/repository_check_logger.rb delete mode 100644 spec/mailers/repo_check_mailer_spec.rb create mode 100644 spec/mailers/repository_check_mailer_spec.rb delete mode 100644 spec/workers/repo_check_worker_spec.rb create mode 100644 spec/workers/repository_check_worker_spec.rb diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index b8c276fb1bb..01257a68616 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -1,5 +1,5 @@ class Admin::ProjectsController < Admin::ApplicationController - before_action :project, only: [:show, :transfer, :repo_check] + before_action :project, only: [:show, :transfer, :repository_check] before_action :group, only: [:show, :transfer] def index @@ -8,7 +8,7 @@ class Admin::ProjectsController < Admin::ApplicationController @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present? @projects = @projects.with_push if params[:with_push].present? @projects = @projects.abandoned if params[:abandoned].present? - @projects = @projects.where(last_repo_check_failed: true) if params[:last_repo_check_failed].present? + @projects = @projects.where(last_repository_check_failed: true) if params[:last_repository_check_failed].present? @projects = @projects.non_archived unless params[:with_archived].present? @projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.sort(@sort = params[:sort]) @@ -31,24 +31,24 @@ class Admin::ProjectsController < Admin::ApplicationController redirect_to admin_namespace_project_path(@project.namespace, @project) end - def repo_check - SingleRepoCheckWorker.perform_async(@project.id) + def repository_check + SingleRepositoryCheckWorker.perform_async(@project.id) redirect_to( admin_namespace_project_path(@project.namespace, @project), - notice: 'Repo check was triggered' + notice: 'Repository check was triggered' ) end - def clear_repo_check_states + def clear_repository_check_states Project.update_all( - last_repo_check_failed: false, - last_repo_check_at: nil + last_repository_check_failed: false, + last_repository_check_at: nil ) redirect_to( admin_namespaces_projects_path, - notice: 'All project repo check states were cleared' + notice: 'All project states were cleared' ) end diff --git a/app/mailers/repo_check_mailer.rb b/app/mailers/repo_check_mailer.rb deleted file mode 100644 index d98533f120d..00000000000 --- a/app/mailers/repo_check_mailer.rb +++ /dev/null @@ -1,16 +0,0 @@ -class RepoCheckMailer < BaseMailer - include ActionView::Helpers::TextHelper - - def notify(failed_count) - if failed_count == 1 - @message = "One project failed its last repository check" - else - @message = "#{failed_count} projects failed their last repository check" - end - - mail( - to: User.admins.pluck(:email), - subject: @message - ) - end -end diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb new file mode 100644 index 00000000000..994054c8769 --- /dev/null +++ b/app/mailers/repository_check_mailer.rb @@ -0,0 +1,16 @@ +class RepositoryCheckMailer < BaseMailer + include ActionView::Helpers::TextHelper + + def notify(failed_count) + if failed_count == 1 + @message = "One project failed its last repository check" + else + @message = "#{failed_count} projects failed their last repository check" + end + + mail( + to: User.admins.pluck(:email), + subject: @message + ) + end +end diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index abcc93f4046..4b475a4d8fa 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -1,7 +1,7 @@ - page_title "Logs" - loggers = [Gitlab::GitLogger, Gitlab::AppLogger, Gitlab::ProductionLogger, Gitlab::SidekiqLogger, - Gitlab::RepoCheckLogger] + Gitlab::RepositoryCheckLogger] %ul.nav-links.log-tabs - loggers.each do |klass| %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') } diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index ed360f2f012..c2bf0659841 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -41,19 +41,19 @@ %fieldset %strong Problems .checkbox - = label_tag :last_repo_check_failed do - = check_box_tag :last_repo_check_failed, 1, params[:last_repo_check_failed] - %span Last repo check failed + = label_tag :last_repository_check_failed do + = check_box_tag :last_repository_check_failed, 1, params[:last_repository_check_failed] + %span Last repository check failed = hidden_field_tag :sort, params[:sort] = button_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel" - .panel.panel-default.repo-check-states + .panel.panel-default.repository-check-states .panel-heading - Repo check states + Repository check states .panel-body - = link_to 'Clear all', clear_repo_check_states_admin_namespace_projects_path(0), data: { confirm: 'This will clear repo check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" + = link_to 'Clear all', clear_repository_check_states_admin_namespace_projects_path(0), data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" %section.col-md-9 .panel.panel-default .panel-heading diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index a7a3f6349ef..5bef8e3ad57 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -5,12 +5,12 @@ %i.fa.fa-pencil-square-o Edit %hr -- if @project.last_repo_check_failed? +- if @project.last_repository_check_failed? .row .col-md-12 .panel .panel-heading.alert.alert-danger - Last repo check failed. See + Last repository check failed. See = link_to 'repocheck.log', admin_logs_path for error messages. .row @@ -103,19 +103,19 @@ .col-sm-offset-2.col-sm-10 = f.submit 'Transfer', class: 'btn btn-primary' - .panel.panel-default.repo-check + .panel.panel-default.repository-check .panel-heading - Repo check + Repository check .panel-body - = form_for @project, url: repo_check_admin_namespace_project_path(@project.namespace, @project), method: :post do |f| + = form_for @project, url: repository_check_admin_namespace_project_path(@project.namespace, @project), method: :post do |f| .form-group - - if @project.last_repo_check_at.nil? + - if @project.last_repository_check_at.nil? This repository has never been checked. - else This repository was last checked - = @project.last_repo_check_at.to_s(:medium) + '.' + = @project.last_repository_check_at.to_s(:medium) + '.' The check - - if @project.last_repo_check_failed? + - if @project.last_repository_check_failed? = succeed '.' do %strong.cred failed See @@ -125,7 +125,7 @@ passed. .form-group - = f.submit 'Trigger repo check', class: 'btn btn-primary' + = f.submit 'Trigger repository check', class: 'btn btn-primary' .col-md-6 - if @group diff --git a/app/views/repo_check_mailer/notify.html.haml b/app/views/repo_check_mailer/notify.html.haml deleted file mode 100644 index ef0016f9d3e..00000000000 --- a/app/views/repo_check_mailer/notify.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -%p - #{@message}. - -%p - = link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repo_check_failed: 1) diff --git a/app/views/repo_check_mailer/notify.text.haml b/app/views/repo_check_mailer/notify.text.haml deleted file mode 100644 index bdf8c2ad675..00000000000 --- a/app/views/repo_check_mailer/notify.text.haml +++ /dev/null @@ -1,3 +0,0 @@ -#{@message}. -\ -View details: #{admin_namespaces_projects_url(last_repo_check_failed: 1)} diff --git a/app/views/repository_check_mailer/notify.html.haml b/app/views/repository_check_mailer/notify.html.haml new file mode 100644 index 00000000000..df16f503570 --- /dev/null +++ b/app/views/repository_check_mailer/notify.html.haml @@ -0,0 +1,5 @@ +%p + #{@message}. + +%p + = link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repository_check_failed: 1) diff --git a/app/views/repository_check_mailer/notify.text.haml b/app/views/repository_check_mailer/notify.text.haml new file mode 100644 index 00000000000..02f3f80288a --- /dev/null +++ b/app/views/repository_check_mailer/notify.text.haml @@ -0,0 +1,3 @@ +#{@message}. +\ +View details: #{admin_namespaces_projects_url(last_repository_check_failed: 1)} diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb index fcccb9ea669..667fff031dd 100644 --- a/app/workers/admin_email_worker.rb +++ b/app/workers/admin_email_worker.rb @@ -4,9 +4,9 @@ class AdminEmailWorker sidekiq_options retry: false # this job auto-repeats via sidekiq-cron def perform - repo_check_failed_count = Project.where(last_repo_check_failed: true).count - return if repo_check_failed_count.zero? + repository_check_failed_count = Project.where(last_repository_check_failed: true).count + return if repository_check_failed_count.zero? - RepoCheckMailer.notify(repo_check_failed_count).deliver_now + RepositoryCheckMailer.notify(repository_check_failed_count).deliver_now end end diff --git a/app/workers/repo_check_worker.rb b/app/workers/repo_check_worker.rb deleted file mode 100644 index 9be795309e0..00000000000 --- a/app/workers/repo_check_worker.rb +++ /dev/null @@ -1,46 +0,0 @@ -class RepoCheckWorker - include Sidekiq::Worker - - RUN_TIME = 3600 - - sidekiq_options retry: false - - def perform - start = Time.now - - # This loop will break after a little more than one hour ('a little - # more' because `git fsck` may take a few minutes), or if it runs out of - # projects to check. By default sidekiq-cron will start a new - # RepoCheckWorker each hour so that as long as there are repositories to - # check, only one (or two) will be checked at a time. - project_ids.each do |project_id| - break if Time.now - start >= RUN_TIME - - next if !try_obtain_lease(project_id) - - SingleRepoCheckWorker.new.perform(project_id) - end - end - - private - - # In an ideal world we would use Project.where(...).find_each. - # Unfortunately, calling 'find_each' drops the 'where', so we must build - # an array of IDs instead. - def project_ids - limit = 10_000 - never_checked_projects = Project.where('last_repo_check_at IS NULL').limit(limit). - pluck(:id) - old_check_projects = Project.where('last_repo_check_at < ?', 1.week.ago). - reorder('last_repo_check_at ASC').limit(limit).pluck(:id) - never_checked_projects + old_check_projects - end - - def try_obtain_lease(id) - lease = Gitlab::ExclusiveLease.new( - "project_repo_check:#{id}", - timeout: RUN_TIME - ) - lease.try_obtain - end -end diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb new file mode 100644 index 00000000000..2d75c8bafde --- /dev/null +++ b/app/workers/repository_check_worker.rb @@ -0,0 +1,46 @@ +class RepositoryCheckWorker + include Sidekiq::Worker + + RUN_TIME = 3600 + + sidekiq_options retry: false + + def perform + start = Time.now + + # This loop will break after a little more than one hour ('a little + # more' because `git fsck` may take a few minutes), or if it runs out of + # projects to check. By default sidekiq-cron will start a new + # RepositoryCheckWorker each hour so that as long as there are repositories to + # check, only one (or two) will be checked at a time. + project_ids.each do |project_id| + break if Time.now - start >= RUN_TIME + + next if !try_obtain_lease(project_id) + + SingleRepositoryCheckWorker.new.perform(project_id) + end + end + + private + + # In an ideal world we would use Project.where(...).find_each. + # Unfortunately, calling 'find_each' drops the 'where', so we must build + # an array of IDs instead. + def project_ids + limit = 10_000 + never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). + pluck(:id) + old_check_projects = Project.where('last_repository_check_at < ?', 1.week.ago). + reorder('last_repository_check_at ASC').limit(limit).pluck(:id) + never_checked_projects + old_check_projects + end + + def try_obtain_lease(id) + lease = Gitlab::ExclusiveLease.new( + "project_repository_check:#{id}", + timeout: RUN_TIME + ) + lease.try_obtain + end +end diff --git a/app/workers/single_repo_check_worker.rb b/app/workers/single_repo_check_worker.rb deleted file mode 100644 index f8b245247c5..00000000000 --- a/app/workers/single_repo_check_worker.rb +++ /dev/null @@ -1,34 +0,0 @@ -class SingleRepoCheckWorker - include Sidekiq::Worker - - sidekiq_options retry: false - - def perform(project_id) - project = Project.find(project_id) - update(project, success: check(project)) - end - - private - - def check(project) - [project.repository.path_to_repo, project.wiki.wiki.path].all? do |path| - git_fsck(path) - end - end - - def git_fsck(path) - cmd = %W(nice git --git-dir=#{path} fsck) - output, status = Gitlab::Popen.popen(cmd) - return true if status.zero? - - Gitlab::RepoCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") - false - end - - def update(project, success:) - project.update_columns( - last_repo_check_failed: !success, - last_repo_check_at: Time.now, - ) - end -end diff --git a/app/workers/single_repository_check_worker.rb b/app/workers/single_repository_check_worker.rb new file mode 100644 index 00000000000..d9eed9bd708 --- /dev/null +++ b/app/workers/single_repository_check_worker.rb @@ -0,0 +1,34 @@ +class SingleRepositoryCheckWorker + include Sidekiq::Worker + + sidekiq_options retry: false + + def perform(project_id) + project = Project.find(project_id) + update(project, success: check(project)) + end + + private + + def check(project) + [project.repository.path_to_repo, project.wiki.wiki.path].all? do |path| + git_fsck(path) + end + end + + def git_fsck(path) + cmd = %W(nice git --git-dir=#{path} fsck) + output, status = Gitlab::Popen.popen(cmd) + return true if status.zero? + + Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") + false + end + + def update(project, success:) + project.update_columns( + last_repository_check_failed: !success, + last_repository_check_at: Time.now, + ) + end +end diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index cf20fc9c63a..4fbef653bc1 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -157,7 +157,7 @@ production: &base cron: "0 0 * * *" # Periodically run 'git fsck' on all repositories. If started more than # once per hour you will have concurrent 'git fsck' jobs. - repo_check_worker: + repository_check_worker: cron: "20 * * * *" # Send admin emails once a day admin_email_worker: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 8240978ef06..23771553c45 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -239,9 +239,9 @@ Settings['cron_jobs'] ||= Settingslogic.new({}) Settings.cron_jobs['stuck_ci_builds_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['stuck_ci_builds_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['stuck_ci_builds_worker']['job_class'] = 'StuckCiBuildsWorker' -Settings.cron_jobs['repo_check_worker'] ||= Settingslogic.new({}) -Settings.cron_jobs['repo_check_worker']['cron'] ||= '20 * * * *' -Settings.cron_jobs['repo_check_worker']['job_class'] = 'RepoCheckWorker' +Settings.cron_jobs['repository_check_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['repository_check_worker']['cron'] ||= '20 * * * *' +Settings.cron_jobs['repository_check_worker']['job_class'] = 'RepositoryCheckWorker' Settings.cron_jobs['admin_email_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['admin_email_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['admin_email_worker']['job_class'] = 'AdminEmailWorker' diff --git a/config/routes.rb b/config/routes.rb index fad8600b77d..c0ed99b1964 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -264,11 +264,11 @@ Rails.application.routes.draw do member do put :transfer - post :repo_check + post :repository_check end collection do - put :clear_repo_check_states + put :clear_repository_check_states end resources :runner_projects diff --git a/db/migrate/20160315135439_project_add_repo_check.rb b/db/migrate/20160315135439_project_add_repo_check.rb deleted file mode 100644 index ba7ddc9ecb4..00000000000 --- a/db/migrate/20160315135439_project_add_repo_check.rb +++ /dev/null @@ -1,6 +0,0 @@ -class ProjectAddRepoCheck < ActiveRecord::Migration - def change - add_column :projects, :last_repo_check_failed, :boolean, default: false - add_column :projects, :last_repo_check_at, :datetime - end -end diff --git a/db/migrate/20160315135439_project_add_repository_check.rb b/db/migrate/20160315135439_project_add_repository_check.rb new file mode 100644 index 00000000000..5a0859a30b2 --- /dev/null +++ b/db/migrate/20160315135439_project_add_repository_check.rb @@ -0,0 +1,6 @@ +class ProjectAddRepositoryCheck < ActiveRecord::Migration + def change + add_column :projects, :last_repository_check_failed, :boolean, default: false + add_column :projects, :last_repository_check_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index d2c183f968b..53509956888 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -732,8 +732,8 @@ ActiveRecord::Schema.define(version: 20160331133914) do t.boolean "public_builds", default: true, null: false t.string "main_language" t.integer "pushes_since_gc", default: 0 - t.boolean "last_repo_check_failed", default: false - t.datetime "last_repo_check_at" + t.boolean "last_repository_check_failed", default: false + t.datetime "last_repository_check_at" end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree diff --git a/doc/administration/repo_checks.md b/doc/administration/repo_checks.md deleted file mode 100644 index 81087f3ac1c..00000000000 --- a/doc/administration/repo_checks.md +++ /dev/null @@ -1,55 +0,0 @@ -# Repo checks - -_**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ - ---- - -Git has a built-in mechanism [git fsck][git-fsck] to verify the -integrity of all data commited to a repository. GitLab administrators can -trigger such a check for a project via the admin panel. The checks run -asynchronously so it may take a few minutes before the check result is -visible on the project admin page. If the checks failed you can see their -output on the admin log page under 'repocheck.log'. - -## Periodical checks - -GitLab periodically runs a repo check on all project repositories and -wiki repositories in order to detect data corruption problems. A -project will be checked no more than once per week. If any projects -fail their repo checks all GitLab administrators will receive an email -notification of the situation. This notification is sent out no more -than once a day. - -## Disabling periodic checks - -You can disable the periodic checks by giving them an empty cron -schedule in gitlab.yml. - -``` -# For omnibus installations, in /etc/gitlab/gitlab.rb: -gitlab_rails['cron_jobs_repo_check_worker_cron'] = '' -``` - -``` -# For installations from source, in config/gitlab.yml: - cron_jobs: - repo_check_worker: - cron: "" -``` - -## What to do if a check failed - -If the repo check fails for some repository you should look up the error -in repocheck.log (in the admin panel or on disk; see -`/var/log/gitlab/gitlab-rails` for Omnibus installations or -`/home/git/gitlab/log` for installations from source). Once you have -resolved the issue use the admin panel to trigger a new repo check on -the project. This will clear the 'check failed' state. - -If for some reason the periodical repo check caused a lot of false -alarms you can choose to clear ALL repo check states from the admin -project index page. - ---- -[ce-3232]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3232 "Auto git fsck" -[git-fsck]: https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html "git fsck documentation" \ No newline at end of file diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md new file mode 100644 index 00000000000..77f28209f2f --- /dev/null +++ b/doc/administration/repository_checks.md @@ -0,0 +1,55 @@ +# Repository checks + +_**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ + +--- + +Git has a built-in mechanism [git fsck][git-fsck] to verify the +integrity of all data commited to a repository. GitLab administrators can +trigger such a check for a project via the admin panel. The checks run +asynchronously so it may take a few minutes before the check result is +visible on the project admin page. If the checks failed you can see their +output on the admin log page under 'repocheck.log'. + +## Periodical checks + +GitLab periodically runs a repository check on all project repositories and +wiki repositories in order to detect data corruption problems. A +project will be checked no more than once per week. If any projects +fail their repository checks all GitLab administrators will receive an email +notification of the situation. This notification is sent out no more +than once a day. + +## Disabling periodic checks + +You can disable the periodic checks by giving them an empty cron +schedule in gitlab.yml. + +``` +# For omnibus installations, in /etc/gitlab/gitlab.rb: +gitlab_rails['cron_jobs_repository_check_worker_cron'] = '' +``` + +``` +# For installations from source, in config/gitlab.yml: + cron_jobs: + repository_check_worker: + cron: "" +``` + +## What to do if a check failed + +If the repository check fails for some repository you should look up the error +in repocheck.log (in the admin panel or on disk; see +`/var/log/gitlab/gitlab-rails` for Omnibus installations or +`/home/git/gitlab/log` for installations from source). Once you have +resolved the issue use the admin panel to trigger a new repository check on +the project. This will clear the 'check failed' state. + +If for some reason the periodical repository check caused a lot of false +alarms you can choose to clear ALL repository check states from the admin +project index page. + +--- +[ce-3232]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3232 "Auto git fsck" +[git-fsck]: https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html "git fsck documentation" \ No newline at end of file diff --git a/lib/gitlab/repo_check_logger.rb b/lib/gitlab/repo_check_logger.rb deleted file mode 100644 index 9409f68722c..00000000000 --- a/lib/gitlab/repo_check_logger.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Gitlab - class RepoCheckLogger < Gitlab::Logger - def self.file_name_noext - 'repocheck' - end - end -end diff --git a/lib/gitlab/repository_check_logger.rb b/lib/gitlab/repository_check_logger.rb new file mode 100644 index 00000000000..485b596ca57 --- /dev/null +++ b/lib/gitlab/repository_check_logger.rb @@ -0,0 +1,7 @@ +module Gitlab + class RepositoryCheckLogger < Gitlab::Logger + def self.file_name_noext + 'repocheck' + end + end +end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index e3991d48ed6..95a230a72c3 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -33,33 +33,33 @@ describe "Admin Projects", feature: true do end end - feature 'repo checks' do - scenario 'trigger repo check' do + feature 'repository checks' do + scenario 'trigger repository check' do visit_admin_project_page - page.within('.repo-check') do - click_button 'Trigger repo check' + page.within('.repository-check') do + click_button 'Trigger repository check' end - expect(page).to have_content('Repo check was triggered') + expect(page).to have_content('Repository check was triggered') end - scenario 'see failed repo check' do - @project.update_column(:last_repo_check_failed, true) + scenario 'see failed repository check' do + @project.update_column(:last_repository_check_failed, true) visit_admin_project_page - expect(page).to have_content('Last repo check failed') + expect(page).to have_content('Last repository check failed') end - scenario 'clear repo checks', js: true do - @project.update_column(:last_repo_check_failed, true) + scenario 'clear repository checks', js: true do + @project.update_column(:last_repository_check_failed, true) visit admin_namespaces_projects_path - page.within('.repo-check-states') do + page.within('.repository-check-states') do click_link 'Clear all' # pop-up should be auto confirmed end - expect(@project.reload.last_repo_check_failed).to eq(false) + expect(@project.reload.last_repository_check_failed).to eq(false) end end diff --git a/spec/mailers/repo_check_mailer_spec.rb b/spec/mailers/repo_check_mailer_spec.rb deleted file mode 100644 index d49a6ae0c05..00000000000 --- a/spec/mailers/repo_check_mailer_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'rails_helper' - -describe RepoCheckMailer do - include EmailSpec::Matchers - - describe '.notify' do - it 'emails all admins' do - admins = 3.times.map { create(:admin) } - - mail = described_class.notify(1) - - expect(mail).to deliver_to admins.map(&:email) - end - - it 'mentions the number of failed checks' do - mail = described_class.notify(3) - - expect(mail).to have_subject '3 projects failed their last repository check' - end - end -end diff --git a/spec/mailers/repository_check_mailer_spec.rb b/spec/mailers/repository_check_mailer_spec.rb new file mode 100644 index 00000000000..6ae9a93aaac --- /dev/null +++ b/spec/mailers/repository_check_mailer_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +describe RepositoryCheckMailer do + include EmailSpec::Matchers + + describe '.notify' do + it 'emails all admins' do + admins = 3.times.map { create(:admin) } + + mail = described_class.notify(1) + + expect(mail).to deliver_to admins.map(&:email) + end + + it 'mentions the number of failed checks' do + mail = described_class.notify(3) + + expect(mail).to have_subject '3 projects failed their last repository check' + end + end +end diff --git a/spec/workers/repo_check_worker_spec.rb b/spec/workers/repo_check_worker_spec.rb deleted file mode 100644 index 7ef3eba9ac5..00000000000 --- a/spec/workers/repo_check_worker_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'spec_helper' - -describe RepoCheckWorker do - subject { RepoCheckWorker.new } - - it 'prefers projects that have never been checked' do - projects = 3.times.map { create(:project) } - projects[0].update_column(:last_repo_check_at, 1.month.ago) - projects[2].update_column(:last_repo_check_at, 3.weeks.ago) - - expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) - end - - it 'sorts projects by last_repo_check_at' do - projects = 3.times.map { create(:project) } - projects[0].update_column(:last_repo_check_at, 2.weeks.ago) - projects[1].update_column(:last_repo_check_at, 1.month.ago) - projects[2].update_column(:last_repo_check_at, 3.weeks.ago) - - expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) - end - - it 'excludes projects that were checked recently' do - projects = 3.times.map { create(:project) } - projects[0].update_column(:last_repo_check_at, 2.days.ago) - projects[1].update_column(:last_repo_check_at, 1.month.ago) - projects[2].update_column(:last_repo_check_at, 3.days.ago) - - expect(subject.perform).to eq([projects[1].id]) - end -end diff --git a/spec/workers/repository_check_worker_spec.rb b/spec/workers/repository_check_worker_spec.rb new file mode 100644 index 00000000000..d1849321f56 --- /dev/null +++ b/spec/workers/repository_check_worker_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe RepositoryCheckWorker do + subject { RepositoryCheckWorker.new } + + it 'prefers projects that have never been checked' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) + end + + it 'sorts projects by last_repository_check_at' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repository_check_at, 2.weeks.ago) + projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) + end + + it 'excludes projects that were checked recently' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repository_check_at, 2.days.ago) + projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.days.ago) + + expect(subject.perform).to eq([projects[1].id]) + end +end -- cgit v1.2.1 From 15044e7d857138b31199b796f02a81f0c29c643f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 7 Apr 2016 15:00:20 +0200 Subject: refactored a few things based on MR feedback --- app/models/project.rb | 13 +++++++++++++ db/schema.rb | 8 ++++---- lib/gitlab/bitbucket_import/project_creator.rb | 5 +---- lib/gitlab/fogbugz_import/importer.rb | 10 +++++----- lib/gitlab/fogbugz_import/project_creator.rb | 7 +------ lib/gitlab/google_code_import/project_creator.rb | 4 +--- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 7b1188420ef..17b971b9d30 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -424,6 +424,19 @@ class Project < ActiveRecord::Base project_import_data.save end + def create_or_update_import_data(credentials) + project_import_data = import_data || build_import_data + project_import_data.credentials ||= {} + project_import_data.credentials = project_import_data.credentials.merge(credentials) + project_import_data.save + end + + def update_import_data(data: nil, credentials: nil) + import_data.data = data if data + import_data.credentials = import_data.credentials.merge(credentials) if credentials + import_data.save + end + def import? external_import? || forked? end diff --git a/db/schema.rb b/db/schema.rb index 5d87dbfe41f..53d12aa35dd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160331223143) do +ActiveRecord::Schema.define(version: 20160320204112) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -44,6 +44,7 @@ ActiveRecord::Schema.define(version: 20160331223143) do t.datetime "updated_at" t.string "home_page_url" t.integer "default_branch_protection", default: 2 + t.boolean "twitter_sharing_enabled", default: true t.text "restricted_visibility_levels" t.boolean "version_check_enabled", default: true t.integer "max_attachment_size", default: 10, null: false @@ -416,9 +417,9 @@ ActiveRecord::Schema.define(version: 20160331223143) do t.string "state" t.integer "iid" t.integer "updated_by_id" - t.boolean "confidential", default: false - t.datetime "deleted_at" t.integer "moved_to_id" + t.boolean "confidential", default: false + t.datetime "deleted_at" end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree @@ -747,7 +748,6 @@ ActiveRecord::Schema.define(version: 20160331223143) do add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree add_index "projects", ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} - add_index "projects", ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index 109010cb962..65b62b2b816 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -23,10 +23,7 @@ module Gitlab import_url: "ssh://git@bitbucket.org/#{repo["owner"]}/#{repo["slug"]}.git", ).execute - import_data = project.import_data - # merge! with a bang doesn't work here - import_data.credentials = import_data.credentials.merge(bb_session: session_data) - import_data.save + project.update_import_data(credentials: { bb_session: session_data }) project end diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index 42f9b6eab84..501d5a95547 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -18,7 +18,7 @@ module Gitlab def execute return true unless repo.valid? - client = Gitlab::FogbugzImport::Client.new(token: import_data_credentials[:fb_session][:token], uri: import_data_credentials[:fb_session][:uri]) + client = Gitlab::FogbugzImport::Client.new(token: fb_session[:token], uri: fb_session[:uri]) @cases = client.cases(@repo.id.to_i) @categories = client.categories @@ -30,8 +30,8 @@ module Gitlab private - def import_data_credentials - @import_data_credentials ||= project.import_data.credentials if project.import_data + def fb_session + @import_data_credentials ||= project.import_data.credentials[:fb_session] if project.import_data && project.import_data.credentials end def user_map @@ -240,8 +240,8 @@ module Gitlab end def build_attachment_url(rel_url) - uri = import_data_credentials[:fb_session][:uri] - token = import_data_credentials[:fb_session][:token] + uri = fb_session[:uri] + token = fb_session[:token] "#{uri}/#{rel_url}&token=#{token}" end diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb index e9fac8968e6..c000b300468 100644 --- a/lib/gitlab/fogbugz_import/project_creator.rb +++ b/lib/gitlab/fogbugz_import/project_creator.rb @@ -24,12 +24,7 @@ module Gitlab import_url: Project::UNKNOWN_IMPORT_URL ).execute - import_data = project.import_data - import_data.data = { 'repo' => repo.raw_data, 'user_map' => user_map } - - # merge! with a bang doesn't work here - import_data.credentials = import_data.credentials.merge(fb_session: fb_session) - import_data.save + project.update_import_data(data: { 'repo' => repo.raw_data, 'user_map' => user_map }, credentials: { fb_session: fb_session }) project end diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb index 49d6013af28..d2e20afbb1e 100644 --- a/lib/gitlab/google_code_import/project_creator.rb +++ b/lib/gitlab/google_code_import/project_creator.rb @@ -24,9 +24,7 @@ module Gitlab import_url: repo.import_url ).execute - import_data = project.import_data - import_data.data = { 'repo' => repo.raw_data, 'user_map' => user_map } - import_data.save + project.update_import_data(data: { 'repo' => repo.raw_data, 'user_map' => user_map }) project end -- cgit v1.2.1 From a1a1d1f7de71f46787f12f1efa23346a2a6b1c29 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 7 Apr 2016 15:08:38 +0200 Subject: refactored create_or_update_import_data --- app/models/project.rb | 23 +++++++---------------- lib/gitlab/bitbucket_import/project_creator.rb | 2 +- lib/gitlab/fogbugz_import/project_creator.rb | 2 +- lib/gitlab/google_code_import/project_creator.rb | 2 +- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 17b971b9d30..273b04c6323 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -404,7 +404,7 @@ class Project < ActiveRecord::Base def import_url=(value) import_url = Gitlab::ImportUrl.new(value) - create_or_update_import_data(import_url.credentials) + create_or_update_import_data(credentials: import_url.credentials) super(import_url.sanitized_url) end @@ -417,26 +417,17 @@ class Project < ActiveRecord::Base end end - def create_or_update_import_data(credentials) + def create_or_update_import_data(data: nil, credentials: nil) project_import_data = import_data || build_import_data - project_import_data.credentials ||= {} - project_import_data.credentials = project_import_data.credentials.merge(credentials) - project_import_data.save - end + project_import_data.data = data if data + if credentials + project_import_data.credentials ||= {} + project_import_data.credentials = project_import_data.credentials.merge(credentials) + end - def create_or_update_import_data(credentials) - project_import_data = import_data || build_import_data - project_import_data.credentials ||= {} - project_import_data.credentials = project_import_data.credentials.merge(credentials) project_import_data.save end - def update_import_data(data: nil, credentials: nil) - import_data.data = data if data - import_data.credentials = import_data.credentials.merge(credentials) if credentials - import_data.save - end - def import? external_import? || forked? end diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index 65b62b2b816..941f818b847 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -23,7 +23,7 @@ module Gitlab import_url: "ssh://git@bitbucket.org/#{repo["owner"]}/#{repo["slug"]}.git", ).execute - project.update_import_data(credentials: { bb_session: session_data }) + project.create_or_update_import_data(credentials: { bb_session: session_data }) project end diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb index c000b300468..3840765db87 100644 --- a/lib/gitlab/fogbugz_import/project_creator.rb +++ b/lib/gitlab/fogbugz_import/project_creator.rb @@ -24,7 +24,7 @@ module Gitlab import_url: Project::UNKNOWN_IMPORT_URL ).execute - project.update_import_data(data: { 'repo' => repo.raw_data, 'user_map' => user_map }, credentials: { fb_session: fb_session }) + project.create_or_update_import_data(data: { 'repo' => repo.raw_data, 'user_map' => user_map }, credentials: { fb_session: fb_session }) project end diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb index d2e20afbb1e..0abb7a64c17 100644 --- a/lib/gitlab/google_code_import/project_creator.rb +++ b/lib/gitlab/google_code_import/project_creator.rb @@ -24,7 +24,7 @@ module Gitlab import_url: repo.import_url ).execute - project.update_import_data(data: { 'repo' => repo.raw_data, 'user_map' => user_map }) + project.create_or_update_import_data(data: { 'repo' => repo.raw_data, 'user_map' => user_map }) project end -- cgit v1.2.1 From 0ceee903f14d1824aa56452b83ae4a8e090cbc14 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 7 Apr 2016 15:49:46 +0200 Subject: fix schema file [ci skip] --- db/schema.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 53d12aa35dd..df4c65a0625 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160320204112) do +ActiveRecord::Schema.define(version: 20160331133914) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -418,7 +418,7 @@ ActiveRecord::Schema.define(version: 20160320204112) do t.integer "iid" t.integer "updated_by_id" t.integer "moved_to_id" - t.boolean "confidential", default: false + t.boolean "confidential", default: false t.datetime "deleted_at" end @@ -748,6 +748,7 @@ ActiveRecord::Schema.define(version: 20160320204112) do add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree add_index "projects", ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} + add_index "projects", ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree -- cgit v1.2.1 From 350a9aa984100a4d91454200fb4ac308108e5cef Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 7 Apr 2016 16:53:15 +0200 Subject: fix create_or_update_import_data --- app/models/project.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 273b04c6323..6304699386d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -419,7 +419,10 @@ class Project < ActiveRecord::Base def create_or_update_import_data(data: nil, credentials: nil) project_import_data = import_data || build_import_data - project_import_data.data = data if data + if data + project_import_data.data ||= {} + project_import_data.data = project_import_data.data.merge(data) + end if credentials project_import_data.credentials ||= {} project_import_data.credentials = project_import_data.credentials.merge(credentials) -- cgit v1.2.1 From 1266f41e28ae7a8da9f85f0ede70291f34ed7988 Mon Sep 17 00:00:00 2001 From: Christoph Junghans Date: Sat, 9 Apr 2016 11:59:12 -0600 Subject: Add test coverage parsing example for gcvor --- app/views/projects/_builds_settings.html.haml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/projects/_builds_settings.html.haml b/app/views/projects/_builds_settings.html.haml index 9ae6964aaac..b6074373e2b 100644 --- a/app/views/projects/_builds_settings.html.haml +++ b/app/views/projects/_builds_settings.html.haml @@ -52,6 +52,9 @@ %li phpunit --coverage-text --colors=never (PHP) - %code ^\s*Lines:\s*\d+.\d+\% + %li + gcovr (C/C++) - + %code ^TOTAL.*\s+(\d+\%)$ .form-group .col-sm-offset-2.col-sm-10 -- cgit v1.2.1 From 5ffa8f057095fb2fe12a60ffa0dd3a611d2f1aeb Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 9 Apr 2016 18:40:15 -0400 Subject: Escape the query argument provided to `git grep` by `search_files` Closes #14963. --- app/models/repository.rb | 2 +- spec/models/repository_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 8dead3a5884..090cccd2c72 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -795,7 +795,7 @@ class Repository def search_files(query, ref) offset = 2 - args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref}) + args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -e #{Regexp.escape(query)} #{ref || root_ref}) Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/) end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 4e49c413f23..bce30aafc4c 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -94,6 +94,12 @@ describe Repository, models: true do it { is_expected.to be_an Array } + it 'regex-escapes the query string' do + results = repository.search_files("test\\", 'master') + + expect(results.first).not_to start_with('fatal:') + end + describe 'result' do subject { results.first } -- cgit v1.2.1 From be834c4de93a7716035f5373210ea3922c26da72 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 11 Apr 2016 11:13:51 +0200 Subject: changed a few things based on feedback --- app/models/project_import_data.rb | 8 ++++++-- ...add_import_credentials_to_project_import_data.rb | 4 ++-- ...2152808_remove_wrong_import_url_from_projects.rb | 21 +++++++++------------ spec/factories/project_import_data.rb | 6 ------ 4 files changed, 17 insertions(+), 22 deletions(-) delete mode 100644 spec/factories/project_import_data.rb diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index a0994312003..79efb403058 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -12,7 +12,11 @@ require 'file_size_validator' class ProjectImportData < ActiveRecord::Base belongs_to :project - attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true, mode: :per_attribute_iv_and_salt + attr_encrypted :credentials, + key: Gitlab::Application.secrets.db_key_base, + marshal: true, + encode: true, + mode: :per_attribute_iv_and_salt serialize :data, JSON @@ -21,7 +25,7 @@ class ProjectImportData < ActiveRecord::Base before_validation :symbolize_credentials def symbolize_credentials - # bang doesn't work here + # bang doesn't work here - attr_encrypted makes it not to work self.credentials = self.credentials.deep_symbolize_keys unless self.credentials.blank? end end diff --git a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb index dd2b3613983..ffcd64266e3 100644 --- a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb +++ b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb @@ -1,7 +1,7 @@ class AddImportCredentialsToProjectImportData < ActiveRecord::Migration def change add_column :project_import_data, :encrypted_credentials, :text - add_column :project_import_data, :encrypted_credentials_iv, :text - add_column :project_import_data, :encrypted_credentials_salt, :text + add_column :project_import_data, :encrypted_credentials_iv, :string + add_column :project_import_data, :encrypted_credentials_salt, :string end end diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 93e040fce28..dd2842b835e 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -3,7 +3,7 @@ # #down method not supported class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration - class FakeProjectImportData + class ProjectImportDataFake extend AttrEncrypted attr_accessor :credentials attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true, :mode => :per_attribute_iv_and_salt @@ -13,14 +13,11 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration say("Encrypting and migrating project import credentials...") # This should cover GitHub, GitLab, Bitbucket user:password, token@domain, and other similar URLs. - say("Projects and GitHub projects with a wrong URL. It also migrates GitLab project credentials.") - in_transaction { process_projects_with_wrong_url } + in_transaction(message: "Projects including GitHub and GitLab projects with an unsecured URL.") { process_projects_with_wrong_url } - say("Migrating Bitbucket credentials...") - in_transaction { process_project(import_type: 'bitbucket', credentials_keys: ['bb_session']) } + in_transaction(message: "Migrating Bitbucket credentials...") { process_project(import_type: 'bitbucket', credentials_keys: ['bb_session']) } - say("Migrating FogBugz credentials...") - in_transaction { process_project(import_type: 'fogbugz', credentials_keys: ['fb_session']) } + in_transaction(message: "Migrating FogBugz credentials...") { process_project(import_type: 'fogbugz', credentials_keys: ['fb_session']) } end @@ -33,7 +30,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end end - def process_project(import_type: , credentials_keys: []) + def process_project(import_type:, credentials_keys: []) unencrypted_import_data(import_type: import_type).each do |data| replace_data_credentials(data, credentials_keys) end @@ -56,8 +53,8 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration new_data_hash.deep_symbolize_keys end - def in_transaction - say_with_time("Processing new transaction...") do + def in_transaction(message:) + say_with_time(message) do ActiveRecord::Base.transaction do yield end @@ -65,7 +62,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def update_import_data(import_url, project) - fake_import_data = FakeProjectImportData.new + fake_import_data = ProjectImportDataFake.new fake_import_data.credentials = import_url.credentials import_data_id = project['import_data_id'] if import_data_id @@ -76,7 +73,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end def update_with_encrypted_data(data_hash, import_data_id, unencrypted_data = ' NULL ') - fake_import_data = FakeProjectImportData.new + fake_import_data = ProjectImportDataFake.new fake_import_data.credentials = data_hash execute(update_import_data_sql(import_data_id, fake_import_data, unencrypted_data)) end diff --git a/spec/factories/project_import_data.rb b/spec/factories/project_import_data.rb deleted file mode 100644 index 9e08d5a22e9..00000000000 --- a/spec/factories/project_import_data.rb +++ /dev/null @@ -1,6 +0,0 @@ -FactoryGirl.define do - factory :project_import_data, class: ProjectImportData do - data "test" - project - end -end -- cgit v1.2.1 From fb63173df2bf81c155ed311d2c8fc5889a5faf1d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 11 Apr 2016 11:28:35 +0200 Subject: typo --- db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index dd2842b835e..b97b79f920d 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -110,7 +110,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration #GitHub projects with token, and any user:password@ based URL #TODO: may need to add import_type != list def projects_with_wrong_import_url - select_all("SELECT p.id, p.import_url, i.id as import_data_id FROM projects p LEFT JOIN project_import_data i on p.id = i.id WHERE p.import_url IS NOT NULL AND p.import_url LIKE '%//%@%'") + select_all("SELECT p.id, p.import_url, i.id as import_data_id FROM projects p LEFT JOIN project_import_data i on p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_url LIKE '%//%@%'") end # All imports with data for import_type -- cgit v1.2.1 From 667d44c25ccefb511fc0d206eaa5990117032236 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 11 Apr 2016 12:46:19 +0200 Subject: Fix high CPU usage when PostReceive receives refs/merge-requests/ --- CHANGELOG | 1 + app/workers/post_receive.rb | 2 +- spec/workers/post_receive_spec.rb | 43 +++++++++++++++++++++++++++++++++++---- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3561c541df0..6a196dd9dce 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,7 @@ v 8.7.0 (unreleased) - API: Ability to filter milestones by state `active` and `closed` (Robert Schilling) - Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.) - Better errors handling when creating milestones inside groups + - Fix high CPU usage when PostReceive receives refs/merge-requests/ - Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.) - Gracefully handle notes on deleted commits in merge requests (Stan Hu) - Fix creation of merge requests for orphaned branches (Stan Hu) diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index 3cc232ef1ae..9e1215b21a6 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -40,7 +40,7 @@ class PostReceive if Gitlab::Git.tag_ref?(ref) GitTagPushService.new.execute(post_received.project, @user, oldrev, newrev, ref) - else + elsif Gitlab::Git.branch_ref?(ref) GitPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute end end diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index 0265dbe9c66..94ff3457902 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -4,6 +4,9 @@ describe PostReceive do let(:changes) { "123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag" } let(:wrongly_encoded_changes) { changes.encode("ISO-8859-1").force_encoding("UTF-8") } let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) } + let(:project) { create(:project) } + let(:key) { create(:key, user: project.owner) } + let(:key_id) { key.shell_id } context "as a resque worker" do it "reponds to #perform" do @@ -11,11 +14,43 @@ describe PostReceive do end end - context "webhook" do - let(:project) { create(:project) } - let(:key) { create(:key, user: project.owner) } - let(:key_id) { key.shell_id } + describe "#process_project_changes" do + before do + allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner) + end + context "branches" do + let(:changes) { "123456 789012 refs/heads/tést" } + + it "should call GitTagPushService" do + expect_any_instance_of(GitPushService).to receive(:execute).and_return(true) + expect_any_instance_of(GitTagPushService).not_to receive(:execute) + PostReceive.new.perform(pwd(project), key_id, base64_changes) + end + end + + context "tags" do + let(:changes) { "123456 789012 refs/tags/tag" } + + it "should call GitTagPushService" do + expect_any_instance_of(GitPushService).not_to receive(:execute) + expect_any_instance_of(GitTagPushService).to receive(:execute).and_return(true) + PostReceive.new.perform(pwd(project), key_id, base64_changes) + end + end + + context "merge-requests" do + let(:changes) { "123456 789012 refs/merge-requests/123" } + + it "should not call any of the services" do + expect_any_instance_of(GitPushService).not_to receive(:execute) + expect_any_instance_of(GitTagPushService).not_to receive(:execute) + PostReceive.new.perform(pwd(project), key_id, base64_changes) + end + end + end + + context "webhook" do it "fetches the correct project" do expect(Project).to receive(:find_with_namespace).with(project.path_with_namespace).and_return(project) PostReceive.new.perform(pwd(project), key_id, base64_changes) -- cgit v1.2.1 From de4d98fd120fd43bd744abc116c62708577b5673 Mon Sep 17 00:00:00 2001 From: Arinde Eniola Date: Mon, 11 Apr 2016 15:19:11 +0100 Subject: fix bug causing comment form in issue to submit twice when CTRL+Enter is pressed twice --- app/assets/javascripts/behaviors/quick_submit.js.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/behaviors/quick_submit.js.coffee b/app/assets/javascripts/behaviors/quick_submit.js.coffee index 6e29d374267..3cb96bacaa7 100644 --- a/app/assets/javascripts/behaviors/quick_submit.js.coffee +++ b/app/assets/javascripts/behaviors/quick_submit.js.coffee @@ -29,7 +29,11 @@ $(document).on 'keydown.quick_submit', '.js-quick-submit', (e) -> e.preventDefault() $form = $(e.target).closest('form') - $form.find('input[type=submit], button[type=submit]').disable() + $submit_button = $form.find('input[type=submit], button[type=submit]') + + return if $submit_button.attr('disabled') + + $submit_button.disable() $form.submit() # If the user tabs to a submit button on a `js-quick-submit` form, display a -- cgit v1.2.1 From 979dedba8a68f33b8e2078f7e2980bf048a8a25a Mon Sep 17 00:00:00 2001 From: Arinde Eniola Date: Mon, 11 Apr 2016 16:24:49 +0100 Subject: make milestone labels in labels tab similar to that of the labels page --- app/assets/stylesheets/pages/labels.scss | 25 ++++++++++++++++------- app/views/shared/milestones/_labels_tab.html.haml | 13 ++++++------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 3e0a3140be7..da20fa28802 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -79,19 +79,30 @@ color: $white-light; } +@mixin labels-mobile { + @media (max-width: $screen-xs-min) { + display: block; + width: 100%; + margin-left: 0; + padding: 10px 0; + } +} + + .manage-labels-list { - .prepend-left-10 { + .prepend-left-10, .prepend-description-left { display: inline-block; width: 40%; vertical-align: middle; - @media (max-width: $screen-xs-min) { - display: block; - width: 100%; - margin-left: 0; - padding: 10px 0; - } + @include labels-mobile; + } + + .prepend-description-left { + width: 57%; + + @include labels-mobile; } .pull-info-right { diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml index 868b2357003..b15e8ea73fe 100644 --- a/app/views/shared/milestones/_labels_tab.html.haml +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -4,15 +4,16 @@ %li %span.label-row - = link_to milestones_label_path(options) do - - render_colored_label(label, tooltip: false) - %span.prepend-left-10 + %span.label-name + = link_to milestones_label_path(options) do + - render_colored_label(label, tooltip: false) + %span.prepend-description-left = markdown(label.description, pipeline: :single_line) - .pull-right - %strong.issues-count + .pull-info-right + %span.append-right-20 = link_to milestones_label_path(options.merge(state: 'opened')) do - pluralize milestone_issues_by_label_count(@milestone, label, state: :opened), 'open issue' - %strong.issues-count + %span.append-right-20 = link_to milestones_label_path(options.merge(state: 'closed')) do - pluralize milestone_issues_by_label_count(@milestone, label, state: :closed), 'closed issue' -- cgit v1.2.1 From 450a39ededbf93d0bfcec1d4774c3562b87fc190 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 11 Apr 2016 17:04:01 +0100 Subject: Fixed alignment on issuable new form Fixes #13802 --- app/assets/stylesheets/pages/issuable.scss | 6 ++++++ app/views/shared/issuable/_form.html.haml | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 88c1b614c74..999b9a2e79a 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -316,3 +316,9 @@ color: #8c8c8c; } } + +.issuable-form-padding-top { + @media (min-width: $screen-sm-min) { + padding-top: 7px; + } +} diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 757a3812deb..1c89a2ee7f3 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -70,13 +70,13 @@ - if can? current_user, :admin_milestone, issuable.project = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank .form-group + - has_labels = issuable.project.labels.any? = f.label :label_ids, "Labels", class: 'control-label' - .col-sm-10 - - if issuable.project.labels.any? + .col-sm-10{ class: ('issuable-form-padding-top' if !has_labels) } + - if has_labels = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, { selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" } - else - .prepend-top-10 %span.light No labels yet.   - if can? current_user, :admin_label, issuable.project -- cgit v1.2.1 From 72e2c1db19fe9a3f45e5df89c03e8077e064ec8b Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Mon, 11 Apr 2016 17:20:38 -0500 Subject: Update delete button --- app/views/shared/issuable/_form.html.haml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 757a3812deb..4b4078cb8a0 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -128,8 +128,6 @@ - else .pull-right - if current_user.can?(:"destroy_#{issuable.to_ability_name}", @project) - = link_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" }, - method: :delete, class: 'btn btn-grouped' do - = icon('trash-o') - Delete + = link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" }, + method: :delete, class: 'btn btn-danger btn-grouped' = link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel' -- cgit v1.2.1 From 158bba238f06f43f2988552f02b32feb2bc245e6 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 11 Apr 2016 17:42:51 -0500 Subject: Set tooltips for new added labels --- app/assets/javascripts/labels_select.js.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee index d1fe116397a..cf0d4f9aae3 100644 --- a/app/assets/javascripts/labels_select.js.coffee +++ b/app/assets/javascripts/labels_select.js.coffee @@ -34,7 +34,7 @@ class @LabelsSelect labelHTMLTemplate = _.template( '<% _.each(labels, function(label){ %> issues?label_name=<%= label.title %>"> - + <%= label.title %> @@ -165,6 +165,8 @@ class @LabelsSelect .html(template) $sidebarCollapsedValue.text(labelCount) + $('.has-tooltip', $value).tooltip(container: 'body') + $value .find('a') .each((i) -> -- cgit v1.2.1 From d88d6e7619c3f976361df638e765472b499f1986 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 11 Apr 2016 18:04:42 -0500 Subject: Hide top search form on the search page --- app/views/layouts/header/_default.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 0f3b8119379..17502148dce 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -8,7 +8,7 @@ .navbar-collapse.collapse %ul.nav.navbar-nav %li.hidden-sm.hidden-xs - = render 'layouts/search' + = render 'layouts/search' unless current_controller?(:search) %li.visible-sm.visible-xs = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('search') -- cgit v1.2.1 From 02cfff4623cc447c20f1990d3e5d9b452e5a7190 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Apr 2016 13:34:03 +0100 Subject: Removed references to subscribe-button CSS class These were being blocked by adblocks Closes #15043 --- app/assets/javascripts/subscription.js.coffee | 2 +- app/assets/stylesheets/pages/issuable.scss | 6 ------ app/assets/stylesheets/pages/labels.scss | 2 +- app/views/projects/labels/_label.html.haml | 2 +- app/views/shared/issuable/_sidebar.html.haml | 4 ++-- features/steps/project/issues/issues.rb | 4 ++-- features/steps/project/labels.rb | 2 +- features/steps/project/merge_requests.rb | 4 ++-- 8 files changed, 10 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/subscription.js.coffee b/app/assets/javascripts/subscription.js.coffee index e4b7a3172ec..3894806d61d 100644 --- a/app/assets/javascripts/subscription.js.coffee +++ b/app/assets/javascripts/subscription.js.coffee @@ -2,7 +2,7 @@ class @Subscription constructor: (container) -> $container = $(container) @url = $container.attr('data-url') - @subscribe_button = $container.find('.subscribe-button') + @subscribe_button = $container.find('.issuable-subscribe-button') @subscription_status = $container.find('.subscription-status') @subscribe_button.unbind('click').click(@toggleSubscription) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 88c1b614c74..f6bdcacda99 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -173,12 +173,6 @@ } } - .subscribe-button { - span { - margin-top: 0; - } - } - &.right-sidebar-collapsed { /* Extra small devices (phones, less than 768px) */ display: none; diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 3e0a3140be7..4f67981975a 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -106,7 +106,7 @@ padding: 6px; color: $gl-text-color; - &.subscribe-button { + &.label-subscribe-button { padding-left: 0; } } diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index 097a65969a6..979726b8def 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -14,7 +14,7 @@ .label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}} .subscription-status{data: {status: label_subscription_status(label)}} - %a.subscribe-button.btn.action-buttons{data: {toggle: "tooltip"}} + %a.label-subscribe-button.btn.action-buttons{data: {toggle: "tooltip"}} %span= label_subscription_toggle_button_text(label) - if can? current_user, :admin_label, @project diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 94affa4b59a..89ce356fedc 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -128,7 +128,7 @@ .title.hide-collapsed Notifications - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' - %button.btn.btn-block.btn-gray.subscribe-button.hide-collapsed{:type => 'button'} + %button.btn.btn-block.btn-gray.issuable-subscribe-button.hide-collapsed{:type => 'button'} %span= subscribed ? 'Unsubscribe' : 'Subscribe' .subscription-status.hide-collapsed{data: {status: subscribtion_status}} .unsubscribed{class: ( 'hidden' if subscribed )} @@ -152,4 +152,4 @@ new LabelsSelect(); new IssuableContext('#{current_user.to_json(only: [:username, :id, :name])}'); new Subscription('.subscription') - new Sidebar(); \ No newline at end of file + new Sidebar(); diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index aff5ca676be..fc12843ea5c 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -20,11 +20,11 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps end step 'I should see that I am subscribed' do - expect(find('.subscribe-button span')).to have_content 'Unsubscribe' + expect(find('.issuable-subscribe-button span')).to have_content 'Unsubscribe' end step 'I should see that I am unsubscribed' do - expect(find('.subscribe-button span')).to have_content 'Subscribe' + expect(find('.issuable-subscribe-button span')).to have_content 'Subscribe' end step 'I click link "Closed"' do diff --git a/features/steps/project/labels.rb b/features/steps/project/labels.rb index 17944527e3a..5bb02189021 100644 --- a/features/steps/project/labels.rb +++ b/features/steps/project/labels.rb @@ -29,6 +29,6 @@ class Spinach::Features::Labels < Spinach::FeatureSteps private def subscribe_button - first('.subscribe-button span') + first('.label-subscribe-button span') end end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index f0af0d097fa..4f883fe7c27 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -77,11 +77,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I should see that I am subscribed' do - expect(find('.subscribe-button span')).to have_content 'Unsubscribe' + expect(find('.issuable-subscribe-button span')).to have_content 'Unsubscribe' end step 'I should see that I am unsubscribed' do - expect(find('.subscribe-button span')).to have_content 'Subscribe' + expect(find('.issuable-subscribe-button span')).to have_content 'Subscribe' end step 'I click button "Unsubscribe"' do -- cgit v1.2.1 From 318314154e09d051cccd8b8391f2065133906bd7 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 15:56:44 +0200 Subject: Increase fsck lock timeout to 24 hours --- app/workers/repository_check_worker.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb index 2d75c8bafde..afdc6a4c63a 100644 --- a/app/workers/repository_check_worker.rb +++ b/app/workers/repository_check_worker.rb @@ -37,9 +37,11 @@ class RepositoryCheckWorker end def try_obtain_lease(id) + # Use a 24-hour timeout because on servers/projects where 'git fsck' is + # super slow we definitely do not want to run it twice in parallel. lease = Gitlab::ExclusiveLease.new( "project_repository_check:#{id}", - timeout: RUN_TIME + timeout: 24.hours ) lease.try_obtain end -- cgit v1.2.1 From b37d3b9423991763ad03fca791a1daf473dafed1 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 16:21:23 +0200 Subject: Add repository_checks_enabled setting --- app/models/application_setting.rb | 3 ++- db/migrate/20160412140240_add_repository_checks_enabled_setting.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20160412140240_add_repository_checks_enabled_setting.rb diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 052cd874733..36f88154232 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -153,7 +153,8 @@ class ApplicationSetting < ActiveRecord::Base require_two_factor_authentication: false, two_factor_grace_period: 48, recaptcha_enabled: false, - akismet_enabled: false + akismet_enabled: false, + repository_checks_enabled: true, ) end diff --git a/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb b/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb new file mode 100644 index 00000000000..ebfa4bcbc7b --- /dev/null +++ b/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb @@ -0,0 +1,5 @@ +class AddRepositoryChecksEnabledSetting < ActiveRecord::Migration + def change + add_column :application_settings, :repository_checks_enabled, :boolean, default: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 53509956888..a9c595fe36d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160331133914) do +ActiveRecord::Schema.define(version: 20160412140240) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -78,6 +78,7 @@ ActiveRecord::Schema.define(version: 20160331133914) do t.string "akismet_api_key" t.boolean "email_author_in_body", default: false t.integer "default_group_visibility" + t.boolean "repository_checks_enabled", default: true end create_table "audit_events", force: :cascade do |t| -- cgit v1.2.1 From beaee0a71fbb1b08676107b3e619e833dc8902c0 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Apr 2016 15:54:26 +0100 Subject: Fixed issue with failing tests --- app/assets/javascripts/subscription.js.coffee | 2 +- app/views/projects/labels/_label.html.haml | 2 +- app/views/shared/issuable/_sidebar.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/subscription.js.coffee b/app/assets/javascripts/subscription.js.coffee index 3894806d61d..1a430f3aa47 100644 --- a/app/assets/javascripts/subscription.js.coffee +++ b/app/assets/javascripts/subscription.js.coffee @@ -2,7 +2,7 @@ class @Subscription constructor: (container) -> $container = $(container) @url = $container.attr('data-url') - @subscribe_button = $container.find('.issuable-subscribe-button') + @subscribe_button = $container.find('.js-subscribe-button') @subscription_status = $container.find('.subscription-status') @subscribe_button.unbind('click').click(@toggleSubscription) diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index 979726b8def..c7b4bb1f6e6 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -14,7 +14,7 @@ .label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}} .subscription-status{data: {status: label_subscription_status(label)}} - %a.label-subscribe-button.btn.action-buttons{data: {toggle: "tooltip"}} + %button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: 'button', data: {toggle: "tooltip"} } %span= label_subscription_toggle_button_text(label) - if can? current_user, :admin_label, @project diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 89ce356fedc..fe6e4128003 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -128,7 +128,7 @@ .title.hide-collapsed Notifications - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' - %button.btn.btn-block.btn-gray.issuable-subscribe-button.hide-collapsed{:type => 'button'} + %button.btn.btn-block.btn-gray.js-subscribe-button.issuable-subscribe-button.hide-collapsed{:type => 'button'} %span= subscribed ? 'Unsubscribe' : 'Subscribe' .subscription-status.hide-collapsed{data: {status: subscribtion_status}} .unsubscribed{class: ( 'hidden' if subscribed )} -- cgit v1.2.1 From 97f4ffff1e7b5da94e18edc20c009ffb46784187 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 17:07:54 +0200 Subject: Add a 'circuit breaker' for repo checks --- app/views/admin/application_settings/_form.html.haml | 13 +++++++++++++ app/workers/repository_check_worker.rb | 8 ++++++++ lib/gitlab/current_settings.rb | 3 ++- spec/workers/repository_check_worker_spec.rb | 8 ++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index de86dacbb12..afd88465a78 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -275,5 +275,18 @@ .col-sm-10 = f.text_field :sentry_dsn, class: 'form-control' + %fieldset + %legend Repository Checks + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :repository_checks_enabled do + = f.check_box :repository_checks_enabled + Enable Repository Checks + .help-block + GitLab will periodically run + %a{ href: 'https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html', target: 'blank' } 'git fsck' + in all project and wiki repositories to look for silent disk corruption issues. + .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb index afdc6a4c63a..017f49de08c 100644 --- a/app/workers/repository_check_worker.rb +++ b/app/workers/repository_check_worker.rb @@ -15,6 +15,7 @@ class RepositoryCheckWorker # check, only one (or two) will be checked at a time. project_ids.each do |project_id| break if Time.now - start >= RUN_TIME + break unless current_settings.repository_checks_enabled next if !try_obtain_lease(project_id) @@ -45,4 +46,11 @@ class RepositoryCheckWorker ) lease.try_obtain end + + def current_settings + # No caching of the settings! If we cache them and an admin disables + # this feature, an active RepositoryCheckWorker would keep going for up + # to 1 hour after the feature was disabled. + ApplicationSetting.current || Gitlab::CurrentSettings.fake_application_settings + end end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 1acc22fe5bf..f44d1b3a44e 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -34,7 +34,8 @@ module Gitlab max_artifacts_size: Settings.artifacts['max_size'], require_two_factor_authentication: false, two_factor_grace_period: 48, - akismet_enabled: false + akismet_enabled: false, + repository_checks_enabled: true, ) end diff --git a/spec/workers/repository_check_worker_spec.rb b/spec/workers/repository_check_worker_spec.rb index d1849321f56..13493ad2c6a 100644 --- a/spec/workers/repository_check_worker_spec.rb +++ b/spec/workers/repository_check_worker_spec.rb @@ -28,4 +28,12 @@ describe RepositoryCheckWorker do expect(subject.perform).to eq([projects[1].id]) end + + it 'does nothing when repository checks are disabled' do + create(:empty_project) + current_settings = double('settings', repository_checks_enabled: false) + expect(subject).to receive(:current_settings) { current_settings } + + expect(subject.perform).to eq(nil) + end end -- cgit v1.2.1 From ea787165b3a9604aa86304e29778066bb014824e Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 17:32:58 +0200 Subject: Move 'clear checks' button to applicatoin settings --- .../admin/application_settings_controller.rb | 14 +++++++++ app/controllers/admin/projects_controller.rb | 12 -------- .../admin/application_settings/_form.html.haml | 6 ++++ app/views/admin/projects/index.html.haml | 5 ---- config/routes.rb | 5 +--- doc/administration/repository_checks.md | 33 ++++++++-------------- 6 files changed, 32 insertions(+), 43 deletions(-) diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index f010436bd36..993a70e63bc 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -19,6 +19,19 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController redirect_to admin_runners_path end + def clear_repository_check_states + Project.update_all( + last_repository_check_failed: false, + last_repository_check_at: nil + ) + + redirect_to( + admin_application_settings_path, + notice: 'All repository check states were cleared' + ) + end + + private def set_application_setting @@ -82,6 +95,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :akismet_enabled, :akismet_api_key, :email_author_in_body, + :repository_checks_enabled, restricted_visibility_levels: [], import_sources: [] ) diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 01257a68616..d7cd9520cc6 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -40,18 +40,6 @@ class Admin::ProjectsController < Admin::ApplicationController ) end - def clear_repository_check_states - Project.update_all( - last_repository_check_failed: false, - last_repository_check_at: nil - ) - - redirect_to( - admin_namespaces_projects_path, - notice: 'All project states were cleared' - ) - end - protected def project diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index afd88465a78..c7c82da72c7 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -287,6 +287,12 @@ GitLab will periodically run %a{ href: 'https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html', target: 'blank' } 'git fsck' in all project and wiki repositories to look for silent disk corruption issues. + .form-group + .col-sm-offset-2.col-sm-10 + = link_to 'Clear all repository checks', clear_repository_check_states_admin_application_settings_path, data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" + .help-block + If you got a lot of false alarms from repository checks (maybe your fileserver was temporarily unavailable) you can choose to clear all repository check information from the database. + .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index c2bf0659841..aa07afa0d62 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -49,11 +49,6 @@ = button_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel" - .panel.panel-default.repository-check-states - .panel-heading - Repository check states - .panel-body - = link_to 'Clear all', clear_repository_check_states_admin_namespace_projects_path(0), data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" %section.col-md-9 .panel.panel-default .panel-heading diff --git a/config/routes.rb b/config/routes.rb index c0ed99b1964..c163602126d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -267,10 +267,6 @@ Rails.application.routes.draw do post :repository_check end - collection do - put :clear_repository_check_states - end - resources :runner_projects end end @@ -286,6 +282,7 @@ Rails.application.routes.draw do resource :application_settings, only: [:show, :update] do resources :services put :reset_runners_token + put :clear_repository_check_states end resources :labels diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index 77f28209f2f..e22b04928cc 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -4,12 +4,13 @@ _**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ --- -Git has a built-in mechanism [git fsck][git-fsck] to verify the -integrity of all data commited to a repository. GitLab administrators can -trigger such a check for a project via the admin panel. The checks run -asynchronously so it may take a few minutes before the check result is -visible on the project admin page. If the checks failed you can see their -output on the admin log page under 'repocheck.log'. +Git has a built-in mechanism \[git fsck\]\[git-fsck\] to verify the +integrity of all data commited to a repository. GitLab administrators +can trigger such a check for a project via the project page under the +admin panel. The checks run asynchronously so it may take a few minutes +before the check result is visible on the project admin page. If the +checks failed you can see their output on the admin log page under +'repocheck.log'. ## Periodical checks @@ -22,20 +23,8 @@ than once a day. ## Disabling periodic checks -You can disable the periodic checks by giving them an empty cron -schedule in gitlab.yml. - -``` -# For omnibus installations, in /etc/gitlab/gitlab.rb: -gitlab_rails['cron_jobs_repository_check_worker_cron'] = '' -``` - -``` -# For installations from source, in config/gitlab.yml: - cron_jobs: - repository_check_worker: - cron: "" -``` +You can disable the periodic checks on the 'Settings' page of the admin +panel. ## What to do if a check failed @@ -47,8 +36,8 @@ resolved the issue use the admin panel to trigger a new repository check on the project. This will clear the 'check failed' state. If for some reason the periodical repository check caused a lot of false -alarms you can choose to clear ALL repository check states from the admin -project index page. +alarms you can choose to clear ALL repository check states from the +'Settings' page of the admin panel. --- [ce-3232]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3232 "Auto git fsck" -- cgit v1.2.1 From 061370790e415361c920e1404063955f4932e5ef Mon Sep 17 00:00:00 2001 From: Charles May Date: Tue, 5 Jan 2016 21:26:31 +0000 Subject: Fix a bug with trailing slash in teamcity_url See https://gitlab.com/gitlab-org/gitlab-ce/issues/3515 --- CHANGELOG | 3 +++ app/models/project_services/teamcity_service.rb | 31 ++++++++++++++----------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3561c541df0..c6ca0ea3de1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -123,6 +123,9 @@ v 8.6.0 - Add information about `image` and `services` field at `job` level in the `.gitlab-ci.yml` documentation (Pat Turner) - HTTP error pages work independently from location and config (Artem Sidorenko) - Update `omniauth-saml` to 1.5.0 to allow for custom response attributes to be set + - Fix avatar stretching by providing a cropping feature (Johann Pardanaud) + - Fix a bug whith trailing slash in teamcity_url (Charles May) + - Don't load all of GitLab in mail_room - Memoize @group in Admin::GroupsController (Yatish Mehta) - Indicate how much an MR diverged from the target branch (Pierre de La Morinerie) - Added omniauth-auth0 Gem (Daniel Carraro) diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index b8e9416131a..246c5eb4a82 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -85,13 +85,15 @@ class TeamcityService < CiService end def build_info(sha) - url = URI.parse("#{teamcity_url}/httpAuth/app/rest/builds/"\ - "branch:unspecified:any,number:#{sha}") + url = URI.join( + teamcity_url, + "/httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}" + ).to_s auth = { username: username, password: password, } - @response = HTTParty.get("#{url}", verify: false, basic_auth: auth) + @response = HTTParty.get(url, verify: false, basic_auth: auth) end def build_page(sha, ref) @@ -100,12 +102,14 @@ class TeamcityService < CiService if @response.code != 200 # If actual build link can't be determined, # send user to build summary page. - "#{teamcity_url}/viewLog.html?buildTypeId=#{build_type}" + URI.join(teamcity_url, "/viewLog.html?buildTypeId=#{build_type}").to_s else # If actual build link is available, go to build result page. built_id = @response['build']['id'] - "#{teamcity_url}/viewLog.html?buildId=#{built_id}"\ - "&buildTypeId=#{build_type}" + URI.join( + teamcity_url, + "#{teamcity_url}/viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}" + ).to_s end end @@ -140,12 +144,13 @@ class TeamcityService < CiService branch = Gitlab::Git.ref_name(data[:ref]) - self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue", - body: ""\ - ""\ - '', - headers: { 'Content-type' => 'application/xml' }, - basic_auth: auth - ) + self.class.post( + URI.join(teamcity_url, "/httpAuth/app/rest/buildQueue").to_s, + body: ""\ + ""\ + '', + headers: { 'Content-type' => 'application/xml' }, + basic_auth: auth + ) end end -- cgit v1.2.1 From 3170e5d226ee107409b4345b827519da64ba967e Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 18:09:45 +0200 Subject: Basta --- app/controllers/admin/application_settings_controller.rb | 2 +- app/controllers/admin/projects_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 993a70e63bc..a54864480a2 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -27,7 +27,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController redirect_to( admin_application_settings_path, - notice: 'All repository check states were cleared' + notice: 'All repository check states were cleared.' ) end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 0719c90b19b..6854e57b650 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -36,7 +36,7 @@ class Admin::ProjectsController < Admin::ApplicationController redirect_to( admin_namespace_project_path(@project.namespace, @project), - notice: 'Repository check was triggered' + notice: 'Repository check was triggered.' ) end -- cgit v1.2.1 From 525ab25ac81a6b81cca56d3cba403ab2a5f372eb Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 18:15:15 +0200 Subject: Changes suggested by Robert --- app/mailers/repository_check_mailer.rb | 2 -- app/views/admin/application_settings/_form.html.haml | 2 +- app/workers/repository_check_worker.rb | 2 +- spec/workers/repository_check_worker_spec.rb | 6 +++--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb index 994054c8769..2bff5b63cc4 100644 --- a/app/mailers/repository_check_mailer.rb +++ b/app/mailers/repository_check_mailer.rb @@ -1,6 +1,4 @@ class RepositoryCheckMailer < BaseMailer - include ActionView::Helpers::TextHelper - def notify(failed_count) if failed_count == 1 @message = "One project failed its last repository check" diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 533a2f42973..555aea554f0 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -287,7 +287,7 @@ .col-sm-offset-2.col-sm-10 = link_to 'Clear all repository checks', clear_repository_check_states_admin_application_settings_path, data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" .help-block - If you got a lot of false alarms from repository checks (maybe your fileserver was temporarily unavailable) you can choose to clear all repository check information from the database. + If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database. .form-actions diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb index 017f49de08c..bdacdd4c6b0 100644 --- a/app/workers/repository_check_worker.rb +++ b/app/workers/repository_check_worker.rb @@ -17,7 +17,7 @@ class RepositoryCheckWorker break if Time.now - start >= RUN_TIME break unless current_settings.repository_checks_enabled - next if !try_obtain_lease(project_id) + next unless try_obtain_lease(project_id) SingleRepositoryCheckWorker.new.perform(project_id) end diff --git a/spec/workers/repository_check_worker_spec.rb b/spec/workers/repository_check_worker_spec.rb index 13493ad2c6a..7a757658e97 100644 --- a/spec/workers/repository_check_worker_spec.rb +++ b/spec/workers/repository_check_worker_spec.rb @@ -4,7 +4,7 @@ describe RepositoryCheckWorker do subject { RepositoryCheckWorker.new } it 'prefers projects that have never been checked' do - projects = 3.times.map { create(:project) } + projects = create_list(:project, 3) projects[0].update_column(:last_repository_check_at, 1.month.ago) projects[2].update_column(:last_repository_check_at, 3.weeks.ago) @@ -12,7 +12,7 @@ describe RepositoryCheckWorker do end it 'sorts projects by last_repository_check_at' do - projects = 3.times.map { create(:project) } + projects = create_list(:project, 3) projects[0].update_column(:last_repository_check_at, 2.weeks.ago) projects[1].update_column(:last_repository_check_at, 1.month.ago) projects[2].update_column(:last_repository_check_at, 3.weeks.ago) @@ -21,7 +21,7 @@ describe RepositoryCheckWorker do end it 'excludes projects that were checked recently' do - projects = 3.times.map { create(:project) } + projects = create_list(:project, 3) projects[0].update_column(:last_repository_check_at, 2.days.ago) projects[1].update_column(:last_repository_check_at, 1.month.ago) projects[2].update_column(:last_repository_check_at, 3.days.ago) -- cgit v1.2.1 From 4e5ae5a281be109ca22929bb21732f0ee1f6630d Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Tue, 12 Apr 2016 15:40:53 -0500 Subject: Fix Grafana docs and link from Influx page --- doc/monitoring/performance/gitlab_configuration.md | 1 + doc/monitoring/performance/grafana_configuration.md | 6 +++--- doc/monitoring/performance/influxdb_configuration.md | 1 + doc/monitoring/performance/influxdb_schema.md | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/monitoring/performance/gitlab_configuration.md b/doc/monitoring/performance/gitlab_configuration.md index b856e7935a3..90e99302210 100644 --- a/doc/monitoring/performance/gitlab_configuration.md +++ b/doc/monitoring/performance/gitlab_configuration.md @@ -37,3 +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 diff --git a/doc/monitoring/performance/grafana_configuration.md b/doc/monitoring/performance/grafana_configuration.md index 416c9870aa0..10ef1009818 100644 --- a/doc/monitoring/performance/grafana_configuration.md +++ b/doc/monitoring/performance/grafana_configuration.md @@ -91,18 +91,18 @@ JSON file. Open the dashboard dropdown menu and click 'Import' -![Grafana dashboard dropdown](/img/grafana_dashboard_dropdown.png) +![Grafana dashboard dropdown](img/grafana_dashboard_dropdown.png) Click 'Choose file' and browse to the location where you downloaded or cloned the dashboard repository. Pick one of the JSON files to import. -![Grafana dashboard import](/img/grafana_dashboard_import.png) +![Grafana dashboard import](img/grafana_dashboard_import.png) Once the dashboard is imported, be sure to click save icon in the top bar. If you do not save the dashboard after importing it will be removed when you navigate away. -![Grafana save icon](/img/grafana_save_icon.png) +![Grafana save icon](img/grafana_save_icon.png) Repeat this process for each dashboard you wish to import. diff --git a/doc/monitoring/performance/influxdb_configuration.md b/doc/monitoring/performance/influxdb_configuration.md index 3a2b598b78f..63aa03985ef 100644 --- a/doc/monitoring/performance/influxdb_configuration.md +++ b/doc/monitoring/performance/influxdb_configuration.md @@ -181,6 +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 [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 a5a8aebd2d1..d31b3788f36 100644 --- a/doc/monitoring/performance/influxdb_schema.md +++ b/doc/monitoring/performance/influxdb_schema.md @@ -85,3 +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 -- cgit v1.2.1 From d8296f873871120b7f4134bdcf8854a09b9e8be8 Mon Sep 17 00:00:00 2001 From: connorshea Date: Tue, 12 Apr 2016 16:11:58 -0600 Subject: Remove Bootstrap Carousel The Bootstrap carousel module is used for image carousels, and we don't use it anywhere on the site. Also separated the Bootstrap JavaScript into separate components and removed the carousel component. Fixes #14670. --- app/assets/javascripts/application.js.coffee | 12 +++++++++++- app/assets/stylesheets/framework/tw_bootstrap.scss | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index b05138ac1ac..6f435e4c542 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -22,7 +22,17 @@ #= require cal-heatmap #= require turbolinks #= require autosave -#= require bootstrap +#= require bootstrap/affix +#= require bootstrap/alert +#= require bootstrap/button +#= require bootstrap/collapse +#= require bootstrap/dropdown +#= require bootstrap/modal +#= require bootstrap/scrollspy +#= require bootstrap/tab +#= require bootstrap/transition +#= require bootstrap/tooltip +#= require bootstrap/popover #= require select2 #= require raphael #= require g.raphael diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index dd42db1840f..96bab7880c2 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -43,7 +43,6 @@ @import "bootstrap/modals"; @import "bootstrap/tooltip"; @import "bootstrap/popovers"; -@import "bootstrap/carousel"; // Utility classes .clearfix { -- cgit v1.2.1 From 3ea955a637127e6e11bc9fe270f87f63226b9d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 5 Apr 2016 15:11:31 +0200 Subject: Improve TeamcityService and its specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- CHANGELOG | 4 +- app/models/project_services/teamcity_service.rb | 6 +- .../project_services/teamcity_service_spec.rb | 251 ++++++++++++++++----- 3 files changed, 203 insertions(+), 58 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c6ca0ea3de1..df40a1e1b81 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.7.0 (unreleased) - Expose label description in API (Mariusz Jachimowicz) - Allow back dating on issues when created through the API - Fix Error 500 after renaming a project path (Stan Hu) + - Fix a bug whith trailing slash in teamcity_url (Charles May) - Fix avatar stretching by providing a cropping feature - API: Expose `subscribed` for issues and merge requests (Robert Schilling) - Allow SAML to handle external users based on user's information !3530 @@ -123,9 +124,6 @@ v 8.6.0 - Add information about `image` and `services` field at `job` level in the `.gitlab-ci.yml` documentation (Pat Turner) - HTTP error pages work independently from location and config (Artem Sidorenko) - Update `omniauth-saml` to 1.5.0 to allow for custom response attributes to be set - - Fix avatar stretching by providing a cropping feature (Johann Pardanaud) - - Fix a bug whith trailing slash in teamcity_url (Charles May) - - Don't load all of GitLab in mail_room - Memoize @group in Admin::GroupsController (Yatish Mehta) - Indicate how much an MR diverged from the target branch (Pierre de La Morinerie) - Added omniauth-auth0 Gem (Daniel Carraro) diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index 246c5eb4a82..8dceee5e2c5 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -91,7 +91,7 @@ class TeamcityService < CiService ).to_s auth = { username: username, - password: password, + password: password } @response = HTTParty.get(url, verify: false, basic_auth: auth) end @@ -108,7 +108,7 @@ class TeamcityService < CiService built_id = @response['build']['id'] URI.join( teamcity_url, - "#{teamcity_url}/viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}" + "/viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}" ).to_s end end @@ -145,7 +145,7 @@ class TeamcityService < CiService branch = Gitlab::Git.ref_name(data[:ref]) self.class.post( - URI.join(teamcity_url, "/httpAuth/app/rest/buildQueue").to_s, + URI.join(teamcity_url, '/httpAuth/app/rest/buildQueue').to_s, body: ""\ ""\ '', diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index f26b47a856c..bc7423cee69 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -21,73 +21,220 @@ require 'spec_helper' describe TeamcityService, models: true do - describe "Associations" do + describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } end - describe "Execute" do - let(:user) { create(:user) } - let(:project) { create(:project) } - - context "when a password was previously set" do - before do - @teamcity_service = TeamcityService.create( - project: create(:project), - properties: { - teamcity_url: 'http://gitlab.com', - username: 'mic', - password: "password" - } - ) + 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 + + expect(teamcity_service).not_to validate_presence_of(:teamcity_url) end - - it "reset password if url changed" do - @teamcity_service.teamcity_url = 'http://gitlab1.com' - @teamcity_service.save - expect(@teamcity_service.password).to be_nil + + it 'validates the presence of teamcity_url if service is active' do + teamcity_service = service + teamcity_service.active = true + + expect(teamcity_service).to validate_presence_of(:teamcity_url) + 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 + + 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 - - it "does not reset password if username changed" do - @teamcity_service.username = "some_name" - @teamcity_service.save - expect(@teamcity_service.password).to eq("password") + 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 reset password if new url is set together with password, even if it's the same password" do - @teamcity_service.teamcity_url = 'http://gitlab_edited.com' - @teamcity_service.password = 'password' - @teamcity_service.save - expect(@teamcity_service.password).to eq("password") - expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com") + 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 "should reset password if url changed, even if setter called multiple times" do - @teamcity_service.teamcity_url = 'http://gitlab1.com' - @teamcity_service.teamcity_url = 'http://gitlab1.com' - @teamcity_service.save - expect(@teamcity_service.password).to be_nil + 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 - - context "when no password was previously set" do - before do - @teamcity_service = TeamcityService.create( - project: create(:project), - properties: { - teamcity_url: 'http://gitlab.com', - username: 'mic' - } - ) + + 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 "saves password if new url is set together with password" do - @teamcity_service.teamcity_url = 'http://gitlab_edited.com' - @teamcity_service.password = 'password' - @teamcity_service.save - expect(@teamcity_service.password).to eq("password") - expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com") + 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 end end + + describe 'Callbacks' do + describe 'before_update :reset_password' do + context 'when a password was previously set' do + it 'resets password if url changed' do + teamcity_service = service + + teamcity_service.teamcity_url = 'http://gitlab1.com' + teamcity_service.save + + expect(teamcity_service.password).to be_nil + end + + it 'does not reset password if username changed' do + teamcity_service = service + + teamcity_service.username = 'some_name' + teamcity_service.save + + expect(teamcity_service.password).to eq('password') + end + + it "does not reset password if new url is set together with password, even if it's the same password" do + teamcity_service = service + + teamcity_service.teamcity_url = 'http://gitlab_edited.com' + teamcity_service.password = 'password' + teamcity_service.save + + expect(teamcity_service.password).to eq('password') + expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com') + end + end + + it 'saves password if new url is set together with password when no password was previously set' do + teamcity_service = service + teamcity_service.password = nil + + teamcity_service.teamcity_url = 'http://gitlab_edited.com' + teamcity_service.password = 'password' + teamcity_service.save + + expect(teamcity_service.password).to eq('password') + expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com') + end + end + end + + describe '#build_page' do + it 'returns a specific URL when status is 500' do + stub_request(status: 500) + + expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildTypeId=foo') + end + + it 'returns a build URL when teamcity_url has no trailing slash' do + stub_request(body: %Q({"build":{"id":"666"}})) + + expect(service(teamcity_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo') + end + + it 'returns a build URL when teamcity_url has a trailing slash' do + stub_request(body: %Q({"build":{"id":"666"}})) + + expect(service(teamcity_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo') + end + end + + describe '#commit_status' do + it 'sets commit status to :error when status is 500' do + stub_request(status: 500) + + expect(service.commit_status('123', 'unused')).to eq(:error) + end + + it 'sets commit status to "pending" when status is 404' do + stub_request(status: 404) + + expect(service.commit_status('123', 'unused')).to eq('pending') + end + + it 'sets commit status to "success" when build status contains SUCCESS' do + stub_request(build_status: 'YAY SUCCESS!') + + expect(service.commit_status('123', 'unused')).to eq('success') + end + + it 'sets commit status to "failed" when build status contains FAILURE' do + stub_request(build_status: 'NO FAILURE!') + + expect(service.commit_status('123', 'unused')).to eq('failed') + end + + it 'sets commit status to "pending" when build status contains Pending' do + stub_request(build_status: 'NO Pending!') + + expect(service.commit_status('123', 'unused')).to eq('pending') + end + + it 'sets commit status to :error when build status is unknown' do + stub_request(build_status: 'FOO BAR!') + + expect(service.commit_status('123', 'unused')).to eq(:error) + end + end + + def service(teamcity_url: 'http://gitlab.com') + described_class.create( + project: build_stubbed(:empty_project), + properties: { + teamcity_url: teamcity_url, + username: 'mic', + password: 'password', + build_type: 'foo' + } + ) + end + + def stub_request(status: 200, body: nil, build_status: 'success') + teamcity_full_url = 'http://mic:password@gitlab.com/httpAuth/app/rest/builds/branch:unspecified:any,number:123' + body ||= %Q({"build":{"status":"#{build_status}","id":"666"}}) + + WebMock.stub_request(:get, teamcity_full_url).to_return( + status: status, + headers: { 'Content-Type' => 'application/json' }, + body: body + ) + end end -- cgit v1.2.1 From a0bede92edf1f1df66e977c22540fced3414f3b7 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 11:12:43 +0200 Subject: More code changes, thanks Robert --- app/workers/repository_check_worker.rb | 19 ++++++++++++------- app/workers/single_repository_check_worker.rb | 25 ++++++++++++------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb index bdacdd4c6b0..d7ead91f94e 100644 --- a/app/workers/repository_check_worker.rb +++ b/app/workers/repository_check_worker.rb @@ -25,9 +25,11 @@ class RepositoryCheckWorker private - # In an ideal world we would use Project.where(...).find_each. - # Unfortunately, calling 'find_each' drops the 'where', so we must build - # an array of IDs instead. + # Project.find_each does not support WHERE clauses and + # Project.find_in_batches does not support ordering. So we just build an + # array of ID's. This is OK because we do it only once an hour, because + # getting ID's from Postgres is not terribly slow, and because no user + # has to sit and wait for this query to finish. def project_ids limit = 10_000 never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). @@ -40,17 +42,20 @@ class RepositoryCheckWorker def try_obtain_lease(id) # Use a 24-hour timeout because on servers/projects where 'git fsck' is # super slow we definitely do not want to run it twice in parallel. - lease = Gitlab::ExclusiveLease.new( + Gitlab::ExclusiveLease.new( "project_repository_check:#{id}", timeout: 24.hours - ) - lease.try_obtain + ).try_obtain end def current_settings # No caching of the settings! If we cache them and an admin disables # this feature, an active RepositoryCheckWorker would keep going for up # to 1 hour after the feature was disabled. - ApplicationSetting.current || Gitlab::CurrentSettings.fake_application_settings + if Rails.env.test? + Gitlab::CurrentSettings.fake_application_settings + else + ApplicationSetting.current + end end end diff --git a/app/workers/single_repository_check_worker.rb b/app/workers/single_repository_check_worker.rb index d9eed9bd708..6257f382d86 100644 --- a/app/workers/single_repository_check_worker.rb +++ b/app/workers/single_repository_check_worker.rb @@ -5,30 +5,29 @@ class SingleRepositoryCheckWorker def perform(project_id) project = Project.find(project_id) - update(project, success: check(project)) + project.update_columns( + last_repository_check_failed: !check(project), + last_repository_check_at: Time.now, + ) end private def check(project) - [project.repository.path_to_repo, project.wiki.wiki.path].all? do |path| - git_fsck(path) + [project.repository, project.wiki.repository].all? do |repository| + git_fsck(repository.path_to_repo) end end def git_fsck(path) cmd = %W(nice git --git-dir=#{path} fsck) output, status = Gitlab::Popen.popen(cmd) - return true if status.zero? - - Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") - false - end - def update(project, success:) - project.update_columns( - last_repository_check_failed: !success, - last_repository_check_at: Time.now, - ) + if status.zero? + true + else + Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") + false + end end end -- cgit v1.2.1 From 74783358268a6ce69375780400e661d9aa1fa741 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 11:15:36 +0200 Subject: Improve English, thanks Robert --- doc/administration/repository_checks.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index e22b04928cc..f4c5a84fd37 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -4,7 +4,7 @@ _**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ --- -Git has a built-in mechanism \[git fsck\]\[git-fsck\] to verify the +Git has a built-in mechanism, \[git fsck\]\[git-fsck\], to verify the integrity of all data commited to a repository. GitLab administrators can trigger such a check for a project via the project page under the admin panel. The checks run asynchronously so it may take a few minutes @@ -12,7 +12,7 @@ before the check result is visible on the project admin page. If the checks failed you can see their output on the admin log page under 'repocheck.log'. -## Periodical checks +## Periodic checks GitLab periodically runs a repository check on all project repositories and wiki repositories in order to detect data corruption problems. A @@ -35,7 +35,7 @@ in repocheck.log (in the admin panel or on disk; see resolved the issue use the admin panel to trigger a new repository check on the project. This will clear the 'check failed' state. -If for some reason the periodical repository check caused a lot of false +If for some reason the periodic repository check caused a lot of false alarms you can choose to clear ALL repository check states from the 'Settings' page of the admin panel. -- cgit v1.2.1 From da87fa8a9fb2e786a4115d2da3898e0ec83c446c Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 11:50:39 +0200 Subject: Make check result staleness more obvious --- app/views/admin/projects/show.html.haml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 5bef8e3ad57..1e44172f066 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -10,7 +10,9 @@ .col-md-12 .panel .panel-heading.alert.alert-danger - Last repository check failed. See + Last repository check + = "(#{time_ago_in_words(@project.last_repository_check_at)} ago)" + failed. See = link_to 'repocheck.log', admin_logs_path for error messages. .row -- cgit v1.2.1 From b60b9094d3581e72fa614771456fa9003bee6426 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 11:50:55 +0200 Subject: Always check the wiki too --- app/workers/single_repository_check_worker.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/workers/single_repository_check_worker.rb b/app/workers/single_repository_check_worker.rb index 6257f382d86..f6c345df8b5 100644 --- a/app/workers/single_repository_check_worker.rb +++ b/app/workers/single_repository_check_worker.rb @@ -14,9 +14,10 @@ class SingleRepositoryCheckWorker private def check(project) - [project.repository, project.wiki.repository].all? do |repository| + # Use 'map do', not 'all? do', to prevent short-circuiting + [project.repository, project.wiki.repository].map do |repository| git_fsck(repository.path_to_repo) - end + end.all? end def git_fsck(path) -- cgit v1.2.1 From 33f786b5d32d02a032af9d258167ddd2bb61d44a Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 13 Apr 2016 13:12:05 +0300 Subject: clean up ExclusiveLease --- lib/gitlab/exclusive_lease.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/gitlab/exclusive_lease.rb b/lib/gitlab/exclusive_lease.rb index c2260a5f7ac..ffe49364379 100644 --- a/lib/gitlab/exclusive_lease.rb +++ b/lib/gitlab/exclusive_lease.rb @@ -52,11 +52,6 @@ module Gitlab private - def redis - # Maybe someday we want to use a connection pool... - @redis ||= Redis.new(url: Gitlab::RedisConfig.url) - end - def redis_key "gitlab:exclusive_lease:#{@key}" end -- cgit v1.2.1 From fa864716a80eba26f75a1d69ca3e5183bee96542 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 12:13:53 +0200 Subject: Use new "clear all" button in tests --- spec/features/admin/admin_projects_spec.rb | 37 +---------------- .../admin/admin_uses_repository_checks_spec.rb | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 36 deletions(-) create mode 100644 spec/features/admin/admin_uses_repository_checks_spec.rb diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 95a230a72c3..101d955d693 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' -require 'rails_helper' -describe "Admin Projects", feature: true do +describe "Admin::Projects", feature: true do before do @project = create(:project) login_as :admin @@ -32,38 +31,4 @@ describe "Admin Projects", feature: true do expect(page).to have_content(@project.name) end end - - feature 'repository checks' do - scenario 'trigger repository check' do - visit_admin_project_page - - page.within('.repository-check') do - click_button 'Trigger repository check' - end - - expect(page).to have_content('Repository check was triggered') - end - - scenario 'see failed repository check' do - @project.update_column(:last_repository_check_failed, true) - visit_admin_project_page - - expect(page).to have_content('Last repository check failed') - end - - scenario 'clear repository checks', js: true do - @project.update_column(:last_repository_check_failed, true) - visit admin_namespaces_projects_path - - page.within('.repository-check-states') do - click_link 'Clear all' # pop-up should be auto confirmed - end - - expect(@project.reload.last_repository_check_failed).to eq(false) - end - end - - def visit_admin_project_page - visit admin_namespace_project_path(@project.namespace, @project) - end end diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb new file mode 100644 index 00000000000..69b82441916 --- /dev/null +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -0,0 +1,48 @@ +require 'rails_helper' + +feature 'Admin uses repository checks', feature: true do + before do + login_as :admin + end + + scenario 'to trigger a single check' do + project = create(:empty_project) + visit_admin_project_page(project) + + page.within('.repository-check') do + click_button 'Trigger repository check' + end + + expect(page).to have_content('Repository check was triggered') + end + + scenario 'to see a single failed repository check' do + visit_admin_project_page(broken_project) + + page.within('.alert') do + expect(page.text).to match(/Last repository check \(.* ago\) failed/) + end + end + + scenario 'to clear all repository checks', js: true do + project = broken_project + visit admin_application_settings_path + + click_link 'Clear all repository checks' # pop-up should be auto confirmed + + expect(project.reload.last_repository_check_failed).to eq(false) + end + + def visit_admin_project_page(project) + visit admin_namespace_project_path(project.namespace, project) + end + + def broken_project + project = create(:empty_project) + project.update_columns( + last_repository_check_failed: true, + last_repository_check_at: Time.now, + ) + project + end +end -- cgit v1.2.1 From 9a30d3b5aef732e782e9496b2e8ae62069ba521a Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 12:20:43 +0200 Subject: Remove \f. WT\F ?? --- doc/administration/repository_checks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index f4c5a84fd37..16f4627a596 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -28,7 +28,7 @@ panel. ## What to do if a check failed -If the repository check fails for some repository you should look up the error +If the repository check fails for some repository you should look up the error in repocheck.log (in the admin panel or on disk; see `/var/log/gitlab/gitlab-rails` for Omnibus installations or `/home/git/gitlab/log` for installations from source). Once you have -- cgit v1.2.1 From bd0be13f5b52b8eaee019d722980b29acbc55b05 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 8 Apr 2016 14:17:42 +0200 Subject: API: Ability to subscribe and unsubscribe from an issue --- CHANGELOG | 1 + doc/api/issues.md | 108 +++++++++++++++++++++++++++++++++++++++ lib/api/helpers.rb | 4 ++ lib/api/issues.rb | 53 +++++++++++++++++++ spec/requests/api/issues_spec.rb | 31 +++++++++++ 5 files changed, 197 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index c633c4bb35f..01fffcc3696 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,7 @@ v 8.7.0 (unreleased) - Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524 - Improved Markdown rendering performance !3389 (Yorick Peterse) - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu) + - API: Ability to subscribe and unsubscribe from an issue (Robert Schilling) - Expose project badges in project settings - Preserve time notes/comments have been updated at when moving issue - Make HTTP(s) label consistent on clone bar (Stan Hu) diff --git a/doc/api/issues.md b/doc/api/issues.md index f09847aef95..4f7d948eba1 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -406,6 +406,114 @@ Example response: } ``` +## Subscribe to an issue + +Subscribes to an issue to receive notifications. If the operation is successful, +status code `201` together with the updated issue is returned. If the user is +already subscribed to the issue, the status code `304` is returned. If the +project or issue is not found, status code `404` is returned. + +``` +POST /projects/:id/issues/:issue_id/subscribe +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a project | +| `issue_id` | integer | yes | The ID of a project's issue | + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/subscribe +``` + +Example response: + +```json +{ + "id": 92, + "iid": 11, + "project_id": 5, + "title": "Sit voluptas tempora quisquam aut doloribus et.", + "description": "Repellat voluptas quibusdam voluptatem exercitationem.", + "state": "opened", + "created_at": "2016-04-05T21:41:45.652Z", + "updated_at": "2016-04-07T12:20:17.596Z", + "labels": [], + "milestone": null, + "assignee": { + "name": "Miss Monserrate Beier", + "username": "axel.block", + "id": 12, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon", + "web_url": "https://gitlab.example.com/u/axel.block" + }, + "author": { + "name": "Kris Steuber", + "username": "solon.cremin", + "id": 10, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/7a190fecbaa68212a4b68aeb6e3acd10?s=80&d=identicon", + "web_url": "https://gitlab.example.com/u/solon.cremin" + } +} +``` + +## Unsubscribe from an issue + +Unsubscribes from an issue to not receive notifications from that issue. If the +operation is successful, status code `201` together with the updated issue is +returned. If the user is not subscribed to the issue, the status code `304` +is returned. If the project or issue is not found, status code `404` is +returned. + +``` +POST /projects/:id/issues/:issue_id/unsubscribe +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a project | +| `issue_id` | integer | yes | The ID of a project's issue | + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/unsubscribe +``` + +Example response: + +```json +{ + "id": 93, + "iid": 12, + "project_id": 5, + "title": "Incidunt et rerum ea expedita iure quibusdam.", + "description": "Et cumque architecto sed aut ipsam.", + "state": "opened", + "created_at": "2016-04-05T21:41:45.217Z", + "updated_at": "2016-04-07T13:02:37.905Z", + "labels": [], + "milestone": null, + "assignee": { + "name": "Edwardo Grady", + "username": "keyon", + "id": 21, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/3e6f06a86cf27fa8b56f3f74f7615987?s=80&d=identicon", + "web_url": "https://gitlab.example.com/u/keyon" + }, + "author": { + "name": "Vivian Hermann", + "username": "orville", + "id": 11, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/5224fd70153710e92fb8bcf79ac29d67?s=80&d=identicon", + "web_url": "http://lgitlab.example.com/u/orville" + }, + "subscribed": false +} +``` + ## Comments on issues Comments are done via the [notes](notes.md) resource. diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 4921ae99e78..aa0597564ed 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -241,6 +241,10 @@ module API render_api_error!('413 Request Entity Too Large', 413) end + def not_modified! + render_api_error!('304 Not modified', 304) + end + def render_validation_error!(model) if model.errors.any? render_api_error!(model.errors.messages || '400 Bad Request', 400) diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 850e99981ff..49911fa2988 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -231,6 +231,59 @@ module API authorize!(:destroy_issue, issue) issue.destroy end + + # Subscribes to a project issue + # + # Parameters: + # id (required) - The ID of a project + # issue_id (required) - The ID of a project issue + # Example Request: + # POST /projects/:id/issues/:issue_id + post ":id/issues/:issue_idsubscribe" do + issue = user_project.issues.find_by(id: params[:issue_id]) + + if !issue.subscribed?(current_user) + present issue, with: Entities::Issue, current_user: current_user + else + not_modified! + end + end + + # Subscribes to a project issue + # + # Parameters: + # id (required) - The ID of a project + # issue_id (required) - The ID of a project issue + # Example Request: + # POST /projects/:id/issues/:issue_id/subscribe + post ":id/issues/:issue_id/subscribe" do + issue = user_project.issues.find_by(id: params[:issue_id]) + + if !issue.subscribed?(current_user) + issue.toggle_subscription(current_user) + present issue, with: Entities::Issue, current_user: current_user + else + not_modified! + end + end + + # Unsubscribes from a project issue + # + # Parameters: + # id (required) - The ID of a project + # issue_id (required) - The ID of a project issue + # Example Request: + # POST /projects/:id/issues/:issue_id/unsubscribe + post ":id/issues/:issue_id/unsubscribe" do + issue = user_project.issues.find_by(id: params[:issue_id]) + + if issue.subscribed?(current_user) + issue.unsubscribe(current_user) + present issue, with: Entities::Issue, current_user: current_user + else + not_modified! + end + end end end end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 3d7a31cbb6a..437caf466b0 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe API::API, api: true do include ApiHelpers let(:user) { create(:user) } + let(:user2) { create(:user) } let(:non_member) { create(:user) } let(:author) { create(:author) } let(:assignee) { create(:assignee) } @@ -569,4 +570,34 @@ describe API::API, api: true do end end end + + describe 'POST :id/issues/:issue_id/subscribe' do + it 'subscribes to an issue' do + post api("/projects/#{project.id}/issues/#{issue.id}/subscribe", user2) + + expect(response.status).to eq(201) + expect(json_response['subscribed']).to eq(true) + end + + it 'returns 304 if already subscribed' do + post api("/projects/#{project.id}/issues/#{issue.id}/subscribe", user) + + expect(response.status).to eq(304) + end + end + + describe 'POST :id/issues/:issue_id/unsubscribe' do + it 'unsubscribes from an issue' do + post api("/projects/#{project.id}/issues/#{issue.id}/unsubscribe", user) + + expect(response.status).to eq(201) + expect(json_response['subscribed']).to eq(false) + end + + it 'returns 304 if not subscribed' do + post api("/projects/#{project.id}/issues/#{issue.id}/unsubscribe", user2) + + expect(response.status).to eq(304) + end + end end -- cgit v1.2.1 From f875189b3962bde6e4b7b8c4ffdd18af83cbc922 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 8 Apr 2016 15:03:34 +0200 Subject: API: Ability to subscribe and unsubscribe from a merge request --- CHANGELOG | 2 +- doc/api/merge_requests.md | 147 +++++++++++++++++++++++++++++++ lib/api/issues.rb | 4 +- lib/api/merge_requests.rb | 36 ++++++++ spec/requests/api/merge_requests_spec.rb | 30 +++++++ 5 files changed, 216 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 01fffcc3696..1184ffce76c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,7 +11,7 @@ v 8.7.0 (unreleased) - Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524 - Improved Markdown rendering performance !3389 (Yorick Peterse) - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu) - - API: Ability to subscribe and unsubscribe from an issue (Robert Schilling) + - API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling) - Expose project badges in project settings - Preserve time notes/comments have been updated at when moving issue - Make HTTP(s) label consistent on clone bar (Stan Hu) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 20db73ea6c0..16cb8926265 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -606,3 +606,150 @@ Example response: }, ] ``` + +## Subscribe to a merge request + +Subscribes to a merge request to receive notification. If the operation is +successful, status code `201` together with the updated merge request is +returned. If the user is already subscribed to the merge request, the status +code `304` is returned. If the project or merge request is not found, status +code `404` is returned. + +``` +POST /projects/:id/merge_requests/:merge_request_id/subscribe +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a project | +| `merge_request_id` | integer | yes | The ID of the merge request | + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscribe +``` + +Example response: + +```json +{ + "id": 17, + "iid": 1, + "project_id": 5, + "title": "Et et sequi est impedit nulla ut rem et voluptatem.", + "description": "Consequatur velit eos rerum optio autem. Quia id officia quaerat dolorum optio. Illo laudantium aut ipsum dolorem.", + "state": "opened", + "created_at": "2016-04-05T21:42:23.233Z", + "updated_at": "2016-04-05T22:11:52.900Z", + "target_branch": "ui-dev-kit", + "source_branch": "version-1-9", + "upvotes": 0, + "downvotes": 0, + "author": { + "name": "Eileen Skiles", + "username": "leila", + "id": 19, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/39ce4a2822cc896933ffbd68c1470e55?s=80&d=identicon", + "web_url": "https://gitlab.example.com/u/leila" + }, + "assignee": { + "name": "Celine Wehner", + "username": "carli", + "id": 16, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/f4cd5605b769dd2ce405a27c6e6f2684?s=80&d=identicon", + "web_url": "https://gitlab.example.com/u/carli" + }, + "source_project_id": 5, + "target_project_id": 5, + "labels": [], + "work_in_progress": false, + "milestone": { + "id": 7, + "iid": 1, + "project_id": 5, + "title": "v2.0", + "description": "Corrupti eveniet et velit occaecati dolorem est rerum aut.", + "state": "closed", + "created_at": "2016-04-05T21:41:40.905Z", + "updated_at": "2016-04-05T21:41:40.905Z", + "due_date": null + }, + "merge_when_build_succeeds": false, + "merge_status": "cannot_be_merged", + "subscribed": true +} +``` +## Unsubscribe from a merge request + +Unsubscribes from a merge request to not receive notifications from that merge +request. If the operation is successful, status code `201` together with the +updated merge request is returned. If the user is not subscribed to the merge +request, the status code `304` is returned. If the project or merge request is +not found, status code `404` is returned. + +``` +POST /projects/:id/merge_requests/:merge_request_id/unsubscribe +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a project | +| `merge_request_id` | integer | yes | The ID of the merge request | + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscribe +``` + +Example response: + +```json +{ + "id": 17, + "iid": 1, + "project_id": 5, + "title": "Et et sequi est impedit nulla ut rem et voluptatem.", + "description": "Consequatur velit eos rerum optio autem. Quia id officia quaerat dolorum optio. Illo laudantium aut ipsum dolorem.", + "state": "opened", + "created_at": "2016-04-05T21:42:23.233Z", + "updated_at": "2016-04-05T22:11:52.900Z", + "target_branch": "ui-dev-kit", + "source_branch": "version-1-9", + "upvotes": 0, + "downvotes": 0, + "author": { + "name": "Eileen Skiles", + "username": "leila", + "id": 19, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/39ce4a2822cc896933ffbd68c1470e55?s=80&d=identicon", + "web_url": "https://gitlab.example.com/u/leila" + }, + "assignee": { + "name": "Celine Wehner", + "username": "carli", + "id": 16, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/f4cd5605b769dd2ce405a27c6e6f2684?s=80&d=identicon", + "web_url": "https://gitlab.example.com/u/carli" + }, + "source_project_id": 5, + "target_project_id": 5, + "labels": [], + "work_in_progress": false, + "milestone": { + "id": 7, + "iid": 1, + "project_id": 5, + "title": "v2.0", + "description": "Corrupti eveniet et velit occaecati dolorem est rerum aut.", + "state": "closed", + "created_at": "2016-04-05T21:41:40.905Z", + "updated_at": "2016-04-05T21:41:40.905Z", + "due_date": null + }, + "merge_when_build_succeeds": false, + "merge_status": "cannot_be_merged", + "subscribed": false +} +``` diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 49911fa2988..049618c00f7 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -235,7 +235,7 @@ module API # Subscribes to a project issue # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID of a project # issue_id (required) - The ID of a project issue # Example Request: # POST /projects/:id/issues/:issue_id @@ -270,7 +270,7 @@ module API # Unsubscribes from a project issue # # Parameters: - # id (required) - The ID of a project + # id (required) - The ID of a project # issue_id (required) - The ID of a project issue # Example Request: # POST /projects/:id/issues/:issue_id/unsubscribe diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 4e7de8867b4..d166484ba54 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -327,6 +327,42 @@ module API issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user)) present paginate(issues), with: Entities::Issue, current_user: current_user end + + # Subscribes to a merge request + # + # Parameters: + # id (required) - The ID of a project + # merge_request_id (required) - The ID of a merge request + # Example Request: + # POST /projects/:id/issues/:merge_request_id/subscribe + post "#{path}/subscribe" do + merge_request = user_project.merge_requests.find(params[:merge_request_id]) + + if !merge_request.subscribed?(current_user) + merge_request.toggle_subscription(current_user) + present merge_request, with: Entities::MergeRequest, current_user: current_user + else + not_modified! + end + end + + # Unsubscribes from a merge request + # + # Parameters: + # id (required) - The ID of a project + # merge_request_id (required) - The ID of a merge request + # Example Request: + # POST /projects/:id/merge_requests/:merge_request_id/unsubscribe + post "#{path}/unsubscribe" do + merge_request = user_project.merge_requests.find(params[:merge_request_id]) + + if merge_request.subscribed?(current_user) + merge_request.unsubscribe(current_user) + present merge_request, with: Entities::MergeRequest, current_user: current_user + else + not_modified! + end + end end end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 25fa30b2f21..b71c72a3829 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -516,6 +516,36 @@ describe API::API, api: true do end end + describe 'POST :id/merge_requests/:merge_request_id/subscribe' do + it 'subscribes to a merge request' do + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", admin) + + expect(response.status).to eq(201) + expect(json_response['subscribed']).to eq(true) + end + + it 'returns 304 if already subscribed' do + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", user) + + expect(response.status).to eq(304) + end + end + + describe 'POST :id/merge_requests/:merge_request_id/unsubscribe' do + it 'unsubscribes from a merge request' do + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", user) + + expect(response.status).to eq(201) + expect(json_response['subscribed']).to eq(false) + end + + it 'returns 304 if not subscribed' do + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", admin) + + expect(response.status).to eq(304) + end + end + def mr_with_later_created_and_updated_at_time merge_request merge_request.created_at += 1.hour -- cgit v1.2.1 From fa3009095fbb995550a20e5d0cbc994f4290fbea Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Tue, 12 Apr 2016 14:46:59 +0200 Subject: Make subscription API more RESTful --- doc/api/issues.md | 27 ++++++++++++++------------- doc/api/merge_requests.md | 28 ++++++++++++++-------------- lib/api/helpers.rb | 2 +- lib/api/issues.rb | 29 ++++++----------------------- lib/api/merge_requests.rb | 14 +++++++------- spec/requests/api/issues_spec.rb | 26 +++++++++++++++++++------- spec/requests/api/merge_requests_spec.rb | 14 +++++++------- 7 files changed, 68 insertions(+), 72 deletions(-) diff --git a/doc/api/issues.md b/doc/api/issues.md index 4f7d948eba1..42024becc36 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -408,13 +408,14 @@ Example response: ## Subscribe to an issue -Subscribes to an issue to receive notifications. If the operation is successful, -status code `201` together with the updated issue is returned. If the user is -already subscribed to the issue, the status code `304` is returned. If the -project or issue is not found, status code `404` is returned. +Subscribes the authenticated user to an issue to receive notifications. If the +operation is successful, status code `201` together with the updated issue is +returned. If the user is already subscribed to the issue, the status code `304` +is returned. If the project or issue is not found, status code `404` is +returned. ``` -POST /projects/:id/issues/:issue_id/subscribe +POST /projects/:id/issues/:issue_id/subscription ``` | Attribute | Type | Required | Description | @@ -423,7 +424,7 @@ POST /projects/:id/issues/:issue_id/subscribe | `issue_id` | integer | yes | The ID of a project's issue | ```bash -curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/subscribe +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/subscription ``` Example response: @@ -461,14 +462,14 @@ Example response: ## Unsubscribe from an issue -Unsubscribes from an issue to not receive notifications from that issue. If the -operation is successful, status code `201` together with the updated issue is -returned. If the user is not subscribed to the issue, the status code `304` -is returned. If the project or issue is not found, status code `404` is -returned. +Unsubscribes the authenticated user from the issue to not receive notifications +from it. If the operation is successful, status code `200` together with the +updated issue is returned. If the user is not subscribed to the issue, the +status code `304` is returned. If the project or issue is not found, status code +`404` is returned. ``` -POST /projects/:id/issues/:issue_id/unsubscribe +DELETE /projects/:id/issues/:issue_id/subscription ``` | Attribute | Type | Required | Description | @@ -477,7 +478,7 @@ POST /projects/:id/issues/:issue_id/unsubscribe | `issue_id` | integer | yes | The ID of a project's issue | ```bash -curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/unsubscribe +curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/subscription ``` Example response: diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 16cb8926265..3c18ebfa31e 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -609,14 +609,14 @@ Example response: ## Subscribe to a merge request -Subscribes to a merge request to receive notification. If the operation is -successful, status code `201` together with the updated merge request is -returned. If the user is already subscribed to the merge request, the status -code `304` is returned. If the project or merge request is not found, status -code `404` is returned. +Subscribes the authenticated user to a merge request to receive notification. If +the operation is successful, status code `201` together with the updated merge +request is returned. If the user is already subscribed to the merge request, the +status code `304` is returned. If the project or merge request is not found, +status code `404` is returned. ``` -POST /projects/:id/merge_requests/:merge_request_id/subscribe +POST /projects/:id/merge_requests/:merge_request_id/subscription ``` | Attribute | Type | Required | Description | @@ -625,7 +625,7 @@ POST /projects/:id/merge_requests/:merge_request_id/subscribe | `merge_request_id` | integer | yes | The ID of the merge request | ```bash -curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscribe +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscription ``` Example response: @@ -682,14 +682,14 @@ Example response: ``` ## Unsubscribe from a merge request -Unsubscribes from a merge request to not receive notifications from that merge -request. If the operation is successful, status code `201` together with the -updated merge request is returned. If the user is not subscribed to the merge -request, the status code `304` is returned. If the project or merge request is -not found, status code `404` is returned. +Unsubscribes the authenticated user from a merge request to not receive +notifications from that merge request. If the operation is successful, status +code `200` together with the updated merge request is returned. If the user is +not subscribed to the merge request, the status code `304` is returned. If the +project or merge request is not found, status code `404` is returned. ``` -POST /projects/:id/merge_requests/:merge_request_id/unsubscribe +DELETE /projects/:id/merge_requests/:merge_request_id/subscription ``` | Attribute | Type | Required | Description | @@ -698,7 +698,7 @@ POST /projects/:id/merge_requests/:merge_request_id/unsubscribe | `merge_request_id` | integer | yes | The ID of the merge request | ```bash -curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscribe +curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/17/subscription ``` Example response: diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index aa0597564ed..54452f763a6 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -242,7 +242,7 @@ module API end def not_modified! - render_api_error!('304 Not modified', 304) + render_api_error!('304 Not Modified', 304) end def render_validation_error!(model) diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 049618c00f7..37d25073074 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -238,32 +238,15 @@ module API # id (required) - The ID of a project # issue_id (required) - The ID of a project issue # Example Request: - # POST /projects/:id/issues/:issue_id - post ":id/issues/:issue_idsubscribe" do + # POST /projects/:id/issues/:issue_id/subscription + post ":id/issues/:issue_id/subscription" do issue = user_project.issues.find_by(id: params[:issue_id]) - if !issue.subscribed?(current_user) - present issue, with: Entities::Issue, current_user: current_user - else + if issue.subscribed?(current_user) not_modified! - end - end - - # Subscribes to a project issue - # - # Parameters: - # id (required) - The ID of a project - # issue_id (required) - The ID of a project issue - # Example Request: - # POST /projects/:id/issues/:issue_id/subscribe - post ":id/issues/:issue_id/subscribe" do - issue = user_project.issues.find_by(id: params[:issue_id]) - - if !issue.subscribed?(current_user) + else issue.toggle_subscription(current_user) present issue, with: Entities::Issue, current_user: current_user - else - not_modified! end end @@ -273,8 +256,8 @@ module API # id (required) - The ID of a project # issue_id (required) - The ID of a project issue # Example Request: - # POST /projects/:id/issues/:issue_id/unsubscribe - post ":id/issues/:issue_id/unsubscribe" do + # DELETE /projects/:id/issues/:issue_id/subscription + delete ":id/issues/:issue_id/subscription" do issue = user_project.issues.find_by(id: params[:issue_id]) if issue.subscribed?(current_user) diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index d166484ba54..7e78609ecb9 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -334,15 +334,15 @@ module API # id (required) - The ID of a project # merge_request_id (required) - The ID of a merge request # Example Request: - # POST /projects/:id/issues/:merge_request_id/subscribe - post "#{path}/subscribe" do + # POST /projects/:id/issues/:merge_request_id/subscription + post "#{path}/subscription" do merge_request = user_project.merge_requests.find(params[:merge_request_id]) - if !merge_request.subscribed?(current_user) + if merge_request.subscribed?(current_user) + not_modified! + else merge_request.toggle_subscription(current_user) present merge_request, with: Entities::MergeRequest, current_user: current_user - else - not_modified! end end @@ -352,8 +352,8 @@ module API # id (required) - The ID of a project # merge_request_id (required) - The ID of a merge request # Example Request: - # POST /projects/:id/merge_requests/:merge_request_id/unsubscribe - post "#{path}/unsubscribe" do + # DELETE /projects/:id/merge_requests/:merge_request_id/subscription + delete "#{path}/subscription" do merge_request = user_project.merge_requests.find(params[:merge_request_id]) if merge_request.subscribed?(current_user) diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 437caf466b0..8361a1649e0 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -571,33 +571,45 @@ describe API::API, api: true do end end - describe 'POST :id/issues/:issue_id/subscribe' do + describe 'POST :id/issues/:issue_id/subscription' do it 'subscribes to an issue' do - post api("/projects/#{project.id}/issues/#{issue.id}/subscribe", user2) + post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) expect(response.status).to eq(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do - post api("/projects/#{project.id}/issues/#{issue.id}/subscribe", user) + post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) expect(response.status).to eq(304) end + + it 'returns 404 if the issue is not found' do + post api("/projects/#{project.id}/issues/123/subscription", user) + + expect(response.status).to eq(404) + end end - describe 'POST :id/issues/:issue_id/unsubscribe' do + describe 'DELETE :id/issues/:issue_id/subscription' do it 'unsubscribes from an issue' do - post api("/projects/#{project.id}/issues/#{issue.id}/unsubscribe", user) + post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) - expect(response.status).to eq(201) + expect(response.status).to eq(200) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do - post api("/projects/#{project.id}/issues/#{issue.id}/unsubscribe", user2) + post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) expect(response.status).to eq(304) end + + it 'returns 404 if the issue is not found' do + post api("/projects/#{project.id}/issues/123/subscription", user) + + expect(response.status).to eq(404) + end end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index b71c72a3829..c247fcf9c96 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -516,31 +516,31 @@ describe API::API, api: true do end end - describe 'POST :id/merge_requests/:merge_request_id/subscribe' do + describe 'POST :id/merge_requests/:merge_request_id/subscription' do it 'subscribes to a merge request' do - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", admin) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) expect(response.status).to eq(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", user) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) expect(response.status).to eq(304) end end - describe 'POST :id/merge_requests/:merge_request_id/unsubscribe' do + describe 'DELETE :id/merge_requests/:merge_request_id/subscription' do it 'unsubscribes from a merge request' do - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", user) + delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) - expect(response.status).to eq(201) + expect(response.status).to eq(200) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", admin) + delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) expect(response.status).to eq(304) end -- cgit v1.2.1 From ea2193aaeb1127746dc78d2dda7037d998911662 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 6 Apr 2016 15:52:16 +0200 Subject: API: Star and unstar a project --- CHANGELOG | 1 + doc/api/README.md | 1 + doc/api/projects.md | 127 +++++++++++++++++++++++++++++++++++++ lib/api/helpers.rb | 4 ++ lib/api/projects.rb | 33 ++++++++++ spec/requests/api/projects_spec.rb | 50 +++++++++++++++ 6 files changed, 216 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index c633c4bb35f..7a2af5a0eb8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -26,6 +26,7 @@ v 8.7.0 (unreleased) - Add links to CI setup documentation from project settings and builds pages - Handle nil descriptions in Slack issue messages (Stan Hu) - API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling) + - API: Ability to star and unstar a project (Robert Schilling) - Add default scope to projects to exclude projects pending deletion - Allow to close merge requests which source projects(forks) are deleted. - Ensure empty recipients are rejected in BuildsEmailService diff --git a/doc/api/README.md b/doc/api/README.md index 7629ef294ac..3a8fa6cebd1 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -108,6 +108,7 @@ The following table shows the possible return codes for API requests. | ------------- | ----------- | | `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON. | | `201 Created` | The `POST` request was successful and the resource is returned as JSON. | +| `304 Not Modified` | Indicates that the resource has not been modified since the last request. | | `400 Bad Request` | A required attribute of the API request is missing, e.g., the title of an issue is not given. | | `401 Unauthorized` | The user is not authenticated, a valid [user token](#authentication) is necessary. | | `403 Forbidden` | The request is not allowed, e.g., the user is not allowed to delete a project. | diff --git a/doc/api/projects.md b/doc/api/projects.md index ab716c229dc..c5ffc5514c7 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -491,6 +491,133 @@ Parameters: - `id` (required) - The ID of the project to be forked +### Star a project + +Stars a given project. Returns status code 201 and the project on success and +304 if the project is already starred. + +``` +POST /projects/:id/star +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of the project | + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/star" +``` + +Example response: + +```json +{ + "id": 3, + "description": null, + "default_branch": "master", + "public": false, + "visibility_level": 10, + "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git", + "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git", + "web_url": "http://example.com/diaspora/diaspora-project-site", + "tag_list": [ + "example", + "disapora project" + ], + "name": "Diaspora Project Site", + "name_with_namespace": "Diaspora / Diaspora Project Site", + "path": "diaspora-project-site", + "path_with_namespace": "diaspora/diaspora-project-site", + "issues_enabled": true, + "open_issues_count": 1, + "merge_requests_enabled": true, + "builds_enabled": true, + "wiki_enabled": true, + "snippets_enabled": false, + "created_at": "2013-09-30T13: 46: 02Z", + "last_activity_at": "2013-09-30T13: 46: 02Z", + "creator_id": 3, + "namespace": { + "created_at": "2013-09-30T13: 46: 02Z", + "description": "", + "id": 3, + "name": "Diaspora", + "owner_id": 1, + "path": "diaspora", + "updated_at": "2013-09-30T13: 46: 02Z" + }, + "archived": true, + "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png", + "shared_runners_enabled": true, + "forks_count": 0, + "star_count": 1 +} +``` + +### Unstar a project + +Unstars a given project. Returns status code 201 and the project on success +and 304 if the project is already unstarred. + +``` +POST /projects/:id/unstar +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of the project | + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/unstar" +``` + +Example response: + +```json +{ + "id": 3, + "description": null, + "default_branch": "master", + "public": false, + "visibility_level": 10, + "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git", + "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git", + "web_url": "http://example.com/diaspora/diaspora-project-site", + "tag_list": [ + "example", + "disapora project" + ], + "name": "Diaspora Project Site", + "name_with_namespace": "Diaspora / Diaspora Project Site", + "path": "diaspora-project-site", + "path_with_namespace": "diaspora/diaspora-project-site", + "issues_enabled": true, + "open_issues_count": 1, + "merge_requests_enabled": true, + "builds_enabled": true, + "wiki_enabled": true, + "snippets_enabled": false, + "created_at": "2013-09-30T13: 46: 02Z", + "last_activity_at": "2013-09-30T13: 46: 02Z", + "creator_id": 3, + "namespace": { + "created_at": "2013-09-30T13: 46: 02Z", + "description": "", + "id": 3, + "name": "Diaspora", + "owner_id": 1, + "path": "diaspora", + "updated_at": "2013-09-30T13: 46: 02Z" + }, + "archived": true, + "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png", + "shared_runners_enabled": true, + "forks_count": 0, + "star_count": 0 +} +``` + + ### Archive a project Archives the project if the user is either admin or the project owner of this project. This action is diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 4921ae99e78..aa0597564ed 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -241,6 +241,10 @@ module API render_api_error!('413 Request Entity Too Large', 413) end + def not_modified! + render_api_error!('304 Not modified', 304) + end + def render_validation_error!(model) if model.errors.any? render_api_error!(model.errors.messages || '400 Bad Request', 400) diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 24b31005475..ebcf7a4eedd 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -272,6 +272,39 @@ module API present user_project, with: Entities::Project end + # Star project + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # POST /projects/:id/star + post ':id/star' do + if !current_user.starred?(user_project) + current_user.toggle_star(user_project) + user_project.reload + present user_project, with: Entities::Project + + else + not_modified! + end + end + + # Unstar project + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # POST /projects/:id/unstar + post ':id/unstar' do + if current_user.starred?(user_project) + current_user.toggle_star(user_project) + user_project.reload + present user_project, with: Entities::Project + else + not_modified! + end + end + # Remove project # # Parameters: diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index be2034e0f39..f05622f77fe 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1020,6 +1020,56 @@ describe API::API, api: true do end end + describe 'POST /projects/:id/star' do + context 'on an unstarred project' do + it 'stars the project' do + post api("/projects/#{project.id}/star", user) + + expect(response.status).to eq(201) + expect(json_response['star_count']).to eq(1) + end + end + + context 'on a starred project' do + before do + user.toggle_star(project) + project.reload + end + + it 'does not modify the star count' do + post api("/projects/#{project.id}/star", user) + + expect(response.status).to eq(304) + expect(project.star_count).to eq(1) + end + end + end + + describe 'POST /projects/:id/unstar' do + context 'on a starred project' do + before do + user.toggle_star(project) + project.reload + end + + it 'unstars the project' do + post api("/projects/#{project.id}/unstar", user) + + expect(response.status).to eq(201) + expect(json_response['star_count']).to eq(0) + end + end + + context 'on an unstarred project' do + it 'does not modify the star count' do + post api("/projects/#{project.id}/unstar", user) + + expect(response.status).to eq(304) + expect(project.star_count).to eq(0) + end + end + end + describe 'DELETE /projects/:id' do context 'when authenticated as user' do it 'should remove project' do -- cgit v1.2.1 From 3ab9ea8dae1edc6ab8c8563843342890736eb24c Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Tue, 12 Apr 2016 18:52:43 +0200 Subject: Make staring API more restful --- doc/api/projects.md | 12 ++++++------ lib/api/projects.rb | 11 +++++------ spec/requests/api/projects_spec.rb | 8 ++++---- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index c5ffc5514c7..b25c9080080 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -493,8 +493,8 @@ Parameters: ### Star a project -Stars a given project. Returns status code 201 and the project on success and -304 if the project is already starred. +Stars a given project. Returns status code `201` and the project on success and +`304` if the project is already starred. ``` POST /projects/:id/star @@ -556,11 +556,11 @@ Example response: ### Unstar a project -Unstars a given project. Returns status code 201 and the project on success -and 304 if the project is already unstarred. +Unstars a given project. Returns status code `200` and the project on success +and `304` if the project is not starred. ``` -POST /projects/:id/unstar +DELETE /projects/:id/star ``` | Attribute | Type | Required | Description | @@ -568,7 +568,7 @@ POST /projects/:id/unstar | `id` | integer | yes | The ID of the project | ```bash -curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/unstar" +curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/star" ``` Example response: diff --git a/lib/api/projects.rb b/lib/api/projects.rb index ebcf7a4eedd..c7fdfbfe57b 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -279,13 +279,12 @@ module API # Example Request: # POST /projects/:id/star post ':id/star' do - if !current_user.starred?(user_project) + if current_user.starred?(user_project) + not_modified! + else current_user.toggle_star(user_project) user_project.reload present user_project, with: Entities::Project - - else - not_modified! end end @@ -294,8 +293,8 @@ module API # Parameters: # id (required) - The ID of a project # Example Request: - # POST /projects/:id/unstar - post ':id/unstar' do + # DELETE /projects/:id/unstar + delete ':id/star' do if current_user.starred?(user_project) current_user.toggle_star(user_project) user_project.reload diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index f05622f77fe..2a7c55fe65e 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1045,7 +1045,7 @@ describe API::API, api: true do end end - describe 'POST /projects/:id/unstar' do + describe 'DELETE /projects/:id/star' do context 'on a starred project' do before do user.toggle_star(project) @@ -1053,16 +1053,16 @@ describe API::API, api: true do end it 'unstars the project' do - post api("/projects/#{project.id}/unstar", user) + delete api("/projects/#{project.id}/star", user) - expect(response.status).to eq(201) + expect(response.status).to eq(200) expect(json_response['star_count']).to eq(0) end end context 'on an unstarred project' do it 'does not modify the star count' do - post api("/projects/#{project.id}/unstar", user) + delete api("/projects/#{project.id}/star", user) expect(response.status).to eq(304) expect(project.star_count).to eq(0) -- cgit v1.2.1 From 3b9edce803c91b5f51675291fdf22f1159cea456 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 13 Apr 2016 15:19:50 +0200 Subject: Instrument the HousekeepingService class This allows us to track how much time is spent in updating the "pushes_since_gc" column as well as the time needed to obtain the lease. --- CHANGELOG | 1 + app/services/projects/housekeeping_service.rb | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 5399e3e5b8b..ec8db471572 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.7.0 (unreleased) + - The Projects::HousekeepingService class has extra instrumentation (Yorick Peterse) - All service classes (those residing in app/services) are now instrumented (Yorick Peterse) - Developers can now add custom tags to transactions (Yorick Peterse) - Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea) diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb index a0973c5d260..3b7c36f0908 100644 --- a/app/services/projects/housekeeping_service.rb +++ b/app/services/projects/housekeeping_service.rb @@ -26,7 +26,9 @@ module Projects GitlabShellOneShotWorker.perform_async(:gc, @project.path_with_namespace) ensure - @project.update_column(:pushes_since_gc, 0) + Gitlab::Metrics.measure(:reset_pushes_since_gc) do + @project.update_column(:pushes_since_gc, 0) + end end def needed? @@ -34,14 +36,18 @@ module Projects end def increment! - @project.increment!(:pushes_since_gc) + Gitlab::Metrics.measure(:increment_pushes_since_gc) do + @project.increment!(:pushes_since_gc) + end end private def try_obtain_lease - lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT) - lease.try_obtain + Gitlab::Metrics.measure(:obtain_housekeeping_lease) do + lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT) + lease.try_obtain + end end end end -- cgit v1.2.1 From 0f602be99f99f1ae493478a8a28df2907cfa0082 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 15:56:05 +0200 Subject: Clear repository check columns asynchronously --- .../admin/application_settings_controller.rb | 8 +-- app/controllers/admin/projects_controller.rb | 2 +- app/workers/repository_check/batch_worker.rb | 63 ++++++++++++++++++++++ app/workers/repository_check/clear_worker.rb | 17 ++++++ .../repository_check/single_repository_worker.rb | 36 +++++++++++++ app/workers/repository_check_worker.rb | 61 --------------------- app/workers/single_repository_check_worker.rb | 34 ------------ config/initializers/1_settings.rb | 2 +- .../admin/admin_uses_repository_checks_spec.rb | 27 ++++------ spec/workers/repository_check/batch_worker_spec.rb | 39 ++++++++++++++ spec/workers/repository_check/clear_worker_spec.rb | 17 ++++++ spec/workers/repository_check_worker_spec.rb | 39 -------------- 12 files changed, 187 insertions(+), 158 deletions(-) create mode 100644 app/workers/repository_check/batch_worker.rb create mode 100644 app/workers/repository_check/clear_worker.rb create mode 100644 app/workers/repository_check/single_repository_worker.rb delete mode 100644 app/workers/repository_check_worker.rb delete mode 100644 app/workers/single_repository_check_worker.rb create mode 100644 spec/workers/repository_check/batch_worker_spec.rb create mode 100644 spec/workers/repository_check/clear_worker_spec.rb delete mode 100644 spec/workers/repository_check_worker_spec.rb diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index a54864480a2..b4a28b8dd3f 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -20,18 +20,14 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController end def clear_repository_check_states - Project.update_all( - last_repository_check_failed: false, - last_repository_check_at: nil - ) + RepositoryCheck::ClearWorker.perform_async redirect_to( admin_application_settings_path, - notice: 'All repository check states were cleared.' + notice: 'Started asynchronous removal of all repository check states.' ) end - private def set_application_setting diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 6854e57b650..87986fdf8b1 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -32,7 +32,7 @@ class Admin::ProjectsController < Admin::ApplicationController end def repository_check - SingleRepositoryCheckWorker.perform_async(@project.id) + RepositoryCheck::SingleRepositoryWorker.perform_async(@project.id) redirect_to( admin_namespace_project_path(@project.namespace, @project), diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb new file mode 100644 index 00000000000..16cd77a9bf0 --- /dev/null +++ b/app/workers/repository_check/batch_worker.rb @@ -0,0 +1,63 @@ +module RepositoryCheck + class BatchWorker + include Sidekiq::Worker + + RUN_TIME = 3600 + + sidekiq_options retry: false + + def perform + start = Time.now + + # This loop will break after a little more than one hour ('a little + # more' because `git fsck` may take a few minutes), or if it runs out of + # projects to check. By default sidekiq-cron will start a new + # RepositoryCheckWorker each hour so that as long as there are repositories to + # check, only one (or two) will be checked at a time. + project_ids.each do |project_id| + break if Time.now - start >= RUN_TIME + break unless current_settings.repository_checks_enabled + + next unless try_obtain_lease(project_id) + + SingleRepositoryWorker.new.perform(project_id) + end + end + + private + + # Project.find_each does not support WHERE clauses and + # Project.find_in_batches does not support ordering. So we just build an + # array of ID's. This is OK because we do it only once an hour, because + # getting ID's from Postgres is not terribly slow, and because no user + # has to sit and wait for this query to finish. + def project_ids + limit = 10_000 + never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). + pluck(:id) + old_check_projects = Project.where('last_repository_check_at < ?', 1.week.ago). + reorder('last_repository_check_at ASC').limit(limit).pluck(:id) + never_checked_projects + old_check_projects + end + + def try_obtain_lease(id) + # Use a 24-hour timeout because on servers/projects where 'git fsck' is + # super slow we definitely do not want to run it twice in parallel. + Gitlab::ExclusiveLease.new( + "project_repository_check:#{id}", + timeout: 24.hours + ).try_obtain + end + + def current_settings + # No caching of the settings! If we cache them and an admin disables + # this feature, an active RepositoryCheckWorker would keep going for up + # to 1 hour after the feature was disabled. + if Rails.env.test? + Gitlab::CurrentSettings.fake_application_settings + else + ApplicationSetting.current + end + end + end +end diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb new file mode 100644 index 00000000000..fe0cce9aab7 --- /dev/null +++ b/app/workers/repository_check/clear_worker.rb @@ -0,0 +1,17 @@ +module RepositoryCheck + class ClearWorker + include Sidekiq::Worker + + sidekiq_options retry: false + + def perform + # Do batched updates because these updates will be slow and locking + Project.select(:id).find_in_batches(batch_size: 1000) do |batch| + Project.where(id: batch.map(&:id)).update_all( + last_repository_check_failed: nil, + last_repository_check_at: nil, + ) + end + end + end +end \ No newline at end of file diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb new file mode 100644 index 00000000000..e54ae86d06c --- /dev/null +++ b/app/workers/repository_check/single_repository_worker.rb @@ -0,0 +1,36 @@ +module RepositoryCheck + class SingleRepositoryWorker + include Sidekiq::Worker + + sidekiq_options retry: false + + def perform(project_id) + project = Project.find(project_id) + project.update_columns( + last_repository_check_failed: !check(project), + last_repository_check_at: Time.now, + ) + end + + private + + def check(project) + # Use 'map do', not 'all? do', to prevent short-circuiting + [project.repository, project.wiki.repository].map do |repository| + git_fsck(repository.path_to_repo) + end.all? + end + + def git_fsck(path) + cmd = %W(nice git --git-dir=#{path} fsck) + output, status = Gitlab::Popen.popen(cmd) + + if status.zero? + true + else + Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") + false + end + end + end +end diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb deleted file mode 100644 index d7ead91f94e..00000000000 --- a/app/workers/repository_check_worker.rb +++ /dev/null @@ -1,61 +0,0 @@ -class RepositoryCheckWorker - include Sidekiq::Worker - - RUN_TIME = 3600 - - sidekiq_options retry: false - - def perform - start = Time.now - - # This loop will break after a little more than one hour ('a little - # more' because `git fsck` may take a few minutes), or if it runs out of - # projects to check. By default sidekiq-cron will start a new - # RepositoryCheckWorker each hour so that as long as there are repositories to - # check, only one (or two) will be checked at a time. - project_ids.each do |project_id| - break if Time.now - start >= RUN_TIME - break unless current_settings.repository_checks_enabled - - next unless try_obtain_lease(project_id) - - SingleRepositoryCheckWorker.new.perform(project_id) - end - end - - private - - # Project.find_each does not support WHERE clauses and - # Project.find_in_batches does not support ordering. So we just build an - # array of ID's. This is OK because we do it only once an hour, because - # getting ID's from Postgres is not terribly slow, and because no user - # has to sit and wait for this query to finish. - def project_ids - limit = 10_000 - never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). - pluck(:id) - old_check_projects = Project.where('last_repository_check_at < ?', 1.week.ago). - reorder('last_repository_check_at ASC').limit(limit).pluck(:id) - never_checked_projects + old_check_projects - end - - def try_obtain_lease(id) - # Use a 24-hour timeout because on servers/projects where 'git fsck' is - # super slow we definitely do not want to run it twice in parallel. - Gitlab::ExclusiveLease.new( - "project_repository_check:#{id}", - timeout: 24.hours - ).try_obtain - end - - def current_settings - # No caching of the settings! If we cache them and an admin disables - # this feature, an active RepositoryCheckWorker would keep going for up - # to 1 hour after the feature was disabled. - if Rails.env.test? - Gitlab::CurrentSettings.fake_application_settings - else - ApplicationSetting.current - end - end -end diff --git a/app/workers/single_repository_check_worker.rb b/app/workers/single_repository_check_worker.rb deleted file mode 100644 index f6c345df8b5..00000000000 --- a/app/workers/single_repository_check_worker.rb +++ /dev/null @@ -1,34 +0,0 @@ -class SingleRepositoryCheckWorker - include Sidekiq::Worker - - sidekiq_options retry: false - - def perform(project_id) - project = Project.find(project_id) - project.update_columns( - last_repository_check_failed: !check(project), - last_repository_check_at: Time.now, - ) - end - - private - - def check(project) - # Use 'map do', not 'all? do', to prevent short-circuiting - [project.repository, project.wiki.repository].map do |repository| - git_fsck(repository.path_to_repo) - end.all? - end - - def git_fsck(path) - cmd = %W(nice git --git-dir=#{path} fsck) - output, status = Gitlab::Popen.popen(cmd) - - if status.zero? - true - else - Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") - false - end - end -end diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 5eb7fdff551..3124dc43065 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -241,7 +241,7 @@ Settings.cron_jobs['stuck_ci_builds_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['stuck_ci_builds_worker']['job_class'] = 'StuckCiBuildsWorker' Settings.cron_jobs['repository_check_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['repository_check_worker']['cron'] ||= '20 * * * *' -Settings.cron_jobs['repository_check_worker']['job_class'] = 'RepositoryCheckWorker' +Settings.cron_jobs['repository_check_worker']['job_class'] = 'RepositoryCheck::BatchWorker' Settings.cron_jobs['admin_email_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['admin_email_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['admin_email_worker']['job_class'] = 'AdminEmailWorker' diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index 69b82441916..661fb761809 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -1,9 +1,7 @@ require 'rails_helper' feature 'Admin uses repository checks', feature: true do - before do - login_as :admin - end + before { login_as :admin } scenario 'to trigger a single check' do project = create(:empty_project) @@ -17,7 +15,12 @@ feature 'Admin uses repository checks', feature: true do end scenario 'to see a single failed repository check' do - visit_admin_project_page(broken_project) + project = create(:empty_project) + project.update_columns( + last_repository_check_failed: true, + last_repository_check_at: Time.now, + ) + visit_admin_project_page(project) page.within('.alert') do expect(page.text).to match(/Last repository check \(.* ago\) failed/) @@ -25,24 +28,16 @@ feature 'Admin uses repository checks', feature: true do end scenario 'to clear all repository checks', js: true do - project = broken_project visit admin_application_settings_path + + expect(RepositoryCheck::ClearWorker).to receive(:perform_async) - click_link 'Clear all repository checks' # pop-up should be auto confirmed + click_link 'Clear all repository checks' - expect(project.reload.last_repository_check_failed).to eq(false) + expect(page).to have_content('Started asynchronous removal of all repository check states.') end def visit_admin_project_page(project) visit admin_namespace_project_path(project.namespace, project) end - - def broken_project - project = create(:empty_project) - project.update_columns( - last_repository_check_failed: true, - last_repository_check_at: Time.now, - ) - project - end end diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb new file mode 100644 index 00000000000..51caa645f47 --- /dev/null +++ b/spec/workers/repository_check/batch_worker_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe RepositoryCheck::BatchWorker do + subject { described_class.new } + + it 'prefers projects that have never been checked' do + projects = create_list(:project, 3) + projects[0].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) + end + + it 'sorts projects by last_repository_check_at' do + projects = create_list(:project, 3) + projects[0].update_column(:last_repository_check_at, 2.weeks.ago) + projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) + end + + it 'excludes projects that were checked recently' do + projects = create_list(:project, 3) + projects[0].update_column(:last_repository_check_at, 2.days.ago) + projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.days.ago) + + expect(subject.perform).to eq([projects[1].id]) + end + + it 'does nothing when repository checks are disabled' do + create(:empty_project) + current_settings = double('settings', repository_checks_enabled: false) + expect(subject).to receive(:current_settings) { current_settings } + + expect(subject.perform).to eq(nil) + end +end diff --git a/spec/workers/repository_check/clear_worker_spec.rb b/spec/workers/repository_check/clear_worker_spec.rb new file mode 100644 index 00000000000..a3b70c74787 --- /dev/null +++ b/spec/workers/repository_check/clear_worker_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe RepositoryCheck::ClearWorker do + it 'clears repository check columns' do + project = create(:empty_project) + project.update_columns( + last_repository_check_failed: true, + last_repository_check_at: Time.now, + ) + + described_class.new.perform + project.reload + + expect(project.last_repository_check_failed).to be_nil + expect(project.last_repository_check_at).to be_nil + end +end diff --git a/spec/workers/repository_check_worker_spec.rb b/spec/workers/repository_check_worker_spec.rb deleted file mode 100644 index 7a757658e97..00000000000 --- a/spec/workers/repository_check_worker_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'spec_helper' - -describe RepositoryCheckWorker do - subject { RepositoryCheckWorker.new } - - it 'prefers projects that have never been checked' do - projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.weeks.ago) - - expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) - end - - it 'sorts projects by last_repository_check_at' do - projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 2.weeks.ago) - projects[1].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.weeks.ago) - - expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) - end - - it 'excludes projects that were checked recently' do - projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 2.days.ago) - projects[1].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.days.ago) - - expect(subject.perform).to eq([projects[1].id]) - end - - it 'does nothing when repository checks are disabled' do - create(:empty_project) - current_settings = double('settings', repository_checks_enabled: false) - expect(subject).to receive(:current_settings) { current_settings } - - expect(subject.perform).to eq(nil) - end -end -- cgit v1.2.1 From acf911eeae0e952aec52d0e491efb69c99fb31f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 12 Apr 2016 18:09:52 +0200 Subject: Fix a bug with trailing slash in bamboo_url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, improve specs for BambooService Similar to https://gitlab.com/gitlab-org/gitlab-ce/issues/3515 Signed-off-by: Rémy Coutable --- CHANGELOG | 1 + app/models/project_services/bamboo_service.rb | 20 +- .../models/project_services/bamboo_service_spec.rb | 262 +++++++++++++++++---- 3 files changed, 221 insertions(+), 62 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 382318a203c..8646d99b64d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,7 @@ v 8.7.0 (unreleased) - API: Expose `subscribed` for issues and merge requests (Robert Schilling) - Allow SAML to handle external users based on user's information !3530 - Add endpoints to archive or unarchive a project !3372 + - Fix a bug whith trailing slash in bamboo_url - Add links to CI setup documentation from project settings and builds pages - Handle nil descriptions in Slack issue messages (Stan Hu) - API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling) diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 9e7f642180e..060062aaf7a 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -82,17 +82,17 @@ class BambooService < CiService end def build_info(sha) - url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}") + url = URI.join(bamboo_url, "/rest/api/latest/result?label=#{sha}").to_s if username.blank? && password.blank? - @response = HTTParty.get(parsed_url.to_s, verify: false) + @response = HTTParty.get(url, verify: false) else - get_url = "#{url}&os_authType=basic" + url << '&os_authType=basic' auth = { - username: username, - password: password, + username: username, + password: password } - @response = HTTParty.get(get_url, verify: false, basic_auth: auth) + @response = HTTParty.get(url, verify: false, basic_auth: auth) end end @@ -101,11 +101,11 @@ class BambooService < CiService if @response.code != 200 || @response['results']['results']['size'] == '0' # If actual build link can't be determined, send user to build summary page. - "#{bamboo_url}/browse/#{build_key}" + URI.join(bamboo_url, "/browse/#{build_key}").to_s else # If actual build link is available, go to build result page. result_key = @response['results']['results']['result']['planResultKey']['key'] - "#{bamboo_url}/browse/#{result_key}" + URI.join(bamboo_url, "/browse/#{result_key}").to_s end end @@ -134,7 +134,7 @@ class BambooService < CiService return unless supported_events.include?(data[:object_kind]) # Bamboo requires a GET and does not take any data. - self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}", - verify: false) + url = URI.join(bamboo_url, "/updateAndBuild.action?buildKey=#{build_key}").to_s + self.class.get(url, verify: false) end end diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index c34b2487ecf..31b2c90122d 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -21,74 +21,232 @@ require 'spec_helper' describe BambooService, models: true do - describe "Associations" do + describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } end - describe "Execute" do - let(:user) { create(:user) } - let(:project) { create(:project) } - - context "when a password was previously set" do - before do - @bamboo_service = BambooService.create( - project: create(:project), - properties: { - bamboo_url: 'http://gitlab.com', - username: 'mic', - password: "password" - } - ) + 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 + + expect(bamboo_service).not_to validate_presence_of(:bamboo_url) + end + + it 'validates the presence of bamboo_url if service is active' do + bamboo_service = service + bamboo_service.active = true + + expect(bamboo_service).to validate_presence_of(:bamboo_url) + 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 + + expect(bamboo_service).not_to validate_presence_of(:build_key) end - - it "reset password if url changed" do - @bamboo_service.bamboo_url = 'http://gitlab1.com' - @bamboo_service.save - expect(@bamboo_service.password).to be_nil + + 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 - - it "does not reset password if username changed" do - @bamboo_service.username = "some_name" - @bamboo_service.save - expect(@bamboo_service.password).to eq("password") + 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 reset password if new url is set together with password, even if it's the same password" do - @bamboo_service.bamboo_url = 'http://gitlab_edited.com' - @bamboo_service.password = 'password' - @bamboo_service.save - expect(@bamboo_service.password).to eq("password") - expect(@bamboo_service.bamboo_url).to eq("http://gitlab_edited.com") + 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 "should reset password if url changed, even if setter called multiple times" do - @bamboo_service.bamboo_url = 'http://gitlab1.com' - @bamboo_service.bamboo_url = 'http://gitlab1.com' - @bamboo_service.save - expect(@bamboo_service.password).to be_nil + 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 end - - context "when no password was previously set" do - before do - @bamboo_service = BambooService.create( - project: create(:project), - properties: { - bamboo_url: 'http://gitlab.com', - username: 'mic' - } - ) + end + + describe 'Callbacks' do + describe 'before_update :reset_password' do + context 'when a password was previously set' do + it 'resets password if url changed' do + bamboo_service = service + + bamboo_service.bamboo_url = 'http://gitlab1.com' + bamboo_service.save + + expect(bamboo_service.password).to be_nil + end + + it 'does not reset password if username changed' do + bamboo_service = service + + bamboo_service.username = 'some_name' + bamboo_service.save + + expect(bamboo_service.password).to eq('password') + end + + it "does not reset password if new url is set together with password, even if it's the same password" do + bamboo_service = service + + bamboo_service.bamboo_url = 'http://gitlab_edited.com' + bamboo_service.password = 'password' + bamboo_service.save + + expect(bamboo_service.password).to eq('password') + expect(bamboo_service.bamboo_url).to eq('http://gitlab_edited.com') + end end - it "saves password if new url is set together with password" do - @bamboo_service.bamboo_url = 'http://gitlab_edited.com' - @bamboo_service.password = 'password' - @bamboo_service.save - expect(@bamboo_service.password).to eq("password") - expect(@bamboo_service.bamboo_url).to eq("http://gitlab_edited.com") + it 'saves password if new url is set together with password when no password was previously set' do + bamboo_service = service + bamboo_service.password = nil + + bamboo_service.bamboo_url = 'http://gitlab_edited.com' + bamboo_service.password = 'password' + bamboo_service.save + + expect(bamboo_service.password).to eq('password') + expect(bamboo_service.bamboo_url).to eq('http://gitlab_edited.com') end + end + end + + describe '#build_page' do + it 'returns a specific URL when status is 500' do + stub_request(status: 500) + + expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo') + end + + it 'returns a specific URL when response has no results' do + stub_request(body: %Q({"results":{"results":{"size":"0"}}})) + + expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo') + end + + it 'returns a build URL when bamboo_url has no trailing slash' do + stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}})) + + expect(service(bamboo_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42') + end + + it 'returns a build URL when bamboo_url has a trailing slash' do + stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}})) + + expect(service(bamboo_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42') + end + end + + describe '#commit_status' do + it 'sets commit status to :error when status is 500' do + stub_request(status: 500) + + expect(service.commit_status('123', 'unused')).to eq(:error) + end + + it 'sets commit status to "pending" when status is 404' do + stub_request(status: 404) + + expect(service.commit_status('123', 'unused')).to eq('pending') + end + + it 'sets commit status to "pending" when response has no results' do + stub_request(body: %Q({"results":{"results":{"size":"0"}}})) + + expect(service.commit_status('123', 'unused')).to eq('pending') + end + + it 'sets commit status to "success" when build state contains Success' do + stub_request(build_state: 'YAY Success!') + expect(service.commit_status('123', 'unused')).to eq('success') end + + it 'sets commit status to "failed" when build state contains Failed' do + stub_request(build_state: 'NO Failed!') + + expect(service.commit_status('123', 'unused')).to eq('failed') + end + + it 'sets commit status to "pending" when build state contains Pending' do + stub_request(build_state: 'NO Pending!') + + expect(service.commit_status('123', 'unused')).to eq('pending') + end + + it 'sets commit status to :error when build state is unknown' do + stub_request(build_state: 'FOO BAR!') + + expect(service.commit_status('123', 'unused')).to eq(:error) + end + end + + def service(bamboo_url: 'http://gitlab.com') + described_class.create( + project: build_stubbed(:empty_project), + properties: { + bamboo_url: bamboo_url, + username: 'mic', + password: 'password', + build_key: 'foo' + } + ) + end + + def stub_request(status: 200, body: nil, build_state: 'success') + bamboo_full_url = 'http://mic:password@gitlab.com/rest/api/latest/result?label=123&os_authType=basic' + body ||= %Q({"results":{"results":{"result":{"buildState":"#{build_state}"}}}}) + + WebMock.stub_request(:get, bamboo_full_url).to_return( + status: status, + headers: { 'Content-Type' => 'application/json' }, + body: body + ) end end -- cgit v1.2.1 From 02cfbf0db5dda8ca86f4811e5d5cb055a8cc5cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 13 Apr 2016 11:25:42 +0200 Subject: Refactor and expose only Gitlab::UrlBuilder.build(record) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/models/commit.rb | 6 +- app/services/issues/base_service.rb | 2 +- app/services/merge_requests/base_service.rb | 3 +- app/views/search/results/_note.html.haml | 2 +- lib/gitlab/note_data_builder.rb | 3 +- lib/gitlab/url_builder.rb | 74 +++++++------- spec/factories/commits.rb | 12 +++ spec/lib/gitlab/note_data_builder_spec.rb | 3 +- spec/lib/gitlab/url_builder_spec.rb | 143 +++++++++++++++++----------- 9 files changed, 147 insertions(+), 101 deletions(-) create mode 100644 spec/factories/commits.rb diff --git a/app/models/commit.rb b/app/models/commit.rb index 11ecfcace14..d1f07ccd55c 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -154,7 +154,7 @@ class Commit id: id, message: safe_message, timestamp: committed_date.xmlschema, - url: commit_url, + url: Gitlab::UrlBuilder.build(self), author: { name: author_name, email: author_email @@ -168,10 +168,6 @@ class Commit data end - def commit_url - project.present? ? "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{id}" : "" - end - # Discover issues should be closed when this commit is pushed to a project's # default branch. def closes_issues(current_user = self.committer) diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 770f32de944..772f5c5fffa 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -3,7 +3,7 @@ module Issues def hook_data(issue, action) issue_data = issue.to_hook_data(current_user) - issue_url = Gitlab::UrlBuilder.new(:issue).build(issue.id) + issue_url = Gitlab::UrlBuilder.build(issue) issue_data[:object_attributes].merge!(url: issue_url, action: action) issue_data end diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index ac5b58db862..e6837a18696 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -20,8 +20,7 @@ module MergeRequests def hook_data(merge_request, action) hook_data = merge_request.to_hook_data(current_user) - merge_request_url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id) - hook_data[:object_attributes][:url] = merge_request_url + hook_data[:object_attributes][:url] = Gitlab::UrlBuilder.build(merge_request) hook_data[:object_attributes][:action] = action hook_data end diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml index 9544e3d3e17..d9400b1d9fa 100644 --- a/app/views/search/results/_note.html.haml +++ b/app/views/search/results/_note.html.haml @@ -1,5 +1,5 @@ - project = note.project -- note_url = Gitlab::UrlBuilder.new(:note).build(note.id) +- note_url = Gitlab::UrlBuilder.build(note) - noteable_identifier = note.noteable.try(:iid) || note.noteable.id .search-result-row %h5.note-search-caption.str-truncated diff --git a/lib/gitlab/note_data_builder.rb b/lib/gitlab/note_data_builder.rb index 18523e0aefe..8bdc89a7751 100644 --- a/lib/gitlab/note_data_builder.rb +++ b/lib/gitlab/note_data_builder.rb @@ -59,8 +59,7 @@ module Gitlab repository: project.hook_attrs.slice(:name, :url, :description, :homepage) } - base_data[:object_attributes][:url] = - Gitlab::UrlBuilder.new(:note).build(note.id) + base_data[:object_attributes][:url] = Gitlab::UrlBuilder.build(note) base_data end diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb index f301d42939d..f1943222edf 100644 --- a/lib/gitlab/url_builder.rb +++ b/lib/gitlab/url_builder.rb @@ -4,50 +4,58 @@ module Gitlab include GitlabRoutingHelper include ActionView::RecordIdentifier - def initialize(type) - @type = type - end + attr_reader :object - def build(id) - case @type - when :issue - build_issue_url(id) - when :merge_request - build_merge_request_url(id) - when :note - build_note_url(id) + def self.build(object) + new(object).url + end + def url + case object + when Commit + commit_url + when Issue + issue_url(object) + when MergeRequest + merge_request_url(object) + when Note + note_url + else + raise NotImplementedError.new("No URL builder defined for #{object.class}") end end private - def build_issue_url(id) - issue = Issue.find(id) - issue_url(issue) + def initialize(object) + @object = object end - def build_merge_request_url(id) - merge_request = MergeRequest.find(id) - merge_request_url(merge_request) + def commit_url(opts = {}) + return '' if object.project.nil? + + namespace_project_commit_url({ + namespace_id: object.project.namespace, + project_id: object.project, + id: object.id + }.merge!(opts)) end - def build_note_url(id) - note = Note.find(id) - if note.for_commit? - namespace_project_commit_url(namespace_id: note.project.namespace, - id: note.commit_id, - project_id: note.project, - anchor: dom_id(note)) - elsif note.for_issue? - issue = Issue.find(note.noteable_id) - issue_url(issue, anchor: dom_id(note)) - elsif note.for_merge_request? - merge_request = MergeRequest.find(note.noteable_id) - merge_request_url(merge_request, anchor: dom_id(note)) - elsif note.for_snippet? - snippet = Snippet.find(note.noteable_id) - project_snippet_url(snippet, anchor: dom_id(note)) + def note_url + if object.for_commit? + commit_url(id: object.commit_id, anchor: dom_id(object)) + + elsif object.for_issue? + issue = Issue.find(object.noteable_id) + issue_url(issue, anchor: dom_id(object)) + + elsif object.for_merge_request? + merge_request = MergeRequest.find(object.noteable_id) + merge_request_url(merge_request, anchor: dom_id(object)) + + elsif object.for_snippet? + snippet = Snippet.find(object.noteable_id) + project_snippet_url(snippet, anchor: dom_id(object)) end end end diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb new file mode 100644 index 00000000000..ac6eb0a7897 --- /dev/null +++ b/spec/factories/commits.rb @@ -0,0 +1,12 @@ +require_relative '../support/repo_helpers' + +FactoryGirl.define do + factory :commit do + git_commit RepoHelpers.sample_commit + project factory: :empty_project + + initialize_with do + new(git_commit, project) + end + end +end diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb index da652677443..f093d0a0d8b 100644 --- a/spec/lib/gitlab/note_data_builder_spec.rb +++ b/spec/lib/gitlab/note_data_builder_spec.rb @@ -4,13 +4,12 @@ describe 'Gitlab::NoteDataBuilder', lib: true do let(:project) { create(:project) } let(:user) { create(:user) } let(:data) { Gitlab::NoteDataBuilder.build(note, user) } - let(:note_url) { Gitlab::UrlBuilder.new(:note).build(note.id) } let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors before(:each) do expect(data).to have_key(:object_attributes) expect(data[:object_attributes]).to have_key(:url) - expect(data[:object_attributes][:url]).to eq(note_url) + expect(data[:object_attributes][:url]).to eq(Gitlab::UrlBuilder.build(note)) expect(data[:object_kind]).to eq('note') expect(data[:user]).to eq(user.hook_attrs) end diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index f023be6ae45..6ffc0d6e658 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -1,77 +1,110 @@ require 'spec_helper' describe Gitlab::UrlBuilder, lib: true do - describe 'When asking for an issue' do - it 'returns the issue url' do - issue = create(:issue) - url = Gitlab::UrlBuilder.new(:issue).build(issue.id) - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" - end - end + describe '.build' do + context 'when passing a Commit' do + it 'returns a proper URL' do + commit = build_stubbed(:commit) - describe 'When asking for an merge request' do - it 'returns the merge request url' do - merge_request = create(:merge_request) - url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id) - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" + url = described_class.build(commit) + + expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" + end end - end - describe 'When asking for a note on commit' do - let(:note) { create(:note_on_commit) } - let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + context 'when passing an Issue' do + it 'returns a proper URL' do + issue = build_stubbed(:issue, iid: 42) - it 'returns the note url' do - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + url = described_class.build(issue) + + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" + end end - end - describe 'When asking for a note on commit diff' do - let(:note) { create(:note_on_commit_diff) } - let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + context 'when passing a MergeRequest' do + it 'returns a proper URL' do + merge_request = build_stubbed(:merge_request, iid: 42) + + url = described_class.build(merge_request) - it 'returns the note url' do - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" + end end - end - describe 'When asking for a note on issue' do - let(:issue) { create(:issue) } - let(:note) { create(:note_on_issue, noteable_id: issue.id) } - let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + context 'when passing a Note' do + context 'on a Commit' do + it 'returns a proper URL' do + note = build_stubbed(:note_on_commit) - it 'returns the note url' do - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" - end - end + url = described_class.build(note) - describe 'When asking for a note on merge request' do - let(:merge_request) { create(:merge_request) } - let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) } - let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + end + end - it 'returns the note url' do - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end + context 'on a CommitDiff' do + it 'returns a proper URL' do + note = build_stubbed(:note_on_commit_diff) - describe 'When asking for a note on merge request diff' do - let(:merge_request) { create(:merge_request) } - let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) } - let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + url = described_class.build(note) - it 'returns the note url' do - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + end + end + + context 'on an Issue' do + it 'returns a proper URL' do + issue = create(:issue, iid: 42) + note = build_stubbed(:note_on_issue, noteable: issue) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" + end + end + + context 'on a MergeRequest' do + it 'returns a proper URL' do + merge_request = create(:merge_request, iid: 42) + note = build_stubbed(:note_on_merge_request, noteable: merge_request) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + end + end + + context 'on a MergeRequestDiff' do + it 'returns a proper URL' do + merge_request = create(:merge_request, iid: 42) + note = build_stubbed(:note_on_merge_request_diff, noteable: merge_request) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + end + end + + context 'on a ProjectSnippet' do + it 'returns a proper URL' do + project_snippet = create(:project_snippet) + note = build_stubbed(:note_on_project_snippet, noteable: project_snippet) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" + end + end - describe 'When asking for a note on project snippet' do - let(:snippet) { create(:project_snippet) } - let(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) } - let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) } + context 'on another object' do + it 'returns a proper URL' do + project = build_stubbed(:project) - it 'returns the note url' do - expect(url).to eq "#{Settings.gitlab['url']}/#{snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" + expect { described_class.build(project) }. + to raise_error(NotImplementedError, 'No URL builder defined for Project') + end + end end end end -- cgit v1.2.1 From 54231aa4e036179d035ddd3641bc15a5b31883f2 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 13 Apr 2016 12:50:00 +0200 Subject: Styling changes to code and docs --- doc/api/projects.md | 1 - lib/api/helpers.rb | 2 +- lib/api/projects.rb | 4 +++- spec/requests/api/projects_spec.rb | 10 ++++------ 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index b25c9080080..de1faadebf5 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -617,7 +617,6 @@ Example response: } ``` - ### Archive a project Archives the project if the user is either admin or the project owner of this project. This action is diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index aa0597564ed..54452f763a6 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -242,7 +242,7 @@ module API end def not_modified! - render_api_error!('304 Not modified', 304) + render_api_error!('304 Not Modified', 304) end def render_validation_error!(model) diff --git a/lib/api/projects.rb b/lib/api/projects.rb index c7fdfbfe57b..cc2c7a0c503 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -284,6 +284,7 @@ module API else current_user.toggle_star(user_project) user_project.reload + present user_project, with: Entities::Project end end @@ -293,11 +294,12 @@ module API # Parameters: # id (required) - The ID of a project # Example Request: - # DELETE /projects/:id/unstar + # DELETE /projects/:id/star delete ':id/star' do if current_user.starred?(user_project) current_user.toggle_star(user_project) user_project.reload + present user_project, with: Entities::Project else not_modified! diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 2a7c55fe65e..fccd08bd6da 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1023,7 +1023,7 @@ describe API::API, api: true do describe 'POST /projects/:id/star' do context 'on an unstarred project' do it 'stars the project' do - post api("/projects/#{project.id}/star", user) + expect { post api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1) expect(response.status).to eq(201) expect(json_response['star_count']).to eq(1) @@ -1037,10 +1037,9 @@ describe API::API, api: true do end it 'does not modify the star count' do - post api("/projects/#{project.id}/star", user) + expect { post api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count } expect(response.status).to eq(304) - expect(project.star_count).to eq(1) end end end @@ -1053,7 +1052,7 @@ describe API::API, api: true do end it 'unstars the project' do - delete api("/projects/#{project.id}/star", user) + expect { delete api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(-1) expect(response.status).to eq(200) expect(json_response['star_count']).to eq(0) @@ -1062,10 +1061,9 @@ describe API::API, api: true do context 'on an unstarred project' do it 'does not modify the star count' do - delete api("/projects/#{project.id}/star", user) + expect { delete api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count } expect(response.status).to eq(304) - expect(project.star_count).to eq(0) end end end -- cgit v1.2.1 From bf31b4495e020ef5fa45b204d2494d8d3b4d1494 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 17:07:12 +0200 Subject: Schema improvements suggested by Yorick --- db/migrate/20160315135439_project_add_repository_check.rb | 4 +++- db/schema.rb | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/db/migrate/20160315135439_project_add_repository_check.rb b/db/migrate/20160315135439_project_add_repository_check.rb index 5a0859a30b2..8687d5d6296 100644 --- a/db/migrate/20160315135439_project_add_repository_check.rb +++ b/db/migrate/20160315135439_project_add_repository_check.rb @@ -1,6 +1,8 @@ class ProjectAddRepositoryCheck < ActiveRecord::Migration def change - add_column :projects, :last_repository_check_failed, :boolean, default: false + add_column :projects, :last_repository_check_failed, :boolean + add_index :projects, :last_repository_check_failed + add_column :projects, :last_repository_check_at, :datetime end end diff --git a/db/schema.rb b/db/schema.rb index db18d56f9cd..863e1f3f075 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -732,7 +732,7 @@ ActiveRecord::Schema.define(version: 20160412140240) do t.boolean "public_builds", default: true, null: false t.string "main_language" t.integer "pushes_since_gc", default: 0 - t.boolean "last_repository_check_failed", default: false + t.boolean "last_repository_check_failed" t.datetime "last_repository_check_at" end @@ -743,6 +743,7 @@ ActiveRecord::Schema.define(version: 20160412140240) do add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree + add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree add_index "projects", ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree -- cgit v1.2.1 From 970b2ed9da79b39123d2e21e6e6d5c70779e903c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 13 Apr 2016 16:10:03 +0100 Subject: Updated MR compare Based on in feedback in !3228 --- app/assets/stylesheets/pages/commits.scss | 5 ++++ app/views/projects/commits/_commit.html.haml | 2 +- .../projects/merge_requests/_new_compare.html.haml | 32 ++++++++-------------- .../merge_requests/dropdowns/_branch.html.haml | 5 ++++ .../merge_requests/dropdowns/_project.html.haml | 5 ++++ .../merge_requests/update_branches.html.haml | 8 ++---- 6 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 app/views/projects/merge_requests/dropdowns/_branch.html.haml create mode 100644 app/views/projects/merge_requests/dropdowns/_project.html.haml diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 6453c91d955..c8c6bbde084 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -75,6 +75,11 @@ li.commit { } } + .item-title { + display: inline-block; + max-width: 70%; + } + .commit-row-description { font-size: 14px; border-left: 1px solid #eee; diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 7da89231243..34f27f1e793 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -11,7 +11,7 @@ = cache(cache_key) do %li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" } .commit-row-title - %span.item-title.str-truncated + %span.item-title = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" - if commit.description? %a.text-expander.js-toggle-button ... diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index 7d7c487e970..3e2a37e050b 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -16,11 +16,9 @@ = dropdown_title("Select source project") = dropdown_filter("Search projects") = dropdown_content do - - is_active = f.object.source_project_id == @merge_request.source_project.id - %ul - %li - %a{ href: "#", class: "#{("is-active" if is_active)}", data: { id: @merge_request.source_project.id } } - = @merge_request.source_project_path + = render 'projects/merge_requests/dropdowns/project', + projects: [@merge_request.source_project], + selected: f.object.source_project_id .merge-request-select.dropdown = f.hidden_field :source_branch = dropdown_toggle "Select source branch", { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" } @@ -28,11 +26,9 @@ = dropdown_title("Select source branch") = dropdown_filter("Search branches") = dropdown_content do - %ul - - @merge_request.source_branches.each do |branch| - %li - %a{ href: "#", class: "#{("is-active" if f.object.source_branch == branch)}", data: { id: branch } } - = branch + = render 'projects/merge_requests/dropdowns/branch', + branches: @merge_request.source_branches, + selected: f.object.source_branch .panel-footer = icon('spinner spin', class: 'js-source-loading') %ul.list-unstyled.mr_source_commit @@ -50,11 +46,9 @@ = dropdown_title("Select target project") = dropdown_filter("Search projects") = dropdown_content do - %ul - - projects.each do |project| - %li - %a{ href: "#", class: "#{("is-active" if f.object.target_project_id == project.id)}", data: { id: project.id } } - = project.path_with_namespace + = render 'projects/merge_requests/dropdowns/project', + projects: projects, + selected: f.object.target_project_id .merge-request-select.dropdown = f.hidden_field :target_branch = dropdown_toggle f.object.target_branch, { toggle: "dropdown", field_name: "#{f.object_name}[target_branch]" }, { toggle_class: "js-compare-dropdown js-target-branch" } @@ -62,11 +56,9 @@ = dropdown_title("Select target branch") = dropdown_filter("Search branches") = dropdown_content do - %ul - - @merge_request.target_branches.each do |branch| - %li - %a{ href: "#", class: "#{("is-active" if f.object.target_branch == branch)}", data: { id: branch } } - = branch + = render 'projects/merge_requests/dropdowns/branch', + branches: @merge_request.target_branches, + selected: f.object.target_branch .panel-footer = icon('spinner spin', class: "js-target-loading") %ul.list-unstyled.mr_target_commit diff --git a/app/views/projects/merge_requests/dropdowns/_branch.html.haml b/app/views/projects/merge_requests/dropdowns/_branch.html.haml new file mode 100644 index 00000000000..ba8d9a5835c --- /dev/null +++ b/app/views/projects/merge_requests/dropdowns/_branch.html.haml @@ -0,0 +1,5 @@ +%ul + - branches.each do |branch| + %li + %a{ href: '#', class: "#{('is-active' if selected == branch)}", data: { id: branch } } + = branch diff --git a/app/views/projects/merge_requests/dropdowns/_project.html.haml b/app/views/projects/merge_requests/dropdowns/_project.html.haml new file mode 100644 index 00000000000..25d5dc92f8a --- /dev/null +++ b/app/views/projects/merge_requests/dropdowns/_project.html.haml @@ -0,0 +1,5 @@ +%ul + - projects.each do |project| + %li + %a{ href: "#", class: "#{('is-active' if selected == project.id)}", data: { id: project.id } } + = project.path_with_namespace diff --git a/app/views/projects/merge_requests/update_branches.html.haml b/app/views/projects/merge_requests/update_branches.html.haml index 1b93188a10c..64482973a89 100644 --- a/app/views/projects/merge_requests/update_branches.html.haml +++ b/app/views/projects/merge_requests/update_branches.html.haml @@ -1,5 +1,3 @@ -%ul - - @target_branches.each do |branch| - %li - %a{ href: "#", class: "#{("is-active" if "a" == branch)}", data: { id: branch } } - = branch += render 'projects/merge_requests/dropdowns/branch', +branches: @target_branches, +selected: nil -- cgit v1.2.1 From a9200d93d3e3d586302887fcaa0cf8f5fbd9a613 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 13 Apr 2016 16:30:20 +0200 Subject: Ensure that issues and merge requests are found --- doc/api/merge_requests.md | 1 + lib/api/issues.rb | 8 ++++---- spec/requests/api/issues_spec.rb | 6 +++--- spec/requests/api/merge_requests_spec.rb | 12 ++++++++++++ 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 3c18ebfa31e..2057f9d77aa 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -680,6 +680,7 @@ Example response: "subscribed": true } ``` + ## Unsubscribe from a merge request Unsubscribes the authenticated user from a merge request to not receive diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 37d25073074..4cdecadfe0f 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -239,8 +239,8 @@ module API # issue_id (required) - The ID of a project issue # Example Request: # POST /projects/:id/issues/:issue_id/subscription - post ":id/issues/:issue_id/subscription" do - issue = user_project.issues.find_by(id: params[:issue_id]) + post ':id/issues/:issue_id/subscription' do + issue = user_project.issues.find(params[:issue_id]) if issue.subscribed?(current_user) not_modified! @@ -257,8 +257,8 @@ module API # issue_id (required) - The ID of a project issue # Example Request: # DELETE /projects/:id/issues/:issue_id/subscription - delete ":id/issues/:issue_id/subscription" do - issue = user_project.issues.find_by(id: params[:issue_id]) + delete ':id/issues/:issue_id/subscription' do + issue = user_project.issues.find(params[:issue_id]) if issue.subscribed?(current_user) issue.unsubscribe(current_user) diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 8361a1649e0..86ea223f206 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -594,20 +594,20 @@ describe API::API, api: true do describe 'DELETE :id/issues/:issue_id/subscription' do it 'unsubscribes from an issue' do - post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) + delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) expect(response.status).to eq(200) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do - post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) + delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) expect(response.status).to eq(304) end it 'returns 404 if the issue is not found' do - post api("/projects/#{project.id}/issues/123/subscription", user) + delete api("/projects/#{project.id}/issues/123/subscription", user) expect(response.status).to eq(404) end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index c247fcf9c96..1fa7e76894f 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -529,6 +529,12 @@ describe API::API, api: true do expect(response.status).to eq(304) end + + it 'returns 404 if the merge request is not found' do + post api("/projects/#{project.id}/merge_requests/123/subscription", user) + + expect(response.status).to eq(404) + end end describe 'DELETE :id/merge_requests/:merge_request_id/subscription' do @@ -544,6 +550,12 @@ describe API::API, api: true do expect(response.status).to eq(304) end + + it 'returns 404 if the merge request is not found' do + post api("/projects/#{project.id}/merge_requests/123/subscription", user) + + expect(response.status).to eq(404) + end end def mr_with_later_created_and_updated_at_time -- cgit v1.2.1 From a5755812847d6866c51738a0b36927aa809b47d2 Mon Sep 17 00:00:00 2001 From: "P.S.V.R" Date: Wed, 17 Feb 2016 19:31:54 +0800 Subject: Use rugged to change HEAD --- CHANGELOG | 1 + app/models/project.rb | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 2ab0cc11248..42f73259fee 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -32,6 +32,7 @@ v 8.7.0 (unreleased) - Add default scope to projects to exclude projects pending deletion - Allow to close merge requests which source projects(forks) are deleted. - Ensure empty recipients are rejected in BuildsEmailService + - Use rugged to change HEAD in Project#change_head (P.S.V.R) - API: Ability to filter milestones by state `active` and `closed` (Robert Schilling) - API: Fix milestone filtering by `iid` (Robert Schilling) - API: Delete notes of issues, snippets, and merge requests (Robert Schilling) diff --git a/app/models/project.rb b/app/models/project.rb index fadc8bb2c9e..c4b0e484347 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -865,7 +865,9 @@ class Project < ActiveRecord::Base def change_head(branch) repository.before_change_head - gitlab_shell.update_repository_head(self.path_with_namespace, branch) + repository.rugged.references.create('HEAD', + "refs/heads/#{branch}", + force: true) reload_default_branch end -- cgit v1.2.1 From 31e28ebcebc054eaeef2eddba64ff2ff7ca3104f Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 12 Apr 2016 15:55:54 +0200 Subject: Load related MRs/branches asynchronously Currently this works by loading the HAML partials via XHR. While this is not the nicest setup it _is_ the easiest setup using the tools we currently have. Loading this data asynchronously doesn't make loading the related MRs/branches itself faster, it merely ensures that loading the issue itself is not slowed down. Fixes gitlab-org/gitlab-ce#14949 --- CHANGELOG | 1 + app/assets/javascripts/issue.js.coffee | 23 +++++++++++++++ app/controllers/projects/issues_controller.rb | 40 ++++++++++++++++++++------- app/views/projects/issues/show.html.haml | 8 ++++-- config/routes.rb | 2 ++ features/steps/shared/issuable.rb | 7 +++-- 6 files changed, 66 insertions(+), 15 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ed59bc1b252..adbcd8f8bcf 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.7.0 (unreleased) - All service classes (those residing in app/services) are now instrumented (Yorick Peterse) - Developers can now add custom tags to transactions (Yorick Peterse) + - Loading of an issue's referenced merge requests and related branches is now done asynchronously (Yorick Peterse) - Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea) - Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea) - All images in discussions and wikis now link to their source files !3464 (Connor Shea). diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index 946d83b7bdd..c7d74a12f99 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -10,6 +10,9 @@ class @Issue @initTaskList() @initIssueBtnEventListeners() + @initMergeRequests() + @initRelatedBranches() + initTaskList: -> $('.detail-page-description .js-task-list-container').taskList('enable') $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList @@ -69,3 +72,23 @@ class @Issue type: 'PATCH' url: $('form.js-issuable-update').attr('action') data: patchData + + initMergeRequests: -> + $container = $('#merge-requests') + + $.getJSON($container.data('url')) + .error -> + new Flash('Failed to load referenced merge requests', 'alert') + .success (data) -> + if 'html' of data + $container.html(data.html) + + initRelatedBranches: -> + $container = $('#related-branches') + + $.getJSON($container.data('url')) + .error -> + new Flash('Failed to load related branches', 'alert') + .success (data) -> + if 'html' of data + $container.html(data.html) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 6d649e72f84..c26cfeccf1d 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -3,7 +3,8 @@ class Projects::IssuesController < Projects::ApplicationController include IssuableActions before_action :module_enabled - before_action :issue, only: [:edit, :update, :show] + before_action :issue, + only: [:edit, :update, :show, :referenced_merge_requests, :related_branches] # Allow read any issue before_action :authorize_read_issue!, only: [:show] @@ -17,9 +18,6 @@ class Projects::IssuesController < Projects::ApplicationController # Allow issues bulk update before_action :authorize_admin_issues!, only: [:bulk_update] - # Cross-reference merge requests - before_action :closed_by_merge_requests, only: [:show] - respond_to :html def index @@ -65,8 +63,6 @@ class Projects::IssuesController < Projects::ApplicationController @note = @project.notes.new(noteable: @issue) @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue - @merge_requests = @issue.referenced_merge_requests(current_user) - @related_branches = @issue.related_branches - @merge_requests.map(&:source_branch) respond_to do |format| format.html @@ -118,15 +114,39 @@ class Projects::IssuesController < Projects::ApplicationController end end + def referenced_merge_requests + @merge_requests = @issue.referenced_merge_requests(current_user) + @closed_by_merge_requests = @issue.closed_by_merge_requests(current_user) + + respond_to do |format| + format.json do + render json: { + html: view_to_html_string('projects/issues/_merge_requests') + } + end + end + end + + def related_branches + merge_requests = @issue.referenced_merge_requests(current_user) + + @related_branches = @issue.related_branches - + merge_requests.map(&:source_branch) + + respond_to do |format| + format.json do + render json: { + html: view_to_html_string('projects/issues/_related_branches') + } + end + end + end + def bulk_update result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" }) end - def closed_by_merge_requests - @closed_by_merge_requests ||= @issue.closed_by_merge_requests(current_user) - end - protected def issue diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 6fa059cbe68..5fe5ddc0819 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -64,9 +64,11 @@ = @issue.description = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago') - .merge-requests - = render 'merge_requests' - = render 'related_branches' + #merge-requests{'data-url' => referenced_merge_requests_namespace_project_issue_url(@project.namespace, @project, @issue)} + // This element is filled in using JavaScript. + + #related-branches{'data-url' => related_branches_namespace_project_issue_url(@project.namespace, @project, @issue)} + // This element is filled in using JavaScript. .content-block.content-block-small = render 'new_branch' diff --git a/config/routes.rb b/config/routes.rb index 48601b7567b..688b83d2c95 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -701,6 +701,8 @@ Rails.application.routes.draw do resources :issues, constraints: { id: /\d+/ } do member do post :toggle_subscription + get :referenced_merge_requests + get :related_branches end collection do post :bulk_update diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb index b6d70a26c21..24b3fb6eacb 100644 --- a/features/steps/shared/issuable.rb +++ b/features/steps/shared/issuable.rb @@ -71,13 +71,16 @@ module SharedIssuable step 'I should not see any related merge requests' do page.within '.issue-details' do - expect(page).not_to have_content('.merge-requests') + expect(page).not_to have_content('#merge-requests .merge-requests-title') end end step 'I should see the "Enterprise fix" related merge request' do - page.within '.merge-requests' do + page.within '#merge-requests .merge-requests-title' do expect(page).to have_content('1 Related Merge Request') + end + + page.within '#merge-requests ul' do expect(page).to have_content('Enterprise fix') end end -- cgit v1.2.1 From 0a37976a4dcc88c7cbb746c89de122d3d10a453d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 13 Apr 2016 17:29:04 +0200 Subject: Updated InfluxDB/Grafana setup/import docs The grafana-dashboards repository now contains _all_ GitLab.com dashboards and thus requires some extra continuous queries to be set up. The repository now also provided a way to automatically import/export dashboards. [ci skip] --- .../performance/grafana_configuration.md | 48 ++++++++++++++-------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/doc/monitoring/performance/grafana_configuration.md b/doc/monitoring/performance/grafana_configuration.md index 10ef1009818..a79c8d48d3b 100644 --- a/doc/monitoring/performance/grafana_configuration.md +++ b/doc/monitoring/performance/grafana_configuration.md @@ -61,24 +61,32 @@ contents below and paste it in to the interactive session: ``` CREATE RETENTION POLICY gitlab_30d ON gitlab DURATION 30d REPLICATION 1 DEFAULT CREATE RETENTION POLICY seven_days ON gitlab DURATION 7d REPLICATION 1 -CREATE CONTINUOUS QUERY rails_transaction_timings_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS "duration_95th", percentile("duration", 99.000) AS "duration_99th", mean("duration") AS "duration_mean", percentile(sql_duration, 95.000) AS "sql_duration_95th", percentile(sql_duration, 99.000) AS "sql_duration_99th", mean(sql_duration) AS "sql_duration_mean", percentile(view_duration, 95.000) AS "view_duration_95th", percentile(view_duration, 99.000) AS "view_duration_99th", mean(view_duration) AS "view_duration_mean" INTO gitlab.seven_days.rails_transaction_timings FROM gitlab.gitlab_30d.rails_transactions GROUP BY time(1m) END -CREATE CONTINUOUS QUERY sidekiq_transaction_timings_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS "duration_95th", percentile("duration", 99.000) AS "duration_99th", mean("duration") AS "duration_mean", percentile(sql_duration, 95.000) AS "sql_duration_95th", percentile(sql_duration, 99.000) AS "sql_duration_99th", mean(sql_duration) AS "sql_duration_mean", percentile(view_duration, 95.000) AS "view_duration_95th", percentile(view_duration, 99.000) AS "view_duration_99th", mean(view_duration) AS "view_duration_mean" INTO gitlab.seven_days.sidekiq_transaction_timings FROM gitlab.gitlab_30d.sidekiq_transactions GROUP BY time(1m) END -CREATE CONTINUOUS QUERY rails_transaction_counts_seven_days ON gitlab BEGIN SELECT count("duration") AS "count" INTO gitlab.seven_days.rails_transaction_counts FROM gitlab.gitlab_30d.rails_transactions GROUP BY time(1m) END -CREATE CONTINUOUS QUERY sidekiq_transaction_counts_seven_days ON gitlab BEGIN SELECT count("duration") AS "count" INTO gitlab.seven_days.sidekiq_transaction_counts FROM gitlab.gitlab_30d.sidekiq_transactions GROUP BY time(1m) END -CREATE CONTINUOUS QUERY rails_method_call_timings_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS "duration_95th", percentile("duration", 99.000) AS "duration_99th", mean("duration") AS "duration_mean" INTO gitlab.seven_days.rails_method_call_timings FROM gitlab.gitlab_30d.rails_method_calls GROUP BY time(1m) END -CREATE CONTINUOUS QUERY sidekiq_method_call_timings_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS "duration_95th", percentile("duration", 99.000) AS "duration_99th", mean("duration") AS "duration_mean" INTO gitlab.seven_days.sidekiq_method_call_timings FROM gitlab.gitlab_30d.sidekiq_method_calls GROUP BY time(1m) END -CREATE CONTINUOUS QUERY rails_method_call_timings_per_method_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS duration_95th, percentile("duration", 99.000) AS duration_99th, mean("duration") AS duration_mean INTO gitlab.seven_days.rails_method_call_timings_per_method FROM gitlab.gitlab_30d.rails_method_calls GROUP BY time(1m), method END -CREATE CONTINUOUS QUERY sidekiq_method_call_timings_per_method_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS duration_95th, percentile("duration", 99.000) AS duration_99th, mean("duration") AS duration_mean INTO gitlab.seven_days.sidekiq_method_call_timings_per_method FROM gitlab.gitlab_30d.sidekiq_method_calls GROUP BY time(1m), method END -CREATE CONTINUOUS QUERY rails_memory_usage_per_minute ON gitlab BEGIN SELECT percentile(value, 95.000) AS memory_95th, percentile(value, 99.000) AS memory_99th, mean(value) AS memory_mean INTO gitlab.seven_days.rails_memory_usage_per_minute FROM gitlab.gitlab_30d.rails_memory_usage GROUP BY time(1m) END -CREATE CONTINUOUS QUERY sidekiq_memory_usage_per_minute ON gitlab BEGIN SELECT percentile(value, 95.000) AS memory_95th, percentile(value, 99.000) AS memory_99th, mean(value) AS memory_mean INTO gitlab.seven_days.sidekiq_memory_usage_per_minute FROM gitlab.gitlab_30d.sidekiq_memory_usage GROUP BY time(1m) END -CREATE CONTINUOUS QUERY sidekiq_file_descriptors_per_minute ON gitlab BEGIN SELECT sum(value) AS value INTO gitlab.seven_days.sidekiq_file_descriptors_per_minute FROM gitlab.gitlab_30d.sidekiq_file_descriptors GROUP BY time(1m) END -CREATE CONTINUOUS QUERY rails_file_descriptors_per_minute ON gitlab BEGIN SELECT sum(value) AS value INTO gitlab.seven_days.rails_file_descriptors_per_minute FROM gitlab.gitlab_30d.rails_file_descriptors GROUP BY time(1m) END -CREATE CONTINUOUS QUERY rails_gc_counts_per_minute ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.seven_days.rails_gc_counts_per_minute FROM gitlab.gitlab_30d.rails_gc_statistics GROUP BY time(1m) END -CREATE CONTINUOUS QUERY sidekiq_gc_counts_per_minute ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.seven_days.sidekiq_gc_counts_per_minute FROM gitlab.gitlab_30d.sidekiq_gc_statistics GROUP BY time(1m) END -CREATE CONTINUOUS QUERY rails_gc_timings_per_minute ON gitlab BEGIN SELECT percentile(total_time, 95.000) AS duration_95th, percentile(total_time, 99.000) AS duration_99th, mean(total_time) AS duration_mean INTO gitlab.seven_days.rails_gc_timings_per_minute FROM gitlab.gitlab_30d.rails_gc_statistics GROUP BY time(1m) END -CREATE CONTINUOUS QUERY sidekiq_gc_timings_per_minute ON gitlab BEGIN SELECT percentile(total_time, 95.000) AS duration_95th, percentile(total_time, 99.000) AS duration_99th, mean(total_time) AS duration_mean INTO gitlab.seven_days.sidekiq_gc_timings_per_minute FROM gitlab.gitlab_30d.sidekiq_gc_statistics GROUP BY time(1m) END -CREATE CONTINUOUS QUERY rails_gc_major_minor_per_minute ON gitlab BEGIN SELECT sum(major_gc_count) AS major, sum(minor_gc_count) AS minor INTO gitlab.seven_days.rails_gc_major_minor_per_minute FROM gitlab.gitlab_30d.rails_gc_statistics GROUP BY time(1m) END -CREATE CONTINUOUS QUERY sidekiq_gc_major_minor_per_minute ON gitlab BEGIN SELECT sum(major_gc_count) AS major, sum(minor_gc_count) AS minor INTO gitlab.seven_days.sidekiq_gc_major_minor_per_minute FROM gitlab.gitlab_30d.sidekiq_gc_statistics GROUP BY time(1m) END +CREATE CONTINUOUS QUERY rails_transaction_counts_seven_days ON gitlab BEGIN SELECT count("duration") AS "count" INTO gitlab.seven_days.rails_transaction_counts FROM rails_transactions GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY sidekiq_transaction_counts_seven_days ON gitlab BEGIN SELECT count("duration") AS "count" INTO gitlab.seven_days.sidekiq_transaction_counts FROM sidekiq_transactions GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY rails_method_call_timings_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS "duration_95th", percentile("duration", 99.000) AS "duration_99th", mean("duration") AS "duration_mean" INTO gitlab.seven_days.rails_method_call_timings FROM rails_method_calls GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY sidekiq_method_call_timings_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS "duration_95th", percentile("duration", 99.000) AS "duration_99th", mean("duration") AS "duration_mean" INTO gitlab.seven_days.sidekiq_method_call_timings FROM sidekiq_method_calls GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY rails_method_call_timings_per_method_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS duration_95th, percentile("duration", 99.000) AS duration_99th, mean("duration") AS duration_mean INTO gitlab.seven_days.rails_method_call_timings_per_method FROM rails_method_calls GROUP BY time(1m), method END; +CREATE CONTINUOUS QUERY sidekiq_method_call_timings_per_method_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS duration_95th, percentile("duration", 99.000) AS duration_99th, mean("duration") AS duration_mean INTO gitlab.seven_days.sidekiq_method_call_timings_per_method FROM sidekiq_method_calls GROUP BY time(1m), method END; +CREATE CONTINUOUS QUERY rails_memory_usage_per_minute ON gitlab BEGIN SELECT percentile(value, 95.000) AS memory_95th, percentile(value, 99.000) AS memory_99th, mean(value) AS memory_mean INTO gitlab.seven_days.rails_memory_usage_per_minute FROM rails_memory_usage GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY sidekiq_memory_usage_per_minute ON gitlab BEGIN SELECT percentile(value, 95.000) AS memory_95th, percentile(value, 99.000) AS memory_99th, mean(value) AS memory_mean INTO gitlab.seven_days.sidekiq_memory_usage_per_minute FROM sidekiq_memory_usage GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY sidekiq_file_descriptors_per_minute ON gitlab BEGIN SELECT sum(value) AS value INTO gitlab.seven_days.sidekiq_file_descriptors_per_minute FROM sidekiq_file_descriptors GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY rails_file_descriptors_per_minute ON gitlab BEGIN SELECT sum(value) AS value INTO gitlab.seven_days.rails_file_descriptors_per_minute FROM rails_file_descriptors GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY rails_gc_counts_per_minute ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.seven_days.rails_gc_counts_per_minute FROM rails_gc_statistics GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY sidekiq_gc_counts_per_minute ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.seven_days.sidekiq_gc_counts_per_minute FROM sidekiq_gc_statistics GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY rails_gc_timings_per_minute ON gitlab BEGIN SELECT percentile(total_time, 95.000) AS duration_95th, percentile(total_time, 99.000) AS duration_99th, mean(total_time) AS duration_mean INTO gitlab.seven_days.rails_gc_timings_per_minute FROM rails_gc_statistics GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY sidekiq_gc_timings_per_minute ON gitlab BEGIN SELECT percentile(total_time, 95.000) AS duration_95th, percentile(total_time, 99.000) AS duration_99th, mean(total_time) AS duration_mean INTO gitlab.seven_days.sidekiq_gc_timings_per_minute FROM sidekiq_gc_statistics GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY rails_gc_major_minor_per_minute ON gitlab BEGIN SELECT sum(major_gc_count) AS major, sum(minor_gc_count) AS minor INTO gitlab.seven_days.rails_gc_major_minor_per_minute FROM rails_gc_statistics GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY sidekiq_gc_major_minor_per_minute ON gitlab BEGIN SELECT sum(major_gc_count) AS major, sum(minor_gc_count) AS minor INTO gitlab.seven_days.sidekiq_gc_major_minor_per_minute FROM sidekiq_gc_statistics GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY grape_internal_allowed_request_counts_per_minute ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.seven_days.grape_internal_allowed_request_counts_per_minute FROM rails_transactions WHERE request_uri = '/api/v3/internal/allowed' GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY grape_internal_allowed_request_timings_per_minute ON gitlab BEGIN SELECT percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean("duration") AS duration_mean INTO gitlab.seven_days.grape_internal_allowed_request_timings_per_minute FROM rails_transactions WHERE request_uri = '/api/v3/internal/allowed' GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY grape_internal_allowed_sql_timings_per_minute ON gitlab BEGIN SELECT percentile(sql_duration, 95) AS duration_95th, percentile(sql_duration, 99) AS duration_99th, mean(sql_duration) AS duration_mean INTO gitlab.seven_days.grape_internal_allowed_sql_timings_per_minute FROM rails_transactions WHERE request_uri = '/api/v3/internal/allowed' GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY grape_internal_authorized_keys_request_counts_per_minute ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.seven_days.grape_internal_authorized_keys_request_counts_per_minute FROM rails_transactions WHERE request_uri = '/api/v3/internal/authorized_keys' GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY grape_internal_authorized_keys_request_timings_per_minute ON gitlab BEGIN SELECT percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean("duration") AS duration_mean INTO gitlab.seven_days.grape_internal_authorized_keys_request_timings_per_minute FROM rails_transactions WHERE request_uri = '/api/v3/internal/authorized_keys' GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY grape_internal_authorized_keys_sql_timings_per_minute ON gitlab BEGIN SELECT percentile(sql_duration, 95) AS duration_95th, percentile(sql_duration, 99) AS duration_99th, mean(sql_duration) AS duration_mean INTO gitlab.seven_days.grape_internal_authorized_keys_sql_timings_per_minute FROM rails_transactions WHERE request_uri = '/api/v3/internal/authorized_keys' GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY rails_transaction_timings_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS duration_95th, percentile("duration", 99.000) AS duration_99th, mean("duration") AS duration_mean, percentile(sql_duration, 95.000) AS sql_duration_95th, percentile(sql_duration, 99.000) AS sql_duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(view_duration, 95.000) AS view_duration_95th, percentile(view_duration, 99.000) AS view_duration_99th, mean(view_duration) AS view_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(cache_duration) AS cache_duration_mean INTO gitlab.seven_days.rails_transaction_timings FROM rails_transactions GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY sidekiq_transaction_timings_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS duration_95th, percentile("duration", 99.000) AS duration_99th, mean("duration") AS duration_mean, percentile(sql_duration, 95.000) AS sql_duration_95th, percentile(sql_duration, 99.000) AS sql_duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(view_duration, 95.000) AS view_duration_95th, percentile(view_duration, 99.000) AS view_duration_99th, mean(view_duration) AS view_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(cache_duration) AS cache_duration_mean INTO gitlab.seven_days.sidekiq_transaction_timings FROM sidekiq_transactions GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY grape_transaction_counts_seven_days ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.seven_days.grape_transaction_counts FROM rails_transactions WHERE action !~ /.+/ GROUP BY time(1m) END; +CREATE CONTINUOUS QUERY grape_transaction_timings_seven_days ON gitlab BEGIN SELECT percentile("duration", 95.000) AS duration_95th, percentile("duration", 99.000) AS duration_99th, mean("duration") AS duration_mean, percentile(sql_duration, 95.000) AS sql_duration_95th, percentile(sql_duration, 99.000) AS sql_duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(cache_duration) AS cache_duration_mean INTO gitlab.seven_days.grape_transaction_timings FROM rails_transactions WHERE action !~ /.+/ GROUP BY time(1m) END; ``` ## Import Dashboards @@ -106,6 +114,10 @@ navigate away. Repeat this process for each dashboard you wish to import. +Alternatively you can automatically import all the dashboards into your Grafana +instance. See the README of the [Grafana dashboards][grafana-dashboards] +repository for more information on this process. + [grafana-dashboards]: https://gitlab.com/gitlab-org/grafana-dashboards --- -- cgit v1.2.1 From 6f6d2d0ad5039fa8ace7e09c4f5867bb680b0f79 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 17:33:25 +0200 Subject: Use more conservative limits --- app/workers/repository_check/batch_worker.rb | 2 +- app/workers/repository_check/clear_worker.rb | 4 ++-- spec/workers/repository_check/batch_worker_spec.rb | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb index 16cd77a9bf0..44b3145d50f 100644 --- a/app/workers/repository_check/batch_worker.rb +++ b/app/workers/repository_check/batch_worker.rb @@ -35,7 +35,7 @@ module RepositoryCheck limit = 10_000 never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). pluck(:id) - old_check_projects = Project.where('last_repository_check_at < ?', 1.week.ago). + old_check_projects = Project.where('last_repository_check_at < ?', 1.month.ago). reorder('last_repository_check_at ASC').limit(limit).pluck(:id) never_checked_projects + old_check_projects end diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb index fe0cce9aab7..9c3347a7040 100644 --- a/app/workers/repository_check/clear_worker.rb +++ b/app/workers/repository_check/clear_worker.rb @@ -5,8 +5,8 @@ module RepositoryCheck sidekiq_options retry: false def perform - # Do batched updates because these updates will be slow and locking - Project.select(:id).find_in_batches(batch_size: 1000) do |batch| + # Do small batched updates because these updates will be slow and locking + Project.select(:id).find_in_batches(batch_size: 100) do |batch| Project.where(id: batch.map(&:id)).update_all( last_repository_check_failed: nil, last_repository_check_at: nil, diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb index 51caa645f47..f486e45ddad 100644 --- a/spec/workers/repository_check/batch_worker_spec.rb +++ b/spec/workers/repository_check/batch_worker_spec.rb @@ -5,17 +5,17 @@ describe RepositoryCheck::BatchWorker do it 'prefers projects that have never been checked' do projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + projects[0].update_column(:last_repository_check_at, 4.months.ago) + projects[2].update_column(:last_repository_check_at, 3.months.ago) expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) end it 'sorts projects by last_repository_check_at' do projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 2.weeks.ago) - projects[1].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + projects[0].update_column(:last_repository_check_at, 2.months.ago) + projects[1].update_column(:last_repository_check_at, 4.months.ago) + projects[2].update_column(:last_repository_check_at, 3.months.ago) expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) end @@ -23,7 +23,7 @@ describe RepositoryCheck::BatchWorker do it 'excludes projects that were checked recently' do projects = create_list(:project, 3) projects[0].update_column(:last_repository_check_at, 2.days.ago) - projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[1].update_column(:last_repository_check_at, 2.months.ago) projects[2].update_column(:last_repository_check_at, 3.days.ago) expect(subject.perform).to eq([projects[1].id]) -- cgit v1.2.1 From 28a7fe25fdf28042630282ace35e37310c8f0a12 Mon Sep 17 00:00:00 2001 From: Michael Greene Date: Tue, 5 Apr 2016 12:05:55 -0500 Subject: Allow back dating issues on update --- CHANGELOG | 2 +- doc/api/issues.md | 1 + lib/api/issues.rb | 7 +++++-- spec/requests/api/issues_spec.rb | 20 ++++++++++++++++---- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 21f24b5b61a..e5b8b9d7ce6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,11 +18,11 @@ v 8.7.0 (unreleased) - Preserve time notes/comments have been updated at when moving issue - Make HTTP(s) label consistent on clone bar (Stan Hu) - Expose label description in API (Mariusz Jachimowicz) - - Allow back dating on issues when created through the API - API: Ability to update a group (Robert Schilling) - API: Ability to move issues (Robert Schilling) - Fix Error 500 after renaming a project path (Stan Hu) - Fix a bug whith trailing slash in teamcity_url (Charles May) + - Allow back dating on issues when created or updated through the API - Fix avatar stretching by providing a cropping feature - API: Expose `subscribed` for issues and merge requests (Robert Schilling) - Allow SAML to handle external users based on user's information !3530 diff --git a/doc/api/issues.md b/doc/api/issues.md index 42024becc36..3e78149f442 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -298,6 +298,7 @@ PUT /projects/:id/issues/:issue_id | `milestone_id` | integer | no | The ID of a milestone to assign the issue to | | `labels` | string | no | Comma-separated label names for an issue | | `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it | +| `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` | ```bash curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85?state_event=close diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 4cdecadfe0f..8aa08fd5acc 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -117,7 +117,7 @@ module API # assignee_id (optional) - The ID of a user to assign issue # milestone_id (optional) - The ID of a milestone to assign issue # labels (optional) - The labels of an issue - # created_at (optional) - The date + # created_at (optional) - Date time string, ISO 8601 formatted # Example Request: # POST /projects/:id/issues post ":id/issues" do @@ -166,12 +166,15 @@ module API # milestone_id (optional) - The ID of a milestone to assign issue # labels (optional) - The labels of an issue # state_event (optional) - The state event of an issue (close|reopen) + # updated_at (optional) - Date time string, ISO 8601 formatted # Example Request: # PUT /projects/:id/issues/:issue_id put ":id/issues/:issue_id" do issue = user_project.issues.find(params[:issue_id]) authorize! :update_issue, issue - attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event] + keys = [:title, :description, :assignee_id, :milestone_id, :state_event] + keys << :updated_at if current_user.admin? || user_project.owner == current_user + attrs = attributes_for_keys(keys) # Validate label names in advance if (errors = validate_label_params(params)).any? diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 86ea223f206..f88e39cad9e 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -321,13 +321,13 @@ describe API::API, api: true do end context 'when an admin or owner makes the request' do - it "accepts the creation date to be set" do + it 'accepts the creation date to be set' do + creation_time = 2.weeks.ago post api("/projects/#{project.id}/issues", user), - title: 'new issue', labels: 'label, label2', created_at: 2.weeks.ago + title: 'new issue', labels: 'label, label2', created_at: creation_time expect(response.status).to eq(201) - # this take about a second, so probably not equal - expect(Time.parse(json_response['created_at'])).to be <= 2.weeks.ago + expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time) end end end @@ -478,6 +478,18 @@ describe API::API, api: true do expect(json_response['labels']).to include 'label2' expect(json_response['state']).to eq "closed" end + + context 'when an admin or owner makes the request' do + it 'accepts the update date to be set' do + update_time = 2.weeks.ago + put api("/projects/#{project.id}/issues/#{issue.id}", user), + labels: 'label3', state_event: 'close', updated_at: update_time + expect(response.status).to eq(200) + + expect(json_response['labels']).to include 'label3' + expect(Time.parse(json_response['updated_at'])).to be_within(1.second).of(update_time) + end + end end describe "DELETE /projects/:id/issues/:issue_id" do -- cgit v1.2.1 From c1467f5d97c04c22e2119ace084bb016f8f53d48 Mon Sep 17 00:00:00 2001 From: Michael Greene Date: Tue, 5 Apr 2016 13:04:11 -0500 Subject: Allow back dating notes on creation --- CHANGELOG | 1 + doc/api/notes.md | 1 + lib/api/notes.rb | 5 +++++ spec/requests/api/notes_spec.rb | 13 +++++++++++++ 4 files changed, 20 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index e5b8b9d7ce6..9baf6516ef6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,7 @@ v 8.7.0 (unreleased) - Fix Error 500 after renaming a project path (Stan Hu) - Fix a bug whith trailing slash in teamcity_url (Charles May) - Allow back dating on issues when created or updated through the API + - Allow back dating on issue notes when created through the API - Fix avatar stretching by providing a cropping feature - API: Expose `subscribed` for issues and merge requests (Robert Schilling) - Allow SAML to handle external users based on user's information !3530 diff --git a/doc/api/notes.md b/doc/api/notes.md index 2e0936f11b5..7aa1c2155bf 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -89,6 +89,7 @@ Parameters: - `id` (required) - The ID of a project - `issue_id` (required) - The ID of an issue - `body` (required) - The content of a note +- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z ### Modify existing issue note diff --git a/lib/api/notes.rb b/lib/api/notes.rb index a1c98f5e8ff..71a53e6f0d6 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -61,6 +61,7 @@ module API # id (required) - The ID of a project # noteable_id (required) - The ID of an issue or snippet # body (required) - The content of a note + # created_at (optional) - The date # Example Request: # POST /projects/:id/issues/:noteable_id/notes # POST /projects/:id/snippets/:noteable_id/notes @@ -73,6 +74,10 @@ module API noteable_id: params[noteable_id_str] } + if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user) + opts[:created_at] = params[:created_at] + end + @note = ::Notes::CreateService.new(user_project, current_user, opts).execute if @note.valid? diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index a467bc935af..ec9eda0a2ed 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -158,6 +158,19 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!' expect(response.status).to eq(401) end + + context 'when an admin or owner makes the request' do + it 'accepts the creation date to be set' do + creation_time = 2.weeks.ago + post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), + body: 'hi!', created_at: creation_time + expect(response.status).to eq(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) + expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time) + end + end + end context "when noteable is a Snippet" do -- cgit v1.2.1 From 2244aaf98f6c9b68e30febf6b80f2c0d965e541a Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 13 Apr 2016 13:20:57 +0300 Subject: Redis configuration consistency --- config/initializers/session_store.rb | 2 +- config/initializers/sidekiq.rb | 6 ++---- lib/gitlab/redis.rb | 2 ++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 70285255877..88cb859871c 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -14,7 +14,7 @@ if Rails.env.test? Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session" else redis_config = Gitlab::Redis.redis_store_options - redis_config[:namespace] = 'session:gitlab' + redis_config[:namespace] = Gitlab::Redis::SESSION_NAMESPACE Gitlab::Application.config.session_store( :redis_store, # Using the cookie_store would enable session replay attacks. diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 9182d929809..f1eec674888 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -1,9 +1,7 @@ -SIDEKIQ_REDIS_NAMESPACE = 'resque:gitlab' - Sidekiq.configure_server do |config| config.redis = { url: Gitlab::Redis.url, - namespace: SIDEKIQ_REDIS_NAMESPACE + namespace: Gitlab::Redis::SIDEKIQ_NAMESPACE } config.server_middleware do |chain| @@ -30,6 +28,6 @@ end Sidekiq.configure_client do |config| config.redis = { url: Gitlab::Redis.url, - namespace: SIDEKIQ_REDIS_NAMESPACE + namespace: Gitlab::Redis::SIDEKIQ_NAMESPACE } end diff --git a/lib/gitlab/redis.rb b/lib/gitlab/redis.rb index 319447669dc..5c352c96de5 100644 --- a/lib/gitlab/redis.rb +++ b/lib/gitlab/redis.rb @@ -1,6 +1,8 @@ module Gitlab class Redis CACHE_NAMESPACE = 'cache:gitlab' + SESSION_NAMESPACE = 'session:gitlab' + SIDEKIQ_NAMESPACE = 'resque:gitlab' attr_reader :url -- cgit v1.2.1 From 11f46b459e9d97900cb71f7f6bd313b5e18fbaa6 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 13 Apr 2016 15:28:10 -0300 Subject: Setup visibility level for project when transfering for a group --- app/services/projects/transfer_service.rb | 21 +++++++++++++++++---- app/views/projects/edit.html.haml | 1 + spec/services/projects/transfer_service_spec.rb | 23 +++++++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 2e734654466..0d8f8c6fbee 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -34,9 +34,12 @@ module Projects raise TransferError.new("Project with same path in target namespace already exists") end - # Apply new namespace id - project.namespace = new_namespace - project.save! + # Apply new namespace id and visibility level + project.tap do |p| + p.namespace = new_namespace + setup_visibility_level(p, new_namespace) + p.save! + end # Notifications project.send_move_instructions(old_path) @@ -56,7 +59,7 @@ module Projects Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) project.old_path_with_namespace = old_path - + SystemHooksService.new.execute_hooks_for(project, :transfer) true end @@ -68,5 +71,15 @@ module Projects namespace.id != project.namespace_id && current_user.can?(:create_projects, namespace) end + + private + + def setup_visibility_level(project, new_namespace) + return unless new_namespace.is_a?(Group) + + if project.visibility_level > new_namespace.visibility_level + project.visibility_level = new_namespace.visibility_level + end + end end end diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 6d872cd0b21..76a4f41193c 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -210,6 +210,7 @@ %li Be careful. Changing the project's namespace can have unintended side effects. %li You can only transfer the project to namespaces you manage. %li You will need to update your local repositories to point to the new location. + %li Project visibility level will be changed to match namespace rules when transfering to a group. .form-actions = f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) } - else diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index c46259431aa..06017317339 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -38,4 +38,27 @@ describe Projects::TransferService, services: true do def transfer_project(project, user, new_namespace) Projects::TransferService.new(project, user).execute(new_namespace) end + + context 'visibility level' do + let(:internal_group) { create(:group, :internal) } + + before { internal_group.add_owner(user) } + + context 'when namespace visibility level < project visibility level' do + let(:public_project) { create(:project, :public, namespace: user.namespace) } + + before { transfer_project(public_project, user, internal_group) } + + it { expect(public_project.visibility_level).to eq(internal_group.visibility_level) } + end + + context 'when namespace visibility level > project visibility level' do + let(:private_project) { create(:project, :private, namespace: user.namespace) } + + before { transfer_project(private_project, user, internal_group) } + + it { expect(private_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) } + end + end + end -- cgit v1.2.1 From 6b3a53848c68c8fd0931de9b7c6ab15b53b9475b Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 13 Apr 2016 15:30:50 -0300 Subject: Add changelog entry --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 5399e3e5b8b..416fffece63 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,7 @@ v 8.7.0 (unreleased) - All images in discussions and wikis now link to their source files !3464 (Connor Shea). - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu) - Add setting for customizing the list of trusted proxies !3524 + - Allow projects to be transfered to a lower visibility level group - Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524 - Improved Markdown rendering performance !3389 (Yorick Peterse) - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu) -- cgit v1.2.1 From f2aacc25fb5ded3c4aa70495d05aaad2cf9b23f8 Mon Sep 17 00:00:00 2001 From: Hyunwoo Jung Date: Wed, 13 Apr 2016 21:19:50 +0000 Subject: Fix typos in CI docs. --- doc/ci/quick_start/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 9aba4326e11..6a42a935abd 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -13,7 +13,7 @@ GitLab offers a [continuous integration][ci] service. If you and configure your GitLab project to use a [Runner], then each merge request or push triggers a build. -The `.gitlab-ci.yml` file tells the GitLab runner what do to. By default it +The `.gitlab-ci.yml` file tells the GitLab runner what to do. By default it runs three [stages]: `build`, `test`, and `deploy`. If everything runs OK (no non-zero return values), you'll get a nice green -- cgit v1.2.1 From a31c1dccb69e6c5999af31c2be4e6ee71043a5e3 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 13 Apr 2016 16:43:07 -0500 Subject: Add test to check if top right search form is not present --- spec/features/search_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 3e6289a46b1..029a11ea43c 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -10,6 +10,10 @@ describe "Search", feature: true do visit search_path end + it 'top right search form is not present' do + expect(page).not_to have_selector('.search') + end + describe 'searching for Projects' do it 'finds a project' do page.within '.search-holder' do -- cgit v1.2.1 From 897892132334f4004719d2489530898491f4fff6 Mon Sep 17 00:00:00 2001 From: Tom Downes Date: Thu, 14 Apr 2016 01:03:50 +0000 Subject: Update shibboleth configuration for GitLab 8.6 and Apache 2.4 --- doc/integration/shibboleth.md | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md index a0be3dd4e5c..b6b2d4e5e88 100644 --- a/doc/integration/shibboleth.md +++ b/doc/integration/shibboleth.md @@ -76,3 +76,50 @@ sudo gitlab-ctl reconfigure ``` On the sign in page there should now be a "Sign in with: Shibboleth" icon below the regular sign in form. Click the icon to begin the authentication process. You will be redirected to IdP server (Depends on your Shibboleth module configuration). If everything goes well the user will be returned to GitLab and will be signed in. + +## Apache 2.4 / GitLab 8.6 update +The order of the first 2 Location directives is important. If they are reversed, +you will not get a shibboleth session! + +``` + + Require all granted + ProxyPassReverse http://127.0.0.1:8181 + ProxyPassReverse http://YOUR_SERVER_FQDN/ + + + + AuthType shibboleth + ShibRequestSetting requireSession 1 + ShibUseHeaders On + Require shib-session + + + Alias /shibboleth-sp /usr/share/shibboleth + + + Require all granted + + + + SetHandler shib + + + RewriteEngine on + + #Don't escape encoded characters in api requests + RewriteCond %{REQUEST_URI} ^/api/v3/.* + RewriteCond %{REQUEST_URI} !/Shibboleth.sso + RewriteCond %{REQUEST_URI} !/shibboleth-sp + RewriteRule .* http://127.0.0.1:8181%{REQUEST_URI} [P,QSA,NE] + + #Forward all requests to gitlab-workhorse except existing files + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f [OR] + RewriteCond %{REQUEST_URI} ^/uploads/.* + RewriteCond %{REQUEST_URI} !/Shibboleth.sso + RewriteCond %{REQUEST_URI} !/shibboleth-sp + RewriteRule .* http://127.0.0.1:8181%{REQUEST_URI} [P,QSA] + + RequestHeader set X_FORWARDED_PROTO 'https' + RequestHeader set X-Forwarded-Ssl on +``` \ No newline at end of file -- cgit v1.2.1 From f5e8667fc5fd7f650704e9507edad48c7fbdef79 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 09:13:32 +0100 Subject: Fixed haml style issues --- app/views/projects/labels/_label.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index c7b4bb1f6e6..8bf544b8371 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -14,7 +14,7 @@ .label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}} .subscription-status{data: {status: label_subscription_status(label)}} - %button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: 'button', data: {toggle: "tooltip"} } + %button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: "button", data: { toggle: "tooltip" } } %span= label_subscription_toggle_button_text(label) - if can? current_user, :admin_label, @project -- cgit v1.2.1 From 2fd05aed463684203cf21923b54e28888fcad0ea Mon Sep 17 00:00:00 2001 From: Frank Groeneveld Date: Thu, 14 Apr 2016 10:24:09 +0200 Subject: Allow empty recipient list when pusher is added Closes #13574 --- CHANGELOG | 1 + .../project_services/builds_email_service.rb | 10 ++++-- .../project_services/builds_email_service_spec.rb | 37 ++++++++++++++++++++-- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9baf6516ef6..ba98b3b100c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -60,6 +60,7 @@ v 8.7.0 (unreleased) - API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling) - API: User can leave a project through the API when not master or owner. !3613 - Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu) + - Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld) v 8.6.6 - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index f9f04838766..6ab6d7417b7 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -23,7 +23,7 @@ class BuildsEmailService < Service prop_accessor :recipients boolean_accessor :add_pusher boolean_accessor :notify_only_broken_builds - validates :recipients, presence: true, if: :activated? + validates :recipients, presence: true, if: ->(s) { s.activated? && !s.add_pusher? } def initialize_properties if properties.nil? @@ -87,10 +87,14 @@ class BuildsEmailService < Service end def all_recipients(data) - all_recipients = recipients.split(',').compact.reject(&:blank?) + all_recipients = [] + + unless recipients.blank? + all_recipients += recipients.split(',').compact.reject(&:blank?) + end if add_pusher? && data[:user][:email] - all_recipients << "#{data[:user][:email]}" + all_recipients << data[:user][:email] end all_recipients diff --git a/spec/models/project_services/builds_email_service_spec.rb b/spec/models/project_services/builds_email_service_spec.rb index 2ccbff553f0..7c23c2efccd 100644 --- a/spec/models/project_services/builds_email_service_spec.rb +++ b/spec/models/project_services/builds_email_service_spec.rb @@ -3,9 +3,10 @@ require 'spec_helper' describe BuildsEmailService do let(:build) { create(:ci_build) } let(:data) { Gitlab::BuildDataBuilder.build(build) } - let(:service) { BuildsEmailService.new } + let!(:project) { create(:project, :public, ci_id: 1) } + let(:service) { described_class.new(project: project, active: true) } - describe :execute do + describe '#execute' do it 'sends email' do service.recipients = 'test@gitlab.com' data[:build_status] = 'failed' @@ -40,4 +41,36 @@ describe BuildsEmailService do 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 + end + end end -- cgit v1.2.1 From ec3f4f5bf06aa662b86079d815f9a68b556190f3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 14 Apr 2016 10:57:13 +0200 Subject: Projects on group page should be sorted by last activity instead of id/created_at Signed-off-by: Dmitriy Zaporozhets --- app/controllers/groups_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index c1adc999567..ee4fcc4e360 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -40,6 +40,7 @@ class GroupsController < Groups::ApplicationController @last_push = current_user.recent_push if current_user @projects = @projects.includes(:namespace) + @projects = @projects.sorted_by_activity @projects = filter_projects(@projects) @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.page(params[:page]) if params[:filter_projects].blank? -- cgit v1.2.1 From 82164a9f777f7eee37707b19ae6ec514a588c1ec Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 5 Apr 2016 10:17:00 +0100 Subject: Notes form JS update Updated the JS to have a standard class with standard actions for each form Created ability to have toolbar buttons that insert different prefixes dependant upon the data-prefix attribute --- app/assets/javascripts/gl_form.js.coffee | 40 ++++++++++++++++ app/assets/javascripts/gl_form_actions.js.coffee | 43 ++++++++++++++++++ app/assets/javascripts/notes.js.coffee | 58 ++++-------------------- app/views/projects/notes/_form.html.haml | 2 +- app/views/projects/notes/_hints.html.haml | 3 ++ 5 files changed, 95 insertions(+), 51 deletions(-) create mode 100644 app/assets/javascripts/gl_form.js.coffee create mode 100644 app/assets/javascripts/gl_form_actions.js.coffee diff --git a/app/assets/javascripts/gl_form.js.coffee b/app/assets/javascripts/gl_form.js.coffee new file mode 100644 index 00000000000..efa9284ed09 --- /dev/null +++ b/app/assets/javascripts/gl_form.js.coffee @@ -0,0 +1,40 @@ +class @GLForm + constructor: (@form) -> + @textarea = @form.find(".js-note-text") + + @setupForm() + + setupForm: -> + isNewForm = @form.is(':not(.gfm-form)') + + @form.removeClass "js-new-note-form" + + if isNewForm + @form.find('.div-dropzone').remove() + @form.addClass('gfm-form') + disableButtonIfEmptyField @form.find(".js-note-text"), @form.find(".js-comment-button") + + # remove notify commit author checkbox for non-commit notes + GitLab.GfmAutoComplete.setup() + new DropzoneInput(@form) + + autosize(@textarea) + + # Setup action buttons + actions = new GLFormActions @form, @textarea + @form.data 'form-actions', actions + + # form and textarea event listeners + @addEventListeners() + + # hide discard button + @form.find('.js-note-discard').hide() + + @form.show() + + addEventListeners: -> + @textarea.on 'focus', -> + $(@).closest('.md-area').addClass 'is-focused' + + @textarea.on 'blur', -> + $(@).closest('.md-area').removeClass 'is-focused' diff --git a/app/assets/javascripts/gl_form_actions.js.coffee b/app/assets/javascripts/gl_form_actions.js.coffee new file mode 100644 index 00000000000..d8de63a2be9 --- /dev/null +++ b/app/assets/javascripts/gl_form_actions.js.coffee @@ -0,0 +1,43 @@ +class @GLFormActions + constructor: (@form, @textarea) -> + @clearEventListeners() + @addEventListeners() + + clearEventListeners: -> + @form.off 'click', '.js-toolbar-button' + + addEventListeners: -> + @form.on 'click', '.js-toolbar-button', @toolbarButtonClick + + toolbarButtonClick: (e) => + $btn = $(e.currentTarget) + + # Get the prefix from the button + prefix = $btn.data('prefix') + @addPrefixToTextarea(prefix) + + addPrefixToTextarea: (prefix) -> + caretStart = @textarea.get(0).selectionStart + caretEnd = @textarea.get(0).selectionEnd + textEnd = @textarea.val().length + + beforeSelection = @textarea.val().substring 0, caretStart + afterSelection = @textarea.val().substring caretEnd, textEnd + + beforeSelectionSplit = beforeSelection.split '' + beforeSelectionLength = beforeSelection.length + + # Get the last character in the before selection + beforeSelectionLastChar = beforeSelectionSplit[beforeSelectionLength - 1] + + if beforeSelectionLastChar? and beforeSelectionLastChar isnt '' + # Append a white space char to the prefix if the previous char isn't a space + prefix = " #{prefix}" + + # Update the textarea + @textarea.val beforeSelection + prefix + afterSelection + @textarea.get(0).setSelectionRange caretStart + prefix.length, caretEnd + prefix.length + + # Focus the textarea + @textarea.focus() + @textarea.trigger('keyup') diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index a67890200dd..0581774424f 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -283,32 +283,10 @@ class @Notes show the form ### setupNoteForm: (form) -> - disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button") - form.removeClass "js-new-note-form" - form.find('.div-dropzone').remove() - - # hide discard button - form.find('.js-note-discard').hide() - - # setup preview buttons - previewButton = form.find(".js-md-preview-button") + new GLForm form textarea = form.find(".js-note-text") - textarea.on "input", -> - if $(this).val().trim() isnt "" - previewButton.removeClass("turn-off").addClass "turn-on" - else - previewButton.removeClass("turn-on").addClass "turn-off" - - textarea.on 'focus', -> - $(this).closest('.md-area').addClass 'is-focused' - - textarea.on 'blur', -> - $(this).closest('.md-area').removeClass 'is-focused' - - autosize(textarea) - new Autosave textarea, [ "Note" form.find("#note_commit_id").val() @@ -317,11 +295,6 @@ class @Notes form.find("#note_noteable_id").val() ] - # remove notify commit author checkbox for non-commit notes - GitLab.GfmAutoComplete.setup() - new DropzoneInput(form) - form.show() - ### Called in response to the new note form being submitted @@ -375,34 +348,15 @@ class @Notes note = $(this).closest(".note") note.addClass "is-editting" form = note.find(".note-edit-form") - isNewForm = form.is(':not(.gfm-form)') - if isNewForm - form.addClass('gfm-form') + form.addClass('current-note-edit-form') # Show the attachment delete link note.find(".js-note-attachment-delete").show() - # Setup markdown form - if isNewForm - GitLab.GfmAutoComplete.setup() - new DropzoneInput(form) - - textarea = form.find("textarea") - textarea.focus() + new GLForm form - if isNewForm - autosize(textarea) - - # HACK (rspeicher/DouweM): Work around a Chrome 43 bug(?). - # The textarea has the correct value, Chrome just won't show it unless we - # modify it, so let's clear it and re-set it! - value = textarea.val() - textarea.val "" - textarea.val value - - if isNewForm - disableButtonIfEmptyField textarea, form.find(".js-comment-button") + form.find(".js-note-text").focus() ### Called in response to clicking the edit note link @@ -570,6 +524,10 @@ class @Notes # only remove the form form.remove() + # Remove the note actions + actions = form.data('form-actions') + actions.clearEventListeners() + form.data('form-actions', null) cancelDiscussionForm: (e) => e.preventDefault() diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index c446ecec2c3..261eef4df4f 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new-note js-new-note-form js-quick-submit common-note-form gfm-form" }, authenticity_token: true do |f| += form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new-note js-new-note-form js-quick-submit common-note-form" }, authenticity_token: true do |f| = hidden_field_tag :view, diff_view = hidden_field_tag :line_type = note_target_fields(@note) diff --git a/app/views/projects/notes/_hints.html.haml b/app/views/projects/notes/_hints.html.haml index 0b002043408..0c6758210b1 100644 --- a/app/views/projects/notes/_hints.html.haml +++ b/app/views/projects/notes/_hints.html.haml @@ -1,4 +1,7 @@ .comment-toolbar.clearfix + %button.toolbar-button.js-toolbar-button{ type: 'button', data: { prefix: ':' }, tabindex: '-1' } + = icon('smile-o', class: 'toolbar-button-icon') + Emoji .toolbar-text Styling with = link_to 'Markdown', help_page_path('markdown', 'markdown'), target: '_blank', tabindex: -1 -- cgit v1.2.1 From 5dd01f572c9a7504f34c5a3b30792197c856bd6f Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 5 Apr 2016 10:28:10 +0100 Subject: Destroy discussion form --- app/assets/javascripts/gl_form.js.coffee | 22 +++++++++++++++++++--- app/assets/javascripts/notes.js.coffee | 8 +++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/gl_form.js.coffee b/app/assets/javascripts/gl_form.js.coffee index efa9284ed09..087a46cbac2 100644 --- a/app/assets/javascripts/gl_form.js.coffee +++ b/app/assets/javascripts/gl_form.js.coffee @@ -1,18 +1,30 @@ class @GLForm constructor: (@form) -> - @textarea = @form.find(".js-note-text") + @textarea = @form.find('.js-note-text') @setupForm() + @form.data 'gl-form', @ + + destroy: -> + # Destroy actions + actions = @form.data 'form-actions' + actions.clearEventListeners() + @form.data 'form-actions', null + + # Clean form listeners + @clearEventListeners() + @form.data 'gl-form', null + setupForm: -> isNewForm = @form.is(':not(.gfm-form)') - @form.removeClass "js-new-note-form" + @form.removeClass 'js-new-note-form' if isNewForm @form.find('.div-dropzone').remove() @form.addClass('gfm-form') - disableButtonIfEmptyField @form.find(".js-note-text"), @form.find(".js-comment-button") + disableButtonIfEmptyField @form.find('.js-note-text'), @form.find('.js-comment-button') # remove notify commit author checkbox for non-commit notes GitLab.GfmAutoComplete.setup() @@ -32,6 +44,10 @@ class @GLForm @form.show() + clearEventListeners: -> + @textarea.off 'focus' + @textarea.off 'blur' + addEventListeners: -> @textarea.on 'focus', -> $(@).closest('.md-area').addClass 'is-focused' diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 0581774424f..fa91baa07c0 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -513,6 +513,9 @@ class @Notes removeDiscussionNoteForm: (form)-> row = form.closest("tr") + glForm = form.data 'gl-form' + glForm.destroy() + form.find(".js-note-text").data("autosave").reset() # show the reply button (will only work for replies) @@ -524,11 +527,6 @@ class @Notes # only remove the form form.remove() - # Remove the note actions - actions = form.data('form-actions') - actions.clearEventListeners() - form.data('form-actions', null) - cancelDiscussionForm: (e) => e.preventDefault() form = $(e.target).closest(".js-discussion-note-form") -- cgit v1.2.1 From ff7fb09ea80036045e6fd17624b894f389de38b9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 5 Apr 2016 10:34:02 +0100 Subject: Updated issue form to use new GLForm --- app/assets/javascripts/dispatcher.js.coffee | 2 +- app/assets/javascripts/gl_form.js.coffee | 12 +++++++++--- app/views/projects/issues/_form.html.haml | 2 +- app/views/shared/issuable/_form.html.haml | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 70fd6f50e9c..6efbe214aad 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -35,7 +35,7 @@ class Dispatcher new Diff() when 'projects:issues:new','projects:issues:edit' shortcut_handler = new ShortcutsNavigation() - new DropzoneInput($('.issue-form')) + new GLForm($('.issue-form')) new IssuableForm($('.issue-form')) when 'projects:merge_requests:new', 'projects:merge_requests:edit' new Diff() diff --git a/app/assets/javascripts/gl_form.js.coffee b/app/assets/javascripts/gl_form.js.coffee index 087a46cbac2..aff3e909ce1 100644 --- a/app/assets/javascripts/gl_form.js.coffee +++ b/app/assets/javascripts/gl_form.js.coffee @@ -1,7 +1,11 @@ class @GLForm constructor: (@form) -> - @textarea = @form.find('.js-note-text') + @textarea = @form.find('textarea.js-gfm-input') + # Before we start, we should clean up any previous data for this form + @destroy() + + # Setup the form @setupForm() @form.data 'gl-form', @ @@ -9,8 +13,10 @@ class @GLForm destroy: -> # Destroy actions actions = @form.data 'form-actions' - actions.clearEventListeners() - @form.data 'form-actions', null + + if actions? + actions.clearEventListeners() + @form.data 'form-actions', null # Clean form listeners @clearEventListeners() diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 33c48199ba5..7076f5db015 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form js-quick-submit js-requires-input' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form common-note-form js-quick-submit js-requires-input' } do |f| = render 'shared/issuable/form', f: f, issuable: @issue :javascript diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 6de07a92cd7..d77c74e5d7a 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -29,7 +29,7 @@ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render 'projects/zen', f: f, attr: :description, - classes: 'description form-control' + classes: 'note-textarea' = render 'projects/notes/hints' .clearfix .error-alert -- cgit v1.2.1 From c45ca936c77eceff367e27e1bf8fd5f11c6a7c39 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 5 Apr 2016 10:39:16 +0100 Subject: merge request form uses new GLForm --- app/assets/javascripts/dispatcher.js.coffee | 2 +- app/views/projects/merge_requests/_form.html.haml | 2 +- app/views/projects/merge_requests/_new_submit.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 6efbe214aad..d02e1130704 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -40,7 +40,7 @@ class Dispatcher when 'projects:merge_requests:new', 'projects:merge_requests:edit' new Diff() shortcut_handler = new ShortcutsNavigation() - new DropzoneInput($('.merge-request-form')) + new GLForm($('.merge-request-form')) new IssuableForm($('.merge-request-form')) when 'projects:tags:new' new ZenMode() diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 1e6724fc92b..88525f4036a 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input js-quick-submit' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal common-note-form js-requires-input js-quick-submit' } do |f| = render 'shared/issuable/form', f: f, issuable: @merge_request :javascript diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 9e59f7df71b..2f14a91e64f 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -10,7 +10,7 @@ %span.pull-right = link_to 'Change branches', mr_change_branches_path(@merge_request) %hr -= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal common-note-form js-requires-input' } do |f| = render 'shared/issuable/form', f: f, issuable: @merge_request = f.hidden_field :source_project_id = f.hidden_field :source_branch -- cgit v1.2.1 From d67c3e35c98cff2372db803f93a97a60aa8b4104 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 5 Apr 2016 10:42:38 +0100 Subject: Milestones use new GLForm class --- app/assets/javascripts/dispatcher.js.coffee | 2 +- app/views/groups/milestones/new.html.haml | 4 ++-- app/views/projects/milestones/_form.html.haml | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index d02e1130704..a56019cd886 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -28,7 +28,7 @@ class Dispatcher new Todos() when 'projects:milestones:new', 'projects:milestones:edit' new ZenMode() - new DropzoneInput($('.milestone-form')) + new GLForm($('.milestone-form')) when 'groups:milestones:new' new ZenMode() when 'projects:compare:show' diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index 4290e0bf72e..caa8c4bc0ec 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -8,7 +8,7 @@ This will create milestone in every selected project %hr -= form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-quick-submit js-requires-input' } do |f| += form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form common-note-form js-quick-submit js-requires-input' } do |f| .row - if @milestone.errors.any? #error_explanation @@ -27,7 +27,7 @@ = f.label :description, "Description", class: "control-label" .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do - = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' + = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea' .clearfix .error-alert .form-group diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index b2dae1c70ee..06b21754797 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -1,6 +1,5 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-quick-submit js-requires-input'} do |f| += form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form common-note-form js-quick-submit js-requires-input'} do |f| = form_errors(@milestone) - .row .col-md-6 .form-group @@ -11,7 +10,7 @@ = f.label :description, "Description", class: "control-label" .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do - = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' + = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea' = render 'projects/notes/hints' .clearfix .error-alert -- cgit v1.2.1 From 3de09d1475db3d23f839991e57d16e00bff07cb4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 5 Apr 2016 12:33:30 +0100 Subject: Updated all GFM forms to use new GLForm class --- app/assets/javascripts/dispatcher.js.coffee | 6 +++--- app/assets/stylesheets/framework/gfm.scss | 18 ------------------ app/views/projects/_zen.html.haml | 4 ++-- app/views/projects/notes/_edit_form.html.haml | 2 +- app/views/projects/notes/_form.html.haml | 2 +- app/views/projects/releases/edit.html.haml | 12 ++++++------ app/views/projects/tags/new.html.haml | 6 +++--- app/views/projects/wikis/_form.html.haml | 4 ++-- app/views/shared/issuable/_form.html.haml | 3 ++- 9 files changed, 20 insertions(+), 37 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index a56019cd886..0b9110d35fa 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -44,10 +44,10 @@ class Dispatcher new IssuableForm($('.merge-request-form')) when 'projects:tags:new' new ZenMode() - new DropzoneInput($('.tag-form')) + new GLForm($('.tag-form')) when 'projects:releases:edit' new ZenMode() - new DropzoneInput($('.release-form')) + new GLForm($('.release-form')) when 'projects:merge_requests:show' new Diff() shortcut_handler = new ShortcutsIssuable(true) @@ -137,7 +137,7 @@ class Dispatcher new Wikis() shortcut_handler = new ShortcutsNavigation() new ZenMode() - new DropzoneInput($('.wiki-form')) + new GLForm($('.wiki-form')) when 'snippets' shortcut_handler = new ShortcutsNavigation() new ZenMode() if path[2] == 'show' diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss index 5ae0520fd7b..f4d35c4b4b1 100644 --- a/app/assets/stylesheets/framework/gfm.scss +++ b/app/assets/stylesheets/framework/gfm.scss @@ -1,24 +1,6 @@ /** * Styles that apply to all GFM related forms. */ -.issue-form, .merge-request-form, .wiki-form { - .description { - height: 16em; - border-top-left-radius: 0; - } -} - -.wiki-form { - .description { - height: 26em; - } -} - -.milestone-form { - .description { - height: 14em; - } -} .gfm-commit, .gfm-commit_range { font-family: $monospace_font; diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml index bddff5cdcbc..e1e35013968 100644 --- a/app/views/projects/_zen.html.haml +++ b/app/views/projects/_zen.html.haml @@ -1,8 +1,8 @@ .zen-backdrop - classes << ' js-gfm-input js-autosize markdown-area' - if defined?(f) && f - = f.text_area attr, class: classes, placeholder: "Write a comment or drag your files here..." + = f.text_area attr, class: classes, placeholder: placeholder - else - = text_area_tag attr, nil, class: classes, placeholder: "Write a comment or drag your files here..." + = text_area_tag attr, nil, class: classes, placeholder: placeholder %a.zen-cotrol.zen-control-leave.js-zen-leave{ href: "#" } = icon('compress') diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml index 23e4f93eab5..c87a3fadf72 100644 --- a/app/views/projects/notes/_edit_form.html.haml +++ b/app/views/projects/notes/_edit_form.html.haml @@ -2,7 +2,7 @@ = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true, html: { class: 'edit-note common-note-form js-quick-submit' } do |f| = note_target_fields(note) = render layout: 'projects/md_preview', locals: { preview_class: 'md-preview' } do - = render 'projects/zen', f: f, attr: :note, classes: 'note-textarea js-note-text js-task-list-field' + = render 'projects/zen', f: f, attr: :note, classes: 'note-textarea js-note-text js-task-list-field', placeholder: "Write a comment or drag your files here..." = render 'projects/notes/hints' .note-form-actions.clearfix diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 261eef4df4f..d0ac380f216 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -8,7 +8,7 @@ = f.hidden_field :noteable_type = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do - = render 'projects/zen', f: f, attr: :note, classes: 'note-textarea js-note-text' + = render 'projects/zen', f: f, attr: :note, classes: 'note-textarea js-note-text', placeholder: "Write a comment or drag your files here..." = render 'projects/notes/hints' .error-alert diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index c4a3f06ee06..6f0b32aa165 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -9,11 +9,11 @@ %strong #{@tag.name} .prepend-top-default - = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form js-quick-submit' }) do |f| + = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal common-note-form release-form js-quick-submit' }) do |f| = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do - = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' + = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here..." = render 'projects/notes/hints' - .error-alert - .form-actions.prepend-top-default - = f.submit 'Save changes', class: 'btn btn-save' - = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" + .error-alert + .form-actions.prepend-top-default + = f.submit 'Save changes', class: 'btn btn-save' + = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 77c7c4d23de..b40a6e5cb2d 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -10,7 +10,7 @@ New Tag %hr -= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form js-quick-submit js-requires-input" do += form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal common-note-form tag-form js-quick-submit js-requires-input" do .form-group = label_tag :tag_name, nil, class: 'control-label' .col-sm-10 @@ -30,9 +30,9 @@ = label_tag :release_description, 'Release notes', class: 'control-label' .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do - = render 'projects/zen', attr: :release_description, classes: 'description form-control' + = render 'projects/zen', attr: :release_description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here..." = render 'projects/notes/hints' - .help-block Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page. + .help-block Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page. .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 812876e2835..797a1a59e9f 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default js-quick-submit' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form common-note-form prepend-top-default js-quick-submit' } do |f| = form_errors(@page) = f.hidden_field :title, value: @page.title @@ -11,7 +11,7 @@ = f.label :content, class: 'control-label' .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do - = render 'projects/zen', f: f, attr: :content, classes: 'description form-control' + = render 'projects/zen', f: f, attr: :content, classes: 'note-textarea', placeholder: 'Write your content or drag files here...' = render 'projects/notes/hints' .clearfix diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index d77c74e5d7a..aed2622a6da 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -29,7 +29,8 @@ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render 'projects/zen', f: f, attr: :description, - classes: 'note-textarea' + classes: 'note-textarea', + placeholder: "Write a comment or drag your files here..." = render 'projects/notes/hints' .clearfix .error-alert -- cgit v1.2.1 From 6e93bd56f0147929006daba6410aa5640403b2c7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 5 Apr 2016 13:23:43 +0100 Subject: Placeholder on milestone form Updated JS spec to include gl_form --- app/views/projects/milestones/_form.html.haml | 2 +- spec/javascripts/notes_spec.js.coffee | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 06b21754797..687222fa92f 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -10,7 +10,7 @@ = f.label :description, "Description", class: "control-label" .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do - = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea' + = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: 'Write milestone description...' = render 'projects/notes/hints' .clearfix .error-alert diff --git a/spec/javascripts/notes_spec.js.coffee b/spec/javascripts/notes_spec.js.coffee index 050b6e362c6..dd160e821b3 100644 --- a/spec/javascripts/notes_spec.js.coffee +++ b/spec/javascripts/notes_spec.js.coffee @@ -1,4 +1,5 @@ #= require notes +#= require gl_form window.gon = {} window.disableButtonIfEmptyField = -> null -- cgit v1.2.1 From 7a1800fec87133e9971a83b457c969b5adaf5919 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 6 Apr 2016 09:49:29 +0100 Subject: Fixed group milestones placeholder bug --- app/views/groups/milestones/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index caa8c4bc0ec..7d9d27ae1fc 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -27,7 +27,7 @@ = f.label :description, "Description", class: "control-label" .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do - = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea' + = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: 'Write milestone description...' .clearfix .error-alert .form-group -- cgit v1.2.1 From 094cafcaa7c135a839a7329ed06fdd5d19a05f4b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 13 Apr 2016 08:46:30 +0100 Subject: Removed GL Actions class --- app/assets/javascripts/gl_form.js.coffee | 11 ------ app/assets/javascripts/gl_form_actions.js.coffee | 43 ------------------------ app/views/projects/notes/_hints.html.haml | 3 -- 3 files changed, 57 deletions(-) delete mode 100644 app/assets/javascripts/gl_form_actions.js.coffee diff --git a/app/assets/javascripts/gl_form.js.coffee b/app/assets/javascripts/gl_form.js.coffee index aff3e909ce1..d540cc4dc46 100644 --- a/app/assets/javascripts/gl_form.js.coffee +++ b/app/assets/javascripts/gl_form.js.coffee @@ -11,13 +11,6 @@ class @GLForm @form.data 'gl-form', @ destroy: -> - # Destroy actions - actions = @form.data 'form-actions' - - if actions? - actions.clearEventListeners() - @form.data 'form-actions', null - # Clean form listeners @clearEventListeners() @form.data 'gl-form', null @@ -38,10 +31,6 @@ class @GLForm autosize(@textarea) - # Setup action buttons - actions = new GLFormActions @form, @textarea - @form.data 'form-actions', actions - # form and textarea event listeners @addEventListeners() diff --git a/app/assets/javascripts/gl_form_actions.js.coffee b/app/assets/javascripts/gl_form_actions.js.coffee deleted file mode 100644 index d8de63a2be9..00000000000 --- a/app/assets/javascripts/gl_form_actions.js.coffee +++ /dev/null @@ -1,43 +0,0 @@ -class @GLFormActions - constructor: (@form, @textarea) -> - @clearEventListeners() - @addEventListeners() - - clearEventListeners: -> - @form.off 'click', '.js-toolbar-button' - - addEventListeners: -> - @form.on 'click', '.js-toolbar-button', @toolbarButtonClick - - toolbarButtonClick: (e) => - $btn = $(e.currentTarget) - - # Get the prefix from the button - prefix = $btn.data('prefix') - @addPrefixToTextarea(prefix) - - addPrefixToTextarea: (prefix) -> - caretStart = @textarea.get(0).selectionStart - caretEnd = @textarea.get(0).selectionEnd - textEnd = @textarea.val().length - - beforeSelection = @textarea.val().substring 0, caretStart - afterSelection = @textarea.val().substring caretEnd, textEnd - - beforeSelectionSplit = beforeSelection.split '' - beforeSelectionLength = beforeSelection.length - - # Get the last character in the before selection - beforeSelectionLastChar = beforeSelectionSplit[beforeSelectionLength - 1] - - if beforeSelectionLastChar? and beforeSelectionLastChar isnt '' - # Append a white space char to the prefix if the previous char isn't a space - prefix = " #{prefix}" - - # Update the textarea - @textarea.val beforeSelection + prefix + afterSelection - @textarea.get(0).setSelectionRange caretStart + prefix.length, caretEnd + prefix.length - - # Focus the textarea - @textarea.focus() - @textarea.trigger('keyup') diff --git a/app/views/projects/notes/_hints.html.haml b/app/views/projects/notes/_hints.html.haml index 0c6758210b1..0b002043408 100644 --- a/app/views/projects/notes/_hints.html.haml +++ b/app/views/projects/notes/_hints.html.haml @@ -1,7 +1,4 @@ .comment-toolbar.clearfix - %button.toolbar-button.js-toolbar-button{ type: 'button', data: { prefix: ':' }, tabindex: '-1' } - = icon('smile-o', class: 'toolbar-button-icon') - Emoji .toolbar-text Styling with = link_to 'Markdown', help_page_path('markdown', 'markdown'), target: '_blank', tabindex: -1 -- cgit v1.2.1 From c5d7b467ab3c67a71419e314c128d0ab5f371cb5 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 13 Apr 2016 09:08:34 +0100 Subject: Updated dropzone hover styling --- app/assets/javascripts/dropzone_input.js.coffee | 9 +++++---- app/assets/stylesheets/framework/markdown_area.scss | 13 +++---------- app/assets/stylesheets/framework/typography.scss | 8 -------- app/assets/stylesheets/pages/note_form.scss | 12 ++++++++++++ 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index b502131a99d..6eb8d27ee2b 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -15,11 +15,13 @@ class @DropzoneInput project_uploads_path = window.project_uploads_path or null max_file_size = gon.max_file_size or 10 - form_textarea = $(form).find("textarea.markdown-area") + form_textarea = $(form).find(".js-gfm-input") form_textarea.wrap "
" form_textarea.on 'paste', (event) => handlePaste(event) + $mdArea = $(form_textarea).closest('.md-area') + $(form).setupMarkdownPreview() form_dropzone = $(form).find('.div-dropzone') @@ -49,17 +51,16 @@ class @DropzoneInput $(".div-dropzone-alert").alert "close" dragover: -> - form_textarea.addClass "div-dropzone-focus" + $mdArea.addClass 'is-dropzone-hover' form.find(".div-dropzone-hover").css "opacity", 0.7 return dragleave: -> - form_textarea.removeClass "div-dropzone-focus" + $mdArea.removeClass 'is-dropzone-hover' form.find(".div-dropzone-hover").css "opacity", 0 return drop: -> - form_textarea.removeClass "div-dropzone-focus" form.find(".div-dropzone-hover").css "opacity", 0 form_textarea.focus() return diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index c8f86d60e3b..0f32d36d59c 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -1,22 +1,15 @@ .div-dropzone-wrapper { .div-dropzone { position: relative; - margin-bottom: -5px; - - .div-dropzone-focus { - border-color: #66afe9 !important; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6) !important; - outline: 0 !important; - } .div-dropzone-hover { position: absolute; top: 50%; left: 50%; - margin-top: -0.5em; - margin-left: -0.6em; + margin-top: -11.5px; + margin-left: -15px; opacity: 0; - font-size: 50px; + font-size: 30px; transition: opacity 200ms ease-in-out; pointer-events: none; } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 7b2aada5a0d..0a5b4b8834c 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -250,14 +250,6 @@ a > code { * Textareas intended for GFM * */ -.js-gfm-input { - font-family: $monospace_font; - color: $gl-text-color; -} - -.md-preview { -} - .strikethrough { text-decoration: line-through; } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index f4da17fadaa..0d92ebcc8e9 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -40,6 +40,7 @@ } .note-textarea { + display: block; padding: 10px 0; font-family: $regular_font; border: 0; @@ -72,6 +73,17 @@ } } + &.is-dropzone-hover { + border-color: $gl-success; + box-shadow: 0 0 2px rgba(#000, .2), + 0 0 4px rgba($gl-success, .4); + + .comment-toolbar, + .nav-links { + border-color: $gl-success; + } + } + p { code { white-space: normal; -- cgit v1.2.1 From 8ffb02d9f812c001514ff4c67e2eac53e1d9dd57 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 13 Apr 2016 09:09:10 +0100 Subject: Added CHANGELOG item --- CHANGELOG | 1 + app/assets/stylesheets/pages/note_form.scss | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ba98b3b100c..e5c8620bebf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -61,6 +61,7 @@ v 8.7.0 (unreleased) - API: User can leave a project through the API when not master or owner. !3613 - Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu) - Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld) + - Improved markdown forms v 8.6.6 - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 0d92ebcc8e9..2395de2e17f 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -64,7 +64,7 @@ &.is-focused { border-color: $focus-border-color; - box-shadow: 0 0 2px rgba(#000, .2), + box-shadow: 0 0 2px $black-transparent, 0 0 4px rgba($focus-border-color, .4); .comment-toolbar, @@ -75,7 +75,7 @@ &.is-dropzone-hover { border-color: $gl-success; - box-shadow: 0 0 2px rgba(#000, .2), + box-shadow: 0 0 2px $black-transparent, 0 0 4px rgba($gl-success, .4); .comment-toolbar, -- cgit v1.2.1 From ca01103dedd1a44937068c6834865881ade3d301 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 13 Apr 2016 18:14:01 +0100 Subject: Focus variable for dropzone --- app/assets/stylesheets/framework/variables.scss | 1 + app/assets/stylesheets/pages/note_form.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 0b6be86ce6a..fcf1f2193b1 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -150,6 +150,7 @@ $light-grey-header: #faf9f9; */ $gl-primary: $blue-normal; $gl-success: $green-normal; +$gl-success-focus: rgba($gl-success, .4) $gl-info: $blue-normal; $gl-warning: $orange-normal; $gl-danger: $red-normal; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 2395de2e17f..07c707e7b77 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -76,7 +76,7 @@ &.is-dropzone-hover { border-color: $gl-success; box-shadow: 0 0 2px $black-transparent, - 0 0 4px rgba($gl-success, .4); + 0 0 4px $gl-success-focus; .comment-toolbar, .nav-links { -- cgit v1.2.1 From 0d8462b897847ae87b131d5111b8772848ba45ab Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 09:11:38 +0100 Subject: Fixed scss syntax issue --- app/assets/stylesheets/framework/variables.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index fcf1f2193b1..f910cf61817 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -150,7 +150,7 @@ $light-grey-header: #faf9f9; */ $gl-primary: $blue-normal; $gl-success: $green-normal; -$gl-success-focus: rgba($gl-success, .4) +$gl-success-focus: rgba($gl-success, .4); $gl-info: $blue-normal; $gl-warning: $orange-normal; $gl-danger: $red-normal; -- cgit v1.2.1 From efff7e9e54e1d1ebbb36dc8dca35b894aeab2630 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 14 Apr 2016 12:03:45 +0200 Subject: updated migration based on testing findings --- ...302152808_remove_wrong_import_url_from_projects.rb | 19 +++++++++++++------ lib/gitlab/import_url.rb | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index b97b79f920d..8a351cf27a3 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -23,10 +23,14 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration def process_projects_with_wrong_url projects_with_wrong_import_url.each do |project| - import_url = Gitlab::ImportUrl.new(project["import_url"]) + begin + import_url = Gitlab::ImportUrl.new(project["import_url"]) - update_import_url(import_url, project) - update_import_data(import_url, project) + update_import_url(import_url, project) + update_import_data(import_url, project) + rescue URI::InvalidURIError + nullify_import_url(project) + end end end @@ -82,6 +86,10 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration execute("UPDATE projects SET import_url = #{quote(import_url.sanitized_url)} WHERE id = #{project['id']}") end + def nullify_import_url(project) + execute("UPDATE projects SET import_url = NULL WHERE id = #{project['id']}") + end + def insert_import_data_sql(project_id, fake_import_data) %( INSERT INTO project_import_data @@ -108,14 +116,13 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration end #GitHub projects with token, and any user:password@ based URL - #TODO: may need to add import_type != list def projects_with_wrong_import_url - select_all("SELECT p.id, p.import_url, i.id as import_data_id FROM projects p LEFT JOIN project_import_data i on p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_url LIKE '%//%@%'") + select_all("SELECT p.id, p.import_url, i.id as import_data_id FROM projects p LEFT JOIN project_import_data i on p.id = i.project_id WHERE p.import_url <> '' AND p.import_url LIKE '%//%@%'") end # All imports with data for import_type def unencrypted_import_data(import_type: ) - select_all("SELECT i.id, p.import_url, i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url IS NOT NULL AND p.import_type = '#{import_type}' ") + select_all("SELECT i.id, p.import_url, i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url <> '' AND p.import_type = '#{import_type}' ") end def quote(value) diff --git a/lib/gitlab/import_url.rb b/lib/gitlab/import_url.rb index 3cfbc17b89b..d23b013c1f5 100644 --- a/lib/gitlab/import_url.rb +++ b/lib/gitlab/import_url.rb @@ -1,7 +1,7 @@ module Gitlab class ImportUrl def initialize(url, credentials: nil) - @url = URI.parse(url) + @url = URI.parse(URI.encode(url)) @credentials = credentials end -- cgit v1.2.1 From 8a8d4c9bf27d17939bfce4b53287210d8108da2c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 11 Apr 2016 13:19:55 +0100 Subject: Diff viewer links to correct part of the file Added highlight colours to diff rows Closes #13852 --- .../javascripts/merge_request_tabs.js.coffee | 28 ++++++++++++++++++---- app/assets/stylesheets/highlight/dark.scss | 6 +++++ app/assets/stylesheets/highlight/monokai.scss | 6 +++++ .../stylesheets/highlight/solarized_dark.scss | 6 +++++ .../stylesheets/highlight/solarized_light.scss | 6 +++++ app/assets/stylesheets/highlight/white.scss | 6 +++++ app/views/projects/diffs/_parallel_view.html.haml | 8 +++---- 7 files changed, 58 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index ef0b534a709..d45c772b8de 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -85,8 +85,11 @@ class @MergeRequestTabs scrollToElement: (container) -> if window.location.hash - $el = $("div#{container} #{window.location.hash}") - $('body').scrollTo($el.offset().top) if $el.length + navBarHeight = $('.navbar-gitlab').outerHeight() + + $el = $("#{container} #{window.location.hash}") + $('body').scrollTo 0 + $.scrollTo("#{container} #{window.location.hash}", offset: -navBarHeight) if $el.length # Activate a tab based on the current action activateTab: (action) -> @@ -152,12 +155,29 @@ class @MergeRequestTabs @_get url: "#{source}.json" + @_location.search success: (data) => - document.querySelector("div#diffs").innerHTML = data.html + $('#diffs').html data.html gl.utils.localTimeAgo($('.js-timeago', 'div#diffs')) - $('div#diffs .js-syntax-highlight').syntaxHighlight() + $('#diffs .js-syntax-highlight').syntaxHighlight() @expandViewContainer() if @diffViewType() is 'parallel' @diffsLoaded = true @scrollToElement("#diffs") + @highlighSelectedLine() + + highlighSelectedLine: -> + locationHash = location.hash + + if locationHash isnt '' + hashClassString = ".#{locationHash.replace('#', '')}" + $diffLine = $(locationHash) + + if $diffLine.is ':not(tr)' + $diffLine = $("td#{locationHash}, td#{hashClassString}") + else + $diffLine = $('td', $diffLine) + + $diffLine.addClass 'hll' + diffLineTop = $diffLine.offset().top + navBarHeight = $('.navbar-gitlab').outerHeight() loadBuilds: (source) -> return if @buildsLoaded diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index 47673944896..77a73dc379b 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -21,6 +21,12 @@ // Diff line .line_holder { + td.diff-line-num.hll:not(.empty-cell), + td.line_content.hll:not(.empty-cell) { + background-color: #557; + border-color: darken(#557, 15%); + } + .diff-line-num.new, .line_content.new { @include diff_background(rgba(51, 255, 51, 0.1), rgba(51, 255, 51, 0.2), #808080); } diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index 806401c21ae..28253d4ccb4 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -21,6 +21,12 @@ // Diff line .line_holder { + td.diff-line-num.hll:not(.empty-cell), + td.line_content.hll:not(.empty-cell) { + background-color: #49483e; + border-color: darken(#49483e, 15%); + } + .diff-line-num.new, .line_content.new { @include diff_background(rgba(166, 226, 46, 0.1), rgba(166, 226, 46, 0.15), #808080); } diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index 6a809d4dfd2..c62bd021aef 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -21,6 +21,12 @@ // Diff line .line_holder { + td.diff-line-num.hll:not(.empty-cell), + td.line_content.hll:not(.empty-cell) { + background-color: #174652; + border-color: darken(#174652, 15%); + } + .diff-line-num.new, .line_content.new { @include diff_background(rgba(133, 153, 0, 0.15), rgba(133, 153, 0, 0.25), #113b46); } diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss index c482a1258f7..524cfaf90c3 100644 --- a/app/assets/stylesheets/highlight/solarized_light.scss +++ b/app/assets/stylesheets/highlight/solarized_light.scss @@ -21,6 +21,12 @@ // Diff line .line_holder { + td.diff-line-num.hll:not(.empty-cell), + td.line_content.hll:not(.empty-cell) { + background-color: #ddd8c5; + border-color: darken(#ddd8c5, 15%); + } + .diff-line-num.new, .line_content.new { @include diff_background(rgba(133, 153, 0, 0.2), rgba(133, 153, 0, 0.25), #c5d0d4); } diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 28331f59754..1ff6ad75e07 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -21,6 +21,12 @@ // Diff line .line_holder { + td.diff-line-num.hll:not(.empty-cell), + td.line_content.hll:not(.empty-cell) { + background-color: #f8eec7; + border-color: darken(#f8eec7, 15%); + } + .diff-line-num { &.old { background-color: $line-number-old; diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index d7c49068745..81948513e43 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -14,11 +14,11 @@ %td.new_line.diff-line-num %td.line_content.parallel.match= left[:text] - else - %td.old_line.diff-line-num{id: left[:line_code], class: "#{left[:type]}"} + %td.old_line.diff-line-num{id: left[:line_code], class: "#{left[:type]} #{'empty-cell' if !left[:number]}"} = link_to raw(left[:number]), "##{left[:line_code]}", id: left[:line_code] - if @comments_allowed && can?(current_user, :create_note, @project) = link_to_new_diff_note(left[:line_code], 'old') - %td.line_content{class: "parallel noteable_line #{left[:type]} #{left[:line_code]}", data: { line_code: left[:line_code] }}= diff_line_content(left[:text]) + %td.line_content{class: "parallel noteable_line #{left[:type]} #{left[:line_code]} #{'empty-cell' if left[:text].empty?}", data: { line_code: left[:line_code] }}= diff_line_content(left[:text]) - if right[:type] == 'new' - new_line_class = 'new' @@ -27,11 +27,11 @@ - new_line_class = nil - new_line_code = left[:line_code] - %td.new_line.diff-line-num{id: new_line_code, class: "#{new_line_class}", data: { linenumber: right[:number] }} + %td.new_line.diff-line-num{id: new_line_code, class: "#{new_line_class} #{'empty-cell' if !right[:number]}", data: { linenumber: right[:number] }} = link_to raw(right[:number]), "##{new_line_code}", id: new_line_code - if @comments_allowed && can?(current_user, :create_note, @project) = link_to_new_diff_note(right[:line_code], 'new') - %td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code}", data: { line_code: new_line_code }}= diff_line_content(right[:text]) + %td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code} #{'empty-cell' if right[:text].empty?}", data: { line_code: new_line_code }}= diff_line_content(right[:text]) - if @reply_allowed - comments_left, comments_right = organize_comments(left[:type], right[:type], left[:line_code], right[:line_code]) -- cgit v1.2.1 From f3ed4763f54ee46a50d2e7b3b894f895b4067b61 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Apr 2016 09:07:20 +0100 Subject: Correctly scrolls to the line when clicking --- app/assets/javascripts/merge_request_tabs.js.coffee | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index d45c772b8de..316d1fefa5a 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -88,7 +88,6 @@ class @MergeRequestTabs navBarHeight = $('.navbar-gitlab').outerHeight() $el = $("#{container} #{window.location.hash}") - $('body').scrollTo 0 $.scrollTo("#{container} #{window.location.hash}", offset: -navBarHeight) if $el.length # Activate a tab based on the current action @@ -163,8 +162,17 @@ class @MergeRequestTabs @scrollToElement("#diffs") @highlighSelectedLine() + $(document) + .off 'click', '.diff-content a' + .on 'click', '.diff-content a', (e) => + e.preventDefault() + window.location.hash = $(e.currentTarget).attr 'href' + @highlighSelectedLine() + @scrollToElement("#diffs") + highlighSelectedLine: -> - locationHash = location.hash + $('.hll').removeClass 'hll' + locationHash = window.location.hash if locationHash isnt '' hashClassString = ".#{locationHash.replace('#', '')}" -- cgit v1.2.1 From fd9c3b7c7446d9b8513793606aa2ae0d980ad4b7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Apr 2016 10:06:35 +0100 Subject: Fixed issue with other links being clicked in diffs --- app/assets/javascripts/merge_request_tabs.js.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 316d1fefa5a..1ab6e5114bc 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -163,8 +163,8 @@ class @MergeRequestTabs @highlighSelectedLine() $(document) - .off 'click', '.diff-content a' - .on 'click', '.diff-content a', (e) => + .off 'click', '.diff-line-num a' + .on 'click', '.diff-line-num a', (e) => e.preventDefault() window.location.hash = $(e.currentTarget).attr 'href' @highlighSelectedLine() -- cgit v1.2.1 From badb35335086b90e2522ea2da767830f2f3ba9a7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 13 Apr 2016 09:23:28 +0100 Subject: Added CHANGELOG item --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index e5c8620bebf..511889b3765 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -62,6 +62,8 @@ v 8.7.0 (unreleased) - Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu) - Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld) - Improved markdown forms + - Diffs load at the correct point when linking from from number + - Selected diff rows highlight v 8.6.6 - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) -- cgit v1.2.1 From 072b9c2c8fe466d43834c16bfbb565043a033fdf Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 14 Apr 2016 15:40:05 +0300 Subject: Add TOC to yaml README and an intro section [ci skip] --- doc/ci/yaml/README.md | 73 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 7da9b31e30d..abb6e97e5e6 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1,5 +1,47 @@ # Configuration of your builds with .gitlab-ci.yml +This document describes the usage of `.gitlab-ci.yml`, the file that is used by +GitLab Runner to manage your project's builds. + +If you want a quick introduction to GitLab CI, follow our +[quick start guide](../quick_start/README.md). + +--- + + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [.gitlab-ci.yml](#gitlab-ci-yml) + - [image and services](#image-and-services) + - [before_script](#before_script) + - [stages](#stages) + - [types](#types) + - [variables](#variables) + - [cache](#cache) + - [cache:key](#cache-key) +- [Jobs](#jobs) + - [script](#script) + - [stage](#stage) + - [only and except](#only-and-except) + - [tags](#tags) + - [when](#when) + - [artifacts](#artifacts) + - [artifacts:name](#artifacts-name) + - [dependencies](#dependencies) +- [Hidden jobs](#hidden-jobs) +- [Special YAML features](#special-yaml-features) + - [Anchors](#anchors) +- [Validate the .gitlab-ci.yml](#validate-the-gitlab-ci-yml) +- [Skipping builds](#skipping-builds) +- [Examples](#examples) + + + +--- + +## .gitlab-ci.yml + From version 7.12, GitLab CI uses a [YAML](https://en.wikipedia.org/wiki/YAML) file (`.gitlab-ci.yml`) for the project configuration. It is placed in the root of your repository and contains definitions of how your project should be built. @@ -23,12 +65,10 @@ Of course a command can execute code directly (`./configure;make;make install`) or run a script (`test.sh`) in the repository. Jobs are used to create builds, which are then picked up by -[runners](../runners/README.md) and executed within the environment of the -runner. What is important, is that each job is run independently from each +[Runners](../runners/README.md) and executed within the environment of the +Runner. What is important, is that each job is run independently from each other. -## .gitlab-ci.yml - The YAML syntax allows for using more complex job specifications than in the above example: @@ -71,7 +111,7 @@ There are a few reserved `keywords` that **cannot** be used as job names: This allows to specify a custom Docker image and a list of services that can be used for time of the build. The configuration of this feature is covered in -separate document: [Use Docker](../docker/README.md). +[a separate document](../docker/README.md). ### before_script @@ -86,7 +126,8 @@ The specification of `stages` allows for having flexible multi stage pipelines. The ordering of elements in `stages` defines the ordering of builds' execution: 1. Builds of the same stage are run in parallel. -1. Builds of next stage are run after success. +1. Builds of the next stage are run after the jobs from the previous stage + complete successfully. Let's consider the following example, which defines 3 stages: @@ -98,9 +139,9 @@ stages: ``` 1. First all jobs of `build` are executed in parallel. -1. If all jobs of `build` succeeds, the `test` jobs are executed in parallel. -1. If all jobs of `test` succeeds, the `deploy` jobs are executed in parallel. -1. If all jobs of `deploy` succeeds, the commit is marked as `success`. +1. If all jobs of `build` succeed, the `test` jobs are executed in parallel. +1. If all jobs of `test` succeed, the `deploy` jobs are executed in parallel. +1. If all jobs of `deploy` succeed, the commit is marked as `success`. 1. If any of the previous jobs fails, the commit is marked as `failed` and no jobs of further stage are executed. @@ -278,14 +319,14 @@ job_name: | Keyword | Required | Description | |---------------|----------|-------------| -| script | yes | Defines a shell script which is executed by runner | +| script | yes | Defines a shell script which is executed by Runner | | image | no | Use docker image, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) | | services | no | Use docker services, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) | | stage | no | Defines a build stage (default: `test`) | | type | no | Alias for `stage` | | only | no | Defines a list of git refs for which build is created | | except | no | Defines a list of git refs for which build is not created | -| tags | no | Defines a list of tags which are used to select runner | +| tags | no | Defines a list of tags which are used to select Runner | | allow_failure | no | Allow build to fail. Failed build doesn't contribute to commit status | | when | no | Define when to run build. Can be `on_success`, `on_failure` or `always` | | dependencies | no | Define other builds that a build depends on so that you can pass artifacts between them| @@ -294,7 +335,7 @@ job_name: ### script -`script` is a shell script which is executed by the runner. For example: +`script` is a shell script which is executed by the Runner. For example: ```yaml job: @@ -375,13 +416,13 @@ except master. ### tags -`tags` is used to select specific runners from the list of all runners that are +`tags` is used to select specific Runners from the list of all Runners that are allowed to run this project. -During the registration of a runner, you can specify the runner's tags, for +During the registration of a Runner, you can specify the Runner's tags, for example `ruby`, `postgres`, `development`. -`tags` allow you to run builds with runners that have the specified tags +`tags` allow you to run builds with Runners that have the specified tags assigned to them: ```yaml @@ -391,7 +432,7 @@ job: - postgres ``` -The specification above, will make sure that `job` is built by a runner that +The specification above, will make sure that `job` is built by a Runner that has both `ruby` AND `postgres` tags defined. ### when -- cgit v1.2.1 From 0385cd5a585572be4d3b72797c14cad23efc48f5 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 13 Apr 2016 21:20:03 +0200 Subject: Start with iid on branch creation --- app/models/issue.rb | 4 ++-- app/services/merge_requests/build_service.rb | 2 +- app/services/system_note_service.rb | 2 +- doc/workflow/web_editor.md | 2 +- spec/models/issue_spec.rb | 17 ++++++++++++----- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/models/issue.rb b/app/models/issue.rb index e064b0f8b95..3f188e04770 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -106,7 +106,7 @@ class Issue < ActiveRecord::Base def related_branches project.repository.branch_names.select do |branch| - branch.end_with?("-#{iid}") + branch =~ /\A#{iid}-(?!\d+-stable)/i end end @@ -151,7 +151,7 @@ class Issue < ActiveRecord::Base end def to_branch_name - "#{title.parameterize}-#{iid}" + "#{iid}-#{title.parameterize}" end def can_be_worked_on?(current_user) diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index 6e9152e444e..fa34753c4fd 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -51,7 +51,7 @@ module MergeRequests # 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(/-(\d+)\z/) + if match = merge_request.source_branch.match(/\A(\d+)-/) iid = match[1] closes_issue = "Closes ##{iid}" diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 658b086496f..82a0e2fd1f5 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -222,7 +222,7 @@ class SystemNoteService # Called when a branch is created from the 'new branch' button on a issue # Example note text: # - # "Started branch `issue-branch-button-201`" + # "Started branch `201-issue-branch-button`" def self.new_issue_branch(issue, project, author, branch) h = Gitlab::Routing.url_helpers link = h.namespace_project_compare_url(project.namespace, project, from: project.default_branch, to: branch) diff --git a/doc/workflow/web_editor.md b/doc/workflow/web_editor.md index 5685a9d89dd..1832567a34c 100644 --- a/doc/workflow/web_editor.md +++ b/doc/workflow/web_editor.md @@ -85,7 +85,7 @@ Once you click it, a new branch will be created that diverges from the default branch of your project, by default `master`. The branch name will be based on the title of the issue and as suffix it will have its ID. Thus, the example screenshot above will yield a branch named -`et-cum-et-sed-expedita-repellat-consequatur-ut-assumenda-numquam-rerum-2`. +`2-et-cum-et-sed-expedita-repellat-consequatur-ut-assumenda-numquam-rerum`. After the branch is created, you can edit files in the repository to fix the issue. When a merge request is created based on the newly created branch, diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 15052aaca28..fac516f9568 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -191,12 +191,19 @@ describe Issue, models: true do end describe '#related_branches' do - it "selects the right branches" do + it 'selects the right branches' do allow(subject.project.repository).to receive(:branch_names). - and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name]) + and_return(['mpempe', "#{subject.iid}mepmep", subject.to_branch_name]) expect(subject.related_branches).to eq([subject.to_branch_name]) end + + it 'excludes stable branches from the related branches' do + allow(subject.project.repository).to receive(:branch_names). + and_return(["#{subject.iid}-0-stable"]) + + expect(subject.related_branches).to eq [] + end end it_behaves_like 'an editable mentionable' do @@ -210,11 +217,11 @@ describe Issue, models: true do let(:subject) { create :issue } end - describe "#to_branch_name" do + describe '#to_branch_name' do let(:issue) { create(:issue, title: 'a' * 30) } - it "starts with the issue iid" do - expect(issue.to_branch_name).to match /-#{issue.iid}\z/ + it 'starts with the issue iid' do + expect(issue.to_branch_name).to match /\A#{issue.iid}-a+\z/ end end end -- cgit v1.2.1 From a2931a392a4a96ad6df00c98578c193b91e4027f Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 14:06:33 +0100 Subject: Fixed style issues --- app/views/shared/issuable/_sidebar.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index fe6e4128003..56c8eaa0597 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -128,7 +128,7 @@ .title.hide-collapsed Notifications - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' - %button.btn.btn-block.btn-gray.js-subscribe-button.issuable-subscribe-button.hide-collapsed{:type => 'button'} + %button.btn.btn-block.btn-gray.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" } %span= subscribed ? 'Unsubscribe' : 'Subscribe' .subscription-status.hide-collapsed{data: {status: subscribtion_status}} .unsubscribed{class: ( 'hidden' if subscribed )} -- cgit v1.2.1 From a54af831bae023770bf9b2633cc45ec0d5f5a66a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 14 Apr 2016 15:53:54 +0200 Subject: Use rake db:reset instead of db:setup Using db:reset ensures existing tables are first dropped. This in turn ensures that we can drop tables regardless of any foreign key constraints. While CE currently doesn't have any foreign keys EE defines the following relation: remote_mirrors.project_id -> projects.id MySQL will complain whenever you try to drop the "projects" table first even when using "DROP TABLE ... CASCADE". --- bin/setup | 2 +- doc/development/rake_tasks.md | 2 +- lib/tasks/gitlab/setup.rake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/setup b/bin/setup index acdb2c1389c..6cb2d7f1e3a 100755 --- a/bin/setup +++ b/bin/setup @@ -18,7 +18,7 @@ Dir.chdir APP_ROOT do # end puts "\n== Preparing database ==" - system "bin/rake db:setup" + system "bin/rake db:reset" puts "\n== Removing old logs and tempfiles ==" system "rm -f log/*" diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index 9f3fd69fc4e..6d04b9590e6 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -9,7 +9,7 @@ bundle exec rake setup ``` The `setup` task is a alias for `gitlab:setup`. -This tasks calls `db:setup` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and finally it calls `db:seed_fu` to seed the database. +This tasks calls `db:reset` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and finally it calls `db:seed_fu` to seed the database. Note: `db:setup` calls `db:seed` but this does nothing. ## Run tests diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake index 4cbccf2ca89..48baecfd2a2 100644 --- a/lib/tasks/gitlab/setup.rake +++ b/lib/tasks/gitlab/setup.rake @@ -14,7 +14,7 @@ namespace :gitlab do puts "" end - Rake::Task["db:setup"].invoke + Rake::Task["db:reset"].invoke Rake::Task["add_limits_mysql"].invoke Rake::Task["setup_postgresql"].invoke Rake::Task["db:seed_fu"].invoke -- cgit v1.2.1 From 7030ffb0e09dbcc0d03d377846a04fe6cf7d20b6 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Fri, 8 Apr 2016 14:43:21 -0700 Subject: Copying and pasting doesn't grab line numbers or +/- --- app/assets/stylesheets/pages/diff.scss | 20 ++++++++++++++++++++ app/helpers/diff_helper.rb | 1 + app/views/projects/blob/diff.html.haml | 6 +++--- app/views/projects/diffs/_line.html.haml | 6 +++--- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index d0855f66911..ca7fa2094b6 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -67,6 +67,14 @@ line-height: $code_line_height; font-size: $code_font_size; + &.noteable_line.old:before { + content: '-'; + } + + &.noteable_line.new:before { + content: '+'; + } + span { white-space: pre; } @@ -391,3 +399,15 @@ margin-bottom: 0; } } + +.diff-line-num:not(.js-unfold-bottom) { + a { + &:before { + content: attr(data-linenumber); + } + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + } +} diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index ff32e834499..f1e213b34e8 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -44,6 +44,7 @@ module DiffHelper if line.blank? "  ".html_safe else + line[0] = '' line end end diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index abcfca4cd11..ea6d4df7255 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -9,9 +9,9 @@ - line_old = line_new - @form.offset %tr.line_holder %td.old_line.diff-line-num{data: {linenumber: line_old}} - = link_to raw(line_old), "#" - %td.new_line.diff-line-num - = link_to raw(line_new) , "#" + / = link_to raw(line_old), "#" + %td.new_line.diff-line-num{data: {linenumber: line_old}} + / = link_to raw(line_new) , "#" %td.line_content.noteable_line==#{' ' * @form.indent}#{line} - if @form.unfold? && @form.bottom? && @form.to < @blob.loc diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml index 9464c8dc996..2dc6f548437 100644 --- a/app/views/projects/diffs/_line.html.haml +++ b/app/views/projects/diffs/_line.html.haml @@ -9,12 +9,12 @@ %td.new_line.diff-line-num %td.line_content.match= line.text - else - %td.old_line.diff-line-num{class: type} + %td.old_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} - link_text = raw(type == "new" ? " " : line.old_pos) - if defined?(plain) && plain = link_text - else - = link_to link_text, "##{line_code}", id: line_code + = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } - if @comments_allowed && can?(current_user, :create_note, @project) = link_to_new_diff_note(line_code) %td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} @@ -22,5 +22,5 @@ - if defined?(plain) && plain = link_text - else - = link_to link_text, "##{line_code}", id: line_code + = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text) -- cgit v1.2.1 From d176f873e1fdf23ebeeebf6d2d25927941016390 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 13 Apr 2016 12:54:21 -0500 Subject: Add content attributes to discussion diffs; change styling of add-note icon to prevent white spaces in paste --- app/assets/stylesheets/pages/diff.scss | 26 +++++++++++++++------- app/assets/stylesheets/pages/notes.scss | 10 ++------- app/views/projects/blob/diff.html.haml | 4 ++-- .../projects/notes/discussions/_diff.html.haml | 6 ++--- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index ca7fa2094b6..bd7640db3b7 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -69,10 +69,12 @@ &.noteable_line.old:before { content: '-'; + position: absolute; } &.noteable_line.new:before { content: '+'; + position: absolute; } span { @@ -400,14 +402,22 @@ } } -.diff-line-num:not(.js-unfold-bottom) { - a { - &:before { - content: attr(data-linenumber); +.file-holder { + .diff-line-num:not(.js-unfold-bottom) { + a { + &:before { + content: attr(data-linenumber); + } + } + } +} + +.discussion { + .diff-content { + .diff-line-num { + &:before { + content: attr(data-linenumber); + } } - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; } } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index e421a31549a..ce44f5aa13b 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -276,8 +276,7 @@ ul.notes { .diff-file tr.line_holder { @mixin show-add-diff-note { - filter: alpha(opacity=100); - opacity: 1.0; + display: inline-block; } .add-diff-note { @@ -291,13 +290,8 @@ ul.notes { position: absolute; z-index: 10; width: 32px; - - transition: all 0.2s ease; - // "hide" it by default - opacity: 0.0; - filter: alpha(opacity=0); - + display: none; &:hover { background: $gl-info; color: #fff; diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index ea6d4df7255..c6ed78aadf6 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -9,9 +9,9 @@ - line_old = line_new - @form.offset %tr.line_holder %td.old_line.diff-line-num{data: {linenumber: line_old}} - / = link_to raw(line_old), "#" + = link_to raw(line_old), "#" %td.new_line.diff-line-num{data: {linenumber: line_old}} - / = link_to raw(line_new) , "#" + = link_to raw(line_new) , "#" %td.line_content.noteable_line==#{' ' * @form.indent}#{line} - if @form.unfold? && @form.bottom? && @form.to < @blob.loc diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml index 820e31ccd61..6abfb3abc3b 100644 --- a/app/views/projects/notes/discussions/_diff.html.haml +++ b/app/views/projects/notes/discussions/_diff.html.haml @@ -20,10 +20,8 @@ %td.new_line.diff-line-num= "..." %td.line_content.match= line.text - else - %td.old_line.diff-line-num - = raw(type == "new" ? " " : line.old_pos) - %td.new_line.diff-line-num - = raw(type == "old" ? " " : line.new_pos) + %td.old_line.diff-line-num{data: {linenumber: raw(type == "new" ? " " : line.old_pos)}} + %td.new_line.diff-line-num{data: {linenumber: raw(type == "old" ? " " : line.new_pos)}} %td.line_content{class: "noteable_line #{type} #{line_code}", line_code: line_code}= diff_line_content(line.text) - if line_code == note.line_code -- cgit v1.2.1 From 82d0221b632fbb2e7711678b11e9ff26214d9d69 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 13 Apr 2016 15:33:44 -0500 Subject: Add line type conditional to diff line helper --- app/assets/stylesheets/pages/diff.scss | 18 +++++++++++------- app/helpers/diff_helper.rb | 6 ++++-- app/views/projects/diffs/_line.html.haml | 2 +- app/views/projects/notes/discussions/_diff.html.haml | 2 +- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index bd7640db3b7..77d7a3024d5 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -67,14 +67,18 @@ line-height: $code_line_height; font-size: $code_font_size; - &.noteable_line.old:before { - content: '-'; - position: absolute; + &.noteable_line.old { + &:before { + content: '-'; + position: absolute; + } } - &.noteable_line.new:before { - content: '+'; - position: absolute; + &.noteable_line.new { + &:before { + content: '+'; + position: absolute; + } } span { @@ -406,7 +410,7 @@ .diff-line-num:not(.js-unfold-bottom) { a { &:before { - content: attr(data-linenumber); + content: attr(data-linenumber); } } } diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index f1e213b34e8..0504cfb7591 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -40,11 +40,13 @@ module DiffHelper (unfold) ? 'unfold js-unfold' : '' end - def diff_line_content(line) + def diff_line_content(line, line_type = nil) if line.blank? "  ".html_safe else - line[0] = '' + if line_type == 'new' || line_type == 'old' + line[0] = " " + end line end end diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml index 2dc6f548437..6c5602acd43 100644 --- a/app/views/projects/diffs/_line.html.haml +++ b/app/views/projects/diffs/_line.html.haml @@ -23,4 +23,4 @@ = link_text - else = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } - %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text) + %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text, type) diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml index 6abfb3abc3b..9fd9d5bb2aa 100644 --- a/app/views/projects/notes/discussions/_diff.html.haml +++ b/app/views/projects/notes/discussions/_diff.html.haml @@ -22,7 +22,7 @@ - else %td.old_line.diff-line-num{data: {linenumber: raw(type == "new" ? " " : line.old_pos)}} %td.new_line.diff-line-num{data: {linenumber: raw(type == "old" ? " " : line.new_pos)}} - %td.line_content{class: "noteable_line #{type} #{line_code}", line_code: line_code}= diff_line_content(line.text) + %td.line_content{class: "noteable_line #{type} #{line_code}", line_code: line_code}= diff_line_content(line.text, type) - if line_code == note.line_code = render "projects/notes/diff_notes_with_reply", notes: discussion_notes -- cgit v1.2.1 From 2f81baf4767df7ce0144a63b4e451b7d8e09f86a Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 13 Apr 2016 18:40:14 -0500 Subject: Update click_diff_line --- features/steps/shared/diff_note.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb index 1448c3f44cc..e846c52d474 100644 --- a/features/steps/shared/diff_note.rb +++ b/features/steps/shared/diff_note.rb @@ -227,7 +227,7 @@ module SharedDiffNote end def click_diff_line(code) - find("button[data-line-code='#{code}']").click + find("button[data-line-code='#{code}']").trigger('click') end def click_parallel_diff_line(code, line_type) -- cgit v1.2.1 From 0c082d5e3a34d787f8b2fea0c22fa4256cf82be3 Mon Sep 17 00:00:00 2001 From: connorshea Date: Tue, 12 Apr 2016 20:38:14 -0600 Subject: Fix the improper delete form being rendered for an oauth_authorized_application This fixes the authorized applications not being revoked properly at `/profile/applications`. Fixes #14370. --- app/views/doorkeeper/applications/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml index 55f4a6f287d..0aff79749ef 100644 --- a/app/views/doorkeeper/applications/index.html.haml +++ b/app/views/doorkeeper/applications/index.html.haml @@ -68,7 +68,7 @@ %td= app.name %td= token.created_at %td= token.scopes - %td= render 'delete_form', application: app + %td= render 'doorkeeper/authorized_applications/delete_form', application: app - @authorized_anonymous_tokens.each do |token| %tr %td -- cgit v1.2.1 From c7e384aab23301ad0ee3559252324fa957d15db3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 12 Apr 2016 22:39:18 -0700 Subject: Add spec for deletion of authorized OAuth2 application Closes #14370 Move gon function into its own helper --- CHANGELOG | 1 + app/controllers/application_controller.rb | 14 -------- app/controllers/oauth/applications_controller.rb | 1 + app/models/oauth_access_token.rb | 19 +++++++++++ lib/gitlab/current_settings.rb | 2 ++ lib/gitlab/gon_helper.rb | 17 ++++++++++ spec/factories/oauth_access_tokens.rb | 23 +++++++++++++ spec/factories/oauth_applications.rb | 9 ++++++ spec/factories/users.rb | 2 +- spec/features/profiles/oauth_applications_spec.rb | 39 +++++++++++++++++++++++ 10 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 app/models/oauth_access_token.rb create mode 100644 lib/gitlab/gon_helper.rb create mode 100644 spec/factories/oauth_access_tokens.rb create mode 100644 spec/factories/oauth_applications.rb create mode 100644 spec/features/profiles/oauth_applications_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 21f24b5b61a..4db4f01bcbe 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.7.0 (unreleased) - The Projects::HousekeepingService class has extra instrumentation (Yorick Peterse) + - Fix revoking of authorized OAuth applications (Connor Shea) - All service classes (those residing in app/services) are now instrumented (Yorick Peterse) - Developers can now add custom tags to transactions (Yorick Peterse) - Loading of an issue's referenced merge requests and related branches is now done asynchronously (Yorick Peterse) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 97d53acde94..bdf2dd68531 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -158,20 +158,6 @@ class ApplicationController < ActionController::Base end end - def add_gon_variables - gon.api_version = API::API.version - gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s - gon.default_issues_tracker = Project.new.default_issue_tracker.to_param - gon.max_file_size = current_application_settings.max_attachment_size - gon.relative_url_root = Gitlab.config.gitlab.relative_url_root - gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class - - if current_user - gon.current_user_id = current_user.id - gon.api_token = current_user.private_token - end - end - def validate_user_service_ticket! return unless signed_in? && session[:service_tickets] diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index d1e4ac10f6c..377fef65a92 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -4,6 +4,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController before_action :verify_user_oauth_applications_enabled before_action :authenticate_user! + before_action :add_gon_variables layout 'profile' diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb new file mode 100644 index 00000000000..c78c7f4aa0e --- /dev/null +++ b/app/models/oauth_access_token.rb @@ -0,0 +1,19 @@ +# == Schema Information +# +# Table name: oauth_access_tokens +# +# id :integer not null, primary key +# resource_owner_id :integer +# application_id :integer +# token :string not null +# refresh_token :string +# expires_in :integer +# revoked_at :datetime +# created_at :datetime not null +# scopes :string +# + +class OauthAccessToken < ActiveRecord::Base + belongs_to :resource_owner, class_name: 'User' + belongs_to :application, class_name: 'Doorkeeper::Application' +end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 1acc22fe5bf..71b53136ed2 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -1,5 +1,7 @@ module Gitlab module CurrentSettings + include ::Gitlab::GonHelper + def current_application_settings key = :current_application_settings diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb new file mode 100644 index 00000000000..5ebaad6ca6e --- /dev/null +++ b/lib/gitlab/gon_helper.rb @@ -0,0 +1,17 @@ +module Gitlab + module GonHelper + def add_gon_variables + gon.api_version = API::API.version + gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s + gon.default_issues_tracker = Project.new.default_issue_tracker.to_param + gon.max_file_size = current_application_settings.max_attachment_size + gon.relative_url_root = Gitlab.config.gitlab.relative_url_root + gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class + + if current_user + gon.current_user_id = current_user.id + gon.api_token = current_user.private_token + end + end + end +end diff --git a/spec/factories/oauth_access_tokens.rb b/spec/factories/oauth_access_tokens.rb new file mode 100644 index 00000000000..4bbc7d3a554 --- /dev/null +++ b/spec/factories/oauth_access_tokens.rb @@ -0,0 +1,23 @@ +# == Schema Information +# +# Table name: oauth_access_tokens +# +# id :integer not null, primary key +# resource_owner_id :integer +# application_id :integer +# token :string not null +# refresh_token :string +# expires_in :integer +# revoked_at :datetime +# created_at :datetime not null +# scopes :string +# + +FactoryGirl.define do + factory :oauth_access_token do + resource_owner + application + token '123456' + created_at :datetime + end +end diff --git a/spec/factories/oauth_applications.rb b/spec/factories/oauth_applications.rb new file mode 100644 index 00000000000..d116a573830 --- /dev/null +++ b/spec/factories/oauth_applications.rb @@ -0,0 +1,9 @@ +FactoryGirl.define do + factory :oauth_application, class: 'Doorkeeper::Application', aliases: [:application] do + name { FFaker::Name.name } + uid { FFaker::Name.name } + redirect_uri { FFaker::Internet.uri('http') } + owner + owner_type 'User' + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index a5c60c51c5b..a9b2148bd2a 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -1,7 +1,7 @@ FactoryGirl.define do sequence(:name) { FFaker::Name.name } - factory :user, aliases: [:author, :assignee, :recipient, :owner, :creator] do + factory :user, aliases: [:author, :assignee, :recipient, :owner, :creator, :resource_owner] do email { FFaker::Internet.email } name sequence(:username) { |n| "#{FFaker::Internet.user_name}#{n}" } diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb new file mode 100644 index 00000000000..1a5a9059dbd --- /dev/null +++ b/spec/features/profiles/oauth_applications_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'Profile > Applications', feature: true do + let(:user) { create(:user) } + + before do + login_as(user) + end + + describe 'User manages applications', js: true do + it 'deletes an application' do + create(:oauth_application, owner: user) + visit oauth_applications_path + + page.within('.oauth-applications') do + expect(page).to have_content('Your applications (1)') + click_button 'Destroy' + end + + expect(page).to have_content('The application was deleted successfully') + expect(page).to have_content('Your applications (0)') + expect(page).to have_content('Authorized applications (0)') + end + + it 'deletes an authorized application' do + create(:oauth_access_token, resource_owner: user) + visit oauth_applications_path + + page.within('.oauth-authorized-applications') do + expect(page).to have_content('Authorized applications (1)') + click_button 'Revoke' + end + + expect(page).to have_content('The application was revoked access.') + expect(page).to have_content('Your applications (0)') + expect(page).to have_content('Authorized applications (0)') + end + end +end -- cgit v1.2.1 From e450892f5688529b8a49e3ae7598f00dbdda7161 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 14 Apr 2016 05:28:46 -0700 Subject: Include GonHelper separately and remove created_at in factory --- app/controllers/application_controller.rb | 1 + app/controllers/oauth/applications_controller.rb | 1 + lib/gitlab/current_settings.rb | 2 -- spec/factories/oauth_access_tokens.rb | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index bdf2dd68531..ce5c84ee9bc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -3,6 +3,7 @@ require 'fogbugz' class ApplicationController < ActionController::Base include Gitlab::CurrentSettings + include Gitlab::GonHelper include GitlabRoutingHelper include PageLayoutHelper diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index 377fef65a92..c6bdd0602c1 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -1,5 +1,6 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController include Gitlab::CurrentSettings + include Gitlab::GonHelper include PageLayoutHelper before_action :verify_user_oauth_applications_enabled diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 71b53136ed2..1acc22fe5bf 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -1,7 +1,5 @@ module Gitlab module CurrentSettings - include ::Gitlab::GonHelper - def current_application_settings key = :current_application_settings diff --git a/spec/factories/oauth_access_tokens.rb b/spec/factories/oauth_access_tokens.rb index 4bbc7d3a554..7700b15d538 100644 --- a/spec/factories/oauth_access_tokens.rb +++ b/spec/factories/oauth_access_tokens.rb @@ -18,6 +18,5 @@ FactoryGirl.define do resource_owner application token '123456' - created_at :datetime end end -- cgit v1.2.1 From a8ea2c18959c700913132d8a2d7ce3a211303c10 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 14 Apr 2016 11:34:42 -0300 Subject: Change transfer service to use existing methods --- app/services/projects/transfer_service.rb | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 0d8f8c6fbee..79a27f4af7e 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -35,11 +35,9 @@ module Projects end # Apply new namespace id and visibility level - project.tap do |p| - p.namespace = new_namespace - setup_visibility_level(p, new_namespace) - p.save! - end + project.namespace = new_namespace + project.visibility_level = new_namespace.visibility_level unless project.visibility_level_allowed_by_group? + project.save! # Notifications project.send_move_instructions(old_path) @@ -71,15 +69,5 @@ module Projects namespace.id != project.namespace_id && current_user.can?(:create_projects, namespace) end - - private - - def setup_visibility_level(project, new_namespace) - return unless new_namespace.is_a?(Group) - - if project.visibility_level > new_namespace.visibility_level - project.visibility_level = new_namespace.visibility_level - end - end end end -- cgit v1.2.1 From 91cebb7289df872ae4887f7103f68a6f948ee61c Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 14 Apr 2016 17:04:18 +0200 Subject: Documentation / help improvements --- app/views/admin/projects/show.html.haml | 4 +++- doc/README.md | 1 + doc/administration/repository_checks.md | 7 +++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 1e44172f066..73986d21bcf 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -10,7 +10,7 @@ .col-md-12 .panel .panel-heading.alert.alert-danger - Last repository check + Last repository check = "(#{time_ago_in_words(@project.last_repository_check_at)} ago)" failed. See = link_to 'repocheck.log', admin_logs_path @@ -126,6 +126,8 @@ - else passed. + = link_to icon('question-circle'), help_page_path('administration', 'repository_checks') + .form-group = f.submit 'Trigger repository check', class: 'btn btn-primary' diff --git a/doc/README.md b/doc/README.md index d2660930653..e6ac4794827 100644 --- a/doc/README.md +++ b/doc/README.md @@ -31,6 +31,7 @@ - [Environment Variables](administration/environment_variables.md) to configure GitLab. - [Operations](operations/README.md) Keeping GitLab up and running - [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects. +- [Repository checks](administration/repository_checks.md) Periodic Git repository checks - [Security](security/README.md) Learn what you can do to further secure your GitLab instance. - [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed. - [Update](update/README.md) Update guides to upgrade your installation. diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index 16f4627a596..61bf8ce6161 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -1,10 +1,9 @@ # Repository checks -_**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ +>**Note:** +This feature was [introduced][ce-3232] in GitLab 8.7. ---- - -Git has a built-in mechanism, \[git fsck\]\[git-fsck\], to verify the +Git has a built-in mechanism, [git fsck][git-fsck], to verify the integrity of all data commited to a repository. GitLab administrators can trigger such a check for a project via the project page under the admin panel. The checks run asynchronously so it may take a few minutes -- cgit v1.2.1 From 77daebe660eb3f4189ca6339e0fcd634abffe3fa Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 14 Apr 2016 17:05:41 +0200 Subject: More create_list --- spec/mailers/repository_check_mailer_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/mailers/repository_check_mailer_spec.rb b/spec/mailers/repository_check_mailer_spec.rb index 6ae9a93aaac..583bf15176f 100644 --- a/spec/mailers/repository_check_mailer_spec.rb +++ b/spec/mailers/repository_check_mailer_spec.rb @@ -5,7 +5,7 @@ describe RepositoryCheckMailer do describe '.notify' do it 'emails all admins' do - admins = 3.times.map { create(:admin) } + admins = create_list(:admin, 3) mail = described_class.notify(1) -- cgit v1.2.1 From b49069b72cbfa73b6f7bb7195659ba2958f02f7e Mon Sep 17 00:00:00 2001 From: Jamie Neubert Pedersen Date: Thu, 14 Apr 2016 16:04:18 +0000 Subject: change: "very demand" changed to "specific demand" This must be an error as the sentence doesn't make sense otherwise --- doc/ci/runners/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index 295d953db11..c7df0713a3d 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -19,7 +19,7 @@ many projects, you can have a single or a small number of runners that handle multiple projects. This makes it easier to maintain and update runners. **Specific runners** are useful for jobs that have special requirements or for -projects with a very demand. If a job has certain requirements, you can set +projects with a specific demand. If a job has certain requirements, you can set up the specific runner with this in mind, while not having to do this for all runners. For example, if you want to deploy a certain project, you can setup a specific runner to have the right credentials for this. -- cgit v1.2.1 From 46d1cf43a43a4d7a25f25be97d1fee79cefdc773 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 14 Apr 2016 18:05:53 +0200 Subject: update changelog [ci skip] --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 1d1e541e65f..cfe47a4e56f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,7 @@ v 8.7.0 (unreleased) - Fix admin/projects when using visibility levels on search (PotHix) - Build status notifications - API: Expose user location (Robert Schilling) + - Add encrypted credentials for imported projects and migrate old ones v 8.6.5 (unreleased) - Only update repository language if it is not set to improve performance -- cgit v1.2.1 From 7ef35b9c61b61b6cf03f20ce4ab34272404dca40 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 14 Apr 2016 12:28:48 -0400 Subject: Shut up, Rubocop --- app/workers/repository_check/clear_worker.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb index 9c3347a7040..b7202ddff34 100644 --- a/app/workers/repository_check/clear_worker.rb +++ b/app/workers/repository_check/clear_worker.rb @@ -1,9 +1,9 @@ module RepositoryCheck class ClearWorker include Sidekiq::Worker - + sidekiq_options retry: false - + def perform # Do small batched updates because these updates will be slow and locking Project.select(:id).find_in_batches(batch_size: 100) do |batch| @@ -14,4 +14,4 @@ module RepositoryCheck end end end -end \ No newline at end of file +end -- cgit v1.2.1 From cce21e7490d8684fb8fbaa6c9d5a8ce76c36d975 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 14 Apr 2016 12:32:33 -0400 Subject: Update CHANGELOG for "Auto git fsck" --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 41b6e2c67fd..1f94a574d5b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,7 @@ v 8.7.0 (unreleased) - Add endpoints to archive or unarchive a project !3372 - Add links to CI setup documentation from project settings and builds pages - Handle nil descriptions in Slack issue messages (Stan Hu) + - Add automated repository integrity checks - API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling) - API: Ability to star and unstar a project (Robert Schilling) - Add default scope to projects to exclude projects pending deletion -- cgit v1.2.1 From 2165bbc7853016ea68f36b44ad0590623add7bcf Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 14 Apr 2016 20:31:19 +0300 Subject: Remove deprecated NGINX CI config --- lib/support/nginx/gitlab_ci | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 lib/support/nginx/gitlab_ci diff --git a/lib/support/nginx/gitlab_ci b/lib/support/nginx/gitlab_ci deleted file mode 100644 index bf05edfd780..00000000000 --- a/lib/support/nginx/gitlab_ci +++ /dev/null @@ -1,29 +0,0 @@ -# GITLAB CI -server { - listen 80 default_server; # e.g., listen 192.168.1.1:80; - server_name YOUR_CI_SERVER_FQDN; # e.g., server_name source.example.com; - - access_log /var/log/nginx/gitlab_ci_access.log; - error_log /var/log/nginx/gitlab_ci_error.log; - - # expose API to fix runners - location /api { - proxy_read_timeout 300; - proxy_connect_timeout 300; - proxy_redirect off; - proxy_set_header X-Real-IP $remote_addr; - - # You need to specify your DNS servers that are able to resolve YOUR_GITLAB_SERVER_FQDN - resolver 8.8.8.8 8.8.4.4; - proxy_pass $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; - } - - # redirect all other CI requests - location / { - return 301 $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; - } - - # adjust this to match the largest build log your runners might submit, - # set to 0 to disable limit - client_max_body_size 10m; -} \ No newline at end of file -- cgit v1.2.1 From 80d8f8b87609ffb9b0fcc1f74fbeb4520a193e07 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Thu, 14 Apr 2016 09:10:36 -0500 Subject: Syntax & style updates --- app/assets/stylesheets/pages/diff.scss | 22 +++++++++++++--------- app/helpers/diff_helper.rb | 4 +--- app/views/projects/blob/diff.html.haml | 12 ++++++------ app/views/projects/diffs/_line.html.haml | 16 ++++++++-------- .../projects/notes/discussions/_diff.html.haml | 6 +++--- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 77d7a3024d5..183f22a1b24 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -67,17 +67,21 @@ line-height: $code_line_height; font-size: $code_font_size; - &.noteable_line.old { - &:before { - content: '-'; - position: absolute; + &.noteable_line { + position: relative; + + &.old { + &:before { + content: '-'; + position: absolute; + } } - } - &.noteable_line.new { - &:before { - content: '+'; - position: absolute; + &.new { + &:before { + content: '+'; + position: absolute; + } } } diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 0504cfb7591..6a3ec83b8c0 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -44,9 +44,7 @@ module DiffHelper if line.blank? "  ".html_safe else - if line_type == 'new' || line_type == 'old' - line[0] = " " - end + line[0] = ' ' if %w[new old].include?(line_type) line end end diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index c6ed78aadf6..38e62c81fed 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -1,20 +1,20 @@ - if @lines.present? - if @form.unfold? && @form.since != 1 && !@form.bottom? %tr.line_holder{ id: @form.since } - = render "projects/diffs/match_line", {line: @match_line, - line_old: @form.since, line_new: @form.since, bottom: false, new_file: false} + = render "projects/diffs/match_line", { line: @match_line, + line_old: @form.since, line_new: @form.since, bottom: false, new_file: false } - @lines.each_with_index do |line, index| - line_new = index + @form.since - line_old = line_new - @form.offset %tr.line_holder - %td.old_line.diff-line-num{data: {linenumber: line_old}} + %td.old_line.diff-line-num{ data: { linenumber: line_old } } = link_to raw(line_old), "#" - %td.new_line.diff-line-num{data: {linenumber: line_old}} + %td.new_line.diff-line-num{ data: { linenumber: line_old } } = link_to raw(line_new) , "#" %td.line_content.noteable_line==#{' ' * @form.indent}#{line} - if @form.unfold? && @form.bottom? && @form.to < @blob.loc %tr.line_holder{ id: @form.to } - = render "projects/diffs/match_line", {line: @match_line, - line_old: @form.to, line_new: @form.to, bottom: true, new_file: false} + = render "projects/diffs/match_line", { line: @match_line, + line_old: @form.to, line_new: @form.to, bottom: true, new_file: false } diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml index 6c5602acd43..107097ad963 100644 --- a/app/views/projects/diffs/_line.html.haml +++ b/app/views/projects/diffs/_line.html.haml @@ -1,26 +1,26 @@ - type = line.type -%tr.line_holder{id: line_code, class: type} +%tr.line_holder{ id: line_code, class: type } - case type - when 'match' - = render "projects/diffs/match_line", {line: line.text, - line_old: line.old_pos, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file} + = render "projects/diffs/match_line", { line: line.text, + line_old: line.old_pos, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file } - when 'nonewline' %td.old_line.diff-line-num %td.new_line.diff-line-num %td.line_content.match= line.text - else - %td.old_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} - - link_text = raw(type == "new" ? " " : line.old_pos) + %td.old_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } } + - link_text = type == "new" ? " ".html_safe : line.old_pos - if defined?(plain) && plain = link_text - else = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } - if @comments_allowed && can?(current_user, :create_note, @project) = link_to_new_diff_note(line_code) - %td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} - - link_text = raw(type == "old" ? " " : line.new_pos) + %td.new_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } } + - link_text = type == "old" ? " ".html_safe : line.new_pos - if defined?(plain) && plain = link_text - else = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } - %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text, type) + %td.line_content{ class: ['noteable_line', type, line_code], data: { line_code: line_code } }= diff_line_content(line.text, type) diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml index 9fd9d5bb2aa..d46aab000c3 100644 --- a/app/views/projects/notes/discussions/_diff.html.haml +++ b/app/views/projects/notes/discussions/_diff.html.haml @@ -20,9 +20,9 @@ %td.new_line.diff-line-num= "..." %td.line_content.match= line.text - else - %td.old_line.diff-line-num{data: {linenumber: raw(type == "new" ? " " : line.old_pos)}} - %td.new_line.diff-line-num{data: {linenumber: raw(type == "old" ? " " : line.new_pos)}} - %td.line_content{class: "noteable_line #{type} #{line_code}", line_code: line_code}= diff_line_content(line.text, type) + %td.old_line.diff-line-num{ data: { linenumber: type == "new" ? " ".html_safe : line.old_pos } } + %td.new_line.diff-line-num{ data: { linenumber: type == "old" ? " ".html_safe : line.new_pos } } + %td.line_content{ class: ['noteable_line', type, line_code], line_code: line_code }= diff_line_content(line.text, type) - if line_code == note.line_code = render "projects/notes/diff_notes_with_reply", notes: discussion_notes -- cgit v1.2.1 From 6ae2680ce7159eedd37e89c7aa99688949139f11 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 14 Apr 2016 19:38:17 +0300 Subject: Emoji categories fix --- CHANGELOG | 1 + lib/award_emoji.rb | 16 +++++++++++++--- spec/lib/award_emoji_spec.rb | 7 +++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9aa40fae18e..4a9a5c1bc36 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -66,6 +66,7 @@ v 8.7.0 (unreleased) - Improved markdown forms - Diffs load at the correct point when linking from from number - Selected diff rows highlight + - Fix emoji catgories in the emoji picker v 8.6.6 - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb index 4fc3443ac68..5f8ff01b0a9 100644 --- a/lib/award_emoji.rb +++ b/lib/award_emoji.rb @@ -14,19 +14,29 @@ class AwardEmoji food_drink: "Food" }.with_indifferent_access + CATEGORY_ALIASES = { + symbols: "objects_symbols", + foods: "food_drink", + travel: "travel_places" + }.with_indifferent_access + def self.normilize_emoji_name(name) aliases[name] || name end def self.emoji_by_category unless @emoji_by_category - @emoji_by_category = {} + @emoji_by_category = Hash.new { |h, key| h[key] = [] } emojis.each do |emoji_name, data| data["name"] = emoji_name - @emoji_by_category[data["category"]] ||= [] - @emoji_by_category[data["category"]] << data + # Skip Fitzpatrick(tone) modifiers + next if data["category"] == "modifier" + + category = CATEGORY_ALIASES[data["category"]] || data["category"] + + @emoji_by_category[category] << data end @emoji_by_category = @emoji_by_category.sort.to_h diff --git a/spec/lib/award_emoji_spec.rb b/spec/lib/award_emoji_spec.rb index 330678f7f16..88c22912950 100644 --- a/spec/lib/award_emoji_spec.rb +++ b/spec/lib/award_emoji_spec.rb @@ -16,4 +16,11 @@ describe AwardEmoji do end end end + + describe '.emoji_by_category' do + it "only contains known categories" do + undefined_categories = AwardEmoji.emoji_by_category.keys - AwardEmoji::CATEGORIES.keys + expect(undefined_categories).to be_empty + end + end end -- cgit v1.2.1 From f58312976733fadb2b0cbf4b734f8d94220cb501 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 14 Apr 2016 13:56:30 -0400 Subject: Add Sentry program context even without a current user --- app/controllers/application_controller.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ce5c84ee9bc..1c53b0b21a3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -14,7 +14,7 @@ class ApplicationController < ActionController::Base before_action :check_password_expiration before_action :check_2fa_requirement before_action :ldap_security_check - before_action :sentry_user_context + before_action :sentry_context before_action :default_headers before_action :add_gon_variables before_action :configure_permitted_parameters, if: :devise_controller? @@ -41,13 +41,15 @@ class ApplicationController < ActionController::Base protected - def sentry_user_context - if Rails.env.production? && current_application_settings.sentry_enabled && current_user - Raven.user_context( - id: current_user.id, - email: current_user.email, - username: current_user.username, - ) + def sentry_context + if Rails.env.production? && current_application_settings.sentry_enabled + if current_user + Raven.user_context( + id: current_user.id, + email: current_user.email, + username: current_user.username, + ) + end Raven.tags_context(program: sentry_program_context) end -- cgit v1.2.1 From 5bf47f14b962b0e676e399dddc238d7bf764304d Mon Sep 17 00:00:00 2001 From: theoretick Date: Thu, 7 Apr 2016 15:49:35 -0700 Subject: Do not include award emojis in issue view comment_count Fixes Issue #14431 --- CHANGELOG | 1 + app/views/projects/issues/_issue.html.haml | 2 +- .../merge_requests/_merge_request.html.haml | 2 +- app/views/projects/merge_requests/_show.html.haml | 2 +- spec/features/issues_spec.rb | 16 ++++++++++++-- spec/features/notes_on_merge_requests_spec.rb | 25 ++++++++++++++++++++++ 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ec210867e7f..b67a12eaee3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,7 @@ v 8.7.0 (unreleased) - Loading of an issue's referenced merge requests and related branches is now done asynchronously (Yorick Peterse) - Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea) - Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea) + - Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles) - All images in discussions and wikis now link to their source files !3464 (Connor Shea). - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu) - Add setting for customizing the list of trusted proxies !3524 diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 4aa92d0b39e..7a8009f6da4 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -27,7 +27,7 @@ = icon('thumbs-down') = downvotes - - note_count = issue.notes.user.count + - note_count = issue.notes.user.nonawards.count - if note_count > 0 %li = link_to issue_path(issue) + "#notes" do diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 391193eed6c..e740fe8c84d 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -35,7 +35,7 @@ = icon('thumbs-down') = downvotes - - note_count = merge_request.mr_and_commit_notes.user.count + - note_count = merge_request.mr_and_commit_notes.user.nonawards.count - if note_count > 0 %li = link_to merge_request_path(merge_request) + "#notes" do diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 1dd8f721f7e..07037a14f51 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -51,7 +51,7 @@ %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.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 diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 79000666ccc..1ce0024e93c 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -45,7 +45,7 @@ describe 'Issues', feature: true do project: project) end - it 'allows user to select unasigned', js: true do + it 'allows user to select unassigned', js: true do visit edit_namespace_project_issue_path(project.namespace, project, issue) expect(page).to have_content "Assignee #{@user.name}" @@ -64,6 +64,18 @@ describe 'Issues', feature: true do end end + describe 'Issue info' do + it 'excludes award_emoji from comment count' do + issue = create(:issue, author: @user, assignee: @user, project: project, title: 'foobar') + create(:upvote_note, noteable: issue) + + visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id) + + expect(page).to have_content 'foobar' + expect(page.all('.issue-no-comments').first.text).to eq "0" + end + end + describe 'Filter issue' do before do ['foobar', 'barbaz', 'gitlab'].each do |title| @@ -187,7 +199,7 @@ describe 'Issues', feature: true do describe 'update assignee from issue#show' do let(:issue) { create(:issue, project: project, author: @user, assignee: @user) } - context 'by autorized user' do + context 'by authorized user' do it 'allows user to select unassigned', js: true do visit namespace_project_issue_path(project.namespace, project, issue) diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index 5f855ccc701..389812ff7e1 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -4,6 +4,20 @@ describe 'Comments', feature: true do include RepoHelpers include WaitForAjax + describe 'On merge requests page', feature: true do + it 'excludes award_emoji from comment count' do + merge_request = create(:merge_request) + project = merge_request.source_project + create(:upvote_note, noteable: merge_request, project: project) + + login_as :admin + visit namespace_project_merge_requests_path(project.namespace, project) + + expect(merge_request.mr_and_commit_notes.count).to eq 1 + expect(page.all('.merge-request-no-comments').first.text).to eq "0" + end + end + describe 'On a merge request', js: true, feature: true do let!(:merge_request) { create(:merge_request) } let!(:project) { merge_request.source_project } @@ -129,6 +143,17 @@ describe 'Comments', feature: true do end end end + + describe 'comment info' do + it 'excludes award_emoji from comment count' do + create(:upvote_note, noteable: merge_request, project: project) + + 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" + end + end end describe 'On a merge request diff', js: true, feature: true do -- cgit v1.2.1 From 70c9ceb7b1bd815f4a3e79a809a9279f841cee63 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 14 Apr 2016 18:46:18 -0300 Subject: Fix datetime format when migrating new notification settings on MySQL --- db/migrate/20160328115649_migrate_new_notification_setting.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20160328115649_migrate_new_notification_setting.rb b/db/migrate/20160328115649_migrate_new_notification_setting.rb index 0a110869027..3c81b2c37bf 100644 --- a/db/migrate/20160328115649_migrate_new_notification_setting.rb +++ b/db/migrate/20160328115649_migrate_new_notification_setting.rb @@ -7,7 +7,7 @@ # class MigrateNewNotificationSetting < ActiveRecord::Migration def up - timestamp = Time.now + timestamp = Time.now.strftime('%F %T') execute "INSERT INTO notification_settings ( user_id, source_id, source_type, level, created_at, updated_at ) SELECT user_id, source_id, source_type, notification_level, '#{timestamp}', '#{timestamp}' FROM members WHERE user_id IS NOT NULL" end -- cgit v1.2.1 From d2f490c8f3601915cae0e125a0d88871c7c2b7e8 Mon Sep 17 00:00:00 2001 From: lurdan Date: Fri, 15 Apr 2016 12:41:26 +0900 Subject: fix required gitlab-shell version. Under the procedure, I've warned about gitlab-shell version, which should be 2.6.12 (may be some minor revisions introduces that). --- doc/update/8.5-to-8.6.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.5-to-8.6.md b/doc/update/8.5-to-8.6.md index b9abcbd2c12..6267f14eba4 100644 --- a/doc/update/8.5-to-8.6.md +++ b/doc/update/8.5-to-8.6.md @@ -46,7 +46,7 @@ sudo -u git -H git checkout 8-6-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch --all -sudo -u git -H git checkout v2.6.11 +sudo -u git -H git checkout v2.6.12 ``` ### 5. Update gitlab-workhorse -- cgit v1.2.1 From ca725aaf4ca7cb300c50d539fe27efc45a32dfd6 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 15 Apr 2016 08:52:26 +0200 Subject: resolve merge conflict --- CHANGELOG | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d0638c880cf..6de5a9b9389 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -57,11 +57,6 @@ v 8.7.0 (unreleased) - Fix admin/projects when using visibility levels on search (PotHix) - Build status notifications - API: Expose user location (Robert Schilling) - - Add encrypted credentials for imported projects and migrate old ones - -v 8.6.5 (unreleased) - - Only update repository language if it is not set to improve performance - - Check permissions when user attempts to import members from another project - API: Do not leak group existence via return code (Robert Schilling) - ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591 - Update number of Todos in the sidebar when it's marked as "Done". !3600 @@ -73,6 +68,7 @@ v 8.6.5 (unreleased) - Diffs load at the correct point when linking from from number - Selected diff rows highlight - Fix emoji catgories in the emoji picker + - Add encrypted credentials for imported projects and migrate old ones v 8.6.6 - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) -- cgit v1.2.1 From a434ffd3b3a895bd75daed76000def23f4002f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 14 Apr 2016 12:20:16 +0200 Subject: Make /profile/keys/new redirects to /profile/keys for back-compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report: https://github.com/gitlabhq/gitlabhq/issues/10138 Signed-off-by: Rémy Coutable --- CHANGELOG | 1 + app/controllers/profiles/keys_controller.rb | 5 +++++ config/routes.rb | 2 +- spec/controllers/profiles/keys_controller_spec.rb | 12 +++++++++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e5c8620bebf..da4f9b0fb65 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ v 8.7.0 (unreleased) - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu) - API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling) - Expose project badges in project settings + - Make /profile/keys/new redirect to /profile/keys for back-compat. !3717 - Preserve time notes/comments have been updated at when moving issue - Make HTTP(s) label consistent on clone bar (Stan Hu) - Expose label description in API (Mariusz Jachimowicz) diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb index b88c080352b..a12549d6bcb 100644 --- a/app/controllers/profiles/keys_controller.rb +++ b/app/controllers/profiles/keys_controller.rb @@ -10,6 +10,11 @@ class Profiles::KeysController < Profiles::ApplicationController @key = current_user.keys.find(params[:id]) end + # Back-compat: We need to support this URL since git-annex webapp points to it + def new + redirect_to profile_keys_path + end + def create @key = current_user.keys.new(key_params) diff --git a/config/routes.rb b/config/routes.rb index 688b83d2c95..f32f7ea8557 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -326,7 +326,7 @@ Rails.application.routes.draw do end end resource :preferences, only: [:show, :update] - resources :keys, except: [:new] + resources :keys resources :emails, only: [:index, :create, :destroy] resource :avatar, only: [:destroy] resource :two_factor_auth, only: [:new, :create, :destroy] do diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb index b6573f105dc..3a82083717f 100644 --- a/spec/controllers/profiles/keys_controller_spec.rb +++ b/spec/controllers/profiles/keys_controller_spec.rb @@ -1,7 +1,17 @@ require 'spec_helper' describe Profiles::KeysController do - let(:user) { create(:user) } + let(:user) { create(:user) } + + describe '#new' do + before { sign_in(user) } + + it 'redirect to #index' do + get :new + + expect(response).to redirect_to(profile_keys_path) + end + end describe "#get_keys" do describe "non existant user" do -- cgit v1.2.1 From f8e8a61c9ce6d90347960a3fda8abd15c32ddc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 15 Apr 2016 11:38:59 +0200 Subject: Remove Gitlab::Shell#update_repository_head since it's not used anymore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/backend/shell.rb | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index b9bb6e76081..db44a9a44d4 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -54,19 +54,6 @@ module Gitlab "#{path}.git", "#{new_path}.git"]) end - # Update HEAD for repository - # - # path - project path with namespace - # branch - repository branch name - # - # Ex. - # update_repository_head("gitlab/gitlab-ci", "3-1-stable") - # - def update_repository_head(path, branch) - Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'update-head', - "#{path}.git", branch]) - end - # Fork repository to new namespace # # path - project path with namespace -- cgit v1.2.1 From 106c443e76fe143ef3d7ef504b404fa4a2667e93 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 15 Apr 2016 13:30:33 +0200 Subject: Remove unused backend methods --- lib/gitlab/backend/shell.rb | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index b9bb6e76081..0c3dbc744b5 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -92,33 +92,6 @@ module Gitlab 'rm-project', "#{name}.git"]) end - # Add repository branch from passed ref - # - # path - project path with namespace - # branch_name - new branch name - # ref - HEAD for new branch - # - # Ex. - # add_branch("gitlab/gitlab-ci", "4-0-stable", "master") - # - def add_branch(path, branch_name, ref) - Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'create-branch', - "#{path}.git", branch_name, ref]) - end - - # Remove repository branch - # - # path - project path with namespace - # branch_name - branch name to remove - # - # Ex. - # rm_branch("gitlab/gitlab-ci", "4-0-stable") - # - def rm_branch(path, branch_name) - Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'rm-branch', - "#{path}.git", branch_name]) - end - # Add repository tag from passed ref # # path - project path with namespace -- cgit v1.2.1 From 3c704c33e0d6c91ecc156d8bcdf260b0c4c23a27 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 15 Apr 2016 14:21:28 +0200 Subject: Delete tags via rugged --- CHANGELOG | 1 + app/models/repository.rb | 7 ++++++- lib/gitlab/backend/shell.rb | 13 ------------- spec/models/repository_spec.rb | 6 ++---- spec/services/delete_tag_service_spec.rb | 11 +---------- 5 files changed, 10 insertions(+), 28 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 69b464bdc6b..453d0ba1ac9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -67,6 +67,7 @@ v 8.7.0 (unreleased) - Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu) - Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld) - Improved markdown forms + - Delete tags using Rugged for performance reasons (Robert Schilling) - Diffs load at the correct point when linking from from number - Selected diff rows highlight - Fix emoji catgories in the emoji picker diff --git a/app/models/repository.rb b/app/models/repository.rb index 89062170481..308c590e3f8 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -169,7 +169,12 @@ class Repository def rm_tag(tag_name) before_remove_tag - gitlab_shell.rm_tag(path_with_namespace, tag_name) + begin + rugged.tags.delete(tag_name) + true + rescue Rugged::ReferenceError + false + end end def branch_names diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index b9bb6e76081..4c6f0d3d681 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -137,19 +137,6 @@ module Gitlab Gitlab::Utils.system_silent(cmd) end - # Remove repository tag - # - # path - project path with namespace - # tag_name - tag name to remove - # - # Ex. - # rm_tag("gitlab/gitlab-ci", "v4.0") - # - def rm_tag(path, tag_name) - Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'rm-tag', - "#{path}.git", tag_name]) - end - # Gc repository # # path - project path with namespace diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 86f68b3a0a0..c163001b7c1 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -770,11 +770,9 @@ describe Repository, models: true do describe '#rm_tag' do it 'removes a tag' do expect(repository).to receive(:before_remove_tag) + expect(repository.rugged.tags).to receive(:delete).with('v1.1.0') - expect_any_instance_of(Gitlab::Shell).to receive(:rm_tag). - with(repository.path_with_namespace, '8.5') - - repository.rm_tag('8.5') + repository.rm_tag('v1.1.0') end end diff --git a/spec/services/delete_tag_service_spec.rb b/spec/services/delete_tag_service_spec.rb index 5b7ba521812..477551f5036 100644 --- a/spec/services/delete_tag_service_spec.rb +++ b/spec/services/delete_tag_service_spec.rb @@ -6,21 +6,12 @@ describe DeleteTagService, services: true do let(:user) { create(:user) } let(:service) { described_class.new(project, user) } - let(:tag) { double(:tag, name: '8.5', target: 'abc123') } - describe '#execute' do - before do - allow(repository).to receive(:find_tag).and_return(tag) - end - it 'removes the tag' do - expect_any_instance_of(Gitlab::Shell).to receive(:rm_tag). - and_return(true) - expect(repository).to receive(:before_remove_tag) expect(service).to receive(:success) - service.execute('8.5') + service.execute('v1.1.0') end end end -- cgit v1.2.1 From 5b374226a76b7369876bad84671be58bcf6e1b4c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 15 Apr 2016 16:36:07 +0100 Subject: Indented code --- app/views/projects/merge_requests/_new_compare.html.haml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index 3e2a37e050b..b08524574e4 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -17,8 +17,8 @@ = dropdown_filter("Search projects") = dropdown_content do = render 'projects/merge_requests/dropdowns/project', - projects: [@merge_request.source_project], - selected: f.object.source_project_id + projects: [@merge_request.source_project], + selected: f.object.source_project_id .merge-request-select.dropdown = f.hidden_field :source_branch = dropdown_toggle "Select source branch", { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" } @@ -27,8 +27,8 @@ = dropdown_filter("Search branches") = dropdown_content do = render 'projects/merge_requests/dropdowns/branch', - branches: @merge_request.source_branches, - selected: f.object.source_branch + branches: @merge_request.source_branches, + selected: f.object.source_branch .panel-footer = icon('spinner spin', class: 'js-source-loading') %ul.list-unstyled.mr_source_commit @@ -47,8 +47,8 @@ = dropdown_filter("Search projects") = dropdown_content do = render 'projects/merge_requests/dropdowns/project', - projects: projects, - selected: f.object.target_project_id + projects: projects, + selected: f.object.target_project_id .merge-request-select.dropdown = f.hidden_field :target_branch = dropdown_toggle f.object.target_branch, { toggle: "dropdown", field_name: "#{f.object_name}[target_branch]" }, { toggle_class: "js-compare-dropdown js-target-branch" } @@ -57,8 +57,8 @@ = dropdown_filter("Search branches") = dropdown_content do = render 'projects/merge_requests/dropdowns/branch', - branches: @merge_request.target_branches, - selected: f.object.target_branch + branches: @merge_request.target_branches, + selected: f.object.target_branch .panel-footer = icon('spinner spin', class: "js-target-loading") %ul.list-unstyled.mr_target_commit -- cgit v1.2.1 From 91dca9a883729d3ba65bb37c4c5d052aba38d405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 15 Apr 2016 19:13:31 +0200 Subject: Add 8.6.6 CHANGELOG entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ci skip] Signed-off-by: Rémy Coutable --- CHANGELOG | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8c0e0f2f0d3..df4e24f1122 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,12 +1,12 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.7.0 (unreleased) - - The Projects::HousekeepingService class has extra instrumentation (Yorick Peterse) - - Fix revoking of authorized OAuth applications (Connor Shea) - - All service classes (those residing in app/services) are now instrumented (Yorick Peterse) - - Developers can now add custom tags to transactions (Yorick Peterse) - - Loading of an issue's referenced merge requests and related branches is now done asynchronously (Yorick Peterse) + - The Projects::HousekeepingService class has extra instrumentation + - All service classes (those residing in app/services) are now instrumented + - Developers can now add custom tags to transactions + - Loading of an issue's referenced merge requests and related branches is now done asynchronously - Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea) + - Project switcher uses new dropdown styling - Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea) - Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles) - All images in discussions and wikis now link to their source files !3464 (Connor Shea). @@ -14,7 +14,7 @@ v 8.7.0 (unreleased) - Add setting for customizing the list of trusted proxies !3524 - Allow projects to be transfered to a lower visibility level group - Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524 - - Improved Markdown rendering performance !3389 (Yorick Peterse) + - Improved Markdown rendering performance !3389 - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu) - API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling) - Expose project badges in project settings @@ -75,8 +75,9 @@ v 8.7.0 (unreleased) - Fix emoji catgories in the emoji picker v 8.6.6 - - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) - - Project switcher uses new dropdown styling + - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 + - Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654 + - Fix revoking of authorized OAuth applications (Connor Shea). !3690 v 8.6.5 - Fix importing from GitHub Enterprise. !3529 @@ -276,7 +277,7 @@ v 8.5.1 v 8.5.0 - Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu) - - Cache various Repository methods to improve performance (Yorick Peterse) + - Cache various Repository methods to improve performance - Fix duplicated branch creation/deletion Webhooks/service notifications when using Web UI (Stan Hu) - Ensure rake tasks that don't need a DB connection can be run without one - Update New Relic gem to 3.14.1.311 (Stan Hu) -- cgit v1.2.1 From 47c6111546c32ec0b1ec4f1720e0e29c455cb438 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 15 Apr 2016 17:17:48 -0400 Subject: Remove extra spacing from merge request target branch name --- app/views/projects/merge_requests/_show.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 07037a14f51..2c34f9c454b 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -32,8 +32,7 @@ %span Request to merge %span.label-branch= source_branch_with_namespace(@merge_request) %span into - = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do - = @merge_request.target_branch + = link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" - if @merge_request.open? && @merge_request.diverged_from_target_branch? %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) -- cgit v1.2.1 From d744a04a5b4b46d970cf3c6c4030fc61718c59c0 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Sat, 16 Apr 2016 09:23:25 +0000 Subject: fix TYPO in the changelog --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index c948e8e5460..f1320b6a28c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -72,7 +72,7 @@ v 8.7.0 (unreleased) - Delete tags using Rugged for performance reasons (Robert Schilling) - Diffs load at the correct point when linking from from number - Selected diff rows highlight - - Fix emoji catgories in the emoji picker + - Fix emoji categories in the emoji picker - Add encrypted credentials for imported projects and migrate old ones v 8.6.6 -- cgit v1.2.1 From 562c9652d687bb03d0aa5b744016946d7a293a82 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Tue, 29 Mar 2016 18:39:25 -0500 Subject: Put owner and participating people first --- app/services/projects/participants_service.rb | 39 ++++++++++++++++----------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index 0004a399f47..ba88ba73c35 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -1,6 +1,7 @@ module Projects class ParticipantsService < BaseService def execute(note_type, note_id) + @target = get_target(note_type, note_id) participating = if note_type && note_id participants_in(note_type, note_id) @@ -8,35 +9,43 @@ module Projects [] end project_members = sorted(project.team.members) - participants = all_members + groups + project_members + participating + participants = target_owner + participating + all_members + groups + project_members participants.uniq end + def get_target(type, id) + case type + when "Issue" + project.issues.find_by_iid(id) + when "MergeRequest" + project.merge_requests.find_by_iid(id) + when "Commit" + project.commit(id) + end + end + + def target_owner + [{ + name: @target.author.name, + username: @target.author.username + }] + end + def participants_in(type, id) - target = - case type - when "Issue" - project.issues.find_by_iid(id) - when "MergeRequest" - project.merge_requests.find_by_iid(id) - when "Commit" - project.commit(id) - end - - return [] unless target + return [] unless @target - users = target.participants(current_user) + users = @target.participants(current_user) sorted(users) end def sorted(users) - users.uniq.to_a.compact.sort_by(&:username).map do |user| + users.uniq.to_a.compact.sort_by(&:username).map do |user| { username: user.username, name: user.name } end end def groups - current_user.authorized_groups.sort_by(&:path).map do |group| + current_user.authorized_groups.sort_by(&:path).map do |group| count = group.users.count { username: group.path, name: group.name, count: count } end -- cgit v1.2.1 From ad48ecacaef508139117ceaea1a9eeaeb7046b48 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Tue, 29 Mar 2016 18:53:01 -0500 Subject: Update method name and remove unneeded params --- app/services/projects/participants_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index ba88ba73c35..800747b7ef6 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -4,7 +4,7 @@ module Projects @target = get_target(note_type, note_id) participating = if note_type && note_id - participants_in(note_type, note_id) + participants_in_target else [] end @@ -31,7 +31,7 @@ module Projects }] end - def participants_in(type, id) + def participants_in_target return [] unless @target users = @target.participants(current_user) -- cgit v1.2.1 From 16459fddd4857bc18a917e13989b755468cfd7e8 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 30 Mar 2016 15:19:56 -0500 Subject: Fixes multiple ajax request and incorrect data being set for the current issuable --- app/assets/javascripts/gfm_auto_complete.js.coffee | 63 ++++++++++++++++------ 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee index 4718bcf7a1e..61e3f811e73 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.coffee +++ b/app/assets/javascripts/gfm_auto_complete.js.coffee @@ -2,6 +2,8 @@ window.GitLab ?= {} GitLab.GfmAutoComplete = + dataLoading: false + dataSource: '' # Emoji @@ -17,17 +19,41 @@ GitLab.GfmAutoComplete = template: '
  • ${id} ${title}
  • ' # Add GFM auto-completion to all input fields, that accept GFM input. - setup: -> - input = $('.js-gfm-input') + setup: (wrap) -> + @input = $('.js-gfm-input') + + # destroy previous instances + @destroyAtWho() + + # set up instances + @setupAtWho() + + if @dataSource + if !@dataLoading + @dataLoading = true + # We should wait until initializations are done + # and only trigger the last .setup since + # The previous .dataSource belongs to the previous issuable + # and the last one will have the **proper** .dataSource property + # TODO: Make this a singleton and turn off events when moving to another page + setTimeout( => + fetch = @fetchData(@dataSource) + fetch.done (data) => + @dataLoading = false + @loadData(data) + , 1000) + + + setupAtWho: -> # Emoji - input.atwho + @input.atwho at: ':' displayTpl: @Emoji.template insertTpl: ':${name}:' # Team Members - input.atwho + @input.atwho at: '@' displayTpl: @Members.template insertTpl: '${atwho-at}${username}' @@ -42,7 +68,7 @@ GitLab.GfmAutoComplete = title: sanitize(title) search: sanitize("#{m.username} #{m.name}") - input.atwho + @input.atwho at: '#' alias: 'issues' searchKey: 'search' @@ -55,7 +81,7 @@ GitLab.GfmAutoComplete = title: sanitize(i.title) search: "#{i.iid} #{i.title}" - input.atwho + @input.atwho at: '!' alias: 'mergerequests' searchKey: 'search' @@ -68,13 +94,18 @@ GitLab.GfmAutoComplete = title: sanitize(m.title) search: "#{m.iid} #{m.title}" - if @dataSource - $.getJSON(@dataSource).done (data) -> - # load members - input.atwho 'load', '@', data.members - # load issues - input.atwho 'load', 'issues', data.issues - # load merge requests - input.atwho 'load', 'mergerequests', data.mergerequests - # load emojis - input.atwho 'load', ':', data.emojis + destroyAtWho: -> + @input.atwho('destroy') + + fetchData: (dataSource) -> + $.getJSON(dataSource) + + loadData: (data) -> + # load members + @input.atwho 'load', '@', data.members + # load issues + @input.atwho 'load', 'issues', data.issues + # load merge requests + @input.atwho 'load', 'mergerequests', data.mergerequests + # load emojis + @input.atwho 'load', ':', data.emojis -- cgit v1.2.1 From 05628e0c2ffc5a3378ad4d89a212b1bd652a9d60 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 30 Mar 2016 16:29:42 -0500 Subject: Fixes failing spec --- app/services/projects/participants_service.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index 800747b7ef6..1eaecc0d270 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -25,6 +25,8 @@ module Projects end def target_owner + return [] unless @target && @target.author.present? + [{ name: @target.author.name, username: @target.author.username -- cgit v1.2.1 From 1eeabdc6a5c5ad751891d725a0957a24282f7a9c Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 8 Apr 2016 12:23:44 -0500 Subject: Change variable name --- app/services/projects/participants_service.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index 1eaecc0d270..11dc3542fa2 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -1,9 +1,9 @@ module Projects class ParticipantsService < BaseService - def execute(note_type, note_id) - @target = get_target(note_type, note_id) + def execute(noteable_type, noteable_id) + @target = get_target(noteable_type, noteable_id) participating = - if note_type && note_id + if noteable_type && noteable_id participants_in_target else [] -- cgit v1.2.1 From a96dc944289e01f7b41112343cf20ca2791860ca Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 8 Apr 2016 13:36:55 -0500 Subject: Memoize target --- app/services/projects/participants_service.rb | 42 +++++++++++++-------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index 11dc3542fa2..02c4eee3d02 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -1,42 +1,40 @@ module Projects class ParticipantsService < BaseService def execute(noteable_type, noteable_id) - @target = get_target(noteable_type, noteable_id) - participating = - if noteable_type && noteable_id - participants_in_target - else - [] - end + @noteable_type = noteable_type + @noteable_id = noteable_id project_members = sorted(project.team.members) - participants = target_owner + participating + all_members + groups + project_members + participants = target_owner + participants_in_target + all_members + groups + project_members participants.uniq end - def get_target(type, id) - case type - when "Issue" - project.issues.find_by_iid(id) - when "MergeRequest" - project.merge_requests.find_by_iid(id) - when "Commit" - project.commit(id) - end + def target + @target ||= + case @noteable_type + when "Issue" + project.issues.find_by_iid(@noteable_id) + when "MergeRequest" + project.merge_requests.find_by_iid(@noteable_id) + when "Commit" + project.commit(@noteable_id) + else + nil + end end def target_owner - return [] unless @target && @target.author.present? + return [] unless target && target.author.present? [{ - name: @target.author.name, - username: @target.author.username + name: target.author.name, + username: target.author.username }] end def participants_in_target - return [] unless @target + return [] unless target - users = @target.participants(current_user) + users = target.participants(current_user) sorted(users) end -- cgit v1.2.1 From 42848f2684c093c114abf20dd5d73a195ec6a451 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 8 Apr 2016 13:45:24 -0500 Subject: Update CHANGELOG --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index f1320b6a28c..7754c3fb9fd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -79,6 +79,9 @@ v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 - Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654 - Fix revoking of authorized OAuth applications (Connor Shea). !3690 + - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) + - Project switcher uses new dropdown styling + - Author and participants are displayed first on users autocompletion v 8.6.5 - Fix importing from GitHub Enterprise. !3529 -- cgit v1.2.1 From a740f0bc99a67d25ed156353055de3b4fe0064a6 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 8 Apr 2016 19:35:43 -0500 Subject: Add tests for autocomplete on a Issue --- spec/features/participants_autocomplete_spec.rb | 53 +++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 spec/features/participants_autocomplete_spec.rb diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb new file mode 100644 index 00000000000..f966fcbbfa1 --- /dev/null +++ b/spec/features/participants_autocomplete_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +feature 'Member autocomplete', feature: true do + let(:project) { create(:project, :public) } + let(:user) { create(:user) } + let(:participant) { create(:user) } + let(:author) { create(:user) } + let(:issue) { create(:issue, author: author, project: project) } + + before do + login_as user + end + + describe 'On a Issue', js: true do + before do + create(:note, note: 'ultralight beam', noteable: issue, author: participant) + visit_issue(project, issue) + end + + describe 'adding a new note' do + describe 'when typing @' do + + before do + sleep 1 + page.within('.new-note') do + sleep 1 + find('#note_note').native.send_keys('@') + end + end + + it 'suggestions are displayed' do + expect(page).to have_selector('.atwho-view', visible: true) + end + + it 'author is a suggestion' do + page.within('.atwho-view', visible: true) do + expect(page).to have_content(author.username) + end + end + + it 'participant is a suggestion' do + page.within('.atwho-view', visible: true) do + expect(page).to have_content(participant.username) + end + end + end + end + end + + def visit_issue(project, issue) + visit namespace_project_issue_path(project.namespace, project, issue) + end +end -- cgit v1.2.1 From 40efb4eb1fb3a1863f16488c7a468d4ca140500a Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Sat, 9 Apr 2016 01:34:07 -0500 Subject: Add tests for autocomplete on a Merge Request --- spec/features/participants_autocomplete_spec.rb | 81 ++++++++++++++++--------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index f966fcbbfa1..5f1b5fd6b54 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -5,49 +5,72 @@ feature 'Member autocomplete', feature: true do let(:user) { create(:user) } let(:participant) { create(:user) } let(:author) { create(:user) } - let(:issue) { create(:issue, author: author, project: project) } before do login_as user end - describe 'On a Issue', js: true do + shared_examples "open suggestions" do + it 'suggestions are displayed' do + expect(page).to have_selector('.atwho-view', visible: true) + end + + it 'author is suggested' do + page.within('.atwho-view', visible: true) do + expect(page).to have_content(author.username) + end + end + + it 'participant is suggested' do + page.within('.atwho-view', visible: true) do + expect(page).to have_content(participant.username) + end + end + end + + context 'On a Issue adding a new note', js: true do before do - create(:note, note: 'ultralight beam', noteable: issue, author: participant) + issue = create(:issue, author: author, project: project) + create(:note, note: 'Ultralight Beam', noteable: issue, author: participant) visit_issue(project, issue) end - describe 'adding a new note' do - describe 'when typing @' do - - before do - sleep 1 - page.within('.new-note') do - sleep 1 - find('#note_note').native.send_keys('@') - end - end - - it 'suggestions are displayed' do - expect(page).to have_selector('.atwho-view', visible: true) - end - - it 'author is a suggestion' do - page.within('.atwho-view', visible: true) do - expect(page).to have_content(author.username) - end - end - - it 'participant is a suggestion' do - page.within('.atwho-view', visible: true) do - expect(page).to have_content(participant.username) - end - end + context 'when typing @' do + include_examples "open suggestions" + before do + open_member_suggestions end end end + context 'On a Merge Request adding a new note', js: true do + before do + merge = create(:merge_request, source_project: project, target_project: project, author: author) + create(:note, note: 'Ultralight Beam', noteable: merge, author: participant) + visit_merge_request(project, merge) + end + + context 'when typing @' do + include_examples "open suggestions" + before do + open_member_suggestions + end + end + end + + def open_member_suggestions + sleep 1 + page.within('.new-note') do + sleep 1 + find('#note_note').native.send_keys('@') + end + end + def visit_issue(project, issue) visit namespace_project_issue_path(project.namespace, project, issue) end + + def visit_merge_request(project, merge) + visit namespace_project_merge_request_path(project.namespace, project, merge) + end end -- cgit v1.2.1 From a53e912bc0578febf87f722887a100f2c6e4670f Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 11 Apr 2016 14:02:00 -0500 Subject: Change context description --- spec/features/participants_autocomplete_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index 5f1b5fd6b54..b566a036a46 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -28,7 +28,7 @@ feature 'Member autocomplete', feature: true do end end - context 'On a Issue adding a new note', js: true do + context 'adding a new note on a Issue', js: true do before do issue = create(:issue, author: author, project: project) create(:note, note: 'Ultralight Beam', noteable: issue, author: participant) @@ -43,7 +43,7 @@ feature 'Member autocomplete', feature: true do end end - context 'On a Merge Request adding a new note', js: true do + context 'adding a new note on a Merge Request ', js: true do before do merge = create(:merge_request, source_project: project, target_project: project, author: author) create(:note, note: 'Ultralight Beam', noteable: merge, author: participant) -- cgit v1.2.1 From 18b4291181ba126a21137320a9f7b7f503c1d776 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 13 Apr 2016 13:50:17 -0500 Subject: Add tests for autocomplete on a Commit --- spec/features/participants_autocomplete_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index b566a036a46..1adab7e9c6c 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -7,6 +7,7 @@ feature 'Member autocomplete', feature: true do let(:author) { create(:user) } before do + allow_any_instance_of(Commit).to receive(:author).and_return(author) login_as user end @@ -58,6 +59,23 @@ feature 'Member autocomplete', feature: true do end end + context 'adding a new note on a Commit ', js: true do + let(:commit) { project.commit } + + before do + allow(commit).to receive(:author).and_return(author) + create(:note_on_commit, author: participant, project: project, commit_id: project.repository.commit.id, note: 'No More Parties in LA') + visit_commit(project, commit) + end + + context 'when typing @' do + include_examples "open suggestions" + before do + open_member_suggestions + end + end + end + def open_member_suggestions sleep 1 page.within('.new-note') do @@ -73,4 +91,8 @@ feature 'Member autocomplete', feature: true do def visit_merge_request(project, merge) visit namespace_project_merge_request_path(project.namespace, project, merge) end + + def visit_commit(project, commit) + visit namespace_project_commit_path(project.namespace, project, commit) + end end -- cgit v1.2.1 From 6e00c3635bfdf10ef2ada7a5b8af3fbc88f7f58a Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Thu, 14 Apr 2016 12:19:33 -0500 Subject: Update CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 7754c3fb9fd..bc523533ee6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -74,6 +74,7 @@ v 8.7.0 (unreleased) - Selected diff rows highlight - Fix emoji categories in the emoji picker - Add encrypted credentials for imported projects and migrate old ones + - Author and participants are displayed first on users autocompletion v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 @@ -81,7 +82,6 @@ v 8.6.6 - Fix revoking of authorized OAuth applications (Connor Shea). !3690 - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) - Project switcher uses new dropdown styling - - Author and participants are displayed first on users autocompletion v 8.6.5 - Fix importing from GitHub Enterprise. !3529 -- cgit v1.2.1 From 5ddda30c353569d3d0af52c0112be4e66f890586 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Sat, 16 Apr 2016 08:59:56 -0400 Subject: Remove errors from CHANGELOG --- CHANGELOG | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bc523533ee6..5c375fcdb39 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -80,8 +80,6 @@ v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 - Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654 - Fix revoking of authorized OAuth applications (Connor Shea). !3690 - - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) - - Project switcher uses new dropdown styling v 8.6.5 - Fix importing from GitHub Enterprise. !3529 -- cgit v1.2.1