Read about our upcoming Code of Conduct on this issue

Commit 55e72065 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 8ab7869078b0
......@@ -200,7 +200,7 @@
.use-pg9:
services:
- name: postgres:9.6
- name: postgres:9.6.17
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
variables:
......@@ -209,7 +209,7 @@
.use-pg10:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
services:
- name: postgres:10.9
- name: postgres:10.12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
variables:
......@@ -217,7 +217,7 @@
.use-pg9-ee:
services:
- name: postgres:9.6
- name: postgres:9.6.17
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
- name: elasticsearch:6.4.2
......@@ -227,7 +227,7 @@
.use-pg10-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
services:
- name: postgres:10.9
- name: postgres:10.12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
- name: elasticsearch:6.4.2
......
......@@ -3,7 +3,7 @@
* This is tightly coupled to projects/issues/_issue.html.haml,
* any changes done to the haml need to be reflected here.
*/
import { escape, isNumber } from 'underscore';
import { escape, isNumber } from 'lodash';
import { GlLink, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
import {
dateInWords,
......@@ -19,8 +19,6 @@ import { mergeUrlParams } from '~/lib/utils/url_utility';
import Icon from '~/vue_shared/components/icon.vue';
import IssueAssignees from '~/vue_shared/components/issue/issue_assignees.vue';
const ISSUE_TOKEN = '#';
export default {
components: {
Icon,
......@@ -119,8 +117,7 @@ export default {
);
},
referencePath() {
// TODO: The API should return the reference path (it doesn't now) https://gitlab.com/gitlab-org/gitlab/issues/31301
return `${ISSUE_TOKEN}${this.issuable.iid}`;
return this.issuable.references.relative;
},
updatedDateString() {
return formatDate(new Date(this.issuable.updated_at), 'mmm d, yyyy h:MMtt');
......@@ -230,7 +227,7 @@ export default {
</div>
<div class="issuable-info">
<span>{{ referencePath }}</span>
<span class="js-ref-path">{{ referencePath }}</span>
<span class="d-none d-sm-inline-block mr-1">
&middot;
......
......@@ -62,9 +62,21 @@ export default {
return (
report.existing_failures.length > 0 ||
report.new_failures.length > 0 ||
report.resolved_failures.length > 0
report.resolved_failures.length > 0 ||
report.existing_errors.length > 0 ||
report.new_errors.length > 0 ||
report.resolved_errors.length > 0
);
},
unresolvedIssues(report) {
return report.existing_failures.concat(report.existing_errors);
},
newIssues(report) {
return report.new_failures.concat(report.new_errors);
},
resolvedIssues(report) {
return report.resolved_failures.concat(report.resolved_errors);
},
},
};
</script>
......@@ -87,9 +99,9 @@ export default {
<issues-list
v-if="shouldRenderIssuesList(report)"
:key="`issues-list-${i}`"
:unresolved-issues="report.existing_failures"
:new-issues="report.new_failures"
:resolved-issues="report.resolved_failures"
:unresolved-issues="unresolvedIssues(report)"
:new-issues="newIssues(report)"
:resolved-issues="resolvedIssues(report)"
:component="$options.componentNames.TestIssueBody"
class="report-block-group-list"
/>
......
......@@ -16,6 +16,7 @@ export default {
state.summary.total = response.summary.total;
state.summary.resolved = response.summary.resolved;
state.summary.failed = response.summary.failed;
state.summary.errored = response.summary.errored;
state.status = response.status;
state.reports = response.suites;
......@@ -29,6 +30,7 @@ export default {
total: 0,
resolved: 0,
failed: 0,
errored: 0,
};
state.status = null;
},
......
......@@ -13,6 +13,7 @@ export default () => ({
total: 0,
resolved: 0,
failed: 0,
errored: 0,
},
/**
......@@ -23,10 +24,14 @@ export default () => ({
* total: {Number},
* resolved: {Number},
* failed: {Number},
* errored: {Number},
* },
* new_failures: {Array.<Object>},
* resolved_failures: {Array.<Object>},
* existing_failures: {Array.<Object>},
* new_errors: {Array.<Object>},
* resolved_errors: {Array.<Object>},
* existing_errors: {Array.<Object>},
* }
*/
reports: [],
......
......@@ -8,10 +8,11 @@ import {
} from '../constants';
const textBuilder = results => {
const { failed, resolved, total } = results;
const { failed, errored, resolved, total } = results;
const failedString = failed
? n__('%d failed/error test result', '%d failed/error test results', failed)
const failedOrErrored = (failed || 0) + (errored || 0);
const failedString = failedOrErrored
? n__('%d failed/error test result', '%d failed/error test results', failedOrErrored)
: null;
const resolvedString = resolved
? n__('%d fixed test result', '%d fixed test results', resolved)
......@@ -20,7 +21,7 @@ const textBuilder = results => {
let resultsString = s__('Reports|no changed test results');
if (failed) {
if (failedOrErrored) {
if (resolved) {
resultsString = sprintf(s__('Reports|%{failedString} and %{resolvedString}'), {
failedString,
......
......@@ -86,8 +86,12 @@ def can_move_issues?
head(:forbidden) unless can?(current_user, :admin_issue, board)
end
def serializer_options(issues)
{}
end
def render_issues(issues, metadata)
data = { issues: serialize_as_json(issues) }
data = { issues: serialize_as_json(issues, opts: serializer_options(issues)) }
data.merge!(metadata)
render json: data
......@@ -133,8 +137,10 @@ def serializer
IssueSerializer.new(current_user: current_user)
end
def serialize_as_json(resource)
serializer.represent(resource, serializer: 'board', include_full_project_path: board.group_board?)
def serialize_as_json(resource, opts: {})
opts.merge!(include_full_project_path: board.group_board?, serializer: 'board')
serializer.represent(resource, opts)
end
def whitelist_query_limiting
......
# frozen_string_literal: true
module Mutations
module Notes
class Update < Base
graphql_name 'UpdateNote'
authorize :admin_note
argument :id,
GraphQL::ID_TYPE,
required: true,
description: 'The global id of the note to update'
argument :body,
GraphQL::STRING_TYPE,
required: true,
description: copy_field_description(Types::Notes::NoteType, :body)
def resolve(args)
note = authorized_find!(id: args[:id])
check_object_is_note!(note)
note = ::Notes::UpdateService.new(
note.project,
current_user,
{ note: args[:body] }
).execute(note)
{
note: note.reset,
errors: errors_on_object(note)
}
end
end
end
end
# frozen_string_literal: true
module Mutations
module Notes
module Update
# This is a Base class for the Note update mutations and is not
# mounted as a GraphQL mutation itself.
class Base < Mutations::Notes::Base
authorize :admin_note
argument :id,
GraphQL::ID_TYPE,
required: true,
description: 'The global id of the note to update'
def resolve(args)
note = authorized_find!(id: args[:id])
pre_update_checks!(note, args)
updated_note = ::Notes::UpdateService.new(
note.project,
current_user,
note_params(note, args)
).execute(note)
# It's possible for updated_note to be `nil`, in the situation
# where the note is deleted within `Notes::UpdateService` due to
# the body of the note only containing Quick Actions.
{
note: updated_note&.reset,
errors: updated_note ? errors_on_object(updated_note) : []
}
end
private
def pre_update_checks!(_note, _args)
raise NotImplementedError
end
def note_params(_note, args)
{ note: args[:body] }.compact
end
end
end
end
end
# frozen_string_literal: true
module Mutations
module Notes
module Update
class ImageDiffNote < Mutations::Notes::Update::Base
graphql_name 'UpdateImageDiffNote'
argument :body,
GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::Notes::NoteType, :body)
argument :position,
Types::Notes::UpdateDiffImagePositionInputType,
required: false,
description: copy_field_description(Types::Notes::NoteType, :position)
def ready?(**args)
# As both arguments are optional, validate here that one of the
# arguments are present.
#
# This may be able to be done using InputUnions in the future
# if this RFC is merged:
# https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md
if args.values_at(:body, :position).compact.blank?
raise Gitlab::Graphql::Errors::ArgumentError,
'body or position arguments are required'
end
super(args)
end
private
def pre_update_checks!(note, args)
unless note.is_a?(DiffNote) && note.position.on_image?
raise Gitlab::Graphql::Errors::ResourceNotAvailable,
'Resource is not an ImageDiffNote'
end
end
def note_params(note, args)
super(note, args).merge(
position: position_params(note, args)
).compact
end
def position_params(note, args)
new_position = args[:position]&.to_h&.compact
return unless new_position
original_position = note.position.to_h
Gitlab::Diff::Position.new(original_position.merge(new_position))
end
end
end
end
end
# frozen_string_literal: true
module Mutations
module Notes
module Update
class Note < Mutations::Notes::Update::Base
graphql_name 'UpdateNote'
argument :body,
GraphQL::STRING_TYPE,
required: true,
description: copy_field_description(Types::Notes::NoteType, :body)
private
def pre_update_checks!(note, _args)
check_object_is_note!(note)
end
end
end
end
end
......@@ -4,7 +4,7 @@ module Types
class MutationType < BaseObject
include Gitlab::Graphql::MountMutation
graphql_name "Mutation"
graphql_name 'Mutation'
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
......@@ -20,7 +20,14 @@ class MutationType < BaseObject
mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true
mount_mutation Mutations::Notes::Create::DiffNote, calls_gitaly: true
mount_mutation Mutations::Notes::Create::ImageDiffNote, calls_gitaly: true
mount_mutation Mutations::Notes::Update
mount_mutation Mutations::Notes::Update::Note,
description: 'Updates a Note. If the body of the Note contains only quick actions, ' \
'the Note will be destroyed during the update, and no Note will be ' \
'returned'
mount_mutation Mutations::Notes::Update::ImageDiffNote,
description: 'Updates a DiffNote on an image (a `Note` where the `position.positionType` is `"image"`). ' \
'If the body of the Note contains only quick actions, the Note will be ' \
'destroyed during the update, and no Note will be returned'
mount_mutation Mutations::Notes::Destroy
mount_mutation Mutations::Todos::MarkDone
mount_mutation Mutations::Todos::Restore
......
......@@ -29,10 +29,10 @@ class DiffPositionType < BaseObject
# Fields for image positions
field :x, GraphQL::INT_TYPE, null: true,
description: 'X position on which the comment was made',
description: 'X position of the note',
resolve: -> (position, _args, _ctx) { position.x if position.on_image? }
field :y, GraphQL::INT_TYPE, null: true,
description: 'Y position on which the comment was made',
description: 'Y position of the note',
resolve: -> (position, _args, _ctx) { position.y if position.on_image? }
field :width, GraphQL::INT_TYPE, null: true,
description: 'Total width of the image',
......
# frozen_string_literal: true
module Types
module Notes
# InputType used for updateImageDiffNote mutation.
#
# rubocop: disable Graphql/AuthorizeTypes
class UpdateDiffImagePositionInputType < BaseInputObject
graphql_name 'UpdateDiffImagePositionInput'
argument :x, GraphQL::INT_TYPE,
required: false,
description: copy_field_description(Types::Notes::DiffPositionType, :x)
argument :y, GraphQL::INT_TYPE,
required: false,
description: copy_field_description(Types::Notes::DiffPositionType, :y)
argument :width, GraphQL::INT_TYPE,
required: false,
description: copy_field_description(Types::Notes::DiffPositionType, :width)
argument :height, GraphQL::INT_TYPE,
required: false,
description: copy_field_description(Types::Notes::DiffPositionType, :height)
end
# rubocop: enable Graphql/AuthorizeTypes
end
end
......@@ -720,8 +720,7 @@ def show_visibility_confirm_modal?(project)
end
def settings_container_registry_expiration_policy_available?(project)
Feature.enabled?(:registry_retention_policies_settings, project) &&
Gitlab.config.registry.enabled &&
Gitlab.config.registry.enabled &&
can?(current_user, :destroy_container_image, project)
end
end
......@@ -15,7 +15,13 @@ def pipeline_failed_email(pipeline, recipients)
def pipeline_mail(pipeline, recipients, status)
@project = pipeline.project
@pipeline = pipeline
@merge_request = pipeline.all_merge_requests.first
@merge_request = if pipeline.merge_request?
pipeline.merge_request
else
pipeline.merge_requests_as_head_pipeline.first
end
add_headers
# We use bcc here because we don't want to generate these emails for a
......
......@@ -15,8 +15,8 @@ module Stage
validates :name, exclusion: { in: Gitlab::Analytics::CycleAnalytics::DefaultStages.names }, if: :custom?
validates :start_event_identifier, presence: true
validates :end_event_identifier, presence: true
validates :start_event_label, presence: true, if: :start_event_label_based?
validates :end_event_label, presence: true, if: :end_event_label_based?
validates :start_event_label_id, presence: true, if: :start_event_label_based?
validates :end_event_label_id, presence: true, if: :end_event_label_based?
validate :validate_stage_event_pairs
validate :validate_labels
......@@ -109,8 +109,8 @@ def pairing_rules
end
def validate_labels
validate_label_within_group(:start_event_label, start_event_label_id) if start_event_label_id_changed?
validate_label_within_group(:end_event_label, end_event_label_id) if end_event_label_id_changed?
validate_label_within_group(:start_event_label_id, start_event_label_id) if start_event_label_id_changed?
validate_label_within_group(:end_event_label_id, end_event_label_id) if end_event_label_id_changed?
end
def validate_label_within_group(association_name, label_id)
......
......@@ -7,6 +7,7 @@ class TestReportsComparerEntity < Grape::Entity
expose :total_count, as: :total
expose :resolved_count, as: :resolved
expose :failed_count, as: :failed
expose :error_count, as: :errored
end
expose :suite_comparers, as: :suites, using: TestSuiteComparerEntity
......
......@@ -11,6 +11,7 @@ class TestSuiteComparerEntity < Grape::Entity
expose :total_count, as: :total
expose :resolved_count, as: :resolved
expose :failed_count, as: :failed
expose :error_count, as: :errored
end
# rubocop: disable CodeReuse/ActiveRecord
......@@ -28,6 +29,20 @@ class TestSuiteComparerEntity < Grape::Entity
max_tests(suite.new_failures, suite.existing_failures))
end
expose :new_errors, using: TestCaseEntity do |suite|
suite.new_errors.take(max_tests)
end
expose :existing_errors, using: TestCaseEntity do |suite|
suite.existing_errors.take(
max_tests(suite.new_errors))
end
expose :resolved_errors, using: TestCaseEntity do |suite|
suite.resolved_errors.take(
max_tests(suite.new_errors, suite.existing_errors))
end
private
def max_tests(*used)
......
......@@ -24,7 +24,9 @@ def execute
spam_check(snippet, current_user)
snippet_saved = snippet.with_transaction_returning_status do
snippet.save
if snippet.save && snippet.store_mentions!
create_repository_for(snippet, current_user)
end
end
if snippet_saved
......@@ -36,5 +38,11 @@ def execute
snippet_error_response(snippet, 400)
end
end
private
def create_repository_for(snippet, user)
snippet.create_repository if Feature.enabled?(:version_snippets, user)
end
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment