Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-03-25 06:09:02 +00:00
parent 3bd9ad5574
commit 807d57c18f
75 changed files with 368 additions and 200 deletions

View File

@ -274,7 +274,7 @@ gem 'licensee', '~> 9.14.1'
gem 'charlock_holmes', '~> 0.7.7'
# Detect mime content type from content
gem 'mimemagic', '~> 0.3.2'
gem 'ruby-magic-static'
# Faster blank
gem 'fast_blank'

View File

@ -1109,6 +1109,7 @@ GEM
i18n
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-magic-static (0.3.0)
ruby-prof (1.3.1)
ruby-progressbar (1.11.0)
ruby-saml (1.7.2)
@ -1481,7 +1482,6 @@ DEPENDENCIES
marginalia (~> 1.10.0)
memory_profiler (~> 0.9)
method_source (~> 1.0)
mimemagic (~> 0.3.2)
mini_magick (~> 4.10.1)
minitest (~> 5.11.0)
multi_json (~> 1.14.1)
@ -1555,6 +1555,7 @@ DEPENDENCIES
rspec_junit_formatter
rspec_profiling (~> 0.0.6)
ruby-fogbugz (~> 0.2.1)
ruby-magic-static
ruby-prof (~> 1.3.0)
ruby-progressbar (~> 1.10)
ruby_parser (~> 3.15)

View File

@ -158,7 +158,7 @@ export default {
data-testid="issue-blocked-icon"
@mouseenter="handleMouseEnter"
/>
<gl-popover :target="glIconId" placement="top" triggers="hover">
<gl-popover :target="glIconId" placement="top">
<template #title
><span data-testid="popover-title">{{ blockedLabel }}</span></template
>

View File

@ -37,7 +37,7 @@ export default {
<template>
<div id="popover-container">
<gl-popover :target="target" triggers="hover" placement="top" container="popover-container">
<gl-popover :target="target" placement="top" container="popover-container">
<div
class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-word-break-all"
>

View File

@ -34,7 +34,7 @@ export default {
<gl-icon name="status_warning" :size="24" class="gl-p-2" />
<gl-popover :container="popoverId" :target="popoverId" placement="top" triggers="hover focus">
<gl-popover :container="popoverId" :target="popoverId" placement="top">
<template #title>
<span class="gl-display-block gl-text-left">{{ errorContent.title }}</span>
</template>

View File

@ -71,7 +71,6 @@ export default {
ref="popover"
:target="$options.targetId"
:css-classes="['feature-highlight-popover']"
triggers="hover"
container="body"
placement="right"
boundary="viewport"

View File

@ -60,11 +60,7 @@ export default {
</select>
<span v-if="requestsWithWarnings.length">
<span id="performance-bar-request-selector-warning" v-html="glEmojiTag('warning')"></span>
<gl-popover
target="performance-bar-request-selector-warning"
:content="warningMessage"
triggers="hover focus"
/>
<gl-popover target="performance-bar-request-selector-warning" :content="warningMessage" />
</span>
</div>
</template>

View File

@ -37,6 +37,6 @@ export default {
<template>
<span v-if="hasWarnings">
<span :id="htmlId" v-html="glEmojiTag('warning')"></span>
<gl-popover :target="htmlId" :content="warningMessage" triggers="hover focus" />
<gl-popover :target="htmlId" :content="warningMessage" />
</span>
</template>

View File

@ -149,12 +149,7 @@ export default {
>
{{ s__('mrWidget|Resolve conflicts') }}
</gl-button>
<gl-popover
v-if="showPopover"
:target="() => $refs.popover"
placement="top"
triggers="hover focus"
>
<gl-popover v-if="showPopover" :target="() => $refs.popover" placement="top">
<template #title>
<div class="gl-font-weight-normal gl-font-base">
{{ $options.i18n.title }}

View File

@ -124,7 +124,7 @@ export default {
},
pipeline() {
if (this.glFeatures.mergeRequestWidgetGraphql) {
return this.state.pipelines?.nodes?.[0];
return this.state.headPipeline;
}
return this.mr.pipeline;

View File

@ -11,11 +11,10 @@ query getState($projectPath: ID!, $iid: String!) {
mergeError
mergeStatus
mergeableDiscussionsState
pipelines(first: 1) {
nodes {
status
warnings
}
headPipeline {
id
status
warnings
}
shouldBeRebased
sourceBranchExists

View File

@ -30,13 +30,11 @@ fragment ReadyToMerge on Project {
message
}
}
pipelines(first: 1) {
nodes {
id
status
path
active
}
headPipeline {
id
status
path
active
}
}
}

View File

@ -163,7 +163,7 @@ export default class MergeRequestStore {
setGraphqlData(project) {
const { mergeRequest } = project;
const pipeline = mergeRequest.pipelines?.nodes?.[0];
const pipeline = mergeRequest.headPipeline;
this.projectArchived = project.archived;
this.onlyAllowMergeIfPipelineSucceeds = project.onlyAllowMergeIfPipelineSucceeds;

View File

@ -21,7 +21,7 @@ import Tracking from '~/tracking';
import initUserPopovers from '~/user_popovers';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { SEVERITY_LEVELS } from '../constants';
import { PAGE_CONFIG, SEVERITY_LEVELS } from '../constants';
import createIssueMutation from '../graphql/mutations/alert_issue_create.mutation.graphql';
import toggleSidebarStatusMutation from '../graphql/mutations/alert_sidebar_status.mutation.graphql';
import alertQuery from '../graphql/queries/alert_details.query.graphql';
@ -92,6 +92,9 @@ export default {
projectIssuesPath: {
default: '',
},
statuses: {
default: PAGE_CONFIG.OPERATIONS.STATUSES,
},
trackAlertsDetailsViewsOptions: {
default: null,
},
@ -367,7 +370,7 @@ export default {
>
{{ alert.runbook }}
</alert-summary-row>
<alert-details-table :alert="alert" :loading="loading" />
<alert-details-table :alert="alert" :loading="loading" :statuses="statuses" />
</gl-tab>
<gl-tab
v-if="!isThreatMonitoringPage"

View File

@ -19,10 +19,6 @@ export default {
projectId: {
default: '',
},
// TODO remove this limitation in https://gitlab.com/gitlab-org/gitlab/-/issues/296717
isThreatMonitoringPage: {
default: false,
},
},
props: {
alert: {
@ -66,7 +62,6 @@ export default {
@alert-error="$emit('alert-error', $event)"
/>
<sidebar-status
v-if="!isThreatMonitoringPage"
:project-path="projectPath"
:alert="alert"
@toggle-sidebar="$emit('toggle-sidebar')"

View File

@ -3,6 +3,7 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import updateAlertStatusMutation from '~/graphql_shared/mutations/alert_status_update.mutation.graphql';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
import { PAGE_CONFIG } from '../constants';
export default {
i18n: {
@ -11,11 +12,6 @@ export default {
),
UPDATE_ALERT_STATUS_INSTRUCTION: s__('AlertManagement|Please try again.'),
},
statuses: {
TRIGGERED: s__('AlertManagement|Triggered'),
ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
RESOLVED: s__('AlertManagement|Resolved'),
},
components: {
GlDropdown,
GlDropdownItem,
@ -42,6 +38,11 @@ export default {
type: Boolean,
required: true,
},
statuses: {
type: Object,
required: false,
default: () => PAGE_CONFIG.OPERATIONS.STATUSES,
},
},
computed: {
dropdownClass() {
@ -57,13 +58,13 @@ export default {
mutation: updateAlertStatusMutation,
variables: {
iid: this.alert.iid,
status: status.toUpperCase(),
status,
projectPath: this.projectPath,
},
})
.then((resp) => {
if (this.trackAlertStatusUpdateOptions) {
this.trackStatusUpdate(status);
this.trackStatusUpdate(this.statuses[status]);
}
const errors = resp.data?.updateAlertStatus?.errors || [];
@ -99,7 +100,7 @@ export default {
<gl-dropdown
ref="dropdown"
right
:text="$options.statuses[alert.status]"
:text="statuses[alert.status]"
class="w-100"
toggle-class="dropdown-menu-toggle"
@keydown.esc.native="$emit('hide-dropdown')"
@ -110,12 +111,12 @@ export default {
</p>
<div class="dropdown-content dropdown-body">
<gl-dropdown-item
v-for="(label, field) in $options.statuses"
v-for="(label, field) in statuses"
:key="field"
data-testid="statusDropdownItem"
:active="label.toUpperCase() === alert.status"
:active="field === alert.status"
:active-class="'is-active'"
@click="updateAlertStatus(label)"
@click="updateAlertStatus(field)"
>
{{ label }}
</gl-dropdown-item>

View File

@ -1,14 +1,9 @@
<script>
import { GlIcon, GlLoadingIcon, GlTooltip, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import { PAGE_CONFIG } from '../../constants';
import AlertStatus from '../alert_status.vue';
export default {
statuses: {
TRIGGERED: s__('AlertManagement|Triggered'),
ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
RESOLVED: s__('AlertManagement|Resolved'),
},
components: {
GlIcon,
GlLoadingIcon,
@ -16,6 +11,11 @@ export default {
GlSprintf,
AlertStatus,
},
inject: {
statuses: {
default: PAGE_CONFIG.OPERATIONS.STATUSES,
},
},
props: {
projectPath: {
type: String,
@ -94,6 +94,7 @@ export default {
:project-path="projectPath"
:is-dropdown-showing="isDropdownShowing"
:is-sidebar="true"
:statuses="statuses"
@alert-error="$emit('alert-error', $event)"
@hide-dropdown="hideDropdown"
@handle-updating="handleUpdating"
@ -103,14 +104,11 @@ export default {
<p
v-else-if="!isDropdownShowing"
class="value gl-m-0"
:class="{ 'no-value': !$options.statuses[alert.status] }"
:class="{ 'no-value': !statuses[alert.status] }"
>
<span
v-if="$options.statuses[alert.status]"
class="gl-text-gray-500"
data-testid="status"
>{{ $options.statuses[alert.status] }}</span
>
<span v-if="statuses[alert.status]" class="gl-text-gray-500" data-testid="status">
{{ statuses[alert.status] }}
</span>
<span v-else>
{{ s__('AlertManagement|None') }}
</span>

View File

@ -13,6 +13,11 @@ export const SEVERITY_LEVELS = {
export const PAGE_CONFIG = {
OPERATIONS: {
TITLE: 'OPERATIONS',
STATUSES: {
TRIGGERED: s__('AlertManagement|Triggered'),
ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
RESOLVED: s__('AlertManagement|Resolved'),
},
// Tracks snowplow event when user views alert details
TRACK_ALERTS_DETAILS_VIEWS_OPTIONS: {
category: 'Alert Management',
@ -27,5 +32,11 @@ export const PAGE_CONFIG = {
},
THREAT_MONITORING: {
TITLE: 'THREAT_MONITORING',
STATUSES: {
TRIGGERED: s__('ThreatMonitoring|Unreviewed'),
ACKNOWLEDGED: s__('ThreatMonitoring|In review'),
RESOLVED: s__('ThreatMonitoring|Resolved'),
IGNORED: s__('ThreatMonitoring|Dismissed'),
},
},
};

View File

@ -55,6 +55,7 @@ export default (selector) => {
page,
projectIssuesPath,
projectId,
statuses: PAGE_CONFIG[page].STATUSES,
};
if (page === PAGE_CONFIG.OPERATIONS.TITLE) {

View File

@ -7,6 +7,7 @@ import {
splitCamelCase,
} from '~/lib/utils/text_utility';
import { s__ } from '~/locale';
import { PAGE_CONFIG } from '~/vue_shared/alert_details/constants';
const thClass = 'gl-bg-transparent! gl-border-1! gl-border-b-solid! gl-border-gray-200!';
const tdClass = 'gl-border-gray-100! gl-p-5!';
@ -42,6 +43,11 @@ export default {
type: Boolean,
required: true,
},
statuses: {
type: Object,
required: false,
default: () => PAGE_CONFIG.OPERATIONS.STATUSES,
},
},
fields: [
{
@ -71,6 +77,8 @@ export default {
let value;
if (fieldName === 'environment') {
value = fieldValue?.name;
} else if (fieldName === 'status') {
value = this.statuses[fieldValue] || fieldValue;
} else {
value = fieldValue;
}

View File

@ -26,7 +26,7 @@ export default {
<template>
<span>
<gl-button ref="popoverTrigger" variant="link" icon="question" tabindex="0" />
<gl-popover triggers="hover focus" :target="() => $refs.popoverTrigger.$el" v-bind="options">
<gl-popover :target="() => $refs.popoverTrigger.$el" v-bind="options">
<template v-if="options.title" #title>
<span v-safe-html="options.title"></span>
</template>

View File

@ -60,7 +60,7 @@ export default {
<template>
<!-- 200ms delay so not every mouseover triggers Popover -->
<gl-popover :target="target" :delay="200" boundary="viewport" triggers="hover" placement="top">
<gl-popover :target="target" :delay="200" boundary="viewport" placement="top">
<div class="gl-p-3 gl-line-height-normal gl-display-flex" data-testid="user-popover">
<div class="gl-p-2 flex-shrink-1">
<user-avatar-image :img-src="user.avatarUrl" :size="60" css-classes="gl-mr-3!" />

View File

@ -226,10 +226,10 @@ class Projects::JobsController < Projects::ApplicationController
end
def raw_trace_content_disposition(raw_data)
mime_type = MimeMagic.by_magic(raw_data)
mime_type = Gitlab::Utils::MimeType.from_string(raw_data)
# if mime_type is nil can also represent 'text/plain'
return 'inline' if mime_type.nil? || mime_type.type == 'text/plain'
return 'inline' if mime_type.nil? || mime_type == 'text/plain'
'attachment'
end

View File

@ -14,8 +14,6 @@ module Ci
BuildArchivedError = Class.new(StandardError)
ignore_columns :artifacts_file, :artifacts_file_store, :artifacts_metadata, :artifacts_metadata_store, :artifacts_size, :commands, remove_after: '2019-12-15', remove_with: '12.7'
belongs_to :project, inverse_of: :builds
belongs_to :runner
belongs_to :trigger_request

View File

@ -45,8 +45,6 @@ module Ci
FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze
MINUTES_COST_FACTOR_FIELDS = %i[public_projects_minutes_cost_factor private_projects_minutes_cost_factor].freeze
ignore_column :is_shared, remove_after: '2019-12-15', remove_with: '12.6'
has_many :builds
has_many :runner_projects, inverse_of: :runner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :projects, through: :runner_projects

View File

@ -13,8 +13,6 @@ class DeployKey < Key
scope :are_public, -> { where(public: true) }
scope :with_projects, -> { includes(deploy_keys_projects: { project: [:route, namespace: :route] }) }
ignore_column :can_push, remove_after: '2019-12-15', remove_with: '12.6'
accepts_nested_attributes_for :deploy_keys_projects
def private?

View File

@ -43,7 +43,7 @@ module ContentTypeWhitelist
def mime_magic_content_type(path)
if path
File.open(path) do |file|
MimeMagic.by_magic(file).try(:type) || 'invalid/invalid'
Gitlab::Utils::MimeType.from_io(file) || 'invalid/invalid'
end
end
rescue Errno::ENOENT

View File

@ -0,0 +1,5 @@
---
title: Generalize alert details status
merge_request: 56800
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Show popovers on hover and focus by default
merge_request: 56778
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Refactor MimeMagic calls to new MimeType class
merge_request: 57421
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Remove direct mimemagic dependency
merge_request: 57387
author:
type: other

View File

@ -1148,7 +1148,7 @@ POST /projects
| `namespace_id` | integer | **{dotted-circle}** No | Namespace for the new project (defaults to the current user's namespace). |
| `operations_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged when all the discussions are resolved. |
| `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful jobs. |
| `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful pipelines. This setting is named [**Pipelines must succeed**](../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds) in the project settings. |
| `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. |
| `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. |
| `requirements_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled` or `public` |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -172,7 +172,7 @@ Add a variable name (key) and value here to override the value defined in
[the UI or `.gitlab-ci.yml`](../variables/README.md#custom-cicd-variables),
for a single run of the manual job.
![Manual job variables](img/manual_job_variables.png)
![Manual job variables](img/manual_job_variables_v13_10.png)
## Delay a job
@ -200,10 +200,10 @@ the duration.
In the following example:
- Two sections are collapsed and can be expanded.
- Three sections are collapsed and can be expanded.
- Three sections are expanded and can be collapsed.
![Collapsible sections](img/collapsible_log_v12_6.png)
![Collapsible sections](img/collapsible_log_v13_10.png)
### Custom collapsible sections

View File

@ -76,7 +76,7 @@ branch in the project.
GitLab CI/CD not only executes the jobs but also shows you what's happening during execution,
just as you would see in your terminal:
![job running](img/job_running.png)
![job running](img/job_running_v13_10.png)
You create the strategy for your app and GitLab runs the pipeline
according to what you've defined. Your pipeline status is also

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -8,9 +8,8 @@ type: reference
# Continuous Integration and Deployment Admin settings **(FREE SELF)**
In this area, you will find settings for Auto DevOps, runners, and job artifacts.
You can find it in the **Admin Area > Settings > CI/CD**.
![Admin Area settings button](../img/admin_area_settings_button.png)
You can find it in the [Admin Area](index.md) by navigating to
**Admin Area > Settings > CI/CD**.
## Auto DevOps **(FREE SELF)**

View File

@ -133,18 +133,6 @@ The results are saved as a
that you can later download and analyze. Due to implementation limitations, we
always take the latest Secret Detection artifact available.
### Post-processing
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4639) in GitLab 13.6.
Upon detection of a secret, GitLab supports post processing hooks. These can be used to take actions like notifying the cloud service who issued the secret. The cloud provider can confirm the credentials and take remediation actions like revoking or reissuing a new secret and notifying the creator of the secret. Post-processing workflows vary by supported cloud providers.
GitLab currently supports post-processing for following service providers:
- Amazon Web Services (AWS)
Third party cloud and SaaS providers can [express integration interest by filling out this form](https://forms.gle/wWpvrtLRK21Q2WJL9). Learn more about the [technical details of post-processing secrets](https://gitlab.com/groups/gitlab-org/-/epics/4639).
### Customizing settings
The Secret Detection scan settings can be changed through [CI/CD variables](#available-variables)
@ -249,6 +237,34 @@ From highest to lowest severity, the logging levels are:
- `info` (default)
- `debug`
## Post-processing and revocation
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4639) in GitLab 13.6.
Upon detection of a secret, GitLab supports post-processing hooks. These can be used to take actions like notifying the cloud service who issued the secret. The cloud provider can confirm the credentials and take remediation actions like revoking or reissuing a new secret and notifying the creator of the secret. Post-processing workflows vary by supported cloud providers.
GitLab currently supports post-processing for following service providers:
- Amazon Web Services (AWS)
Third party cloud and SaaS providers can [express integration interest by filling out this form](https://forms.gle/wWpvrtLRK21Q2WJL9). Learn more about the [technical details of post-processing secrets](https://gitlab.com/groups/gitlab-org/-/epics/4639).
NOTE:
Post-processing is currently limited to a project's default branch, see the above epic for future efforts to support additional branches.
```mermaid
sequenceDiagram
autonumber
Rails->>+Sidekiq: gl-secret-detection-report.json
Sidekiq-->+Sidekiq: BuildFinishedWorker
Sidekiq-->+RevocationAPI: GET revocable keys types
RevocationAPI-->>-Sidekiq: OK
Sidekiq->>+RevocationAPI: POST revoke revocable keys
RevocationAPI-->>-Sidekiq: ACCEPTED
RevocationAPI-->>+Cloud Vendor: revoke revocable keys
Cloud Vendor-->>+RevocationAPI: ACCEPTED
```
## Full History Secret Scan
GitLab 12.11 introduced support for scanning the full history of a repository. This new functionality

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -73,12 +73,13 @@ CSV file containing details of the resources scanned.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235558) in GitLab 13.6.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285476) in GitLab 13.10, options to zoom in on a date range, and download the vulnerabilities chart.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285477) in GitLab 13.11, date range slider to visualise data between given dates.
At the project level, the Security Dashboard displays a chart with the number of vulnerabilities over time.
Access it by navigating to **Security & Compliance > Security Dashboard**. We display historical
data up to 365 days. The chart's data is updated daily.
![Project Security Dashboard](img/project_security_dashboard_chart_v13_10.png)
![Project Security Dashboard](img/project_security_dashboard_chart_v13_11.png)
Filter the historical data by clicking on the corresponding legend name. The image above, for example, shows
only the graph for vulnerabilities with **high** severity.

View File

@ -62,7 +62,7 @@ module API
authorize_upload!(project)
bad_request!('File is too large') if max_file_size_exceeded?
track_event('push_package')
::Gitlab::Tracking.event(self.options[:for].name, 'push_package')
create_package_file_params = declared_params.merge(build: current_authenticated_job)
::Packages::Generic::CreatePackageFileService
@ -94,7 +94,7 @@ module API
package = ::Packages::Generic::PackageFinder.new(project).execute!(params[:package_name], params[:package_version])
package_file = ::Packages::PackageFileFinder.new(package, params[:file_name]).execute!
track_event('pull_package')
::Gitlab::Tracking.event(self.options[:for].name, 'pull_package')
present_carrierwave_file!(package_file.file)
end

View File

@ -544,17 +544,6 @@ module API
end
end
def track_event(action = action_name, **args)
category = args.delete(:category) || self.options[:for].name
raise "invalid category" unless category
::Gitlab::Tracking.event(category, action.to_s, **args)
rescue => error
Gitlab::AppLogger.warn(
"Tracking event failed for action: #{action}, category: #{category}, message: #{error.message}"
)
end
def increment_counter(event_name)
feature_name = "usage_data_#{event_name}"
return unless Feature.enabled?(feature_name)

View File

@ -10,7 +10,7 @@ module API
def redirect_registry_request(forward_to_registry, package_type, options)
if forward_to_registry && redirect_registry_request_available?
track_event("#{package_type}_request_forward")
::Gitlab::Tracking.event(self.options[:for].name, "#{package_type}_request_forward")
redirect(registry_url(package_type, options))
else
yield

View File

@ -50,7 +50,8 @@ module API
def track_package_event(event_name, scope, **args)
::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute
track_event(event_name, **args)
category = args.delete(:category) || self.options[:for].name
::Gitlab::Tracking.event(category, event_name.to_s, **args)
end
end
end

View File

@ -85,7 +85,6 @@ module Gitlab
id: runner.id,
description: runner.description,
active: runner.active?,
is_shared: runner.instance_type?,
tags: runner.tags&.map(&:name)
}
end

View File

@ -77,7 +77,6 @@ module Gitlab
id: runner.id,
description: runner.description,
active: runner.active?,
is_shared: runner.instance_type?,
tags: runner.tags&.map(&:name)
}
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'magic'
# This wraps calls to a gem which support mime type detection.
# We use the `ruby-magic` gem instead of `mimemagic` due to licensing issues
module Gitlab
module Utils
class MimeType
class << self
def from_io(io)
return unless io.is_a?(IO) || io.is_a?(StringIO)
mime_type = File.magic(io, Magic::MIME_TYPE)
mime_type == 'inode/x-empty' ? nil : mime_type
end
def from_string(string)
return unless string.is_a?(String)
string.type
end
end
end
end
end

View File

@ -59,7 +59,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.185.0",
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "28.9.1",
"@gitlab/ui": "28.15.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4",

View File

@ -333,6 +333,31 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
end
end
context 'when the latest pipeline is running in the parent project' do
before do
Ci::CreatePipelineService.new(project, user, ref: 'feature')
.execute(:merge_request_event, merge_request: merge_request)
end
context 'when the previous pipeline failed in the fork project' do
before do
detached_merge_request_pipeline.drop!
end
context 'when the parent project enables pipeline must succeed' do
before do
project.update!(only_allow_merge_if_pipeline_succeeds: true)
end
it 'shows MWPS button' do
visit project_merge_request_path(project, merge_request)
expect(page).to have_button('Merge when pipeline succeeds')
end
end
end
end
context 'when a user merges a merge request from a forked project to the parent project' do
before do
click_link("Overview")

BIN
spec/fixtures/rails_sample.bmp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

BIN
spec/fixtures/rails_sample.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
spec/fixtures/rails_sample.tif vendored Normal file

Binary file not shown.

BIN
spec/fixtures/sample.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -41,7 +41,6 @@ describe('feature_highlight/feature_highlight_popover', () => {
expect(findPopover().props()).toMatchObject({
target: POPOVER_TARGET_ID,
cssClasses: ['feature-highlight-popover'],
triggers: 'hover',
container: 'body',
placement: 'right',
boundary: 'viewport',

View File

@ -52,7 +52,9 @@ exports[`packages_list_app renders 1`] = `
with GitLab.
</p>
<div>
<div
class="gl-display-flex gl-flex-wrap gl-justify-content-center"
>
<!---->
<!---->

View File

@ -10,8 +10,8 @@ exports[`EmptyStateComponent should render content 1`] = `
<h1 class=\\"h4\\">Getting started with serverless</h1>
<p>In order to start using functions as a service, you must first install Knative on your Kubernetes cluster. <gl-link-stub href=\\"/help\\">More information</gl-link-stub>
</p>
<div>
<gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" href=\\"/clusters\\">Install Knative</gl-button-stub>
<div class=\\"gl-display-flex gl-flex-wrap gl-justify-content-center\\">
<gl-button-stub category=\\"primary\\" variant=\\"confirm\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" href=\\"/clusters\\" class=\\"gl-mb-3 gl-mx-2\\">Install Knative</gl-button-stub>
<!---->
</div>
</div>

View File

@ -8,7 +8,7 @@ import { joinPaths } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
import AlertDetails from '~/vue_shared/alert_details/components/alert_details.vue';
import AlertSummaryRow from '~/vue_shared/alert_details/components/alert_summary_row.vue';
import { SEVERITY_LEVELS } from '~/vue_shared/alert_details/constants';
import { PAGE_CONFIG, SEVERITY_LEVELS } from '~/vue_shared/alert_details/constants';
import createIssueMutation from '~/vue_shared/alert_details/graphql/mutations/alert_issue_create.mutation.graphql';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import mockAlerts from './mocks/alerts.json';
@ -271,7 +271,13 @@ describe('AlertDetails', () => {
});
it('should display a table of raw alert details data', () => {
expect(findDetailsTable().exists()).toBe(true);
const details = findDetailsTable();
expect(details.exists()).toBe(true);
expect(details.props()).toStrictEqual({
alert: mockAlert,
statuses: PAGE_CONFIG.OPERATIONS.STATUSES,
loading: false,
});
});
});

View File

@ -12,6 +12,7 @@ describe('AlertManagementStatus', () => {
let wrapper;
const findStatusDropdown = () => wrapper.find(GlDropdown);
const findFirstStatusOption = () => findStatusDropdown().find(GlDropdownItem);
const findAllStatusOptions = () => findStatusDropdown().findAll(GlDropdownItem);
const selectFirstStatusOption = () => {
findFirstStatusOption().vm.$emit('click');
@ -131,6 +132,24 @@ describe('AlertManagementStatus', () => {
});
});
describe('Statuses', () => {
it('renders default translated statuses', () => {
mountComponent({});
expect(findAllStatusOptions().length).toBe(3);
expect(findFirstStatusOption().text()).toBe('Triggered');
});
it('renders translated statuses', () => {
const status = 'TEST';
const translatedStatus = 'Test';
mountComponent({
props: { alert: { ...mockAlert, status }, statuses: { [status]: translatedStatus } },
});
expect(findAllStatusOptions().length).toBe(1);
expect(findFirstStatusOption().text()).toBe(translatedStatus);
});
});
describe('Snowplow tracking', () => {
beforeEach(() => {
jest.spyOn(Tracking, 'event');

View File

@ -76,20 +76,4 @@ describe('Alert Details Sidebar', () => {
expect(wrapper.find(SidebarStatus).exists()).toBe(true);
});
});
describe('the sidebar renders for threat monitoring', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mountComponent();
});
it('should not render side bar status dropdown', () => {
mountComponent({
mountMethod: mount,
alert: mockAlert,
provide: { isThreatMonitoringPage: true },
});
expect(wrapper.find(SidebarStatus).exists()).toBe(false);
});
});
});

View File

@ -1,7 +1,9 @@
import { GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import updateAlertStatusMutation from '~/graphql_shared/mutations/alert_status_update.mutation.graphql';
import AlertStatus from '~/vue_shared/alert_details/components/alert_status.vue';
import AlertSidebarStatus from '~/vue_shared/alert_details/components/sidebar/sidebar_status.vue';
import { PAGE_CONFIG } from '~/vue_shared/alert_details/constants';
import mockAlerts from '../mocks/alerts.json';
const mockAlert = mockAlerts[0];
@ -12,8 +14,16 @@ describe('Alert Details Sidebar Status', () => {
const findStatusDropdownItem = () => wrapper.find(GlDropdownItem);
const findStatusLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findStatusDropdownHeader = () => wrapper.find('[data-testid="dropdown-header"]');
const findAlertStatus = () => wrapper.findComponent(AlertStatus);
const findStatus = () => wrapper.find('[data-testid="status"]');
function mountComponent({ data, sidebarCollapsed = true, loading = false, stubs = {} } = {}) {
function mountComponent({
data,
sidebarCollapsed = true,
loading = false,
stubs = {},
provide = {},
} = {}) {
wrapper = mount(AlertSidebarStatus, {
propsData: {
alert: { ...mockAlert },
@ -32,6 +42,7 @@ describe('Alert Details Sidebar Status', () => {
},
},
stubs,
provide,
});
}
@ -96,8 +107,24 @@ describe('Alert Details Sidebar Status', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error()));
findStatusDropdownItem().vm.$emit('click');
expect(findStatusLoadingIcon().exists()).toBe(false);
expect(wrapper.find('[data-testid="status"]').text()).toBe('Triggered');
expect(findStatus().text()).toBe('Triggered');
});
});
});
describe('Statuses', () => {
it('renders default translated statuses', () => {
mountComponent({});
expect(findAlertStatus().props('statuses')).toBe(PAGE_CONFIG.OPERATIONS.STATUSES);
expect(findStatus().text()).toBe('Triggered');
});
it('renders translated statuses', () => {
const status = 'TEST';
const statuses = { [status]: 'Test' };
mountComponent({ data: { alert: { ...mockAlert, status } }, provide: { statuses } });
expect(findAlertStatus().props('statuses')).toBe(statuses);
expect(findStatus().text()).toBe(statuses.TEST);
});
});
});

View File

@ -75,45 +75,62 @@ describe('AlertDetails', () => {
});
describe('with table data', () => {
beforeEach(mountComponent);
describe('default', () => {
beforeEach(mountComponent);
it('renders a table', () => {
expect(findTableComponent().exists()).toBe(true);
it('renders a table', () => {
expect(findTableComponent().exists()).toBe(true);
});
it('renders a cell based on alert data', () => {
expect(findTableComponent().text()).toContain('SyntaxError: Invalid or unexpected token');
});
it('should show allowed alert fields', () => {
const fields = findTableKeys();
['Iid', 'Title', 'Severity', 'Status', 'Hosts', 'Environment'].forEach((field) => {
expect(findTableField(fields, field).exists()).toBe(true);
});
});
it('should not show disallowed alert fields', () => {
const fields = findTableKeys();
['Typename', 'Todos', 'Notes', 'Assignees'].forEach((field) => {
expect(findTableField(fields, field).exists()).toBe(false);
});
});
});
it('renders a cell based on alert data', () => {
expect(findTableComponent().text()).toContain('SyntaxError: Invalid or unexpected token');
describe('environment', () => {
it('should display only the name for the environment', () => {
mountComponent();
expect(findTableFieldValueByKey('Environment').text()).toBe(environmentName);
});
it('should not display the environment row if there is not data', () => {
environmentData = { name: null, path: null };
mountComponent();
expect(findTableFieldValueByKey('Environment').text()).toBeFalsy();
});
});
it('should show allowed alert fields', () => {
const fields = findTableKeys();
describe('status', () => {
it('should show the translated status for the default statuses', () => {
mountComponent();
expect(findTableFieldValueByKey('Status').text()).toBe('Triggered');
});
expect(findTableField(fields, 'Iid').exists()).toBe(true);
expect(findTableField(fields, 'Title').exists()).toBe(true);
expect(findTableField(fields, 'Severity').exists()).toBe(true);
expect(findTableField(fields, 'Status').exists()).toBe(true);
expect(findTableField(fields, 'Hosts').exists()).toBe(true);
expect(findTableField(fields, 'Environment').exists()).toBe(true);
});
it('should show the translated status for provided statuses', () => {
const translatedStatus = 'Test';
mountComponent({ statuses: { TRIGGERED: translatedStatus } });
expect(findTableFieldValueByKey('Status').text()).toBe(translatedStatus);
});
it('should not show disallowed alert fields', () => {
const fields = findTableKeys();
expect(findTableField(fields, 'Typename').exists()).toBe(false);
expect(findTableField(fields, 'Todos').exists()).toBe(false);
expect(findTableField(fields, 'Notes').exists()).toBe(false);
expect(findTableField(fields, 'Assignees').exists()).toBe(false);
});
it('should display only the name for the environment', () => {
expect(findTableFieldValueByKey('Environment').text()).toBe(environmentName);
});
it('should not display the environment row if there is not data', () => {
environmentData = { name: null, path: null };
mountComponent();
expect(findTableFieldValueByKey('Environment').text()).toBeFalsy();
it('should show the provided status if value is not defined in statuses', () => {
mountComponent({ statuses: {} });
expect(findTableFieldValueByKey('Status').text()).toBe('TRIGGERED');
});
});
});
});

View File

@ -41,10 +41,6 @@ describe('HelpPopover', () => {
expect(findPopover().props().target()).toBe(findQuestionButton().vm.$el);
});
it('triggers popover on hover and focus', () => {
expect(findPopover().props().triggers).toBe('hover focus');
});
it('allows rendering title with HTML tags', () => {
expect(findPopover().find('strong').exists()).toBe(true);
});

View File

@ -12,6 +12,10 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
subject { helper.redirect_registry_request(forward_to_registry, package_type, options) { helper.fallback } }
before do
allow(helper).to receive(:options).and_return(for: API::NpmInstancePackages)
end
shared_examples 'executing fallback' do
it 'redirects to package registry' do
expect(helper).to receive(:registry_url).never
@ -23,13 +27,14 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
end
shared_examples 'executing redirect' do
it 'redirects to package registry' do
expect(helper).to receive(:track_event).with('npm_request_forward').once
it 'redirects to package registry', :snowplow do
expect(helper).to receive(:registry_url).once
expect(helper).to receive(:redirect).once
expect(helper).to receive(:fallback).never
subject
expect_snowplow_event(category: 'API::NpmInstancePackages', action: 'npm_request_forward')
end
end
@ -64,7 +69,6 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
let(:package_type) { pkg_type }
it 'raises an error' do
allow(helper).to receive(:track_event)
expect { subject }.to raise_error(ArgumentError, "Can't build registry_url for package_type #{package_type}")
end
end

View File

@ -175,20 +175,6 @@ RSpec.describe API::Helpers do
end
end
describe '#track_event' do
it "creates a gitlab tracking event", :snowplow do
subject.track_event('my_event', category: 'foo')
expect_snowplow_event(category: 'foo', action: 'my_event')
end
it "logs an exception" do
expect(Gitlab::AppLogger).to receive(:warn).with(/Tracking event failed/)
subject.track_event('my_event', category: nil)
end
end
describe '#increment_unique_values' do
let(:value) { '9f302fea-f828-4ca9-aef4-e10bd723c0b3' }
let(:event_name) { 'g_compliance_dashboard' }

View File

@ -59,7 +59,6 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
expect(runner_data[:id]).to eq(ci_runner.id)
expect(runner_data[:description]).to eq(ci_runner.description)
expect(runner_data[:active]).to eq(ci_runner.active)
expect(runner_data[:is_shared]).to eq(ci_runner.instance_type?)
expect(runner_data[:tags]).to match_array(tag_names)
end
end

View File

@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::SQL::CTE do
describe '#to_arel' do
it 'generates an Arel relation for the CTE body' do
it 'generates an Arel relation for the CTE body', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/325916' do
relation = User.where(id: 1)
cte = described_class.new(:cte_name, relation)
sql = cte.to_arel.to_sql

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
require "fast_spec_helper"
require "rspec/parameterized"
RSpec.describe Gitlab::Utils::MimeType do
describe ".from_io" do
subject { described_class.from_io(io) }
context "input isn't an IO" do
let(:io) { "test" }
it "returns nil" do
expect(subject).to be_nil
end
end
context "input is a file" do
using RSpec::Parameterized::TableSyntax
where(:fixture, :mime_type) do
"banana_sample.gif" | "image/gif"
"rails_sample.jpg" | "image/jpeg"
"rails_sample.png" | "image/png"
"rails_sample.bmp" | "image/bmp"
"rails_sample.tif" | "image/tiff"
"sample.ico" | "image/vnd.microsoft.icon"
"blockquote_fence_before.md" | "text/plain"
"csv_empty.csv" | "application/x-empty"
end
with_them do
let(:io) { File.open(File.join(__dir__, "../../../fixtures", fixture)) }
it { is_expected.to eq(mime_type) }
end
end
end
describe ".from_string" do
subject { described_class.from_string(str) }
context "input isn't a string" do
let(:str) { nil }
it "returns nil" do
expect(subject).to be_nil
end
end
context "input is a string" do
let(:str) { "plain text" }
it { is_expected.to eq('text/plain') }
end
end
end

View File

@ -13,7 +13,6 @@ end
# @param mime_type [String] mime type to forcibly detect.
RSpec.shared_context 'force content type detection to mime_type' do
before do
magic_mime_obj = MimeMagic.new(mime_type)
allow(MimeMagic).to receive(:by_magic).with(anything).and_return(magic_mime_obj)
allow(Gitlab::Utils::MimeType).to receive(:from_io).and_return(mime_type)
end
end

View File

@ -907,16 +907,16 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@28.9.1":
version "28.9.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-28.9.1.tgz#7d4d4502ff09fca19ab815504f80afbf03dd2fc1"
integrity sha512-+JqkpwzkKBnxo4KkC8XSPEJ5Au9y+TIOE7w9I5o+04krgWCbZKNqaiKZkg2IqSlo/sZSfvihXZMhEVc/JXf7HQ==
"@gitlab/ui@28.15.0":
version "28.15.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-28.15.0.tgz#e78a1c0724c7cc8880fcff8161e529ca7bcaf6e8"
integrity sha512-muz1tX3nQmu9dMv7GbTNIkWkEwYhvnLPhtwtnrt8eyRGQ0zIUWLEzdoSiwvMNLAqT2JB8kxahoavR5iSFAYtXA==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"
bootstrap-vue "2.13.1"
copy-to-clipboard "^3.0.8"
dompurify "^2.2.6"
dompurify "^2.2.7"
echarts "^4.9.0"
highlight.js "^10.6.0"
js-beautify "^1.8.8"
@ -4270,7 +4270,7 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
dompurify@^2.2.6, dompurify@^2.2.7:
dompurify@^2.2.7:
version "2.2.7"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.2.7.tgz#a5f055a2a471638680e779bd08fc334962d11fd8"
integrity sha512-jdtDffdGNY+C76jvodNTu9jt5yYj59vuTUyx+wXdzcSwAGTYZDAQkQ7Iwx9zcGrA4ixC1syU4H3RZROqRxokxg==