summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 18:07:07 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 18:07:07 +0000
commit8746f6e79d7717a8cb16737fecdb977feaa22cdb (patch)
treecd1080192931a17e459fc5b476f3c5d91f83dde3
parent30785cadee10a5deaa45ada13def96bcfa6663b0 (diff)
downloadgitlab-ce-8746f6e79d7717a8cb16737fecdb977feaa22cdb.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/qa-common/main.gitlab-ci.yml6
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js8
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue1
-rw-r--r--app/assets/javascripts/super_sidebar/components/frequent_items_list.vue24
-rw-r--r--app/assets/javascripts/super_sidebar/components/help_center.vue6
-rw-r--r--app/assets/javascripts/super_sidebar/components/items_list.vue17
-rw-r--r--app/assets/javascripts/super_sidebar/components/sidebar_menu.vue7
-rw-r--r--app/graphql/mutations/environments/update.rb44
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/helpers/safe_format_helper.rb3
-rw-r--r--app/services/environments/update_service.rb23
-rw-r--r--doc/api/graphql/reference/index.md23
-rw-r--r--doc/architecture/blueprints/cells/cells-feature-ci-runners.md1
-rw-r--r--doc/development/testing_guide/end_to_end/index.md6
-rw-r--r--doc/security/token_overview.md12
-rw-r--r--doc/user/ai_features.md26
-rw-r--r--doc/user/project/protected_branches.md12
-rw-r--r--doc/user/project/protected_tags.md3
-rw-r--r--lib/gitlab/ci/parsers/security/common.rb1
-rw-r--r--lib/gitlab/ci/reports/security/finding.rb7
-rw-r--r--lib/gitlab/database/schema_validation/schema_inconsistency.rb2
-rw-r--r--lib/gitlab/database/schema_validation/track_inconsistency.rb2
-rw-r--r--locale/gitlab.pot41
-rw-r--r--spec/factories/ci/reports/security/findings.rb1
-rw-r--r--spec/fixtures/security_reports/master/gl-common-scanning-report.json6
-rw-r--r--spec/frontend/super_sidebar/components/frequent_items_list_spec.js28
-rw-r--r--spec/frontend/super_sidebar/components/help_center_spec.js6
-rw-r--r--spec/frontend/super_sidebar/components/items_list_spec.js44
-rw-r--r--spec/frontend/super_sidebar/components/sidebar_menu_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/chronic_duration_input_spec.js7
-rw-r--r--spec/graphql/mutations/environments/update_spec.rb56
-rw-r--r--spec/helpers/safe_format_helper_spec.rb17
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb22
-rw-r--r--spec/lib/gitlab/ci/reports/security/report_spec.rb4
-rw-r--r--spec/lib/gitlab/database/schema_validation/schema_inconsistency_spec.rb23
-rw-r--r--spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb24
-rw-r--r--spec/requests/api/graphql/mutations/environments/update_spec.rb70
-rw-r--r--spec/services/environments/update_service_spec.rb55
-rw-r--r--spec/services/security/merge_reports_service_spec.rb58
39 files changed, 552 insertions, 153 deletions
diff --git a/.gitlab/ci/qa-common/main.gitlab-ci.yml b/.gitlab/ci/qa-common/main.gitlab-ci.yml
index 21fd7c77573..c006465194f 100644
--- a/.gitlab/ci/qa-common/main.gitlab-ci.yml
+++ b/.gitlab/ci/qa-common/main.gitlab-ci.yml
@@ -6,7 +6,7 @@ workflow:
include:
- project: gitlab-org/quality/pipeline-common
- ref: 3.1.5
+ ref: 5.1.0
file:
- /ci/base.gitlab-ci.yml
- /ci/allure-report.yml
@@ -235,12 +235,12 @@ stages:
--project "gitlab-org/quality/testcase-sessions" \
--token "${QA_TEST_SESSION_TOKEN}" \
--ci-project-token "${GENERATE_TEST_SESSION_READ_API_REPORTER_TOKEN}" \
- --issue-url-file REPORT_ISSUE_URL
+ --issue-url-file report_issue_url.txt
artifacts:
when: always
expire_in: 1d
paths:
- - qa/REPORT_ISSUE_URL
+ - qa/report_issue_url.txt
.notify-slack:
extends:
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index f16ff188edb..4301fbf2f0e 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -8,10 +8,14 @@ const SHA_REGEX = /[\da-f]{40}/gi;
// GitLab default domain (override in jh)
export const DOMAIN = 'gitlab.com';
-// About GitLab default host (overwrite in jh)
+// Following URLs will be overwritten in jh
+export const FORUM_URL = `https://forum.${DOMAIN}/`; // forum.gitlab.com
+export const DOCS_URL = `https://docs.${DOMAIN}`; // docs.gitlab.com
+
+// About GitLab default host
export const PROMO_HOST = `about.${DOMAIN}`; // about.gitlab.com
-// About Gitlab default url (overwrite in jh)
+// About Gitlab default url
export const PROMO_URL = `https://${PROMO_HOST}`;
// Reset the cursor in a Regex so that multiple uses before a recompile don't fail
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
index 4934df0adc1..7d41700c492 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
@@ -391,6 +391,7 @@ export default {
@filterPipelines="filterPipelines"
/>
<gl-collapsible-listbox
+ v-model="visibilityPipelineIdType"
data-testid="pipeline-key-collapsible-box"
:toggle-text="selectedPipelineKeyOption.text"
:items="$options.PipelineKeyOptions"
diff --git a/app/assets/javascripts/super_sidebar/components/frequent_items_list.vue b/app/assets/javascripts/super_sidebar/components/frequent_items_list.vue
index 11bf2ddbd30..79f3faacac9 100644
--- a/app/assets/javascripts/super_sidebar/components/frequent_items_list.vue
+++ b/app/assets/javascripts/super_sidebar/components/frequent_items_list.vue
@@ -1,13 +1,19 @@
<script>
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import AccessorUtilities from '~/lib/utils/accessor';
+import { __ } from '~/locale';
import { getTopFrequentItems, formatContextSwitcherItems } from '../utils';
import ItemsList from './items_list.vue';
export default {
components: {
+ GlButton,
ItemsList,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
title: {
type: String,
@@ -68,6 +74,9 @@ export default {
}
},
},
+ i18n: {
+ removeItem: __('Remove'),
+ },
};
</script>
@@ -87,7 +96,20 @@ export default {
>
{{ pristineText }}
</div>
- <items-list :aria-label="title" :items="cachedFrequentItems" @remove-item="handleItemRemove">
+ <items-list :aria-label="title" :items="cachedFrequentItems">
+ <template #actions="{ item }">
+ <gl-button
+ v-gl-tooltip.right.viewport
+ size="small"
+ category="tertiary"
+ icon="dash"
+ :aria-label="$options.i18n.removeItem"
+ :title="$options.i18n.removeItem"
+ class="gl-align-self-center gl-p-1! gl-absolute gl-right-4"
+ data-testid="item-remove"
+ @click.stop.prevent="handleItemRemove(item)"
+ />
+ </template>
<template #view-all-items>
<slot name="view-all-items"></slot>
</template>
diff --git a/app/assets/javascripts/super_sidebar/components/help_center.vue b/app/assets/javascripts/super_sidebar/components/help_center.vue
index 1fffbb05d03..4de17ffa8b6 100644
--- a/app/assets/javascripts/super_sidebar/components/help_center.vue
+++ b/app/assets/javascripts/super_sidebar/components/help_center.vue
@@ -8,7 +8,7 @@ import {
} from '@gitlab/ui';
import GitlabVersionCheckBadge from '~/gitlab_version_check/components/gitlab_version_check_badge.vue';
import { helpPagePath } from '~/helpers/help_page_helper';
-import { DOMAIN, PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
+import { FORUM_URL, DOCS_URL, PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
import { __, s__ } from '~/locale';
import { STORAGE_KEY } from '~/whats_new/utils/notification';
import Tracking from '~/tracking';
@@ -93,7 +93,7 @@ export default {
},
{
text: this.$options.i18n.docs,
- href: `https://docs.${DOMAIN}`,
+ href: DOCS_URL,
extraAttrs: {
...this.trackingAttrs('gitlab_documentation'),
},
@@ -107,7 +107,7 @@ export default {
},
{
text: this.$options.i18n.forum,
- href: `https://forum.${DOMAIN}/`,
+ href: FORUM_URL,
extraAttrs: {
...this.trackingAttrs('community_forum'),
},
diff --git a/app/assets/javascripts/super_sidebar/components/items_list.vue b/app/assets/javascripts/super_sidebar/components/items_list.vue
index ef27251dc6c..46f27dd7d06 100644
--- a/app/assets/javascripts/super_sidebar/components/items_list.vue
+++ b/app/assets/javascripts/super_sidebar/components/items_list.vue
@@ -1,17 +1,12 @@
<script>
-import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
import NavItem from './nav_item.vue';
export default {
components: {
- GlButton,
ProjectAvatar,
NavItem,
},
- directives: {
- GlTooltip: GlTooltipDirective,
- },
props: {
items: {
type: Array,
@@ -40,17 +35,7 @@ export default {
/>
</template>
<template #actions>
- <gl-button
- v-gl-tooltip.right.viewport
- size="small"
- category="tertiary"
- icon="dash"
- :aria-label="__('Remove')"
- :title="__('Remove')"
- class="gl-align-self-center gl-p-1! gl-absolute gl-right-4"
- data-testid="item-remove"
- @click.stop.prevent="$emit('remove-item', item)"
- />
+ <slot name="actions" :item="item"></slot>
</template>
</nav-item>
<slot name="view-all-items"></slot>
diff --git a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue
index 08af9232107..0ec4c759acc 100644
--- a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue
+++ b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue
@@ -1,6 +1,7 @@
<script>
import * as Sentry from '@sentry/browser';
import axios from '~/lib/utils/axios_utils';
+import { s__ } from '~/locale';
import { PANELS_WITH_PINS } from '../constants';
import NavItem from './nav_item.vue';
import PinnedSection from './pinned_section.vue';
@@ -42,6 +43,10 @@ export default {
},
},
+ i18n: {
+ mainNavigation: s__('Navigation|Main navigation'),
+ },
+
data() {
return {
// This is used as a provide and injected into the nav items.
@@ -137,7 +142,7 @@ export default {
</script>
<template>
- <nav class="gl-p-2 gl-relative">
+ <nav :aria-label="$options.i18n.mainNavigation" class="gl-p-2 gl-relative">
<ul v-if="hasStaticItems" class="gl-p-0 gl-m-0">
<nav-item v-for="item in staticItems" :key="item.id" :item="item" is-static />
</ul>
diff --git a/app/graphql/mutations/environments/update.rb b/app/graphql/mutations/environments/update.rb
new file mode 100644
index 00000000000..dc1fb9b23af
--- /dev/null
+++ b/app/graphql/mutations/environments/update.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Environments
+ class Update < ::Mutations::BaseMutation
+ graphql_name 'EnvironmentUpdate'
+ description 'Update an environment.'
+
+ authorize :update_environment
+
+ argument :id,
+ ::Types::GlobalIDType[::Environment],
+ required: true,
+ description: 'Global ID of the environment to update.'
+
+ argument :external_url,
+ GraphQL::Types::String,
+ required: false,
+ description: 'External URL of the environment.'
+
+ argument :tier,
+ Types::DeploymentTierEnum,
+ required: false,
+ description: 'Tier of the environment.'
+
+ field :environment,
+ Types::EnvironmentType,
+ null: true,
+ description: 'Environment after attempt to update.'
+
+ def resolve(id:, **kwargs)
+ environment = authorized_find!(id: id)
+
+ response = ::Environments::UpdateService.new(environment.project, current_user, kwargs).execute(environment)
+
+ if response.success?
+ { environment: response.payload[:environment], errors: [] }
+ else
+ { environment: response.payload[:environment], errors: response.errors }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 7e436d74dcf..9f84e2efab6 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -53,6 +53,7 @@ module Types
mount_mutation Mutations::DependencyProxy::GroupSettings::Update
mount_mutation Mutations::Environments::CanaryIngress::Update
mount_mutation Mutations::Environments::Stop
+ mount_mutation Mutations::Environments::Update
mount_mutation Mutations::IncidentManagement::TimelineEvent::Create, alpha: { milestone: '15.6' }
mount_mutation Mutations::IncidentManagement::TimelineEvent::PromoteFromNote
mount_mutation Mutations::IncidentManagement::TimelineEvent::Update
diff --git a/app/helpers/safe_format_helper.rb b/app/helpers/safe_format_helper.rb
index c79e8b50a1a..f05cf5ab50f 100644
--- a/app/helpers/safe_format_helper.rb
+++ b/app/helpers/safe_format_helper.rb
@@ -15,7 +15,8 @@ module SafeFormatHelper
def safe_format(format, **args)
raise ArgumentError, 'Argument `format` must not be marked as html_safe!' if format.html_safe?
- format(
+ # Use `Kernel.format` to avoid conflicts with ViewComponent's `format`.
+ Kernel.format(
html_escape(format),
args.transform_values { |value| html_escape(value) }
).html_safe
diff --git a/app/services/environments/update_service.rb b/app/services/environments/update_service.rb
new file mode 100644
index 00000000000..e02b2398426
--- /dev/null
+++ b/app/services/environments/update_service.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Environments
+ class UpdateService < BaseService
+ def execute(environment)
+ unless can?(current_user, :update_environment, environment)
+ return ServiceResponse.error(
+ message: _('Unauthorized to update the environment'),
+ payload: { environment: environment }
+ )
+ end
+
+ if environment.update(**params)
+ ServiceResponse.success(payload: { environment: environment })
+ else
+ ServiceResponse.error(
+ message: environment.errors.full_messages,
+ payload: { environment: environment }
+ )
+ end
+ end
+ end
+end
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 9f6f141a877..3f37e892831 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -3060,6 +3060,29 @@ Input type: `EnvironmentStopInput`
| <a id="mutationenvironmentstopenvironment"></a>`environment` | [`Environment`](#environment) | Environment after attempt to stop. |
| <a id="mutationenvironmentstoperrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.environmentUpdate`
+
+Update an environment.
+
+Input type: `EnvironmentUpdateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationenvironmentupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationenvironmentupdateexternalurl"></a>`externalUrl` | [`String`](#string) | External URL of the environment. |
+| <a id="mutationenvironmentupdateid"></a>`id` | [`EnvironmentID!`](#environmentid) | Global ID of the environment to update. |
+| <a id="mutationenvironmentupdatetier"></a>`tier` | [`DeploymentTier`](#deploymenttier) | Tier of the environment. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationenvironmentupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationenvironmentupdateenvironment"></a>`environment` | [`Environment`](#environment) | Environment after attempt to update. |
+| <a id="mutationenvironmentupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
### `Mutation.environmentsCanaryIngressUpdate`
**Deprecated** This endpoint is planned to be removed along with certificate-based clusters. [See this epic](https://gitlab.com/groups/gitlab-org/configure/-/epics/8) for more information.
diff --git a/doc/architecture/blueprints/cells/cells-feature-ci-runners.md b/doc/architecture/blueprints/cells/cells-feature-ci-runners.md
index e352be17dd3..8a6790ae49f 100644
--- a/doc/architecture/blueprints/cells/cells-feature-ci-runners.md
+++ b/doc/architecture/blueprints/cells/cells-feature-ci-runners.md
@@ -39,6 +39,7 @@ GitLab Runners use a set of globally scoped endpoints to:
- registration of a new runner via registration token `https://gitlab.com/api/v4/runners`
([subject for removal](../runner_tokens/index.md)) (`registration token`)
+- creation of a new runner in the context of a user `https://gitlab.com/api/v4/user/runners` (`runner token`)
- requests jobs via an authenticated `https://gitlab.com/api/v4/jobs/request` endpoint (`runner token`)
- upload job status via `https://gitlab.com/api/v4/jobs/:job_id` (`build token`)
- upload trace via `https://gitlab.com/api/v4/jobs/:job_id/trace` (`build token`)
diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md
index 77ceb9fa546..07a5397a802 100644
--- a/doc/development/testing_guide/end_to_end/index.md
+++ b/doc/development/testing_guide/end_to_end/index.md
@@ -97,8 +97,8 @@ This problem was discovered in <https://gitlab.com/gitlab-org/gitlab-qa/-/issues
work-around was suggested in <https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4717>.
A feature proposal to segregate access control regarding running pipelines from ability to push/merge was also created at <https://gitlab.com/gitlab-org/gitlab/-/issues/24585>.
-For more technical details on CI/CD setup and documentation on adding new test jobs to `package-and-test` pipeline, see
-[`package_and_test` setup documentation](package_and_test_pipeline.md).
+For more technical details on CI/CD setup and documentation on adding new test jobs to `e2e:package-and-test` pipeline, see
+[`e2e:package_and_test` setup documentation](package_and_test_pipeline.md).
#### With merged results pipelines
@@ -198,7 +198,7 @@ Use these environment variables to configure metrics export:
| -------- | -------- | ----------- |
| `QA_INFLUXDB_URL` | `true` | Should be set to `https://influxdb.quality.gitlab.net`. No default value. |
| `QA_INFLUXDB_TOKEN` | `true` | InfluxDB write token that can be found under `Influxdb auth tokens` document in `Gitlab-QA` `1Password` vault. No default value. |
-| `QA_RUN_TYPE` | `false` | Arbitrary name for test execution, like `package-and-test`. Automatically inferred from the project name for live environment test executions. No default value. |
+| `QA_RUN_TYPE` | `false` | Arbitrary name for test execution, like `e2e:package-and-test`. Automatically inferred from the project name for live environment test executions. No default value. |
| `QA_EXPORT_TEST_METRICS` | `false` | Flag to enable or disable metrics export to InfluxDB. Defaults to `false`. |
| `QA_SAVE_TEST_METRICS` | `false` | Flag to enable or disable saving metrics as JSON file. Defaults to `false`. |
diff --git a/doc/security/token_overview.md b/doc/security/token_overview.md
index 8acd4a125ce..40c05ddc53a 100644
--- a/doc/security/token_overview.md
+++ b/doc/security/token_overview.md
@@ -20,6 +20,10 @@ You can create [Personal access tokens](../user/profile/personal_access_tokens.m
You can limit the scope and expiration date of your personal access tokens. By default,
they inherit permissions from the user who created them.
+You can use the [personal access tokens API](../api/personal_access_tokens.md) to
+programmatically take action, such as
+[rotating a personal access token](../api/personal_access_tokens.md#rotate-a-personal-access-token).
+
## OAuth2 tokens
GitLab can serve as an [OAuth2 provider](../api/oauth2.md) to allow other services to access the GitLab API on a user's behalf.
@@ -47,6 +51,10 @@ You can limit the scope and expiration date of project access tokens. When you
create a project access token, GitLab creates a [bot user for projects](../user/project/settings/project_access_tokens.md#bot-users-for-projects).
Bot users for projects are service accounts and do not count as licensed seats.
+You can use the [project access tokens API](../api/project_access_tokens.md) to
+programmatically take action, such as
+[rotating a project access token](../api/project_access_tokens.md#rotate-a-project-access-token).
+
## Group access tokens
[Group access tokens](../user/group/settings/group_access_tokens.md#group-access-tokens)
@@ -60,6 +68,10 @@ You can limit the scope and expiration date of group access tokens. When you
create a group access token, GitLab creates a [bot user for groups](../user/group/settings/group_access_tokens.md#bot-users-for-groups).
Bot users for groups are service accounts and do not count as licensed seats.
+You can use the [group access tokens API](../api/group_access_tokens.md) to
+programmatically take action, such as
+[rotating a project access token](../api/group_access_tokens.md#rotate-a-group-access-token).
+
## Deploy tokens
[Deploy tokens](../user/project/deploy_tokens/index.md) allow you to download (`git clone`) or push and pull packages and container registry images of a project without having a user and a password. Deploy tokens cannot be used with the GitLab API.
diff --git a/doc/user/ai_features.md b/doc/user/ai_features.md
index 7c9480d308f..72d1c5a6a4b 100644
--- a/doc/user/ai_features.md
+++ b/doc/user/ai_features.md
@@ -11,7 +11,7 @@ GitLab is creating AI-assisted features across our DevSecOps platform. These fea
## Enable AI/ML features
-> Introduced in GitLab 16.0 and is [actively being rolled out](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118222).
+> Introduced in GitLab 16.0 and [actively being rolled out](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118222).
Prerequisites:
@@ -133,7 +133,11 @@ To give feedback, select the **Give Feedback** link.
This feature is an [Experiment](../policy/alpha-beta-support.md) on GitLab.com that is powered by OpenAI's GPT-3. It requires the [group-level third-party AI features setting](group/manage.md#group-third-party-ai-features-setting) to be enabled.
-You can generate a merge request summary by using the `/summarize_diff` quick action in a merge request comment. This action posts a comment from a GitLab bot. The comment provides a summary of the changes and the related SHA for when that summary was generated.
+You can generate a merge request summary in a merge request comment.
+
+- In a comment, type `/summarize_diff`.
+
+This action posts a comment from a GitLab bot. The comment provides a summary of the changes and the related SHA for when that summary was generated.
Provide feedback on this experimental feature in [issue 408726](https://gitlab.com/gitlab-org/gitlab/-/issues/408726).
@@ -146,12 +150,15 @@ and the target branch is sent to the large language model referenced above.
This feature is an [Experiment](../policy/alpha-beta-support.md) on GitLab.com that is powered by OpenAI's GPT-3. It requires the [group-level third-party AI features setting](group/manage.md#group-third-party-ai-features-setting) to be enabled.
-When you've completed your review of a merge request and are ready to [submit your review](project/merge_requests/reviews/index.md#submit-a-review) you can choose to have summary generated for you. To generate the summary:
+When you've completed your review of a merge request and are ready to [submit your review](project/merge_requests/reviews/index.md#submit-a-review), you can have a summary generated for you.
-1. Select the AI Actions dropdown list.
+To generate the summary:
+
+1. When you are ready to submit your review, select **Finish review**.
+1. Select **AI Actions** (**{tanuki}**).
1. Select **Summarize my code review**.
-The summary is generated and entered in to the comment box where you can edit and refine prior to submitting with your review.
+The summary is displayed in the comment box. You can edit and refine the summary prior to submitting your review.
Provide feedback on this experimental feature in [issue 408991](https://gitlab.com/gitlab-org/gitlab/-/issues/408991).
@@ -166,12 +173,15 @@ Provide feedback on this experimental feature in [issue 408991](https://gitlab.c
This feature is an [Experiment](../policy/alpha-beta-support.md) on GitLab.com that is powered by OpenAI's GPT-3. It requires the [group-level third-party AI features setting](group/manage.md#group-third-party-ai-features-setting) to be enabled.
-When in a merge request you can choose to have GitLab suggest tests for the file you are reviewing. This can help to determine if appropriate test coverage has been provided or help with writing tests to provide more coverage for your project. To generate a test suggestion:
+In a merge request, you can get a list of suggested tests for the file you are reviewing. This functionality can help determine if appropriate test coverage has been provided, or if you need more coverage for your project.
+
+To generate a test suggestion:
-1. Select the menu icon on the header of a file.
+1. In a merge request, select the **Changes** tab.
+1. On the header for the file, in the upper-right corner, select **Options** (**{ellipsis_v}**).
1. Select **Generate test with AI**.
-A sidebar opens where the test suggestion is generated. From there you can choose to copy that suggestion in to your editor as the start of your tests.
+The test suggestion is generated in a sidebar. You can copy the suggestion to your editor and use it as the start of your tests.
Feedback on this experimental feature can be provided in [issue 408995](https://gitlab.com/gitlab-org/gitlab/-/issues/408995).
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index 1d279436d2c..c7c41ee8558 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -122,8 +122,10 @@ To protect a branch:
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. From the **Branch** dropdown list, select the branch you want to protect.
-1. From the **Allowed to merge** list, select a role, or group that can merge into this branch. In GitLab Premium, you can also add users.
-1. From the **Allowed to push and merge** list, select a role, group, or user that can push to this branch. In GitLab Premium, you can also add users.
+1. From the **Allowed to merge** list, select a role that can merge into this branch.
+ In GitLab Premium and Ultimate, you can also add groups or individual users.
+1. From the **Allowed to push and merge** list, select a role that can push to this branch.
+ In GitLab Premium and Ultimate, you can also add groups or individual users.
1. Select **Protect**.
The protected branch displays in the list of protected branches.
@@ -152,8 +154,10 @@ To protect multiple branches at the same time:
| `production/*` | `production/app-server`, `production/load-balancer` |
| `*gitlab*` | `gitlab`, `gitlab/staging`, `master/gitlab/production` |
-1. From the **Allowed to merge** list, select a role, or group that can merge into this branch. In GitLab Premium, you can also add users.
-1. From the **Allowed to push and merge** list, select a role, group, or user that can push to this branch. In GitLab Premium, you can also add users.
+1. From the **Allowed to merge** list, select a role that can merge into
+ this branch.
+1. From the **Allowed to push and merge** list, select a role that can
+ push to this branch. In GitLab Premium or Ultimate, you can also add groups or individual users.
1. Select **Protect**.
The protected branch displays in the list of protected branches.
diff --git a/doc/user/project/protected_tags.md b/doc/user/project/protected_tags.md
index 3af475afa4f..ac9f990a598 100644
--- a/doc/user/project/protected_tags.md
+++ b/doc/user/project/protected_tags.md
@@ -39,7 +39,8 @@ Prerequisites:
1. Select **Tag**.
1. Enter the string to use for tag matching. Wildcards (`*`) are supported.
1. Select **Create wildcard**.
-1. In **Allowed to create** , select either the users or roles that may create protected tags.
+1. In **Allowed to create** , select roles that may create protected tags.
+ In GitLab Premium and Ultimate, you can also select groups or individual users.
1. Select **Protect**.
The protected tag (or wildcard) displays in the **Protected tags** list.
diff --git a/lib/gitlab/ci/parsers/security/common.rb b/lib/gitlab/ci/parsers/security/common.rb
index 447136df81f..f393d86dea4 100644
--- a/lib/gitlab/ci/parsers/security/common.rb
+++ b/lib/gitlab/ci/parsers/security/common.rb
@@ -123,7 +123,6 @@ module Gitlab
uuid: uuid,
report_type: report.type,
name: finding_name(data, identifiers, location),
- compare_key: data['cve'] || '',
location: location,
evidence: evidence,
severity: parse_severity_level(data['severity']),
diff --git a/lib/gitlab/ci/reports/security/finding.rb b/lib/gitlab/ci/reports/security/finding.rb
index bf48c7d0bb7..aad730349cd 100644
--- a/lib/gitlab/ci/reports/security/finding.rb
+++ b/lib/gitlab/ci/reports/security/finding.rb
@@ -7,7 +7,6 @@ module Gitlab
class Finding
include ::VulnerabilityFindingHelpers
- attr_reader :compare_key
attr_reader :confidence
attr_reader :identifiers
attr_reader :flags
@@ -33,10 +32,10 @@ module Gitlab
delegate :file_path, :start_line, :end_line, to: :location
+ alias_method :compare_key, :uuid
alias_method :cve, :compare_key
- def initialize(compare_key:, identifiers:, flags: [], links: [], remediations: [], location:, evidence:, metadata_version:, name:, original_data:, report_type:, scanner:, scan:, uuid:, confidence: nil, severity: nil, details: {}, signatures: [], project_id: nil, vulnerability_finding_signatures_enabled: false, found_by_pipeline: nil) # rubocop:disable Metrics/ParameterLists
- @compare_key = compare_key
+ def initialize(identifiers:, flags: [], links: [], remediations: [], location:, evidence:, metadata_version:, name:, original_data:, report_type:, scanner:, scan:, uuid:, confidence: nil, severity: nil, details: {}, signatures: [], project_id: nil, vulnerability_finding_signatures_enabled: false, found_by_pipeline: nil) # rubocop:disable Metrics/ParameterLists
@confidence = confidence
@identifiers = identifiers
@flags = flags
@@ -203,7 +202,7 @@ module Gitlab
private
def generate_project_fingerprint
- Digest::SHA1.hexdigest(compare_key)
+ Digest::SHA1.hexdigest(compare_key.to_s)
end
def location_fingerprints
diff --git a/lib/gitlab/database/schema_validation/schema_inconsistency.rb b/lib/gitlab/database/schema_validation/schema_inconsistency.rb
index 6f50603e784..b682b26775a 100644
--- a/lib/gitlab/database/schema_validation/schema_inconsistency.rb
+++ b/lib/gitlab/database/schema_validation/schema_inconsistency.rb
@@ -9,6 +9,8 @@ module Gitlab
belongs_to :issue
validates :object_name, :valitador_name, :table_name, presence: true
+
+ scope :with_open_issues, -> { joins(:issue).where('issue.state_id': Issue.available_states[:opened]) }
end
end
end
diff --git a/lib/gitlab/database/schema_validation/track_inconsistency.rb b/lib/gitlab/database/schema_validation/track_inconsistency.rb
index 47c3492971c..5e437e2f2ff 100644
--- a/lib/gitlab/database/schema_validation/track_inconsistency.rb
+++ b/lib/gitlab/database/schema_validation/track_inconsistency.rb
@@ -86,7 +86,7 @@ module Gitlab
end
def inconsistency_record
- schema_inconsistency_model.find_by(
+ schema_inconsistency_model.with_open_issues.find_by(
object_name: inconsistency.object_name,
table_name: inconsistency.table_name,
valitador_name: inconsistency.type
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 78bc8b447b9..ccf1f669e2f 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5645,6 +5645,9 @@ msgstr ""
msgid "ApprovalRule|Name"
msgstr ""
+msgid "ApprovalRule|Need triage"
+msgstr ""
+
msgid "ApprovalRule|Needs triage"
msgstr ""
@@ -29460,6 +29463,9 @@ msgstr ""
msgid "Navigation|Leave admin mode"
msgstr ""
+msgid "Navigation|Main navigation"
+msgstr ""
+
msgid "Navigation|Manage"
msgstr ""
@@ -40367,6 +40373,9 @@ msgstr ""
msgid "SecurityOrchestration| and "
msgstr ""
+msgid "SecurityOrchestration| and all the following apply:"
+msgstr ""
+
msgid "SecurityOrchestration| or "
msgstr ""
@@ -40397,7 +40406,7 @@ msgstr ""
msgid "SecurityOrchestration|%{scanners}"
msgstr ""
-msgid "SecurityOrchestration|%{scanners} %{vulnerabilitiesAllowed} %{severities} in an open merge request targeting %{branches}."
+msgid "SecurityOrchestration|%{state} and %{statuses}"
msgstr ""
msgid "SecurityOrchestration|, and %{count} more"
@@ -40442,9 +40451,6 @@ msgstr ""
msgid "SecurityOrchestration|An error occurred while fetching the scan result policies."
msgstr ""
-msgid "SecurityOrchestration|Any security scanner finds"
-msgstr ""
-
msgid "SecurityOrchestration|Are you sure you want to delete this policy? This action cannot be undone."
msgstr ""
@@ -40547,9 +40553,6 @@ msgstr ""
msgid "SecurityOrchestration|License Scan"
msgstr ""
-msgid "SecurityOrchestration|License scanner finds any license %{matching} %{licenses}%{detection} in an open merge request targeting %{branches}."
-msgstr ""
-
msgid "SecurityOrchestration|New policy"
msgstr ""
@@ -40703,6 +40706,9 @@ msgstr ""
msgid "SecurityOrchestration|Select users"
msgstr ""
+msgid "SecurityOrchestration|Severity is %{severity}."
+msgstr ""
+
msgid "SecurityOrchestration|Something went wrong, unable to fetch policies"
msgstr ""
@@ -40778,6 +40784,15 @@ msgstr ""
msgid "SecurityOrchestration|View policy project"
msgstr ""
+msgid "SecurityOrchestration|Vulnerabilities are %{vulnerabilityStates}."
+msgstr ""
+
+msgid "SecurityOrchestration|When %{scanners} %{vulnerabilitiesAllowed} %{vulnerability} in an open merge request targeting %{branches}%{criteriaApply}"
+msgstr ""
+
+msgid "SecurityOrchestration|When license scanner finds any license %{matching} %{licenses}%{detection} in an open merge request targeting %{branches}."
+msgstr ""
+
msgid "SecurityOrchestration|YAML"
msgstr ""
@@ -40793,6 +40808,9 @@ msgstr ""
msgid "SecurityOrchestration|any"
msgstr ""
+msgid "SecurityOrchestration|any security scanner finds"
+msgstr ""
+
msgid "SecurityOrchestration|branch"
msgstr ""
@@ -40826,12 +40844,6 @@ msgstr ""
msgid "SecurityOrchestration|the %{namespaces} namespace"
msgstr ""
-msgid "SecurityOrchestration|vulnerabilities"
-msgstr ""
-
-msgid "SecurityOrchestration|vulnerability"
-msgstr ""
-
msgid "SecurityPolicies|Invalid or empty policy"
msgstr ""
@@ -47834,6 +47846,9 @@ msgstr ""
msgid "Unauthenticated web rate limit period in seconds"
msgstr ""
+msgid "Unauthorized to update the environment"
+msgstr ""
+
msgid "Unavailable"
msgstr ""
diff --git a/spec/factories/ci/reports/security/findings.rb b/spec/factories/ci/reports/security/findings.rb
index c57a2dd479f..2641c618b09 100644
--- a/spec/factories/ci/reports/security/findings.rb
+++ b/spec/factories/ci/reports/security/findings.rb
@@ -2,7 +2,6 @@
FactoryBot.define do
factory :ci_reports_security_finding, class: '::Gitlab::Ci::Reports::Security::Finding' do
- compare_key { "#{identifiers.first&.external_type}:#{identifiers.first&.external_id}:#{location.fingerprint}" }
confidence { :medium }
identifiers { Array.new(1) { association(:ci_reports_security_identifier) } }
location factory: :ci_reports_security_locations_sast
diff --git a/spec/fixtures/security_reports/master/gl-common-scanning-report.json b/spec/fixtures/security_reports/master/gl-common-scanning-report.json
index 4c494963a79..d1f824b90fa 100644
--- a/spec/fixtures/security_reports/master/gl-common-scanning-report.json
+++ b/spec/fixtures/security_reports/master/gl-common-scanning-report.json
@@ -90,6 +90,7 @@
"message": "Remediation for this vulnerability should remediate CVE-2140 as well",
"description": "",
"cve": "CVE-2139",
+ "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d4",
"severity": "High",
"solution": "Upgrade to latest version.",
"scanner": {
@@ -132,6 +133,7 @@
"message": "Remediation for this vulnerability should remediate CVE-2139 as well",
"description": "",
"cve": "CVE-2140",
+ "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d5",
"severity": "High",
"solution": "Upgrade to latest version.",
"scanner": {
@@ -439,10 +441,10 @@
{
"fixes": [
{
- "cve": "CVE-2139"
+ "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d4"
},
{
- "cve": "CVE-2140"
+ "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d5"
}
],
"summary": "this remediates CVE-2139 and CVE-2140",
diff --git a/spec/frontend/super_sidebar/components/frequent_items_list_spec.js b/spec/frontend/super_sidebar/components/frequent_items_list_spec.js
index 5329a8f5da3..63dd941974a 100644
--- a/spec/frontend/super_sidebar/components/frequent_items_list_spec.js
+++ b/spec/frontend/super_sidebar/components/frequent_items_list_spec.js
@@ -1,4 +1,4 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import { s__ } from '~/locale';
import FrequentItemsList from '~/super_sidebar/components//frequent_items_list.vue';
import ItemsList from '~/super_sidebar/components/items_list.vue';
@@ -18,18 +18,20 @@ describe('FrequentItemsList component', () => {
const findListTitle = () => wrapper.findByTestId('list-title');
const findItemsList = () => wrapper.findComponent(ItemsList);
const findEmptyText = () => wrapper.findByTestId('empty-text');
+ const findRemoveItemButton = () => wrapper.findByTestId('item-remove');
- const createWrapper = ({ props = {} } = {}) => {
- wrapper = shallowMountExtended(FrequentItemsList, {
+ const createWrapperFactory = (mountFn = shallowMountExtended) => () => {
+ wrapper = mountFn(FrequentItemsList, {
propsData: {
title,
pristineText,
storageKey,
maxItems,
- ...props,
},
});
};
+ const createWrapper = createWrapperFactory();
+ const createFullWrapper = createWrapperFactory(mountExtended);
describe('default', () => {
beforeEach(() => {
@@ -64,16 +66,20 @@ describe('FrequentItemsList component', () => {
it('does not render the empty text slot', () => {
expect(findEmptyText().exists()).toBe(false);
});
+ });
- describe('items editing', () => {
- it('remove-item event emission from items-list causes list item to be removed', async () => {
- const localStorageProjects = findItemsList().props('items');
+ describe('items editing', () => {
+ beforeEach(() => {
+ window.localStorage.setItem(storageKey, cachedFrequentProjects);
+ createFullWrapper();
+ });
- await findItemsList().vm.$emit('remove-item', localStorageProjects[0]);
+ it('remove-item event emission from items-list causes list item to be removed', async () => {
+ const localStorageProjects = findItemsList().props('items');
+ await findRemoveItemButton().trigger('click');
- expect(findItemsList().props('items')).toHaveLength(maxItems - 1);
- expect(findItemsList().props('items')).not.toContain(localStorageProjects[0]);
- });
+ expect(findItemsList().props('items')).toHaveLength(maxItems - 1);
+ expect(findItemsList().props('items')).not.toContain(localStorageProjects[0]);
});
});
});
diff --git a/spec/frontend/super_sidebar/components/help_center_spec.js b/spec/frontend/super_sidebar/components/help_center_spec.js
index 808c30436a3..a727fe6c243 100644
--- a/spec/frontend/super_sidebar/components/help_center_spec.js
+++ b/spec/frontend/super_sidebar/components/help_center_spec.js
@@ -4,7 +4,7 @@ import toggleWhatsNewDrawer from '~/whats_new';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import HelpCenter from '~/super_sidebar/components/help_center.vue';
import { helpPagePath } from '~/helpers/help_page_helper';
-import { DOMAIN, PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
+import { DOCS_URL, FORUM_URL, PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { STORAGE_KEY } from '~/whats_new/utils/notification';
import { helpCenterState } from '~/super_sidebar/constants';
@@ -52,7 +52,7 @@ describe('HelpCenter component', () => {
},
{
text: HelpCenter.i18n.docs,
- href: `https://docs.${DOMAIN}`,
+ href: DOCS_URL,
extraAttrs: trackingAttrs('gitlab_documentation'),
},
{
@@ -62,7 +62,7 @@ describe('HelpCenter component', () => {
},
{
text: HelpCenter.i18n.forum,
- href: `https://forum.${DOMAIN}/`,
+ href: FORUM_URL,
extraAttrs: trackingAttrs('community_forum'),
},
{
diff --git a/spec/frontend/super_sidebar/components/items_list_spec.js b/spec/frontend/super_sidebar/components/items_list_spec.js
index d5e8043cce9..8e00984f500 100644
--- a/spec/frontend/super_sidebar/components/items_list_spec.js
+++ b/spec/frontend/super_sidebar/components/items_list_spec.js
@@ -1,5 +1,4 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ItemsList from '~/super_sidebar/components/items_list.vue';
import NavItem from '~/super_sidebar/components/nav_item.vue';
import { cachedFrequentProjects } from '../mock_data';
@@ -12,8 +11,8 @@ describe('ItemsList component', () => {
const findNavItems = () => wrapper.findAllComponents(NavItem);
- const createWrapper = ({ props = {}, slots = {}, mountFn = shallowMountExtended } = {}) => {
- wrapper = mountFn(ItemsList, {
+ const createWrapper = ({ props = {}, slots = {} } = {}) => {
+ wrapper = shallowMountExtended(ItemsList, {
propsData: {
...props,
},
@@ -61,41 +60,4 @@ describe('ItemsList component', () => {
expect(wrapper.findByTestId(testId).exists()).toBe(true);
});
-
- describe('item removal', () => {
- const findRemoveButton = () => wrapper.findByTestId('item-remove');
- const mockProject = {
- ...firstMockedProject,
- title: firstMockedProject.name,
- };
-
- beforeEach(() => {
- createWrapper({
- props: {
- items: [mockProject],
- },
- mountFn: mountExtended,
- });
- });
-
- it('renders the remove button', () => {
- const itemRemoveButton = findRemoveButton();
-
- expect(itemRemoveButton.exists()).toBe(true);
- expect(itemRemoveButton.attributes('title')).toBe('Remove');
- expect(itemRemoveButton.findComponent(GlIcon).props('name')).toBe('dash');
- });
-
- it('emits `remove-item` event with item param when remove button is clicked', () => {
- const itemRemoveButton = findRemoveButton();
-
- itemRemoveButton.vm.$emit(
- 'click',
- { stopPropagation: jest.fn(), preventDefault: jest.fn() },
- mockProject,
- );
-
- expect(wrapper.emitted('remove-item')).toEqual([[mockProject]]);
- });
- });
});
diff --git a/spec/frontend/super_sidebar/components/sidebar_menu_spec.js b/spec/frontend/super_sidebar/components/sidebar_menu_spec.js
index 9b726b620dd..19471f28fab 100644
--- a/spec/frontend/super_sidebar/components/sidebar_menu_spec.js
+++ b/spec/frontend/super_sidebar/components/sidebar_menu_spec.js
@@ -1,4 +1,5 @@
import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { s__ } from '~/locale';
import SidebarMenu from '~/super_sidebar/components/sidebar_menu.vue';
import PinnedSection from '~/super_sidebar/components/pinned_section.vue';
import { PANELS_WITH_PINS } from '~/super_sidebar/constants';
@@ -181,4 +182,11 @@ describe('SidebarMenu component', () => {
expect(findMainMenuSeparator().exists()).toBe(false);
});
});
+
+ describe('template', () => {
+ it('adds aria-label attribute to nav element', () => {
+ createWrapper({ ...sidebarData });
+ expect(wrapper.find('nav').attributes('aria-label')).toBe(s__('Navigation|Main navigation'));
+ });
+ });
});
diff --git a/spec/frontend/vue_shared/components/chronic_duration_input_spec.js b/spec/frontend/vue_shared/components/chronic_duration_input_spec.js
index 2a40511affb..374babe3a97 100644
--- a/spec/frontend/vue_shared/components/chronic_duration_input_spec.js
+++ b/spec/frontend/vue_shared/components/chronic_duration_input_spec.js
@@ -310,12 +310,11 @@ describe('vue_shared/components/chronic_duration_input', () => {
});
it('passes updated prop via v-model', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ value: MOCK_VALUE });
+ textElement.value = '2hr20min';
+ textElement.dispatchEvent(new Event('input'));
await nextTick();
- expect(textElement.value).toBe('2 hrs 20 mins');
+ expect(textElement.value).toBe('2hr20min');
expect(hiddenElement.value).toBe(MOCK_VALUE.toString());
});
});
diff --git a/spec/graphql/mutations/environments/update_spec.rb b/spec/graphql/mutations/environments/update_spec.rb
new file mode 100644
index 00000000000..87c1bd5a44b
--- /dev/null
+++ b/spec/graphql/mutations/environments/update_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Environments::Update, feature_category: :environment_management do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
+
+ let(:user) { maintainer }
+
+ subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
+
+ before_all do
+ project.add_maintainer(maintainer)
+ project.add_reporter(reporter)
+ end
+
+ describe '#resolve' do
+ subject { mutation.resolve(id: environment_id, external_url: external_url) }
+
+ let(:environment_id) { environment.to_global_id }
+ let(:external_url) { 'https://gitlab.com/' }
+
+ context 'when service execution succeeded' do
+ it 'returns no errors' do
+ expect(subject[:errors]).to be_empty
+ end
+
+ it 'updates the environment' do
+ expect(subject[:environment][:external_url]).to eq(external_url)
+ end
+ end
+
+ context 'when service cannot update the attribute' do
+ let(:external_url) { 'http://${URL}' }
+
+ it 'returns an error' do
+ expect(subject)
+ .to eq({
+ environment: environment,
+ errors: ['External url URI is invalid']
+ })
+ end
+ end
+
+ context 'when user is reporter who does not have permission to access the environment' do
+ let(:user) { reporter }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
+ end
+ end
+ end
+end
diff --git a/spec/helpers/safe_format_helper_spec.rb b/spec/helpers/safe_format_helper_spec.rb
index ced48b0c9c1..33c4c86ecc8 100644
--- a/spec/helpers/safe_format_helper_spec.rb
+++ b/spec/helpers/safe_format_helper_spec.rb
@@ -37,5 +37,22 @@ RSpec.describe SafeFormatHelper, feature_category: :shared do
.to raise_error ArgumentError, message
end
end
+
+ context 'with a view component' do
+ let(:view_component) do
+ Class.new(ViewComponent::Base) do
+ include SafeFormatHelper
+
+ def call
+ safe_format('<b>%{value}</b>', value: '<br>')
+ end
+ end
+ end
+
+ it 'safetly formats' do
+ expect(view_component.new.call)
+ .to eq('&lt;b&gt;&lt;br&gt;&lt;/b&gt;')
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
index 421aa29f860..bce82a5fb4a 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -184,8 +184,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera
let(:artifact) { build(:ci_job_artifact, :common_security_report_with_blank_names) }
context 'when message is provided' do
+ let(:finding) { report.findings.first }
+
it 'sets message from the report as a finding name' do
- finding = report.findings.find { |x| x.compare_key == 'CVE-1020' }
expected_name = Gitlab::Json.parse(finding.raw_metadata)['message']
expect(finding.name).to eq(expected_name)
@@ -194,8 +195,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera
context 'when message is not provided' do
context 'and name is provided' do
+ let(:finding) { report.findings.second }
+
it 'sets name from the report as a name' do
- finding = report.findings.find { |x| x.compare_key == 'CVE-1030' }
expected_name = Gitlab::Json.parse(finding.raw_metadata)['name']
expect(finding.name).to eq(expected_name)
@@ -203,11 +205,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera
end
context 'and name is not provided' do
+ let(:finding) { report.findings[2] }
+
context 'when location does not exist' do
let(:location) { nil }
it 'returns only identifier name' do
- finding = report.findings.find { |x| x.compare_key == 'CVE-2017-11429' }
expect(finding.name).to eq("CVE-2017-11429")
end
end
@@ -215,21 +218,22 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera
context 'when location exists' do
context 'when CVE identifier exists' do
it 'combines identifier with location to create name' do
- finding = report.findings.find { |x| x.compare_key == 'CVE-2017-11429' }
expect(finding.name).to eq("CVE-2017-11429 in yarn.lock")
end
end
context 'when CWE identifier exists' do
+ let(:finding) { report.findings[3] }
+
it 'combines identifier with location to create name' do
- finding = report.findings.find { |x| x.compare_key == 'CWE-2017-11429' }
expect(finding.name).to eq("CWE-2017-11429 in yarn.lock")
end
end
context 'when neither CVE nor CWE identifier exist' do
+ let(:finding) { report.findings[4] }
+
it 'combines identifier with location to create name' do
- finding = report.findings.find { |x| x.compare_key == 'OTHER-2017-11429' }
expect(finding.name).to eq("other-2017-11429 in yarn.lock")
end
end
@@ -240,8 +244,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera
describe 'parsing finding.details' do
context 'when details are provided' do
+ let(:finding) { report.findings[4] }
+
it 'sets details from the report' do
- finding = report.findings.find { |x| x.compare_key == 'CVE-1020' }
expected_details = Gitlab::Json.parse(finding.raw_metadata)['details']
expect(finding.details).to eq(expected_details)
@@ -249,8 +254,9 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnera
end
context 'when details are not provided' do
+ let(:finding) { report.findings[5] }
+
it 'sets empty hash' do
- finding = report.findings.find { |x| x.compare_key == 'CVE-1030' }
expect(finding.details).to eq({})
end
end
diff --git a/spec/lib/gitlab/ci/reports/security/report_spec.rb b/spec/lib/gitlab/ci/reports/security/report_spec.rb
index d7f967f1c55..dabee0f32de 100644
--- a/spec/lib/gitlab/ci/reports/security/report_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/report_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Reports::Security::Report do
+RSpec.describe Gitlab::Ci::Reports::Security::Report, feature_category: :vulnerability_management do
let_it_be(:pipeline) { create(:ci_pipeline) }
let(:created_at) { 2.weeks.ago }
@@ -89,7 +89,7 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
let(:other_report) do
create(
:ci_reports_security_report,
- findings: [create(:ci_reports_security_finding, compare_key: 'other_finding')],
+ findings: [create(:ci_reports_security_finding)],
scanners: [create(:ci_reports_security_scanner, external_id: 'other_scanner', name: 'Other Scanner')],
identifiers: [create(:ci_reports_security_identifier, external_id: 'other_id', name: 'other_scanner')]
)
diff --git a/spec/lib/gitlab/database/schema_validation/schema_inconsistency_spec.rb b/spec/lib/gitlab/database/schema_validation/schema_inconsistency_spec.rb
index 7d6a279def9..653ca0a2b2b 100644
--- a/spec/lib/gitlab/database/schema_validation/schema_inconsistency_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/schema_inconsistency_spec.rb
@@ -14,4 +14,27 @@ RSpec.describe Gitlab::Database::SchemaValidation::SchemaInconsistency, type: :m
it { is_expected.to validate_presence_of(:valitador_name) }
it { is_expected.to validate_presence_of(:table_name) }
end
+
+ describe 'scopes' do
+ describe '.with_open_issues' do
+ subject(:inconsistencies) { described_class.with_open_issues }
+
+ let(:closed_issue) { create(:issue, :closed) }
+ let(:open_issue) { create(:issue, :opened) }
+
+ let!(:schema_inconsistency_with_issue_closed) do
+ create(:schema_inconsistency, object_name: 'index_name', table_name: 'achievements',
+ valitador_name: 'different_definition_indexes', issue: closed_issue)
+ end
+
+ let!(:schema_inconsistency_with_issue_opened) do
+ create(:schema_inconsistency, object_name: 'index_name', table_name: 'achievements',
+ valitador_name: 'different_definition_indexes', issue: open_issue)
+ end
+
+ it 'returns only schema inconsistencies with GitLab issues open' do
+ expect(inconsistencies).to eq([schema_inconsistency_with_issue_opened])
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb b/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb
index 84db721fc2d..bb83dfa796f 100644
--- a/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/track_inconsistency_spec.rb
@@ -63,19 +63,31 @@ RSpec.describe Gitlab::Database::SchemaValidation::TrackInconsistency, feature_c
end
context 'when the schema inconsistency already exists' do
+ let!(:schema_inconsistency) do
+ create(:schema_inconsistency, object_name: 'index_name', table_name: 'achievements',
+ valitador_name: 'different_definition_indexes')
+ end
+
before do
project.add_developer(user)
end
- let!(:schema_inconsistency) do
- create(:schema_inconsistency, object_name: 'index_name', table_name: 'achievements',
- valitador_name: 'different_definition_indexes')
+ context 'when the GitLab issue is open' do
+ it 'does not create a new schema inconsistency record' do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ schema_inconsistency.issue.update!(state_id: Issue.available_states[:opened])
+
+ expect { execute }.not_to change { Gitlab::Database::SchemaValidation::SchemaInconsistency.count }
+ end
end
- it 'does not create a schema inconsistency record' do
- allow(Gitlab).to receive(:com?).and_return(true)
+ context 'when the GitLab is not open' do
+ it 'creates a new schema inconsistency record' do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ schema_inconsistency.issue.update!(state_id: Issue.available_states[:closed])
- expect { execute }.not_to change { Gitlab::Database::SchemaValidation::SchemaInconsistency.count }
+ expect { execute }.to change { Gitlab::Database::SchemaValidation::SchemaInconsistency.count }
+ end
end
end
end
diff --git a/spec/requests/api/graphql/mutations/environments/update_spec.rb b/spec/requests/api/graphql/mutations/environments/update_spec.rb
new file mode 100644
index 00000000000..9c68b3a024c
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/environments/update_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Update Environment', feature_category: :deployment_management do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
+ let_it_be(:developer) { create(:user).tap { |u| project.add_maintainer(u) } }
+
+ let(:environment_id) { environment.to_global_id.to_s }
+ let(:current_user) { developer }
+
+ let(:mutation) do
+ graphql_mutation(:environment_update, input)
+ end
+
+ context 'when updating external URL' do
+ let(:input) do
+ {
+ id: environment_id,
+ external_url: 'https://gitlab.com/'
+ }
+ end
+
+ it 'updates successfully' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to change { environment.reload.external_url }.to('https://gitlab.com/')
+
+ expect(graphql_mutation_response(:environment_update)['errors']).to be_empty
+ end
+
+ context 'when url is invalid' do
+ let(:input) do
+ {
+ id: environment_id,
+ external_url: 'http://${URL}'
+ }
+ end
+
+ it 'returns error' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.not_to change { environment.reload.external_url }
+
+ expect(graphql_mutation_response(:environment_update)['errors'].first).to include('URI is invalid')
+ end
+ end
+ end
+
+ context 'when updating tier' do
+ let(:input) do
+ {
+ id: environment_id,
+ tier: 'STAGING'
+ }
+ end
+
+ it 'updates successfully' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to change { environment.reload.tier }.to('staging')
+
+ expect(graphql_mutation_response(:environment_update)['errors']).to be_empty
+ end
+ end
+end
diff --git a/spec/services/environments/update_service_spec.rb b/spec/services/environments/update_service_spec.rb
new file mode 100644
index 00000000000..72ace3b050e
--- /dev/null
+++ b/spec/services/environments/update_service_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Environments::UpdateService, feature_category: :environment_management do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+ let_it_be(:reporter) { create(:user).tap { |u| project.add_reporter(u) } }
+ let_it_be(:environment) { create(:environment, project: project) }
+
+ let(:service) { described_class.new(project, current_user, params) }
+ let(:current_user) { developer }
+ let(:params) { {} }
+
+ describe '#execute' do
+ subject { service.execute(environment) }
+
+ let(:params) { { external_url: 'https://gitlab.com/' } }
+
+ it 'updates the external URL' do
+ expect { subject }.to change { environment.reload.external_url }.to('https://gitlab.com/')
+ end
+
+ it 'returns successful response' do
+ response = subject
+
+ expect(response).to be_success
+ expect(response.payload[:environment]).to eq(environment)
+ end
+
+ context 'when params contain invalid value' do
+ let(:params) { { external_url: 'http://${URL}' } }
+
+ it 'returns an error' do
+ response = subject
+
+ expect(response).to be_error
+ expect(response.message).to match_array("External url URI is invalid")
+ expect(response.payload[:environment]).to eq(environment)
+ end
+ end
+
+ context 'when user is reporter' do
+ let(:current_user) { reporter }
+
+ it 'returns an error' do
+ response = subject
+
+ expect(response).to be_error
+ expect(response.message).to eq('Unauthorized to update the environment')
+ expect(response.payload[:environment]).to eq(environment)
+ end
+ end
+ end
+end
diff --git a/spec/services/security/merge_reports_service_spec.rb b/spec/services/security/merge_reports_service_spec.rb
index 809d0b27c20..a101003a7dd 100644
--- a/spec/services/security/merge_reports_service_spec.rb
+++ b/spec/services/security/merge_reports_service_spec.rb
@@ -19,7 +19,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
build(:ci_reports_security_finding,
identifiers: [identifier_1_primary, identifier_1_cve],
scanner: scanner_1,
- severity: :low
+ severity: :low,
+ uuid: '61eb8e3e-3be1-4d6c-ba26-4e0dd4f94610'
)
end
@@ -27,7 +28,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
build(:ci_reports_security_finding,
identifiers: [identifier_1_primary, identifier_1_cve],
scanner: scanner_1,
- severity: :low
+ severity: :low,
+ uuid: '61eb8e3e-3be1-4d6c-ba26-4e0dd4f94611'
)
end
@@ -36,7 +38,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
identifiers: [identifier_2_primary, identifier_2_cve],
location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34),
scanner: scanner_2,
- severity: :medium
+ severity: :medium,
+ uuid: '61eb8e3e-3be1-4d6c-ba26-4e0dd4f94612'
)
end
@@ -45,7 +48,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
identifiers: [identifier_2_primary, identifier_2_cve],
location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34),
scanner: scanner_2,
- severity: :medium
+ severity: :medium,
+ uuid: '61eb8e3e-3be1-4d6c-ba26-4e0dd4f94613'
)
end
@@ -54,7 +58,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
identifiers: [identifier_2_primary, identifier_2_cve],
location: build(:ci_reports_security_locations_sast, start_line: 42, end_line: 44),
scanner: scanner_2,
- severity: :medium
+ severity: :medium,
+ uuid: '61eb8e3e-3be1-4d6c-ba26-4e0dd4f94614'
)
end
@@ -62,7 +67,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
build(:ci_reports_security_finding,
identifiers: [identifier_cwe],
scanner: scanner_3,
- severity: :high
+ severity: :high,
+ uuid: '61eb8e3e-3be1-4d6c-ba26-4e0dd4f94615'
)
end
@@ -70,7 +76,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
build(:ci_reports_security_finding,
identifiers: [identifier_cwe],
scanner: scanner_1,
- severity: :critical
+ severity: :critical,
+ uuid: '61eb8e3e-3be1-4d6c-ba26-4e0dd4f94616'
)
end
@@ -78,7 +85,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
build(:ci_reports_security_finding,
identifiers: [identifier_wasc],
scanner: scanner_1,
- severity: :medium
+ severity: :medium,
+ uuid: '61eb8e3e-3be1-4d6c-ba26-4e0dd4f94617'
)
end
@@ -86,7 +94,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
build(:ci_reports_security_finding,
identifiers: [identifier_wasc],
scanner: scanner_2,
- severity: :critical
+ severity: :critical,
+ uuid: '61eb8e3e-3be1-4d6c-ba26-4e0dd4f94618'
)
end
@@ -190,8 +199,8 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
finding_cwe_2,
finding_wasc_2,
finding_cwe_1,
- finding_id_2_loc_2,
finding_id_2_loc_1,
+ finding_id_2_loc_2,
finding_wasc_1,
finding_id_1
])
@@ -217,9 +226,32 @@ RSpec.describe Security::MergeReportsService, '#execute', feature_category: :cod
let(:identifier_cve) { build(:ci_reports_security_identifier, external_id: 'CVE-2019-123', external_type: 'cve') }
let(:identifier_semgrep) { build(:ci_reports_security_identifier, external_id: 'rules.bandit.B105', external_type: 'semgrep_id') }
- let(:finding_id_1) { build(:ci_reports_security_finding, identifiers: [identifier_bandit, identifier_cve], scanner: bandit_scanner, report_type: :sast) }
- let(:finding_id_2) { build(:ci_reports_security_finding, identifiers: [identifier_cve], scanner: semgrep_scanner, report_type: :sast) }
- let(:finding_id_3) { build(:ci_reports_security_finding, identifiers: [identifier_semgrep], scanner: semgrep_scanner, report_type: :sast) }
+ let(:finding_id_1) do
+ build(
+ :ci_reports_security_finding,
+ identifiers: [identifier_bandit, identifier_cve],
+ scanner: bandit_scanner,
+ report_type: :sast,
+ uuid: '21ab978a-7052-5428-af0b-c7a4b3fe5020')
+ end
+
+ let(:finding_id_2) do
+ build(
+ :ci_reports_security_finding,
+ identifiers: [identifier_cve],
+ scanner: semgrep_scanner,
+ report_type: :sast,
+ uuid: '21ab978a-7052-5428-af0b-c7a4b3fe5021')
+ end
+
+ let(:finding_id_3) do
+ build(
+ :ci_reports_security_finding,
+ identifiers: [identifier_semgrep],
+ scanner: semgrep_scanner,
+ report_type: :sast,
+ uuid: '21ab978a-7052-5428-af0b-c7a4b3fe5022')
+ end
let(:bandit_report) do
build(:ci_reports_security_report,