Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-07-20 18:08:44 +00:00
parent 93b6ee78bf
commit 61b943c213
35 changed files with 383 additions and 192 deletions

View File

@ -1,9 +1,6 @@
---
# Cop supports --auto-correct.
RSpec/ScatteredLet:
# Offense count: 720
# Temporarily disabled due to too many offenses
Enabled: false
Exclude:
- 'ee/spec/features/groups/group_roadmap_spec.rb'
- 'ee/spec/features/merge_trains/two_merge_requests_on_train_spec.rb'
@ -19,10 +16,10 @@ RSpec/ScatteredLet:
- 'ee/spec/graphql/types/instance_security_dashboard_type_spec.rb'
- 'ee/spec/helpers/ee/subscribable_banner_helper_spec.rb'
- 'ee/spec/helpers/trial_status_widget_helper_spec.rb'
- 'ee/spec/lib/banzai/reference_parser/iteration_parser_spec.rb'
- 'ee/spec/lib/ee/audit/compliance_framework_changes_auditor_spec.rb'
- 'ee/spec/lib/ee/gitlab/ci/config_spec.rb'
- 'ee/spec/lib/ee/gitlab/email/handler/service_desk_handler_spec.rb'
- 'ee/spec/lib/gitlab/background_migration/migrate_requirements_to_work_items_spec.rb'
- 'ee/spec/lib/gitlab/ci/parsers/security/dast_spec.rb'
- 'ee/spec/lib/gitlab/ci/parsers/security/formatters/dependency_list_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/dast_api_gitlab_ci_yaml_spec.rb'
@ -62,9 +59,12 @@ RSpec/ScatteredLet:
- 'ee/spec/requests/api/graphql/project/pipelines/dast_profile_spec.rb'
- 'ee/spec/requests/api/internal/base_spec.rb'
- 'ee/spec/requests/api/projects_spec.rb'
- 'ee/spec/requests/api/settings_spec.rb'
- 'ee/spec/requests/api/vulnerability_findings_spec.rb'
- 'ee/spec/requests/git_http_geo_spec.rb'
- 'ee/spec/serializers/license_compliance/collapsed_comparer_entity_spec.rb'
- 'ee/spec/serializers/status_page/incident_serializer_spec.rb'
- 'ee/spec/services/app_sec/dast/scan_configs/fetch_service_spec.rb'
- 'ee/spec/services/app_sec/dast/scanner_profiles/update_service_spec.rb'
- 'ee/spec/services/arkose/blocked_users_report_service_spec.rb'
- 'ee/spec/services/arkose/user_verification_service_spec.rb'
@ -88,13 +88,13 @@ RSpec/ScatteredLet:
- 'ee/spec/services/requirements_management/update_requirement_service_spec.rb'
- 'ee/spec/services/search/group_service_spec.rb'
- 'ee/spec/services/search/project_service_spec.rb'
- 'ee/spec/services/security/ingestion/tasks/update_vulnerability_uuids_spec.rb'
- 'ee/spec/services/todo_service_spec.rb'
- 'ee/spec/views/shared/_mirror_update_button.html.haml_spec.rb'
- 'ee/spec/views/subscriptions/groups/edit.html.haml_spec.rb'
- 'ee/spec/workers/compliance_management/merge_requests/compliance_violations_worker_spec.rb'
- 'ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_audit_logs_1_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/project/project_audit_logs_spec.rb'
- 'spec/controllers/projects/artifacts_controller_spec.rb'
- 'spec/controllers/projects/deploy_keys_controller_spec.rb'
- 'spec/controllers/projects/environments_controller_spec.rb'
@ -126,14 +126,19 @@ RSpec/ScatteredLet:
- 'spec/lib/banzai/reference_parser/project_parser_spec.rb'
- 'spec/lib/banzai/reference_parser/snippet_parser_spec.rb'
- 'spec/lib/banzai/reference_parser/user_parser_spec.rb'
- 'spec/lib/bulk_imports/pipeline/runner_spec.rb'
- 'spec/lib/bulk_imports/projects/pipelines/snippets_repository_pipeline_spec.rb'
- 'spec/lib/gitlab/asciidoc/include_processor_spec.rb'
- 'spec/lib/gitlab/auth/ldap/person_spec.rb'
- 'spec/lib/gitlab/auth/saml/auth_hash_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb'
- 'spec/lib/gitlab/background_migration/copy_ci_builds_columns_to_security_scans_spec.rb'
- 'spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_inactive_public_projects_spec.rb'
- 'spec/lib/gitlab/background_migration/encrypt_static_object_token_spec.rb'
- 'spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb'
- 'spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb'
- 'spec/lib/gitlab/background_migration/reset_too_many_tags_skipped_registry_imports_spec.rb'
- 'spec/lib/gitlab/background_migration/set_legacy_open_source_license_available_for_non_public_projects_spec.rb'
- 'spec/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url_spec.rb'
- 'spec/lib/gitlab/ci/config/external/file/artifact_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb'
@ -162,6 +167,7 @@ RSpec/ScatteredLet:
- 'spec/lib/gitlab/git/blame_spec.rb'
- 'spec/lib/gitlab/git/diff_collection_spec.rb'
- 'spec/lib/gitlab/git_access_spec.rb'
- 'spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb'
- 'spec/lib/gitlab/github_import/parallel_scheduling_spec.rb'
- 'spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb'
- 'spec/lib/gitlab/import_export/project/export_task_spec.rb'
@ -182,7 +188,6 @@ RSpec/ScatteredLet:
- 'spec/lib/gitlab/spamcheck/client_spec.rb'
- 'spec/lib/gitlab/template/finders/global_template_finder_spec.rb'
- 'spec/lib/gitlab/tree_summary_spec.rb'
- 'spec/lib/gitlab/usage/service_ping_report_spec.rb'
- 'spec/lib/gitlab/usage_data_metrics_spec.rb'
- 'spec/lib/gitlab/utils/measuring_spec.rb'
- 'spec/lib/gitlab/zentao/client_spec.rb'
@ -236,6 +241,7 @@ RSpec/ScatteredLet:
- 'spec/requests/api/project_clusters_spec.rb'
- 'spec/requests/api/project_export_spec.rb'
- 'spec/requests/api/rubygem_packages_spec.rb'
- 'spec/requests/jira_routing_spec.rb'
- 'spec/requests/projects/releases_controller_spec.rb'
- 'spec/rubocop/cop/migration/update_column_in_batches_spec.rb'
- 'spec/scripts/pipeline_test_report_builder_spec.rb'
@ -277,6 +283,8 @@ RSpec/ScatteredLet:
- 'spec/services/system_notes/design_management_service_spec.rb'
- 'spec/services/system_notes/merge_requests_service_spec.rb'
- 'spec/services/todo_service_spec.rb'
- 'spec/services/web_hook_service_spec.rb'
- 'spec/services/work_items/update_service_spec.rb'
- 'spec/support/shared_examples/graphql/sorted_paginated_query_shared_examples.rb'
- 'spec/tasks/gitlab/artifacts/migrate_rake_spec.rb'
- 'spec/workers/concerns/gitlab/github_import/object_importer_spec.rb'

View File

@ -1 +1 @@
1c907781819bf8810e15578f3d4d2b25e3ca1053
f8e688fbf64938cf8563f765c040af39f33e0790

View File

@ -56,7 +56,7 @@ gem 'omniauth-authentiq', '~> 0.3.3'
gem 'gitlab-omniauth-openid-connect', '~> 0.9.0', require: 'omniauth_openid_connect'
gem 'omniauth-salesforce', '~> 1.0.5'
gem 'omniauth-atlassian-oauth2', '~> 0.2.0'
gem 'rack-oauth2', '~> 1.19.0'
gem 'rack-oauth2', '~> 1.21.2'
gem 'jwt', '~> 2.1.0'
# Kerberos authentication. EE-only
@ -187,7 +187,7 @@ gem 'rack', '~> 2.2.4'
gem 'rack-timeout', '~> 0.6.0', require: 'rack/timeout/base'
group :puma do
gem 'puma', '~> 5.6.2', require: false
gem 'puma', '~> 5.6.4', require: false
gem 'puma_worker_killer', '~> 0.3.1', require: false
gem 'sd_notify', '~> 0.1.0', require: false
end

View File

@ -1005,7 +1005,7 @@ GEM
tty-markdown
tty-prompt
public_suffix (4.0.7)
puma (5.6.2)
puma (5.6.4)
nio4r (~> 2.0)
puma_worker_killer (0.3.1)
get_process_mem (~> 0.2)
@ -1020,7 +1020,7 @@ GEM
rack (>= 1.0, < 3)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-oauth2 (1.19.0)
rack-oauth2 (1.21.2)
activesupport
attr_required
httpclient
@ -1670,12 +1670,12 @@ DEPENDENCIES
pry-byebug
pry-rails (~> 0.3.9)
pry-shell (~> 0.5.0)
puma (~> 5.6.2)
puma (~> 5.6.4)
puma_worker_killer (~> 0.3.1)
rack (~> 2.2.4)
rack-attack (~> 6.6.0)
rack-cors (~> 1.1.0)
rack-oauth2 (~> 1.19.0)
rack-oauth2 (~> 1.21.2)
rack-proxy (~> 0.7.2)
rack-timeout (~> 0.6.0)
rails (~> 6.1.4.7)

View File

@ -1,5 +1,5 @@
<script>
import { GlLink, GlTableLite, GlDropdownItem, GlDropdown, GlIcon, GlButton } from '@gitlab/ui';
import { GlLink, GlTableLite, GlDropdownItem, GlDropdown, GlButton } from '@gitlab/ui';
import { last } from 'lodash';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
@ -18,7 +18,6 @@ export default {
components: {
GlLink,
GlTableLite,
GlIcon,
GlDropdown,
GlDropdownItem,
GlButton,
@ -77,7 +76,7 @@ export default {
label: '',
hide: !this.canDelete,
class: 'gl-text-right',
tdClass: 'gl-w-4',
tdClass: 'gl-w-4 gl-pt-3!',
},
].filter((c) => !c.hide);
},
@ -102,6 +101,7 @@ export default {
},
i18n: {
deleteFile: __('Delete file'),
moreActionsText: __('More actions'),
},
};
</script>
@ -156,10 +156,14 @@ export default {
</template>
<template #cell(actions)="{ item }">
<gl-dropdown category="tertiary" right>
<template #button-content>
<gl-icon name="ellipsis_v" />
</template>
<gl-dropdown
category="tertiary"
icon="ellipsis_v"
:text-sr-only="true"
:text="$options.i18n.moreActionsText"
no-caret
right
>
<gl-dropdown-item data-testid="delete-file" @click="$emit('delete-file', item)">
{{ $options.i18n.deleteFile }}
</gl-dropdown-item>

View File

@ -7,6 +7,10 @@ import { BACK_URL_PARAM } from '~/releases/constants';
export default {
i18n: {
editButton: __('Edit this release'),
historical: __('Historical release'),
historicalTooltip: __(
'This release was created with a date in the past. Evidence collection at the moment of the release is unavailable.',
),
},
name: 'ReleaseBlockHeader',
components: {
@ -65,6 +69,14 @@ export default {
<gl-badge v-if="release.upcomingRelease" variant="warning" class="align-middle">{{
__('Upcoming Release')
}}</gl-badge>
<gl-badge
v-else-if="release.historicalRelease"
v-gl-tooltip
:title="$options.i18n.historicalTooltip"
class="gl-vertical-align-middle"
>
{{ $options.i18n.historical }}
</gl-badge>
</h2>
<gl-button
v-if="editLink"

View File

@ -8,6 +8,7 @@ fragment Release on Release {
releasedAt
createdAt
upcomingRelease
historicalRelease
assets {
__typename
count

View File

@ -1,3 +1,5 @@
#import "../fragments/release.fragment.graphql"
query allReleases(
$fullPath: ID!
$first: Int
@ -12,88 +14,7 @@ query allReleases(
releases(first: $first, last: $last, before: $before, after: $after, sort: $sort) {
__typename
nodes {
__typename
id
name
tagName
tagPath
descriptionHtml
releasedAt
createdAt
upcomingRelease
assets {
__typename
count
sources {
__typename
nodes {
__typename
format
url
}
}
links {
__typename
nodes {
__typename
id
name
url
directAssetUrl
linkType
external
}
}
}
evidences {
__typename
nodes {
__typename
id
filepath
collectedAt
sha
}
}
links {
__typename
editUrl
selfUrl
openedIssuesUrl
closedIssuesUrl
openedMergeRequestsUrl
mergedMergeRequestsUrl
closedMergeRequestsUrl
}
commit {
__typename
id
sha
webUrl
title
}
author {
__typename
id
webUrl
avatarUrl
username
}
milestones {
__typename
nodes {
__typename
id
title
description
webPath
stats {
__typename
totalIssuesCount
closedIssuesCount
}
}
}
...Release
}
pageInfo {
__typename

View File

@ -12,6 +12,7 @@ const convertScalarProperties = (graphQLRelease) =>
'description',
'descriptionHtml',
'upcomingRelease',
'historicalRelease',
]);
const convertDateProperties = ({ releasedAt }) => ({

View File

@ -114,7 +114,7 @@ export default {
return this.workItem?.mockWidgets?.find((widget) => widget.type === WIDGET_TYPE_LABELS);
},
workItemWeight() {
return this.workItem?.mockWidgets?.find((widget) => widget.type === WIDGET_TYPE_WEIGHT);
return this.workItem?.widgets?.find((widget) => widget.type === WIDGET_TYPE_WEIGHT);
},
workItemHierarchy() {
return this.workItem?.widgets?.find((widget) => widget.type === WIDGET_TYPE_HIERARCHY);
@ -142,7 +142,7 @@ export default {
<template>
<section class="gl-pt-5">
<gl-alert v-if="error" variant="danger" @dismiss="error = undefined">
<gl-alert v-if="error" class="gl-mb-3" variant="danger" @dismiss="error = undefined">
{{ error }}
</gl-alert>
@ -236,6 +236,7 @@ export default {
:weight="workItemWeight.weight"
:work-item-id="workItem.id"
:work-item-type="workItemType"
@error="error = $event"
/>
</template>
<work-item-description

View File

@ -1,9 +1,10 @@
<script>
import { GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { __ } from '~/locale';
import Tracking from '~/tracking';
import { TRACKING_CATEGORY_SHOW } from '../constants';
import localUpdateWorkItemMutation from '../graphql/local_update_work_item.mutation.graphql';
import { i18n, TRACKING_CATEGORY_SHOW } from '../constants';
import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
/* eslint-disable @gitlab/require-i18n-strings */
const allowedKeys = [
@ -98,16 +99,34 @@ export default {
},
updateWeight(event) {
this.isEditing = false;
const weight = Number(event.target.value);
if (this.weight === weight) {
return;
}
this.track('updated_weight');
this.$apollo.mutate({
mutation: localUpdateWorkItemMutation,
variables: {
input: {
id: this.workItemId,
weight: event.target.value === '' ? null : Number(event.target.value),
this.$apollo
.mutate({
mutation: updateWorkItemMutation,
variables: {
input: {
id: this.workItemId,
weightWidget: {
weight: event.target.value === '' ? null : weight,
},
},
},
},
});
})
.then(({ data }) => {
if (data.workItemUpdate.errors.length) {
throw new Error(data.workItemUpdate.errors.join('\n'));
}
})
.catch((error) => {
this.$emit('error', i18n.updateError);
Sentry.captureException(error);
});
},
},
};

View File

@ -2,7 +2,7 @@ import produce from 'immer';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { WIDGET_TYPE_ASSIGNEES, WIDGET_TYPE_LABELS, WIDGET_TYPE_WEIGHT } from '../constants';
import { WIDGET_TYPE_ASSIGNEES, WIDGET_TYPE_LABELS } from '../constants';
import typeDefs from './typedefs.graphql';
import workItemQuery from './work_item.query.graphql';
@ -10,7 +10,7 @@ export const temporaryConfig = {
typeDefs,
cacheConfig: {
possibleTypes: {
LocalWorkItemWidget: ['LocalWorkItemLabels', 'LocalWorkItemWeight'],
LocalWorkItemWidget: ['LocalWorkItemLabels'],
},
typePolicies: {
WorkItem: {
@ -25,11 +25,6 @@ export const temporaryConfig = {
allowScopedLabels: true,
nodes: [],
},
{
__typename: 'LocalWorkItemWeight',
type: 'WEIGHT',
weight: null,
},
]
);
},
@ -56,13 +51,6 @@ export const resolvers = {
assigneesWidget.assignees.nodes = [...input.assignees];
}
if (input.weight != null) {
const weightWidget = draftData.workItem.mockWidgets.find(
(widget) => widget.type === WIDGET_TYPE_WEIGHT,
);
weightWidget.weight = input.weight;
}
if (input.labels) {
const labelsWidget = draftData.workItem.mockWidgets.find(
(widget) => widget.type === WIDGET_TYPE_LABELS,

View File

@ -1,7 +1,6 @@
enum LocalWidgetType {
ASSIGNEES
LABELS
WEIGHT
}
interface LocalWorkItemWidget {
@ -19,11 +18,6 @@ type LocalWorkItemLabels implements LocalWorkItemWidget {
nodes: [Label!]
}
type LocalWorkItemWeight implements LocalWorkItemWidget {
type: LocalWidgetType!
weight: Int
}
extend type WorkItem {
mockWidgets: [LocalWorkItemWidget]
}
@ -32,7 +26,6 @@ input LocalUpdateWorkItemInput {
id: WorkItemID!
assignees: [UserCore!]
labels: [Label]
weight: Int
}
type LocalWorkItemPayload {

View File

@ -28,6 +28,10 @@ fragment WorkItem on WorkItem {
}
}
}
... on WorkItemWidgetWeight {
type
weight
}
... on WorkItemWidgetHierarchy {
type
parent {

View File

@ -12,10 +12,6 @@ query workItem($id: WorkItemID!) {
...Label
}
}
... on LocalWorkItemWeight {
type
weight
}
}
}
}

View File

@ -1507,7 +1507,7 @@ class MergeRequest < ApplicationRecord
lock_mr
yield
ensure
unlock_mr
unlock_mr if locked?
end
def update_and_mark_in_progress_merge_commit_sha(commit_id)

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366823
milestone: '15.2'
type: development
group: group::source code
default_enabled: false
default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: highlight_diffs_optimize_memory_usage
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92456
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367890
milestone: '15.3'
type: development
group: group::source code
default_enabled: false

View File

@ -79,6 +79,7 @@ POST /projects/:id/approvals
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3.
> - Moved to GitLab Premium in 13.9.
> - `protected_branches` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/460) in GitLab 12.7.
> - Pagination support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31011) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `approval_rules_pagination`. Enabled by default.
You can request information about a project's approval rules using the following endpoint:
@ -86,6 +87,8 @@ You can request information about a project's approval rules using the following
GET /projects/:id/approval_rules
```
Use the `page` and `per_page` [pagination](index.md#offset-based-pagination) parameters to restrict the list of approval rules.
**Parameters:**
| Attribute | Type | Required | Description |
@ -684,6 +687,7 @@ This includes additional information about the users who have already approved
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13712) in GitLab 12.3.
> - Moved to GitLab Premium in 13.9.
> - Pagination support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31011) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `approval_rules_pagination`. Enabled by default.
You can request information about a merge request's approval rules using the following endpoint:
@ -691,6 +695,8 @@ You can request information about a merge request's approval rules using the fol
GET /projects/:id/merge_requests/:merge_request_iid/approval_rules
```
Use the `page` and `per_page` [pagination](index.md#offset-based-pagination) parameters to restrict the list of approval rules.
**Parameters:**
| Attribute | Type | Required | Description |

View File

@ -740,4 +740,15 @@ Example response:
A release with a `released_at` attribute set to a future date is labeled
as an **Upcoming Release** [in the UI](../../user/project/releases/index.md#upcoming-releases).
Additionally, if a [release is requested from the API](#list-releases), for each release with a `release_at` attribute set to a future date, an additional attribute `upcoming_release` (set to true) will be returned as part of the response.
Additionally, if a [release is requested from the API](#list-releases), for each release with a `release_at` attribute set to a future date, an additional attribute `upcoming_release` (set to true) is returned as part of the response.
## Historical releases
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/199429) in GitLab 15.2.
A release with a `released_at` attribute set to a past date is labeled
as an **Historical release** [in the UI](../../user/project/releases/index.md#historical-releases).
Additionally, if a [release is requested from the API](#list-releases), for each
release with a `release_at` attribute set to a past date, an additional
attribute `historical_release` (set to true) is returned as part of the response.

View File

@ -311,7 +311,7 @@ table.supported-languages ul {
<p>
Although Gradle with Java 8 is supported, there are other issues such that Android project builds are not supported at this time.
Please see the backlog issue <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/336866">Android support for Dependency
Scanning (gemnasium-maven)</a> for more details. Also, Gradle is not supported when [FIPS mode](../../../development/fips_compliance.md#enable-fips-mode) is enabled.
Scanning (gemnasium-maven)</a> for more details. Also, Gradle is not supported when <a href="https://docs.gitlab.com/ee/development/fips_compliance.html#enable-fips-mode">FIPS mode</a> is enabled.
</p>
</li>
<li>

View File

@ -127,7 +127,7 @@ To remove all resources:
- cleanup
destroy:
extends: .destroy
extends: .terraform:destroy
needs: []
```

View File

@ -298,6 +298,16 @@ release tag. When the `released_at` date and time has passed, the badge is autom
![An upcoming release](img/upcoming_release_v12_7.png)
## Historical releases
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/199429) in GitLab 15.2.
You can create a release in the past using either the
[Releases API](../../../api/releases/index.md#historical-releases) or the UI. When you set
a past `released_at` date, an **Historical release** badge is displayed next to
the release tag. Due to being released in the past, [release evidence](#release-evidence)
is not available.
## Edit a release
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26016) in GitLab 12.6.
@ -828,10 +838,11 @@ keyword. Learn more in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issue
In the API:
- If you specify a future `released_at` date, the release becomes an **Upcoming Release**
- If you specify a future `released_at` date, the release becomes an **Upcoming release**
and the evidence is collected on the date of the release. You cannot collect
release evidence before then.
- If you use a past `released_at` date, no evidence is collected.
- If you specify a past `released_at` date, the release becomes an **Historical
release** and no evidence is collected.
- If you do not specify a `released_at` date, release evidence is collected on the
date the release is created.

View File

@ -481,7 +481,11 @@ module API
.execute(merge_request, AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
end
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
if immediately_mergeable && !merge_request.merged?
render_api_error!("Failed to merge branch", 422)
else
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
end
desc 'Returns the up to date merge-ref HEAD commit'

View File

@ -6,7 +6,8 @@ module Gitlab
include Gitlab::Utils::Gzip
include Gitlab::Utils::StrongMemoize
EXPIRATION = 1.week
EXPIRATION = 1.day
PREVIOUS_EXPIRATION_PERIOD = 7.days
VERSION = 2
delegate :diffable, to: :@diff_collection
@ -69,19 +70,33 @@ module Gitlab
def key
strong_memoize(:redis_key) do
[
'highlighted-diff-files',
diffable.cache_key,
VERSION,
options = [
diff_options,
Feature.enabled?(:use_marker_ranges, diffable.project),
Feature.enabled?(:diff_line_syntax_highlighting, diffable.project)
].join(":")
]
options_for_key =
if Feature.enabled?(:highlight_diffs_optimize_memory_usage, diffable.project)
[OpenSSL::Digest::SHA256.hexdigest(options.join)]
else
options
end
['highlighted-diff-files', diffable.cache_key, VERSION, *options_for_key].join(":")
end
end
private
def expiration_period
if Feature.enabled?(:highlight_diffs_optimize_memory_usage, diffable.project)
EXPIRATION
else
PREVIOUS_EXPIRATION_PERIOD
end
end
def set_highlighted_diff_lines(diff_file, content)
diff_file.highlighted_diff_lines = content.map do |line|
Gitlab::Diff::Line.safe_init_from_hash(line)
@ -138,7 +153,7 @@ module Gitlab
# HSETs have to have their expiration date manually updated
#
redis.expire(key, EXPIRATION)
redis.expire(key, expiration_period)
end
record_memory_usage(fetch_memory_usage(redis, key))

View File

@ -19290,6 +19290,9 @@ msgstr ""
msgid "HighlightBar|Time to SLA:"
msgstr ""
msgid "Historical release"
msgstr ""
msgid "History"
msgstr ""
@ -40061,6 +40064,9 @@ msgstr ""
msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
msgstr ""
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
msgid "This repository"
msgstr ""

View File

@ -164,6 +164,9 @@ describe('Package Files', () => {
createComponent();
expect(findFirstActionMenu().exists()).toBe(true);
expect(findFirstActionMenu().props('icon')).toBe('ellipsis_v');
expect(findFirstActionMenu().props('textSrOnly')).toBe(true);
expect(findFirstActionMenu().props('text')).toMatchInterpolatedText('More actions');
});
describe('menu items', () => {

View File

@ -55,6 +55,7 @@ Object {
"commitPath": "http://localhost/releases-namespace/releases-project/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0",
"descriptionHtml": "<p data-sourcepos=\\"1:1-1:23\\" dir=\\"auto\\">An okay release <gl-emoji title=\\"shrug\\" data-name=\\"shrug\\" data-unicode-version=\\"9.0\\">🤷</gl-emoji></p>",
"evidences": Array [],
"historicalRelease": false,
"milestones": Array [],
"name": "The second release",
"releasedAt": 2019-01-10T00:00:00.000Z,
@ -159,6 +160,7 @@ Object {
"sha": "760d6cdfb0879c3ffedec13af470e0f71cf52c6cde4d",
},
],
"historicalRelease": false,
"milestones": Array [
Object {
"__typename": "Milestone",
@ -373,6 +375,7 @@ Object {
"sha": "760d6cdfb0879c3ffedec13af470e0f71cf52c6cde4d",
},
],
"historicalRelease": false,
"milestones": Array [
Object {
"__typename": "Milestone",

View File

@ -1,8 +1,9 @@
import { GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { GlLink, GlBadge } from '@gitlab/ui';
import { merge } from 'lodash';
import originalRelease from 'test_fixtures/api/releases/release.json';
import setWindowLocation from 'helpers/set_window_location_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { __ } from '~/locale';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import ReleaseBlockHeader from '~/releases/components/release_block_header.vue';
import { BACK_URL_PARAM } from '~/releases/constants';
@ -12,10 +13,11 @@ describe('Release block header', () => {
let release;
const factory = (releaseUpdates = {}) => {
wrapper = shallowMount(ReleaseBlockHeader, {
wrapper = shallowMountExtended(ReleaseBlockHeader, {
propsData: {
release: merge({}, release, releaseUpdates),
},
stubs: { GlBadge },
});
};
@ -30,6 +32,7 @@ describe('Release block header', () => {
const findHeader = () => wrapper.find('h2');
const findHeaderLink = () => findHeader().find(GlLink);
const findEditButton = () => wrapper.find('.js-edit-button');
const findBadge = () => wrapper.findComponent(GlBadge);
describe('when _links.self is provided', () => {
beforeEach(() => {
@ -84,4 +87,34 @@ describe('Release block header', () => {
expect(findEditButton().exists()).toBe(false);
});
});
describe('upcoming release', () => {
beforeEach(() => {
factory({ upcomingRelease: true, historicalRelease: false });
});
it('shows a badge that the release is upcoming', () => {
const badge = findBadge();
expect(badge.text()).toBe(__('Upcoming Release'));
expect(badge.props('variant')).toBe('warning');
});
});
describe('historical release', () => {
beforeEach(() => {
factory({ upcomingRelease: false, historicalRelease: true });
});
it('shows a badge that the release is historical', () => {
const badge = findBadge();
expect(badge.text()).toBe(__('Historical release'));
expect(badge.attributes('title')).toBe(
__(
'This release was created with a date in the past. Evidence collection at the moment of the release is unavailable.',
),
);
});
});
});

View File

@ -1,16 +1,21 @@
import { GlForm, GlFormInput } from '@gitlab/ui';
import { nextTick } from 'vue';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mockTracking } from 'helpers/tracking_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { __ } from '~/locale';
import WorkItemWeight from '~/work_items/components/work_item_weight.vue';
import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import localUpdateWorkItemMutation from '~/work_items/graphql/local_update_work_item.mutation.graphql';
import { i18n, TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import { updateWorkItemMutationResponse } from 'jest/work_items/mock_data';
describe('WorkItemWeight component', () => {
Vue.use(VueApollo);
let wrapper;
const mutateSpy = jest.fn();
const workItemId = 'gid://gitlab/WorkItem/1';
const workItemType = 'Task';
@ -22,8 +27,10 @@ describe('WorkItemWeight component', () => {
hasIssueWeightsFeature = true,
isEditing = false,
weight,
mutationHandler = jest.fn().mockResolvedValue(updateWorkItemMutationResponse),
} = {}) => {
wrapper = mountExtended(WorkItemWeight, {
apolloProvider: createMockApollo([[updateWorkItemMutation, mutationHandler]]),
propsData: {
canUpdate,
weight,
@ -33,11 +40,6 @@ describe('WorkItemWeight component', () => {
provide: {
hasIssueWeightsFeature,
},
mocks: {
$apollo: {
mutate: mutateSpy,
},
},
});
if (isEditing) {
@ -131,23 +133,63 @@ describe('WorkItemWeight component', () => {
});
describe('when blurred', () => {
it('calls a mutation to update the weight', () => {
const weight = 0;
createComponent({ isEditing: true, weight });
it('calls a mutation to update the weight when the input value is different', () => {
const mutationSpy = jest.fn().mockResolvedValue(updateWorkItemMutationResponse);
createComponent({ isEditing: true, weight: 0, mutationHandler: mutationSpy });
findInput().trigger('blur');
findInput().vm.$emit('blur', { target: { value: 1 } });
expect(mutateSpy).toHaveBeenCalledWith({
mutation: localUpdateWorkItemMutation,
variables: {
input: {
id: workItemId,
weight,
expect(mutationSpy).toHaveBeenCalledWith({
input: {
id: workItemId,
weightWidget: {
weight: 1,
},
},
});
});
it('does not call a mutation to update the weight when the input value is the same', () => {
const mutationSpy = jest.fn().mockResolvedValue(updateWorkItemMutationResponse);
createComponent({ isEditing: true, mutationHandler: mutationSpy });
findInput().trigger('blur');
expect(mutationSpy).not.toHaveBeenCalledWith();
});
it('emits an error when there is a GraphQL error', async () => {
const response = {
data: {
workItemUpdate: {
errors: ['Error!'],
workItem: {},
},
},
};
createComponent({
isEditing: true,
mutationHandler: jest.fn().mockResolvedValue(response),
});
findInput().trigger('blur');
await waitForPromises();
expect(wrapper.emitted('error')).toEqual([[i18n.updateError]]);
});
it('emits an error when there is a network error', async () => {
createComponent({
isEditing: true,
mutationHandler: jest.fn().mockRejectedValue(new Error()),
});
findInput().trigger('blur');
await waitForPromises();
expect(wrapper.emitted('error')).toEqual([[i18n.updateError]]);
});
it('tracks updating the weight', () => {
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
createComponent();

View File

@ -77,6 +77,7 @@ export const updateWorkItemMutationResponse = {
data: {
workItemUpdate: {
__typename: 'WorkItemUpdatePayload',
errors: [],
workItem: {
__typename: 'WorkItem',
id: 'gid://gitlab/WorkItem/1',
@ -112,6 +113,7 @@ export const workItemResponseFactory = ({
canUpdate = false,
allowsMultipleAssignees = true,
assigneesWidgetPresent = true,
weightWidgetPresent = true,
parent = null,
} = {}) => ({
data: {
@ -148,6 +150,13 @@ export const workItemResponseFactory = ({
},
}
: { type: 'MOCK TYPE' },
weightWidgetPresent
? {
__typename: 'WorkItemWidgetWeight',
type: 'WEIGHT',
weight: 0,
}
: { type: 'MOCK TYPE' },
{
__typename: 'WorkItemWidgetHierarchy',
type: 'HIERARCHY',

View File

@ -278,12 +278,14 @@ describe('WorkItemDetail component', () => {
describe('weight widget', () => {
describe('when work_items_mvc_2 feature flag is enabled', () => {
describe.each`
description | includeWidgets | exists
${'when widget is returned from API'} | ${true} | ${true}
${'when widget is not returned from API'} | ${false} | ${false}
`('$description', ({ includeWidgets, exists }) => {
it(`${includeWidgets ? 'renders' : 'does not render'} weight component`, async () => {
createComponent({ includeWidgets, workItemsMvc2Enabled: true });
description | weightWidgetPresent | exists
${'when widget is returned from API'} | ${true} | ${true}
${'when widget is not returned from API'} | ${false} | ${false}
`('$description', ({ weightWidgetPresent, exists }) => {
it(`${weightWidgetPresent ? 'renders' : 'does not render'} weight component`, async () => {
const response = workItemResponseFactory({ weightWidgetPresent });
const handler = jest.fn().mockResolvedValue(response);
createComponent({ workItemsMvc2Enabled: true, handler });
await waitForPromises();
expect(findWorkItemWeight().exists()).toBe(exists);
@ -293,18 +295,28 @@ describe('WorkItemDetail component', () => {
describe('when work_items_mvc_2 feature flag is disabled', () => {
describe.each`
description | includeWidgets | exists
${'when widget is returned from API'} | ${true} | ${false}
${'when widget is not returned from API'} | ${false} | ${false}
`('$description', ({ includeWidgets, exists }) => {
it(`${includeWidgets ? 'renders' : 'does not render'} weight component`, async () => {
createComponent({ includeWidgets, workItemsMvc2Enabled: false });
description | weightWidgetPresent | exists
${'when widget is returned from API'} | ${true} | ${false}
${'when widget is not returned from API'} | ${false} | ${false}
`('$description', ({ weightWidgetPresent, exists }) => {
it(`${weightWidgetPresent ? 'renders' : 'does not render'} weight component`, async () => {
createComponent({ weightWidgetPresent, workItemsMvc2Enabled: false });
await waitForPromises();
expect(findWorkItemWeight().exists()).toBe(exists);
});
});
});
it('shows an error message when it emits an `error` event', async () => {
createComponent({ workItemsMvc2Enabled: true });
await waitForPromises();
findWorkItemWeight().vm.$emit('error', i18n.updateError);
await waitForPromises();
expect(findAlert().text()).toBe(i18n.updateError);
});
});
describe('work item information', () => {

View File

@ -115,6 +115,10 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
.once
.and_call_original
Gitlab::Redis::Cache.with do |redis|
expect(redis).to receive(:expire).with(cache.key, described_class::EXPIRATION)
end
2.times { cache.write_if_empty }
end
@ -123,6 +127,20 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
cache.write_if_empty
end
context 'when highlight_diffs_optimize_memory_usage is disabled' do
before do
stub_feature_flags(highlight_diffs_optimize_memory_usage: false)
end
it 'sets the previous expiration period' do
Gitlab::Redis::Cache.with do |redis|
expect(redis).to receive(:expire).with(cache.key, described_class::PREVIOUS_EXPIRATION_PERIOD)
end
cache.write_if_empty
end
end
end
describe '#write_if_empty' do
@ -259,8 +277,12 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
describe '#key' do
subject { cache.key }
def options_hash(options_array)
OpenSSL::Digest::SHA256.hexdigest(options_array.join)
end
it 'returns cache key' do
is_expected.to eq("highlighted-diff-files:#{cache.diffable.cache_key}:2:#{cache.diff_options}:true:true")
is_expected.to eq("highlighted-diff-files:#{cache.diffable.cache_key}:2:#{options_hash([cache.diff_options, true, true])}")
end
context 'when the `use_marker_ranges` feature flag is disabled' do
@ -269,7 +291,7 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
end
it 'returns the original version of the cache' do
is_expected.to eq("highlighted-diff-files:#{cache.diffable.cache_key}:2:#{cache.diff_options}:false:true")
is_expected.to eq("highlighted-diff-files:#{cache.diffable.cache_key}:2:#{options_hash([cache.diff_options, false, true])}")
end
end
@ -279,7 +301,17 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
end
it 'returns the original version of the cache' do
is_expected.to eq("highlighted-diff-files:#{cache.diffable.cache_key}:2:#{cache.diff_options}:true:false")
is_expected.to eq("highlighted-diff-files:#{cache.diffable.cache_key}:2:#{options_hash([cache.diff_options, true, false])}")
end
end
context 'when highlight_diffs_optimize_memory_usage is disabled' do
before do
stub_feature_flags(highlight_diffs_optimize_memory_usage: false)
end
it 'uses the options hash as a part of the cache key' do
is_expected.to eq("highlighted-diff-files:#{cache.diffable.cache_key}:2:#{cache.diff_options}:true:true")
end
end
end

View File

@ -4660,6 +4660,37 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
describe '#in_locked_state' do
let(:merge_request) { create(:merge_request, :opened) }
context 'when the merge request does not change state' do
it 'returns to previous state and has no errors on the object' do
expect(merge_request.opened?).to eq(true)
merge_request.in_locked_state do
expect(merge_request.locked?).to eq(true)
end
expect(merge_request.opened?).to eq(true)
expect(merge_request.errors).to be_empty
end
end
context 'when the merge request is merged while locked' do
it 'becomes merged and has no errors on the object' do
expect(merge_request.opened?).to eq(true)
merge_request.in_locked_state do
expect(merge_request.locked?).to eq(true)
merge_request.mark_as_merged!
end
expect(merge_request.merged?).to eq(true)
expect(merge_request.errors).to be_empty
end
end
end
describe '#cleanup_refs' do
subject { merge_request.cleanup_refs(only: only) }

View File

@ -2482,11 +2482,28 @@ RSpec.describe API::MergeRequests do
let(:pipeline) { create(:ci_pipeline, project: project) }
it "returns merge_request in case of success" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user)
expect { put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user) }
.to change { merge_request.reload.merged? }
.from(false)
.to(true)
expect(response).to have_gitlab_http_status(:ok)
end
context 'when the merge request fails to merge' do
it 'returns 422' do
expect_next_instance_of(::MergeRequests::MergeService) do |service|
expect(service).to receive(:execute)
end
expect { put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user) }
.not_to change { merge_request.reload.merged? }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response['message']).to eq("Failed to merge branch")
end
end
context 'when change_response_code_merge_status is enabled' do
it "returns 422 if branch can't be merged" do
allow_next_found_instance_of(MergeRequest) do |merge_request|