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: [PATCH 001/213] 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 77a21e757c..e93979fd46 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 9467011799..93c0c7adfe 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 77c8dafc01..23f2ab45ff 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 67faa1e443..8aa85e448c 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 05386d790c..368762b826 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 e5f089fb8a..f9314a241b 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 ca87dca4a7..99a28b8c63 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 770f32de94..4527d1c74e 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 7b306a8a53..2afbdfae66 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 a45775f36b..1df44eaa64 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 85379a8e98..0754a2889f 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 600089802b..16fc1a6069 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 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: [PATCH 002/213] 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 23f2ab45ff..5d81a996fb 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 2afbdfae66..e472381ba3 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 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: [PATCH 003/213] 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 5d81a996fb..1e4fc612a3 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 f9314a241b..e5f089fb8a 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 99a28b8c63..ca87dca4a7 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 4527d1c74e..770f32de94 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 e472381ba3..7b306a8a53 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 1df44eaa64..ef351fe809 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 0754a2889f..612e344c41 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 16fc1a6069..600089802b 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 From 18411645505f4bf4bb877743cb4dc027d422414b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 2 Mar 2016 17:31:17 +0100 Subject: [PATCH 004/213] add initial migrations --- app/models/project.rb | 1 + ...151724_add_import_credentials_to_projects.rb | 6 ++++++ ...808_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 6f5d592755..36b11366f3 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 0000000000..3cfe2bbd50 --- /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 0000000000..3f1b65aff1 --- /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 From cefefb2adea23c81c5e6254992da975eca71b559 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 3 Mar 2016 18:10:06 +0100 Subject: [PATCH 005/213] WIP - refactored migration and updated project_import_data with encrypted att --- app/models/project.rb | 1 - app/models/project_import_data.rb | 3 +- ...port_credentials_to_project_import_data.rb | 6 +++ ...1724_add_import_credentials_to_projects.rb | 6 --- ...8_remove_wrong_import_url_from_projects.rb | 38 +++++++++++++++---- 5 files changed, 38 insertions(+), 16 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 36b11366f3..6f5d592755 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 cd3319f077..2900b86d64 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 0000000000..ff3d8b466d --- /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 3cfe2bbd50..0000000000 --- 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 3f1b65aff1..dda7648fb8 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 - 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 ? + + 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 - private + 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) + end + end + end - - 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 From 06b36c00d55df38cd2aaa4d5251185485c8abe5c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Mar 2016 12:21:53 +0100 Subject: [PATCH 006/213] some refactoring in the migration. Also fixed github import issue and updated spec --- ...52808_remove_wrong_import_url_from_projects.rb | 15 ++++++++++----- db/schema.rb | 4 +++- lib/gitlab/github_import/project_creator.rb | 12 ++++++++++-- .../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 dda7648fb8..dfa9f2d4de 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 53a941d30d..05c19fc7a2 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 474927069a..d6cab3c2d2 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 c93a3ebdae..36abe87f52 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 From c2b33d3b71215858892debeb45d93a69d530fd8e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Mar 2016 16:23:19 +0100 Subject: [PATCH 007/213] 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 2900b86d64..493c82a94f 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 e2a85f2982..515fd4720d 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 d6cab3c2d2..b5ed32e5b1 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 6c592ff469..8be8292410 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 0000000000..6b4af0bf26 --- /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 From 7085850c50a6dd7072bd2c80f092b0c20f74d1dc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Mar 2016 18:37:00 +0100 Subject: [PATCH 008/213] 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 493c82a94f..23f4e97b8a 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 6b4af0bf26..f1919dffa8 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 0000000000..18393cdda9 --- /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 36abe87f52..290c855642 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 aed2aa39e3..46d5c2f329 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 From 735563329d1f86ee4d72b37cd22eed1168935e8e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 7 Mar 2016 12:50:35 +0100 Subject: [PATCH 009/213] refactored a bunch of stuff based on MR feedback --- app/models/project_import_data.rb | 2 +- ...8_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 23f4e97b8a..f3b9daa0d1 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 dfa9f2d4de..881af783c6 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 515fd4720d..d478d3b539 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 b5ed32e5b1..52aba93a51 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 f1919dffa8..bf03f5a6da 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 0000000000..878947caea --- /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 From 383cc8404741f65bd52fbe80eec6c2dae2578fce Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 13:15:51 +0100 Subject: [PATCH 010/213] some refactoring based on feedback --- app/models/project.rb | 12 +++++ ...8_remove_wrong_import_url_from_projects.rb | 50 +++++++------------ 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, 61 insertions(+), 49 deletions(-) create mode 100644 lib/gitlab/import_url_sanitizer.rb diff --git a/app/models/project.rb b/app/models/project.rb index 412c6c6732..ab4afd4159 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 881af783c6..f98ec925ac 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"]) + 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 = '#{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']}") + 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 5b2f5aa3dd..72a3ec2fb1 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 a96cfa8af9..d407be5dcf 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 52aba93a51..f4221003db 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 8be8292410..db2c49a497 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 0000000000..dfbc4f8303 --- /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 From 8d7d9c8daa61d58a17fea648771a1bb6c9341304 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 13:23:35 +0100 Subject: [PATCH 011/213] added missing column for current att_encrypted version --- ...160302151724_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 ff3d8b466d..dd2b361398 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 From 030b13944534be505dc97667ce2094ed6c588f12 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 15:11:05 +0100 Subject: [PATCH 012/213] more refactoring --- app/models/project.rb | 12 ++++++---- ...8_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 +- ...{import_url_sanitizer.rb => import_url.rb} | 16 +++++++++++-- lib/gitlab/import_url_exposer.rb | 13 ----------- .../github_import/project_creator_spec.rb | 5 ++-- .../github_import/wiki_formatter_spec.rb | 7 +----- spec/lib/gitlab/import_url_exposer_spec.rb | 13 ----------- spec/lib/gitlab/import_url_spec.rb | 21 +++++++++++++++++ 11 files changed, 72 insertions(+), 64 deletions(-) rename lib/gitlab/{import_url_sanitizer.rb => import_url.rb} (52%) delete mode 100644 lib/gitlab/import_url_exposer.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 ab4afd4159..4e5fa8821e 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 f98ec925ac..fd718ef397 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 72a3ec2fb1..02300c028d 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 d407be5dcf..0b1ed51022 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 db2c49a497..6c592ff469 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_sanitizer.rb b/lib/gitlab/import_url.rb similarity index 52% rename from lib/gitlab/import_url_sanitizer.rb rename to lib/gitlab/import_url.rb index dfbc4f8303..7358edac2e 100644 --- a/lib/gitlab/import_url_sanitizer.rb +++ b/lib/gitlab/import_url.rb @@ -1,7 +1,8 @@ module Gitlab - class ImportUrlSanitizer - def initialize(url) + class ImportUrl + def initialize(url, credentials: nil) @url = URI.parse(url) + @credentials = credentials end def sanitized_url @@ -12,8 +13,19 @@ module Gitlab @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 diff --git a/lib/gitlab/import_url_exposer.rb b/lib/gitlab/import_url_exposer.rb deleted file mode 100644 index bf03f5a6da..0000000000 --- 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/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb index 290c855642..2092c1b958 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 46d5c2f329..91cd370987 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 878947caea..0000000000 --- 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 0000000000..f758cb8693 --- /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 From bd8a77674f7226aafa4bd9befa8ed7482c372dc1 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 17:16:27 +0100 Subject: [PATCH 013/213] fixed few issues with the migration --- app/models/project.rb | 3 ++- app/models/project_import_data.rb | 2 +- ...8_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 4e5fa8821e..242ad19b11 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 f3b9daa0d1..420c01f996 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 fd718ef397..0f7da3103b 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 7358edac2e..aa43092025 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] From dff4050f1d3f00815c095ec2645bd935f14e51a7 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 17:29:19 +0100 Subject: [PATCH 014/213] 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 420c01f996..ba4334055d 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 aa43092025..adcbcc43d2 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 18393cdda9..a799af9996 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 2092c1b958..0f363b8b0a 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 91cd370987..1bd29b8a56 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 From 6dfb5d7cad3e3cefd766e30a4c25a9549fa2b041 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 17:34:20 +0100 Subject: [PATCH 015/213] remove byebug --- .../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 0f7da3103b..6b4b6726fc 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"]) From ced56641bfc42c8380af1760fae93ba37bf2e785 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 18:09:47 +0100 Subject: [PATCH 016/213] 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 242ad19b11..a1aa1d5fdb 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 adcbcc43d2..5b18d67ddc 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 From 19bfdcf4689e9c994f5a630da9cff75e5baa0e55 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 18:20:25 +0100 Subject: [PATCH 017/213] 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 a1aa1d5fdb..2a530715a9 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 From 868e4918f93021430162ec097d3362f313a4bb80 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Mar 2016 18:38:06 +0100 Subject: [PATCH 018/213] updated migration --- ...0302152808_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 6b4b6726fc..cf6b983323 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 02300c028d..71f1e1e496 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 From 1b8d995492baca8984bde950e0449dad6342befc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 11:32:15 +0100 Subject: [PATCH 019/213] refactoring migration to add bitbucket stuff --- ...8_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 cf6b983323..8ba68e61c7 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) From 23146fca1878c9f6e90deed6856d3da2c731d513 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 12:25:28 +0100 Subject: [PATCH 020/213] update bitbucket importer --- ...808_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 8ba68e61c7..4ca245035d 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 46e51a4bf6..36110962e0 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 0b1ed51022..a5d3ab5fcf 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}" From 3b78885eac025b5cf90e1a370b7bbbbd88cc9727 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 12:26:50 +0100 Subject: [PATCH 021/213] 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 03aac1a025..c9ccecef0c 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 From b372968e936a8b02f8d7ff73a1e1cc25b332406c Mon Sep 17 00:00:00 2001 From: Baldinof Date: Tue, 22 Mar 2016 10:56:44 +0100 Subject: [PATCH 022/213] Add number sign on external issue reference text --- app/models/external_issue.rb | 8 ++++++++ spec/models/external_issue_spec.rb | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index 2ca79df0a2..92f1f17494 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -34,7 +34,15 @@ class ExternalIssue %r{(?\b([A-Z][A-Z0-9_]+-)\d+)} end + def self.reference_prefix + '#' + end + def to_reference(_from_project = nil) id end + + def reference_link_text(from_project = nil) + "#{self.class.reference_prefix}#{id}" + end end diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb index 9b144dd1ec..b3d0c4efe9 100644 --- a/spec/models/external_issue_spec.rb +++ b/spec/models/external_issue_spec.rb @@ -36,4 +36,10 @@ describe ExternalIssue, models: true do expect(issue.title).to eq "External Issue #{issue}" end end + + describe '#reference_link_text' do + it 'returns a String reference to the object' do + expect(issue.reference_link_text).to eq '#EXT-1234' + end + end end From 8aafe685837d12b623f70eec86cae6e7cef9a849 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 17:53:53 +0100 Subject: [PATCH 023/213] 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 ++------- .../bitbucket_import/project_creator.rb | 4 +-- .../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 c4287d314e..b464356326 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 36110962e0..f80410641a 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 0000000000..6d81811d36 --- /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 f4dd393ad2..312ed58150 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 c9ccecef0c..0e6f66de32 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 c413132abe..1a833f255a 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) } From c136edbbe343cbb54f135928db5d901c1cb65c4f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 18:03:54 +0100 Subject: [PATCH 024/213] 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 850b73244c..afc06d01eb 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 7baaadb813..77c33db4b5 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 From 46346caf5ba5af7ad9af6913e479f301a3ff2d2d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Mar 2016 18:14:13 +0100 Subject: [PATCH 025/213] 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 6d81811d36..08b710003e 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 - - From 8da04f6b64884c642e8e4b630a30e6400e47b09e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 23 Mar 2016 11:55:43 +0100 Subject: [PATCH 026/213] refactored migration a bit and fixed a few problems. Also added some logging --- ...8_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 4ca245035d..3d97a66c0f 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 From 6967871fc567cbadd63f26f1ef87c4008cc6387b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 23 Mar 2016 12:52:50 +0100 Subject: [PATCH 027/213] fogbugz importer, also refactored migration again to make it easier to add new importers --- ...8_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 3d97a66c0f..f3a4fc26be 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 db580b5e57..c33b3541dd 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 e0163499e3..73d6272720 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 From cc4d04f97f891479c4d033196c6868e19528c51c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 23 Mar 2016 17:57:10 +0100 Subject: [PATCH 028/213] added rest of importers, fixed specs and some issues with the migration --- ...8_remove_wrong_import_url_from_projects.rb | 10 +++++++--- db/schema.rb | 4 ++-- lib/gitlab/google_code_import/importer.rb | 20 +++++++++++-------- .../google_code_import/project_creator.rb | 2 +- .../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 f3a4fc26be..a55ab6a42b 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 53d12aa35d..75509c35b0 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 62da327931..6b0715d149 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 87821c2346..acd3a832d5 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 647631271e..6ecf3d7182 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 From 459ad34493c57b40fd431b18750fef85884d51e1 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 28 Mar 2016 16:35:03 +0200 Subject: [PATCH 029/213] refactored code based on feedback plus fixed a couple of other issues --- ...8_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 +++--- .../google_code_import/project_creator.rb | 2 +- .../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 a55ab6a42b..fb5d8591c0 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 d88a6eaac6..1d1bd5e321 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 f80410641a..7beaecd1cf 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 08b710003e..0000000000 --- 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 312ed58150..e03c3155b3 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 6b0715d149..62da327931 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 acd3a832d5..87821c2346 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 6ecf3d7182..647631271e 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 From 28df200c66f88348e0c995fc8023b6dd78b9bf9b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 29 Mar 2016 15:23:32 +0200 Subject: [PATCH 030/213] 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 5b18d67ddc..3cfbc17b89 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 a799af9996..9e08d5a22e 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 From c93570d8fb439a8a60a204bcc41e5e8a720730e4 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 29 Mar 2016 16:10:35 +0200 Subject: [PATCH 031/213] 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 0e6f66de32..7dc3e01753 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 73d6272720..15914aea3b 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 87821c2346..49d6013af2 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 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: [PATCH 032/213] Target right release --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 1adfa49ad1..b0ddf6c3a9 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 From 075b56aae2e045e930580985234276edb353747f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 29 Mar 2016 18:43:23 +0200 Subject: [PATCH 033/213] 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 7dc3e01753..cc7f201714 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 15914aea3b..939cb96c10 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 From 6d12d79d29e035c7238aa7112db1429711e61a65 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 1 Apr 2016 12:04:41 +0200 Subject: [PATCH 034/213] 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 ba4334055d..fa6055ff64 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 c33b3541dd..c88a44573a 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 939cb96c10..0a87b406c5 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 From 6aeb753ba81c857c23d29f9d4c5e22aaeed737dc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 1 Apr 2016 15:51:54 +0200 Subject: [PATCH 035/213] 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 52f70256be..941e444a4f 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 a5d3ab5fcf..0b1ed51022 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}" From 255cd31652f1afda5cd1c075526bbe3ee56a708e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 1 Apr 2016 17:27:07 +0200 Subject: [PATCH 036/213] 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 1d1bd5e321..acd0f298b3 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 afc06d01eb..19dc79462c 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 From 43ee65e173cbb4f846c118c531f635dc0f8602ac Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 1 Apr 2016 17:50:50 +0200 Subject: [PATCH 037/213] 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 c88a44573a..fffeb66ce2 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}" From ef85c510fa24a0deec6f680f9226dccd937a7bbe Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 4 Apr 2016 09:57:52 +0200 Subject: [PATCH 038/213] corrected a couple of based on MR review --- .../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 fb5d8591c0..60f86c74df 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 fffeb66ce2..5c1c1c4865 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) From 3cf512362ef6e459c4a10f53818db799b9b1a248 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 4 Apr 2016 15:39:08 +0200 Subject: [PATCH 039/213] Add notice about GitLab Runner to requirements docs This is related to !14589, and problems that may stem from running GitLab Runner on same machine user installed GitLab web app on. --- doc/install/requirements.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 03cb08dd1f..03433a72fa 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -79,6 +79,16 @@ With less memory GitLab will give strange errors during the reconfigure run and Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about many you need of those. +## Gitlab Runner + +We strongly advise against installing GitLab Runner on the same machine you plan to install GitLab on. Depending on how you decide to configure GitLab Runner and what tools you use to exercise your application in the CI environment, GitLab Runner can consume significant amount of available memory. + +Memory consumption calculations, that are available above, will not be valid if you decide to run GitLab Runner and GitLab web application on the same machine. + +It is also not safe to install everything on a single machine, because of the security reasons - especially when you plan to use shell executor with GitLab Runner. + +We recommend using a separate machine for each GitLab Runner, if you plan to use CI features. + ## Unicorn Workers It's possible to increase the amount of unicorn workers and this will usually help for to reduce the response time of the applications and increase the ability to handle parallel requests. From 288d8e669883abe995fe27ad577189da42ef78fb Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 4 Apr 2016 18:03:55 +0200 Subject: [PATCH 040/213] fixes based on MR review --- ...8_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 60f86c74df..708dde8eca 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) From 172d37a2385424eb5f4c157fa691fd47b9f5a850 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 4 Apr 2016 19:45:53 +0200 Subject: [PATCH 041/213] fix wording --- ...0302152808_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 708dde8eca..0a7ba65acc 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 '%//%@%'") From 4835e680a4624ab8de3316b367b8375bb5a270a0 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 4 Apr 2016 19:55:13 +0200 Subject: [PATCH 042/213] fix migration issue --- .../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 0a7ba65acc..8fef93233e 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 From 5e51fce4dcd62997f372aed44badc844f98851e9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 5 Apr 2016 15:41:15 +0200 Subject: [PATCH 043/213] some refactoring to symbolise keys across importers and left a TODO --- app/models/project_import_data.rb | 8 ++++++-- ...0160302152808_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 fa6055ff64..225cbda15b 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 8fef93233e..93e040fce2 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 df4c65a062..5d87dbfe41 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 acd0f298b3..49f86ab5ed 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 cc7f201714..109010cb96 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 5c1c1c4865..249c5b48b1 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 0a87b406c5..e9fac8968e 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 19dc79462c..96717b42ba 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}" 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: [PATCH 044/213] 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 33d3357a1f..a7e934936e 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') From b97654393e326095c7d95ccc1eb9f583a3b23da9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 6 Apr 2016 10:36:30 +0200 Subject: [PATCH 045/213] 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 225cbda15b..e984832685 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 49f86ab5ed..9bb507b5ed 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 249c5b48b1..42f9b6eab8 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 From 05be0c306ea8663461ee73023f162aeaf77a4325 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 6 Apr 2016 11:13:19 +0200 Subject: [PATCH 046/213] 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 e984832685..a099431200 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 From 15044e7d857138b31199b796f02a81f0c29c643f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 7 Apr 2016 15:00:20 +0200 Subject: [PATCH 047/213] 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 7b1188420e..17b971b9d3 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 5d87dbfe41..53d12aa35d 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 109010cb96..65b62b2b81 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 42f9b6eab8..501d5a9554 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 e9fac8968e..c000b30046 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 49d6013af2..d2e20afbb1 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 From a1a1d1f7de71f46787f12f1efa23346a2a6b1c29 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 7 Apr 2016 15:08:38 +0200 Subject: [PATCH 048/213] refactored create_or_update_import_data --- app/models/project.rb | 23 ++++++------------- .../bitbucket_import/project_creator.rb | 2 +- lib/gitlab/fogbugz_import/project_creator.rb | 2 +- .../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 17b971b9d3..273b04c632 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 65b62b2b81..941f818b84 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 c000b30046..3840765db8 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 d2e20afbb1..0abb7a64c1 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 From 0ceee903f14d1824aa56452b83ae4a8e090cbc14 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 7 Apr 2016 15:49:46 +0200 Subject: [PATCH 049/213] 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 53d12aa35d..df4c65a062 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 From 350a9aa984100a4d91454200fb4ac308108e5cef Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 7 Apr 2016 16:53:15 +0200 Subject: [PATCH 050/213] 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 273b04c632..6304699386 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) From 1266f41e28ae7a8da9f85f0ede70291f34ed7988 Mon Sep 17 00:00:00 2001 From: Christoph Junghans Date: Sat, 9 Apr 2016 11:59:12 -0600 Subject: [PATCH 051/213] 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 9ae6964aaa..b6074373e2 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 From be834c4de93a7716035f5373210ea3922c26da72 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 11 Apr 2016 11:13:51 +0200 Subject: [PATCH 052/213] changed a few things based on feedback --- app/models/project_import_data.rb | 8 +++++-- ...port_credentials_to_project_import_data.rb | 4 ++-- ...8_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 a099431200..79efb40305 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 dd2b361398..ffcd64266e 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 93e040fce2..dd2842b835 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 9e08d5a22e..0000000000 --- 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 From fb63173df2bf81c155ed311d2c8fc5889a5faf1d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 11 Apr 2016 11:28:35 +0200 Subject: [PATCH 053/213] typo --- .../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 dd2842b835..b97b79f920 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 From 3122e5cc847b224d5531f23a9c2fe9bc2043b01f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 11 Apr 2016 12:35:26 +0300 Subject: [PATCH 054/213] Add link to Runner security doc [ci skip] --- doc/install/requirements.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 03433a72fa..eb9fe5e1b1 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -81,13 +81,23 @@ Notice: The 25 workers of Sidekiq will show up as separate processes in your pro ## Gitlab Runner -We strongly advise against installing GitLab Runner on the same machine you plan to install GitLab on. Depending on how you decide to configure GitLab Runner and what tools you use to exercise your application in the CI environment, GitLab Runner can consume significant amount of available memory. +We strongly advise against installing GitLab Runner on the same machine you plan +to install GitLab on. Depending on how you decide to configure GitLab Runner and +what tools you use to exercise your application in the CI environment, GitLab +Runner can consume significant amount of available memory. -Memory consumption calculations, that are available above, will not be valid if you decide to run GitLab Runner and GitLab web application on the same machine. +Memory consumption calculations, that are available above, will not be valid if +you decide to run GitLab Runner and the GitLab Rails application on the same +machine. -It is also not safe to install everything on a single machine, because of the security reasons - especially when you plan to use shell executor with GitLab Runner. +It is also not safe to install everything on a single machine, because of the +[security reasons] - especially when you plan to use shell executor with GitLab +Runner. -We recommend using a separate machine for each GitLab Runner, if you plan to use CI features. +We recommend using a separate machine for each GitLab Runner, if you plan to +use the CI features. + +[security reasons]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md ## Unicorn Workers From d86190eead0075d1b0ba7e73ff2efa8a610b5398 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 11 Apr 2016 12:41:27 +0300 Subject: [PATCH 055/213] Add a note about installing Runners in ci/runners/README.md --- doc/ci/runners/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index 295d953db1..36e90e2141 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -7,6 +7,10 @@ through the coordinator API of GitLab CI. A runner can be specific to a certain project or serve any project in GitLab CI. A runner that serves all projects is called a shared runner. +Ideally, GitLab Runner should not be installed on the same machine as GitLab. +Read the [requirements documentation](../../install/requirements.md#gitlab-runner) +for more information. + ## Shared vs. Specific Runners A runner that is specific only runs for the specified project. A shared runner @@ -140,7 +144,7 @@ to it. This means that if you have shared runners setup for a project and someone forks that project, the shared runners will also serve jobs of this project. -# Attack vectors in runners +## Attack vectors in Runners Mentioned briefly earlier, but the following things of runners can be exploited. We're always looking for contributions that can mitigate these [Security Considerations](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md). From 3cfbd2b1601842feea0a7739098a90e2d1dccec4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 11 Apr 2016 18:27:34 +0100 Subject: [PATCH 056/213] Added loading icon to import buttons See #14488 for more details --- .../javascripts/importer_status.js.coffee | 37 +++++++++++++------ app/assets/stylesheets/pages/import.scss | 21 +++++++++++ app/views/import/bitbucket/status.html.haml | 20 +++++++--- app/views/import/fogbugz/status.html.haml | 15 ++++++-- app/views/import/github/status.html.haml | 15 ++++++-- app/views/import/gitlab/status.html.haml | 15 ++++++-- app/views/import/gitorious/status.html.haml | 15 ++++++-- app/views/import/google_code/status.html.haml | 19 +++++++--- 8 files changed, 119 insertions(+), 38 deletions(-) diff --git a/app/assets/javascripts/importer_status.js.coffee b/app/assets/javascripts/importer_status.js.coffee index be8d225e73..c51c4a1a18 100644 --- a/app/assets/javascripts/importer_status.js.coffee +++ b/app/assets/javascripts/importer_status.js.coffee @@ -4,18 +4,33 @@ class @ImporterStatus this.setAutoUpdate() initStatusPage: -> - $(".js-add-to-import").click (event) => - new_namespace = null - tr = $(event.currentTarget).closest("tr") - id = tr.attr("id").replace("repo_", "") - if tr.find(".import-target input").length > 0 - new_namespace = tr.find(".import-target input").prop("value") - tr.find(".import-target").empty().append(new_namespace + "/" + tr.find(".import-target").data("project_name")) - $.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script' + $(".js-add-to-import") + .off 'click' + .on 'click', (event) => + new_namespace = null + $btn = $(event.currentTarget) + $tr = $btn.closest("tr") + id = $tr.attr("id").replace("repo_", "") + if $tr.find(".import-target input").length > 0 + new_namespace = $tr.find(".import-target input").prop("value") + $tr.find(".import-target").empty().append(new_namespace + "/" + $tr.find(".import-target").data("project_name")) - $(".js-import-all").click (event) => - $(".js-add-to-import").each -> - $(this).click() + $btn + .disable() + .addClass 'is-loading' + + $.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script' + + $(".js-import-all") + .off 'click' + .on 'click', (event) => + $btn = $(event.currentTarget) + $btn + .disable() + .addClass 'is-loading' + + $(".js-add-to-import").each -> + $(this).click() setAutoUpdate: -> setInterval (=> diff --git a/app/assets/stylesheets/pages/import.scss b/app/assets/stylesheets/pages/import.scss index 6a99cd9cb9..84cc35239f 100644 --- a/app/assets/stylesheets/pages/import.scss +++ b/app/assets/stylesheets/pages/import.scss @@ -16,3 +16,24 @@ i.icon-gitorious-big { width: 18px; height: 18px; } + +.import-jobs-from-col, +.import-jobs-to-col { + width: 40%; +} + +.import-jobs-status-col { + width: 20%; +} + +.btn-import { + .loading-icon { + display: none; + } + + &.is-loading { + .loading-icon { + display: inline-block; + } + } +} diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index aec2e836c9..134d88481d 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -10,13 +10,19 @@ %hr %p - if @incompatible_repos.any? - = button_tag 'Import all compatible projects', class: "btn btn-success js-import-all" + = button_tag class: "btn btn-import btn-success js-import-all" do + = icon('spinner spin', class: 'loading-icon') + Import all compatible projects - else - = button_tag 'Import all projects', class: "btn btn-success js-import-all" + = button_tag class: "btn btn-success js-import-all" do + = icon('spinner spin', class: 'loading-icon') + Import all projects - -.table-holder +.table-responsive %table.table.import-jobs + %colgroup.import-jobs-from-col + %colgroup.import-jobs-to-col + %colgroup.import-jobs-status-col %thead %tr %th From Bitbucket @@ -28,7 +34,7 @@ %td = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: "_blank" %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span @@ -47,7 +53,9 @@ %td.import-target = "#{repo["owner"]}/#{repo["slug"]}" %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + = button_tag class: "btn btn-import js-add-to-import" do + = icon('spinner spin', class: 'loading-icon') + Import - @incompatible_repos.each do |repo| %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} %td diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index 6ee16c8be4..f5966fac65 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -13,10 +13,15 @@ how FogBugz email addresses and usernames are imported into GitLab. %hr %p - = button_tag 'Import all projects', class: 'btn btn-success js-import-all' + = button_tag class: 'btn btn-import btn-success js-import-all' do + = icon('spinner spin', class: 'loading-icon') + Import all projects -.table-holder +.table-responsive %table.table.import-jobs + %colgroup.import-jobs-from-col + %colgroup.import-jobs-to-col + %colgroup.import-jobs-status-col %thead %tr %th From FogBugz @@ -28,7 +33,7 @@ %td = project.import_source %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span @@ -47,7 +52,9 @@ %td.import-target = "#{current_user.username}/#{repo.name}" %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + = button_tag class: "btn btn-import js-add-to-import" do + = icon('spinner spin', class: 'loading-icon') + Import :javascript new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}"); diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 1416ee5bd5..65432951ec 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -8,10 +8,15 @@ Select projects you want to import. %hr %p - = button_tag 'Import all projects', class: "btn btn-success js-import-all" + = button_tag class: "btn btn-import btn-success js-import-all" do + = icon('spinner spin', class: 'loading-icon') + Import all projects -.table-holder +.table-responsive %table.table.import-jobs + %colgroup.import-jobs-from-col + %colgroup.import-jobs-to-col + %colgroup.import-jobs-status-col %thead %tr %th From GitHub @@ -23,7 +28,7 @@ %td = link_to project.import_source, "https://github.com/#{project.import_source}", target: "_blank" %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span @@ -42,7 +47,9 @@ %td.import-target = repo.full_name %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + = button_tag class: "btn btn-import js-add-to-import" do + = icon('spinner spin', class: 'loading-icon') + Import :javascript new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}"); diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index 911a55eb85..f20011f568 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -8,10 +8,15 @@ Select projects you want to import. %hr %p - = button_tag 'Import all projects', class: "btn btn-success js-import-all" + = button_tag class: "btn btn-import btn-success js-import-all" do + = icon('spinner spin', class: 'loading-icon') + Import all projects -.table-holder +.table-responsive %table.table.import-jobs + %colgroup.import-jobs-from-col + %colgroup.import-jobs-to-col + %colgroup.import-jobs-status-col %thead %tr %th From GitLab.com @@ -23,7 +28,7 @@ %td = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank" %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span @@ -42,7 +47,9 @@ %td.import-target = repo["path_with_namespace"] %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + = button_tag class: "btn js-add-to-import" do + = icon('spinner spin', class: 'loading-icon') + Import :javascript new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}"); diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml index 6b0fa1edf8..725910ed21 100644 --- a/app/views/import/gitorious/status.html.haml +++ b/app/views/import/gitorious/status.html.haml @@ -8,10 +8,15 @@ Select projects you want to import. %hr %p - = button_tag 'Import all projects', class: "btn btn-success js-import-all" + = button_tag class: "btn btn-import btn-success js-import-all" do + = icon('spinner spin', class: 'loading-icon') + Import all projects -.table-holder +.table-responsive %table.table.import-jobs + %colgroup.import-jobs-from-col + %colgroup.import-jobs-to-col + %colgroup.import-jobs-status-col %thead %tr %th From Gitorious.org @@ -23,7 +28,7 @@ %td = link_to project.import_source, "https://gitorious.org/#{project.import_source}", target: "_blank" %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span @@ -42,7 +47,9 @@ %td.import-target = repo.full_name %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + = button_tag class: "btn btn-import js-add-to-import" do + = icon('spinner spin', class: 'loading-icon') + Import :javascript new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}"); diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index 175ef6921c..2cb0931838 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -14,12 +14,19 @@ %hr %p - if @incompatible_repos.any? - = button_tag 'Import all compatible projects', class: "btn btn-success js-import-all" + = button_tag class: "btn btn-import btn-success js-import-all" do + = icon('spinner spin', class: 'loading-icon') + Import all compatible projects - else - = button_tag 'Import all projects', class: "btn btn-success js-import-all" + = button_tag class: "btn btn-import btn-success js-import-all" do + = icon('spinner spin', class: 'loading-icon') + Import all projects -.table-holder +.table-responsive %table.table.import-jobs + %colgroup.import-jobs-from-col + %colgroup.import-jobs-to-col + %colgroup.import-jobs-status-col %thead %tr %th From Google Code @@ -31,7 +38,7 @@ %td = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank" %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status - if project.import_status == 'finished' %span @@ -50,7 +57,9 @@ %td.import-target = "#{current_user.username}/#{repo.name}" %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + = button_tag class: "btn btn-import js-add-to-import" do + = icon('spinner spin', class: 'loading-icon') + Import - @incompatible_repos.each do |repo| %tr{id: "repo_#{repo.id}"} %td From 377b59da3029786f2265058972ad0bc6aefd8b53 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 12 Apr 2016 09:59:01 +0530 Subject: [PATCH 057/213] Sanitize branch names for confidential issues. - When creating new branches for confidential issues, prefer a branch name like `issue-15` to `some-sensitive-issue-title-15`. - The behaviour for non-confidential issues stays the same. --- app/models/issue.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/issue.rb b/app/models/issue.rb index e064b0f8b9..68e0113380 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -151,7 +151,11 @@ class Issue < ActiveRecord::Base end def to_branch_name - "#{title.parameterize}-#{iid}" + if self.confidential? + "issue-#{iid}" + else + "#{title.parameterize}-#{iid}" + end end def can_be_worked_on?(current_user) From 5d88de092f37497d9c08878954b099c425376bda Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 12 Apr 2016 11:43:15 +0530 Subject: [PATCH 058/213] Refactor `Issue#related_branches` - Previously, the controller held the logic to calculate related branches, which was: ` - ` - This logic belongs in the `related_branches` method, not in the controller. This commit makes this change. - This means that `Issue#related_branches` now needs to take a `User`. When we find the branches that have a merge request referenced in the current issue, this is limited to merge requests that the current user has access to. - This is not directly related to #14566, but is a related refactoring. --- app/controllers/projects/issues_controller.rb | 2 +- app/models/issue.rb | 13 ++++++++++--- spec/models/issue_spec.rb | 3 ++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 6d649e72f8..a384203698 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -66,7 +66,7 @@ class Projects::IssuesController < Projects::ApplicationController @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) + @related_branches = @issue.related_branches(current_user) respond_to do |format| format.html diff --git a/app/models/issue.rb b/app/models/issue.rb index 68e0113380..252545d114 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -104,10 +104,17 @@ class Issue < ActiveRecord::Base end end - def related_branches - project.repository.branch_names.select do |branch| + # All branches containing the current issue's ID, except for + # those with a merge request open (that the current user can see) + # referencing the current issue. + def related_branches(current_user) + branches_with_iid = project.repository.branch_names.select do |branch| branch.end_with?("-#{iid}") end + + branches_with_merge_request = self.referenced_merge_requests(current_user).map(&:source_branch) + + branches_with_iid - branches_with_merge_request end # Reset issue events cache @@ -161,7 +168,7 @@ class Issue < ActiveRecord::Base def can_be_worked_on?(current_user) !self.closed? && !self.project.forked? && - self.related_branches.empty? && + self.related_branches(current_user).empty? && self.closed_by_merge_requests(current_user).empty? end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 15052aaca2..f33b705810 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -192,10 +192,11 @@ describe Issue, models: true do describe '#related_branches' do it "selects the right branches" do + user = build(:user) allow(subject.project.repository).to receive(:branch_names). and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name]) - expect(subject.related_branches).to eq([subject.to_branch_name]) + expect(subject.related_branches(user)).to eq([subject.to_branch_name]) end end From 3918fce5bd073e18addb7d1d4aaf3c81ce8150b0 Mon Sep 17 00:00:00 2001 From: Baldinof Date: Tue, 12 Apr 2016 10:01:52 +0200 Subject: [PATCH 059/213] Hide number sign for string prefixed external issues --- app/models/external_issue.rb | 8 +++----- spec/models/external_issue_spec.rb | 13 +++++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index 92f1f17494..d47b479faa 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -34,15 +34,13 @@ class ExternalIssue %r{(?\b([A-Z][A-Z0-9_]+-)\d+)} end - def self.reference_prefix - '#' - end - def to_reference(_from_project = nil) id end def reference_link_text(from_project = nil) - "#{self.class.reference_prefix}#{id}" + return "##{id}" if /^\d+$/.match(id) + + id end end diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb index b3d0c4efe9..4fc3b06559 100644 --- a/spec/models/external_issue_spec.rb +++ b/spec/models/external_issue_spec.rb @@ -38,8 +38,17 @@ describe ExternalIssue, models: true do end describe '#reference_link_text' do - it 'returns a String reference to the object' do - expect(issue.reference_link_text).to eq '#EXT-1234' + context 'if issue id has a prefix' do + it 'returns the issue ID' do + expect(issue.reference_link_text).to eq 'EXT-1234' + end + end + + context 'if issue id is a number' do + let(:issue) { described_class.new('1234', project) } + it 'returns the issue ID prefixed by #' do + expect(issue.reference_link_text).to eq '#1234' + end end end end From a650af12261b850d9565cbfc473ea82db59f2e3d Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Apr 2016 09:48:39 +0100 Subject: [PATCH 060/213] Moved loading icon to match todo loading icon --- app/views/import/base/create.js.haml | 4 ++-- app/views/import/bitbucket/status.html.haml | 6 +++--- app/views/import/fogbugz/status.html.haml | 4 ++-- app/views/import/github/status.html.haml | 4 ++-- app/views/import/gitlab/status.html.haml | 4 ++-- app/views/import/gitorious/status.html.haml | 4 ++-- app/views/import/google_code/status.html.haml | 6 +++--- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml index d8af0295b2..ec7a2b8656 100644 --- a/app/views/import/base/create.js.haml +++ b/app/views/import/base/create.js.haml @@ -20,10 +20,10 @@ job.attr("id", "project_#{@project.id}") target_field = job.find(".import-target") target_field.empty() - target_field.append('#{link_to @project.path_with_namespace, namespace_project_path(@project.namespace, @project)}') + target_field.append('#{link_to @project.path_with_namespace, namespace_project_path(@project.namespace, @project)}') $("table.import-jobs tbody").prepend(job) job.addClass("active").find(".import-actions").html(" started") - else :plain job = $("tr#repo_#{@repo_id}") - job.find(".import-actions").html(" Error saving project: #{escape_javascript(@project.errors.full_messages.join(','))}") + job.find(".import-actions").html(" Error saving project: #{escape_javascript(@project.errors.full_messages.join(','))}") diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index 134d88481d..e24c01f3e0 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -11,12 +11,12 @@ %p - if @incompatible_repos.any? = button_tag class: "btn btn-import btn-success js-import-all" do - = icon('spinner spin', class: 'loading-icon') Import all compatible projects + = icon('spinner spin', class: 'loading-icon') - else = button_tag class: "btn btn-success js-import-all" do - = icon('spinner spin', class: 'loading-icon') Import all projects + = icon('spinner spin', class: 'loading-icon') .table-responsive %table.table.import-jobs @@ -54,8 +54,8 @@ = "#{repo["owner"]}/#{repo["slug"]}" %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do - = icon('spinner spin', class: 'loading-icon') Import + = icon('spinner spin', class: 'loading-icon') - @incompatible_repos.each do |repo| %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} %td diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index f5966fac65..d842d1cbd9 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -14,8 +14,8 @@ %hr %p = button_tag class: 'btn btn-import btn-success js-import-all' do - = icon('spinner spin', class: 'loading-icon') Import all projects + = icon('spinner spin', class: 'loading-icon') .table-responsive %table.table.import-jobs @@ -53,8 +53,8 @@ = "#{current_user.username}/#{repo.name}" %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do - = icon('spinner spin', class: 'loading-icon') Import + = icon('spinner spin', class: 'loading-icon') :javascript new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}"); diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 65432951ec..d6fa0d8023 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -9,8 +9,8 @@ %hr %p = button_tag class: "btn btn-import btn-success js-import-all" do - = icon('spinner spin', class: 'loading-icon') Import all projects + = icon('spinner spin', class: 'loading-icon') .table-responsive %table.table.import-jobs @@ -48,8 +48,8 @@ = repo.full_name %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do - = icon('spinner spin', class: 'loading-icon') Import + = icon('spinner spin', class: 'loading-icon') :javascript new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}"); diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index f20011f568..8f79f38561 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -9,8 +9,8 @@ %hr %p = button_tag class: "btn btn-import btn-success js-import-all" do - = icon('spinner spin', class: 'loading-icon') Import all projects + = icon('spinner spin', class: 'loading-icon') .table-responsive %table.table.import-jobs @@ -48,8 +48,8 @@ = repo["path_with_namespace"] %td.import-actions.job-status = button_tag class: "btn js-add-to-import" do - = icon('spinner spin', class: 'loading-icon') Import + = icon('spinner spin', class: 'loading-icon') :javascript new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}"); diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml index 725910ed21..f0256e0f93 100644 --- a/app/views/import/gitorious/status.html.haml +++ b/app/views/import/gitorious/status.html.haml @@ -9,8 +9,8 @@ %hr %p = button_tag class: "btn btn-import btn-success js-import-all" do - = icon('spinner spin', class: 'loading-icon') Import all projects + = icon('spinner spin', class: 'loading-icon') .table-responsive %table.table.import-jobs @@ -48,8 +48,8 @@ = repo.full_name %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do - = icon('spinner spin', class: 'loading-icon') Import + = icon('spinner spin', class: 'loading-icon') :javascript new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}"); diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index 2cb0931838..c4ac1b4f9b 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -15,12 +15,12 @@ %p - if @incompatible_repos.any? = button_tag class: "btn btn-import btn-success js-import-all" do - = icon('spinner spin', class: 'loading-icon') Import all compatible projects + = icon('spinner spin', class: 'loading-icon') - else = button_tag class: "btn btn-import btn-success js-import-all" do - = icon('spinner spin', class: 'loading-icon') Import all projects + = icon('spinner spin', class: 'loading-icon') .table-responsive %table.table.import-jobs @@ -58,8 +58,8 @@ = "#{current_user.username}/#{repo.name}" %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do - = icon('spinner spin', class: 'loading-icon') Import + = icon('spinner spin', class: 'loading-icon') - @incompatible_repos.each do |repo| %tr{id: "repo_#{repo.id}"} %td From 91034af3c998ce4a4f83281525304e8c50add384 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 12 Apr 2016 15:50:19 +0530 Subject: [PATCH 061/213] Augment the tests for `Issue#related_branches` - Test the case where we have a referenced merge request that's being - excluded as a "related branch" - This took a while to figure out, especially the `create_cross_references!` line. --- spec/models/issue_spec.rb | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index f33b705810..8cacce6a7b 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -191,11 +191,27 @@ describe Issue, models: true do end describe '#related_branches' do - it "selects the right branches" do - user = build(:user) - allow(subject.project.repository).to receive(:branch_names). - and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name]) + let(:user) { build(:user, :admin) } + let(:merge_request) { create(:merge_request, description: "Closes ##{subject.iid}", + source_project: subject.project, source_branch: "branch-#{subject.iid}") } + before(:each) do + allow(subject.project.repository).to receive(:branch_names). + and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "branch-#{subject.iid}"]) + + # Without this stub, the `create(:merge_request)` above fails because it can't find + # the source branch. This seems like a reasonable compromise, in comparison with + # setting up a full repo here. + allow_any_instance_of(MergeRequest).to receive(:create_merge_request_diff) + end + + it "selects the right branches when there are no referenced merge requests" do + expect(subject.related_branches(user)).to eq([subject.to_branch_name, "branch-#{subject.iid}"]) + end + + it "selects the right branches when there is a referenced merge request" do + merge_request.create_cross_references!(user) + expect(subject.referenced_merge_requests).to_not be_empty expect(subject.related_branches(user)).to eq([subject.to_branch_name]) end end From 66ca80181bcb5aef97296fde567b899603186be6 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 12 Apr 2016 16:01:44 +0530 Subject: [PATCH 062/213] Test the `Issue#to_branch_name` method. --- spec/models/issue_spec.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 8cacce6a7b..22dabaa040 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -228,10 +228,19 @@ describe Issue, models: true do end describe "#to_branch_name" do - let(:issue) { create(:issue, title: 'a' * 30) } + let(:issue) { create(:issue, title: 'testing-issue') } - it "starts with the issue iid" do + it "ends with the issue iid" do expect(issue.to_branch_name).to match /-#{issue.iid}\z/ end + + it "contains the issue title if not confidential" do + expect(issue.to_branch_name).to match /\Atesting-issue/ + end + + it "does not contain the issue title if confidential" do + issue = create(:issue, title: 'testing-issue', confidential: true) + expect(issue.to_branch_name).to match /\Aissue/ + end end end From 8dd1a6fc917231e83fb64dbfa65e1edbe9ab3fee Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Wed, 13 Apr 2016 09:04:08 +0530 Subject: [PATCH 063/213] Fix the rubocop check. --- spec/models/issue_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 22dabaa040..202ce846dc 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -192,8 +192,6 @@ describe Issue, models: true do describe '#related_branches' do let(:user) { build(:user, :admin) } - let(:merge_request) { create(:merge_request, description: "Closes ##{subject.iid}", - source_project: subject.project, source_branch: "branch-#{subject.iid}") } before(:each) do allow(subject.project.repository).to receive(:branch_names). @@ -210,6 +208,9 @@ describe Issue, models: true do end it "selects the right branches when there is a referenced merge request" do + merge_request = create(:merge_request, { description: "Closes ##{subject.iid}", + source_project: subject.project, + source_branch: "branch-#{subject.iid}" }) merge_request.create_cross_references!(user) expect(subject.referenced_merge_requests).to_not be_empty expect(subject.related_branches(user)).to eq([subject.to_branch_name]) From 769a8bf7284da492732d923cb888c0f5be16aff8 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Wed, 13 Apr 2016 09:18:29 +0530 Subject: [PATCH 064/213] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 3561c541df..738162d983 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -34,6 +34,7 @@ v 8.7.0 (unreleased) - API: Expose user location (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 + - Sanitize branch names created for confidential issues v 8.6.5 - Fix importing from GitHub Enterprise. !3529 From e57a9551df0a6b6de218bf881322fcba5a77485b Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Wed, 13 Apr 2016 12:02:20 +0530 Subject: [PATCH 065/213] Don't populate the password field on signup validation errors. - Previously, we were pulling `params[:user][:password] as the default value for the password field. This is incorrect; we should be pulling it from `@user.password` or the like. --- app/views/devise/shared/_signup_box.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index cb93ff2465..7f23dbfed9 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -17,7 +17,7 @@ %div = f.email_field :email, class: "form-control middle", value: user[:email], placeholder: "Email", required: true .form-group.append-bottom-20#password-strength - = f.password_field :password, class: "form-control bottom", value: user[:password], id: "user_password_sign_up", placeholder: "Password", required: true + = f.password_field :password, class: "form-control bottom", id: "user_password_sign_up", placeholder: "Password", required: true %div - if current_application_settings.recaptcha_enabled = recaptcha_tags From d5f44d8a4b23b20f8292147db9c7f7730de70a3b Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Wed, 13 Apr 2016 12:57:40 +0530 Subject: [PATCH 066/213] Don't retrieve default values from `params` - In the signup page. --- app/views/devise/shared/_signup_box.html.haml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 7f23dbfed9..e5607dacd0 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -6,16 +6,15 @@ .login-heading %h3 Create an account .login-body - - user = params[:user].present? ? params[:user] : {} = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| .devise-errors = devise_error_messages! %div - = f.text_field :name, class: "form-control top", value: user[:name], placeholder: "Name", required: true + = f.text_field :name, class: "form-control top", placeholder: "Name", required: true %div - = f.text_field :username, class: "form-control middle", value: user[:username], placeholder: "Username", required: true + = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true %div - = f.email_field :email, class: "form-control middle", value: user[:email], placeholder: "Email", required: true + = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true .form-group.append-bottom-20#password-strength = f.password_field :password, class: "form-control bottom", id: "user_password_sign_up", placeholder: "Password", required: true %div From 970b2ed9da79b39123d2e21e6e6d5c70779e903c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 13 Apr 2016 16:10:03 +0100 Subject: [PATCH 067/213] Updated MR compare Based on in feedback in !3228 --- app/assets/stylesheets/pages/commits.scss | 5 +++ app/views/projects/commits/_commit.html.haml | 2 +- .../merge_requests/_new_compare.html.haml | 32 +++++++------------ .../dropdowns/_branch.html.haml | 5 +++ .../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 6453c91d95..c8c6bbde08 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 7da8923124..34f27f1e79 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 7d7c487e97..3e2a37e050 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 0000000000..ba8d9a5835 --- /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 0000000000..25d5dc92f8 --- /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 1b93188a10..64482973a8 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 From a5755812847d6866c51738a0b36927aa809b47d2 Mon Sep 17 00:00:00 2001 From: "P.S.V.R" Date: Wed, 17 Feb 2016 19:31:54 +0800 Subject: [PATCH 068/213] 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 2ab0cc1124..42f73259fe 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 fadc8bb2c9..c4b0e48434 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 From f2aacc25fb5ded3c4aa70495d05aaad2cf9b23f8 Mon Sep 17 00:00:00 2001 From: Hyunwoo Jung Date: Wed, 13 Apr 2016 21:19:50 +0000 Subject: [PATCH 069/213] 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 9aba4326e1..6a42a935ab 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 From 3d438870f34cb1bd5667bc5db2f848c4f82d5d25 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 09:32:02 +0100 Subject: [PATCH 070/213] Updated based on feedback --- .../javascripts/importer_status.js.coffee | 20 +++++++++---------- app/views/import/base/create.js.haml | 2 +- app/views/import/bitbucket/status.html.haml | 6 +++--- app/views/import/fogbugz/status.html.haml | 4 ++-- app/views/import/github/status.html.haml | 4 ++-- app/views/import/gitlab/status.html.haml | 4 ++-- app/views/import/gitorious/status.html.haml | 4 ++-- app/views/import/google_code/status.html.haml | 6 +++--- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/importer_status.js.coffee b/app/assets/javascripts/importer_status.js.coffee index c51c4a1a18..95f9f86c0a 100644 --- a/app/assets/javascripts/importer_status.js.coffee +++ b/app/assets/javascripts/importer_status.js.coffee @@ -4,16 +4,16 @@ class @ImporterStatus this.setAutoUpdate() initStatusPage: -> - $(".js-add-to-import") + $('.js-add-to-import') .off 'click' .on 'click', (event) => new_namespace = null $btn = $(event.currentTarget) - $tr = $btn.closest("tr") - id = $tr.attr("id").replace("repo_", "") - if $tr.find(".import-target input").length > 0 - new_namespace = $tr.find(".import-target input").prop("value") - $tr.find(".import-target").empty().append(new_namespace + "/" + $tr.find(".import-target").data("project_name")) + $tr = $btn.closest('tr') + id = $tr.attr('id').replace('repo_', '') + if $tr.find('.import-target input').length > 0 + new_namespace = $tr.find('.import-target input').prop('value') + $tr.find('.import-target').empty().append("#{new_namespace} / #{$tr.find('.import-target').data('project_name')}") $btn .disable() @@ -21,15 +21,15 @@ class @ImporterStatus $.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script' - $(".js-import-all") + $('.js-import-all') .off 'click' - .on 'click', (event) => - $btn = $(event.currentTarget) + .on 'click', (e) => + $btn = $(e.currentTarget) $btn .disable() .addClass 'is-loading' - $(".js-add-to-import").each -> + $('.js-add-to-import').each -> $(this).click() setAutoUpdate: -> diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml index ec7a2b8656..dfebf7768d 100644 --- a/app/views/import/base/create.js.haml +++ b/app/views/import/base/create.js.haml @@ -26,4 +26,4 @@ - else :plain job = $("tr#repo_#{@repo_id}") - job.find(".import-actions").html(" Error saving project: #{escape_javascript(@project.errors.full_messages.join(','))}") + job.find(".import-actions").html(" Error saving project: #{escape_javascript(@project.errors.full_messages.join(','))}") diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index e24c01f3e0..6e993e58f0 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -12,11 +12,11 @@ - if @incompatible_repos.any? = button_tag class: "btn btn-import btn-success js-import-all" do Import all compatible projects - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") - else = button_tag class: "btn btn-success js-import-all" do Import all projects - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") .table-responsive %table.table.import-jobs @@ -55,7 +55,7 @@ %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do Import - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") - @incompatible_repos.each do |repo| %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} %td diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index d842d1cbd9..d3d3c595c1 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -15,7 +15,7 @@ %p = button_tag class: 'btn btn-import btn-success js-import-all' do Import all projects - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") .table-responsive %table.table.import-jobs @@ -54,7 +54,7 @@ %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do Import - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") :javascript new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}"); diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index d6fa0d8023..9639da4cb5 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -10,7 +10,7 @@ %p = button_tag class: "btn btn-import btn-success js-import-all" do Import all projects - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") .table-responsive %table.table.import-jobs @@ -49,7 +49,7 @@ %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do Import - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") :javascript new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}"); diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index 8f79f38561..e3a356b537 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -10,7 +10,7 @@ %p = button_tag class: "btn btn-import btn-success js-import-all" do Import all projects - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") .table-responsive %table.table.import-jobs @@ -49,7 +49,7 @@ %td.import-actions.job-status = button_tag class: "btn js-add-to-import" do Import - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") :javascript new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}"); diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml index f0256e0f93..267eee4f26 100644 --- a/app/views/import/gitorious/status.html.haml +++ b/app/views/import/gitorious/status.html.haml @@ -10,7 +10,7 @@ %p = button_tag class: "btn btn-import btn-success js-import-all" do Import all projects - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") .table-responsive %table.table.import-jobs @@ -49,7 +49,7 @@ %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do Import - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") :javascript new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}"); diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index c4ac1b4f9b..5ada6b174e 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -16,11 +16,11 @@ - if @incompatible_repos.any? = button_tag class: "btn btn-import btn-success js-import-all" do Import all compatible projects - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") - else = button_tag class: "btn btn-import btn-success js-import-all" do Import all projects - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") .table-responsive %table.table.import-jobs @@ -59,7 +59,7 @@ %td.import-actions.job-status = button_tag class: "btn btn-import js-add-to-import" do Import - = icon('spinner spin', class: 'loading-icon') + = icon("spinner spin", class: "loading-icon") - @incompatible_repos.each do |repo| %tr{id: "repo_#{repo.id}"} %td From 65410e63ac8c2e4e25acabb73ede6a2dfdc3d829 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 10:15:38 +0100 Subject: [PATCH 071/213] Variable name --- app/assets/javascripts/importer_status.js.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/importer_status.js.coffee b/app/assets/javascripts/importer_status.js.coffee index 95f9f86c0a..2eb151a641 100644 --- a/app/assets/javascripts/importer_status.js.coffee +++ b/app/assets/javascripts/importer_status.js.coffee @@ -6,9 +6,9 @@ class @ImporterStatus initStatusPage: -> $('.js-add-to-import') .off 'click' - .on 'click', (event) => + .on 'click', (e) => new_namespace = null - $btn = $(event.currentTarget) + $btn = $(e.currentTarget) $tr = $btn.closest('tr') id = $tr.attr('id').replace('repo_', '') if $tr.find('.import-target input').length > 0 From efff7e9e54e1d1ebbb36dc8dca35b894aeab2630 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 14 Apr 2016 12:03:45 +0200 Subject: [PATCH 072/213] updated migration based on testing findings --- ...8_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 b97b79f920..8a351cf27a 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 3cfbc17b89..d23b013c1f 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 From e490a54105b6c820e330514aeb91d004997b031c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 14:50:48 +0100 Subject: [PATCH 073/213] Discussion notes update At the moment discussion notes are rendered in a list but arent actually a list item and that can cause overflow issues Moved the dates to be in the header like other notes Fixed overlapping issue on mobile --- .../stylesheets/framework/timeline.scss | 3 +-- app/assets/stylesheets/pages/notes.scss | 6 +++++ .../projects/notes/_discussion.html.haml | 23 +++++++++---------- app/views/projects/notes/_notes.html.haml | 3 ++- .../notes/discussions/_active.html.haml | 6 +---- .../notes/discussions/_commit.html.haml | 9 +++----- .../notes/discussions/_outdated.html.haml | 6 +---- 7 files changed, 25 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss index b91f2f6f89..f0ec250de2 100644 --- a/app/assets/stylesheets/framework/timeline.scss +++ b/app/assets/stylesheets/framework/timeline.scss @@ -39,8 +39,7 @@ .diff-file { border: 1px solid $border-color; border-bottom: none; - margin-left: 0; - margin-right: 0; + margin: 0; } } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index e421a31549..7489d5de5f 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -198,6 +198,12 @@ ul.notes { color: $notes-light-color; } +.discussion-headline-light { + a { + color: $gl-link-color; + } +} + /** * Actions for Discussions/Notes */ diff --git a/app/views/projects/notes/_discussion.html.haml b/app/views/projects/notes/_discussion.html.haml index b8068835b3..b66fead09b 100644 --- a/app/views/projects/notes/_discussion.html.haml +++ b/app/views/projects/notes/_discussion.html.haml @@ -1,13 +1,12 @@ - note = discussion_notes.first -.timeline-entry - .timeline-entry-inner - .timeline-icon - = link_to user_path(note.author) do - = image_tag avatar_icon(note.author_email), class: "avatar s40" - .timeline-content - - if note.for_merge_request? - - (active_notes, outdated_notes) = discussion_notes.partition(&:active?) - = render "projects/notes/discussions/active", discussion_notes: active_notes if active_notes.length > 0 - = render "projects/notes/discussions/outdated", discussion_notes: outdated_notes if outdated_notes.length > 0 - - else - = render "projects/notes/discussions/commit", discussion_notes: discussion_notes +.timeline-entry-inner + .timeline-icon + = link_to user_path(note.author) do + = image_tag avatar_icon(note.author_email), class: "avatar s40" + .timeline-content + - if note.for_merge_request? + - (active_notes, outdated_notes) = discussion_notes.partition(&:active?) + = render "projects/notes/discussions/active", discussion_notes: active_notes if active_notes.length > 0 + = render "projects/notes/discussions/outdated", discussion_notes: outdated_notes if outdated_notes.length > 0 + - else + = render "projects/notes/discussions/commit", discussion_notes: discussion_notes diff --git a/app/views/projects/notes/_notes.html.haml b/app/views/projects/notes/_notes.html.haml index 62db86fb18..4eeaf70e98 100644 --- a/app/views/projects/notes/_notes.html.haml +++ b/app/views/projects/notes/_notes.html.haml @@ -6,7 +6,8 @@ = render discussion_notes - else - = render 'projects/notes/discussion', discussion_notes: discussion_notes + %li.note.note-discussion.timeline-entry + = render 'projects/notes/discussion', discussion_notes: discussion_notes - else - @notes.each do |note| - next unless note.author diff --git a/app/views/projects/notes/discussions/_active.html.haml b/app/views/projects/notes/discussions/_active.html.haml index cd8a5f0bd0..0ea8862a68 100644 --- a/app/views/projects/notes/discussions/_active.html.haml +++ b/app/views/projects/notes/discussions/_active.html.haml @@ -6,15 +6,11 @@ = "#{note.author.to_reference} started a discussion" = link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do on the diff + = time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "discussion_updated_ago") .discussion-actions = link_to "#", class: "discussion-action-button discussion-toggle-button js-toggle-button" do %i.fa.fa-chevron-up Show/hide discussion - .last-update.hide.js-toggle-content - - last_note = discussion_notes.last - last updated by - = link_to_member(@project, last_note.author, avatar: false) - #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')} .discussion-body.js-toggle-content = render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note diff --git a/app/views/projects/notes/discussions/_commit.html.haml b/app/views/projects/notes/discussions/_commit.html.haml index 46f2ba4bbc..2a2ead58ee 100644 --- a/app/views/projects/notes/discussions/_commit.html.haml +++ b/app/views/projects/notes/discussions/_commit.html.haml @@ -8,21 +8,18 @@ = "#{note.author.to_reference} started a discussion on #{commit_description}" - if commit = link_to(commit.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable), class: 'monospace') + = time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "discussion_updated_ago") .discussion-actions = link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do %i.fa.fa-chevron-up Show/hide discussion - .last-update.hide.js-toggle-content - - last_note = discussion_notes.last - last updated by - = link_to_member(@project, last_note.author, avatar: false) - #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')} .discussion-body.js-toggle-content - if note.for_diff_line? = render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note - else .panel.panel-default .notes{ data: { discussion_id: discussion_notes.first.discussion_id } } - = render discussion_notes + %ul.notes.timeline + = render discussion_notes .discussion-reply-holder = link_to_reply_diff(discussion_notes.first) diff --git a/app/views/projects/notes/discussions/_outdated.html.haml b/app/views/projects/notes/discussions/_outdated.html.haml index f8e000b424..45141bcd1d 100644 --- a/app/views/projects/notes/discussions/_outdated.html.haml +++ b/app/views/projects/notes/discussions/_outdated.html.haml @@ -5,14 +5,10 @@ .inline.discussion-headline-light = "#{note.author.to_reference} started a discussion" on the outdated diff + = time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "discussion_updated_ago") .discussion-actions = link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do %i.fa.fa-chevron-down Show/hide discussion - .last-update.hide.js-toggle-content - - last_note = discussion_notes.last - last updated by - = link_to_member(@project, last_note.author, avatar: false) - #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')} .discussion-body.js-toggle-content.hide = render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note From 44f3f42bac143f87d756ed38fa7142f98c7be90d Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 14:56:05 +0100 Subject: [PATCH 074/213] Dicussion action mobile fix --- app/assets/stylesheets/pages/notes.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 7489d5de5f..e4f96c11aa 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -215,6 +215,17 @@ ul.notes { color: $notes-action-color; } +.discussion-actions { + @media (max-width: $screen-sm-max) { + float: none; + margin-left: 0; + + .note-action-button { + margin-left: 0; + } + } +} + .note-action-button, .discussion-action-button { display: inline-block; From 01f70fc97b887dcbab8d859d2fc6ded28a2c3718 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 15:07:38 +0100 Subject: [PATCH 075/213] Fixed issue with commit time not using timeago --- app/assets/javascripts/application.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 6f435e4c54..5bac8eef1c 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -174,7 +174,7 @@ $ -> $('.trigger-submit').on 'change', -> $(@).parents('form').submit() - gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), false) + gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true) # Flash if (flash = $(".flash-container")).length > 0 From 46d1cf43a43a4d7a25f25be97d1fee79cefdc773 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 14 Apr 2016 18:05:53 +0200 Subject: [PATCH 076/213] update changelog [ci skip] --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 1d1e541e65..cfe47a4e56 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 From 038e623042af614fb739f49a9302ec42cab9812a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 17:26:32 +0100 Subject: [PATCH 077/213] Fixed tests --- .../projects/notes/_discussion.html.haml | 23 ++++++++++--------- app/views/projects/notes/_notes.html.haml | 3 +-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/views/projects/notes/_discussion.html.haml b/app/views/projects/notes/_discussion.html.haml index b66fead09b..572b00a38c 100644 --- a/app/views/projects/notes/_discussion.html.haml +++ b/app/views/projects/notes/_discussion.html.haml @@ -1,12 +1,13 @@ - note = discussion_notes.first -.timeline-entry-inner - .timeline-icon - = link_to user_path(note.author) do - = image_tag avatar_icon(note.author_email), class: "avatar s40" - .timeline-content - - if note.for_merge_request? - - (active_notes, outdated_notes) = discussion_notes.partition(&:active?) - = render "projects/notes/discussions/active", discussion_notes: active_notes if active_notes.length > 0 - = render "projects/notes/discussions/outdated", discussion_notes: outdated_notes if outdated_notes.length > 0 - - else - = render "projects/notes/discussions/commit", discussion_notes: discussion_notes +%li.note.note-discussion.timeline-entry + .timeline-entry-inner + .timeline-icon + = link_to user_path(note.author) do + = image_tag avatar_icon(note.author_email), class: "avatar s40" + .timeline-content + - if note.for_merge_request? + - (active_notes, outdated_notes) = discussion_notes.partition(&:active?) + = render "projects/notes/discussions/active", discussion_notes: active_notes if active_notes.length > 0 + = render "projects/notes/discussions/outdated", discussion_notes: outdated_notes if outdated_notes.length > 0 + - else + = render "projects/notes/discussions/commit", discussion_notes: discussion_notes diff --git a/app/views/projects/notes/_notes.html.haml b/app/views/projects/notes/_notes.html.haml index 4eeaf70e98..62db86fb18 100644 --- a/app/views/projects/notes/_notes.html.haml +++ b/app/views/projects/notes/_notes.html.haml @@ -6,8 +6,7 @@ = render discussion_notes - else - %li.note.note-discussion.timeline-entry - = render 'projects/notes/discussion', discussion_notes: discussion_notes + = render 'projects/notes/discussion', discussion_notes: discussion_notes - else - @notes.each do |note| - next unless note.author From 101f57d9170f5ea8e736f17c2fd8294bbc15e142 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Thu, 14 Apr 2016 16:26:48 -0500 Subject: [PATCH 078/213] Wrap code in parallel view --- app/assets/stylesheets/pages/notes.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index e421a31549..21eca0a036 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -171,6 +171,11 @@ ul.notes { &.parallel { border-width: 1px; + + .code, + code { + white-space: pre-wrap; + } } .notes { From f5ce601c2b052b18398d2207c64ce8828b628521 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Fri, 15 Apr 2016 09:31:26 +0530 Subject: [PATCH 079/213] Make a few style changes based on MR feedback. --- spec/models/issue_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 202ce846dc..ed982d8a9d 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -191,9 +191,9 @@ describe Issue, models: true do end describe '#related_branches' do - let(:user) { build(:user, :admin) } + let(:user) { build(:admin) } - before(:each) do + before do allow(subject.project.repository).to receive(:branch_names). and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "branch-#{subject.iid}"]) From cfcd95b0c3c0c3699de8724e60cc2bce687a9132 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Fri, 15 Apr 2016 10:03:21 +0530 Subject: [PATCH 080/213] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 071e35167f..963d2ad681 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -43,6 +43,7 @@ v 8.7.0 (unreleased) - Update number of Todos in the sidebar when it's marked as "Done". !3600 - 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 + - While signing up, don't persist the user password across form redisplays v 8.6.6 - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) From ca725aaf4ca7cb300c50d539fe27efc45a32dfd6 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 15 Apr 2016 08:52:26 +0200 Subject: [PATCH 081/213] resolve merge conflict --- CHANGELOG | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d0638c880c..6de5a9b938 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) From fcf0612f38b719212a7b40cdaf6b026f5b5f59e7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 15 Apr 2016 09:47:46 +0100 Subject: [PATCH 082/213] Updated note form focus to better match form-control focus --- app/assets/stylesheets/pages/note_form.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 07c707e7b7..c2371d7998 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -61,11 +61,11 @@ padding: $gl-padding-top $gl-padding; border: 1px solid $note-form-border-color; border-radius: $border-radius-base; + transition: border-color ease-in-out 0.15s, + box-shadow ease-in-out 0.15s; &.is-focused { - border-color: $focus-border-color; - box-shadow: 0 0 2px $black-transparent, - 0 0 4px rgba($focus-border-color, .4); + @extend .form-control:focus; .comment-toolbar, .nav-links { 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: [PATCH 083/213] 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 b9bb6e7608..db44a9a44d 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 From 106c443e76fe143ef3d7ef504b404fa4a2667e93 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 15 Apr 2016 13:30:33 +0200 Subject: [PATCH 084/213] 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 b9bb6e7608..0c3dbc744b 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 From 3c704c33e0d6c91ecc156d8bcdf260b0c4c23a27 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 15 Apr 2016 14:21:28 +0200 Subject: [PATCH 085/213] 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 69b464bdc6..453d0ba1ac 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 8906217048..308c590e3f 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 b9bb6e7608..4c6f0d3d68 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 86f68b3a0a..c163001b7c 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 5b7ba52181..477551f503 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 From 5b374226a76b7369876bad84671be58bcf6e1b4c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 15 Apr 2016 16:36:07 +0100 Subject: [PATCH 086/213] Indented code --- .../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 3e2a37e050..b08524574e 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 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: [PATCH 087/213] 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 8c0e0f2f0d..df4e24f112 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) From 2c5ab1b61f3b75298381e3258e303322c58f4808 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 15 Apr 2016 18:24:16 +0100 Subject: [PATCH 088/213] Removes dropzone focus class after dropping file --- app/assets/javascripts/dropzone_input.js.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index 6eb8d27ee2..e2194589b3 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -61,6 +61,7 @@ class @DropzoneInput return drop: -> + $mdArea.removeClass 'is-dropzone-hover' form.find(".div-dropzone-hover").css "opacity", 0 form_textarea.focus() return From f2d94c5d8627adee985e605a18cf147003ef16de Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Fri, 15 Apr 2016 16:05:37 -0400 Subject: [PATCH 089/213] Scroll to the last comment I made and edit it. --- app/assets/javascripts/notes.js.coffee | 27 +++++++++++++++++++++--- app/views/projects/notes/_note.html.haml | 9 ++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index fa91baa07c..a3d0d212be 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -75,6 +75,9 @@ class @Notes # when issue status changes, we need to refresh data $(document).on "issuable:change", @refresh + # when a key is clicked on the notes + $(document).on "keydown", ".js-note-text", @keydownNoteText + cleanBinding: -> $(document).off "ajax:success", ".js-main-target-form" $(document).off "ajax:success", ".js-discussion-note-form" @@ -92,10 +95,19 @@ class @Notes $(document).off "click", ".js-note-target-reopen" $(document).off "click", ".js-note-target-close" $(document).off "click", ".js-note-discard" + $(document).off "keydown", ".js-note-text" $('.note .js-task-list-container').taskList('disable') $(document).off 'tasklist:changed', '.note .js-task-list-container' + keydownNoteText: (e) -> + $this = $(this) + if $this.val() is '' and e.which is 38 #aka the up key + myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last") + if myLastNote.length + myLastNoteEditBtn = myLastNote.find('.js-note-edit') + myLastNoteEditBtn.trigger('click', [true, myLastNote]) + initRefresh: -> clearInterval(Notes.interval) Notes.interval = setInterval => @@ -343,7 +355,7 @@ class @Notes Adds a hidden div with the original content of the note to fill the edit note form with if the user cancels ### - showEditForm: (e) -> + showEditForm: (e, scrollTo, myLastNote) -> e.preventDefault() note = $(this).closest(".note") note.addClass "is-editting" @@ -355,8 +367,17 @@ class @Notes note.find(".js-note-attachment-delete").show() new GLForm form - - form.find(".js-note-text").focus() + if scrollTo? and myLastNote? + # scroll to the bottom + # so the open of the last element doesn't make a jump + $('html, body').scrollTop($(document).height()); + $('html, body').animate({ + scrollTop: myLastNote.offset().top - 150 + }, 500, -> + form.find(".js-note-text").focus() + ); + else + form.find(".js-note-text").focus() ### Called in response to clicking the edit note link diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 03a44ca99c..6e9ecdf764 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -1,4 +1,5 @@ -%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)] } +- note_editable = note_editable?(note) +%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable} } .timeline-entry-inner .timeline-icon %a{href: user_path(note.author)} @@ -15,16 +16,16 @@ - if access %span.note-role = access - - if note_editable?(note) + - if note_editable = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do = icon('pencil') = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete danger' do = icon('trash-o') - .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''} + .note-body{class: note_editable ? 'js-task-list-container' : ''} .note-text = preserve do = markdown(note.note, pipeline: :note, cache_key: [note, "note"]) - - if note_editable?(note) + - if note_editable = render 'projects/notes/edit_form', note: note = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) From 262ca7b651037e02d0de662b6462fe4edfa77e81 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Fri, 15 Apr 2016 16:36:25 -0400 Subject: [PATCH 090/213] When editing put the cursor at the end of the textarea --- app/assets/javascripts/notes.js.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index a3d0d212be..c2e103ff85 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -374,7 +374,11 @@ class @Notes $('html, body').animate({ scrollTop: myLastNote.offset().top - 150 }, 500, -> - form.find(".js-note-text").focus() + $noteText = form.find(".js-note-text") + $noteText.focus() + # Neat little trick to put the cursor at the end + noteTextVal = $noteText.val() + $noteText.val('').val(noteTextVal); ); else form.find(".js-note-text").focus() From 47c6111546c32ec0b1ec4f1720e0e29c455cb438 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 15 Apr 2016 17:17:48 -0400 Subject: [PATCH 091/213] 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 07037a14f5..2c34f9c454 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) From 0fcdcadd75514fd8f1526bff2f1becb43c6d25f6 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 15 Apr 2016 23:00:32 +0100 Subject: [PATCH 092/213] Fixed issue with dropzone not working on new issuable Closes #15295 --- app/views/shared/issuable/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index aed2622a6d..bae15b7f84 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -4,7 +4,7 @@ = f.label :title, class: 'control-label' .col-sm-10 = f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off', - class: 'form-control pad js-gfm-input', required: true + class: 'form-control pad', required: true - if issuable.is_a?(MergeRequest) %p.help-block From eceada9a6868d16d9a11ce8bc646623f20f58c95 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Sat, 16 Apr 2016 09:43:49 +0100 Subject: [PATCH 093/213] Fix issue with go full screen triggering attach file --- app/views/projects/_md_preview.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index 7a78d61a61..8de44a6c91 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -1,6 +1,6 @@ .md-area .md-header - %ul.nav-links + %ul.nav-links.clearfix %li.active %a.js-md-write-button{ href: "#md-write-holder", tabindex: -1 } Write From d744a04a5b4b46d970cf3c6c4030fc61718c59c0 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Sat, 16 Apr 2016 09:23:25 +0000 Subject: [PATCH 094/213] fix TYPO in the changelog --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index c948e8e546..f1320b6a28 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 From 562c9652d687bb03d0aa5b744016946d7a293a82 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Tue, 29 Mar 2016 18:39:25 -0500 Subject: [PATCH 095/213] Put owner and participating people first --- app/services/projects/participants_service.rb | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index 0004a399f4..ba88ba73c3 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 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 + 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 - users = target.participants(current_user) + def target_owner + [{ + name: @target.author.name, + username: @target.author.username + }] + end + + def participants_in(type, id) + return [] unless @target + + 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 From ad48ecacaef508139117ceaea1a9eeaeb7046b48 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Tue, 29 Mar 2016 18:53:01 -0500 Subject: [PATCH 096/213] 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 ba88ba73c3..800747b7ef 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) From 16459fddd4857bc18a917e13989b755468cfd7e8 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 30 Mar 2016 15:19:56 -0500 Subject: [PATCH 097/213] Fixes multiple ajax request and incorrect data being set for the current issuable --- .../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 4718bcf7a1..61e3f811e7 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 From 05628e0c2ffc5a3378ad4d89a212b1bd652a9d60 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 30 Mar 2016 16:29:42 -0500 Subject: [PATCH 098/213] 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 800747b7ef..1eaecc0d27 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 From 1eeabdc6a5c5ad751891d725a0957a24282f7a9c Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 8 Apr 2016 12:23:44 -0500 Subject: [PATCH 099/213] 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 1eaecc0d27..11dc3542fa 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 [] From a96dc944289e01f7b41112343cf20ca2791860ca Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 8 Apr 2016 13:36:55 -0500 Subject: [PATCH 100/213] 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 11dc3542fa..02c4eee3d0 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 From 42848f2684c093c114abf20dd5d73a195ec6a451 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 8 Apr 2016 13:45:24 -0500 Subject: [PATCH 101/213] Update CHANGELOG --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index f1320b6a28..7754c3fb9f 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 From a740f0bc99a67d25ed156353055de3b4fe0064a6 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 8 Apr 2016 19:35:43 -0500 Subject: [PATCH 102/213] Add tests for autocomplete on a Issue --- .../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 0000000000..f966fcbbfa --- /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 From 40efb4eb1fb3a1863f16488c7a468d4ca140500a Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Sat, 9 Apr 2016 01:34:07 -0500 Subject: [PATCH 103/213] Add tests for autocomplete on a Merge Request --- .../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 f966fcbbfa..5f1b5fd6b5 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 From a53e912bc0578febf87f722887a100f2c6e4670f Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 11 Apr 2016 14:02:00 -0500 Subject: [PATCH 104/213] 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 5f1b5fd6b5..b566a036a4 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) From 18b4291181ba126a21137320a9f7b7f503c1d776 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 13 Apr 2016 13:50:17 -0500 Subject: [PATCH 105/213] Add tests for autocomplete on a Commit --- .../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 b566a036a4..1adab7e9c6 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 From 6e00c3635bfdf10ef2ada7a5b8af3fbc88f7f58a Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Thu, 14 Apr 2016 12:19:33 -0500 Subject: [PATCH 106/213] Update CHANGELOG --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 7754c3fb9f..bc523533ee 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 From 5ddda30c353569d3d0af52c0112be4e66f890586 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Sat, 16 Apr 2016 08:59:56 -0400 Subject: [PATCH 107/213] Remove errors from CHANGELOG --- CHANGELOG | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bc523533ee..5c375fcdb3 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 From ffd2416c5636917c0da0a3376342d6c465bb1954 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Sat, 16 Apr 2016 09:28:04 -0400 Subject: [PATCH 108/213] Make a user available for the user variable --- app/controllers/help_controller.rb | 6 ++++++ app/views/help/ui.html.haml | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index 5505061547..406142f29c 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -51,6 +51,12 @@ class HelpController < ApplicationController end def ui + # this will work on gitlab.com + @some_user = User.find_by(username: 'dzaporozhets') + if @some_user.nil? + # this will work in dev + @some_user = User.find(1) + end end private diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index d084559abc..64414f5d8b 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -345,11 +345,11 @@ %ul %li %a.dropdown-menu-user-link.is-active{href: "#"} - = link_to_member_avatar(current_user, size: 30) + = link_to_member_avatar(@some_user, size: 30) %strong.dropdown-menu-user-full-name - = current_user.name + = @some_user.name .dropdown-menu-user-username - = current_user.to_reference + = @some_user.to_reference .example %div @@ -372,11 +372,11 @@ %ul %li %a.dropdown-menu-user-link.is-active{href: "#"} - = link_to_member_avatar(current_user, size: 30) + = link_to_member_avatar(@some_user, size: 30) %strong.dropdown-menu-user-full-name - = current_user.name + = @some_user.name .dropdown-menu-user-username - = current_user.to_reference + = @some_user.to_reference .dropdown-page-two .dropdown-title %button.dropdown-title-button.dropdown-menu-back{aria: {label: "Go back"}} From b340b59743e8cd47fc1f4fa2020b400d82bfd86e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sat, 16 Apr 2016 18:46:18 +0200 Subject: [PATCH 109/213] Implement finally_script which allows to do cleanups as part of the build process --- lib/ci/gitlab_ci_yaml_processor.rb | 10 +++++-- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 29 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index b7209c1414..be2462949f 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -4,12 +4,12 @@ module Ci DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGE = 'test' - ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables, :cache] + ALLOWED_YAML_KEYS = [:before_script, :finally_script, :image, :services, :types, :stages, :variables, :cache] ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts, :cache, :dependencies] - attr_reader :before_script, :image, :services, :variables, :path, :cache + attr_reader :before_script, :finally_script, :image, :services, :variables, :path, :cache def initialize(config, path = nil) @config = YAML.safe_load(config, [Symbol], [], true) @@ -44,6 +44,7 @@ module Ci def initial_parsing @before_script = @config[:before_script] || [] + @finally_script = @config[:finally_script] @image = @config[:image] @services = @config[:services] @stages = @config[:stages] || @config[:types] @@ -85,6 +86,7 @@ module Ci artifacts: job[:artifacts], cache: job[:cache] || @cache, dependencies: job[:dependencies], + finally_script: @finally_script, }.compact } end @@ -102,6 +104,10 @@ module Ci raise ValidationError, "before_script should be an array of strings" end + unless @finally_script.nil? || validate_array_of_strings(@finally_script) + raise ValidationError, "finally_script should be an array of strings" + end + unless @image.nil? || @image.is_a?(String) raise ValidationError, "image should be a string" end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index dcb8a3451b..8e373ae55b 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -286,6 +286,28 @@ module Ci end end + + describe "Scripts handling" do + let(:config_data) { YAML.dump(config) } + let(:config_processor) { GitlabCiYamlProcessor.new(config_data, path) } + + subject { config_processor.builds_for_stage_and_ref("test", "master").first } + + describe "finally_script" do + context "in global context" do + let(:config) { + { + finally_script: ["finally_script"], + test: { script: ["script"] } + } + } + + it "return finally_script in options" do + expect(subject[:options][:finally_script]).to eq(["finally_script"]) + end + end + end + end describe "Image and service handling" do it "returns image and service when defined" do @@ -607,6 +629,13 @@ EOT end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") end + it "returns errors if finally_script parameter is invalid" do + config = YAML.dump({ finally_script: "bundle update", rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config, path) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "finally_script should be an array of strings") + end + it "returns errors if image parameter is invalid" do config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) expect do From 4cc9a02ee033564b62b45977571319d129df465b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sat, 16 Apr 2016 18:49:15 +0200 Subject: [PATCH 110/213] Allow the before_script and finally_script to be overwritten in context of job --- lib/ci/gitlab_ci_yaml_processor.rb | 22 +++--- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 73 +++++++++++++++++++- 2 files changed, 81 insertions(+), 14 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index be2462949f..2e5b84a57d 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -7,7 +7,7 @@ module Ci ALLOWED_YAML_KEYS = [:before_script, :finally_script, :image, :services, :types, :stages, :variables, :cache] ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts, :cache, - :dependencies] + :dependencies, :before_script, :finally_script] attr_reader :before_script, :finally_script, :image, :services, :variables, :path, :cache @@ -73,7 +73,7 @@ module Ci { stage_idx: stages.index(job[:stage]), stage: job[:stage], - commands: "#{@before_script.join("\n")}\n#{normalize_script(job[:script])}", + commands: [job[:before_script] || @before_script, job[:script]].flatten.join("\n"), tag_list: job[:tags] || [], name: name, only: job[:only], @@ -86,19 +86,11 @@ module Ci artifacts: job[:artifacts], cache: job[:cache] || @cache, dependencies: job[:dependencies], - finally_script: @finally_script, + finally_script: job[:finally_script] || @finally_script, }.compact } end - def normalize_script(script) - if script.is_a? Array - script.join("\n") - else - script - end - end - def validate! unless validate_array_of_strings(@before_script) raise ValidationError, "before_script should be an array of strings" @@ -177,6 +169,14 @@ module Ci raise ValidationError, "#{name} job: script should be a string or an array of a strings" end + if job[:before_script] && !validate_array_of_strings(job[:before_script]) + raise ValidationError, "#{name} job: before_script should be an array of strings" + end + + if job[:finally_script] && !validate_array_of_strings(job[:finally_script]) + raise ValidationError, "#{name} job: finally_script should be an array of strings" + end + if job[:image] && !validate_string(job[:image]) raise ValidationError, "#{name} job: image should be a string" end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 8e373ae55b..a6a1a5e3be 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -293,6 +293,46 @@ module Ci subject { config_processor.builds_for_stage_and_ref("test", "master").first } + describe "before_script" do + context "in global context" do + let(:config) { + { + before_script: ["global script"], + test: { script: ["script"] } + } + } + + it "return commands with scripts concencaced" do + expect(subject[:commands]).to eq("global script\nscript") + end + end + + context "overwritten in local context" do + let(:config) { + { + before_script: ["global script"], + test: { before_script: ["local script"], script: ["script"] } + } + } + + it "return commands with scripts concencaced" do + expect(subject[:commands]).to eq("local script\nscript") + end + end + end + + describe "script" do + let(:config) { + { + test: { script: ["script"] } + } + } + + it "return commands with scripts concencaced" do + expect(subject[:commands]).to eq("script") + end + end + describe "finally_script" do context "in global context" do let(:config) { @@ -306,6 +346,19 @@ module Ci expect(subject[:options][:finally_script]).to eq(["finally_script"]) end end + + context "overwritten in local context" do + let(:config) { + { + finally_script: ["local finally_script"], + test: { finally_script: ["local finally_script"], script: ["script"] } + } + } + + it "return finally_script in options" do + expect(subject[:options][:finally_script]).to eq(["local finally_script"]) + end + end end end @@ -558,7 +611,7 @@ module Ci stage_idx: 1, name: :normal_job, only: nil, - commands: "\ntest", + commands: "test", tag_list: [], options: {}, when: "on_success", @@ -585,7 +638,7 @@ EOT stage_idx: 1, name: :job1, only: nil, - commands: "\nexecute-script-for-job", + commands: "execute-script-for-job", tag_list: [], options: {}, when: "on_success", @@ -597,7 +650,7 @@ EOT stage_idx: 1, name: :job2, only: nil, - commands: "\nexecute-script-for-job", + commands: "execute-script-for-job", tag_list: [], options: {}, when: "on_success", @@ -629,6 +682,13 @@ EOT end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") end + it "returns errors if job before_script parameter is not an array of strings" do + config = YAML.dump({ rspec: { script: "test", before_script: [10, "test"] } }) + expect do + GitlabCiYamlProcessor.new(config, path) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: before_script should be an array of strings") + end + it "returns errors if finally_script parameter is invalid" do config = YAML.dump({ finally_script: "bundle update", rspec: { script: "test" } }) expect do @@ -636,6 +696,13 @@ EOT end.to raise_error(GitlabCiYamlProcessor::ValidationError, "finally_script should be an array of strings") end + it "returns errors if job finally_script parameter is not an array of strings" do + config = YAML.dump({ rspec: { script: "test", finally_script: [10, "test"] } }) + expect do + GitlabCiYamlProcessor.new(config, path) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: finally_script should be an array of strings") + end + it "returns errors if image parameter is invalid" do config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) expect do From e79b0dd762719828203bd48c253a88d876bcf61a Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Sat, 16 Apr 2016 16:00:30 -0400 Subject: [PATCH 111/213] Simplify random user generation. --- app/controllers/help_controller.rb | 7 +------ app/views/help/ui.html.haml | 12 ++++++------ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index 406142f29c..9b5c43b17e 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -51,12 +51,7 @@ class HelpController < ApplicationController end def ui - # this will work on gitlab.com - @some_user = User.find_by(username: 'dzaporozhets') - if @some_user.nil? - # this will work in dev - @some_user = User.find(1) - end + @user = User.new(id: 0, name: 'John Doe', username: '@johndoe') end private diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 64414f5d8b..f12df5c8ff 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -345,11 +345,11 @@ %ul %li %a.dropdown-menu-user-link.is-active{href: "#"} - = link_to_member_avatar(@some_user, size: 30) + = link_to_member_avatar(@user, size: 30) %strong.dropdown-menu-user-full-name - = @some_user.name + = @user.name .dropdown-menu-user-username - = @some_user.to_reference + = @user.to_reference .example %div @@ -372,11 +372,11 @@ %ul %li %a.dropdown-menu-user-link.is-active{href: "#"} - = link_to_member_avatar(@some_user, size: 30) + = link_to_member_avatar(@user, size: 30) %strong.dropdown-menu-user-full-name - = @some_user.name + = @user.name .dropdown-menu-user-username - = @some_user.to_reference + = @user.to_reference .dropdown-page-two .dropdown-title %button.dropdown-title-button.dropdown-menu-back{aria: {label: "Go back"}} From dd0478656d877bea84a313826258f1a35f9b1ab4 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Sat, 16 Apr 2016 16:08:53 -0400 Subject: [PATCH 112/213] Add move cursor to last character for editing comment also. --- app/assets/javascripts/notes.js.coffee | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index c2e103ff85..82e210fed7 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -366,6 +366,11 @@ class @Notes # Show the attachment delete link note.find(".js-note-attachment-delete").show() + done = ($noteText) -> + # Neat little trick to put the cursor at the end + noteTextVal = $noteText.val() + $noteText.val('').val(noteTextVal); + new GLForm form if scrollTo? and myLastNote? # scroll to the bottom @@ -376,12 +381,12 @@ class @Notes }, 500, -> $noteText = form.find(".js-note-text") $noteText.focus() - # Neat little trick to put the cursor at the end - noteTextVal = $noteText.val() - $noteText.val('').val(noteTextVal); + done($noteText) ); else - form.find(".js-note-text").focus() + $noteText = form.find('.js-note-text') + $noteText.focus() + done($noteText) ### Called in response to clicking the edit note link From fd42b0db02f7911c9aa1a1b0656b6547ad2c0841 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 1 Apr 2016 11:55:26 +0100 Subject: [PATCH 113/213] Refactored issue header to work better for mobile This will also work better for when the title makes the buttons drop down a new line Closes #14228 --- .../stylesheets/framework/issue_box.scss | 2 +- app/assets/stylesheets/framework/mobile.scss | 7 -- app/assets/stylesheets/pages/detail_page.scss | 5 - app/assets/stylesheets/pages/issuable.scss | 51 +++++++- app/assets/stylesheets/pages/issues.scss | 40 ------- app/views/projects/issues/show.html.haml | 111 +++++++++--------- 6 files changed, 105 insertions(+), 111 deletions(-) diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss index 7f7b7c806e..8bfc0d583c 100644 --- a/app/assets/stylesheets/framework/issue_box.scss +++ b/app/assets/stylesheets/framework/issue_box.scss @@ -5,7 +5,7 @@ */ .status-box { - + /* Extra small devices (phones, less than 768px) */ /* No media query since this is the default in Bootstrap */ padding: 5px 11px; diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index 66180f38a4..7eb451c124 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -70,13 +70,6 @@ display: none; } - .issue-details { - .creator, - .page-title .btn-close { - display: none; - } - } - %ul.notes .note-role, .note-actions { display: none; } diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss index 5917f08972..89bdb9ae03 100644 --- a/app/assets/stylesheets/pages/detail_page.scss +++ b/app/assets/stylesheets/pages/detail_page.scss @@ -16,11 +16,6 @@ .issue_created_ago, .author_link { white-space: nowrap; } - - .issue-meta { - display: inline-block; - line-height: 20px; - } } .detail-page-description { diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 6bd90a2362..5bf44c1cdb 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -273,10 +273,6 @@ } } -.btn-default.gutter-toggle { - margin-top: 4px; -} - .detail-page-description { small { color: $gray-darkest; @@ -322,3 +318,50 @@ padding-top: 7px; } } + +.issuable-status-box { + float: none; + display: inline-block; + margin-top: 0; + + @media (max-width: $screen-xs-max) { + position: absolute; + top: 0; + left: 0; + } +} + +.issuable-header { + position: relative; + padding-left: 45px; + padding-right: 45px; + line-height: 35px; + + @media (min-width: $screen-sm-min) { + float: left; + padding-left: 0; + padding-right: 0; + } +} + +.issuable-actions { + padding-top: 10px; + + @media (min-width: $screen-sm-min) { + float: right; + padding-top: 0; + } +} + +.issuable-gutter-toggle { + @media (max-width: $screen-sm-max) { + position: absolute; + top: 0; + right: 0; + } +} + +.issuable-meta { + display: inline-block; + line-height: 18px; +} diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 6a1d28590c..fc9db97132 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -86,41 +86,9 @@ form.edit-issue { @media (max-width: $screen-xs-max) { .issue-btn-group { width: 100%; - margin-top: 5px; - - .btn-group { - width: 100%; - - ul { - width: 100%; - text-align: center; - } - } .btn { width: 100%; - - &:first-child:not(:last-child) { - - } - - &:not(:first-child):not(:last-child) { - margin-top: 10px; - } - - &:last-child:not(:first-child) { - margin-top: 10px; - } - } - } - - .issue { - &:hover .issue-actions { - display: none !important; - } - - .issue-updated-at { - display: none; } } } @@ -133,11 +101,3 @@ form.edit-issue { color: $gl-text-color; margin-left: 52px; } - -.editor-details { - display: block; - - @media (min-width: $screen-sm-min) { - display: inline-block; - } -} diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 5fe5ddc081..4f4e6c59d6 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,68 +1,73 @@ - page_title "#{@issue.title} (##{@issue.iid})", "Issues" - page_description @issue.description - page_card_attributes @issue.card_attributes +- header_title project_title(@project, "Issues", namespace_project_issues_path(@project.namespace, @project)) -= render "header_title" +.clearfix.detail-page-header + .issuable-header + .issuable-status-box.status-box.status-box-closed{ class: "#{issue_button_visibility(@issue, false)}" } + = icon('check', class: "hidden-sm hidden-md hidden-lg") + %span.hidden-xs + Closed + .issuable-status-box.status-box.status-box-open{ class: "#{issue_button_visibility(@issue, true)}"} + = icon('circle-o', class: "hidden-sm hidden-md hidden-lg") + %span.hidden-xs Open -.issue - .detail-page-header.issuable-header - .pull-left - .status-box{ class: "status-box-closed #{issue_button_visibility(@issue, false)}"} - %span.hidden-xs - Closed - %span.hidden-sm.hidden-md.hidden-lg - = icon('check') - .status-box{ class: "status-box-open #{issue_button_visibility(@issue, true)}"} - %span.hidden-xs - Open - %span.hidden-sm.hidden-md.hidden-lg - = icon('circle-o') - - %a.btn.btn-default.pull-right.visible-xs-block.gutter-toggle.js-sidebar-toggle{ href: "#" } + %a.btn.btn-default.pull-right.visible-xs-block.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ href: "#" } = icon('angle-double-left') - .issue-meta + .issuable-meta = confidential_icon(@issue) %strong.identifier - Issue ##{@issue.iid} - %span.creator - opened - .editor-details - .editor-details - = time_ago_with_tooltip(@issue.created_at) - by - %strong - = link_to_member(@project, @issue.author, size: 24, mobile_classes: "hidden-xs") - %strong - = link_to_member(@project, @issue.author, size: 24, mobile_classes: "hidden-sm hidden-md hidden-lg", - by_username: true, avatar: false) + Issue #{@issue.to_reference} + opened + = time_ago_with_tooltip(@issue.created_at) + by + %strong + = link_to_member(@project, @issue.author, size: 24, mobile_classes: "hidden-xs") + = link_to_member(@project, @issue.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") - .pull-right.issue-btn-group + .issuable-actions + .clearfix.issue-btn-group.dropdown + %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ data: { toggle: "dropdown" } } + %span.caret + Options + .dropdown-menu.dropdown-menu-align-right.hidden-lg + %ul + - if can?(current_user, :create_issue, @project) + %li + = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link' + - if can?(current_user, :update_issue, @issue) + %li + = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: " #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' + %li + = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "#{issue_button_visibility(@issue, true)}", title: 'Close issue' + %li + = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue) - if can?(current_user, :create_issue, @project) - = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'btn btn-nr btn-grouped new-issue-link btn-success', title: 'New issue', id: 'new_issue_link' do + = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped new-issue-link btn-success', title: 'New issue', id: 'new_issue_link' do = icon('plus') New issue - if can?(current_user, :update_issue, @issue) - = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' - = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' - = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-nr btn-grouped issuable-edit' do + = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' + = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' + = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit' do = icon('pencil-square-o') Edit - .issue-details.issuable-details - .detail-page-description.content-block - %h2.title - = markdown escape_once(@issue.title), pipeline: :single_line - %div - - if @issue.description.present? - .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} - .wiki - = preserve do - = markdown(@issue.description, cache_key: [@issue, "description"]) - %textarea.hidden.js-task-list-field - = @issue.description - = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago') +.issue-details.issuable-details + .detail-page-description.content-block + %h2.title + = markdown escape_once(@issue.title), pipeline: :single_line + - if @issue.description.present? + .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} + .wiki + = preserve do + = markdown(@issue.description, cache_key: [@issue, "description"]) + %textarea.hidden.js-task-list-field + = @issue.description + = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago') #merge-requests{'data-url' => referenced_merge_requests_namespace_project_issue_url(@project.namespace, @project, @issue)} // This element is filled in using JavaScript. @@ -70,13 +75,11 @@ #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' - = render 'votes/votes_block', votable: @issue + .content-block.content-block-small + = render 'new_branch' + = render 'votes/votes_block', votable: @issue - .row - %section.col-md-12 - .issuable-discussion - = render 'projects/issues/discussion' + %section.issuable-discussion + = render 'projects/issues/discussion' = render 'shared/issuable/sidebar', issuable: @issue From 60bb7007c3a165543a1787aa7ee6543a0def2152 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 1 Apr 2016 12:12:13 +0100 Subject: [PATCH 114/213] Merge request header markup now matches issue markup for consistancy --- .../projects/merge_requests/_show.html.haml | 3 +- .../merge_requests/show/_mr_title.html.haml | 74 ++++++++++--------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 2c34f9c454..285ad26316 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -1,8 +1,7 @@ - page_title "#{@merge_request.title} (#{@merge_request.to_reference})", "Merge Requests" - page_description @merge_request.description - page_card_attributes @merge_request.card_attributes - -= render "header_title" +- header_title project_title(@project, "Merge Requests", namespace_project_merge_requests_path(@project.namespace, @project)) - if params[:view] == 'parallel' - fluid_layout true diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index ab4b1f14be..c4e5788325 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -1,35 +1,43 @@ -.detail-page-header - .status-box{ class: status_box_class(@merge_request) } - %span.hidden-xs - = @merge_request.state_human_name - %span.hidden-sm.hidden-md.hidden-lg - = icon(@merge_request.state_icon_name) - %a.btn.btn-default.pull-right.visible-xs-block.gutter-toggle.js-sidebar-toggle{ href: "#" } - = icon('angle-double-left') - .issue-meta - %strong.identifier - %span.hidden-sm.hidden-md.hidden-lg - MR +.clearfix.detail-page-header + .issuable-header + .issuable-status-box.status-box{ class: "#{status_box_class(@merge_request)}" } + = icon(@merge_request.state_icon_name, class: "hidden-sm hidden-md hidden-lg") %span.hidden-xs - Merge Request - !#{@merge_request.iid} - %span.creator - opened - .editor-details - = time_ago_with_tooltip(@merge_request.created_at) - by - %strong - = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-xs") - %strong - = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-sm hidden-md hidden-lg", - by_username: true, avatar: false) + = @merge_request.state_human_name - .issue-btn-group.pull-right - - if can?(current_user, :update_merge_request, @merge_request) - - if @merge_request.open? - = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'btn btn-nr btn-grouped btn-close', title: 'Close merge request' - = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-nr btn-grouped issuable-edit', id: 'edit_merge_request' do - = icon('pencil-square-o') - Edit - - if @merge_request.closed? - = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'btn btn-nr btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request' + %a.btn.btn-default.pull-right.visible-xs-block.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ href: "#" } + = icon('angle-double-left') + + .issuable-meta + %strong.identifier + Merge Request #{@merge_request.to_reference} + opened + = time_ago_with_tooltip(@merge_request.created_at) + by + %strong + = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-xs") + = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-sm hidden-md hidden-lg", by_username: true, avatar: false) + + - if can?(current_user, :update_merge_request, @merge_request) + .issuable-actions + .clearfix.issue-btn-group.dropdown + %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ data: { toggle: "dropdown" } } + %span.caret + Options + .dropdown-menu.dropdown-menu-align-right.hidden-lg + %ul + - if @merge_request.open? + %li + = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request' + %li + = link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'issuable-edit', id: 'edit_merge_request' + - if @merge_request.closed? + %li + = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request' + - if @merge_request.open? + = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'hidden-xs hidden-sm btn btn-nr btn-grouped btn-close', title: 'Close merge request' + = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit', id: 'edit_merge_request' do + = icon('pencil-square-o') + Edit + - if @merge_request.closed? + = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request' From 6c949b2bf1c9ee47fb5b181f44021ad6eeb1cd5a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 1 Apr 2016 12:20:54 +0100 Subject: [PATCH 115/213] Moved meta information into an issuable helper method --- app/helpers/issuables_helper.rb | 11 +++++++++++ app/views/projects/issues/show.html.haml | 9 +-------- .../projects/merge_requests/show/_mr_title.html.haml | 11 ++--------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index b14b8218d0..94e1a4d2ec 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -55,6 +55,17 @@ module IssuablesHelper h(milestone_title.presence || default_label) end + def issuable_meta(issuable, project, text) + output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier" + output << " opened " + output << time_ago_with_tooltip(issuable.created_at) + output << " by " + output << content_tag(:strong) do + author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs") + author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") + end + end + private def sidebar_gutter_collapsed? diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 4f4e6c59d6..e3df2ec924 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -18,14 +18,7 @@ .issuable-meta = confidential_icon(@issue) - %strong.identifier - Issue #{@issue.to_reference} - opened - = time_ago_with_tooltip(@issue.created_at) - by - %strong - = link_to_member(@project, @issue.author, size: 24, mobile_classes: "hidden-xs") - = link_to_member(@project, @issue.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") + = issuable_meta(@issue, @project, "Issue") .issuable-actions .clearfix.issue-btn-group.dropdown diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index c4e5788325..e78fd2054c 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -7,16 +7,9 @@ %a.btn.btn-default.pull-right.visible-xs-block.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ href: "#" } = icon('angle-double-left') - + .issuable-meta - %strong.identifier - Merge Request #{@merge_request.to_reference} - opened - = time_ago_with_tooltip(@merge_request.created_at) - by - %strong - = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-xs") - = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-sm hidden-md hidden-lg", by_username: true, avatar: false) + = issuable_meta(@merge_request, @project, "Merge Request") - if can?(current_user, :update_merge_request, @merge_request) .issuable-actions From ce2e37d446c113575b137f1a11d56df080d02148 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 1 Apr 2016 13:44:22 +0100 Subject: [PATCH 116/213] Fixed tests --- features/steps/shared/issuable.rb | 2 +- spec/features/issues/move_spec.rb | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb index 24b3fb6eac..a58b3cb7e1 100644 --- a/features/steps/shared/issuable.rb +++ b/features/steps/shared/issuable.rb @@ -2,7 +2,7 @@ module SharedIssuable include Spinach::DSL def edit_issuable - find(:css, '.issuable-edit').click + find('.issuable-edit', visible: true).click end step 'project "Community" has "Community issue" open issue' do diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb index 6fda0c3186..caa7b0ec48 100644 --- a/spec/features/issues/move_spec.rb +++ b/spec/features/issues/move_spec.rb @@ -42,11 +42,9 @@ feature 'issue move to another project' do expect(current_url).to include project_path(new_project) - page.within('.issue') do - expect(page).to have_content("Text with #{cross_reference}!1") - expect(page).to have_content("Moved from #{cross_reference}#1") - expect(page).to have_content(issue.title) - end + expect(page).to have_content("Text with #{cross_reference}!1") + expect(page).to have_content("Moved from #{cross_reference}#1") + expect(page).to have_content(issue.title) end context 'projects user does not have permission to move issue to exist' do @@ -74,7 +72,7 @@ feature 'issue move to another project' do def edit_issue(issue) visit issue_path(issue) - page.within('.issuable-header') { click_link 'Edit' } + page.within('.issuable-actions') { find('.issuable-edit').click } end def issue_path(issue) From 365f351a6c4679db8e7cda52c899d1ce14200097 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 1 Apr 2016 14:43:42 +0100 Subject: [PATCH 117/213] Alignment with sidebar --- app/assets/stylesheets/pages/detail_page.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss index 89bdb9ae03..2c2ac903f2 100644 --- a/app/assets/stylesheets/pages/detail_page.scss +++ b/app/assets/stylesheets/pages/detail_page.scss @@ -1,5 +1,5 @@ .detail-page-header { - padding: 11px 0; + padding: $gl-padding-top 0; border-bottom: 1px solid $border-color; color: #5c5d5e; font-size: 16px; From 4b15a8ed262147ba38fcd2cddfda02fb481c37d1 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Apr 2016 16:27:16 +0100 Subject: [PATCH 118/213] Fixed up based on feedback --- app/helpers/issuables_helper.rb | 4 +--- app/views/projects/issues/show.html.haml | 12 ++++++------ .../projects/merge_requests/show/_mr_title.html.haml | 2 +- spec/features/issues/move_spec.rb | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 94e1a4d2ec..e1b5af242f 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -57,9 +57,7 @@ module IssuablesHelper def issuable_meta(issuable, project, text) output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier" - output << " opened " - output << time_ago_with_tooltip(issuable.created_at) - output << " by " + output << " opened #{time_ago_with_tooltip(issuable.created_at, skip_js: true)} by".html_safe output << content_tag(:strong) do author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs") author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index e3df2ec924..e07ac04baf 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -5,11 +5,11 @@ .clearfix.detail-page-header .issuable-header - .issuable-status-box.status-box.status-box-closed{ class: "#{issue_button_visibility(@issue, false)}" } + .issuable-status-box.status-box.status-box-closed{ class: issue_button_visibility(@issue, false) } = icon('check', class: "hidden-sm hidden-md hidden-lg") %span.hidden-xs Closed - .issuable-status-box.status-box.status-box-open{ class: "#{issue_button_visibility(@issue, true)}"} + .issuable-status-box.status-box.status-box-open{ class: issue_button_visibility(@issue, true) } = icon('circle-o', class: "hidden-sm hidden-md hidden-lg") %span.hidden-xs Open @@ -32,9 +32,9 @@ = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link' - if can?(current_user, :update_issue, @issue) %li - = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: " #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' + = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' %li - = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "#{issue_button_visibility(@issue, true)}", title: 'Close issue' + = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' %li = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue) - if can?(current_user, :create_issue, @project) @@ -42,8 +42,8 @@ = icon('plus') New issue - if can?(current_user, :update_issue, @issue) - = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' - = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' + = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' + = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit' do = icon('pencil-square-o') Edit diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index e78fd2054c..42a9d463e9 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -1,6 +1,6 @@ .clearfix.detail-page-header .issuable-header - .issuable-status-box.status-box{ class: "#{status_box_class(@merge_request)}" } + .issuable-status-box.status-box{ class: status_box_class(@merge_request) } = icon(@merge_request.state_icon_name, class: "hidden-sm hidden-md hidden-lg") %span.hidden-xs = @merge_request.state_human_name diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb index caa7b0ec48..84c8e20eba 100644 --- a/spec/features/issues/move_spec.rb +++ b/spec/features/issues/move_spec.rb @@ -72,7 +72,7 @@ feature 'issue move to another project' do def edit_issue(issue) visit issue_path(issue) - page.within('.issuable-actions') { find('.issuable-edit').click } + page.within('.issuable-actions') { first(:link, 'Edit').click } end def issue_path(issue) From a716e8eadcb93779f1a89f05c0ed1a6b72a8dc6b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 13 Apr 2016 09:13:47 +0100 Subject: [PATCH 119/213] Added CHANGELOG item --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 5c375fcdb3..d8fbfafb72 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -80,6 +80,10 @@ 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 + - Issuable header is consistent between issues and merge requests + - Improved spacing in issuable header on mobile v 8.6.5 - Fix importing from GitHub Enterprise. !3529 From 2629ab4472c535416791cfc13b41c7f50d7d343c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 10:14:12 +0100 Subject: [PATCH 120/213] Fixed tests --- app/helpers/issuables_helper.rb | 2 +- app/views/projects/issues/show.html.haml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index e1b5af242f..49b6b79ce3 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -57,7 +57,7 @@ module IssuablesHelper def issuable_meta(issuable, project, text) output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier" - output << " opened #{time_ago_with_tooltip(issuable.created_at, skip_js: true)} by".html_safe + output << " opened #{time_ago_with_tooltip(issuable.created_at)} by".html_safe output << content_tag(:strong) do author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs") author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index e07ac04baf..9fce9c3f33 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -62,11 +62,11 @@ = @issue.description = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago') - #merge-requests{'data-url' => referenced_merge_requests_namespace_project_issue_url(@project.namespace, @project, @issue)} - // This element is filled in using JavaScript. + #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. + #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' From 84fba4d16248de96dcea1ab7e6a56aae58668175 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 14:04:06 +0100 Subject: [PATCH 121/213] Updated some if statements --- app/views/projects/issues/show.html.haml | 57 ++++++++++--------- .../merge_requests/show/_mr_title.html.haml | 26 ++++----- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 9fce9c3f33..bde80bbb54 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -20,33 +20,34 @@ = confidential_icon(@issue) = issuable_meta(@issue, @project, "Issue") - .issuable-actions - .clearfix.issue-btn-group.dropdown - %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ data: { toggle: "dropdown" } } - %span.caret - Options - .dropdown-menu.dropdown-menu-align-right.hidden-lg - %ul - - if can?(current_user, :create_issue, @project) - %li - = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link' - - if can?(current_user, :update_issue, @issue) - %li - = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' - %li - = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' - %li - = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue) - - if can?(current_user, :create_issue, @project) - = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped new-issue-link btn-success', title: 'New issue', id: 'new_issue_link' do - = icon('plus') - New issue - - if can?(current_user, :update_issue, @issue) - = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' - = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' - = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit' do - = icon('pencil-square-o') - Edit + - if can?(current_user, :create_issue, @project) || can?(current_user, :update_issue, @issue) + .issuable-actions + .clearfix.issue-btn-group.dropdown + %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ data: { toggle: "dropdown" } } + %span.caret + Options + .dropdown-menu.dropdown-menu-align-right.hidden-lg + %ul + - if can?(current_user, :create_issue, @project) + %li + = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link' + - if can?(current_user, :update_issue, @issue) + %li + = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' + %li + = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' + %li + = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue) + - if can?(current_user, :create_issue, @project) + = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped new-issue-link btn-success', title: 'New issue', id: 'new_issue_link' do + = icon('plus') + New issue + - if can?(current_user, :update_issue, @issue) + = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' + = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' + = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit' do + = icon('pencil-square-o') + Edit .issue-details.issuable-details @@ -54,7 +55,7 @@ %h2.title = markdown escape_once(@issue.title), pipeline: :single_line - if @issue.description.present? - .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} + .description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' } .wiki = preserve do = markdown(@issue.description, cache_key: [@issue, "description"]) diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index 42a9d463e9..0a99e8c959 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -19,18 +19,14 @@ Options .dropdown-menu.dropdown-menu-align-right.hidden-lg %ul - - if @merge_request.open? - %li - = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request' - %li - = link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'issuable-edit', id: 'edit_merge_request' - - if @merge_request.closed? - %li - = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request' - - if @merge_request.open? - = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'hidden-xs hidden-sm btn btn-nr btn-grouped btn-close', title: 'Close merge request' - = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit', id: 'edit_merge_request' do - = icon('pencil-square-o') - Edit - - if @merge_request.closed? - = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request' + %li{ class: issue_button_visibility(@merge_request, true) } + = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request' + %li{ class: issue_button_visibility(@merge_request, false) } + = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request' + %li + = link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'issuable-edit', id: 'edit_merge_request' + = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-close #{issue_button_visibility(@merge_request, true)}", title: 'Close merge request' + = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen reopen-mr-link #{issue_button_visibility(@merge_request, false)}", title: 'Reopen merge request' + = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit", id: 'edit_merge_request' do + = icon('pencil-square-o') + Edit From fa7d99ebd4cd86134e33f9f79e2b92f50de3af6c Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 16 Apr 2016 10:23:36 +0200 Subject: [PATCH 122/213] API: Present an array of Gitlab::Git::Tag instead of array of rugged tags --- CHANGELOG | 1 + lib/api/tags.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index d8fbfafb72..7fa1fce1f2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -73,6 +73,7 @@ v 8.7.0 (unreleased) - Diffs load at the correct point when linking from from number - Selected diff rows highlight - Fix emoji categories in the emoji picker + - API: Properly display annotated tags for GET /projects/:id/repository/tags (Robert Schilling) - Add encrypted credentials for imported projects and migrate old ones - Author and participants are displayed first on users autocompletion diff --git a/lib/api/tags.rb b/lib/api/tags.rb index d1a10479e4..3e1ed3fe5c 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -12,7 +12,7 @@ module API # Example Request: # GET /projects/:id/repository/tags get ":id/repository/tags" do - present user_project.repo.tags.sort_by(&:name).reverse, + present user_project.repository.tags.sort_by(&:name).reverse, with: Entities::RepoTag, project: user_project end From 0be158a0725c254629e7e4e5de194021322e2fa9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Sun, 17 Apr 2016 12:15:45 +0100 Subject: [PATCH 123/213] Added upload file test to new issue form --- spec/features/issues_spec.rb | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 1ce0024e93..35c8f93abc 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -292,6 +292,23 @@ describe 'Issues', feature: true do end end + describe 'new issue' do + context 'dropzone upload file', js: true do + before do + visit new_namespace_project_issue_path(project.namespace, project) + end + + it 'should upload file when dragging into textarea' do + drop_in_dropzone test_image_file + + # Wait for the file to upload + sleep 1 + + expect(page.find_field("issue_description").value).to have_content 'banana_sample' + end + end + end + def first_issue page.all('ul.issues-list > li').first.text end @@ -299,4 +316,25 @@ describe 'Issues', feature: true do def last_issue page.all('ul.issues-list > li').last.text end + + def drop_in_dropzone(file_path) + # Generate a fake input selector + page.execute_script <<-JS + var fakeFileInput = window.$('').attr( + {id: 'fakeFileInput', type: 'file'} + ).appendTo('body'); + JS + # Attach the file to the fake input selector with Capybara + attach_file("fakeFileInput", file_path) + # Add the file to a fileList array and trigger the fake drop event + page.execute_script <<-JS + var fileList = [$('#fakeFileInput')[0].files[0]]; + var e = jQuery.Event('drop', { dataTransfer : { files : fileList } }); + $('.div-dropzone')[0].dropzone.listeners[0].events.drop(e); + JS + end + + def test_image_file + File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') + end end From cc57d61023c6dd5ef274bac5d4e6cde1cae97d2c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 17 Apr 2016 08:59:57 -0400 Subject: [PATCH 124/213] Rename finally_script to after_script --- lib/ci/gitlab_ci_yaml_processor.rb | 12 ++++++------ spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index be2462949f..a87329c296 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -4,12 +4,12 @@ module Ci DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGE = 'test' - ALLOWED_YAML_KEYS = [:before_script, :finally_script, :image, :services, :types, :stages, :variables, :cache] + ALLOWED_YAML_KEYS = [:before_script, :after_script, :image, :services, :types, :stages, :variables, :cache] ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts, :cache, :dependencies] - attr_reader :before_script, :finally_script, :image, :services, :variables, :path, :cache + attr_reader :before_script, :after_script, :image, :services, :variables, :path, :cache def initialize(config, path = nil) @config = YAML.safe_load(config, [Symbol], [], true) @@ -44,7 +44,7 @@ module Ci def initial_parsing @before_script = @config[:before_script] || [] - @finally_script = @config[:finally_script] + @after_script = @config[:after_script] @image = @config[:image] @services = @config[:services] @stages = @config[:stages] || @config[:types] @@ -86,7 +86,7 @@ module Ci artifacts: job[:artifacts], cache: job[:cache] || @cache, dependencies: job[:dependencies], - finally_script: @finally_script, + after_script: @after_script, }.compact } end @@ -104,8 +104,8 @@ module Ci raise ValidationError, "before_script should be an array of strings" end - unless @finally_script.nil? || validate_array_of_strings(@finally_script) - raise ValidationError, "finally_script should be an array of strings" + unless @after_script.nil? || validate_array_of_strings(@after_script) + raise ValidationError, "after_script should be an array of strings" end unless @image.nil? || @image.is_a?(String) diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 8e373ae55b..2421d6eee8 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -293,17 +293,17 @@ module Ci subject { config_processor.builds_for_stage_and_ref("test", "master").first } - describe "finally_script" do + describe "after_script" do context "in global context" do let(:config) { { - finally_script: ["finally_script"], + after_script: ["after_script"], test: { script: ["script"] } } } - it "return finally_script in options" do - expect(subject[:options][:finally_script]).to eq(["finally_script"]) + it "return after_script in options" do + expect(subject[:options][:after_script]).to eq(["after_script"]) end end end @@ -629,11 +629,11 @@ EOT end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") end - it "returns errors if finally_script parameter is invalid" do - config = YAML.dump({ finally_script: "bundle update", rspec: { script: "test" } }) + it "returns errors if after_script parameter is invalid" do + config = YAML.dump({ after_script: "bundle update", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "finally_script should be an array of strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "after_script should be an array of strings") end it "returns errors if image parameter is invalid" do From a0afeefd76ebfacae59343f48e127828b27ba77a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 17 Apr 2016 09:02:42 -0400 Subject: [PATCH 125/213] Add CHANGELOG and documentation --- CHANGELOG | 1 + doc/ci/yaml/README.md | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index c948e8e546..f52897892e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ v 8.7.0 (unreleased) - 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) + - Add support for `after_script`, requires Runner 1.2 (Kamil TrzciÅ„ski) - Expose label description in API (Mariusz Jachimowicz) - API: Ability to update a group (Robert Schilling) - API: Ability to move issues (Robert Schilling) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index abb6e97e5e..54b06f10b9 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -15,6 +15,7 @@ If you want a quick introduction to GitLab CI, follow our - [.gitlab-ci.yml](#gitlab-ci-yml) - [image and services](#image-and-services) - [before_script](#before_script) + - [after_script](#after_script) - [stages](#stages) - [types](#types) - [variables](#variables) @@ -80,6 +81,9 @@ services: before_script: - bundle install +after_script: + - rm secrets + stages: - build - test @@ -104,6 +108,7 @@ There are a few reserved `keywords` that **cannot** be used as job names: | stages | no | Define build stages | | types | no | Alias for `stages` | | before_script | no | Define commands that run before each job's script | +| after_script | no | Define commands that run after each job's script | | variables | no | Define build variables | | cache | no | Define list of files that should be cached between subsequent runs | @@ -118,6 +123,11 @@ used for time of the build. The configuration of this feature is covered in `before_script` is used to define the command that should be run before all builds, including deploy builds. This can be an array or a multi-line string. +### after_script + +`after_script` is used to define the command that will be run after for all +builds. This has to be an array or a multi-line string. + ### stages `stages` is used to define build stages that can be used by jobs. From 38b15e35d48550a5621b8fc292cabc5670897a44 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 17 Apr 2016 09:10:47 -0400 Subject: [PATCH 126/213] Update CHANGELOG and add documentation --- CHANGELOG | 1 + doc/ci/yaml/README.md | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index f52897892e..df1887f798 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -46,6 +46,7 @@ v 8.7.0 (unreleased) - 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) + - Make before_script and after_script overridable on per-job (Kamil TrzciÅ„ski) - API: Delete notes of issues, snippets, and merge requests (Robert Schilling) - Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.) - Better errors handling when creating milestones inside groups diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 54b06f10b9..c626bee570 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -30,6 +30,7 @@ If you want a quick introduction to GitLab CI, follow our - [artifacts](#artifacts) - [artifacts:name](#artifacts-name) - [dependencies](#dependencies) + - [before_script and after_script](#before_script-and-after_script) - [Hidden jobs](#hidden-jobs) - [Special YAML features](#special-yaml-features) - [Anchors](#anchors) @@ -342,6 +343,8 @@ job_name: | dependencies | no | Define other builds that a build depends on so that you can pass artifacts between them| | artifacts | no | Define list build artifacts | | cache | no | Define list of files that should be cached between subsequent runs | +| before_script | no | Override a set of commands that are executed before build | +| after_script | no | Override a set of commands that are executed after build | ### script @@ -686,6 +689,23 @@ deploy: script: make deploy ``` +### before_script and after_script + +It's possible to overwrite globally defined `before_script` and `after_script`: + +```yaml +before_script +- global before script + +job: + before_script: + - execute this instead of global before script + script: + - my command + after_script: + - execute this after my script +``` + ## Hidden jobs >**Note:** From c764b57f09ac55d181d17db018bdea9587fadea8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 17 Apr 2016 09:11:37 -0400 Subject: [PATCH 127/213] Add note about version --- doc/ci/yaml/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 54b06f10b9..697445bcee 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -125,6 +125,9 @@ builds, including deploy builds. This can be an array or a multi-line string. ### after_script +>**Note:** +Introduced in GitLab 8.7 and GitLab Runner v1.2. + `after_script` is used to define the command that will be run after for all builds. This has to be an array or a multi-line string. From a281f68b64c72bfad7796b8dc0ae3805583c66b6 Mon Sep 17 00:00:00 2001 From: BaldinoF Date: Sun, 17 Apr 2016 18:22:23 +0200 Subject: [PATCH 128/213] Add CHANGELOG entry --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index d8fbfafb72..de52033078 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -75,6 +75,7 @@ v 8.7.0 (unreleased) - 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 + - Show number sign on external issue reference text (Florent Baldino) v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 From 7cc239528ea7f4905e6d773771006ec661d628d6 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 17 Apr 2016 17:48:51 -0400 Subject: [PATCH 129/213] Remove persistent XSS vulnerability in `commit_person_link` helper Because we were incorrectly supplying the tooltip title as `data-original-title` (which Bootstrap's Tooltip JS automatically applies based on the `title` attribute; we should never be setting it directly), the value was being passed through as-is. Instead, we should be supplying the normal `title` attribute and letting Rails escape the value, which also negates the need for us to call `sanitize` on it. Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/15126 --- app/helpers/commits_helper.rb | 2 +- features/steps/project/commits/user_lookup.rb | 3 +- spec/helpers/commits_helper_spec.rb | 29 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 spec/helpers/commits_helper_spec.rb diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 35ba543cef..5394347bd1 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -183,7 +183,7 @@ module CommitsHelper options = { class: "commit-#{options[:source]}-link has-tooltip", - data: { 'original-title'.to_sym => sanitize(source_email) } + title: source_email } if user.nil? diff --git a/features/steps/project/commits/user_lookup.rb b/features/steps/project/commits/user_lookup.rb index 40cada6da4..2d43be5a38 100644 --- a/features/steps/project/commits/user_lookup.rb +++ b/features/steps/project/commits/user_lookup.rb @@ -29,8 +29,9 @@ class Spinach::Features::ProjectCommitsUserLookup < Spinach::FeatureSteps def check_author_link(email, user) author_link = find('.commit-author-link') + expect(author_link['href']).to eq user_path(user) - expect(author_link['data-original-title']).to eq email + expect(author_link['title']).to eq email expect(find('.commit-author-name').text).to eq user.name end diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb new file mode 100644 index 0000000000..727c25ff52 --- /dev/null +++ b/spec/helpers/commits_helper_spec.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +describe CommitsHelper do + describe 'commit_author_link' do + it 'escapes the author email' do + commit = double( + author: nil, + author_name: 'Persistent XSS', + author_email: 'my@email.com" onmouseover="alert(1)' + ) + + expect(helper.commit_author_link(commit)). + not_to include('onmouseover="alert(1)"') + end + end + + describe 'commit_committer_link' do + it 'escapes the committer email' do + commit = double( + committer: nil, + committer_name: 'Persistent XSS', + committer_email: 'my@email.com" onmouseover="alert(1)' + ) + + expect(helper.commit_committer_link(commit)). + not_to include('onmouseover="alert(1)"') + end + end +end From 40bc8e7677cb27f87fef7d4362fbd17bee8980ef Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Mon, 18 Apr 2016 12:19:11 +0530 Subject: [PATCH 130/213] Add acceptance test to check if the user password persists after form redisplays. - While signing up. --- spec/features/signup_spec.rb | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 spec/features/signup_spec.rb diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb new file mode 100644 index 0000000000..01472743b2 --- /dev/null +++ b/spec/features/signup_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +feature 'Signup', feature: true do + describe 'signup with no errors' do + it 'creates the user account and sends a confirmation email' do + user = build(:user) + + visit root_path + + fill_in 'user_name', with: user.name + fill_in 'user_username', with: user.username + fill_in 'user_email', with: user.email + fill_in 'user_password_sign_up', with: user.password + click_button "Sign up" + + expect(current_path).to eq user_session_path + expect(page).to have_content("A message with a confirmation link has been sent to your email address.") + end + end + + describe 'signup with errors' do + it "displays the errors" do + existing_user = create(:user) + user = build(:user) + + visit root_path + + fill_in 'user_name', with: user.name + fill_in 'user_username', with: user.username + fill_in 'user_email', with: existing_user.email + fill_in 'user_password_sign_up', with: user.password + click_button "Sign up" + + expect(current_path).to eq user_registration_path + expect(page).to have_content("error prohibited this user from being saved") + expect(page).to have_content("Email has already been taken") + end + + it 'does not redisplay the password' do + existing_user = create(:user) + user = build(:user) + + visit root_path + + fill_in 'user_name', with: user.name + fill_in 'user_username', with: user.username + fill_in 'user_email', with: existing_user.email + fill_in 'user_password_sign_up', with: user.password + click_button "Sign up" + + expect(current_path).to eq user_registration_path + expect(page.body).not_to match(/#{user.password}/) + end + end +end From 38557ec400d8c28ea73df4bc5142e156c7ab8855 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Mon, 18 Apr 2016 12:30:04 +0530 Subject: [PATCH 131/213] Move CHANGELOG entry to a random place. --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 225751cbb0..ede0c00e90 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -56,6 +56,7 @@ v 8.7.0 (unreleased) - Decouple membership and notifications - Fix creation of merge requests for orphaned branches (Stan Hu) - API: Ability to retrieve a single tag (Robert Schilling) + - While signing up, don't persist the user password across form redisplays - Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla) - Remove "Congratulations!" tweet button on newly-created project. (Connor Shea) - Fix admin/projects when using visibility levels on search (PotHix) @@ -66,7 +67,6 @@ v 8.7.0 (unreleased) - Update number of Todos in the sidebar when it's marked as "Done". !3600 - 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 - - While signing up, don't persist the user password across form redisplays - 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 From aa396ae5ee5715c1a2a8a82731cef6e3d7f73056 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Mon, 18 Apr 2016 13:01:23 +0530 Subject: [PATCH 132/213] Remove unused variable in `IssuesController`. --- app/controllers/projects/issues_controller.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index f3056b2100..f2ae572cfe 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -128,8 +128,6 @@ class Projects::IssuesController < Projects::ApplicationController end def related_branches - merge_requests = @issue.referenced_merge_requests(current_user) - @related_branches = @issue.related_branches(current_user) respond_to do |format| From 6f31262f747b7c1ed0173b6615ef0990967a0db6 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 18 Apr 2016 08:48:41 +0100 Subject: [PATCH 133/213] Fixed overlap in header on mobile --- app/assets/stylesheets/framework/header.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 3f015427d0..c303380764 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -71,7 +71,7 @@ header { .header-content { position: relative; height: $header-height; - padding-right: 20px; + padding-right: 40px; @media (min-width: $screen-sm-min) { padding-right: 0; @@ -122,6 +122,10 @@ header { } } + .project-item-select-holder { + display: inline; + } + .impersonation i { color: $red-normal; } From fce97dfce7f7be141c48892a63e3fe95e8fb756b Mon Sep 17 00:00:00 2001 From: Con Vissenberg Date: Mon, 18 Apr 2016 08:15:15 +0000 Subject: [PATCH 134/213] "git lfs init" is deprecated, use "git lfs install" instead. --- doc/workflow/lfs/manage_large_binaries_with_git_lfs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index ba91685a20..31620044b1 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -44,7 +44,7 @@ check it into your Git repository: ```bash git clone git@gitlab.example.com:group/project.git -git lfs init # initialize the Git LFS project project +git lfs install # initialize the Git LFS project project git lfs track "*.iso" # select the file extensions that you want to treat as large files ``` From d7a4a2fe7577f2ca17d28f10d312ed98d5253ae4 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 18 Apr 2016 10:52:01 +0200 Subject: [PATCH 135/213] When a project wiki is disabled skip it for fsck --- .../single_repository_worker.rb | 6 ++-- .../single_repository_worker_spec.rb | 30 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 spec/workers/repository_check/single_repository_worker_spec.rb diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb index e54ae86d06..a76729e3c7 100644 --- a/app/workers/repository_check/single_repository_worker.rb +++ b/app/workers/repository_check/single_repository_worker.rb @@ -15,10 +15,10 @@ module RepositoryCheck private def check(project) + repositories = [project.repository] + repositories << project.wiki.repository if project.wiki_enabled? # Use 'map do', not 'all? do', to prevent short-circuiting - [project.repository, project.wiki.repository].map do |repository| - git_fsck(repository.path_to_repo) - end.all? + repositories.map { |repository| git_fsck(repository.path_to_repo) }.all? end def git_fsck(path) diff --git a/spec/workers/repository_check/single_repository_worker_spec.rb b/spec/workers/repository_check/single_repository_worker_spec.rb new file mode 100644 index 0000000000..3179dd70b0 --- /dev/null +++ b/spec/workers/repository_check/single_repository_worker_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' +require 'fileutils' + +describe RepositoryCheck::SingleRepositoryWorker do + subject { described_class.new } + + it 'fails if the wiki repository is broken' do + project = create(:project_empty_repo, wiki_enabled: true) + project.create_wiki + + # Test sanity: everything should be fine before the wiki repo is broken + subject.perform(project.id) + expect(project.reload.last_repository_check_failed).to eq(false) + + FileUtils.rm_rf(project.wiki.repository.path_to_repo) + subject.perform(project.id) + + expect(project.reload.last_repository_check_failed).to eq(true) + end + + it 'skips wikis when disabled' do + project = create(:project_empty_repo, wiki_enabled: false) + # Make sure the test would fail if it checked the wiki repo + FileUtils.rm_rf(project.wiki.repository.path_to_repo) + + subject.perform(project.id) + + expect(project.reload.last_repository_check_failed).to eq(false) + end +end From fdf6ca6c01ec54bf86ee72d5adbaf4ae3cff8483 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 18 Apr 2016 10:58:40 +0200 Subject: [PATCH 136/213] Improve 'auto fsck' admin emails --- app/mailers/repository_check_mailer.rb | 2 +- app/views/repository_check_mailer/notify.html.haml | 3 +++ app/views/repository_check_mailer/notify.text.haml | 3 +++ spec/mailers/repository_check_mailer_spec.rb | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb index 2bff5b63cc..21db2fe04a 100644 --- a/app/mailers/repository_check_mailer.rb +++ b/app/mailers/repository_check_mailer.rb @@ -8,7 +8,7 @@ class RepositoryCheckMailer < BaseMailer mail( to: User.admins.pluck(:email), - subject: @message + subject: "GitLab Admin | #{@message}" ) end end diff --git a/app/views/repository_check_mailer/notify.html.haml b/app/views/repository_check_mailer/notify.html.haml index df16f50357..a585147ddd 100644 --- a/app/views/repository_check_mailer/notify.html.haml +++ b/app/views/repository_check_mailer/notify.html.haml @@ -3,3 +3,6 @@ %p = link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repository_check_failed: 1) + +%p + You are receiving this message because you are a GitLab administrator for #{Gitlab.config.gitlab.url}. diff --git a/app/views/repository_check_mailer/notify.text.haml b/app/views/repository_check_mailer/notify.text.haml index 02f3f80288..93db151329 100644 --- a/app/views/repository_check_mailer/notify.text.haml +++ b/app/views/repository_check_mailer/notify.text.haml @@ -1,3 +1,6 @@ #{@message}. \ View details: #{admin_namespaces_projects_url(last_repository_check_failed: 1)} + +You are receiving this message because you are a GitLab administrator +for #{Gitlab.config.gitlab.url}. diff --git a/spec/mailers/repository_check_mailer_spec.rb b/spec/mailers/repository_check_mailer_spec.rb index 583bf15176..00613c7b67 100644 --- a/spec/mailers/repository_check_mailer_spec.rb +++ b/spec/mailers/repository_check_mailer_spec.rb @@ -15,7 +15,7 @@ describe RepositoryCheckMailer do 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' + expect(mail).to have_subject 'GitLab Admin | 3 projects failed their last repository check' end end end From 1394ccfe834c4e34bed701d206c77961a594fa23 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 18 Apr 2016 11:03:53 +0200 Subject: [PATCH 137/213] Destroy wikis uniformly --- .../repository_check/single_repository_worker_spec.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/workers/repository_check/single_repository_worker_spec.rb b/spec/workers/repository_check/single_repository_worker_spec.rb index 3179dd70b0..087e4c667d 100644 --- a/spec/workers/repository_check/single_repository_worker_spec.rb +++ b/spec/workers/repository_check/single_repository_worker_spec.rb @@ -12,7 +12,7 @@ describe RepositoryCheck::SingleRepositoryWorker do subject.perform(project.id) expect(project.reload.last_repository_check_failed).to eq(false) - FileUtils.rm_rf(project.wiki.repository.path_to_repo) + destroy_wiki(project) subject.perform(project.id) expect(project.reload.last_repository_check_failed).to eq(true) @@ -21,10 +21,14 @@ describe RepositoryCheck::SingleRepositoryWorker do it 'skips wikis when disabled' do project = create(:project_empty_repo, wiki_enabled: false) # Make sure the test would fail if it checked the wiki repo - FileUtils.rm_rf(project.wiki.repository.path_to_repo) + destroy_wiki(project) subject.perform(project.id) expect(project.reload.last_repository_check_failed).to eq(false) end + + def destroy_wiki(project) + FileUtils.rm_rf(project.wiki.repository.path_to_repo) + end end From 7b6785b3b1d03ef8512e098285744e9956ec0891 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 15 Apr 2016 18:31:01 +0200 Subject: [PATCH 138/213] Use Module#prepend for method instrumentation By using Module#prepend we can define a Module containing all proxy methods. This removes the need for setting up crazy method alias chains and in turn prevents us from having to deal with all that madness (e.g. methods calling each other recursively). Fixes gitlab-org/gitlab-ce#15281 --- CHANGELOG | 1 + lib/gitlab/metrics/instrumentation.rb | 37 ++++++++++---- .../gitlab/metrics/instrumentation_spec.rb | 51 ++++++++++++++----- 3 files changed, 65 insertions(+), 24 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ede0c00e90..c29c207ca7 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) + - Method instrumentation now uses Module#prepend instead of aliasing methods - 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 diff --git a/lib/gitlab/metrics/instrumentation.rb b/lib/gitlab/metrics/instrumentation.rb index face1921d2..708ef79f30 100644 --- a/lib/gitlab/metrics/instrumentation.rb +++ b/lib/gitlab/metrics/instrumentation.rb @@ -11,6 +11,8 @@ module Gitlab module Instrumentation SERIES = 'method_calls' + PROXY_IVAR = :@__gitlab_instrumentation_proxy + def self.configure yield self end @@ -91,6 +93,18 @@ module Gitlab end end + # Returns true if a module is instrumented. + # + # mod - The module to check + def self.instrumented?(mod) + mod.instance_variable_defined?(PROXY_IVAR) + end + + # Returns the proxy module (if any) of `mod`. + def self.proxy_module(mod) + mod.instance_variable_get(PROXY_IVAR) + end + # Instruments a method. # # type - The type (:class or :instance) of method to instrument. @@ -99,9 +113,8 @@ module Gitlab def self.instrument(type, mod, name) return unless Metrics.enabled? - name = name.to_sym - alias_name = :"_original_#{name}" - target = type == :instance ? mod : mod.singleton_class + name = name.to_sym + target = type == :instance ? mod : mod.singleton_class if type == :instance target = mod @@ -113,6 +126,12 @@ module Gitlab method = mod.method(name) end + unless instrumented?(target) + target.instance_variable_set(PROXY_IVAR, Module.new) + end + + proxy_module = self.proxy_module(target) + # Some code out there (e.g. the "state_machine" Gem) checks the arity of # a method to make sure it only passes arguments when the method expects # any. If we were to always overwrite a method to take an `*args` @@ -125,17 +144,13 @@ module Gitlab args_signature = '*args, &block' end - send_signature = "__send__(#{alias_name.inspect}, #{args_signature})" - - target.class_eval <<-EOF, __FILE__, __LINE__ + 1 - alias_method #{alias_name.inspect}, #{name.inspect} - + proxy_module.class_eval <<-EOF, __FILE__, __LINE__ + 1 def #{name}(#{args_signature}) trans = Gitlab::Metrics::Instrumentation.transaction if trans start = Time.now - retval = #{send_signature} + retval = super duration = (Time.now - start) * 1000.0 if duration >= Gitlab::Metrics.method_call_threshold @@ -148,10 +163,12 @@ module Gitlab retval else - #{send_signature} + super end end EOF + + target.prepend(proxy_module) end # Small layer of indirection to make it easier to stub out the current diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb index ad4290c43b..5c885a7a98 100644 --- a/spec/lib/gitlab/metrics/instrumentation_spec.rb +++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb @@ -33,8 +33,16 @@ describe Gitlab::Metrics::Instrumentation do described_class.instrument_method(@dummy, :foo) end - it 'renames the original method' do - expect(@dummy).to respond_to(:_original_foo) + it 'instruments the Class' do + target = @dummy.singleton_class + + expect(described_class.instrumented?(target)).to eq(true) + end + + it 'defines a proxy method' do + mod = described_class.proxy_module(@dummy.singleton_class) + + expect(mod.method_defined?(:foo)).to eq(true) end it 'calls the instrumented method with the correct arguments' do @@ -76,6 +84,14 @@ describe Gitlab::Metrics::Instrumentation do expect(dummy.method(:test).arity).to eq(0) end + + describe 'when a module is instrumented multiple times' do + it 'calls the instrumented method with the correct arguments' do + described_class.instrument_method(@dummy, :foo) + + expect(@dummy.foo).to eq('foo') + end + end end describe 'with metrics disabled' do @@ -86,7 +102,9 @@ describe Gitlab::Metrics::Instrumentation do it 'does not instrument the method' do described_class.instrument_method(@dummy, :foo) - expect(@dummy).to_not respond_to(:_original_foo) + target = @dummy.singleton_class + + expect(described_class.instrumented?(target)).to eq(false) end end end @@ -100,8 +118,14 @@ describe Gitlab::Metrics::Instrumentation do instrument_instance_method(@dummy, :bar) end - it 'renames the original method' do - expect(@dummy.method_defined?(:_original_bar)).to eq(true) + it 'instruments instances of the Class' do + expect(described_class.instrumented?(@dummy)).to eq(true) + end + + it 'defines a proxy method' do + mod = described_class.proxy_module(@dummy) + + expect(mod.method_defined?(:bar)).to eq(true) end it 'calls the instrumented method with the correct arguments' do @@ -144,7 +168,7 @@ describe Gitlab::Metrics::Instrumentation do described_class. instrument_instance_method(@dummy, :bar) - expect(@dummy.method_defined?(:_original_bar)).to eq(false) + expect(described_class.instrumented?(@dummy)).to eq(false) end end end @@ -167,18 +191,17 @@ describe Gitlab::Metrics::Instrumentation do it 'recursively instruments a class hierarchy' do described_class.instrument_class_hierarchy(@dummy) - expect(@child1).to respond_to(:_original_child1_foo) - expect(@child2).to respond_to(:_original_child2_foo) + expect(described_class.instrumented?(@child1.singleton_class)).to eq(true) + expect(described_class.instrumented?(@child2.singleton_class)).to eq(true) - expect(@child1.method_defined?(:_original_child1_bar)).to eq(true) - expect(@child2.method_defined?(:_original_child2_bar)).to eq(true) + expect(described_class.instrumented?(@child1)).to eq(true) + expect(described_class.instrumented?(@child2)).to eq(true) end it 'does not instrument the root module' do described_class.instrument_class_hierarchy(@dummy) - expect(@dummy).to_not respond_to(:_original_foo) - expect(@dummy.method_defined?(:_original_bar)).to eq(false) + expect(described_class.instrumented?(@dummy)).to eq(false) end end @@ -190,7 +213,7 @@ describe Gitlab::Metrics::Instrumentation do it 'instruments all public class methods' do described_class.instrument_methods(@dummy) - expect(@dummy).to respond_to(:_original_foo) + expect(described_class.instrumented?(@dummy.singleton_class)).to eq(true) end it 'only instruments methods directly defined in the module' do @@ -223,7 +246,7 @@ describe Gitlab::Metrics::Instrumentation do it 'instruments all public instance methods' do described_class.instrument_instance_methods(@dummy) - expect(@dummy.method_defined?(:_original_bar)).to eq(true) + expect(described_class.instrumented?(@dummy)).to eq(true) end it 'only instruments methods directly defined in the module' do From c24640c721aed987a22c2e097cada73145cfb12b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 18 Apr 2016 11:06:30 +0100 Subject: [PATCH 139/213] Fixed issue with tasklist toggling sidebar refresh Closes #15270 --- app/views/projects/issues/update.js.haml | 3 --- app/views/projects/merge_requests/update.js.haml | 3 --- 2 files changed, 6 deletions(-) diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml index 986d8c220d..e69de29bb2 100644 --- a/app/views/projects/issues/update.js.haml +++ b/app/views/projects/issues/update.js.haml @@ -1,3 +0,0 @@ -$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}"; -$('aside.right-sidebar').effect('highlight'); -new IssuableContext(); diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml index 9cce5660e1..e69de29bb2 100644 --- a/app/views/projects/merge_requests/update.js.haml +++ b/app/views/projects/merge_requests/update.js.haml @@ -1,3 +0,0 @@ -$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}"; -$('aside.right-sidebar').effect('highlight'); -new IssuableContext(); From 651c3e841d6b0ca46edb6aa5cc62d42051a46d61 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 15 Apr 2016 12:14:27 +0200 Subject: [PATCH 140/213] Instrument Repository.clean_old_archives --- CHANGELOG | 1 + app/models/repository.rb | 8 +++++--- spec/models/repository_spec.rb | 25 ++++++++++++++++++++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c29c207ca7..7ef374b04d 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) - Method instrumentation now uses Module#prepend instead of aliasing methods + - Repository.clean_old_archives is now instrumented - 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 diff --git a/app/models/repository.rb b/app/models/repository.rb index 308c590e3f..589756f853 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -12,11 +12,13 @@ class Repository attr_accessor :path_with_namespace, :project def self.clean_old_archives - repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path + Gitlab::Metrics.measure(:clean_old_archives) do + repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path - return unless File.directory?(repository_downloads_path) + return unless File.directory?(repository_downloads_path) - Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete)) + Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete)) + end end def initialize(path_with_namespace, project) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index c163001b7c..f30a21e79a 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -910,9 +910,32 @@ describe Repository, models: true do end end + describe '.clean_old_archives' do + let(:path) { Gitlab.config.gitlab.repository_downloads_path } + + context 'when the downloads directory does not exist' do + it 'does not remove any archives' do + expect(File).to receive(:directory?).with(path).and_return(false) + + expect(Gitlab::Popen).not_to receive(:popen) + + described_class.clean_old_archives + end + end + + context 'when the downloads directory exists' do + it 'removes old archives' do + expect(File).to receive(:directory?).with(path).and_return(true) + + expect(Gitlab::Popen).to receive(:popen) + + described_class.clean_old_archives + end + end + end + def create_remote_branch(remote_name, branch_name, target) rugged = repository.rugged rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target) end - end From 1a57601d8cac0de35f301f8a8094c61fc94ed1cb Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 18 Apr 2016 12:00:20 +0100 Subject: [PATCH 141/213] Updated print style Closes #14201 --- app/assets/stylesheets/print.scss | 44 ++++++++++++++----- .../projects/notes/_notes_with_form.html.haml | 2 +- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss index 1be0551ad3..a30b649257 100644 --- a/app/assets/stylesheets/print.scss +++ b/app/assets/stylesheets/print.scss @@ -1,17 +1,37 @@ -/* Generic print styles */ -header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;} -.profiler-results {display: none;} - -/* Styles targeted specifically at printing files */ -.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;} -.file-title {display: none;} -.file-holder {border: none;} - .wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; } .wiki h1 {font-size: 30px;} .wiki h2 {font-size: 22px;} .wiki h3 {font-size: 18px; font-weight: bold; } -.sidebar-wrapper { display: none; } -.nav { display: none; } -.btn { display: none; } +header, +nav, +nav.main-nav, +nav.navbar-collapse, +nav.navbar-collapse.collapse, +.profiler-results, +.tree-ref-holder, +.tree-holder .breadcrumb, +.blob-commit-info, +.file-title, +.file-holder, +.sidebar-wrapper, +.nav, +.btn, +ul.notes-form, +.merge-request-ci-status .ci-status-link:after, +.issuable-gutter-toggle, +.gutter-toggle, +.issuable-details .content-block-small, +.edit-link, +.note-action-button { + display: none!important; +} + +.page-gutter { + padding-top: 0; + padding-left: 0; +} + +.right-sidebar { + top: 0; +} diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml index cc42aab5c5..1c39ce897a 100644 --- a/app/views/projects/notes/_notes_with_form.html.haml +++ b/app/views/projects/notes/_notes_with_form.html.haml @@ -1,6 +1,6 @@ %ul#notes-list.notes.main-notes-list.timeline = render "projects/notes/notes" -%ul.notes.timeline +%ul.notes.notes-form.timeline %li.timeline-entry - if can? current_user, :create_note, @project .timeline-icon.hidden-xs.hidden-sm From 167fa5c459f275e17a4d8f7155f15004cc29c28d Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 18 Apr 2016 12:02:39 +0100 Subject: [PATCH 142/213] CHANGELOG item --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index de52033078..9deb043787 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -76,6 +76,7 @@ v 8.7.0 (unreleased) - Add encrypted credentials for imported projects and migrate old ones - Author and participants are displayed first on users autocompletion - Show number sign on external issue reference text (Florent Baldino) + - Updated print style for issues v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 From a1363d39c6fe79d830dbce468c02880d2a5d7996 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 8 Apr 2016 11:02:26 +0200 Subject: [PATCH 143/213] Add `variables` keyword to job in CI config YAML --- app/models/ci/build.rb | 12 +++++- lib/ci/gitlab_ci_yaml_processor.rb | 3 +- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 44 ++++++++++++++------ spec/models/build_spec.rb | 16 +++++++ 4 files changed, 60 insertions(+), 15 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 7d33838044..89a9eb7633 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -365,9 +365,11 @@ module Ci self.update(erased_by: user, erased_at: Time.now) end - private - def yaml_variables + global_yaml_variables + job_yaml_variables + end + + def global_yaml_variables if commit.config_processor commit.config_processor.variables.map do |key, value| { key: key, value: value, public: true } @@ -377,6 +379,12 @@ module Ci end end + def job_yaml_variables + options[:variables].to_h.map do |key, value| + { key: key, value: value, public: true } + end + end + def project_variables project.variables.map do |variable| { key: variable.key, value: variable.value, public: false } diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index b7209c1414..712dc4492c 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -7,7 +7,7 @@ module Ci ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables, :cache] ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts, :cache, - :dependencies] + :dependencies, :variables] attr_reader :before_script, :image, :services, :variables, :path, :cache @@ -85,6 +85,7 @@ module Ci artifacts: job[:artifacts], cache: job[:cache] || @cache, dependencies: job[:dependencies], + variables: job[:variables], }.compact } end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index dcb8a3451b..a3a0d06e14 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -346,19 +346,39 @@ module Ci end describe "Variables" do - it "returns variables when defined" do - variables = { - var1: "value1", - var2: "value2", - } - config = YAML.dump({ - variables: variables, - before_script: ["pwd"], - rspec: { script: "rspec" } - }) + context 'when global variables are defined' do + it 'returns variables' do + variables = { + var1: "value1", + var2: "value2", + } + config = YAML.dump({ + variables: variables, + before_script: ["pwd"], + rspec: { script: "rspec" } + }) - config_processor = GitlabCiYamlProcessor.new(config, path) - expect(config_processor.variables).to eq(variables) + config_processor = GitlabCiYamlProcessor.new(config, path) + expect(config_processor.variables).to eq(variables) + end + end + + context 'when job variables are defined' do + let(:job_variables) { { KEY1: 'value1', SOME_KEY_2: 'value2'} } + let(:yaml_config) do + YAML.dump( + { before_script: ['pwd'], + rspec: { + variables: job_variables, + script: 'rspec' } + }) + end + + it 'appends job variable to job attributes' do + config = GitlabCiYamlProcessor.new(yaml_config, path) + + expect(config.builds.first[:options][:variables]).to eq job_variables + end end end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index b745780804..ee44a4c5f1 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -238,6 +238,22 @@ describe Ci::Build, models: true do it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) } end + + context 'when job variables are defined' do + before { build.update_attribute(:options, variables: job_variables) } + + context 'when job variables are unique' do + let(:job_variables) { { KEY1: 'value1', KEY2: 'value2' } } + let(:resulting_variables) do + [{ key: :KEY1, value: 'value1', public: true }, + { key: :KEY2, value: 'value2', public: true }] + end + + it 'includes job variables' do + expect(subject).to include(*resulting_variables) + end + end + end end end end From b578fbfb8572860490cdfd0163bfbf5f999bb1e6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 11 Apr 2016 13:09:46 +0200 Subject: [PATCH 144/213] Make it possible to override build variables --- app/models/ci/build.rb | 3 ++- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 2 +- spec/models/build_spec.rb | 24 ++++++++++++++++---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 89a9eb7633..61d39caeb7 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -147,7 +147,8 @@ module Ci end def variables - predefined_variables + yaml_variables + project_variables + trigger_variables + (predefined_variables + yaml_variables + project_variables + trigger_variables) + .reverse.uniq { |var| var[:key] }.reverse end def merge_request diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index a3a0d06e14..c2908f855e 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -364,7 +364,7 @@ module Ci end context 'when job variables are defined' do - let(:job_variables) { { KEY1: 'value1', SOME_KEY_2: 'value2'} } + let(:job_variables) { { KEY1: 'value1', SOME_KEY_2: 'value2' } } let(:yaml_config) do YAML.dump( { before_script: ['pwd'], diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index ee44a4c5f1..94d51435f3 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -240,17 +240,31 @@ describe Ci::Build, models: true do end context 'when job variables are defined' do + def result_variables + job_variables.map do |key, value| + { key: key, value: value, public: true } + end + end + before { build.update_attribute(:options, variables: job_variables) } context 'when job variables are unique' do let(:job_variables) { { KEY1: 'value1', KEY2: 'value2' } } - let(:resulting_variables) do - [{ key: :KEY1, value: 'value1', public: true }, - { key: :KEY2, value: 'value2', public: true }] - end it 'includes job variables' do - expect(subject).to include(*resulting_variables) + expect(subject).to include(*result_variables) + end + end + + context 'when job variable has same key other variable has' do + let(:job_variables) { { CI_BUILD_NAME: 'overridden' } } + + it 'contains job yaml variable' do + expect(subject).to include(*result_variables) + end + + it 'contains only one variable with this key' do + expect(subject.count { |var| var[:key] == :CI_BUILD_NAME } ).to eq 1 end end end From b7946b50fc1b23b1974f7d0306c06f6d92cc8466 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 15 Apr 2016 12:18:46 +0200 Subject: [PATCH 145/213] Read job variables directly from gitlab CI config --- app/models/ci/build.rb | 8 +++++-- lib/ci/gitlab_ci_yaml_processor.rb | 12 +++++++++-- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 4 ++-- spec/models/build_spec.rb | 22 ++++++++------------ spec/support/gitlab_stubs/gitlab_ci.yml | 17 +++++++++------ 5 files changed, 38 insertions(+), 25 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 61d39caeb7..6c4ee2a0d4 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -381,8 +381,12 @@ module Ci end def job_yaml_variables - options[:variables].to_h.map do |key, value| - { key: key, value: value, public: true } + if commit.config_processor + commit.config_processor.job_variables(name).map do |key, value| + { key: key, value: value, public: true } + end + else + [] end end diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 712dc4492c..548c6250e0 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -9,7 +9,7 @@ module Ci :allow_failure, :type, :stage, :when, :artifacts, :cache, :dependencies, :variables] - attr_reader :before_script, :image, :services, :variables, :path, :cache + attr_reader :before_script, :image, :services, :path, :cache def initialize(config, path = nil) @config = YAML.safe_load(config, [Symbol], [], true) @@ -40,6 +40,15 @@ module Ci @stages || DEFAULT_STAGES end + def variables + @variables + end + + def job_variables(name) + job = @jobs[name.to_sym] + job ? job[:variables] : [] + end + private def initial_parsing @@ -85,7 +94,6 @@ module Ci artifacts: job[:artifacts], cache: job[:cache] || @cache, dependencies: job[:dependencies], - variables: job[:variables], }.compact } end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index c2908f855e..04b1d8baeb 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -374,10 +374,10 @@ module Ci }) end - it 'appends job variable to job attributes' do + it 'returns job variables' do config = GitlabCiYamlProcessor.new(yaml_config, path) - expect(config.builds.first[:options][:variables]).to eq job_variables + expect(config.job_variables(:rspec)).to eq job_variables end end end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 94d51435f3..26a063de1e 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -240,31 +240,27 @@ describe Ci::Build, models: true do end context 'when job variables are defined' do - def result_variables - job_variables.map do |key, value| - { key: key, value: value, public: true } - end - end - - before { build.update_attribute(:options, variables: job_variables) } - context 'when job variables are unique' do - let(:job_variables) { { KEY1: 'value1', KEY2: 'value2' } } + before { allow(build).to receive(:name) { 'staging' } } it 'includes job variables' do - expect(subject).to include(*result_variables) + expect(subject).to include( + { key: :KEY1, value: 'value1', public: true }, + { key: :KEY2, value: 'value2', public: true } + ) end end context 'when job variable has same key other variable has' do - let(:job_variables) { { CI_BUILD_NAME: 'overridden' } } + before { allow(build).to receive(:name) { 'production' } } it 'contains job yaml variable' do - expect(subject).to include(*result_variables) + expect(subject).to include(key: :DB_NAME, value: 'mysql', + public: true) end it 'contains only one variable with this key' do - expect(subject.count { |var| var[:key] == :CI_BUILD_NAME } ).to eq 1 + expect(subject.count { |var| var[:key] == :DB_NAME } ).to eq 1 end end end diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml index a5b256bd3e..e55a61b2b9 100644 --- a/spec/support/gitlab_stubs/gitlab_ci.yml +++ b/spec/support/gitlab_stubs/gitlab_ci.yml @@ -4,7 +4,7 @@ services: before_script: - gem install bundler - - bundle install + - bundle install - bundle exec rake db:create variables: @@ -17,7 +17,7 @@ types: rspec: script: "rake spec" - tags: + tags: - ruby - postgres only: @@ -26,27 +26,32 @@ rspec: spinach: script: "rake spinach" allow_failure: true - tags: + tags: - ruby - mysql except: - tags staging: + variables: + KEY1: value1 + KEY2: value2 script: "cap deploy stating" type: deploy - tags: + tags: - ruby - mysql except: - stable production: + variables: + DB_NAME: mysql type: deploy - script: + script: - cap deploy production - cap notify - tags: + tags: - ruby - mysql only: From 3dec6e262984faa18b235db4bbd669ccdc80fc3f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 15 Apr 2016 12:28:27 +0200 Subject: [PATCH 146/213] Rename method that returns global envs in CI conf --- app/models/ci/build.rb | 2 +- lib/ci/gitlab_ci_yaml_processor.rb | 2 +- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 6c4ee2a0d4..1146028b9a 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -372,7 +372,7 @@ module Ci def global_yaml_variables if commit.config_processor - commit.config_processor.variables.map do |key, value| + commit.config_processor.global_variables.map do |key, value| { key: key, value: value, public: true } end else diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 548c6250e0..f69b861c98 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -40,7 +40,7 @@ module Ci @stages || DEFAULT_STAGES end - def variables + def global_variables @variables end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 04b1d8baeb..ba5f7b0e09 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -359,7 +359,7 @@ module Ci }) config_processor = GitlabCiYamlProcessor.new(config, path) - expect(config_processor.variables).to eq(variables) + expect(config_processor.global_variables).to eq(variables) end end From 1f3248644ef98879a8a4c31d5be0de9a774cc32a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 15 Apr 2016 14:57:22 +0200 Subject: [PATCH 147/213] Make CI config return empty array if no job variables --- lib/ci/gitlab_ci_yaml_processor.rb | 7 +++++-- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index f69b861c98..e152de590d 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -45,8 +45,11 @@ module Ci end def job_variables(name) - job = @jobs[name.to_sym] - job ? job[:variables] : [] + if job = @jobs[name.to_sym] + job[:variables] || [] + else + [] + end end private diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index ba5f7b0e09..45f08c95cc 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -380,6 +380,18 @@ module Ci expect(config.job_variables(:rspec)).to eq job_variables end end + + context 'when job variables are not defined' do + it 'returns empty array' do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec" } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + expect(config_processor.job_variables(:rspec)).to eq [] + end + end end describe "When" do From bdb06fa0437ebc1b03377d95763d1fefdcfd0441 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 15 Apr 2016 20:43:23 +0200 Subject: [PATCH 148/213] Improve build specs for job environment variables --- spec/models/build_spec.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 26a063de1e..5ee40c2253 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -240,8 +240,11 @@ describe Ci::Build, models: true do end context 'when job variables are defined' do + ## + # Job-level variables are defined in gitlab_ci.yml fixture + # context 'when job variables are unique' do - before { allow(build).to receive(:name) { 'staging' } } + let(:build) { create(:ci_build, name: 'staging') } it 'includes job variables' do expect(subject).to include( @@ -252,7 +255,7 @@ describe Ci::Build, models: true do end context 'when job variable has same key other variable has' do - before { allow(build).to receive(:name) { 'production' } } + let(:build) { create(:ci_build, name: 'production') } it 'contains job yaml variable' do expect(subject).to include(key: :DB_NAME, value: 'mysql', From 9a74b92d2afe028453c3fde7221fce20bcb073d9 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 15 Apr 2016 20:45:18 +0200 Subject: [PATCH 149/213] Add Changelog entry for job-level variables --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 7ef374b04d..b929f1fd7b 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) - Method instrumentation now uses Module#prepend instead of aliasing methods - Repository.clean_old_archives is now instrumented + - Add support for environment variables on a job level in CI configuration file - 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 From a6260b1eb7b4f30817d0cc5dbd80c860b5ed8a31 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 16 Apr 2016 20:21:01 +0200 Subject: [PATCH 150/213] Remove code that removes duplicate CI variables At this point this is being handled by GitLab Runner and we need to introduce this as a separate merge request. --- app/models/ci/build.rb | 3 +-- spec/models/build_spec.rb | 13 ------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 1146028b9a..85ef0523b3 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -147,8 +147,7 @@ module Ci end def variables - (predefined_variables + yaml_variables + project_variables + trigger_variables) - .reverse.uniq { |var| var[:key] }.reverse + predefined_variables + yaml_variables + project_variables + trigger_variables end def merge_request diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 5ee40c2253..b5d356aa06 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -253,19 +253,6 @@ describe Ci::Build, models: true do ) end end - - context 'when job variable has same key other variable has' do - let(:build) { create(:ci_build, name: 'production') } - - it 'contains job yaml variable' do - expect(subject).to include(key: :DB_NAME, value: 'mysql', - public: true) - end - - it 'contains only one variable with this key' do - expect(subject.count { |var| var[:key] == :DB_NAME } ).to eq 1 - end - end end end end From cf3e3effb01215c2556ba509d3cb4debe1d901e8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 16 Apr 2016 20:52:01 +0200 Subject: [PATCH 151/213] Minor refactoring in code related to job variables --- lib/ci/gitlab_ci_yaml_processor.rb | 6 +-- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 43 +++++++++++--------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index e152de590d..209da7ad81 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -45,11 +45,7 @@ module Ci end def job_variables(name) - if job = @jobs[name.to_sym] - job[:variables] || [] - else - [] - end + @jobs[name.to_sym].try(:fetch, :variables, []) || [] end private diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 45f08c95cc..9a014c4b6c 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -345,50 +345,55 @@ module Ci end end - describe "Variables" do + describe 'Variables' do context 'when global variables are defined' do - it 'returns variables' do + it 'returns global variables' do variables = { - var1: "value1", - var2: "value2", + VAR1: 'value1', + VAR2: 'value2', } + config = YAML.dump({ - variables: variables, - before_script: ["pwd"], - rspec: { script: "rspec" } - }) + variables: variables, + before_script: ['pwd'], + rspec: { script: 'rspec' } + }) config_processor = GitlabCiYamlProcessor.new(config, path) + expect(config_processor.global_variables).to eq(variables) end end context 'when job variables are defined' do - let(:job_variables) { { KEY1: 'value1', SOME_KEY_2: 'value2' } } - let(:yaml_config) do - YAML.dump( + it 'returns job variables' do + variables = { + KEY1: 'value1', + SOME_KEY_2: 'value2' + } + + config = YAML.dump( { before_script: ['pwd'], rspec: { - variables: job_variables, + variables: variables, script: 'rspec' } }) - end - it 'returns job variables' do - config = GitlabCiYamlProcessor.new(yaml_config, path) + config_processor = GitlabCiYamlProcessor.new(config, path) - expect(config.job_variables(:rspec)).to eq job_variables + expect(config_processor.job_variables(:rspec)).to eq variables end end context 'when job variables are not defined' do it 'returns empty array' do config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec" } - }) + before_script: ['pwd'], + rspec: { script: 'rspec' } + }) config_processor = GitlabCiYamlProcessor.new(config, path) + expect(config_processor.job_variables(:rspec)).to eq [] end end From 5d89bdb16a9340c7d8b43efc03dba623f8fb4d31 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 16 Apr 2016 21:17:05 +0200 Subject: [PATCH 152/213] Add documentation for job-level build variables --- doc/ci/variables/README.md | 4 +++- doc/ci/yaml/README.md | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index b0e53cbc26..35dac4c933 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -70,12 +70,14 @@ These variables can be later used in all executed commands and scripts. The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them. +Variables can be defined on a global level, but also on a job level. + More information about Docker integration can be found in [Using Docker Images](../docker/using_docker_images.md). ### User-defined variables (Secure Variables) **This feature requires GitLab Runner 0.4.0 or higher** -GitLab CI allows you to define per-project **Secure Variables** that are set in build environment. +GitLab CI allows you to define per-project **Secure Variables** that are set in build environment. The secure variables are stored out of the repository (the `.gitlab-ci.yml`). The variables are securely passed to GitLab Runner and are available in build environment. It's desired method to use them for storing passwords, secret keys or whatever you want. diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index abb6e97e5e..ea7d6dfa46 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -23,6 +23,7 @@ If you want a quick introduction to GitLab CI, follow our - [Jobs](#jobs) - [script](#script) - [stage](#stage) + - [job variables](#job-variables) - [only and except](#only-and-except) - [tags](#tags) - [when](#when) @@ -174,6 +175,8 @@ These variables can be later used in all executed commands and scripts. The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them. +Variables can be also defined on [job level](#job-variables). + ### cache >**Note:** @@ -324,6 +327,7 @@ job_name: | 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` | +| variables | no | Define build variables on a job level | | 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 | @@ -414,6 +418,23 @@ job: The above example will run `job` for all branches on `gitlab-org/gitlab-ce`, except master. +### job variables + +It is possible to define build variables using a `variables` keyword on a job +level. It works basically the same way like it's global-level equivalent but +allows you to define a job specific build variables. + +When `variables` keyword is used on a job level, it can override global YAML +build variables and predefined variables. + +Build variables priority is defined as follows: + +* predefined variables +* global YAML variables +* job YAML variables +* secure variables +* trigger variables + ### tags `tags` is used to select specific Runners from the list of all Runners that are From a2649739055842ce519fab5a8b0c68dbf23cb35a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 16 Apr 2016 22:04:59 +0200 Subject: [PATCH 153/213] Improve grammar in CI documentation for build vars --- doc/ci/variables/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 35dac4c933..f321a618f3 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -70,17 +70,20 @@ These variables can be later used in all executed commands and scripts. The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them. -Variables can be defined on a global level, but also on a job level. +Variables can be defined at a global level, but also at a job level. More information about Docker integration can be found in [Using Docker Images](../docker/using_docker_images.md). ### User-defined variables (Secure Variables) **This feature requires GitLab Runner 0.4.0 or higher** -GitLab CI allows you to define per-project **Secure Variables** that are set in build environment. +GitLab CI allows you to define per-project **Secure Variables** that are set in +the build environment. The secure variables are stored out of the repository (the `.gitlab-ci.yml`). -The variables are securely passed to GitLab Runner and are available in build environment. -It's desired method to use them for storing passwords, secret keys or whatever you want. +The variables are securely passed to GitLab Runner and are available in the +build environment. +It's desired method to use them for storing passwords, secret keys or whatever +you want. **The value of the variable can be shown in build log if explicitly asked to do so.** If your project is public or internal you can make the builds private. From a2957291b3a28eff3988dfc8bdcff4b99b20a1b3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sun, 17 Apr 2016 20:08:02 +0200 Subject: [PATCH 154/213] Update documentation on job level build variables --- doc/ci/variables/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index f321a618f3..e1df9e5f54 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -1,17 +1,19 @@ ## Variables + When receiving a build from GitLab CI, the runner prepares the build environment. It starts by setting a list of **predefined variables** (Environment Variables) and a list of **user-defined variables** The variables can be overwritten. They take precedence over each other in this order: 1. Secure variables -1. YAML-defined variables +1. YAML-defined job-leve variables +1. YAML-defined global variables 1. Predefined variables For example, if you define: -1. API_TOKEN=SECURE as Secure Variable -1. API_TOKEN=YAML as YAML-defined variable +1. `API_TOKEN=SECURE` as Secure Variable +1. `API_TOKEN=YAML` as YAML-defined variable -The API_TOKEN will take the Secure Variable value: `SECURE`. +The `API_TOKEN` will take the Secure Variable value: `SECURE`. ### Predefined variables (Environment Variables) From 2b1c08be8fa5c18f1fe151feb2a0938734086481 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sun, 17 Apr 2016 20:23:42 +0200 Subject: [PATCH 155/213] Validate job-level variables in YAML config file --- lib/ci/gitlab_ci_yaml_processor.rb | 8 ++++ spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 45 ++++++++++++++------ 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 209da7ad81..ddf34b60a8 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -153,6 +153,7 @@ module Ci validate_job_types!(name, job) validate_job_stage!(name, job) if job[:stage] + validate_job_variables!(name, job) if job[:variables] validate_job_cache!(name, job) if job[:cache] validate_job_artifacts!(name, job) if job[:artifacts] validate_job_dependencies!(name, job) if job[:dependencies] @@ -214,6 +215,13 @@ module Ci end end + def validate_job_variables!(name, job) + if job[:variables] && !validate_variables(job[:variables]) + raise ValidationError, + "#{name} job: variables should be a map of key-valued strings" + end + end + def validate_job_cache!(name, job) if job[:cache][:key] && !validate_string(job[:cache][:key]) raise ValidationError, "#{name} job: cache:key parameter should be a string" diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 9a014c4b6c..8813a72477 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -366,22 +366,41 @@ module Ci end context 'when job variables are defined' do - it 'returns job variables' do - variables = { - KEY1: 'value1', - SOME_KEY_2: 'value2' - } + context 'when syntax is correct' do + it 'returns job variables' do + variables = { + KEY1: 'value1', + SOME_KEY_2: 'value2' + } - config = YAML.dump( - { before_script: ['pwd'], - rspec: { - variables: variables, - script: 'rspec' } - }) + config = YAML.dump( + { before_script: ['pwd'], + rspec: { + variables: variables, + script: 'rspec' } + }) - config_processor = GitlabCiYamlProcessor.new(config, path) + config_processor = GitlabCiYamlProcessor.new(config, path) - expect(config_processor.job_variables(:rspec)).to eq variables + expect(config_processor.job_variables(:rspec)).to eq variables + end + end + + context 'when syntax is incorrect' do + it 'raises error' do + variables = [:KEY1, 'value1', :KEY2, 'value2'] + + config = YAML.dump( + { before_script: ['pwd'], + rspec: { + variables: variables, + script: 'rspec' } + }) + + expect { GitlabCiYamlProcessor.new(config, path) } + .to raise_error(GitlabCiYamlProcessor::ValidationError, + /job: variables should be a map/) + end end end From 2972a991df0e7a0071de1803205814f2d105df46 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 18 Apr 2016 12:29:36 +0200 Subject: [PATCH 156/213] Add minor fixes in docs for job-specific variables --- doc/ci/variables/README.md | 3 ++- doc/ci/yaml/README.md | 15 +++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index e1df9e5f54..70fb81492d 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -4,8 +4,9 @@ When receiving a build from GitLab CI, the runner prepares the build environment It starts by setting a list of **predefined variables** (Environment Variables) and a list of **user-defined variables** The variables can be overwritten. They take precedence over each other in this order: +1. Trigger variables 1. Secure variables -1. YAML-defined job-leve variables +1. YAML-defined job-level variables 1. YAML-defined global variables 1. Predefined variables diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index ea7d6dfa46..61475b4598 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -421,19 +421,14 @@ except master. ### job variables It is possible to define build variables using a `variables` keyword on a job -level. It works basically the same way like it's global-level equivalent but -allows you to define a job specific build variables. +level. It works basically the same way as its global-level equivalent but +allows you to define job-specific build variables. -When `variables` keyword is used on a job level, it can override global YAML +When the `variables` keyword is used on a job level, it overrides global YAML build variables and predefined variables. -Build variables priority is defined as follows: - -* predefined variables -* global YAML variables -* job YAML variables -* secure variables -* trigger variables +Build variables priority is defined in +[variables documentation](../variables/README.md). ### tags From 0592112c6e9cebfcae6869c8fa273d7c13b74c16 Mon Sep 17 00:00:00 2001 From: Arinde Eniola Date: Mon, 18 Apr 2016 12:36:15 +0100 Subject: [PATCH 157/213] change the link color of the target branch in a merge rquest --- app/views/projects/merge_requests/_show.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 285ad26316..7125d7d9d1 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -31,7 +31,8 @@ %span Request to merge %span.label-branch= source_branch_with_namespace(@merge_request) %span into - = link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" + %span.label-branch + = link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch) - if @merge_request.open? && @merge_request.diverged_from_target_branch? %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) From 1339fda1cd3325c0186b5f1b53444e7319ad3cb6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 18 Apr 2016 12:41:13 +0200 Subject: [PATCH 158/213] Minor refactorings in CI config --- lib/ci/gitlab_ci_yaml_processor.rb | 11 +++++++---- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index ddf34b60a8..b8ede3a7ed 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -45,7 +45,10 @@ module Ci end def job_variables(name) - @jobs[name.to_sym].try(:fetch, :variables, []) || [] + job = @jobs[name.to_sym] + return [] unless job + + job.fetch(:variables, []) end private @@ -123,7 +126,7 @@ module Ci end unless @variables.nil? || validate_variables(@variables) - raise ValidationError, "variables should be a map of key-valued strings" + raise ValidationError, "variables should be a map of key-value strings" end if @cache @@ -216,9 +219,9 @@ module Ci end def validate_job_variables!(name, job) - if job[:variables] && !validate_variables(job[:variables]) + unless validate_variables(job[:variables]) raise ValidationError, - "#{name} job: variables should be a map of key-valued strings" + "#{name} job: variables should be a map of key-value strings" end end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 8813a72477..5f4b63bcaf 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -786,14 +786,14 @@ EOT config = YAML.dump({ variables: "test", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings") end - it "returns errors if variables is not a map of key-valued strings" do + it "returns errors if variables is not a map of key-value strings" do config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings") end it "returns errors if job when is not on_success, on_failure or always" do From 63bd1f92d96c9d023723a78259be3c86846e30a5 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 18 Apr 2016 07:51:49 -0400 Subject: [PATCH 159/213] Fix rubocop complains --- lib/ci/gitlab_ci_yaml_processor.rb | 64 +++++++++++--------- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 4 +- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index a87329c296..f1d6f2be1e 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -100,6 +100,29 @@ module Ci end def validate! + validate_global! + + @jobs.each do |name, job| + validate_job!(name, job) + end + + true + end + + def validate_job!(name, job) + validate_job_name!(name) + validate_job_keys!(name, job) + validate_job_types!(name, job) + + validate_job_stage!(name, job) if job[:stage] + validate_job_cache!(name, job) if job[:cache] + validate_job_artifacts!(name, job) if job[:artifacts] + validate_job_dependencies!(name, job) if job[:dependencies] + end + + private + + def validate_global! unless validate_array_of_strings(@before_script) raise ValidationError, "before_script should be an array of strings" end @@ -124,40 +147,23 @@ module Ci raise ValidationError, "variables should be a map of key-valued strings" end - if @cache - if @cache[:key] && !validate_string(@cache[:key]) - raise ValidationError, "cache:key parameter should be a string" - end - - if @cache[:untracked] && !validate_boolean(@cache[:untracked]) - raise ValidationError, "cache:untracked parameter should be an boolean" - end - - if @cache[:paths] && !validate_array_of_strings(@cache[:paths]) - raise ValidationError, "cache:paths parameter should be an array of strings" - end - end - - @jobs.each do |name, job| - validate_job!(name, job) - end - - true + validate_global_cache! if @cache end - def validate_job!(name, job) - validate_job_name!(name) - validate_job_keys!(name, job) - validate_job_types!(name, job) + def validate_global_cache! + if @cache[:key] && !validate_string(@cache[:key]) + raise ValidationError, "cache:key parameter should be a string" + end - validate_job_stage!(name, job) if job[:stage] - validate_job_cache!(name, job) if job[:cache] - validate_job_artifacts!(name, job) if job[:artifacts] - validate_job_dependencies!(name, job) if job[:dependencies] + if @cache[:untracked] && !validate_boolean(@cache[:untracked]) + raise ValidationError, "cache:untracked parameter should be an boolean" + end + + if @cache[:paths] && !validate_array_of_strings(@cache[:paths]) + raise ValidationError, "cache:paths parameter should be an array of strings" + end end - private - def validate_job_name!(name) if name.blank? || !validate_string(name) raise ValidationError, "job name should be non-empty string" diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 2421d6eee8..b94fec2ddf 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -295,12 +295,12 @@ module Ci describe "after_script" do context "in global context" do - let(:config) { + let(:config) do { after_script: ["after_script"], test: { script: ["script"] } } - } + end it "return after_script in options" do expect(subject[:options][:after_script]).to eq(["after_script"]) From fe3ac403b97eb8dbe45189ba3737537a536b8956 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 18 Apr 2016 13:45:26 +0100 Subject: [PATCH 160/213] Addressed feedback --- app/assets/javascripts/importer_status.js.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/importer_status.js.coffee b/app/assets/javascripts/importer_status.js.coffee index 2eb151a641..b0edc89564 100644 --- a/app/assets/javascripts/importer_status.js.coffee +++ b/app/assets/javascripts/importer_status.js.coffee @@ -23,14 +23,14 @@ class @ImporterStatus $('.js-import-all') .off 'click' - .on 'click', (e) => - $btn = $(e.currentTarget) + .on 'click', (e) -> + $btn = $(@) $btn .disable() .addClass 'is-loading' $('.js-add-to-import').each -> - $(this).click() + $(this).trigger('click') setAutoUpdate: -> setInterval (=> From 7e6f0ac0e04c3fa05a8d952bf86463001dc48487 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 18 Apr 2016 12:07:40 +0200 Subject: [PATCH 161/213] Count the number of SQL queries per transaction Fixes gitlab-org/gitlab-ce#15335 --- CHANGELOG | 1 + lib/gitlab/metrics/subscribers/active_record.rb | 1 + spec/lib/gitlab/metrics/subscribers/active_record_spec.rb | 3 +++ 3 files changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index b929f1fd7b..502ed0e959 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.7.0 (unreleased) - Method instrumentation now uses Module#prepend instead of aliasing methods - Repository.clean_old_archives is now instrumented - Add support for environment variables on a job level in CI configuration file + - SQL query counts are now tracked per transaction - 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 diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index 8008b3bc89..96cad941d5 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -9,6 +9,7 @@ module Gitlab return unless current_transaction current_transaction.increment(:sql_duration, event.duration) + current_transaction.increment(:sql_count, 1) end private diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb index 7bc070a4d0..e3293a0120 100644 --- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -28,6 +28,9 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do expect(transaction).to receive(:increment). with(:sql_duration, 0.2) + expect(transaction).to receive(:increment). + with(:sql_count, 1) + subscriber.sql(event) end end From 6b124d42d9861a5f8c3dd5add7cabc864fa3673c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 15 Apr 2016 15:51:41 +0200 Subject: [PATCH 162/213] Add configurable shared runners text --- CHANGELOG | 2 ++ .../admin/application_settings_controller.rb | 1 + app/helpers/application_settings_helper.rb | 4 ++++ .../admin/application_settings/_form.html.haml | 6 +++++- .../projects/runners/_shared_runners.html.haml | 7 +++++-- ...hared_runners_text_to_application_settings.rb | 5 +++++ db/schema.rb | 3 ++- spec/features/runners_spec.rb | 16 ++++++++++++++++ 8 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb diff --git a/CHANGELOG b/CHANGELOG index 571c31e63a..d1b20a34b1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,8 @@ v 8.7.0 (unreleased) - 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 + - Improved Markdown rendering performance !3389 (Yorick Peterse) + - Make shared runners text in box configurable (Kamil TrzciÅ„ski) - 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 diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index b4a28b8dd3..2520d4452a 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -75,6 +75,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :admin_notification_email, :user_oauth_applications, :shared_runners_enabled, + :shared_runners_text, :max_artifacts_size, :metrics_enabled, :metrics_host, diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 60a0ff32c9..914b0ef604 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -15,6 +15,10 @@ module ApplicationSettingsHelper current_application_settings.sign_in_text end + def shared_runners_text + current_application_settings.shared_runners_text + end + def user_oauth_applications? current_application_settings.user_oauth_applications end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 555aea554f..e5bc7d4fdb 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -153,7 +153,11 @@ = f.label :shared_runners_enabled do = f.check_box :shared_runners_enabled Enable shared runners for new projects - + .form-group + = f.label :shared_runners_text, class: 'control-label col-sm-2' + .col-sm-10 + = f.text_area :shared_runners_text, class: 'form-control', rows: 4 + .help-block Markdown enabled .form-group = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2' .col-sm-10 diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml index 6a37f444bb..ec47ed79e5 100644 --- a/app/views/projects/runners/_shared_runners.html.haml +++ b/app/views/projects/runners/_shared_runners.html.haml @@ -1,7 +1,10 @@ %h3 Shared runners -.bs-callout.bs-callout-warning - GitLab Runners do not offer secure isolation between projects that they do builds for. You are TRUSTING all GitLab users who can push code to project A, B or C to run shell scripts on the machine hosting runner X. +.bs-callout.bs-callout-warning.shared-runners-description + - if shared_runners_text.present? + = markdown(shared_runners_text) + - else + GitLab Runners do not offer secure isolation between projects that they do builds for. You are TRUSTING all GitLab users who can push code to project A, B or C to run shell scripts on the machine hosting runner X. %hr - if @project.shared_runners_enabled? = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-warning', method: :post do diff --git a/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb new file mode 100644 index 0000000000..d493044c67 --- /dev/null +++ b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb @@ -0,0 +1,5 @@ +class AddSharedRunnersTextToApplicationSettings < ActiveRecord::Migration + def change + add_column :application_settings, :shared_runners_text, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index 42c261003b..0ab32cab8b 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: 20160412140240) do +ActiveRecord::Schema.define(version: 20160415133440) 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: 20160412140240) do t.boolean "email_author_in_body", default: false t.integer "default_group_visibility" t.boolean "repository_checks_enabled", default: true + t.text "shared_runners_text" end create_table "audit_events", force: :cascade do |t| diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index e8886e7edf..1fe7645250 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -80,6 +80,22 @@ describe "Runners" do end end + describe "shared runners description" do + let(:shared_runners_text) { 'custom shared runners description' } + + before { stub_application_setting(shared_runners_text: shared_runners_text) } + + before do + @project = FactoryGirl.create :empty_project, shared_runners_enabled: false + @project.team << [user, :master] + visit runners_path(@project) + end + + it "sees shared runners description" do + expect(page.find(".shared-runners-description")).to have_content(shared_runners_text) + end + end + describe "show page" do before do @project = FactoryGirl.create :empty_project From 21041525af5a678053cc47e082dc7484a67dfdf5 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 18 Apr 2016 09:56:38 -0400 Subject: [PATCH 163/213] Update according to review comments --- CHANGELOG | 2 +- spec/features/runners_spec.rb | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d1b20a34b1..d691113e19 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,7 +19,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 - Improved Markdown rendering performance !3389 (Yorick Peterse) - - Make shared runners text in box configurable (Kamil TrzciÅ„ski) + - Make shared runners text in box configurable - 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 diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 1fe7645250..789b06d7c6 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -83,12 +83,11 @@ describe "Runners" do describe "shared runners description" do let(:shared_runners_text) { 'custom shared runners description' } - before { stub_application_setting(shared_runners_text: shared_runners_text) } - before do - @project = FactoryGirl.create :empty_project, shared_runners_enabled: false - @project.team << [user, :master] - visit runners_path(@project) + stub_application_setting(shared_runners_text: shared_runners_text) + project = FactoryGirl.create :empty_project, shared_runners_enabled: false + project.team << [user, :master] + visit runners_path(project) end it "sees shared runners description" do From b05f0a48584ea45cc89a8efaafd8e54642b8497c Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 24 Mar 2016 12:55:04 -0300 Subject: [PATCH 164/213] Restrict user profiles based on restricted visibility levels --- app/controllers/users_controller.rb | 4 ++++ app/models/user.rb | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 8e7956da48..49ddcfed7b 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,6 +1,7 @@ class UsersController < ApplicationController skip_before_action :authenticate_user! before_action :set_user + before_filter :authorize_read_user, only: [:show] def show respond_to do |format| @@ -74,6 +75,9 @@ class UsersController < ApplicationController end private + def authorize_read_user + render_404 unless @user.public? + end def set_user @user = User.find_by_username!(params[:username]) diff --git a/app/models/user.rb b/app/models/user.rb index 031315debd..e2b602d598 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -835,6 +835,10 @@ class User < ActiveRecord::Base notification_settings.find_or_initialize_by(source: source) end + def public? + current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) + end + private def projects_union From 57519565f167cb771ffed504feefe7b0eb37c027 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Tue, 29 Mar 2016 12:24:42 -0300 Subject: [PATCH 165/213] Move verification to abilities --- .../groups/group_members_controller.rb | 7 ++++ .../projects/project_members_controller.rb | 7 ++++ app/controllers/users_controller.rb | 8 +++-- app/models/ability.rb | 33 ++++++++++++++++--- app/models/user.rb | 4 --- 5 files changed, 47 insertions(+), 12 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index d5ef33888c..50398d5d3b 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,6 +1,7 @@ class Groups::GroupMembersController < Groups::ApplicationController # Authorize before_action :authorize_admin_group_member!, except: [:index, :leave] + before_action :authorize_read_group_members, only: [:index] def index @project = @group.projects.find(params[:project_id]) if params[:project_id] @@ -79,4 +80,10 @@ class Groups::GroupMembersController < Groups::ApplicationController def member_params params.require(:group_member).permit(:access_level, :user_id) end + + private + + def authorize_read_group_members + render_404 unless can?(current_user, :read_group_members, @group) + end end diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index e457db2f0b..7badbb47d0 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -1,6 +1,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController # Authorize before_action :authorize_admin_project_member!, except: :leave + before_action :authorize_read_project_members, only: :index def index @project_members = @project.project_members @@ -112,4 +113,10 @@ class Projects::ProjectMembersController < Projects::ApplicationController def member_params params.require(:project_member).permit(:user_id, :access_level) end + + private + + def authorize_read_project_members + can?(current_user, :read_project_members, @project) + end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 49ddcfed7b..69b66e161c 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,7 +1,8 @@ class UsersController < ApplicationController skip_before_action :authenticate_user! - before_action :set_user - before_filter :authorize_read_user, only: [:show] + #TO-DO Remove this "set_user" before action. It is not good to use before filters for loading database records. + before_action :set_user, except: [:show] + before_action :authorize_read_user, only: [:show] def show respond_to do |format| @@ -76,7 +77,8 @@ class UsersController < ApplicationController private def authorize_read_user - render_404 unless @user.public? + set_user + render_404 unless can?(current_user, :read_user, @user) end def set_user diff --git a/app/models/ability.rb b/app/models/ability.rb index c0bf6def7c..d3e724b84e 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -18,6 +18,7 @@ class Ability when Namespace then namespace_abilities(user, subject) when GroupMember then group_member_abilities(user, subject) when ProjectMember then project_member_abilities(user, subject) + when User then user_abilities() else [] end.concat(global_abilities(user)) end @@ -35,6 +36,8 @@ class Ability anonymous_project_abilities(subject) when subject.is_a?(Group) || subject.respond_to?(:group) anonymous_group_abilities(subject) + when subject.is_a?(User) + anonymous_user_abilities() else [] end @@ -67,6 +70,10 @@ class Ability # Allow to read issues by anonymous user if issue is not confidential rules << :read_issue unless subject.is_a?(Issue) && subject.confidential? + # Allow anonymous users to read project members if public is not a restricted level + restricted_public_level = current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) + rules << :read_project_member unless restricted_public_level + rules - project_disabled_features_rules(project) else [] @@ -81,17 +88,23 @@ class Ability end def anonymous_group_abilities(subject) + rules = [] + group = if subject.is_a?(Group) subject else subject.group end - if group && group.public? - [:read_group] - else - [] + if group + rules << [:read_group] if group.public? + + # Allow anonymous users to read project members if public is not a restricted level + restricted_public_level = current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) + rules << [:read_group_members] unless restricted_public_level end + + rules end def anonymous_personal_snippet_abilities(snippet) @@ -110,6 +123,11 @@ class Ability end end + def anonymous_user_abilities() + restricted_by_public = current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) + [:read_user] unless restricted_by_public + end + def global_abilities(user) rules = [] rules << :create_group if user.can_create_group @@ -164,6 +182,7 @@ class Ability :download_code, :fork_project, :read_commit_status, + :read_project_members ] end @@ -285,7 +304,7 @@ class Ability def group_abilities(user, group) rules = [] - rules << :read_group if can_read_group?(user, group) + rules << [:read_group, :read_group_members] if can_read_group?(user, group) # Only group masters and group owners can create new projects if group.has_master?(user) || group.has_owner?(user) || user.admin? @@ -456,6 +475,10 @@ class Ability rules end + def user_abilities() + [:read_user] + end + def abilities @abilities ||= begin abilities = Six.new diff --git a/app/models/user.rb b/app/models/user.rb index e2b602d598..031315debd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -835,10 +835,6 @@ class User < ActiveRecord::Base notification_settings.find_or_initialize_by(source: source) end - def public? - current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) - end - private def projects_union From 668d6ffa437aa5c920e987beb5de4e8dacbfd00c Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 30 Mar 2016 17:14:21 -0300 Subject: [PATCH 166/213] Add specs and fix code --- app/controllers/users_controller.rb | 2 +- app/models/ability.rb | 25 +++++++++++-------- app/views/layouts/nav/_group.html.haml | 13 ++++++---- app/views/layouts/nav/_project.html.haml | 2 +- .../groups/group_members_controller_spec.rb | 19 ++++++++++++++ spec/controllers/users_controller_spec.rb | 22 ++++++++++++++++ 6 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 spec/controllers/groups/group_members_controller_spec.rb diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 69b66e161c..642f5eea1d 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,6 +1,6 @@ class UsersController < ApplicationController skip_before_action :authenticate_user! - #TO-DO Remove this "set_user" before action. It is not good to use before filters for loading database records. + #TODO felipe_artur: Remove this "set_user" before action. It is not good to use before filters for loading database records. before_action :set_user, except: [:show] before_action :authorize_read_user, only: [:show] diff --git a/app/models/ability.rb b/app/models/ability.rb index d3e724b84e..2914ca16b2 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,4 +1,6 @@ class Ability + @public_restricted = nil + class << self def allowed(user, subject) return anonymous_abilities(user, subject) if user.nil? @@ -18,7 +20,7 @@ class Ability when Namespace then namespace_abilities(user, subject) when GroupMember then group_member_abilities(user, subject) when ProjectMember then project_member_abilities(user, subject) - when User then user_abilities() + when User then user_abilities else [] end.concat(global_abilities(user)) end @@ -37,7 +39,7 @@ class Ability when subject.is_a?(Group) || subject.respond_to?(:group) anonymous_group_abilities(subject) when subject.is_a?(User) - anonymous_user_abilities() + anonymous_user_abilities else [] end @@ -71,8 +73,7 @@ class Ability rules << :read_issue unless subject.is_a?(Issue) && subject.confidential? # Allow anonymous users to read project members if public is not a restricted level - restricted_public_level = current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) - rules << :read_project_member unless restricted_public_level + rules << :read_project_member unless restricted_public_level? rules - project_disabled_features_rules(project) else @@ -100,8 +101,7 @@ class Ability rules << [:read_group] if group.public? # Allow anonymous users to read project members if public is not a restricted level - restricted_public_level = current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) - rules << [:read_group_members] unless restricted_public_level + rules << [:read_group_members] unless restricted_public_level? end rules @@ -123,9 +123,8 @@ class Ability end end - def anonymous_user_abilities() - restricted_by_public = current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) - [:read_user] unless restricted_by_public + def anonymous_user_abilities + [:read_user] unless restricted_public_level? end def global_abilities(user) @@ -303,7 +302,6 @@ class Ability def group_abilities(user, group) rules = [] - rules << [:read_group, :read_group_members] if can_read_group?(user, group) # Only group masters and group owners can create new projects @@ -475,7 +473,7 @@ class Ability rules end - def user_abilities() + def user_abilities [:read_user] end @@ -493,6 +491,11 @@ class Ability private + def restricted_public_level? + @public_restricted ||= current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) + @public_restricted + end + def named_abilities(name) [ :"read_#{name}", diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 55940741dc..927f61c89f 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -36,11 +36,14 @@ Merge Requests - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute %span.count= number_with_delimiter(merge_requests.count) - = nav_link(controller: [:group_members]) do - = link_to group_group_members_path(@group), title: 'Members' do - = icon('users fw') - %span - Members + + - if can?(current_user, :read_group_members, @group) + = nav_link(controller: [:group_members]) do + = link_to group_group_members_path(@group), title: 'Members' do + = icon('users fw') + %span + Members + - if can?(current_user, :admin_group, @group) = nav_link(html_options: { class: "separate-item" }) do = link_to edit_group_path(@group), title: 'Settings' do diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 86b46e8c75..d651de0fbe 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -77,7 +77,7 @@ Merge Requests %span.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) - - if project_nav_tab? :settings + - if project_nav_tab?(:settings) && can?(current_user, :read_project_members, @project) = nav_link(controller: [:project_members, :teams]) do = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do = icon('users fw') diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb new file mode 100644 index 0000000000..3a4dd2bf1f --- /dev/null +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Groups::GroupMembersController do + let(:user) { create(:user) } + let(:group) { create(:group) } + + + context "When public visibility level is restricted" do + before do + group.add_owner(user) + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + it 'does not show group members' do + get :index, group_id: group.path + expect(response.status).to eq(404) + end + end +end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 7337ff58be..f6235c29a1 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -38,6 +38,28 @@ describe UsersController do end end end + + context 'When public visibility level is restricted' do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + context 'when logged out' do + it 'renders 404' do + get :show, username: user.username + expect(response.status).to eq(404) + end + end + + context 'when logged in' do + before { sign_in(user) } + + it 'renders 404' do + get :show, username: user.username + expect(response.status).to eq(200) + end + end + end end describe 'GET #calendar' do From e8a77c0aee3eaf99793b3678a0eb97194244b339 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 31 Mar 2016 11:36:40 -0300 Subject: [PATCH 167/213] Fix code --- app/controllers/groups/group_members_controller.rb | 4 ++-- app/controllers/users_controller.rb | 6 +++--- app/models/ability.rb | 4 ---- spec/controllers/groups/group_members_controller_spec.rb | 3 +-- spec/controllers/users_controller_spec.rb | 3 ++- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 50398d5d3b..9fc7263580 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,7 +1,7 @@ class Groups::GroupMembersController < Groups::ApplicationController # Authorize before_action :authorize_admin_group_member!, except: [:index, :leave] - before_action :authorize_read_group_members, only: [:index] + before_action :authorize_read_group_members!, only: [:index] def index @project = @group.projects.find(params[:project_id]) if params[:project_id] @@ -83,7 +83,7 @@ class Groups::GroupMembersController < Groups::ApplicationController private - def authorize_read_group_members + def authorize_read_group_members! render_404 unless can?(current_user, :read_group_members, @group) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 642f5eea1d..233dca54b9 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,8 +1,7 @@ class UsersController < ApplicationController skip_before_action :authenticate_user! - #TODO felipe_artur: Remove this "set_user" before action. It is not good to use before filters for loading database records. before_action :set_user, except: [:show] - before_action :authorize_read_user, only: [:show] + before_action :authorize_read_user!, only: [:show] def show respond_to do |format| @@ -76,7 +75,8 @@ class UsersController < ApplicationController end private - def authorize_read_user + + def authorize_read_user! set_user render_404 unless can?(current_user, :read_user, @user) end diff --git a/app/models/ability.rb b/app/models/ability.rb index 2914ca16b2..874ec36094 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,5 +1,4 @@ class Ability - @public_restricted = nil class << self def allowed(user, subject) @@ -72,7 +71,6 @@ class Ability # Allow to read issues by anonymous user if issue is not confidential rules << :read_issue unless subject.is_a?(Issue) && subject.confidential? - # Allow anonymous users to read project members if public is not a restricted level rules << :read_project_member unless restricted_public_level? rules - project_disabled_features_rules(project) @@ -100,7 +98,6 @@ class Ability if group rules << [:read_group] if group.public? - # Allow anonymous users to read project members if public is not a restricted level rules << [:read_group_members] unless restricted_public_level? end @@ -493,7 +490,6 @@ class Ability def restricted_public_level? @public_restricted ||= current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) - @public_restricted end def named_abilities(name) diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index 3a4dd2bf1f..cf8f2a2805 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -4,8 +4,7 @@ describe Groups::GroupMembersController do let(:user) { create(:user) } let(:group) { create(:group) } - - context "When public visibility level is restricted" do + context "when public visibility level is restricted" do before do group.add_owner(user) stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index f6235c29a1..26acfd3bd9 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -54,9 +54,10 @@ describe UsersController do context 'when logged in' do before { sign_in(user) } - it 'renders 404' do + it 'renders show' do get :show, username: user.username expect(response.status).to eq(200) + expect(response).to render_template('show') end end end From 147879ae66fd742d13bbb5b72d492788bc48c8d9 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 1 Apr 2016 16:50:18 -0300 Subject: [PATCH 168/213] Fix specs --- app/models/ability.rb | 4 ++-- spec/controllers/users_controller_spec.rb | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 874ec36094..684834aa39 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -156,6 +156,7 @@ class Ability rules -= project_archived_rules end + rules << :read_project_members rules - project_disabled_features_rules(project) end end @@ -177,8 +178,7 @@ class Ability @public_project_rules ||= project_guest_rules + [ :download_code, :fork_project, - :read_commit_status, - :read_project_members + :read_commit_status ] end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 26acfd3bd9..7701da9747 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -30,10 +30,12 @@ describe UsersController do end describe 'when logged out' do + before { stub_application_setting(restricted_visibility_levels: []) } + it 'renders the show template' do get :show, username: user.username - expect(response).to be_success + expect(response.status).to eq(200) expect(response).to render_template('show') end end From 07b38c3b389b8b0b6a3d6af7a38555c189e71afe Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Tue, 5 Apr 2016 18:56:07 -0300 Subject: [PATCH 169/213] Code fixes --- .../projects/project_members_controller.rb | 7 ------- app/controllers/users_controller.rb | 21 +++++++++---------- app/models/ability.rb | 10 ++------- app/views/layouts/nav/_project.html.haml | 2 +- spec/controllers/users_controller_spec.rb | 2 +- 5 files changed, 14 insertions(+), 28 deletions(-) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 7badbb47d0..e457db2f0b 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -1,7 +1,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController # Authorize before_action :authorize_admin_project_member!, except: :leave - before_action :authorize_read_project_members, only: :index def index @project_members = @project.project_members @@ -113,10 +112,4 @@ class Projects::ProjectMembersController < Projects::ApplicationController def member_params params.require(:project_member).permit(:user_id, :access_level) end - - private - - def authorize_read_project_members - can?(current_user, :read_project_members, @project) - end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 233dca54b9..2ae180c8a1 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,6 +1,6 @@ class UsersController < ApplicationController skip_before_action :authenticate_user! - before_action :set_user, except: [:show] + before_action :user before_action :authorize_read_user!, only: [:show] def show @@ -77,26 +77,25 @@ class UsersController < ApplicationController private def authorize_read_user! - set_user - render_404 unless can?(current_user, :read_user, @user) + render_404 unless can?(current_user, :read_user, user) end - def set_user - @user = User.find_by_username!(params[:username]) + def user + @user ||= User.find_by_username!(params[:username]) end def contributed_projects - ContributedProjectsFinder.new(@user).execute(current_user) + ContributedProjectsFinder.new(user).execute(current_user) end def contributions_calendar @contributions_calendar ||= Gitlab::ContributionsCalendar. - new(contributed_projects, @user) + new(contributed_projects, user) end def load_events # Get user activity feed for projects common for both users - @events = @user.recent_events. + @events = user.recent_events. merge(projects_for_current_user). references(:project). with_associations. @@ -105,16 +104,16 @@ class UsersController < ApplicationController def load_projects @projects = - PersonalProjectsFinder.new(@user).execute(current_user) + PersonalProjectsFinder.new(user).execute(current_user) .page(params[:page]) end def load_contributed_projects - @contributed_projects = contributed_projects.joined(@user) + @contributed_projects = contributed_projects.joined(user) end def load_groups - @groups = JoinedGroupsFinder.new(@user).execute(current_user) + @groups = JoinedGroupsFinder.new(user).execute(current_user) end def projects_for_current_user diff --git a/app/models/ability.rb b/app/models/ability.rb index 684834aa39..7c452c69d1 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,5 +1,4 @@ class Ability - class << self def allowed(user, subject) return anonymous_abilities(user, subject) if user.nil? @@ -58,7 +57,6 @@ class Ability :read_label, :read_milestone, :read_project_snippet, - :read_project_member, :read_merge_request, :read_note, :read_commit_status, @@ -71,8 +69,6 @@ class Ability # Allow to read issues by anonymous user if issue is not confidential rules << :read_issue unless subject.is_a?(Issue) && subject.confidential? - rules << :read_project_member unless restricted_public_level? - rules - project_disabled_features_rules(project) else [] @@ -96,9 +92,8 @@ class Ability end if group - rules << [:read_group] if group.public? - - rules << [:read_group_members] unless restricted_public_level? + rules << :read_group if group.public? + rules << :read_group_members unless restricted_public_level? end rules @@ -156,7 +151,6 @@ class Ability rules -= project_archived_rules end - rules << :read_project_members rules - project_disabled_features_rules(project) end end diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index d651de0fbe..2c9e215948 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -77,7 +77,7 @@ Merge Requests %span.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) - - if project_nav_tab?(:settings) && can?(current_user, :read_project_members, @project) + - if project_nav_tab?(:settings) = nav_link(controller: [:project_members, :teams]) do = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do = icon('users fw') diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 7701da9747..948935bc10 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -41,7 +41,7 @@ describe UsersController do end end - context 'When public visibility level is restricted' do + context 'when public visibility level is restricted' do before do stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) end From ce96d482d9056e9acdfea02d055c2706653cba92 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 6 Apr 2016 18:09:24 -0300 Subject: [PATCH 170/213] Insert users check into api --- app/models/ability.rb | 6 +++--- lib/api/api_guard.rb | 4 ++++ lib/api/users.rb | 10 ++++++++-- spec/controllers/users_controller_spec.rb | 2 -- spec/requests/api/users_spec.rb | 18 ++++++++++++++++++ 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 7c452c69d1..e327d4eef2 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -91,8 +91,8 @@ class Ability subject.group end - if group - rules << :read_group if group.public? + if group.public? + rules << :read_group rules << :read_group_members unless restricted_public_level? end @@ -483,7 +483,7 @@ class Ability private def restricted_public_level? - @public_restricted ||= current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) + current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) end def named_abilities(name) diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index b9994fcefd..6ce5529abf 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -79,6 +79,10 @@ module APIGuard @current_user end + def public_access_restricted? + current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) + end + private def find_access_token @access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods) diff --git a/lib/api/users.rb b/lib/api/users.rb index 0a14bac07c..9647a40686 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -11,6 +11,10 @@ module API # GET /users?search=Admin # GET /users?username=root get do + if !current_user && public_access_restricted? + render_api_error!("Not authorized.", 403) + end + if params[:username].present? @users = User.where(username: params[:username]) else @@ -36,10 +40,12 @@ module API get ":id" do @user = User.find(params[:id]) - if current_user.is_admin? + if current_user.present? && current_user.is_admin? present @user, with: Entities::UserFull - else + elsif can?(current_user, :read_user, @user) present @user, with: Entities::User + else + render_api_error!("User not found.", 404) end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 948935bc10..8045c8b940 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -30,8 +30,6 @@ describe UsersController do end describe 'when logged out' do - before { stub_application_setting(restricted_visibility_levels: []) } - it 'renders the show template' do get :show, username: user.username diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 679227bf88..40b24c125b 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -20,6 +20,24 @@ describe API::API, api: true do end context "when authenticated" do + #These specs are written just in case API authentication is not required anymore + context "when public level is restricted" do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + allow_any_instance_of(API::Helpers).to receive(:authenticate!).and_return(true) + end + + it "renders 403" do + get api("/users") + expect(response.status).to eq(403) + end + + it "renders 404" do + get api("/users/#{user.id}") + expect(response.status).to eq(404) + end + end + it "should return an array of users" do get api("/users", user) expect(response.status).to eq(200) From 09c8cf9de68c5d6f1250d6717b00f3b7e2008d3f Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 7 Apr 2016 16:36:26 -0300 Subject: [PATCH 171/213] Remove group members check --- app/controllers/groups/group_members_controller.rb | 7 ------- app/models/ability.rb | 8 +++----- app/views/layouts/nav/_group.html.haml | 13 +++++-------- app/views/layouts/nav/_project.html.haml | 2 +- .../groups/group_members_controller_spec.rb | 8 +++++--- 5 files changed, 14 insertions(+), 24 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 9fc7263580..d5ef33888c 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,7 +1,6 @@ class Groups::GroupMembersController < Groups::ApplicationController # Authorize before_action :authorize_admin_group_member!, except: [:index, :leave] - before_action :authorize_read_group_members!, only: [:index] def index @project = @group.projects.find(params[:project_id]) if params[:project_id] @@ -80,10 +79,4 @@ class Groups::GroupMembersController < Groups::ApplicationController def member_params params.require(:group_member).permit(:access_level, :user_id) end - - private - - def authorize_read_group_members! - render_404 unless can?(current_user, :read_group_members, @group) - end end diff --git a/app/models/ability.rb b/app/models/ability.rb index e327d4eef2..a4bde72d99 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -57,6 +57,7 @@ class Ability :read_label, :read_milestone, :read_project_snippet, + :read_project_member, :read_merge_request, :read_note, :read_commit_status, @@ -91,10 +92,7 @@ class Ability subject.group end - if group.public? - rules << :read_group - rules << :read_group_members unless restricted_public_level? - end + rules << :read_group if group.public? rules end @@ -293,7 +291,7 @@ class Ability def group_abilities(user, group) rules = [] - rules << [:read_group, :read_group_members] if can_read_group?(user, group) + rules << :read_group if can_read_group?(user, group) # Only group masters and group owners can create new projects if group.has_master?(user) || group.has_owner?(user) || user.admin? diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 927f61c89f..55940741dc 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -36,14 +36,11 @@ Merge Requests - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute %span.count= number_with_delimiter(merge_requests.count) - - - if can?(current_user, :read_group_members, @group) - = nav_link(controller: [:group_members]) do - = link_to group_group_members_path(@group), title: 'Members' do - = icon('users fw') - %span - Members - + = nav_link(controller: [:group_members]) do + = link_to group_group_members_path(@group), title: 'Members' do + = icon('users fw') + %span + Members - if can?(current_user, :admin_group, @group) = nav_link(html_options: { class: "separate-item" }) do = link_to edit_group_path(@group), title: 'Settings' do diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 2c9e215948..86b46e8c75 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -77,7 +77,7 @@ Merge Requests %span.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) - - if project_nav_tab?(:settings) + - if project_nav_tab? :settings = nav_link(controller: [:project_members, :teams]) do = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do = icon('users fw') diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index cf8f2a2805..a598659871 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -4,15 +4,17 @@ describe Groups::GroupMembersController do let(:user) { create(:user) } let(:group) { create(:group) } - context "when public visibility level is restricted" do + context "index" do before do group.add_owner(user) stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) end - it 'does not show group members' do + it 'renders index with group members' do get :index, group_id: group.path - expect(response.status).to eq(404) + + expect(response.status).to eq(200) + expect(response).to render_template(:index) end end end From 7d54e721da0ccd21f0150bbb6ab60b51970033c2 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 7 Apr 2016 17:44:46 -0300 Subject: [PATCH 172/213] Insert instructions in admin page and permissions document --- app/views/admin/application_settings/_form.html.haml | 4 +++- doc/permissions/permissions.md | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 555aea554f..37b07c348d 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -26,7 +26,9 @@ .btn-group{ data: data_attrs } - restricted_level_checkboxes('restricted-visibility-help').each do |level| = level - %span.help-block#restricted-visibility-help Selected levels cannot be used by non-admin users for projects or snippets + %span.help-block#restricted-visibility-help + Selected levels cannot be used by non-admin users for projects or snippets. + If public level is restricted user profiles are not accessible to not logged users. .form-group = f.label :import_sources, class: 'control-label col-sm-2' .col-sm-10 diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md index 6219693b8a..f8cfd2898f 100644 --- a/doc/permissions/permissions.md +++ b/doc/permissions/permissions.md @@ -93,3 +93,10 @@ An administrator can flag a user as external [through the API](../api/users.md) or by checking the checkbox on the admin panel. As an administrator, navigate to **Admin > Users** to create a new user or edit an existing one. There, you will find the option to flag the user as external. + +## Restricted visibility levels + +Visibility levels can be restricted in admin settings page by administrator, when +restricting a visibility level groups, projects and snippets are not allowed to be +created with that visibility setting. If the public visibility level is restricted +user profiles are accessible to not logged users. From 820c08cefd78e593e94012061be29000d523ffd0 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Tue, 12 Apr 2016 12:04:33 -0300 Subject: [PATCH 173/213] Fix documentation and improve permissions code --- app/models/ability.rb | 1 + app/views/admin/application_settings/_form.html.haml | 2 +- doc/permissions/permissions.md | 7 ------- doc/public_access/public_access.md | 3 +++ lib/api/api_guard.rb | 4 ---- lib/api/users.rb | 2 +- 6 files changed, 6 insertions(+), 13 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index a4bde72d99..6103a2947e 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -120,6 +120,7 @@ class Ability def global_abilities(user) rules = [] rules << :create_group if user.can_create_group + rules << :read_users_list rules end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 37b07c348d..aadd2c54f2 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -28,7 +28,7 @@ = level %span.help-block#restricted-visibility-help Selected levels cannot be used by non-admin users for projects or snippets. - If public level is restricted user profiles are not accessible to not logged users. + If the public level is restricted, user profiles are only visible to logged in users. .form-group = f.label :import_sources, class: 'control-label col-sm-2' .col-sm-10 diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md index f8cfd2898f..6219693b8a 100644 --- a/doc/permissions/permissions.md +++ b/doc/permissions/permissions.md @@ -93,10 +93,3 @@ An administrator can flag a user as external [through the API](../api/users.md) or by checking the checkbox on the admin panel. As an administrator, navigate to **Admin > Users** to create a new user or edit an existing one. There, you will find the option to flag the user as external. - -## Restricted visibility levels - -Visibility levels can be restricted in admin settings page by administrator, when -restricting a visibility level groups, projects and snippets are not allowed to be -created with that visibility setting. If the public visibility level is restricted -user profiles are accessible to not logged users. diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md index 20aa90f0d6..17bb75ecec 100644 --- a/doc/public_access/public_access.md +++ b/doc/public_access/public_access.md @@ -58,6 +58,9 @@ you are logged in or not. When visiting the public page of a user, you can only see the projects which you are privileged to. +If the public level is restricted, user profiles are only visible to logged in users. + + ## Restricting the use of public or internal projects In the Admin area under **Settings** (`/admin/application_settings`), you can diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index 6ce5529abf..b9994fcefd 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -79,10 +79,6 @@ module APIGuard @current_user end - def public_access_restricted? - current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) - end - private def find_access_token @access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods) diff --git a/lib/api/users.rb b/lib/api/users.rb index 9647a40686..315268fc0c 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -11,7 +11,7 @@ module API # GET /users?search=Admin # GET /users?username=root get do - if !current_user && public_access_restricted? + unless can?(current_user, :read_users_list, nil) render_api_error!("Not authorized.", 403) end From 2366768d3b28ea70c91fc49c471e66152650d442 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 13 Apr 2016 15:37:17 -0300 Subject: [PATCH 174/213] Add changelog entry --- CHANGELOG | 1 + lib/api/users.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 571c31e63a..3cb89582d5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.7.0 (unreleased) - 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) + - Restrict user profiles when public visibility level is restricted. - 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/lib/api/users.rb b/lib/api/users.rb index 315268fc0c..ea6fa2dc8a 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -40,7 +40,7 @@ module API get ":id" do @user = User.find(params[:id]) - if current_user.present? && current_user.is_admin? + if current_user && current_user.is_admin? present @user, with: Entities::UserFull elsif can?(current_user, :read_user, @user) present @user, with: Entities::User From 0ce5cc99621c84dfc61f6105d177ced4d9a4ed85 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 18 Apr 2016 10:18:46 -0400 Subject: [PATCH 175/213] Resolve merge --- lib/ci/gitlab_ci_yaml_processor.rb | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 2a228845ec..d8a7fcfe15 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -120,17 +120,6 @@ module Ci true end - def validate_job!(name, job) - validate_job_name!(name) - validate_job_keys!(name, job) - validate_job_types!(name, job) - - validate_job_stage!(name, job) if job[:stage] - validate_job_cache!(name, job) if job[:cache] - validate_job_artifacts!(name, job) if job[:artifacts] - validate_job_dependencies!(name, job) if job[:dependencies] - end - private def validate_global! @@ -170,12 +159,9 @@ module Ci raise ValidationError, "cache:untracked parameter should be an boolean" end -<<<<<<< HEAD if @cache[:paths] && !validate_array_of_strings(@cache[:paths]) raise ValidationError, "cache:paths parameter should be an array of strings" end -======= - true end def validate_job!(name, job) @@ -188,7 +174,6 @@ module Ci validate_job_cache!(name, job) if job[:cache] validate_job_artifacts!(name, job) if job[:artifacts] validate_job_dependencies!(name, job) if job[:dependencies] ->>>>>>> origin/master end def validate_job_name!(name) From d13bba44f0de0aa5df00c37b4f743504d187bb03 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 15 Apr 2016 18:14:41 -0300 Subject: [PATCH 176/213] Use GitHub Issue/PR number as iid to keep references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With these changes we don’t lost the issue/pr references when importing them to GitLab. --- app/models/concerns/internal_id.rb | 10 ++++++---- lib/gitlab/github_import/issue_formatter.rb | 1 + lib/gitlab/github_import/pull_request_formatter.rb | 1 + spec/lib/gitlab/github_import/issue_formatter_spec.rb | 2 ++ .../github_import/pull_request_formatter_spec.rb | 3 +++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/internal_id.rb b/app/models/concerns/internal_id.rb index 51288094ef..5382dde676 100644 --- a/app/models/concerns/internal_id.rb +++ b/app/models/concerns/internal_id.rb @@ -7,11 +7,13 @@ module InternalId end def set_iid - records = project.send(self.class.name.tableize) - records = records.with_deleted if self.paranoid? - max_iid = records.maximum(:iid) + if iid.blank? + records = project.send(self.class.name.tableize) + records = records.with_deleted if self.paranoid? + max_iid = records.maximum(:iid) - self.iid = max_iid.to_i + 1 + self.iid = max_iid.to_i + 1 + end end def to_param diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb index 1e3ba44f27..acb332cb0c 100644 --- a/lib/gitlab/github_import/issue_formatter.rb +++ b/lib/gitlab/github_import/issue_formatter.rb @@ -3,6 +3,7 @@ module Gitlab class IssueFormatter < BaseFormatter def attributes { + iid: number, project: project, title: raw_data.title, description: description, diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb index 4e507b090e..5ee8a14624 100644 --- a/lib/gitlab/github_import/pull_request_formatter.rb +++ b/lib/gitlab/github_import/pull_request_formatter.rb @@ -3,6 +3,7 @@ module Gitlab class PullRequestFormatter < BaseFormatter def attributes { + iid: number, title: raw_data.title, description: description, source_project: source_project, diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb index fd05428b32..4f3d7f4405 100644 --- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -30,6 +30,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do it 'returns formatted attributes' do expected = { + iid: 1347, project: project, title: 'Found a bug', description: "*Created by: octocat*\n\nI'm having a problem with this.", @@ -50,6 +51,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do it 'returns formatted attributes' do expected = { + iid: 1347, project: project, title: 'Found a bug', description: "*Created by: octocat*\n\nI'm having a problem with this.", diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb index e49dcb4234..11249e57ca 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -35,6 +35,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do it 'returns formatted attributes' do expected = { + iid: 1347, title: 'New feature', description: "*Created by: octocat*\n\nPlease pull these awesome changes", source_project: project, @@ -58,6 +59,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do it 'returns formatted attributes' do expected = { + iid: 1347, title: 'New feature', description: "*Created by: octocat*\n\nPlease pull these awesome changes", source_project: project, @@ -81,6 +83,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do it 'returns formatted attributes' do expected = { + iid: 1347, title: 'New feature', description: "*Created by: octocat*\n\nPlease pull these awesome changes", source_project: project, From c9f2fc3c1802aabf913df3094aebf2d020b19f2a Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 15 Apr 2016 18:16:11 -0300 Subject: [PATCH 177/213] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index e641adbfce..330b500dee 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -82,6 +82,7 @@ v 8.7.0 (unreleased) - Author and participants are displayed first on users autocompletion - Show number sign on external issue reference text (Florent Baldino) - Updated print style for issues + - Use GitHub Issue/PR number as iid to keep references v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 From 17a730178def129d49e92d7a9a1b309efc67c755 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 18 Apr 2016 11:40:24 -0400 Subject: [PATCH 178/213] Remove additional misuse of `data-original-title` attribute --- app/helpers/projects_helper.rb | 2 +- app/views/shared/milestones/_issuable.html.haml | 2 +- spec/features/issues/update_issues_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 7e00aaccea..4470aa16e3 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -52,7 +52,7 @@ module ProjectsHelper link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe else title = opts[:title].sub(":name", sanitize(author.name)) - link_to(author_html, user_path(author), class: "author_link has-tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe + link_to(author_html, user_path(author), class: "author_link has-tooltip", title: title, data: { container: 'body' } ).html_safe end end diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index e1127b2311..47b66d44e4 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -23,5 +23,5 @@ - if assignee = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), - class: 'has-tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do + class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index 3eb903a93f..b03dd0f666 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -48,7 +48,7 @@ feature 'Multiple issue updating from issues#index', feature: true do click_update_issues_button page.within('.issue .controls') do - expect(find('.author_link')["data-original-title"]).to have_content(user.name) + expect(find('.author_link')["title"]).to have_content(user.name) end end From b9698d2b4512cca4687579aafba6a4403cbc6067 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 18 Apr 2016 17:42:48 +0200 Subject: [PATCH 179/213] Add "action" tag to /internal/allowed API This allows us to re-use any other analytics that rely on the "action" tag having a value set. --- CHANGELOG | 1 + lib/api/internal.rb | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 350be45627..9a6f1bbfef 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) + - Transactions for /internal/allowed now have an "action" tag set - Method instrumentation now uses Module#prepend instead of aliasing methods - Repository.clean_old_archives is now instrumented - Add support for environment variables on a job level in CI configuration file diff --git a/lib/api/internal.rb b/lib/api/internal.rb index 2200208b94..8cfa1f1556 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -23,9 +23,11 @@ module API end post "/allowed" do + Gitlab::Metrics.tag_transaction('action', 'Grape#/internal/allowed') + status 200 - actor = + actor = if params[:key_id] Key.find_by(id: params[:key_id]) elsif params[:user_id] @@ -33,7 +35,7 @@ module API end project_path = params[:project] - + # Check for *.wiki repositories. # Strip out the .wiki from the pathname before finding the # project. This applies the correct project permissions to From ea7114541838652fe3e6dcf3bdc4c3812f013e7b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 18 Apr 2016 16:45:49 +0100 Subject: [PATCH 180/213] Fixed issue with multi select not working with arrow keys --- app/assets/javascripts/gl_dropdown.js.coffee | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 2dc37257e2..fac928ef20 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -32,10 +32,8 @@ class GitLabDropdownFilter else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS $inputContainer.removeClass HAS_VALUE_CLASS - if keyCode is 13 and @input.val() isnt "" - if @options.enterCallback - @options.enterCallback() - return + if keyCode is 13 + return false clearTimeout timeout timeout = setTimeout => @@ -132,7 +130,6 @@ class GitLabDropdown @filterInput = @getElement(FILTER_INPUT) @highlight = false @filterInputBlur = true - @enterCallback = true } = @options self = @ @@ -178,9 +175,6 @@ class GitLabDropdown callback: (data) => currentIndex = -1 @parseData data - enterCallback: => - if @enterCallback - @selectRowAtIndex 0 # Event listeners From 872d31d6e9566f55f9eadd12d496d07e86c13f0a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 18 Apr 2016 16:56:48 +0100 Subject: [PATCH 181/213] Fixed issue with spinner not hiding on diff changes --- app/assets/javascripts/merge_request_tabs.js.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 1ab6e5114b..e8613cab72 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -183,9 +183,10 @@ class @MergeRequestTabs else $diffLine = $('td', $diffLine) - $diffLine.addClass 'hll' - diffLineTop = $diffLine.offset().top - navBarHeight = $('.navbar-gitlab').outerHeight() + if $diffLine.length + $diffLine.addClass 'hll' + diffLineTop = $diffLine.offset().top + navBarHeight = $('.navbar-gitlab').outerHeight() loadBuilds: (source) -> return if @buildsLoaded From fe603d4d314ddb584d160913ae52bac813abbaad Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 18 Apr 2016 17:53:38 +0100 Subject: [PATCH 182/213] Removed aside toggle on profile pages Closes #13943 --- app/views/users/show.html.haml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 0c4b6a5618..3028491e5b 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -6,8 +6,6 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity") -= render 'shared/show_aside' - .user-profile .cover-block .cover-controls From 2665af68a5a151bc967797d5eb1e3a1c1041f336 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 18 Apr 2016 12:09:37 -0400 Subject: [PATCH 183/213] Trigger builds From 7fa3a1c05c9ab04d59c9000e69881cd1cbeeaf1d Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 18 Apr 2016 20:01:32 +0200 Subject: [PATCH 184/213] Ensure that annoation is presented properly --- spec/requests/api/tags_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 9f9c3b1cf4..edcb2bedbf 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -32,9 +32,11 @@ describe API::API, api: true do it "should return an array of project tags with release info" do get api("/projects/#{project.id}/repository/tags", user) + expect(response.status).to eq(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(tag_name) + expect(json_response.first['message']).to eq('Version 1.1.0') expect(json_response.first['release']['description']).to eq(description) end end From beb32f31fba16e84c6bcfb59d538379db2d2a842 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Mon, 18 Apr 2016 13:04:36 -0500 Subject: [PATCH 185/213] Add null check to formData.append --- app/assets/javascripts/profile.js.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee index f4a2562885..26a1242352 100644 --- a/app/assets/javascripts/profile.js.coffee +++ b/app/assets/javascripts/profile.js.coffee @@ -45,9 +45,10 @@ class @Profile saveForm: -> self = @ - formData = new FormData(@form[0]) - formData.append('user[avatar]', @avatarGlCrop.getBlob(), 'avatar.png') + + avatarBlob = @avatarGlCrop.getBlob() + formData.append('user[avatar]', avatarBlob, 'avatar.png') if avatarBlob? $.ajax url: @form.attr('action') From f30053ba608ccc7a9edc8ca720ba09ab15a8f1f5 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 18 Apr 2016 07:42:26 -0700 Subject: [PATCH 186/213] Use raw SQL commands for 20140502125220 MigrateRepoSize Partial fix for #15210 --- .../20140502125220_migrate_repo_size.rb | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/db/migrate/20140502125220_migrate_repo_size.rb b/db/migrate/20140502125220_migrate_repo_size.rb index eed6d36681..efdf53112f 100644 --- a/db/migrate/20140502125220_migrate_repo_size.rb +++ b/db/migrate/20140502125220_migrate_repo_size.rb @@ -1,19 +1,26 @@ class MigrateRepoSize < ActiveRecord::Migration def up - Project.reset_column_information - Project.find_each(batch_size: 500) do |project| + project_data = execute('SELECT projects.id, namespaces.path AS namespace_path, projects.path AS project_path FROM projects LEFT JOIN namespaces ON projects.namespace_id = namespaces.id') + + project_data.each do |project| + id = project['id'] + namespace_path = project['namespace_path'] || '' + path = File.join(Gitlab.config.gitlab_shell.repos_path, namespace_path, project['project_path'] + '.git') + begin - if project.empty_repo? + repo = Gitlab::Git::Repository.new(path) + if repo.empty? print '-' else - project.update_repository_size + size = repo.size print '.' + execute("UPDATE projects SET repository_size = #{size} WHERE id = #{id}") end - rescue - print 'F' + rescue => e + puts "\nFailed to update project #{id}: #{e}" end end - puts 'Done' + puts "\nDone" end def down From 1a8fd9d7a5dfd43cc7e3f6b159f8852f722ba4e4 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 18 Apr 2016 15:15:57 -0400 Subject: [PATCH 187/213] Fix CHANGELOG --- CHANGELOG | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index d691113e19..0b3d1003b5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,7 +18,6 @@ v 8.7.0 (unreleased) - 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 - - Improved Markdown rendering performance !3389 (Yorick Peterse) - Make shared runners text in box configurable - 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) From 3f66f4470a7399a5079de04b8b2506edc159b90a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 18 Apr 2016 15:19:24 -0400 Subject: [PATCH 188/213] Make rubocop happy --- lib/ci/gitlab_ci_yaml_processor.rb | 27 +++++++++++--------- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 16 ++++++------ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 88d6ce01b0..ff9887cba1 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -160,6 +160,7 @@ module Ci validate_job_name!(name) validate_job_keys!(name, job) validate_job_types!(name, job) + validate_job_script!(name, job) validate_job_stage!(name, job) if job[:stage] validate_job_variables!(name, job) if job[:variables] @@ -183,18 +184,6 @@ module Ci end def validate_job_types!(name, job) - if !validate_string(job[:script]) && !validate_array_of_strings(job[:script]) - raise ValidationError, "#{name} job: script should be a string or an array of a strings" - end - - if job[:before_script] && !validate_array_of_strings(job[:before_script]) - raise ValidationError, "#{name} job: before_script should be an array of strings" - end - - if job[:after_script] && !validate_array_of_strings(job[:after_script]) - raise ValidationError, "#{name} job: after_script should be an array of strings" - end - if job[:image] && !validate_string(job[:image]) raise ValidationError, "#{name} job: image should be a string" end @@ -224,6 +213,20 @@ module Ci end end + def validate_job_script!(name, job) + if !validate_string(job[:script]) && !validate_array_of_strings(job[:script]) + raise ValidationError, "#{name} job: script should be a string or an array of a strings" + end + + if job[:before_script] && !validate_array_of_strings(job[:before_script]) + raise ValidationError, "#{name} job: before_script should be an array of strings" + end + + if job[:after_script] && !validate_array_of_strings(job[:after_script]) + raise ValidationError, "#{name} job: after_script should be an array of strings" + end + end + def validate_job_stage!(name, job) unless job[:stage].is_a?(String) && job[:stage].in?(stages) raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}" diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index a20b9ead55..643acf0303 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -295,12 +295,12 @@ module Ci describe "before_script" do context "in global context" do - let(:config) { + let(:config) do { before_script: ["global script"], test: { script: ["script"] } } - } + end it "return commands with scripts concencaced" do expect(subject[:commands]).to eq("global script\nscript") @@ -308,12 +308,12 @@ module Ci end context "overwritten in local context" do - let(:config) { + let(:config) do { before_script: ["global script"], test: { before_script: ["local script"], script: ["script"] } } - } + end it "return commands with scripts concencaced" do expect(subject[:commands]).to eq("local script\nscript") @@ -322,11 +322,11 @@ module Ci end describe "script" do - let(:config) { + let(:config) do { test: { script: ["script"] } } - } + end it "return commands with scripts concencaced" do expect(subject[:commands]).to eq("script") @@ -348,12 +348,12 @@ module Ci end context "overwritten in local context" do - let(:config) { + let(:config) do { after_script: ["local after_script"], test: { after_script: ["local after_script"], script: ["script"] } } - } + end it "return after_script in options" do expect(subject[:options][:after_script]).to eq(["local after_script"]) From 5f39729c19fa22836686dab32fbcf7c34f32fd41 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Sun, 17 Apr 2016 12:32:31 -0300 Subject: [PATCH 189/213] Update octokit gem to version 4.3.0 --- Gemfile | 2 +- Gemfile.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 199ef65d92..8caa749a8d 100644 --- a/Gemfile +++ b/Gemfile @@ -315,7 +315,7 @@ end gem "newrelic_rpm", '~> 3.14' -gem 'octokit', '~> 3.8.0' +gem 'octokit', '~> 4.3.0' gem "mail_room", "~> 0.6.1" diff --git a/Gemfile.lock b/Gemfile.lock index ad7d7c1855..958824f7ed 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -485,8 +485,8 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (~> 1.2) - octokit (3.8.0) - sawyer (~> 0.6.0, >= 0.5.3) + octokit (4.3.0) + sawyer (~> 0.7.0, >= 0.5.3) omniauth (1.3.1) hashie (>= 1.2, < 4) rack (>= 1.0, < 3) @@ -712,8 +712,8 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - sawyer (0.6.0) - addressable (~> 2.3.5) + sawyer (0.7.0) + addressable (>= 2.3.5, < 2.5) faraday (~> 0.8, < 0.10) scss_lint (0.47.1) rake (>= 0.9, < 11) @@ -968,7 +968,7 @@ DEPENDENCIES newrelic_rpm (~> 3.14) nokogiri (~> 1.6.7, >= 1.6.7.2) oauth2 (~> 1.0.0) - octokit (~> 3.8.0) + octokit (~> 4.3.0) omniauth (~> 1.3.1) omniauth-auth0 (~> 1.4.1) omniauth-azure-oauth2 (~> 0.0.6) From 05a4f444c368593216ab335db93ca98ce2218e94 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Sun, 17 Apr 2016 23:52:34 -0300 Subject: [PATCH 190/213] Import labels from GitHub --- lib/gitlab/github_import/importer.rb | 12 +++++++++- lib/gitlab/github_import/label_formatter.rb | 23 +++++++++++++++++++ .../github_import/label_formatter_spec.rb | 19 +++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/github_import/label_formatter.rb create mode 100644 spec/lib/gitlab/github_import/label_formatter_spec.rb diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 0b1ed51022..e406a0283b 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -16,7 +16,7 @@ module Gitlab end def execute - import_issues && import_pull_requests && import_wiki + import_labels && import_issues && import_pull_requests && import_wiki end private @@ -25,6 +25,16 @@ module Gitlab @import_data_credentials ||= project.import_data.credentials if project.import_data end + def import_labels + client.labels(project.import_source).each do |raw_data| + Label.create!(LabelFormatter.new(project, raw_data).attributes) + end + + true + rescue ActiveRecord::RecordInvalid => e + raise Projects::ImportService::Error, e.message + end + def import_issues client.list_issues(project.import_source, state: :all, sort: :created, diff --git a/lib/gitlab/github_import/label_formatter.rb b/lib/gitlab/github_import/label_formatter.rb new file mode 100644 index 0000000000..c2b9d40b51 --- /dev/null +++ b/lib/gitlab/github_import/label_formatter.rb @@ -0,0 +1,23 @@ +module Gitlab + module GithubImport + class LabelFormatter < BaseFormatter + def attributes + { + project: project, + title: title, + color: color + } + end + + private + + def color + "##{raw_data.color}" + end + + def title + raw_data.name + end + end + end +end diff --git a/spec/lib/gitlab/github_import/label_formatter_spec.rb b/spec/lib/gitlab/github_import/label_formatter_spec.rb new file mode 100644 index 0000000000..e94440a7fb --- /dev/null +++ b/spec/lib/gitlab/github_import/label_formatter_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::LabelFormatter, lib: true do + + describe '#attributes' do + it 'returns formatted attributes' do + project = create(:project) + raw = double(name: 'improvements', color: 'e6e6e6') + + formatter = described_class.new(project, raw) + + expect(formatter.attributes).to eq({ + project: project, + title: 'improvements', + color: '#e6e6e6' + }) + end + end +end From 7462a8f90a719e4850a28d9c6d2f223ce1e29b69 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 18 Apr 2016 01:02:22 -0300 Subject: [PATCH 191/213] Apply GitHub labels to Issue/Merge Request that were imported --- lib/gitlab/github_import/importer.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index e406a0283b..7f70ab63f3 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -43,6 +43,7 @@ module Gitlab if gh_issue.valid? issue = Issue.create!(gh_issue.attributes) + apply_labels(gh_issue.number, issue) if gh_issue.has_comments? import_comments(gh_issue.number, issue) @@ -65,6 +66,7 @@ module Gitlab merge_request = MergeRequest.new(pull_request.attributes) if merge_request.save + apply_labels(pull_request.number, merge_request) import_comments(pull_request.number, merge_request) import_comments_on_diff(pull_request.number, merge_request) end @@ -76,6 +78,18 @@ module Gitlab raise Projects::ImportService::Error, e.message end + def apply_labels(number, issuable) + issue = client.issue(project.import_source, number) + + if issue.labels.count > 0 + label_ids = issue.labels.map do |raw| + Label.find_by(LabelFormatter.new(project, raw).attributes).try(:id) + end + + issuable.update_attribute(:label_ids, label_ids) + end + end + def import_comments(issue_number, noteable) comments = client.issue_comments(project.import_source, issue_number) create_comments(comments, noteable) From 696b1e965732e229f9b0f6f49297e78265379bb4 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 18 Apr 2016 01:02:50 -0300 Subject: [PATCH 192/213] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 2882a81afc..290c956814 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -85,6 +85,7 @@ v 8.7.0 (unreleased) - Show number sign on external issue reference text (Florent Baldino) - Updated print style for issues - Use GitHub Issue/PR number as iid to keep references + - Import GitHub labels v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 From 06ec511164b37cadbfdf069037745c108486162c Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 18 Apr 2016 13:44:27 -0300 Subject: [PATCH 193/213] Import milestones from GitHub --- lib/gitlab/github_import/importer.rb | 13 ++- .../github_import/milestone_formatter.rb | 48 +++++++++++ .../github_import/milestone_formatter_spec.rb | 82 +++++++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/github_import/milestone_formatter.rb create mode 100644 spec/lib/gitlab/github_import/milestone_formatter_spec.rb diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 7f70ab63f3..0f9e3ee14e 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -16,7 +16,8 @@ module Gitlab end def execute - import_labels && import_issues && import_pull_requests && import_wiki + import_labels && import_milestones && import_issues && + import_pull_requests && import_wiki end private @@ -35,6 +36,16 @@ module Gitlab raise Projects::ImportService::Error, e.message end + def import_milestones + client.list_milestones(project.import_source, state: :all).each do |raw_data| + Milestone.create!(MilestoneFormatter.new(project, raw_data).attributes) + end + + true + rescue ActiveRecord::RecordInvalid => e + raise Projects::ImportService::Error, e.message + end + def import_issues client.list_issues(project.import_source, state: :all, sort: :created, diff --git a/lib/gitlab/github_import/milestone_formatter.rb b/lib/gitlab/github_import/milestone_formatter.rb new file mode 100644 index 0000000000..e91a7e328c --- /dev/null +++ b/lib/gitlab/github_import/milestone_formatter.rb @@ -0,0 +1,48 @@ +module Gitlab + module GithubImport + class MilestoneFormatter < BaseFormatter + def attributes + { + iid: number, + project: project, + title: title, + description: description, + due_date: due_date, + state: state, + created_at: created_at, + updated_at: updated_at + } + end + + private + + def number + raw_data.number + end + + def title + raw_data.title + end + + def description + raw_data.description + end + + def due_date + raw_data.due_on + end + + def state + raw_data.state == 'closed' ? 'closed' : 'active' + end + + def created_at + raw_data.created_at + end + + def updated_at + state == 'closed' ? raw_data.closed_at : raw_data.updated_at + end + end + end +end diff --git a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb new file mode 100644 index 0000000000..5a421e5058 --- /dev/null +++ b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::MilestoneFormatter, lib: true do + let(:project) { create(:empty_project) } + let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } + let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } + let(:base_data) do + { + number: 1347, + state: 'open', + title: '1.0', + description: 'Version 1.0', + due_on: nil, + created_at: created_at, + updated_at: updated_at, + closed_at: nil + } + end + + subject(:formatter) { described_class.new(project, raw_data)} + + describe '#attributes' do + context 'when milestone is open' do + let(:raw_data) { double(base_data.merge(state: 'open')) } + + it 'returns formatted attributes' do + expected = { + iid: 1347, + project: project, + title: '1.0', + description: 'Version 1.0', + state: 'active', + due_date: nil, + created_at: created_at, + updated_at: updated_at + } + + expect(formatter.attributes).to eq(expected) + end + end + + context 'when milestone is closed' do + let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } + let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) } + + it 'returns formatted attributes' do + expected = { + iid: 1347, + project: project, + title: '1.0', + description: 'Version 1.0', + state: 'closed', + due_date: nil, + created_at: created_at, + updated_at: closed_at + } + + expect(formatter.attributes).to eq(expected) + end + end + + context 'when milestone has a due date' do + let(:due_date) { DateTime.strptime('2011-01-28T19:01:12Z') } + let(:raw_data) { double(base_data.merge(due_on: due_date)) } + + it 'returns formatted attributes' do + expected = { + iid: 1347, + project: project, + title: '1.0', + description: 'Version 1.0', + state: 'active', + due_date: due_date, + created_at: created_at, + updated_at: updated_at + } + + expect(formatter.attributes).to eq(expected) + end + end + end +end From f2fe4af19da9d477e9b0df6ec530d4bcc1ca2a5f Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 18 Apr 2016 14:08:35 -0300 Subject: [PATCH 194/213] Set GitHub milestones to Issue/Merge Request that were imported --- lib/gitlab/github_import/issue_formatter.rb | 7 +++++++ .../github_import/pull_request_formatter.rb | 7 +++++++ .../github_import/issue_formatter_spec.rb | 17 +++++++++++++++++ .../pull_request_formatter_spec.rb | 18 ++++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb index acb332cb0c..c8173913b4 100644 --- a/lib/gitlab/github_import/issue_formatter.rb +++ b/lib/gitlab/github_import/issue_formatter.rb @@ -5,6 +5,7 @@ module Gitlab { iid: number, project: project, + milestone: milestone, title: raw_data.title, description: description, state: state, @@ -55,6 +56,12 @@ module Gitlab @formatter.author_line(author) + body end + def milestone + if raw_data.milestone.present? + project.milestones.find_by(iid: raw_data.milestone.number) + end + end + def state raw_data.state == 'closed' ? 'closed' : 'opened' end diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb index 5ee8a14624..d21b942ad4 100644 --- a/lib/gitlab/github_import/pull_request_formatter.rb +++ b/lib/gitlab/github_import/pull_request_formatter.rb @@ -11,6 +11,7 @@ module Gitlab target_project: target_project, target_branch: target_branch.name, state: state, + milestone: milestone, author_id: author_id, assignee_id: assignee_id, created_at: raw_data.created_at, @@ -58,6 +59,12 @@ module Gitlab formatter.author_line(author) + body end + def milestone + if raw_data.milestone.present? + project.milestones.find_by(iid: raw_data.milestone.number) + end + end + def source_project project end diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb index 4f3d7f4405..46899b529b 100644 --- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -32,6 +32,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do expected = { iid: 1347, project: project, + milestone: nil, title: 'Found a bug', description: "*Created by: octocat*\n\nI'm having a problem with this.", state: 'opened', @@ -53,6 +54,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do expected = { iid: 1347, project: project, + milestone: nil, title: 'Found a bug', description: "*Created by: octocat*\n\nI'm having a problem with this.", state: 'closed', @@ -80,6 +82,21 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end end + context 'when it has a milestone' do + let(:milestone) { OpenStruct.new(number: 45) } + let(:raw_data) { OpenStruct.new(base_data.merge(milestone: milestone)) } + + it 'returns nil when milestone does not exist' do + expect(issue.attributes.fetch(:milestone)).to be_nil + end + + it 'returns milestone when it exists' do + milestone = create(:milestone, project: project, iid: 45) + + expect(issue.attributes.fetch(:milestone)).to eq milestone + end + end + context 'when author is a GitLab user' do let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb index 11249e57ca..590085718c 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -43,6 +43,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do target_project: project, target_branch: 'master', state: 'opened', + milestone: nil, author_id: project.creator_id, assignee_id: nil, created_at: created_at, @@ -67,6 +68,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do target_project: project, target_branch: 'master', state: 'closed', + milestone: nil, author_id: project.creator_id, assignee_id: nil, created_at: created_at, @@ -91,6 +93,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do target_project: project, target_branch: 'master', state: 'merged', + milestone: nil, author_id: project.creator_id, assignee_id: nil, created_at: created_at, @@ -128,6 +131,21 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id end end + + context 'when it has a milestone' do + let(:milestone) { OpenStruct.new(number: 45) } + let(:raw_data) { OpenStruct.new(base_data.merge(milestone: milestone)) } + + it 'returns nil when milestone does not exists' do + expect(pull_request.attributes.fetch(:milestone)).to be_nil + end + + it 'returns milestone when is exists' do + milestone = create(:milestone, project: project, iid: 45) + + expect(pull_request.attributes.fetch(:milestone)).to eq milestone + end + end end describe '#number' do From 3225f78fdbe80bf2d6624508b2d75bcd0a285351 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 18 Apr 2016 14:08:58 -0300 Subject: [PATCH 195/213] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 290c956814..1a4b367e47 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -86,6 +86,7 @@ v 8.7.0 (unreleased) - Updated print style for issues - Use GitHub Issue/PR number as iid to keep references - Import GitHub labels + - Import GitHub milestones v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 From 0ed07587359aba66543dc0c620affe1086e80b60 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 18 Apr 2016 17:28:14 -0300 Subject: [PATCH 196/213] Use double instead of OpenStruct in the Issue/PR formatters specs --- .../github_import/issue_formatter_spec.rb | 25 +++++------ .../pull_request_formatter_spec.rb | 41 ++++++++++--------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb index 46899b529b..0e7ffbe9b8 100644 --- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -2,13 +2,14 @@ require 'spec_helper' describe Gitlab::GithubImport::IssueFormatter, lib: true do let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) } - let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:octocat) { double(id: 123456, login: 'octocat') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:base_data) do { number: 1347, + milestone: nil, state: 'open', title: 'Found a bug', body: "I'm having a problem with this.", @@ -26,7 +27,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do describe '#attributes' do context 'when issue is open' do - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } + let(:raw_data) { double(base_data.merge(state: 'open')) } it 'returns formatted attributes' do expected = { @@ -48,7 +49,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do context 'when issue is closed' do let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } + let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) } it 'returns formatted attributes' do expected = { @@ -69,7 +70,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when it is assigned to someone' do - let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } + let(:raw_data) { double(base_data.merge(assignee: octocat)) } it 'returns nil as assignee_id when is not a GitLab user' do expect(issue.attributes.fetch(:assignee_id)).to be_nil @@ -83,8 +84,8 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when it has a milestone' do - let(:milestone) { OpenStruct.new(number: 45) } - let(:raw_data) { OpenStruct.new(base_data.merge(milestone: milestone)) } + let(:milestone) { double(number: 45) } + let(:raw_data) { double(base_data.merge(milestone: milestone)) } it 'returns nil when milestone does not exist' do expect(issue.attributes.fetch(:milestone)).to be_nil @@ -98,7 +99,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when author is a GitLab user' do - let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + let(:raw_data) { double(base_data.merge(user: octocat)) } it 'returns project#creator_id as author_id when is not a GitLab user' do expect(issue.attributes.fetch(:author_id)).to eq project.creator_id @@ -114,7 +115,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do describe '#has_comments?' do context 'when number of comments is greater than zero' do - let(:raw_data) { OpenStruct.new(base_data.merge(comments: 1)) } + let(:raw_data) { double(base_data.merge(comments: 1)) } it 'returns true' do expect(issue.has_comments?).to eq true @@ -122,7 +123,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when number of comments is equal to zero' do - let(:raw_data) { OpenStruct.new(base_data.merge(comments: 0)) } + let(:raw_data) { double(base_data.merge(comments: 0)) } it 'returns false' do expect(issue.has_comments?).to eq false @@ -131,7 +132,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end describe '#number' do - let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) } + let(:raw_data) { double(base_data.merge(number: 1347)) } it 'returns pull request number' do expect(issue.number).to eq 1347 @@ -140,7 +141,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do describe '#valid?' do context 'when mention a pull request' do - let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: OpenStruct.new)) } + let(:raw_data) { double(base_data.merge(pull_request: double)) } it 'returns false' do expect(issue.valid?).to eq false @@ -148,7 +149,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when does not mention a pull request' do - let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: nil)) } + let(:raw_data) { double(base_data.merge(pull_request: nil)) } it 'returns true' do expect(issue.valid?).to eq true diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb index 590085718c..e59c0ca110 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -2,17 +2,18 @@ require 'spec_helper' describe Gitlab::GithubImport::PullRequestFormatter, lib: true do let(:project) { create(:project) } - let(:repository) { OpenStruct.new(id: 1, fork: false) } + let(:repository) { double(id: 1, fork: false) } let(:source_repo) { repository } - let(:source_branch) { OpenStruct.new(ref: 'feature', repo: source_repo) } + let(:source_branch) { double(ref: 'feature', repo: source_repo) } let(:target_repo) { repository } - let(:target_branch) { OpenStruct.new(ref: 'master', repo: target_repo) } - let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:target_branch) { double(ref: 'master', repo: target_repo) } + let(:octocat) { double(id: 123456, login: 'octocat') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:base_data) do { number: 1347, + milestone: nil, state: 'open', title: 'New feature', body: 'Please pull these awesome changes', @@ -31,7 +32,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do describe '#attributes' do context 'when pull request is open' do - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } + let(:raw_data) { double(base_data.merge(state: 'open')) } it 'returns formatted attributes' do expected = { @@ -56,7 +57,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do context 'when pull request is closed' do let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } + let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) } it 'returns formatted attributes' do expected = { @@ -81,7 +82,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do context 'when pull request is merged' do let(:merged_at) { DateTime.strptime('2011-01-28T13:01:12Z') } - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', merged_at: merged_at)) } + let(:raw_data) { double(base_data.merge(state: 'closed', merged_at: merged_at)) } it 'returns formatted attributes' do expected = { @@ -105,7 +106,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when it is assigned to someone' do - let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } + let(:raw_data) { double(base_data.merge(assignee: octocat)) } it 'returns nil as assignee_id when is not a GitLab user' do expect(pull_request.attributes.fetch(:assignee_id)).to be_nil @@ -119,7 +120,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when author is a GitLab user' do - let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + let(:raw_data) { double(base_data.merge(user: octocat)) } it 'returns project#creator_id as author_id when is not a GitLab user' do expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id @@ -133,8 +134,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when it has a milestone' do - let(:milestone) { OpenStruct.new(number: 45) } - let(:raw_data) { OpenStruct.new(base_data.merge(milestone: milestone)) } + let(:milestone) { double(number: 45) } + let(:raw_data) { double(base_data.merge(milestone: milestone)) } it 'returns nil when milestone does not exists' do expect(pull_request.attributes.fetch(:milestone)).to be_nil @@ -149,7 +150,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end describe '#number' do - let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) } + let(:raw_data) { double(base_data.merge(number: 1347)) } it 'returns pull request number' do expect(pull_request.number).to eq 1347 @@ -157,11 +158,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end describe '#valid?' do - let(:invalid_branch) { OpenStruct.new(ref: 'invalid-branch') } + let(:invalid_branch) { double(ref: 'invalid-branch').as_null_object } context 'when source, and target repositories are the same' do context 'and source and target branches exists' do - let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: target_branch)) } + let(:raw_data) { double(base_data.merge(head: source_branch, base: target_branch)) } it 'returns true' do expect(pull_request.valid?).to eq true @@ -169,7 +170,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'and source branch doesn not exists' do - let(:raw_data) { OpenStruct.new(base_data.merge(head: invalid_branch, base: target_branch)) } + let(:raw_data) { double(base_data.merge(head: invalid_branch, base: target_branch)) } it 'returns false' do expect(pull_request.valid?).to eq false @@ -177,7 +178,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'and target branch doesn not exists' do - let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: invalid_branch)) } + let(:raw_data) { double(base_data.merge(head: source_branch, base: invalid_branch)) } it 'returns false' do expect(pull_request.valid?).to eq false @@ -186,8 +187,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when source repo is a fork' do - let(:source_repo) { OpenStruct.new(id: 2, fork: true) } - let(:raw_data) { OpenStruct.new(base_data) } + let(:source_repo) { double(id: 2, fork: true) } + let(:raw_data) { double(base_data) } it 'returns false' do expect(pull_request.valid?).to eq false @@ -195,8 +196,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when target repo is a fork' do - let(:target_repo) { OpenStruct.new(id: 2, fork: true) } - let(:raw_data) { OpenStruct.new(base_data) } + let(:target_repo) { double(id: 2, fork: true) } + let(:raw_data) { double(base_data) } it 'returns false' do expect(pull_request.valid?).to eq false From f801e2243dcce6d3bdce01acf62fbf5a49a301da Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 19 Apr 2016 09:22:55 +0530 Subject: [PATCH 197/213] A new branch created for a confidential issue is named `-confidential-issue`. --- app/models/issue.rb | 2 +- spec/models/issue_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/issue.rb b/app/models/issue.rb index 530d210759..2f77386960 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -158,7 +158,7 @@ class Issue < ActiveRecord::Base def to_branch_name if self.confidential? - "issue-#{iid}" + "#{iid}-confidential-issue" else "#{iid}-#{title.parameterize}" end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index da1c673653..060e659910 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -248,7 +248,7 @@ describe Issue, models: true do it "does not contain the issue title if confidential" do issue = create(:issue, title: 'testing-issue', confidential: true) - expect(issue.to_branch_name).to match /\Aissue/ + expect(issue.to_branch_name).to match /confidential-issue\z/ end end end From c634d37086dde8ad2855e20051ba5bd551848122 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 19 Apr 2016 10:36:14 +0200 Subject: [PATCH 198/213] Bump version to 8.8.0-pre Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 2 ++ VERSION | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 1a4b367e47..a3b7a238f5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. +v 8.8.0 (unreleased) + v 8.7.0 (unreleased) - Transactions for /internal/allowed now have an "action" tag set - Method instrumentation now uses Module#prepend instead of aliasing methods diff --git a/VERSION b/VERSION index 91ab1f99da..d5a967c393 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.7.0-pre +8.8.0-pre From ee1090e2b2bc7b3762f6e2775f3fd15e92ae212b Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Fri, 15 Apr 2016 08:08:22 -0300 Subject: [PATCH 199/213] Added System Hooks for push and tag_push Code is based on Project Webhooks, removing deprecations and without commits listing. --- app/models/hooks/system_hook.rb | 6 ++ app/models/project.rb | 21 +++-- app/services/git_push_service.rb | 6 ++ app/services/git_tag_push_service.rb | 6 ++ app/services/system_hooks_service.rb | 12 +-- doc/system_hooks/system_hooks.md | 77 +++++++++++++++++++ doc/web_hooks/web_hooks.md | 4 +- lib/gitlab/push_data_builder.rb | 22 +++++- spec/lib/gitlab/push_data_builder_spec.rb | 31 +++++++- .../project_hook_data_shared_example.rb | 17 +++- 10 files changed, 181 insertions(+), 21 deletions(-) diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index c147d8762a..bacd42ec60 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -19,4 +19,10 @@ # class SystemHook < WebHook + scope :push_hooks, -> { where(push_events: true) } + scope :tag_push_hooks, -> { where(tag_push_events: true) } + + def async_execute(data, hook_name) + Sidekiq::Client.enqueue(SystemHookWorker, id, data, hook_name) + end end diff --git a/app/models/project.rb b/app/models/project.rb index 8f20922e3c..e7dac23a12 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -831,8 +831,8 @@ class Project < ActiveRecord::Base end end - def hook_attrs - { + def hook_attrs(backward: true) + attrs = { name: name, description: description, web_url: web_url, @@ -843,12 +843,19 @@ class Project < ActiveRecord::Base visibility_level: visibility_level, path_with_namespace: path_with_namespace, default_branch: default_branch, - # Backward compatibility - homepage: web_url, - url: url_to_repo, - ssh_url: ssh_url_to_repo, - http_url: http_url_to_repo } + + # Backward compatibility + if backward + attrs.merge!({ + homepage: web_url, + url: url_to_repo, + ssh_url: ssh_url_to_repo, + http_url: http_url_to_repo + }) + end + + attrs end # Reset events cache related to this project diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index dc74c02760..25d8e2cf05 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -73,6 +73,7 @@ class GitPushService < BaseService @project.update_merge_requests(params[:oldrev], params[:newrev], params[:ref], current_user) EventCreateService.new.push(@project, current_user, build_push_data) + SystemHooksService.new.execute_hooks(build_push_data_system_hook.dup, :push_hooks) @project.execute_hooks(build_push_data.dup, :push_hooks) @project.execute_services(build_push_data.dup, :push_hooks) CreateCommitBuildsService.new.execute(@project, current_user, build_push_data) @@ -138,6 +139,11 @@ class GitPushService < BaseService build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], push_commits) end + def build_push_data_system_hook + @push_data_system ||= Gitlab::PushDataBuilder. + build_system(@project, current_user, params[:oldrev], params[:newrev], params[:ref]) + end + def push_to_existing_branch? # Return if this is not a push to a branch (e.g. new commits) Gitlab::Git.branch_ref?(params[:ref]) && !Gitlab::Git.blank_ref?(params[:oldrev]) diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index c88c767280..81ba68fa67 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -8,6 +8,7 @@ class GitTagPushService @push_data = build_push_data(oldrev, newrev, ref) EventCreateService.new.push(project, user, @push_data) + SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks) project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks) CreateCommitBuildsService.new.execute(project, @user, @push_data) @@ -35,4 +36,9 @@ class GitTagPushService Gitlab::PushDataBuilder. build(project, user, oldrev, newrev, ref, commits, message) end + + def build_system_push_data(oldrev, newrev, ref) + Gitlab::PushDataBuilder. + build_system(project, user, oldrev, newrev, ref) + end end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index f0615ec742..e43b5b51e5 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -3,17 +3,13 @@ class SystemHooksService execute_hooks(build_event_data(model, event)) end - private - - def execute_hooks(data) - SystemHook.all.each do |sh| - async_execute_hook(sh, data, 'system_hooks') + def execute_hooks(data, hooks_scope = :all) + SystemHook.send(hooks_scope).each do |hook| + hook.async_execute(data, 'system_hooks') end end - def async_execute_hook(hook, data, hook_name) - Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data, hook_name) - end + private def build_event_data(model, event) data = { diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index 612376e3a4..4c5a9e366c 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -240,3 +240,80 @@ X-Gitlab-Event: System Hook "user_id": 41 } ``` + +## Push events + +Triggered when you push to the repository except when pushing tags. + +**Request header**: + +``` +X-Gitlab-Event: System Hook +``` + +**Request body:** + +```json +{ + "event_name": "push", + "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", + "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "ref": "refs/heads/master", + "checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "user_id": 4, + "user_name": "John Smith", + "user_email": "john@example.com", + "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", + "project_id": 15, + "project":{ + "name":"Diaspora", + "description":"", + "web_url":"http://example.com/mike/diaspora", + "avatar_url":null, + "git_ssh_url":"git@example.com:mike/diaspora.git", + "git_http_url":"http://example.com/mike/diaspora.git", + "namespace":"Mike", + "visibility_level":0, + "path_with_namespace":"mike/diaspora", + "default_branch":"master", + } +} +``` + +## Tag events + +Triggered when you create (or delete) tags to the repository. + +**Request header**: + +``` +X-Gitlab-Event: System Hook +``` + +**Request body:** + +```json +{ + "event_name": "tag_push", + "before": "0000000000000000000000000000000000000000", + "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", + "ref": "refs/tags/v1.0.0", + "checkout_sha": "5937ac0a7beb003549fc5fd26fc247adbce4a52e", + "user_id": 1, + "user_name": "John Smith", + "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", + "project_id": 1, + "project":{ + "name":"Example", + "description":"", + "web_url":"http://example.com/jsmith/example", + "avatar_url":null, + "git_ssh_url":"git@example.com:jsmith/example.git", + "git_http_url":"http://example.com/jsmith/example.git", + "namespace":"Jsmith", + "visibility_level":0, + "path_with_namespace":"jsmith/example", + "default_branch":"master", + } +} +``` diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index 22e207b6d3..c1c51302e7 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -41,6 +41,7 @@ X-Gitlab-Event: Push Hook "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "ref": "refs/heads/master", + "checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "user_id": 4, "user_name": "John Smith", "user_email": "john@example.com", @@ -118,9 +119,10 @@ X-Gitlab-Event: Tag Push Hook ```json { "object_kind": "tag_push", - "ref": "refs/tags/v1.0.0", "before": "0000000000000000000000000000000000000000", "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", + "ref": "refs/tags/v1.0.0", + "checkout_sha": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", "user_id": 1, "user_name": "John Smith", "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index 97d1edab9c..eb8e45040b 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -36,7 +36,7 @@ module Gitlab commit.hook_attrs(with_changed_files: true) end - type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push" + type = Gitlab::Git.tag_ref?(ref) ? 'tag_push' : 'push' # Hash to be passed as post_receive_data data = { @@ -62,6 +62,26 @@ module Gitlab data end + def build_system(project, user, oldrev, newrev, ref) + type = Gitlab::Git.tag_ref?(ref) ? 'tag_push' : 'push' + + data = { + event_name: type, + before: oldrev, + after: newrev, + ref: ref, + checkout_sha: checkout_sha(project.repository, newrev, ref), + user_id: user.id, + user_name: user.name, + user_email: user.email, + user_avatar: user.avatar_url, + project_id: project.id, + project: project.hook_attrs(backward: false) + } + + data + end + # This method provide a sample data generated with # existing project and commits to test webhooks def build_sample(project, user) diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb index 961022b9d1..d6fb65e694 100644 --- a/spec/lib/gitlab/push_data_builder_spec.rb +++ b/spec/lib/gitlab/push_data_builder_spec.rb @@ -14,11 +14,11 @@ describe Gitlab::PushDataBuilder, lib: true do it { expect(data[:ref]).to eq('refs/heads/master') } it { expect(data[:commits].size).to eq(3) } it { expect(data[:total_commits_count]).to eq(3) } - it { expect(data[:commits].first[:added]).to eq(["gitlab-grack"]) } - it { expect(data[:commits].first[:modified]).to eq([".gitmodules"]) } + it { expect(data[:commits].first[:added]).to eq(['gitlab-grack']) } + it { expect(data[:commits].first[:modified]).to eq(['.gitmodules']) } it { expect(data[:commits].first[:removed]).to eq([]) } - include_examples 'project hook data' + include_examples 'project hook data with deprecateds' include_examples 'deprecated repository hook data' end @@ -37,9 +37,34 @@ describe Gitlab::PushDataBuilder, lib: true do it { expect(data[:commits]).to be_empty } it { expect(data[:total_commits_count]).to be_zero } + include_examples 'project hook data with deprecateds' + include_examples 'deprecated repository hook data' + it 'does not raise an error when given nil commits' do expect { described_class.build(spy, spy, spy, spy, spy, nil) }. not_to raise_error end end + + describe '.build_system' do + let(:data) do + described_class.build_system(project, user, Gitlab::Git::BLANK_SHA, + '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b', + 'refs/tags/v1.1.0') + end + + it { expect(data).to be_a(Hash) } + it { expect(data[:before]).to eq(Gitlab::Git::BLANK_SHA) } + it { expect(data[:checkout_sha]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } + it { expect(data[:after]).to eq('8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b') } + it { expect(data[:ref]).to eq('refs/tags/v1.1.0') } + it { expect(data[:user_id]).to eq(user.id) } + it { expect(data[:user_name]).to eq(user.name) } + it { expect(data[:user_email]).to eq(user.email) } + it { expect(data[:user_avatar]).to eq(user.avatar_url) } + it { expect(data[:project_id]).to eq(project.id) } + it { expect(data[:project]).to be_a(Hash) } + + include_examples 'project hook data' + end end diff --git a/spec/support/project_hook_data_shared_example.rb b/spec/support/project_hook_data_shared_example.rb index 422083875d..7dbaa6a645 100644 --- a/spec/support/project_hook_data_shared_example.rb +++ b/spec/support/project_hook_data_shared_example.rb @@ -1,4 +1,4 @@ -RSpec.shared_examples 'project hook data' do |project_key: :project| +RSpec.shared_examples 'project hook data with deprecateds' do |project_key: :project| it 'contains project data' do expect(data[project_key][:name]).to eq(project.name) expect(data[project_key][:description]).to eq(project.description) @@ -17,6 +17,21 @@ RSpec.shared_examples 'project hook data' do |project_key: :project| end end +RSpec.shared_examples 'project hook data' do |project_key: :project| + it 'contains project data' do + expect(data[project_key][:name]).to eq(project.name) + expect(data[project_key][:description]).to eq(project.description) + expect(data[project_key][:web_url]).to eq(project.web_url) + expect(data[project_key][:avatar_url]).to eq(project.avatar_url) + expect(data[project_key][:git_http_url]).to eq(project.http_url_to_repo) + expect(data[project_key][:git_ssh_url]).to eq(project.ssh_url_to_repo) + expect(data[project_key][:namespace]).to eq(project.namespace.name) + expect(data[project_key][:visibility_level]).to eq(project.visibility_level) + expect(data[project_key][:path_with_namespace]).to eq(project.path_with_namespace) + expect(data[project_key][:default_branch]).to eq(project.default_branch) + end +end + RSpec.shared_examples 'deprecated repository hook data' do |project_key: :project| it 'contains deprecated repository data' do expect(data[:repository][:name]).to eq(project.name) From 2384bed4d8e3f79461664911715e5f2a2a66c008 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Fri, 15 Apr 2016 09:26:27 -0300 Subject: [PATCH 200/213] Refactor GitTagPushService and fig tags_push system event hook --- app/services/git_tag_push_service.rb | 27 +++++++++++----------- app/workers/post_receive.rb | 4 ++-- spec/services/git_tag_push_service_spec.rb | 24 +++++++++---------- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index 81ba68fa67..f4a1d0da5d 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -1,17 +1,16 @@ -class GitTagPushService - attr_accessor :project, :user, :push_data +class GitTagPushService < BaseService + attr_accessor :push_data - def execute(project, user, oldrev, newrev, ref) + def execute project.repository.before_push_tag - @project, @user = project, user - @push_data = build_push_data(oldrev, newrev, ref) + @push_data = build_push_data - EventCreateService.new.push(project, user, @push_data) + EventCreateService.new.push(project, current_user, @push_data) SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks) project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks) - CreateCommitBuildsService.new.execute(project, @user, @push_data) + CreateCommitBuildsService.new.execute(project, current_user, @push_data) ProjectCacheWorker.perform_async(project.id) true @@ -19,14 +18,14 @@ class GitTagPushService private - def build_push_data(oldrev, newrev, ref) + def build_push_data commits = [] message = nil - if !Gitlab::Git.blank_ref?(newrev) - tag_name = Gitlab::Git.ref_name(ref) + if !Gitlab::Git.blank_ref?(params[:newrev]) + tag_name = Gitlab::Git.ref_name(params[:ref]) tag = project.repository.find_tag(tag_name) - if tag && tag.target == newrev + if tag && tag.target == params[:newrev] commit = project.commit(tag.target) commits = [commit].compact message = tag.message @@ -34,11 +33,11 @@ class GitTagPushService end Gitlab::PushDataBuilder. - build(project, user, oldrev, newrev, ref, commits, message) + build(project, current_user, params[:oldrev], params[:newrev], params[:ref], commits, message) end - def build_system_push_data(oldrev, newrev, ref) + def build_system_push_data Gitlab::PushDataBuilder. - build_system(project, user, oldrev, newrev, ref) + build_system(project, current_user, params[:oldrev], params[:newrev], params[:ref]) end end diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index 9e1215b21a..f3327ca9e6 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -39,7 +39,7 @@ class PostReceive end if Gitlab::Git.tag_ref?(ref) - GitTagPushService.new.execute(post_received.project, @user, oldrev, newrev, ref) + GitTagPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute elsif Gitlab::Git.branch_ref?(ref) GitPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute end @@ -47,7 +47,7 @@ class PostReceive end private - + def log(message) Gitlab::GitLogger.error("POST-RECEIVE: #{message}") end diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index cc780587e7..a63656e626 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -5,19 +5,17 @@ describe GitTagPushService, services: true do let(:user) { create :user } let(:project) { create :project } - let(:service) { GitTagPushService.new } + let(:service) { GitTagPushService.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) } - before do - @oldrev = Gitlab::Git::BLANK_SHA - @newrev = "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" # gitlab-test: git rev-parse refs/tags/v1.1.0 - @ref = 'refs/tags/v1.1.0' - end + let(:oldrev) { Gitlab::Git::BLANK_SHA } + let(:newrev) { "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" } # gitlab-test: git rev-parse refs/tags/v1.1.0 + let(:ref) { 'refs/tags/v1.1.0' } describe "Git Tag Push Data" do before do - service.execute(project, user, @oldrev, @newrev, @ref) + service.execute @push_data = service.push_data - @tag_name = Gitlab::Git.ref_name(@ref) + @tag_name = Gitlab::Git.ref_name(ref) @tag = project.repository.find_tag(@tag_name) @commit = project.commit(@tag.target) end @@ -25,9 +23,9 @@ describe GitTagPushService, services: true do subject { @push_data } it { is_expected.to include(object_kind: 'tag_push') } - it { is_expected.to include(ref: @ref) } - it { is_expected.to include(before: @oldrev) } - it { is_expected.to include(after: @newrev) } + it { is_expected.to include(ref: ref) } + it { is_expected.to include(before: oldrev) } + it { is_expected.to include(after: newrev) } it { is_expected.to include(message: @tag.message) } it { is_expected.to include(user_id: user.id) } it { is_expected.to include(user_name: user.name) } @@ -80,9 +78,11 @@ describe GitTagPushService, services: true do describe "Webhooks" do context "execute webhooks" do + let(:service) { GitTagPushService.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') } + it "when pushing tags" do expect(project).to receive(:execute_hooks) - service.execute(project, user, 'oldrev', 'newrev', 'refs/tags/v1.0.0') + service.execute end end end From 5330af3fa69d4c47437ce27480c7f3b74652b2ca Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Mon, 18 Apr 2016 21:52:43 -0300 Subject: [PATCH 201/213] Using single builder for push and tag events --- app/services/git_push_service.rb | 2 +- app/services/git_tag_push_service.rb | 2 +- doc/system_hooks/system_hooks.md | 40 +++++++++++++++++++++-- lib/gitlab/push_data_builder.rb | 21 +----------- spec/lib/gitlab/push_data_builder_spec.rb | 28 ++++------------ 5 files changed, 47 insertions(+), 46 deletions(-) diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 25d8e2cf05..1e1be8cd04 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -141,7 +141,7 @@ class GitPushService < BaseService def build_push_data_system_hook @push_data_system ||= Gitlab::PushDataBuilder. - build_system(@project, current_user, params[:oldrev], params[:newrev], params[:ref]) + build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], []) end def push_to_existing_branch? diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index f4a1d0da5d..64271d8bc5 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -38,6 +38,6 @@ class GitTagPushService < BaseService def build_system_push_data Gitlab::PushDataBuilder. - build_system(project, current_user, params[:oldrev], params[:newrev], params[:ref]) + build(project, current_user, params[:oldrev], params[:newrev], params[:ref], [], '') end end diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index 4c5a9e366c..c44930a4ce 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -4,6 +4,12 @@ Your GitLab instance can perform HTTP POST requests on the following events: `pr System hooks can be used, e.g. for logging or changing information in a LDAP server. +> **Note:** +> +> We follow the same structure from Webhooks for Push and Tag events, but we never display commits. +> +> Same deprecations from Webhooks are valid here. + ## Hooks request example **Request header**: @@ -276,7 +282,22 @@ X-Gitlab-Event: System Hook "visibility_level":0, "path_with_namespace":"mike/diaspora", "default_branch":"master", - } + "homepage":"http://example.com/mike/diaspora", + "url":"git@example.com:mike/diaspora.git", + "ssh_url":"git@example.com:mike/diaspora.git", + "http_url":"http://example.com/mike/diaspora.git" + }, + "repository":{ + "name": "Diaspora", + "url": "git@example.com:mike/diaspora.git", + "description": "", + "homepage": "http://example.com/mike/diaspora", + "git_http_url":"http://example.com/mike/diaspora.git", + "git_ssh_url":"git@example.com:mike/diaspora.git", + "visibility_level":0 + }, + "commits": [], + "total_commits_count": 0 } ``` @@ -314,6 +335,21 @@ X-Gitlab-Event: System Hook "visibility_level":0, "path_with_namespace":"jsmith/example", "default_branch":"master", - } + "homepage":"http://example.com/jsmith/example", + "url":"git@example.com:jsmith/example.git", + "ssh_url":"git@example.com:jsmith/example.git", + "http_url":"http://example.com/jsmith/example.git" + }, + "repository":{ + "name": "Example", + "url": "ssh://git@example.com/jsmith/example.git", + "description": "", + "homepage": "http://example.com/jsmith/example", + "git_http_url":"http://example.com/jsmith/example.git", + "git_ssh_url":"git@example.com:jsmith/example.git", + "visibility_level":0 + }, + "commits": [], + "total_commits_count": 0 } ``` diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index eb8e45040b..67622f321a 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -41,6 +41,7 @@ module Gitlab # Hash to be passed as post_receive_data data = { object_kind: type, + event_name: type, before: oldrev, after: newrev, ref: ref, @@ -62,26 +63,6 @@ module Gitlab data end - def build_system(project, user, oldrev, newrev, ref) - type = Gitlab::Git.tag_ref?(ref) ? 'tag_push' : 'push' - - data = { - event_name: type, - before: oldrev, - after: newrev, - ref: ref, - checkout_sha: checkout_sha(project.repository, newrev, ref), - user_id: user.id, - user_name: user.name, - user_email: user.email, - user_avatar: user.avatar_url, - project_id: project.id, - project: project.hook_attrs(backward: false) - } - - data - end - # This method provide a sample data generated with # existing project and commits to test webhooks def build_sample(project, user) diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb index d6fb65e694..7fc34139ef 100644 --- a/spec/lib/gitlab/push_data_builder_spec.rb +++ b/spec/lib/gitlab/push_data_builder_spec.rb @@ -34,6 +34,12 @@ describe Gitlab::PushDataBuilder, lib: true do it { expect(data[:checkout_sha]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { expect(data[:after]).to eq('8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b') } it { expect(data[:ref]).to eq('refs/tags/v1.1.0') } + it { expect(data[:user_id]).to eq(user.id) } + it { expect(data[:user_name]).to eq(user.name) } + it { expect(data[:user_email]).to eq(user.email) } + it { expect(data[:user_avatar]).to eq(user.avatar_url) } + it { expect(data[:project_id]).to eq(project.id) } + it { expect(data[:project]).to be_a(Hash) } it { expect(data[:commits]).to be_empty } it { expect(data[:total_commits_count]).to be_zero } @@ -45,26 +51,4 @@ describe Gitlab::PushDataBuilder, lib: true do not_to raise_error end end - - describe '.build_system' do - let(:data) do - described_class.build_system(project, user, Gitlab::Git::BLANK_SHA, - '8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b', - 'refs/tags/v1.1.0') - end - - it { expect(data).to be_a(Hash) } - it { expect(data[:before]).to eq(Gitlab::Git::BLANK_SHA) } - it { expect(data[:checkout_sha]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } - it { expect(data[:after]).to eq('8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b') } - it { expect(data[:ref]).to eq('refs/tags/v1.1.0') } - it { expect(data[:user_id]).to eq(user.id) } - it { expect(data[:user_name]).to eq(user.name) } - it { expect(data[:user_email]).to eq(user.email) } - it { expect(data[:user_avatar]).to eq(user.avatar_url) } - it { expect(data[:project_id]).to eq(project.id) } - it { expect(data[:project]).to be_a(Hash) } - - include_examples 'project hook data' - end end From f6fa18668a8618d3677afd15ebbcda41d3329247 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Mon, 18 Apr 2016 21:53:41 -0300 Subject: [PATCH 202/213] moving overlapping scopes to webhook --- app/models/hooks/project_hook.rb | 2 -- app/models/hooks/system_hook.rb | 3 --- app/models/hooks/web_hook.rb | 3 +++ 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index fe923fafbe..7365e360de 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -21,8 +21,6 @@ class ProjectHook < WebHook belongs_to :project - scope :push_hooks, -> { where(push_events: true) } - scope :tag_push_hooks, -> { where(tag_push_events: true) } scope :issue_hooks, -> { where(issues_events: true) } scope :note_hooks, -> { where(note_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) } diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index bacd42ec60..15dddcc244 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -19,9 +19,6 @@ # class SystemHook < WebHook - scope :push_hooks, -> { where(push_events: true) } - scope :tag_push_hooks, -> { where(tag_push_events: true) } - def async_execute(data, hook_name) Sidekiq::Client.enqueue(SystemHookWorker, id, data, hook_name) end diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 7a13c3f0a3..3a2e4f546f 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -30,6 +30,9 @@ class WebHook < ActiveRecord::Base default_value_for :build_events, false default_value_for :enable_ssl_verification, true + scope :push_hooks, -> { where(push_events: true) } + scope :tag_push_hooks, -> { where(tag_push_events: true) } + # HTTParty timeout default_timeout Gitlab.config.gitlab.webhook_timeout From 8ead3d0d183338eeef9390eda0d2da07770b9274 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 19 Apr 2016 10:41:01 +0200 Subject: [PATCH 203/213] Add CHANGELOG item Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 1a4b367e47..3f23b18320 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -87,6 +87,8 @@ v 8.7.0 (unreleased) - Use GitHub Issue/PR number as iid to keep references - Import GitHub labels - Import GitHub milestones + - Fix emoji catgories in the emoji picker + - Execute system web hooks on push to the project v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 From 8614ff758492ecd90cf0dafce98b858901c0ea4a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 19 Apr 2016 11:08:02 +0200 Subject: [PATCH 204/213] Allow enable/disable push events for system hooks Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/views/admin/hooks/index.html.haml | 36 ++++++++++++++++++++---- app/views/projects/hooks/index.html.haml | 11 ++++---- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3f23b18320..3ced5aa3f8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -89,6 +89,7 @@ v 8.7.0 (unreleased) - Import GitHub milestones - Fix emoji catgories in the emoji picker - Execute system web hooks on push to the project + - Allow enable/disable push events for system hooks v 8.6.6 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index ad952052f2..67d23c8023 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -16,6 +16,27 @@ = f.label :url, "URL:", class: 'control-label' .col-sm-10 = f.text_field :url, class: "form-control" + .form-group + = f.label :url, "Trigger", class: 'control-label' + .col-sm-10.prepend-top-10 + %div + System hook will be triggered on set of events like creating project + or adding ssh key. But you can also enable extra triggers like Push events. + + %div.prepend-top-default + = f.check_box :push_events, class: 'pull-left' + .prepend-left-20 + = f.label :push_events, class: 'list-label' do + %strong Push events + %p.light + This url will be triggered by a push to the repository + %div + = f.check_box :tag_push_events, class: 'pull-left' + .prepend-left-20 + = f.label :tag_push_events, class: 'list-label' do + %strong Tag push events + %p.light + This url will be triggered when a new tag is pushed to the repository .form-group = f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox' .col-sm-10 @@ -31,13 +52,16 @@ .panel.panel-default .panel-heading System hooks (#{@hooks.count}) - %ul.well-list + %ul.content-list - @hooks.each do |hook| %li - .list-item-name - %strong= hook.url - %p SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} - - .pull-right + .controls = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-sm" = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm" + .monospace= hook.url + %div + - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger| + - if hook.send(trigger) + %span.label.label-gray= trigger.titleize + %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} + diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index e39224d86c..aae3abcad4 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -74,16 +74,15 @@ .panel.panel-default .panel-heading Webhooks (#{@hooks.count}) - %ul.well-list + %ul.content-list - @hooks.each do |hook| %li - .pull-right + .controls = link_to 'Test Hook', test_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped" = link_to 'Remove', namespace_project_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" - .clearfix - %span.monospace= hook.url - %p + .monospace= hook.url + %div - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger| - if hook.send(trigger) %span.label.label-gray= trigger.titleize - SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} + %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} From d1b2e7b440e03e9ad5cac72fe767b16bf11d05cc Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 19 Apr 2016 10:26:07 +0100 Subject: [PATCH 205/213] Trigger filtering after ajax is complete in dropdown Closes #15186 --- app/assets/javascripts/gl_dropdown.js.coffee | 3 +++ app/views/shared/issuable/_sidebar.html.haml | 1 + 2 files changed, 4 insertions(+) diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index fac928ef20..641141072f 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -154,6 +154,9 @@ class GitLabDropdown @fullData = data @parseData @fullData + + if @options.filterable + @filterInput.trigger 'keyup' } # Init filterable diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 08bfd93f4e..55d7a38cce 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -118,6 +118,7 @@ Manage labels - else View labels + = dropdown_loading = render "shared/issuable/participants", participants: issuable.participants(current_user) - if current_user From 45525edd5b8eea87a2e0ef48907b71439587361d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 19 Apr 2016 11:42:39 +0200 Subject: [PATCH 206/213] Add push events to permit params in system hooks Signed-off-by: Dmitriy Zaporozhets --- app/controllers/admin/hooks_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 0bd19c49d8..93c4894ea0 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -39,6 +39,6 @@ class Admin::HooksController < Admin::ApplicationController end def hook_params - params.require(:hook).permit(:url, :enable_ssl_verification) + params.require(:hook).permit(:url, :enable_ssl_verification, :push_events, :tag_push_events) end end From b3a5f636446376d73d7ec3206a333251ef93cf57 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 19 Apr 2016 11:57:10 +0200 Subject: [PATCH 207/213] Fix prepare build execution in docker environment --- scripts/prepare_build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index 4a7ee7dbb6..247383aa46 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -11,7 +11,7 @@ retry() { return 1 } -if [ -f /.dockerinit ]; then +if [ -f /.dockerenv ] || [ -f ./dockerinit ]; then mkdir -p vendor # Install phantomjs package From f2e3868124c1b0acef4eb57ffc42577b74fab334 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 18 Apr 2016 10:56:10 +0200 Subject: [PATCH 208/213] Check permissions when sharing project with group Closes #15330 --- .../projects/group_links_controller.rb | 14 ++++-- .../projects/group_links_controller_spec.rb | 50 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 spec/controllers/projects/group_links_controller_spec.rb diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb index 4159e53bfa..92113b9dd8 100644 --- a/app/controllers/projects/group_links_controller.rb +++ b/app/controllers/projects/group_links_controller.rb @@ -7,10 +7,16 @@ class Projects::GroupLinksController < Projects::ApplicationController end def create - link = project.project_group_links.new - link.group_id = params[:link_group_id] - link.group_access = params[:link_group_access] - link.save + group = Group.find(params[:link_group_id]) + + if can?(current_user, :read_group, group) + link = project.project_group_links.new + link.group_id = params[:link_group_id] + link.group_access = params[:link_group_access] + link.save + else + return render_404 + end redirect_to namespace_project_group_links_path(project.namespace, project) end diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb new file mode 100644 index 0000000000..40bd83af86 --- /dev/null +++ b/spec/controllers/projects/group_links_controller_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Projects::GroupLinksController do + let(:project) { create(:project, :private) } + let(:group) { create(:group, :private) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + sign_in(user) + end + + describe '#create' do + shared_context 'link project to group' do + before do + post(:create, namespace_id: project.namespace.to_param, + project_id: project.to_param, + link_group_id: group.id, + link_group_access: ProjectGroupLink.default_access) + end + end + + context 'when user has access to group he want to link project to' do + before { group.add_developer(user) } + include_context 'link project to group' + + it 'links project with selected group' do + expect(group.shared_projects).to include project + end + + it 'redirects to project group links page'do + expect(response).to redirect_to( + namespace_project_group_links_path(project.namespace, project) + ) + end + end + + context 'when user doers not have access to group he want to link to' do + include_context 'link project to group' + + it 'renders 404' do + expect(response.status).to eq 404 + end + + it 'does not share project with that group' do + expect(group.shared_projects).to_not include project + end + end + end +end From d177abb32b0b31ec27c51d0eb42e1ea131d64a03 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 18 Apr 2016 11:17:55 +0200 Subject: [PATCH 209/213] Refactor method that shares project with a group --- app/controllers/projects/group_links_controller.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb index 92113b9dd8..cd0f081cd7 100644 --- a/app/controllers/projects/group_links_controller.rb +++ b/app/controllers/projects/group_links_controller.rb @@ -10,10 +10,9 @@ class Projects::GroupLinksController < Projects::ApplicationController group = Group.find(params[:link_group_id]) if can?(current_user, :read_group, group) - link = project.project_group_links.new - link.group_id = params[:link_group_id] - link.group_access = params[:link_group_access] - link.save + project.project_group_links.create( + group: group, group_access: params[:link_group_access] + ) else return render_404 end From 66b6d82a3e2f3ea32cdd534f8bcbba8ed515ce2d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 18 Apr 2016 12:00:47 +0200 Subject: [PATCH 210/213] Use guard clause to check ability to share project --- app/controllers/projects/group_links_controller.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb index cd0f081cd7..606552fa85 100644 --- a/app/controllers/projects/group_links_controller.rb +++ b/app/controllers/projects/group_links_controller.rb @@ -8,14 +8,11 @@ class Projects::GroupLinksController < Projects::ApplicationController def create group = Group.find(params[:link_group_id]) + return render_404 unless can?(current_user, :read_group, group) - if can?(current_user, :read_group, group) - project.project_group_links.create( - group: group, group_access: params[:link_group_access] - ) - else - return render_404 - end + project.project_group_links.create( + group: group, group_access: params[:link_group_access] + ) redirect_to namespace_project_group_links_path(project.namespace, project) end From b9e13c2481a4cc8c25a94a095c795ce9a1d61f4d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 18 Apr 2016 14:39:32 +0200 Subject: [PATCH 211/213] Add Changelog entry for group link permissions fix --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 3f23b18320..1a6d80231b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -90,6 +90,9 @@ v 8.7.0 (unreleased) - Fix emoji catgories in the emoji picker - Execute system web hooks on push to the project +v 8.6.7 (unreleased) + - Fix vulnerability that made it possible to enumerate private projects belonging to group + 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 From d58d88fbd5dca6efdb95d5e07ad0b1c6f43bb565 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 19 Apr 2016 07:39:53 -0400 Subject: [PATCH 212/213] Use different markdown --- app/views/projects/runners/_shared_runners.html.haml | 2 +- spec/features/runners_spec.rb | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml index ec47ed79e5..b31fcfea76 100644 --- a/app/views/projects/runners/_shared_runners.html.haml +++ b/app/views/projects/runners/_shared_runners.html.haml @@ -2,7 +2,7 @@ .bs-callout.bs-callout-warning.shared-runners-description - if shared_runners_text.present? - = markdown(shared_runners_text) + = markdown(shared_runners_text, pipeline: 'plain_markdown') - else GitLab Runners do not offer secure isolation between projects that they do builds for. You are TRUSTING all GitLab users who can push code to project A, B or C to run shell scripts on the machine hosting runner X. %hr diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 789b06d7c6..8edeb8d18a 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -81,7 +81,8 @@ describe "Runners" do end describe "shared runners description" do - let(:shared_runners_text) { 'custom shared runners description' } + let(:shared_runners_text) { 'custom **shared** runners description' } + let(:shared_runners_html) { 'custom shared runners description' } before do stub_application_setting(shared_runners_text: shared_runners_text) @@ -91,7 +92,7 @@ describe "Runners" do end it "sees shared runners description" do - expect(page.find(".shared-runners-description")).to have_content(shared_runners_text) + expect(page.find(".shared-runners-description")).to have_content(shared_runners_html) end end From 229f3ce9cec8d525d960cabc7a915b47dfdcd910 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 19 Apr 2016 14:27:08 +0200 Subject: [PATCH 213/213] Configuring of points per UDP packet This allows users to configure the number of points stored in a single UDP packet. This in turn can be used to reduce the number of UDP packets being sent at the cost of these packets being somewhat larger. The default setting is 1 point per packet so nothing changes for existing users. --- CHANGELOG | 1 + .../admin/application_settings_controller.rb | 1 + .../application_settings/_form.html.haml | 11 +++++- .../20160419120017_add_metrics_packet_size.rb | 5 +++ db/schema.rb | 39 ++++++++++--------- lib/gitlab/metrics.rb | 7 ++-- 6 files changed, 40 insertions(+), 24 deletions(-) create mode 100644 db/migrate/20160419120017_add_metrics_packet_size.rb diff --git a/CHANGELOG b/CHANGELOG index c6fe19d568..e21bde1f02 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 number of InfluxDB points stored per UDP packet can now be configured - Transactions for /internal/allowed now have an "action" tag set - Method instrumentation now uses Module#prepend instead of aliasing methods - Repository.clean_old_archives is now instrumented diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 2520d4452a..ec22548dde 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -93,6 +93,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :akismet_api_key, :email_author_in_body, :repository_checks_enabled, + :metrics_packet_size, restricted_visibility_levels: [], import_sources: [] ) diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index e320b3d749..e0d8d16a95 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -218,6 +218,13 @@ .help-block The sampling interval in seconds. Sampled data includes memory usage, retained Ruby objects, file descriptors and so on. + .form-group + = f.label :metrics_packet_size, 'Metrics per packet', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :metrics_packet_size, class: 'form-control' + .help-block + The amount of points to store in a single UDP packet. More points + results in fewer but larger UDP packets being sent. %fieldset %legend Spam and Anti-bot Protection @@ -286,7 +293,7 @@ = f.check_box :repository_checks_enabled Enable Repository Checks .help-block - GitLab will periodically run + 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 @@ -294,7 +301,7 @@ = 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 you can choose to clear all repository check information from the database. - + .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/db/migrate/20160419120017_add_metrics_packet_size.rb b/db/migrate/20160419120017_add_metrics_packet_size.rb new file mode 100644 index 0000000000..78c163d62a --- /dev/null +++ b/db/migrate/20160419120017_add_metrics_packet_size.rb @@ -0,0 +1,5 @@ +class AddMetricsPacketSize < ActiveRecord::Migration + def change + add_column :application_settings, :metrics_packet_size, :integer, default: 1 + end +end diff --git a/db/schema.rb b/db/schema.rb index 0ab32cab8b..7cecbf16f6 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: 20160415133440) do +ActiveRecord::Schema.define(version: 20160419120017) 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: 20160415133440) do t.boolean "email_author_in_body", default: false t.integer "default_group_visibility" t.boolean "repository_checks_enabled", default: true + t.integer "metrics_packet_size", default: 1 t.text "shared_runners_text" end @@ -717,37 +718,37 @@ ActiveRecord::Schema.define(version: 20160415133440) do t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" - t.boolean "issues_enabled", default: true, null: false - t.boolean "wall_enabled", default: true, null: false - t.boolean "merge_requests_enabled", default: true, null: false - t.boolean "wiki_enabled", default: true, null: false + t.boolean "issues_enabled", default: true, null: false + t.boolean "wall_enabled", default: true, null: false + t.boolean "merge_requests_enabled", default: true, null: false + t.boolean "wiki_enabled", default: true, null: false t.integer "namespace_id" - t.string "issues_tracker", default: "gitlab", null: false + t.string "issues_tracker", default: "gitlab", null: false t.string "issues_tracker_id" - t.boolean "snippets_enabled", default: true, null: false + t.boolean "snippets_enabled", default: true, null: false t.datetime "last_activity_at" t.string "import_url" - t.integer "visibility_level", default: 0, null: false - t.boolean "archived", default: false, null: false + t.integer "visibility_level", default: 0, null: false + t.boolean "archived", default: false, null: false t.string "avatar" t.string "import_status" - t.float "repository_size", default: 0.0 - t.integer "star_count", default: 0, null: false + t.float "repository_size", default: 0.0 + t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.integer "commit_count", default: 0 + t.integer "commit_count", default: 0 t.text "import_error" t.integer "ci_id" - t.boolean "builds_enabled", default: true, null: false - t.boolean "shared_runners_enabled", default: true, null: false + t.boolean "builds_enabled", default: true, null: false + t.boolean "shared_runners_enabled", default: true, null: false t.string "runners_token" t.string "build_coverage_regex" - t.boolean "build_allow_git_fetch", default: true, null: false - t.integer "build_timeout", default: 3600, null: false - t.boolean "pending_delete", default: false - t.boolean "public_builds", default: true, null: false + t.boolean "build_allow_git_fetch", default: true, null: false + t.integer "build_timeout", default: 3600, null: false + 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 + t.integer "pushes_since_gc", default: 0 t.boolean "last_repository_check_failed" t.datetime "last_repository_check_at" end diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb index 484970c5a1..f82dce1486 100644 --- a/lib/gitlab/metrics.rb +++ b/lib/gitlab/metrics.rb @@ -14,7 +14,8 @@ module Gitlab method_call_threshold: current_application_settings[:metrics_method_call_threshold], host: current_application_settings[:metrics_host], port: current_application_settings[:metrics_port], - sample_interval: current_application_settings[:metrics_sample_interval] || 15 + sample_interval: current_application_settings[:metrics_sample_interval] || 15, + packet_size: current_application_settings[:metrics_packet_size] || 1 } end @@ -41,9 +42,9 @@ module Gitlab prepared = prepare_metrics(metrics) pool.with do |connection| - prepared.each do |metric| + prepared.each_slice(settings[:packet_size]) do |slice| begin - connection.write_points([metric]) + connection.write_points(slice) rescue StandardError end end