提交 6e81d7f6 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 3fc9a8e6
......@@ -136,7 +136,7 @@ gem 'faraday_middleware-aws-signers-v4'
# Markdown and HTML processing
gem 'html-pipeline', '~> 2.8'
gem 'deckar01-task_list', '2.2.0'
gem 'deckar01-task_list', '2.2.1'
gem 'gitlab-markup', '~> 1.7.0'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'commonmarker', '~> 0.17'
......
......@@ -194,7 +194,7 @@ GEM
database_cleaner (1.7.0)
debug_inspector (0.0.3)
debugger-ruby_core_source (1.3.8)
deckar01-task_list (2.2.0)
deckar01-task_list (2.2.1)
html-pipeline
declarative (0.0.10)
declarative-option (0.1.0)
......@@ -604,7 +604,7 @@ GEM
netrc (0.11.0)
nio4r (2.3.1)
no_proxy_fix (0.1.2)
nokogiri (1.10.4)
nokogiri (1.10.5)
mini_portile2 (~> 2.4.0)
nokogumbo (1.5.0)
nokogiri
......@@ -1128,7 +1128,7 @@ DEPENDENCIES
creole (~> 0.5.0)
danger (~> 6.0)
database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.2.0)
deckar01-task_list (= 2.2.1)
default_value_for (~> 3.3.0)
derailed_benchmarks
device_detector
......
......@@ -36,25 +36,26 @@ export const humanize = string =>
export const dasherize = str => str.replace(/[_\s]+/g, '-');
/**
* Replaces whitespaces with hyphens, convert to lower case and remove non-allowed special characters
* @param {String} str
* Replaces whitespace and non-sluggish characters with a given separator
* @param {String} str - The string to slugify
* @param {String=} separator - The separator used to separate words (defaults to "-")
* @returns {String}
*/
export const slugify = str => {
export const slugify = (str, separator = '-') => {
const slug = str
.trim()
.toLowerCase()
.replace(/[^a-zA-Z0-9_.-]+/g, '-');
.replace(/[^a-zA-Z0-9_.-]+/g, separator);
return slug === '-' ? '' : slug;
return slug === separator ? '' : slug;
};
/**
* Replaces whitespaces with underscore and converts to lower case
* Replaces whitespace and non-sluggish characters with underscores
* @param {String} str
* @returns {String}
*/
export const slugifyWithUnderscore = str => str.toLowerCase().replace(/\s+/g, '_');
export const slugifyWithUnderscore = str => slugify(str, '_');
/**
* Truncates given text
......
......@@ -55,10 +55,12 @@ export default {
};
},
},
mounted() {
this.verifyTimeRange();
watch: {
selectedTimeWindow() {
this.verifyTimeRange();
},
},
updated() {
mounted() {
this.verifyTimeRange();
},
methods: {
......
......@@ -72,16 +72,14 @@ export default class NewBranchForm {
});
return `${restriction.prefix} ${formatted.join(restriction.conjunction)}`;
};
const validator = (function(_this) {
return function(errors, restriction) {
const matched = _this.name.val().match(restriction.pattern);
if (matched) {
return errors.concat(formatter(matched.reduce(unique, []), restriction));
} else {
return errors;
}
};
})(this);
const validator = (errors, restriction) => {
const matched = this.name.val().match(restriction.pattern);
if (matched) {
return errors.concat(formatter(matched.reduce(unique, []), restriction));
} else {
return errors;
}
};
const errors = this.restrictions.reduce(validator, []);
if (errors.length > 0) {
const errorMessage = $('<span/>').text(errors.join(', '));
......
# frozen_string_literal: true
module RendersCommits
def limited_commits(commits)
if commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
def limited_commits(commits, commits_count)
if commits_count > MergeRequestDiff::COMMITS_SAFE_SIZE
[
commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE),
commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE
commits_count - MergeRequestDiff::COMMITS_SAFE_SIZE
]
else
[commits, 0]
......@@ -14,9 +14,10 @@ module RendersCommits
# This is used as a helper method in a controller.
# rubocop: disable Gitlab/ModuleWithInstanceVariables
def set_commits_for_rendering(commits)
@total_commit_count = commits.size
limited, @hidden_commit_count = limited_commits(commits)
def set_commits_for_rendering(commits, commits_count: nil)
@total_commit_count = commits_count || commits.size
limited, @hidden_commit_count = limited_commits(commits, @total_commit_count)
commits.each(&:lazy_author) # preload authors
prepare_commits_for_rendering(limited)
end
# rubocop: enable Gitlab/ModuleWithInstanceVariables
......
......@@ -109,7 +109,13 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
@target_project = @merge_request.target_project
@source_project = @merge_request.source_project
@commits = set_commits_for_rendering(@merge_request.commits)
@commits =
set_commits_for_rendering(
@merge_request.recent_commits.with_latest_pipeline(@merge_request.source_branch),
commits_count: @merge_request.commits_count
)
@commit = @merge_request.diff_head_commit
# FIXME: We have to assign a presenter to another instance variable
......
......@@ -90,7 +90,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
# Get commits from repository
# or from cache if already merged
@commits =
set_commits_for_rendering(@merge_request.commits.with_latest_pipeline)
set_commits_for_rendering(
@merge_request.recent_commits.with_latest_pipeline(@merge_request.source_branch),
commits_count: @merge_request.commits_count
)
render json: { html: view_to_html_string('projects/merge_requests/_commits') }
end
......@@ -252,7 +255,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def merge_params_attributes
[:should_remove_source_branch, :commit_message, :squash_commit_message, :squash, :auto_merge_strategy]
MergeRequest::KNOWN_MERGE_PARAMS
end
def auto_merge_requested?
......@@ -292,7 +295,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
return :sha_mismatch if params[:sha] != @merge_request.diff_head_sha
@merge_request.update(merge_error: nil, squash: merge_params.fetch(:squash, false))
@merge_request.update(merge_error: nil, squash: params.fetch(:squash, false))
if auto_merge_requested?
if merge_request.auto_merge_enabled?
......
......@@ -117,12 +117,6 @@ class TodosFinder
params[:group_id].present?
end
def project
strong_memoize(:project) do
Project.find_without_deleted(params[:project_id]) if project?
end
end
def group
strong_memoize(:group) do
Group.find(params[:group_id])
......@@ -181,7 +175,7 @@ class TodosFinder
def by_project(items)
if project?
items.for_project(project)
items.for_undeleted_projects.for_project(params[:project_id])
else
items
end
......
......@@ -374,11 +374,12 @@ class MergeRequest < ApplicationRecord
"#{project.to_reference(from, full: full)}#{reference}"
end
def commits
return merge_request_diff.commits if persisted?
def commits(limit: nil)
return merge_request_diff.commits(limit: limit) if persisted?
commits_arr = if compare_commits
compare_commits.reverse
reversed_commits = compare_commits.reverse
limit ? reversed_commits.take(limit) : reversed_commits
else
[]
end
......@@ -386,6 +387,10 @@ class MergeRequest < ApplicationRecord
CommitCollection.new(source_project, commits_arr, source_branch)
end
def recent_commits
commits(limit: MergeRequestDiff::COMMITS_SAFE_SIZE)
end
def commits_count
if persisted?
merge_request_diff.commits_count
......
......@@ -213,8 +213,10 @@ class MergeRequestDiff < ApplicationRecord
end
end
def commits
@commits ||= load_commits
def commits(limit: nil)
strong_memoize(:"commits_#{limit || 'all'}") do
load_commits(limit: limit)
end
end
def last_commit_sha
......@@ -529,8 +531,9 @@ class MergeRequestDiff < ApplicationRecord
end
end
def load_commits
commits = merge_request_diff_commits.map { |commit| Commit.from_hash(commit.to_hash, project) }
def load_commits(limit: nil)
commits = merge_request_diff_commits.limit(limit)
.map { |commit| Commit.from_hash(commit.to_hash, project) }
CommitCollection
.new(merge_request.source_project, commits, merge_request.source_branch)
......
......@@ -464,13 +464,6 @@ class Project < ApplicationRecord
# Used by Projects::CleanupService to hold a map of rewritten object IDs
mount_uploader :bfg_object_map, AttachmentUploader
# Returns a project, if it is not about to be removed.
#
# id - The ID of the project to retrieve.
def self.find_without_deleted(id)
without_deleted.find_by_id(id)
end
def self.eager_load_namespace_and_owner
includes(namespace: :owner)
end
......
......@@ -55,7 +55,8 @@ class Todo < ApplicationRecord
scope :done, -> { with_state(:done) }
scope :for_action, -> (action) { where(action: action) }
scope :for_author, -> (author) { where(author: author) }
scope :for_project, -> (project) { where(project: project) }
scope :for_project, -> (projects) { where(project: projects) }
scope :for_undeleted_projects, -> { joins(:project).merge(Project.without_deleted) }
scope :for_group, -> (group) { where(group: group) }
scope :for_type, -> (type) { where(target_type: type) }
scope :for_target, -> (id) { where(target_id: id) }
......
......@@ -19,10 +19,12 @@ module MergeRequests
end
def source
if merge_request.squash
squash_sha!
else
merge_request.diff_head_sha
strong_memoize(:source) do
if merge_request.squash
squash_sha!
else
merge_request.diff_head_sha
end
end
end
......@@ -58,16 +60,14 @@ module MergeRequests
end
def squash_sha!
strong_memoize(:squash_sha) do
params[:merge_request] = merge_request
squash_result = ::MergeRequests::SquashService.new(project, current_user, params).execute
case squash_result[:status]
when :success
squash_result[:squash_sha]
when :error
raise ::MergeRequests::MergeService::MergeError, squash_result[:message]
end
params[:merge_request] = merge_request
squash_result = ::MergeRequests::SquashService.new(project, current_user, params).execute
case squash_result[:status]
when :success
squash_result[:squash_sha]
when :error
raise ::MergeRequests::MergeService::MergeError, squash_result[:message]
end
end
end
......
......@@ -37,6 +37,7 @@ module MergeRequests
def validate!
authorization_check!
error_check!
updated_check!
end
def authorization_check!
......@@ -60,6 +61,15 @@ module MergeRequests
raise_error(error) if error
end
def updated_check!
return unless Feature.enabled?(:validate_merge_sha, merge_request.target_project, default_enabled: false)
unless source_matches?
raise_error('Branch has been updated since the merge was requested. '\
'Please review the changes.')
end
end
def commit
log_info("Git merge started on JID #{merge_jid}")
commit_id = try_merge
......@@ -125,5 +135,11 @@ module MergeRequests
def merge_request_info
merge_request.to_reference(full: true)
end
def source_matches?
# params-keys are symbols coming from the controller, but when they get
# loaded from the database they're strings
params.with_indifferent_access[:sha] == merge_request.diff_head_sha
end
end
end
......@@ -88,9 +88,9 @@ module MergeRequests
merge_request.update(merge_error: nil)
if merge_request.head_pipeline && merge_request.head_pipeline.active?
AutoMergeService.new(project, current_user).execute(merge_request, AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
AutoMergeService.new(project, current_user, { sha: last_diff_sha }).execute(merge_request, AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
else
merge_request.merge_async(current_user.id, {})
merge_request.merge_async(current_user.id, { sha: last_diff_sha })
end
end
......
---
title: Fix checking task item when previous tasks contain only spaces
merge_request: 19724
author:
type: fixed
---
title: Correctly cleanup orphan job artifacts
merge_request: 17679
author: Adam Mulvany
type: fixed
---
title: Remove IIFEs from new_branch_form.js
merge_request: 20009
author: minghuan lei
type: other
---
title: Remove update hook from date filter to prevent js from getting stuck
merge_request: 20215
author:
type: fixed
---
title: Remove duplication from slugifyWithUnderscore function
merge_request: 20016
author: Arun Kumar Mohan
type: other
# frozen_string_literal: true
class AddResolvedAttributesToVulnerabilities < ActiveRecord::Migration[5.2]
DOWNTIME = false
def up
add_column :vulnerabilities, :resolved_by_id, :bigint
add_column :vulnerabilities, :resolved_at, :datetime_with_timezone
end
def down
remove_column :vulnerabilities, :resolved_at
remove_column :vulnerabilities, :resolved_by_id
end
end
# frozen_string_literal: true
class AddForeignKeyOnResolvedByIdToVulnerabilities < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :vulnerabilities, :resolved_by_id
add_concurrent_foreign_key :vulnerabilities, :users, column: :resolved_by_id, on_delete: :nullify
end
def down
remove_foreign_key :vulnerabilities, column: :resolved_by_id
remove_concurrent_index :vulnerabilities, :resolved_by_id
end
end
# frozen_string_literal: true
class SetResolvedStateOnVulnerabilities < ActiveRecord::Migration[5.2]
DOWNTIME = false
def up
execute <<~SQL
-- selecting IDs for all non-orphan Findings that either have no feedback or it's a non-dismissal feedback
WITH resolved_vulnerability_ids AS (
SELECT DISTINCT vulnerability_id AS id
FROM vulnerability_occurrences
LEFT JOIN vulnerability_feedback ON vulnerability_feedback.project_fingerprint = ENCODE(vulnerability_occurrences.project_fingerprint::bytea, 'HEX')
WHERE vulnerability_id IS NOT NULL
AND (vulnerability_feedback.id IS NULL OR vulnerability_feedback.feedback_type <> 0)
)
UPDATE vulnerabilities
SET state = 3, resolved_by_id = closed_by_id, resolved_at = NOW()
FROM resolved_vulnerability_ids
WHERE vulnerabilities.id IN (resolved_vulnerability_ids.id)
AND state = 2 -- only 'closed' Vulnerabilities become 'resolved'
SQL
end
def down
execute <<~SQL
UPDATE vulnerabilities
SET state = 2, resolved_by_id = NULL, resolved_at = NULL -- state = 'closed'
WHERE state = 3 -- 'resolved'
SQL
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_11_12_232338) do
ActiveRecord::Schema.define(version: 2019_11_14_173624) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
......@@ -3952,6 +3952,8 @@ ActiveRecord::Schema.define(version: 2019_11_12_232338) do
t.boolean "severity_overridden", default: false
t.integer "confidence", limit: 2, null: false
t.boolean "confidence_overridden", default: false
t.bigint "resolved_by_id"
t.datetime_with_timezone "resolved_at"
t.integer "report_type", limit: 2, null: false
t.integer "cached_markdown_version"
t.index ["author_id"], name: "index_vulnerabilities_on_author_id"
......@@ -3961,6 +3963,7 @@ ActiveRecord::Schema.define(version: 2019_11_12_232338) do
t.index ["last_edited_by_id"], name: "index_vulnerabilities_on_last_edited_by_id"
t.index ["milestone_id"], name: "index_vulnerabilities_on_milestone_id"
t.index ["project_id"], name: "index_vulnerabilities_on_project_id"
t.index ["resolved_by_id"], name: "index_vulnerabilities_on_resolved_by_id"
t.index ["start_date_sourcing_milestone_id"], name: "index_vulnerabilities_on_start_date_sourcing_milestone_id"
t.index ["updated_by_id"], name: "index_vulnerabilities_on_updated_by_id"
end
......@@ -4518,6 +4521,7 @@ ActiveRecord::Schema.define(version: 2019_11_12_232338) do
add_foreign_key "vulnerabilities", "users", column: "author_id", name: "fk_b1de915a15", on_delete: :nullify
add_foreign_key "vulnerabilities", "users", column: "closed_by_id", name: "fk_cf5c60acbf", on_delete: :nullify
add_foreign_key "vulnerabilities", "users", column: "last_edited_by_id", name: "fk_1302949740", on_delete: :nullify
add_foreign_key "vulnerabilities", "users", column: "resolved_by_id", name: "fk_76bc5f5455", on_delete: :nullify
add_foreign_key "vulnerabilities", "users", column: "updated_by_id", name: "fk_7ac31eacb9", on_delete: :nullify
add_foreign_key "vulnerability_feedback", "ci_pipelines", column: "pipeline_id", on_delete: :nullify
add_foreign_key "vulnerability_feedback", "issues", on_delete: :nullify
......
......@@ -1179,12 +1179,12 @@ Rendered example:
- Prefer to use examples using the personal access token and don't pass data of
username and password.
| Methods | Description |
|:-------------------------------------------|:------------------------------------------------------|
| `-H "PRIVATE-TOKEN: <your_access_token>"` | Use this method as is, whenever authentication needed |
| `-X POST` | Use this method when creating new objects |
| `-X PUT` | Use this method when updating existing objects |
| `-X DELETE` | Use this method when removing existing objects |
| Methods | Description |
|:------------------------------------------- |:------------------------------------------------------|
| `--header "PRIVATE-TOKEN: <your_access_token>"` | Use this method as is, whenever authentication needed |
| `--request POST` | Use this method when creating new objects |
| `--request PUT` | Use this method when updating existing objects |
| `--request DELETE` | Use this method when removing existing objects |
### cURL Examples
......@@ -1206,9 +1206,9 @@ Create a new project under the authenticated user's namespace:
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects?name=foo"
```
#### Post data using cURL's --data
#### Post data using cURL's `--data`
Instead of using `-X POST` and appending the parameters to the URI, you can use
Instead of using `--request POST` and appending the parameters to the URI, you can use
cURL's `--data` option. The example below will create a new project `foo` under
the authenticated user's namespace.
......
......@@ -491,6 +491,24 @@ When some write actions are not allowed because the node is a
The database itself will already be read-only in a replicated setup,
so we don't need to take any extra step for that.
## Steps needed to replicate a new data type
As GitLab evolves, we constantly need to add new resources to the Geo replication system.
The implementation depends on resource specifics, but there are several things
that need to be taken care of:
- Event generation on the primary site. Whenever a new resource is changed/updated, we need to
create a task for the Log Cursor.
- Event handling. The Log Cursor needs to have a handler for every event type generated by the primary site.
- Dispatch worker (cron job). Make sure the backfill condition works well.
- Sync worker.
- Registry with all possible states.
- Verification.
- Cleaner. When sync settings are changed for the secondary site, some resources need to be cleaned up.
- Geo Node Status. We need to provide API endpoints as well as some presentation in the GitLab Admin Area.
- Health Check. If we can perform some pre-cheсks and make node unhealthy if something is wrong, we should do that.
The `rake gitlab:geo:check` command has to be updated too.
## History of communication channel
The communication channel has changed since first iteration, you can
......
......@@ -407,7 +407,8 @@ module API
merge_params = HashWithIndifferentAccess.new(
commit_message: params[:merge_commit_message],
squash_commit_message: params[:squash_commit_message],
should_remove_source_branch: params[:should_remove_source_branch]
should_remove_source_branch: params[:should_remove_source_branch],
sha: params[:sha] || merge_request.diff_head_sha
)
if merge_when_pipeline_succeeds && merge_request.head_pipeline && merge_request.head_pipeline.active?
......
......@@ -8,7 +8,8 @@ module Gitlab
ABSOLUTE_ARTIFACT_DIR = ::JobArtifactUploader.root.freeze
LOST_AND_FOUND = File.join(ABSOLUTE_ARTIFACT_DIR, '-', 'lost+found').freeze
BATCH_SIZE = 500
DEFAULT_NICENESS = 'Best-effort'
DEFAULT_NICENESS = 'best-effort'
VALID_NICENESS_LEVELS = %w{none realtime best-effort idle}.freeze
attr_accessor :batch, :total_found, :total_cleaned
attr_reader :limit, :dry_run, :niceness, :logger
......@@ -16,7 +17,7 @@ module Gitlab
def initialize(limit: nil, dry_run: true, niceness: nil, logger: nil)
@limit = limit
@dry_run = dry_run
@niceness = niceness || DEFAULT_NICENESS
@niceness = (niceness || DEFAULT_NICENESS).downcase
@logger = logger || Rails.logger # rubocop:disable Gitlab/RailsLogger
@total_found = @total_cleaned = 0
......@@ -35,7 +36,7 @@ module Gitlab
clean_batch!
log_info("Processed #{total_found} job artifacts to find and clean #{total_cleaned} orphans.")
log_info("Processed #{total_found} job artifact(s) to find and cleaned #{total_cleaned} orphan(s).")
end
private
......@@ -75,7 +76,7 @@ module Gitlab
def find_artifacts
Open3.popen3(*find_command) do |stdin, stdout, stderr, status_thread|
stdout.each_line do |line|
yield line
yield line.chomp
end
log_error(stderr.read.color(:red)) unless status_thread.value.success?
......@@ -99,7 +100,7 @@ module Gitlab
cmd += %w[-type d]
if ionice
raise ArgumentError, 'Invalid niceness' unless niceness.match?(/^\w[\w\-]*$/)
raise ArgumentError, 'Invalid niceness' unless VALID_NICENESS_LEVELS.include?(niceness)
cmd.unshift(*%W[#{ionice} --class #{niceness}])
end
......
# frozen_string_literal: true
require 'spec_helper'
describe RendersCommits do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
let_it_be(:user) { create(:user) }
controller(ApplicationController) do
# `described_class` is not available in this context
include RendersCommits # rubocop:disable RSpec/DescribedClass
def index
@merge_request = MergeRequest.find(params[:id])
@commits = set_commits_for_rendering(
@merge_request.recent_commits.with_latest_pipeline(@merge_request.source_branch),
commits_count: @merge_request.commits_count
)
render json: { html: view_to_html_string('projects/merge_requests/_commits') }
end
end
before do
sign_in(user)
end
def go
get :index, params: { id: merge_request.id }
end
it 'sets instance variables for counts' do
stub_const("MergeRequestDiff::COMMITS_SAFE_SIZE", 10)
go
expect(assigns[:total_commit_count]).to eq(29)
expect(assigns[:hidden_commit_count]).to eq(19)
expect(assigns[:commits].size).to eq(10)
end
context 'rendering commits' do
render_views
it 'avoids N + 1' do
stub_const("MergeRequestDiff::COMMITS_SAFE_SIZE", 5)
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
go
end.count
stub_const("MergeRequestDiff::COMMITS_SAFE_SIZE", 15)
expect do
go
end.not_to exceed_all_query_limit(control_count)
end
end
end
......@@ -405,7 +405,7 @@ describe Projects::MergeRequestsController do
end
it 'starts the merge immediately with permitted params' do
expect(MergeWorker).to receive(:perform_async).with(merge_request.id, anything, { 'squash' => false })
expect(MergeWorker).to receive(:perform_async).with(merge_request.id, anything, { 'sha' => merge_request.diff_head_sha })
merge_with_sha
end
......@@ -432,9 +432,14 @@ describe Projects::MergeRequestsController do
let(:message) { 'My custom squash commit message' }
it 'passes the same message to SquashService', :sidekiq_might_not_need_inline do
params = { squash: '1', squash_commit_message: message }
expect_next_instance_of(MergeRequests::SquashService, project, user, params.merge(merge_request: merge_request)) do |squash_service|
params = { squash: '1',
squash_commit_message: message,
sha: merge_request.diff_head_sha }
expected_squash_params = { squash_commit_message: message,
sha: merge_request.diff_head_sha,
merge_request: merge_request }
expect_next_instance_of(MergeRequests::SquashService, project, user, expected_squash_params) do |squash_service|
expect(squash_service).to receive(:execute).and_return({
status: :success,
squash_sha: SecureRandom.hex(20)
......
......@@ -100,6 +100,7 @@ FactoryBot.define do
auto_merge_enabled { true }
auto_merge_strategy { AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS }
merge_user { author }
merge_params { { sha: diff_head_sha } }
end
trait :remove_source_branch do
......
......@@ -15,7 +15,7 @@ describe 'Merge request > User cherry-picks', :js do
context 'Viewing a merged merge request' do
before do
service = MergeRequests::MergeService.new(project, user)
service = MergeRequests::MergeService.new(project, user, sha: merge_request.diff_head_sha)
perform_enqueued_jobs do
service.execute(merge_request)
......
......@@ -163,6 +163,28 @@ describe TodosFinder do
expect(todos).to match_array([todo1, todo2])
end
end
context 'by project' do
let_it_be(:project1) { create(:project) }
let_it_be(:project2) { create(:project) }
let_it_be(:project3) { create(:project) }
let!(:todo1) { create(:todo, user: user, project: project1, state: :pending) }
let!(:todo2) { create(:todo, user: user, project: project2, state: :pending) }
let!(:todo3) { create(:todo, user: user, project: project3, state: :pending) }
it 'returns the expected todos for one project' do
todos = finder.new(user, { project_id: project2.id }).execute
expect(todos).to match_array([todo2])
end
it 'returns the expected todos for many projects' do
todos = finder.new(user, { project_id: [project2.id, project1.id] }).execute
expect(todos).to match_array([todo2, todo1])
end
end
end
context 'external authorization' do
......
......@@ -8,7 +8,23 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') }
let(:merge_request) { create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') }
# rubocop: disable Layout/TrailingWhitespace
let(:merge_request) do
create(
:merge_request,
:with_diffs,
source_project: project,
target_project: project,
description: <<~MARKDOWN.strip_heredoc
- [ ] Task List Item
- [ ]
- [ ] Task List Item 2
MARKDOWN
)
end
# rubocop: enable Layout/TrailingWhitespace
let(:merged_merge_request) { create(:merge_request, :merged, source_project: project, target_project: project) }
let(:pipeline) do
create(
......
......@@ -51,6 +51,16 @@ describe('DateTimePicker', () => {
});
});
it('renders dropdown without a selectedTimeWindow set', done => {
createComponent({
selectedTimeWindow: {},
});
dateTimePicker.vm.$nextTick(() => {
expect(dateTimePicker.findAll('input').length).toBe(2);
done();
});
});
it('renders inputs with h/m/s truncated if its all 0s', done => {
createComponent({
selectedTimeWindow: {
......
/* eslint-disable no-return-assign */
import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
......@@ -22,7 +20,8 @@ describe('MergeRequest', function() {
.onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`)
.reply(200, {});
return (this.merge = new MergeRequest());
this.merge = new MergeRequest();
return this.merge;
});
afterEach(() => {
......@@ -34,10 +33,30 @@ describe('MergeRequest', function() {
const changeEvent = document.createEvent('HTMLEvents');
changeEvent.initEvent('change', true, true);
$('input[type=checkbox]')
.first()
.attr('checked', true)[0]
.dispatchEvent(changeEvent);
setTimeout(() => {
expect($('.js-task-list-field').val()).toBe(
'- [x] Task List Item\n- [ ] \n- [ ] Task List Item 2\n',
);
done();
});
});
it('ensure that task with only spaces does not get checked incorrectly', done => {
// fixed in 'deckar01-task_list', '2.2.1' gem
spyOn($, 'ajax').and.stub();
const changeEvent = document.createEvent('HTMLEvents');
changeEvent.initEvent('change', true, true);
$('input[type=checkbox]')
.last()
.attr('checked', true)[0]
.dispatchEvent(changeEvent);
setTimeout(() => {
expect($('.js-task-list-field').val()).toBe('- [x] Task List Item');
expect($('.js-task-list-field').val()).toBe(
'- [ ] Task List Item\n- [ ] \n- [x] Task List Item 2\n',
);
done();
});
});
......@@ -59,7 +78,7 @@ describe('MergeRequest', function() {
`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`,
{
merge_request: {
description: '- [ ] Task List Item',
description: '- [ ] Task List Item\n- [ ] \n- [ ] Task List Item 2\n',
lock_version: 0,
update_task: { line_number: lineNumber, line_source: lineSource, index, checked },
},
......
......@@ -21,11 +21,10 @@ describe Gitlab::Cleanup::OrphanJobArtifactFiles do
end
it 'errors when invalid niceness is given' do
allow(Gitlab::Utils).to receive(:which).with('ionice').and_return('/fake/ionice')
cleanup = described_class.new(logger: null_logger, niceness: 'FooBar')
expect(null_logger).to receive(:error).with(/FooBar/)
cleanup.run!
expect { cleanup.run! }.to raise_error('Invalid niceness')
end
it 'finds artifacts on disk' do
......@@ -63,6 +62,8 @@ describe Gitlab::Cleanup::OrphanJobArtifactFiles do
def mock_artifacts_found(cleanup, *files)
mock = allow(cleanup).to receive(:find_artifacts)
files.each { |file| mock.and_yield(file) }
# Because we shell out to run `find -L ...`, each file actually
# contains a trailing newline
files.each { |file| mock.and_yield("#{file}\n") }
end
end
......@@ -1934,7 +1934,7 @@ describe MergeRequest do
context 'when the MR has been merged' do
before do
MergeRequests::MergeService
.new(subject.target_project, subject.author)
.new(subject.target_project, subject.author, { sha: subject.diff_head_sha })
.execute(subject)
end
......@@ -3484,4 +3484,67 @@ describe MergeRequest do
end
it_behaves_like 'versioned description'
describe '#commits' do
context 'persisted merge request' do
context 'with a limit' do
it 'returns a limited number of commits' do
expect(subject.commits(limit: 2).map(&:sha)).to eq(%w[
b83d6e391c22777fca1ed3012fce84f633d7fed0
498214de67004b1da3d820901307bed2a68a8ef6
])
expect(subject.commits(limit: 3).map(&:sha)).to eq(%w[
b83d6e391c22777fca1ed3012fce84f633d7fed0
498214de67004b1da3d820901307bed2a68a8ef6
1b12f15a11fc6e62177bef08f47bc7b5ce50b141
])
end
end
context 'without a limit' do
it 'returns all commits of the merge request diff' do
expect(subject.commits.size).to eq(29)
end
end
end
context 'new merge request' do
subject { build(:merge_request) }
context 'compare commits' do
let(:first_commit) { double }
let(:second_commit) { double }
before do
subject.compare_commits = [
first_commit, second_commit
]
end
context 'without a limit' do
it 'returns all the compare commits' do
expect(subject.commits.to_a).to eq([second_commit, first_commit])
end
end
context 'with a limit' do
it 'returns a limited number of commits' do
expect(subject.commits(limit: 1).to_a).to eq([second_commit])
end
end
end
end
end
describe '#recent_commits' do
before do
stub_const("#{MergeRequestDiff}::COMMITS_SAFE_SIZE", 2)
end
it 'returns the safe number of commits' do
expect(subject.recent_commits.map(&:sha)).to eq(%w[
b83d6e391c22777fca1ed3012fce84f633d7fed0 498214de67004b1da3d820901307bed2a68a8ef6
])
end
end
end
......@@ -4899,20 +4899,6 @@ describe Project do
end
end
describe '.find_without_deleted' do
it 'returns nil if the project is about to be removed' do
project = create(:project, pending_delete: true)
expect(described_class.find_without_deleted(project.id)).to be_nil
end
it 'returns a project when it is not about to be removed' do
project = create(:project)
expect(described_class.find_without_deleted(project.id)).to eq(project)
end
end
describe '.for_group' do
it 'returns the projects for a given group' do
group = create(:group)
......
......@@ -221,6 +221,40 @@ describe Todo do
expect(described_class.for_project(project1)).to eq([todo])
end
it 'returns the todos for many projects' do
project1 = create(:project)
project2 = create(:project)
project3 = create(:project)
todo1 = create(:todo, project: project1)
todo2 = create(:todo, project: project2)
create(:todo, project: project3)
expect(described_class.for_project([project2, project1])).to contain_exactly(todo2, todo1)
end
end
describe '.for_undeleted_projects' do
let(:project1) { create(:project) }
let(:project2) { create(:project) }
let(:project3) { create(:project) }
let!(:todo1) { create(:todo, project: project1) }
let!(:todo2) { create(:todo, project: project2) }
let!(:todo3) { create(:todo, project: project3) }
it 'returns the todos for a given project' do
expect(described_class.for_undeleted_projects).to contain_exactly(todo1, todo2, todo3)
end
context 'when todo belongs to deleted project' do
let(:project2) { create(:project, pending_delete: true) }
it 'excludes todos of deleted projects' do
expect(described_class.for_undeleted_projects).to contain_exactly(todo1, todo3)
end
end
end
describe '.for_group' do
......
......@@ -13,6 +13,7 @@ describe MergeRequests::FfMergeService do
author: create(:user))
end
let(:project) { merge_request.project }
let(:valid_merge_params) { { sha: merge_request.diff_head_sha } }
before do
project.add_maintainer(user)
......@@ -21,7 +22,7 @@ describe MergeRequests::FfMergeService do
describe '#execute' do
context 'valid params' do
let(:service) { described_class.new(project, user, {}) }
let(:service) { described_class.new(project, user, valid_merge_params) }
before do
allow(service).to receive(:execute_hooks)
......@@ -52,8 +53,8 @@ describe MergeRequests::FfMergeService do
end
end
context "error handling" do
let(:service) { described_class.new(project, user, commit_message: 'Awesome message') }
context 'error handling' do
let(:service) { described_class.new(project, user, valid_merge_params.merge(commit_message: 'Awesome message')) }
before do
allow(Rails.logger).to receive(:error)
......
......@@ -14,9 +14,12 @@ describe MergeRequests::MergeService do
end
describe '#execute' do
context 'valid params' do
let(:service) { described_class.new(project, user, commit_message: 'Awesome message') }
let(:service) { described_class.new(project, user, merge_params) }
let(:merge_params) do
{ commit_message: 'Awesome message', sha: merge_request.diff_head_sha }
end
context 'valid params' do
before do
allow(service).to receive(:execute_hooks)
......@@ -38,11 +41,80 @@ describe MergeRequests::MergeService do
note = merge_request.notes.last
expect(note.note).to include 'merged'
end
context 'when squashing' do
let(:merge_params) do
{ commit_message: 'Merge commit message',
squash_commit_message: 'Squash commit message',
sha: merge_request.diff_head_sha }
end
let(:merge_request) do
# A merge reqeust with 5 commits
create(:merge_request, :simple,
author: user2,
assignees: [user2],
squash: true,
source_branch: 'improve/awesome',
target_branch: 'fix')
end
it 'merges the merge request with squashed commits' do
expect(merge_request).to be_merged
merge_commit = merge_request.merge_commit
squash_commit = merge_request.merge_commit.parents.last
expect(merge_commit.message).to eq('Merge commit message')
expect(squash_commit.message).to eq("Squash commit message\n")
end
end
end
context 'closes related issues' do
let(:service) { described_class.new(project, user, commit_message: 'Awesome message') }
context 'when an invalid sha is passed' do
let(:merge_request) do
create(:merge_request, :simple,
author: user2,
assignees: [user2],
squash: true,
source_branch: 'improve/awesome',
target_branch: 'fix')
end
let(:merge_params) do
{ sha: merge_request.commits.second.sha }
end
it 'does not merge the MR' do
service.execute(merge_request)
expect(merge_request).not_to be_merged
expect(merge_request.merge_error).to match(/Branch has been updated/)
end
end
context 'when the `sha` param is missing' do
let(:merge_params) { {} }
it 'returns the error' do
merge_error = 'Branch has been updated since the merge was requested. '\
'Please review the changes.'
expect { service.execute(merge_request) }
.to change { merge_request.merge_error }
.from(nil).to(merge_error)
end
it 'merges the MR when the feature is disabled' do
stub_feature_flags(validate_merge_sha: false)
service.execute(merge_request)
expect(merge_request).to be_merged
end
end
context 'closes related issues' do
before do
allow(project).to receive(:default_branch).and_return(merge_request.target_branch)
end
......@@ -83,12 +155,12 @@ describe MergeRequests::MergeService do
service.execute(merge_request)
end
context "when jira_issue_transition_id is not present" do
context 'when jira_issue_transition_id is not present' do
before do
allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(nil)
end
it "does not close issue" do
it 'does not close issue' do
allow(jira_tracker).to receive_messages(jira_issue_transition_id: nil)
expect_any_instance_of(JiraService).not_to receive(:transition_issue)
......@@ -97,7 +169,7 @@ describe MergeRequests::MergeService do
end
end
context "wrong issue markdown" do
context 'wrong issue markdown' do
it 'does not close issues on Jira issue tracker' do
jira_issue = ExternalIssue.new('#JIRA-123', project)
stub_jira_urls(jira_issue)
......@@ -115,7 +187,7 @@ describe MergeRequests::MergeService do
context 'closes related todos' do
let(:merge_request) { create(:merge_request, assignees: [user], author: user) }
let(:project) { merge_request.project }
let(:service) { described_class.new(project, user, commit_message: 'Awesome message') }
let!(:todo) do
create(:todo, :assigned,
project: project,
......@@ -139,7 +211,7 @@ describe MergeRequests::MergeService do
context 'source branch removal' do
context 'when the source branch is protected' do
let(:service) do
described_class.new(project, user, 'should_remove_source_branch' => true)
described_class.new(project, user, merge_params.merge('should_remove_source_branch' => true))
end
before do
......@@ -154,7 +226,7 @@ describe MergeRequests::MergeService do
context 'when the source branch is the default branch' do
let(:service) do
described_class.new(project, user, 'should_remove_source_branch' => true)
described_class.new(project, user, merge_params.merge('should_remove_source_branch' => true))
end
before do
......@@ -169,8 +241,6 @@ describe MergeRequests::MergeService do
context 'when the source branch can be removed' do
context 'when MR author set the source branch to be removed' do
let(:service) { described_class.new(project, user, commit_message: 'Awesome message') }
before do
merge_request.update_attribute(:merge_params, { 'force_remove_source_branch' => '1' })
end
......@@ -183,7 +253,7 @@ describe MergeRequests::MergeService do
end
context 'when the merger set the source branch not to be removed' do
let(:service) { described_class.new(project, user, commit_message: 'Awesome message', 'should_remove_source_branch' => false) }
let(:service) { described_class.new(project, user, merge_params.merge('should_remove_source_branch' => false)) }
it 'does not delete the source branch' do
expect(DeleteBranchService).not_to receive(:new)
......@@ -194,7 +264,7 @@ describe MergeRequests::MergeService do
context 'when MR merger set the source branch to be removed' do
let(:service) do
described_class.new(project, user, commit_message: 'Awesome message', 'should_remove_source_branch' => true)
described_class.new(project, user, merge_params.merge('should_remove_source_branch' => true))
end
it 'removes the source branch using the current user' do
......@@ -207,9 +277,7 @@ describe MergeRequests::MergeService do
end
end
context "error handling" do
let(:service) { described_class.new(project, user, commit_message: 'Awesome message') }
context 'error handling' do
before do
allow(Rails.logger).to receive(:error)
end
......@@ -230,7 +298,7 @@ describe MergeRequests::MergeService do
it 'logs and saves error if there is an exception' do
error_message = 'error message'
allow(service).to receive(:repository).and_raise("error message")
allow(service).to receive(:repository).and_raise('error message')
allow(service).to receive(:execute_hooks)
service.execute(merge_request)
......@@ -310,7 +378,7 @@ describe MergeRequests::MergeService do
expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
end
context "when fast-forward merge is not allowed" do
context 'when fast-forward merge is not allowed' do
before do
allow_any_instance_of(Repository).to receive(:ancestor?).and_return(nil)
end
......
......@@ -76,7 +76,7 @@ describe MergeRequests::MergeToRefService do
described_class.new(project, user, **params)
end
let(:params) { { commit_message: 'Awesome message', should_remove_source_branch: true } }
let(:params) { { commit_message: 'Awesome message', should_remove_source_branch: true, sha: merge_request.diff_head_sha } }
def process_merge_to_ref
perform_enqueued_jobs do
......@@ -103,7 +103,7 @@ describe MergeRequests::MergeToRefService do
end
let(:merge_service) do
MergeRequests::MergeService.new(project, user, {})
MergeRequests::MergeService.new(project, user, { sha: merge_request.diff_head_sha })
end
context 'when merge commit' do
......@@ -205,7 +205,7 @@ describe MergeRequests::MergeToRefService do
end
context 'when target ref is passed as a parameter' do
let(:params) { { commit_message: 'merge train', target_ref: target_ref } }
let(:params) { { commit_message: 'merge train', target_ref: target_ref, sha: merge_request.diff_head_sha } }
it_behaves_like 'successfully merges to ref with merge method' do
let(:first_parent_ref) { 'refs/heads/master' }
......@@ -215,7 +215,7 @@ describe MergeRequests::MergeToRefService do
describe 'cascading merge refs' do
set(:project) { create(:project, :repository) }
let(:params) { { commit_message: 'Cascading merge', first_parent_ref: first_parent_ref, target_ref: target_ref } }
let(:params) { { commit_message: 'Cascading merge', first_parent_ref: first_parent_ref, target_ref: target_ref, sha: merge_request.diff_head_sha } }
context 'when first merge happens' do
let(:merge_request) do
......
......@@ -219,7 +219,7 @@ describe MergeRequests::UpdateService, :mailer do
head_pipeline_of: merge_request
)
expect(AutoMerge::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user, {})
expect(AutoMerge::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user, { sha: merge_request.diff_head_sha })
.and_return(service_mock)
allow(service_mock).to receive(:available_for?) { true }
expect(service_mock).to receive(:execute).with(merge_request)
......@@ -411,7 +411,7 @@ describe MergeRequests::UpdateService, :mailer do
context 'when auto merge is enabled and target branch changed' do
before do
AutoMergeService.new(project, user).execute(merge_request, AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
AutoMergeService.new(project, user, { sha: merge_request.diff_head_sha }).execute(merge_request, AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
update_merge_request({ target_branch: 'target' })
end
......
......@@ -77,7 +77,7 @@ module CycleAnalyticsHelpers
.new(project, user)
.closed_by_merge_requests(issue)
merge_requests.each { |merge_request| MergeRequests::MergeService.new(project, user).execute(merge_request) }
merge_requests.each { |merge_request| MergeRequests::MergeService.new(project, user, sha: merge_request.diff_head_sha).execute(merge_request) }
end
def deploy_master(user, project, environment: 'production')
......
......@@ -20,6 +20,7 @@ describe MergeWorker do
described_class.new.perform(
merge_request.id, merge_request.author_id,
commit_message: 'wow such merge',
sha: merge_request.diff_head_sha,
should_remove_source_branch: true)
merge_request.reload
......
......@@ -81,9 +81,10 @@ describe ProcessCommitWorker do
let(:commit) do
project.repository.create_branch('feature-merged', 'feature')
project.repository.after_create_branch
MergeRequests::MergeService
.new(project, merge_request.author)
.new(project, merge_request.author, { sha: merge_request.diff_head_sha })
.execute(merge_request)
merge_request.reload.merge_commit
......
......@@ -3614,10 +3614,10 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
deckar01-task_list@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/deckar01-task_list/-/deckar01-task_list-2.2.0.tgz#5cc3ea06f01d3d786b1a667064a462eb5d069bd3"
integrity sha512-NUfu5ARoD9SC2k+fBT5cBer59iKfEdawPrmfqp5+zAahTECb8z9dsuS1Xnx7jzFAmCCLnEs3z/aYucYXzNrKkQ==
deckar01-task_list@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/deckar01-task_list/-/deckar01-task_list-2.2.1.tgz#e1e8a16c4fd6e153e51fd9258fdbee067ebcd86b"
integrity sha512-aNAVYAYwONXezSQy2p5M67wjZE+U7JpPotdegbyy1Wq35V6jDhF3qndJYA1rYnY3aI9ifCep6EMGPav/UQaBZw==
decode-uri-component@^0.2.0:
version "0.2.0"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册