Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-09-07 15:11:26 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-09-07 15:11:26 +0300
commit325245c6f5803227b13051883d00da5b3c235ab0 (patch)
treedc30fdeb752d4699a27c2b45ba5992e77bf66ccc
parent378308b6cde44eb1a320b9202ff8946a911f35f6 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/qa.gitlab-ci.yml1
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue22
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue72
-rw-r--r--app/helpers/invite_members_helper.rb8
-rw-r--r--app/helpers/profiles_helper.rb4
-rw-r--r--app/models/concerns/enums/ci/commit_status.rb1
-rw-r--r--app/presenters/commit_status_presenter.rb17
-rw-r--r--app/views/profiles/_email_settings.html.haml2
-rw-r--r--app/views/projects/project_members/index.html.haml2
-rw-r--r--config/feature_flags/development/groups_tokens_optional_encryption.yml2
-rw-r--r--config/feature_flags/development/projects_tokens_optional_encryption.yml2
-rw-r--r--config/feature_flags/development/surface_environment_creation_failure.yml8
-rw-r--r--config/feature_flags/development/surface_environment_creation_failure_override.yml8
-rw-r--r--doc/administration/redis/replication_and_failover_external.md2
-rw-r--r--doc/administration/reference_architectures/10k_users.md2
-rw-r--r--doc/administration/reference_architectures/1k_users.md2
-rw-r--r--doc/api/graphql/audit_report.md2
-rw-r--r--doc/api/graphql/custom_emoji.md4
-rw-r--r--doc/api/graphql/index.md2
-rw-r--r--doc/api/graphql/reference/index.md69
-rw-r--r--doc/api/graphql/removed_items.md4
-rw-r--r--doc/api/graphql/users_example.md2
-rw-r--r--doc/api/v3_to_v4.md2
-rw-r--r--doc/api/version.md2
-rw-r--r--doc/api/vulnerabilities.md2
-rw-r--r--doc/api/vulnerability_exports.md2
-rw-r--r--doc/api/vulnerability_findings.md2
-rw-r--r--doc/ci/environments/index.md38
-rw-r--r--doc/user/application_security/security_dashboard/index.md27
-rw-r--r--doc/user/project/integrations/img/zentao_product_id.pngbin0 -> 248100 bytes
-rw-r--r--doc/user/project/integrations/overview.md1
-rw-r--r--doc/user/project/integrations/zentao.md40
-rw-r--r--lib/gitlab/ci/pipeline/seed/build.rb11
-rw-r--r--lib/gitlab/ci/status/build/failed.rb3
-rw-r--r--qa/Gemfile1
-rw-r--r--qa/Gemfile.lock2
-rw-r--r--qa/qa/resource/group_base.rb16
-rw-r--r--qa/qa/resource/group_milestone.rb67
-rw-r--r--qa/qa/resource/label_base.rb4
-rw-r--r--qa/qa/runtime/allure_report.rb2
-rw-r--r--qa/qa/runtime/env.rb4
-rw-r--r--qa/qa/specs/features/api/1_manage/bulk_import_group_spec.rb104
-rw-r--r--qa/qa/specs/helpers/rspec.rb15
-rw-r--r--qa/qa/support/allure_metadata_formatter.rb34
-rw-r--r--qa/qa/support/formatters/allure_metadata_formatter.rb33
-rw-r--r--qa/qa/support/formatters/context_formatter.rb (renamed from qa/qa/specs/helpers/context_formatter.rb)9
-rw-r--r--qa/qa/support/formatters/formatters.rb11
-rw-r--r--qa/qa/support/formatters/quarantine_formatter.rb (renamed from qa/qa/specs/helpers/quarantine_formatter.rb)9
-rw-r--r--qa/qa/support/formatters/test_stats_formatter.rb119
-rw-r--r--qa/spec/spec_helper.rb8
-rw-r--r--qa/spec/specs/allure_report_spec.rb3
-rw-r--r--qa/spec/specs/helpers/context_selector_spec.rb2
-rw-r--r--qa/spec/specs/helpers/quarantine_spec.rb2
-rw-r--r--qa/spec/support/formatters/allure_metadata_formatter_spec.rb (renamed from qa/spec/support/allure_metadata_formatter_spec.rb)2
-rw-r--r--qa/spec/support/formatters/test_stats_formatter_spec.rb138
-rw-r--r--spec/features/issues/resource_label_events_spec.rb2
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb13
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js48
-rw-r--r--spec/helpers/profiles_helper_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb31
-rw-r--r--spec/presenters/commit_status_presenter_spec.rb19
-rw-r--r--spec/spec_helper.rb4
63 files changed, 765 insertions, 321 deletions
diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml
index c3e6de76894..88e732c2e75 100644
--- a/.gitlab/ci/qa.gitlab-ci.yml
+++ b/.gitlab/ci/qa.gitlab-ci.yml
@@ -7,6 +7,7 @@
variables:
USE_BUNDLE_INSTALL: "false"
SETUP_DB: "false"
+ QA_EXPORT_TEST_METRICS: "false"
before_script:
- !reference [.default-before_script, before_script]
- cd qa/
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 1f552d1dace..00c6dc6d36b 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -1435,8 +1435,6 @@
changes: *code-qa-patterns
when: manual
allow_failure: true
- - <<: *if-dot-com-gitlab-org-schedule
- allow_failure: true
.review:rules:danger:
rules:
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
index d796b6e38db..857367a0721 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
@@ -32,7 +32,6 @@ export default {
return {
searchKey: '',
labels: [],
- currentHighlightItem: -1,
localSelectedLabels: [...this.selectedLabels],
};
},
@@ -79,16 +78,6 @@ export default {
return Boolean(this.searchKey) && this.visibleLabels.length === 0;
},
},
- watch: {
- searchKey(value) {
- // When there is search string present
- // and there are matching results,
- // highlight first item by default.
- if (value && this.visibleLabels.length) {
- this.currentHighlightItem = 0;
- }
- },
- },
created() {
this.debouncedSearchKeyUpdate = debounce(this.setSearchKey, DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
},
@@ -162,16 +151,15 @@ export default {
/>
<template v-else>
<gl-dropdown-item
- v-for="(label, index) in visibleLabels"
+ v-for="label in visibleLabels"
:key="label.id"
+ :is-checked="isLabelSelected(label)"
+ :is-check-centered="true"
+ :is-check-item="true"
data-testid="labels-list"
@click.native.capture.stop="handleLabelClick(label)"
>
- <label-item
- :label="label"
- :is-label-set="isLabelSelected(label)"
- :highlight="index === currentHighlightItem"
- />
+ <label-item :label="label" />
</gl-dropdown-item>
<gl-dropdown-item
v-show="showNoMatchingResultsMessage"
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue
index 7f330147c10..f27f0b4e34c 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue
@@ -1,71 +1,21 @@
<script>
-import { GlLink, GlIcon } from '@gitlab/ui';
-
export default {
- functional: true,
props: {
label: {
type: Object,
required: true,
},
- isLabelSet: {
- type: Boolean,
- required: true,
- },
- highlight: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- render(h, { props }) {
- const { label, highlight, isLabelSet } = props;
-
- const labelColorBox = h('span', {
- class: 'dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3',
- style: {
- backgroundColor: label.color,
- },
- attrs: {
- 'data-testid': 'label-color-box',
- },
- });
-
- const checkedIcon = h(GlIcon, {
- class: {
- 'gl-mr-3 gl-flex-shrink-0': true,
- hidden: !isLabelSet,
- },
- props: {
- name: 'mobile-issue-close',
- },
- });
-
- const noIcon = h('span', {
- class: {
- 'gl-mr-5 gl-pr-3': true,
- hidden: isLabelSet,
- },
- attrs: {
- 'data-testid': 'no-icon',
- },
- });
-
- const labelTitle = h('span', label.title);
-
- const labelLink = h(GlLink, [noIcon, checkedIcon, labelColorBox, labelTitle]);
-
- return h(
- 'li',
- {
- class: {
- 'gl-display-block': true,
- 'gl-text-left': true,
- 'is-focused': highlight,
- },
- },
- [labelLink],
- );
},
};
</script>
+
+<template>
+ <div>
+ <span
+ class="dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3"
+ :style="{ 'background-color': label.color }"
+ data-testid="label-color-box"
+ ></span>
+ <span>{{ label.title }}</span>
+ </div>
+</template>
diff --git a/app/helpers/invite_members_helper.rb b/app/helpers/invite_members_helper.rb
index 5134b484249..d9bd64f4c2e 100644
--- a/app/helpers/invite_members_helper.rb
+++ b/app/helpers/invite_members_helper.rb
@@ -9,14 +9,6 @@ module InviteMembersHelper
Feature.enabled?(:invite_members_group_modal, project.group) && can?(current_user, :admin_project_member, project)
end
- def can_invite_group_for_project?(project)
- # do not use the can_admin_project_member? helper here due to structure of the view and how membership_locked?
- # is leveraged for inviting groups
- Feature.enabled?(:invite_members_group_modal, project.group) &&
- can?(current_user, :admin_project_member, project) &&
- project.allowed_to_share_with_group?
- end
-
def invite_accepted_notice(member)
case member.source
when Project
diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb
index d30d044788e..09fc1ab9d50 100644
--- a/app/helpers/profiles_helper.rb
+++ b/app/helpers/profiles_helper.rb
@@ -12,10 +12,6 @@ module ProfilesHelper
]
end
- def selected_commit_email(user)
- user.read_attribute(:commit_email) || user.commit_email
- end
-
def attribute_provider_label(attribute)
user_synced_attributes_metadata = current_user.user_synced_attributes_metadata
if user_synced_attributes_metadata&.synced?(attribute)
diff --git a/app/models/concerns/enums/ci/commit_status.rb b/app/models/concerns/enums/ci/commit_status.rb
index 16dec5fb081..bffed080d97 100644
--- a/app/models/concerns/enums/ci/commit_status.rb
+++ b/app/models/concerns/enums/ci/commit_status.rb
@@ -26,6 +26,7 @@ module Enums
pipeline_loop_detected: 17,
no_matching_runner: 18, # not used anymore, but cannot be deleted because of old data
trace_size_exceeded: 19,
+ environment_creation_failure: 20,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
invalid_bridge_trigger: 1_003,
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index 5f5bbf13f92..f0f85c95ae9 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
+ include ActionView::Helpers::UrlHelper
+
CALLOUT_FAILURE_MESSAGES = {
unknown_failure: 'There is an unknown failure, please try again',
script_failure: nil,
@@ -27,7 +29,12 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
user_blocked: 'The user who created this job is blocked',
ci_quota_exceeded: 'No more CI minutes available',
no_matching_runner: 'No matching runner available',
- trace_size_exceeded: 'The job log size limit was reached'
+ trace_size_exceeded: 'The job log size limit was reached',
+ environment_creation_failure: 'This job could not be executed because it would create an environment with an invalid parameter.'
+ }.freeze
+
+ TROUBLESHOOTING_DOC = {
+ environment_creation_failure: { path: 'ci/environments/index', anchor: 'a-deployment-job-failed-with-this-job-could-not-be-executed-because-it-would-create-an-environment-with-an-invalid-parameter-error' }
}.freeze
private_constant :CALLOUT_FAILURE_MESSAGES
@@ -39,7 +46,13 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
end
def callout_failure_message
- self.class.callout_failure_messages.fetch(failure_reason.to_sym)
+ message = self.class.callout_failure_messages.fetch(failure_reason.to_sym)
+
+ if doc = TROUBLESHOOTING_DOC[failure_reason.to_sym]
+ message += " #{link_to('How do I fix it?', help_page_path(doc[:path], anchor: doc[:anchor]))}"
+ end
+
+ message
end
end
diff --git a/app/views/profiles/_email_settings.html.haml b/app/views/profiles/_email_settings.html.haml
index 6691d20c8f7..95306633556 100644
--- a/app/views/profiles/_email_settings.html.haml
+++ b/app/views/profiles/_email_settings.html.haml
@@ -11,6 +11,6 @@
- commit_email_link_url = help_page_path('user/profile/index', anchor: 'change-the-email-displayed-on-your-commits', target: '_blank')
- commit_email_link_start = '<a href="%{url}">'.html_safe % { url: commit_email_link_url }
- commit_email_docs_link = s_('Profiles|This email will be used for web based operations, such as edits and merges. %{commit_email_link_start}Learn more%{commit_email_link_end}').html_safe % { commit_email_link_start: commit_email_link_start, commit_email_link_end: '</a>'.html_safe }
-= form.select :commit_email, options_for_select(commit_email_select_options(@user), selected: selected_commit_email(@user)),
+= form.select :commit_email, options_for_select(commit_email_select_options(@user), selected: @user.read_attribute(:commit_email)),
{ help: commit_email_docs_link },
control_class: 'select2 input-lg', disabled: email_change_disabled
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 0239e408e87..4cd91cc3be6 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -3,7 +3,7 @@
.row.gl-mt-3
.col-lg-12
- - if can_invite_members_for_project?(@project) || can_invite_group_for_project?(@project)
+ - if can_invite_members_for_project?(@project)
.row
.col-md-12.col-lg-6.gl-display-flex
.gl-flex-direction-column.gl-flex-wrap.align-items-baseline
diff --git a/config/feature_flags/development/groups_tokens_optional_encryption.yml b/config/feature_flags/development/groups_tokens_optional_encryption.yml
index 25c172422f6..c34cae689c2 100644
--- a/config/feature_flags/development/groups_tokens_optional_encryption.yml
+++ b/config/feature_flags/development/groups_tokens_optional_encryption.yml
@@ -1,7 +1,7 @@
---
name: groups_tokens_optional_encryption
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/25532
-rollout_issue_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/333862
milestone: '11.9'
type: development
group: group::runner
diff --git a/config/feature_flags/development/projects_tokens_optional_encryption.yml b/config/feature_flags/development/projects_tokens_optional_encryption.yml
index c9af986b6b7..6a0fed009ea 100644
--- a/config/feature_flags/development/projects_tokens_optional_encryption.yml
+++ b/config/feature_flags/development/projects_tokens_optional_encryption.yml
@@ -1,7 +1,7 @@
---
name: projects_tokens_optional_encryption
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/25532
-rollout_issue_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/333864
milestone: '11.9'
type: development
group: group::runner
diff --git a/config/feature_flags/development/surface_environment_creation_failure.yml b/config/feature_flags/development/surface_environment_creation_failure.yml
new file mode 100644
index 00000000000..315d15dc9b3
--- /dev/null
+++ b/config/feature_flags/development/surface_environment_creation_failure.yml
@@ -0,0 +1,8 @@
+---
+name: surface_environment_creation_failure
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69537
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340169
+milestone: '14.3'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/surface_environment_creation_failure_override.yml b/config/feature_flags/development/surface_environment_creation_failure_override.yml
new file mode 100644
index 00000000000..1e81015f259
--- /dev/null
+++ b/config/feature_flags/development/surface_environment_creation_failure_override.yml
@@ -0,0 +1,8 @@
+---
+name: surface_environment_creation_failure_override
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69537
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340169
+milestone: '14.3'
+type: development
+group: group::release
+default_enabled: false
diff --git a/doc/administration/redis/replication_and_failover_external.md b/doc/administration/redis/replication_and_failover_external.md
index 234b1aa7fb9..c19e42a5f14 100644
--- a/doc/administration/redis/replication_and_failover_external.md
+++ b/doc/administration/redis/replication_and_failover_external.md
@@ -349,7 +349,7 @@ or a failover promotes a different **Primary** node.
```yaml
production:
- url: redis://:redi-password-goes-here@gitlab-redis/
+ url: redis://:redis-password-goes-here@gitlab-redis/
sentinels:
-
host: 10.0.0.1
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index f9397e6dbca..d74da8aa574 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -1550,7 +1550,7 @@ To configure the Praefect nodes, on each one:
# Configure the Consul agent
consul['enable'] = true
## Enable service discovery for Prometheus
- consul['monitoring_service_discovery'] = true
+ consul['monitoring_service_discovery'] = true
# START user configuration
# Please set the real values as explained in Required Information section
diff --git a/doc/administration/reference_architectures/1k_users.md b/doc/administration/reference_architectures/1k_users.md
index 18e34711953..ea40e150e58 100644
--- a/doc/administration/reference_architectures/1k_users.md
+++ b/doc/administration/reference_architectures/1k_users.md
@@ -13,7 +13,7 @@ full list of reference architectures, see
If you need to serve up to 1,000 users and you don't have strict availability
requirements, a single-node solution with
[frequent backups](index.md#automated-backups) is appropriate for
-many organizations .
+many organizations.
> - **Supported users (approximate):** 1,000
> - **High Availability:** No. For a highly-available environment, you can
diff --git a/doc/api/graphql/audit_report.md b/doc/api/graphql/audit_report.md
index ba9967f85f2..76f3da2d6ea 100644
--- a/doc/api/graphql/audit_report.md
+++ b/doc/api/graphql/audit_report.md
@@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Set up an Audit Report with GraphQL
+# Set up an Audit Report with GraphQL **(FREE)**
This page describes how you can use the GraphiQL explorer to set up an audit report
for a specific subset of users.
diff --git a/doc/api/graphql/custom_emoji.md b/doc/api/graphql/custom_emoji.md
index cb5c0275e08..7307abc0568 100644
--- a/doc/api/graphql/custom_emoji.md
+++ b/doc/api/graphql/custom_emoji.md
@@ -4,7 +4,7 @@ group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Use custom emojis with GraphQL **(FREE SELF)**
+# Use custom emojis with GraphQL **(FREE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37911) in GitLab 13.6
> - [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
@@ -93,7 +93,7 @@ For more information on:
## Enable or disable custom emoji API **(FREE SELF)**
-Custom emoji is under development and but ready for production use. It is
+Custom emoji is under development but ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
can enable it.
diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md
index e77e6102594..3523276bdf5 100644
--- a/doc/api/graphql/index.md
+++ b/doc/api/graphql/index.md
@@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# GraphQL API
+# GraphQL API **(FREE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/19008) in GitLab 11.0 (enabled by feature flag `graphql`).
> - [Always enabled](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30444) in GitLab 12.1.
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 4622618db42..da2133e1f37 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1437,6 +1437,7 @@ Input type: `DastProfileCreateInput`
| ---- | ---- | ----------- |
| <a id="mutationdastprofilecreatebranchname"></a>`branchName` | [`String`](#string) | Associated branch. |
| <a id="mutationdastprofilecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationdastprofilecreatedastprofileschedule"></a>`dastProfileSchedule` | [`DastProfileScheduleInput`](#dastprofilescheduleinput) | Represents a DAST Profile Schedule. Results in an error if `dast_on_demand_scans_scheduler` feature flag is disabled. |
| <a id="mutationdastprofilecreatedastscannerprofileid"></a>`dastScannerProfileId` | [`DastScannerProfileID!`](#dastscannerprofileid) | ID of the scanner profile to be associated. |
| <a id="mutationdastprofilecreatedastsiteprofileid"></a>`dastSiteProfileId` | [`DastSiteProfileID!`](#dastsiteprofileid) | ID of the site profile to be associated. |
| <a id="mutationdastprofilecreatedescription"></a>`description` | [`String`](#string) | Description of the profile. Defaults to an empty string. |
@@ -8564,6 +8565,7 @@ Represents a DAST Profile.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="dastprofilebranch"></a>`branch` | [`DastProfileBranch`](#dastprofilebranch) | The associated branch. |
+| <a id="dastprofiledastprofileschedule"></a>`dastProfileSchedule` | [`DastProfileSchedule`](#dastprofileschedule) | Associated profile schedule. Will always return `null` if `dast_on_demand_scans_scheduler` feature flag is disabled. |
| <a id="dastprofiledastscannerprofile"></a>`dastScannerProfile` | [`DastScannerProfile`](#dastscannerprofile) | The associated scanner profile. |
| <a id="dastprofiledastsiteprofile"></a>`dastSiteProfile` | [`DastSiteProfile`](#dastsiteprofile) | The associated site profile. |
| <a id="dastprofiledescription"></a>`description` | [`String`](#string) | The description of the scan. |
@@ -8582,6 +8584,32 @@ Represents a DAST Profile Branch.
| <a id="dastprofilebranchexists"></a>`exists` | [`Boolean`](#boolean) | Indicates whether or not the branch exists. |
| <a id="dastprofilebranchname"></a>`name` | [`String`](#string) | The name of the branch. |
+### `DastProfileCadence`
+
+Represents DAST Profile Cadence.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dastprofilecadenceduration"></a>`duration` | [`Int`](#int) | Duration of the DAST profile cadence. |
+| <a id="dastprofilecadenceunit"></a>`unit` | [`DastProfileCadenceUnit`](#dastprofilecadenceunit) | Unit for the duration of DAST profile cadence. |
+
+### `DastProfileSchedule`
+
+Represents a DAST profile schedule.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dastprofilescheduleactive"></a>`active` | [`Boolean`](#boolean) | Status of the DAST profile schedule. |
+| <a id="dastprofileschedulecadence"></a>`cadence` | [`DastProfileCadence`](#dastprofilecadence) | Cadence of the DAST profile schedule. |
+| <a id="dastprofilescheduleid"></a>`id` | [`DastProfileScheduleID!`](#dastprofilescheduleid) | ID of the DAST profile schedule. |
+| <a id="dastprofileschedulenextrunat"></a>`nextRunAt` | [`Time`](#time) | Next run time of the DAST profile schedule in the given timezone. |
+| <a id="dastprofileschedulestartsat"></a>`startsAt` | [`Time`](#time) | Start time of the DAST profile schedule in the given timezone. |
+| <a id="dastprofilescheduletimezone"></a>`timezone` | [`String`](#string) | Time zone of the start time of the DAST profile schedule. |
+
### `DastScannerProfile`
Represents a DAST scanner profile.
@@ -15179,6 +15207,17 @@ Status of a container repository.
| <a id="containerrepositorystatusdelete_failed"></a>`DELETE_FAILED` | Delete Failed status. |
| <a id="containerrepositorystatusdelete_scheduled"></a>`DELETE_SCHEDULED` | Delete Scheduled status. |
+### `DastProfileCadenceUnit`
+
+Unit for the duration of Dast Profile Cadence.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="dastprofilecadenceunitday"></a>`DAY` | DAST Profile Cadence duration in days. |
+| <a id="dastprofilecadenceunitmonth"></a>`MONTH` | DAST Profile Cadence duration in months. |
+| <a id="dastprofilecadenceunitweek"></a>`WEEK` | DAST Profile Cadence duration in weeks. |
+| <a id="dastprofilecadenceunityear"></a>`YEAR` | DAST Profile Cadence duration in years. |
+
### `DastScanTypeEnum`
| Value | Description |
@@ -16431,6 +16470,12 @@ A `DastProfileID` is a global ID. It is encoded as a string.
An example `DastProfileID` is: `"gid://gitlab/Dast::Profile/1"`.
+### `DastProfileScheduleID`
+
+A `DastProfileScheduleID` is a global ID. It is encoded as a string.
+
+An example `DastProfileScheduleID` is: `"gid://gitlab/Dast::ProfileSchedule/1"`.
+
### `DastScannerProfileID`
A `DastScannerProfileID` is a global ID. It is encoded as a string.
@@ -17396,6 +17441,30 @@ Field that are available while modifying the custom mapping attributes for an HT
| <a id="complianceframeworkinputname"></a>`name` | [`String`](#string) | New name for the compliance framework. |
| <a id="complianceframeworkinputpipelineconfigurationfullpath"></a>`pipelineConfigurationFullPath` | [`String`](#string) | Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hipaa` **(ULTIMATE)**. |
+### `DastProfileCadenceInput`
+
+Represents DAST Profile Cadence.
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dastprofilecadenceinputduration"></a>`duration` | [`Int`](#int) | Duration of the DAST Profile Cadence. |
+| <a id="dastprofilecadenceinputunit"></a>`unit` | [`DastProfileCadenceUnit`](#dastprofilecadenceunit) | Unit for the duration of DAST Profile Cadence. |
+
+### `DastProfileScheduleInput`
+
+Input type for DAST Profile Schedules.
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dastprofilescheduleinputactive"></a>`active` | [`Boolean`](#boolean) | Status of a Dast Profile Schedule. |
+| <a id="dastprofilescheduleinputcadence"></a>`cadence` | [`DastProfileCadenceInput`](#dastprofilecadenceinput) | Cadence of a Dast Profile Schedule. |
+| <a id="dastprofilescheduleinputstartsat"></a>`startsAt` | [`Time`](#time) | Start time of a Dast Profile Schedule. |
+| <a id="dastprofilescheduleinputtimezone"></a>`timezone` | [`String`](#string) | Time Zone for the Start time of a Dast Profile Schedule. |
+
### `DastSiteProfileAuthInput`
Input type for DastSiteProfile authentication.
diff --git a/doc/api/graphql/removed_items.md b/doc/api/graphql/removed_items.md
index 1c425d5f1d5..0048148ab11 100644
--- a/doc/api/graphql/removed_items.md
+++ b/doc/api/graphql/removed_items.md
@@ -12,7 +12,7 @@ According to our [process for removing items](index.md#deprecation-and-removal-p
## GitLab 14.0
-Fields removed in [GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63293):
+Fields [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63293) in GitLab 14.0:
### GraphQL Mutations
@@ -38,7 +38,7 @@ Fields removed in [GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/merge_req
## GitLab 13.6
-Fields removed in [GitLab 13.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44866):
+Fields [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44866) in GitLab 13.6:
| Field name | GraphQL type | Deprecated in | Use instead |
|----------------------|--------------------------|---------------|----------------------------|
diff --git a/doc/api/graphql/users_example.md b/doc/api/graphql/users_example.md
index 8fbfb67d166..0658a9402e7 100644
--- a/doc/api/graphql/users_example.md
+++ b/doc/api/graphql/users_example.md
@@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Query users with GraphQL
+# Query users with GraphQL **(FREE)**
This page describes how you can use the GraphiQL explorer to query users.
diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md
index 8875e4daa87..3fba95c1fb3 100644
--- a/doc/api/v3_to_v4.md
+++ b/doc/api/v3_to_v4.md
@@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# API V3 to API V4
WARNING:
-The GitLab API v3 was removed in [GitLab 11.0](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/36819).
+The GitLab API v3 was [removed](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/36819) in GitLab 11.0.
For information about the current version of the GitLab API, read the [API documentation](index.md).
diff --git a/doc/api/version.md b/doc/api/version.md
index b23930e70f9..b076993f00e 100644
--- a/doc/api/version.md
+++ b/doc/api/version.md
@@ -4,7 +4,7 @@ group: Integrations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Version API
+# Version API **(FREE)**
> Introduced in GitLab 8.13.
diff --git a/doc/api/vulnerabilities.md b/doc/api/vulnerabilities.md
index b18a837de26..1c6f7a760e6 100644
--- a/doc/api/vulnerabilities.md
+++ b/doc/api/vulnerabilities.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Vulnerabilities API **(ULTIMATE)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10242) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10242) in GitLab 12.6.
NOTE:
The former Vulnerabilities API was renamed to Vulnerability Findings API
diff --git a/doc/api/vulnerability_exports.md b/doc/api/vulnerability_exports.md
index efe0ea000a7..9395a4ee5de 100644
--- a/doc/api/vulnerability_exports.md
+++ b/doc/api/vulnerability_exports.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Vulnerability export API **(ULTIMATE)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/197494) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10. [Updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30397) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.0.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/197494) in GitLab 12.10. [Updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30397) in GitLab 13.0.
WARNING:
This API is in an alpha stage and considered unstable.
diff --git a/doc/api/vulnerability_findings.md b/doc/api/vulnerability_findings.md
index c7f045a67a0..dfc6074a1aa 100644
--- a/doc/api/vulnerability_findings.md
+++ b/doc/api/vulnerability_findings.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Vulnerability Findings API **(ULTIMATE)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19029) in GitLab Ultimate 12.5.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/19029) in GitLab 12.5.
NOTE:
This API resource is renamed from Vulnerabilities to Vulnerability Findings because the Vulnerabilities are reserved
diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md
index f0204180d8a..f7db790925b 100644
--- a/doc/ci/environments/index.md
+++ b/doc/ci/environments/index.md
@@ -816,3 +816,41 @@ To ensure the `action: stop` can always run when needed, you can:
action: stop
when: manual
```
+
+### A deployment job failed with "This job could not be executed because it would create an environment with an invalid parameter" error
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21182) in GitLab 14.3.
+
+FLAG:
+On self-managed GitLab, by default this bug fix is not available. To make it available per project or for your entire instance, ask an administrator to [enable the `surface_environment_creation_failure` flag](../../administration/feature_flags.md). On GitLab.com, this bug fix is not available, but will be rolled out shortly.
+
+If your project is configured to [create a dynamic environment](#create-a-dynamic-environment),
+such as a [Review App](../review_apps/index.md), you might encounter this error
+because the dynamically generated parameter is invalid for the system.
+
+For example, if you have the following in your `.gitlab-ci.yml`:
+
+```yaml
+review:
+ script: deploy review app
+ environment: review/$CI_COMMIT_REF_NAME
+```
+
+When you create a new merge request with a branch name `bug-fix!`,
+the `review` job tries to create an environment with `review/bug-fix!`.
+However, the `!` is an invalid character for environments, so the
+deployment job fails since it was about to run without an environment.
+
+To fix this, you can:
+
+- Re-create your feature branch without the invalid characters,
+ such as `bug-fix`.
+- Replace the `CI_COMMIT_REF_NAME`
+ [predefined variable](../variables/predefined_variables.md) with
+ `CI_COMMIT_REF_SLUG` which strips any invalid characters:
+
+ ```yaml
+ review:
+ script: deploy review app
+ environment: review/$CI_COMMIT_REF_SLUG
+ ```
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index 5df2cb1ec72..c78179e9693 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -166,22 +166,17 @@ To add projects to the Security Center:
After you add projects, the security dashboard and vulnerability report display the vulnerabilities
found in those projects' default branches.
-## Keeping the dashboards up to date
+## Keep dashboards up to date
-The Security Dashboard displays information from the results of the most recent
-security scan on the [default branch](../../project/repository/branches/default.md),
-which means that security scans are performed every time the branch is updated.
-
-If the default branch is updated infrequently, scans are run infrequently and the
-information on the Security Dashboard can become outdated as new vulnerabilities
-are discovered.
+The Security Dashboard displays results of the most recent security scan on the
+[default branch](../../project/repository/branches/default.md). By default, security scans are run
+only when the default branch is updated. Information on the Security Dashboard may not reflect
+newly-discovered vulnerabilities.
To ensure the information on the Security Dashboard is regularly updated,
-[configure a scheduled pipeline](../../../ci/pipelines/schedules.md) to run a
-daily security scan. This updates the information displayed on the Security
-Dashboard regardless of how often the default branch is updated.
-
-That way, reports are created even if no code change happens.
+[configure a scheduled pipeline](../../../ci/pipelines/schedules.md) to run a daily security scan.
+This updates the information displayed on the Security Dashboard regardless of how often the default
+branch is updated.
WARNING:
Running Dependency Scanning from a scheduled pipeline might result in false negatives if your
@@ -191,12 +186,6 @@ can occur because the dependency version resolved during the scan might differ f
resolved when your project was built and released, in a previous pipeline. Java projects can't have
lock files. Python projects can have lock files, but GitLab Secure tools don't support them.
-## Security scans using Auto DevOps
-
-When using [Auto DevOps](../../../topics/autodevops/index.md), use
-[special environment variables](../../../topics/autodevops/customize.md#cicd-variables)
-to configure daily security scans.
-
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/user/project/integrations/img/zentao_product_id.png b/doc/user/project/integrations/img/zentao_product_id.png
new file mode 100644
index 00000000000..b6fb8e1fb1a
--- /dev/null
+++ b/doc/user/project/integrations/img/zentao_product_id.png
Binary files differ
diff --git a/doc/user/project/integrations/overview.md b/doc/user/project/integrations/overview.md
index 13def74450c..de82fb793f7 100644
--- a/doc/user/project/integrations/overview.md
+++ b/doc/user/project/integrations/overview.md
@@ -60,6 +60,7 @@ Click on the service links to see further configuration instructions and details
| [Unify Circuit](unify_circuit.md) | Receive events notifications. | **{dotted-circle}** No |
| [Webex Teams](webex_teams.md) | Receive events notifications. | **{dotted-circle}** No |
| [YouTrack](youtrack.md) | Use YouTrack as the issue tracker. | **{dotted-circle}** No |
+| [ZenTao](zentao.md) | Use ZenTao as the issue tracker. | **{dotted-circle}** No |
## Push hooks limit
diff --git a/doc/user/project/integrations/zentao.md b/doc/user/project/integrations/zentao.md
new file mode 100644
index 00000000000..ab8a7829139
--- /dev/null
+++ b/doc/user/project/integrations/zentao.md
@@ -0,0 +1,40 @@
+---
+stage: Ecosystem
+group: Integrations
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# ZenTao product integration **(PREMIUM)**
+
+[ZenTao](https://www.zentao.net/) is a web-based project management platform.
+
+## Configure ZenTao
+
+This integration requires a ZenTao API secret key.
+
+Complete these steps in ZenTao:
+
+1. Go to your **Admin** page and select **Develop > Application**.
+1. Select **Add Application**.
+1. Under **Name** and **Code**, enter a name and a code for the new secret key.
+1. Under **Account**, select an existing account name.
+1. Select **Save**.
+1. Copy the generated key to use in GitLab.
+
+## Configure GitLab
+
+Complete these steps in GitLab:
+
+1. Go to your project and select **Settings > Integrations**.
+1. Select **ZenTao**.
+1. Turn on the **Active** toggle under **Enable Integration**.
+1. Provide the ZenTao configuration information:
+ - **ZenTao Web URL**: The base URL of the ZenTao instance web interface you're linking to this GitLab project (for example, `example.zentao.net`).
+ - **ZenTao API URL** (optional): The base URL to the ZenTao instance API. Defaults to the Web URL value if not set.
+ - **ZenTao API token**: Use the key you generated when you [configured ZenTao](#configure-zentao).
+ - **ZenTao Product ID**: To display issues from a single ZenTao product in a given GitLab project. The Product ID can be found in the ZenTao product page under **Settings > Overview**.
+
+ ![ZenTao settings page](img/zentao_product_id.png)
+
+1. To verify the ZenTao connection is working, select **Test settings**.
+1. Select **Save changes**.
diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb
index 934bf22d8ad..9ad5d6538b7 100644
--- a/lib/gitlab/ci/pipeline/seed/build.rb
+++ b/lib/gitlab/ci/pipeline/seed/build.rb
@@ -106,10 +106,15 @@ module Gitlab
environment = Seed::Environment.new(build).to_resource
- # If there is a validation error on environment creation, such as
- # the name contains invalid character, the build falls back to a
- # non-environment job.
unless environment.persisted?
+ if Feature.enabled?(:surface_environment_creation_failure, build.project, default_enabled: :yaml) &&
+ Feature.disabled?(:surface_environment_creation_failure_override, build.project)
+ return { status: :failed, failure_reason: :environment_creation_failure }
+ end
+
+ # If there is a validation error on environment creation, such as
+ # the name contains invalid character, the build falls back to a
+ # non-environment job.
Gitlab::ErrorTracking.track_exception(
EnvironmentCreationFailure.new,
project_id: build.project_id,
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index dbbb9a01dab..b9525ce97a8 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -32,7 +32,8 @@ module Gitlab
user_blocked: 'pipeline user was blocked',
ci_quota_exceeded: 'no more CI minutes available',
no_matching_runner: 'no matching runner available',
- trace_size_exceeded: 'log size limit exceeded'
+ trace_size_exceeded: 'log size limit exceeded',
+ environment_creation_failure: 'environment creation failure'
}.freeze
private_constant :REASONS
diff --git a/qa/Gemfile b/qa/Gemfile
index d3d0fe95e49..cc2355cdfa3 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -24,6 +24,7 @@ gem 'rspec-parameterized', '~> 0.4.2'
gem 'octokit', '~> 4.21'
gem 'webdrivers', '~> 4.6'
gem 'zeitwerk', '~> 2.4'
+gem 'influxdb-client', '~> 1.17'
gem 'chemlab', '~> 0.7'
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 1df4550a2ba..5f33afaa77b 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -88,6 +88,7 @@ GEM
i18n (1.8.10)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
+ influxdb-client (1.17.0)
knapsack (1.17.1)
rake
launchy (2.4.3)
@@ -220,6 +221,7 @@ DEPENDENCIES
deprecation_toolkit (~> 1.5.1)
faker (~> 2.19, >= 2.19.0)
gitlab-qa
+ influxdb-client (~> 1.17)
knapsack (~> 1.17)
octokit (~> 4.21)
parallel (~> 1.19)
diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb
index b937b704613..a1e5b19f409 100644
--- a/qa/qa/resource/group_base.rb
+++ b/qa/qa/resource/group_base.rb
@@ -30,6 +30,22 @@ module QA
end
end
+ # Get group milestones
+ #
+ # @return [Array<QA::Resource::GroupMilestone>]
+ def milestones
+ parse_body(api_get_from("#{api_get_path}/milestones")).map do |milestone|
+ GroupMilestone.init do |resource|
+ resource.api_client = api_client
+ resource.group = self
+ resource.id = milestone[:id]
+ resource.iid = milestone[:iid]
+ resource.title = milestone[:title]
+ resource.description = milestone[:description]
+ end
+ end
+ end
+
# API get path
#
# @return [String]
diff --git a/qa/qa/resource/group_milestone.rb b/qa/qa/resource/group_milestone.rb
index 1fb07fdbd0b..880ca2b9721 100644
--- a/qa/qa/resource/group_milestone.rb
+++ b/qa/qa/resource/group_milestone.rb
@@ -3,11 +3,14 @@
module QA
module Resource
class GroupMilestone < Base
- attr_writer :start_date, :due_date
-
- attribute :id
- attribute :title
- attribute :description
+ attributes :id,
+ :iid,
+ :title,
+ :description,
+ :start_date,
+ :due_date,
+ :updated_at,
+ :created_at
attribute :group do
Group.fabricate_via_api! do |resource|
@@ -20,6 +23,21 @@ module QA
@description = "My awesome group milestone."
end
+ def fabricate!
+ group.visit!
+
+ Page::Group::Menu.perform(&:go_to_milestones)
+ Page::Group::Milestone::Index.perform(&:click_new_milestone_link)
+
+ Page::Group::Milestone::New.perform do |new_milestone|
+ new_milestone.set_title(@title)
+ new_milestone.set_description(@description)
+ new_milestone.set_start_date(@start_date) if @start_date
+ new_milestone.set_due_date(@due_date) if @due_date
+ new_milestone.click_create_milestone_button
+ end
+ end
+
def api_get_path
"/groups/#{group.id}/milestones/#{id}"
end
@@ -38,19 +56,36 @@ module QA
end
end
- def fabricate!
- group.visit!
+ # Object comparison
+ #
+ # @param [QA::Resource::GroupMilestone] other
+ # @return [Boolean]
+ def ==(other)
+ other.is_a?(GroupMilestone) && comparable_milestone == other.comparable_milestone
+ end
- Page::Group::Menu.perform(&:go_to_milestones)
- Page::Group::Milestone::Index.perform(&:click_new_milestone_link)
+ # Override inspect for a better rspec failure diff output
+ #
+ # @return [String]
+ def inspect
+ JSON.pretty_generate(comparable_milestone)
+ end
- Page::Group::Milestone::New.perform do |new_milestone|
- new_milestone.set_title(@title)
- new_milestone.set_description(@description)
- new_milestone.set_start_date(@start_date) if @start_date
- new_milestone.set_due_date(@due_date) if @due_date
- new_milestone.click_create_milestone_button
- end
+ protected
+
+ # Return subset of fields for comparing milestones
+ #
+ # @return [Hash]
+ def comparable_milestone
+ reload! unless api_response
+
+ api_response.slice(
+ :title,
+ :description,
+ :state,
+ :due_date,
+ :start_date
+ )
end
end
end
diff --git a/qa/qa/resource/label_base.rb b/qa/qa/resource/label_base.rb
index 8f6534cb451..b1af0e23561 100644
--- a/qa/qa/resource/label_base.rb
+++ b/qa/qa/resource/label_base.rb
@@ -64,9 +64,9 @@ module QA
JSON.pretty_generate(comparable_label)
end
- # protected
+ protected
- # Return subset of fields for comparing groups
+ # Return subset of fields for comparing labels
#
# @return [Hash]
def comparable_label
diff --git a/qa/qa/runtime/allure_report.rb b/qa/qa/runtime/allure_report.rb
index 0630e9d333c..5b0456dc607 100644
--- a/qa/qa/runtime/allure_report.rb
+++ b/qa/qa/runtime/allure_report.rb
@@ -73,7 +73,7 @@ module QA
def configure_rspec
RSpec.configure do |config|
config.add_formatter(AllureRspecFormatter)
- config.add_formatter(QA::Support::AllureMetadataFormatter)
+ config.add_formatter(QA::Support::Formatters::AllureMetadataFormatter)
end
end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 16c8c4aff3e..cdfa95457c7 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -403,6 +403,10 @@ module QA
ENV['GITLAB_TLS_CERTIFICATE']
end
+ def export_metrics?
+ running_in_ci? && enabled?(ENV['QA_EXPORT_TEST_METRICS'], default: true)
+ end
+
private
def remote_grid_credentials
diff --git a/qa/qa/specs/features/api/1_manage/bulk_import_group_spec.rb b/qa/qa/specs/features/api/1_manage/bulk_import_group_spec.rb
index 925d50a6639..1422dd5a029 100644
--- a/qa/qa/specs/features/api/1_manage/bulk_import_group_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/bulk_import_group_spec.rb
@@ -5,6 +5,7 @@ module QA
describe 'Bulk group import' do
let!(:staging?) { Runtime::Scenario.gitlab_address.include?('staging.gitlab.com') }
+ let(:import_wait_duration) { { max_duration: 300, sleep_interval: 2 } }
let(:admin_api_client) { Runtime::API::Client.as_admin }
let(:user) do
Resource::User.fabricate_via_api! do |usr|
@@ -14,7 +15,6 @@ module QA
end
let(:api_client) { Runtime::API::Client.new(user: user) }
- let(:personal_access_token) { api_client.personal_access_token }
let(:sandbox) do
Resource::Sandbox.fabricate_via_api! do |group|
@@ -29,22 +29,6 @@ module QA
end
end
- let(:subgroup) do
- Resource::Group.fabricate_via_api! do |group|
- group.api_client = api_client
- group.sandbox = source_group
- group.path = "subgroup-for-import-#{SecureRandom.hex(4)}"
- end
- end
-
- let(:imported_subgroup) do
- Resource::Group.init do |group|
- group.api_client = api_client
- group.sandbox = imported_group
- group.path = subgroup.path
- end
- end
-
let(:imported_group) do
Resource::BulkImportGroup.fabricate_via_api! do |group|
group.api_client = api_client
@@ -57,33 +41,79 @@ module QA
Runtime::Feature.enable(:top_level_group_creation_enabled) if staging?
sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
+ end
- Resource::GroupLabel.fabricate_via_api! do |label|
- label.api_client = api_client
- label.group = source_group
- label.title = "source-group-#{SecureRandom.hex(4)}"
+ context 'with subgroups and labels' do
+ let(:subgroup) do
+ Resource::Group.fabricate_via_api! do |group|
+ group.api_client = api_client
+ group.sandbox = source_group
+ group.path = "subgroup-for-import-#{SecureRandom.hex(4)}"
+ end
end
- Resource::GroupLabel.fabricate_via_api! do |label|
- label.api_client = api_client
- label.group = subgroup
- label.title = "subgroup-#{SecureRandom.hex(4)}"
+
+ let(:imported_subgroup) do
+ Resource::Group.init do |group|
+ group.api_client = api_client
+ group.sandbox = imported_group
+ group.path = subgroup.path
+ end
+ end
+
+ before do
+ Resource::GroupLabel.fabricate_via_api! do |label|
+ label.api_client = api_client
+ label.group = source_group
+ label.title = "source-group-#{SecureRandom.hex(4)}"
+ end
+ Resource::GroupLabel.fabricate_via_api! do |label|
+ label.api_client = api_client
+ label.group = subgroup
+ label.title = "subgroup-#{SecureRandom.hex(4)}"
+ end
+ end
+
+ it(
+ 'successfully imports groups and labels',
+ testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1873'
+ ) do
+ expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
+
+ aggregate_failures do
+ expect(imported_group.reload!).to eq(source_group)
+ expect(imported_group.labels).to include(*source_group.labels)
+
+ expect(imported_subgroup.reload!).to eq(subgroup)
+ expect(imported_subgroup.labels).to include(*subgroup.labels)
+ end
end
end
- it(
- 'imports group with subgroups and labels',
- testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1873'
- ) do
- expect { imported_group.import_status }.to(
- eventually_eq('finished').within(max_duration: 300, sleep_interval: 2)
- )
+ context 'with milestones' do
+ let(:source_milestone) do
+ Resource::GroupMilestone.fabricate_via_api! do |milestone|
+ milestone.api_client = api_client
+ milestone.group = source_group
+ end
+ end
+
+ before do
+ source_milestone
+ end
- aggregate_failures do
- expect(imported_group.reload!).to eq(source_group)
- expect(imported_group.labels).to include(*source_group.labels)
+ it(
+ 'successfully imports group milestones',
+ testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/2245'
+ ) do
+ expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
- expect(imported_subgroup.reload!).to eq(subgroup)
- expect(imported_subgroup.labels).to include(*subgroup.labels)
+ imported_milestone = imported_group.reload!.milestones.find { |ml| ml.title == source_milestone.title }
+ aggregate_failures do
+ expect(imported_milestone).to eq(source_milestone)
+ expect(imported_milestone.iid).to eq(source_milestone.iid)
+ expect(imported_milestone.created_at).to eq(source_milestone.created_at)
+ expect(imported_milestone.updated_at).to eq(source_milestone.updated_at)
+ end
end
end
diff --git a/qa/qa/specs/helpers/rspec.rb b/qa/qa/specs/helpers/rspec.rb
index 853dfbfd1b6..3e97dbd118a 100644
--- a/qa/qa/specs/helpers/rspec.rb
+++ b/qa/qa/specs/helpers/rspec.rb
@@ -19,13 +19,22 @@ module QA
# expanding into the global state
# See: https://github.com/rspec/rspec-core/issues/2603
def describe_successfully(*args, &describe_body)
- reporter = ::RSpec.configuration.reporter
-
- example_group = RSpec.describe(*args, &describe_body)
+ example_group = ::RSpec.describe(*args, &describe_body)
ran_successfully = example_group.run reporter
expect(ran_successfully).to eq true
example_group
end
+
+ def send_stop_notification
+ reporter.notify(
+ :stop,
+ ::RSpec::Core::Notifications::ExamplesNotification.new(reporter)
+ )
+ end
+
+ def reporter
+ ::RSpec.configuration.reporter
+ end
end
end
end
diff --git a/qa/qa/support/allure_metadata_formatter.rb b/qa/qa/support/allure_metadata_formatter.rb
deleted file mode 100644
index 98b7077b0ae..00000000000
--- a/qa/qa/support/allure_metadata_formatter.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-require 'rspec/core'
-require "rspec/core/formatters/base_formatter"
-
-module QA
- module Support
- class AllureMetadataFormatter < ::RSpec::Core::Formatters::BaseFormatter
- ::RSpec::Core::Formatters.register(
- self,
- :example_started
- )
-
- # Starts example
- # @param [RSpec::Core::Notifications::ExampleNotification] example_notification
- # @return [void]
- def example_started(example_notification)
- example = example_notification.example
-
- quarantine_issue = example.metadata.dig(:quarantine, :issue)
- example.issue('Quarantine issue', quarantine_issue) if quarantine_issue
-
- spec_file = example.file_path.split('/').last
- example.issue(
- 'Failure issues',
- "https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&search=#{spec_file}"
- )
- return unless Runtime::Env.running_in_ci?
-
- example.add_link(name: "Job(#{Runtime::Env.ci_job_name})", url: Runtime::Env.ci_job_url)
- end
- end
- end
-end
diff --git a/qa/qa/support/formatters/allure_metadata_formatter.rb b/qa/qa/support/formatters/allure_metadata_formatter.rb
new file mode 100644
index 00000000000..10769ba5c57
--- /dev/null
+++ b/qa/qa/support/formatters/allure_metadata_formatter.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ module Formatters
+ class AllureMetadataFormatter < ::RSpec::Core::Formatters::BaseFormatter
+ ::RSpec::Core::Formatters.register(
+ self,
+ :example_started
+ )
+
+ # Starts example
+ # @param [RSpec::Core::Notifications::ExampleNotification] example_notification
+ # @return [void]
+ def example_started(example_notification)
+ example = example_notification.example
+
+ quarantine_issue = example.metadata.dig(:quarantine, :issue)
+ example.issue('Quarantine issue', quarantine_issue) if quarantine_issue
+
+ spec_file = example.file_path.split('/').last
+ example.issue(
+ 'Failure issues',
+ "https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&search=#{spec_file}"
+ )
+ return unless Runtime::Env.running_in_ci?
+
+ example.add_link(name: "Job(#{Runtime::Env.ci_job_name})", url: Runtime::Env.ci_job_url)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/helpers/context_formatter.rb b/qa/qa/support/formatters/context_formatter.rb
index 26db7c3b67e..c8991561f45 100644
--- a/qa/qa/specs/helpers/context_formatter.rb
+++ b/qa/qa/support/formatters/context_formatter.rb
@@ -1,13 +1,10 @@
# frozen_string_literal: true
-require 'rspec/core'
-require "rspec/core/formatters/base_formatter"
-
module QA
- module Specs
- module Helpers
+ module Support
+ module Formatters
class ContextFormatter < ::RSpec::Core::Formatters::BaseFormatter
- include ContextSelector
+ include Specs::Helpers::ContextSelector
::RSpec::Core::Formatters.register(
self,
diff --git a/qa/qa/support/formatters/formatters.rb b/qa/qa/support/formatters/formatters.rb
new file mode 100644
index 00000000000..f0abf98001f
--- /dev/null
+++ b/qa/qa/support/formatters/formatters.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'rspec/core'
+require 'rspec/core/formatters/base_formatter'
+
+module QA
+ module Support
+ module Formatters
+ end
+ end
+end
diff --git a/qa/qa/specs/helpers/quarantine_formatter.rb b/qa/qa/support/formatters/quarantine_formatter.rb
index c42debee07c..c5d16988dbd 100644
--- a/qa/qa/specs/helpers/quarantine_formatter.rb
+++ b/qa/qa/support/formatters/quarantine_formatter.rb
@@ -1,13 +1,10 @@
# frozen_string_literal: true
-require 'rspec/core'
-require "rspec/core/formatters/base_formatter"
-
module QA
- module Specs
- module Helpers
+ module Support
+ module Formatters
class QuarantineFormatter < ::RSpec::Core::Formatters::BaseFormatter
- include Quarantine
+ include Specs::Helpers::Quarantine
::RSpec::Core::Formatters.register(
self,
diff --git a/qa/qa/support/formatters/test_stats_formatter.rb b/qa/qa/support/formatters/test_stats_formatter.rb
new file mode 100644
index 00000000000..91fdc902e2d
--- /dev/null
+++ b/qa/qa/support/formatters/test_stats_formatter.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+
+module QA
+ module Support
+ module Formatters
+ class TestStatsFormatter < RSpec::Core::Formatters::BaseFormatter
+ RSpec::Core::Formatters.register(self, :stop)
+
+ # Finish test execution
+ #
+ # @param [RSpec::Core::Notifications::ExamplesNotification] notification
+ # @return [void]
+ def stop(notification)
+ return log(:warn, 'Missing QA_INFLUXDB_URL, skipping metrics export!') unless influxdb_url
+ return log(:warn, 'Missing QA_INFLUXDB_TOKEN, skipping metrics export!') unless influxdb_token
+
+ data = notification.examples.map { |example| test_stats(example) }.compact
+ influx_client.create_write_api.write(data: data)
+ log(:info, "Pushed #{data.length} entries to influxdb")
+ rescue StandardError => e
+ log(:error, "Failed to push data to influxdb, error: #{e}")
+ end
+
+ private
+
+ # InfluxDb client
+ #
+ # @return [InfluxDB2::Client]
+ def influx_client
+ @influx_client ||= InfluxDB2::Client.new(
+ influxdb_url,
+ influxdb_token,
+ bucket: 'e2e-test-stats',
+ org: 'gitlab-qa',
+ use_ssl: false,
+ precision: InfluxDB2::WritePrecision::NANOSECOND
+ )
+ end
+
+ # InfluxDb instance url
+ #
+ # @return [String]
+ def influxdb_url
+ @influxdb_url ||= ENV['QA_INFLUXDB_URL']
+ end
+
+ # Influxdb token
+ #
+ # @return [String]
+ def influxdb_token
+ @influxdb_token ||= ENV['QA_INFLUXDB_TOKEN']
+ end
+
+ # Transform example to influxdb compatible metrics data
+ # https://github.com/influxdata/influxdb-client-ruby#data-format
+ #
+ # @param [RSpec::Core::Example] example
+ # @return [Hash]
+ def test_stats(example)
+ {
+ name: 'test-stats',
+ time: time,
+ tags: {
+ name: example.full_description,
+ file_path: example.metadata[:file_path].gsub('./qa/specs/features', ''),
+ status: example.execution_result.status,
+ reliable: example.metadata.key?(:reliable).to_s,
+ quarantined: example.metadata.key?(:quarantine).to_s,
+ retried: ((example.metadata[:retry_attempts] || 0) > 0).to_s,
+ job_name: job_name,
+ merge_request: merge_request,
+ run_type: ENV['QA_RUN_TYPE']
+ },
+ fields: {
+ id: example.id,
+ run_time: (example.execution_result.run_time * 1000).round,
+ retry_attempts: example.metadata[:retry_attempts] || 0,
+ job_url: QA::Runtime::Env.ci_job_url,
+ pipeline_id: ENV['CI_PIPELINE_ID']
+ }
+ }
+ rescue StandardError => e
+ log(:error, "Failed to transform example '#{example.id}', error: #{e}")
+ nil
+ end
+
+ # Single common timestamp for all exported example metrics to keep data points consistently grouped
+ #
+ # @return [Time]
+ def time
+ @time ||= DateTime.strptime(ENV['CI_PIPELINE_CREATED_AT']).to_time
+ end
+
+ # Is a merge request execution
+ #
+ # @return [String]
+ def merge_request
+ @merge_request ||= (!!ENV['CI_MERGE_REQUEST_IID'] || !!ENV['TOP_UPSTREAM_MERGE_REQUEST_IID']).to_s
+ end
+
+ # Base ci job name
+ #
+ # @return [String]
+ def job_name
+ @job_name ||= QA::Runtime::Env.ci_job_name.gsub(%r{ \d{1,2}/\d{1,2}}, '')
+ end
+
+ # Print log message
+ #
+ # @param [Symbol] level
+ # @param [String] message
+ # @return [void]
+ def log(level, message)
+ QA::Runtime::Logger.public_send(level, "influxdb exporter: #{message}")
+ end
+ end
+ end
+ end
+end
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index 6bc889ebc49..4f0f93bf020 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -16,17 +16,15 @@ QA::Runtime::Browser.configure!
QA::Runtime::AllureReport.configure!
QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes)
-Dir[::File.join(__dir__, "support/helpers/*.rb")].sort.each { |f| require f }
-Dir[::File.join(__dir__, "support/matchers/*.rb")].sort.each { |f| require f }
-Dir[::File.join(__dir__, "support/shared_contexts/*.rb")].sort.each { |f| require f }
Dir[::File.join(__dir__, "support/shared_examples/*.rb")].sort.each { |f| require f }
RSpec.configure do |config|
config.include QA::Support::Matchers::EventuallyMatcher
config.include QA::Support::Matchers::HaveMatcher
- config.add_formatter QA::Specs::Helpers::ContextFormatter
- config.add_formatter QA::Specs::Helpers::QuarantineFormatter
+ config.add_formatter QA::Support::Formatters::ContextFormatter
+ config.add_formatter QA::Support::Formatters::QuarantineFormatter
+ config.add_formatter QA::Support::Formatters::TestStatsFormatter if QA::Runtime::Env.export_metrics?
config.before do |example|
QA::Runtime::Logger.debug("\nStarting test: #{example.full_description}\n")
diff --git a/qa/spec/specs/allure_report_spec.rb b/qa/spec/specs/allure_report_spec.rb
index d17fb8e41d0..34116ca6cbd 100644
--- a/qa/spec/specs/allure_report_spec.rb
+++ b/qa/spec/specs/allure_report_spec.rb
@@ -68,7 +68,8 @@ describe QA::Runtime::AllureReport do
it 'adds rspec and metadata formatter' do
expect(rspec_config).to have_received(:add_formatter).with(AllureRspecFormatter).ordered
- expect(rspec_config).to have_received(:add_formatter).with(QA::Support::AllureMetadataFormatter).ordered
+ expect(rspec_config).to have_received(:add_formatter)
+ .with(QA::Support::Formatters::AllureMetadataFormatter).ordered
end
it 'configures screenshot saving' do
diff --git a/qa/spec/specs/helpers/context_selector_spec.rb b/qa/spec/specs/helpers/context_selector_spec.rb
index 1492008972d..0152fee6f5b 100644
--- a/qa/spec/specs/helpers/context_selector_spec.rb
+++ b/qa/spec/specs/helpers/context_selector_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
QA::Runtime::Scenario.define(:gitlab_address, 'https://staging.gitlab.com')
RSpec::Core::Sandbox.sandboxed do |config|
- config.formatter = QA::Specs::Helpers::ContextFormatter
+ config.formatter = QA::Support::Formatters::ContextFormatter
# If there is an example-within-an-example, we want to make sure the inner example
# does not get a reference to the outer example (the real spec) if it calls
diff --git a/qa/spec/specs/helpers/quarantine_spec.rb b/qa/spec/specs/helpers/quarantine_spec.rb
index f064aa0b9af..8ea375cdb05 100644
--- a/qa/spec/specs/helpers/quarantine_spec.rb
+++ b/qa/spec/specs/helpers/quarantine_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe QA::Specs::Helpers::Quarantine do
around do |ex|
RSpec::Core::Sandbox.sandboxed do |config|
- config.formatter = QA::Specs::Helpers::QuarantineFormatter
+ config.formatter = QA::Support::Formatters::QuarantineFormatter
# If there is an example-within-an-example, we want to make sure the inner example
# does not get a reference to the outer example (the real spec) if it calls
diff --git a/qa/spec/support/allure_metadata_formatter_spec.rb b/qa/spec/support/formatters/allure_metadata_formatter_spec.rb
index f01e5c9f5f8..631d2eda54f 100644
--- a/qa/spec/support/allure_metadata_formatter_spec.rb
+++ b/qa/spec/support/formatters/allure_metadata_formatter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-describe QA::Support::AllureMetadataFormatter do
+describe QA::Support::Formatters::AllureMetadataFormatter do
include QA::Support::Helpers::StubEnv
let(:formatter) { described_class.new(StringIO.new) }
diff --git a/qa/spec/support/formatters/test_stats_formatter_spec.rb b/qa/spec/support/formatters/test_stats_formatter_spec.rb
new file mode 100644
index 00000000000..30ffa5839e6
--- /dev/null
+++ b/qa/spec/support/formatters/test_stats_formatter_spec.rb
@@ -0,0 +1,138 @@
+# frozen_string_literal: true
+
+require 'rspec/core/sandbox'
+
+describe QA::Support::Formatters::TestStatsFormatter do
+ include QA::Support::Helpers::StubEnv
+ include QA::Specs::Helpers::RSpec
+
+ let(:url) { "http://influxdb.net" }
+ let(:token) { "token" }
+ let(:ci_timestamp) { "2021-02-23T20:58:41Z" }
+ let(:ci_job_name) { "test-job 1/5" }
+ let(:ci_job_url) { "url" }
+ let(:ci_pipeline_id) { "123" }
+ let(:run_type) { 'staging-full' }
+ let(:influx_client) { instance_double('InfluxDB2::Client', create_write_api: influx_write_api) }
+ let(:influx_write_api) { instance_double('InfluxDB2::WriteApi', write: nil) }
+
+ let(:influx_client_args) do
+ {
+ bucket: 'e2e-test-stats',
+ org: 'gitlab-qa',
+ use_ssl: false,
+ precision: InfluxDB2::WritePrecision::NANOSECOND
+ }
+ end
+
+ let(:data) do
+ {
+ name: 'test-stats',
+ time: DateTime.strptime(ci_timestamp).to_time,
+ tags: {
+ name: "stats export #{spec_name}",
+ file_path: './spec/support/formatters/test_stats_formatter_spec.rb',
+ status: :passed,
+ reliable: reliable,
+ quarantined: quarantined,
+ retried: "false",
+ job_name: "test-job",
+ merge_request: "false",
+ run_type: run_type
+ },
+ fields: {
+ id: './spec/support/formatters/test_stats_formatter_spec.rb[1:1]',
+ run_time: 0,
+ retry_attempts: 0,
+ job_url: ci_job_url,
+ pipeline_id: ci_pipeline_id
+ }
+ }
+ end
+
+ def run_spec(&spec)
+ describe_successfully('stats export', &spec)
+ send_stop_notification
+ end
+
+ around do |example|
+ RSpec::Core::Sandbox.sandboxed do |config|
+ config.formatter = QA::Support::Formatters::TestStatsFormatter
+
+ config.before(:context) { RSpec.current_example = nil }
+
+ example.run
+ end
+ end
+
+ before do
+ allow(InfluxDB2::Client).to receive(:new).with(url, token, **influx_client_args) { influx_client }
+ end
+
+ context "without influxdb variables configured" do
+ it "skips export without influxdb url" do
+ stub_env('QA_INFLUXDB_URL', nil)
+ stub_env('QA_INFLUXDB_TOKEN', nil)
+
+ run_spec do
+ it('skips export') {}
+ end
+
+ expect(influx_client).not_to have_received(:create_write_api)
+ end
+
+ it "skips export without influxdb token" do
+ stub_env('QA_INFLUXDB_URL', url)
+ stub_env('QA_INFLUXDB_TOKEN', nil)
+
+ run_spec do
+ it('skips export') {}
+ end
+
+ expect(influx_client).not_to have_received(:create_write_api)
+ end
+ end
+
+ context 'with influxdb variables configured' do
+ let(:spec_name) { 'exports data' }
+ let(:run_type) { ci_job_name.gsub(%r{ \d{1,2}/\d{1,2}}, '') }
+
+ before do
+ stub_env('QA_INFLUXDB_URL', url)
+ stub_env('QA_INFLUXDB_TOKEN', token)
+ stub_env('CI_PIPELINE_CREATED_AT', ci_timestamp)
+ stub_env('CI_JOB_URL', ci_job_url)
+ stub_env('CI_JOB_NAME', ci_job_name)
+ stub_env('CI_PIPELINE_ID', ci_pipeline_id)
+ stub_env('CI_MERGE_REQUEST_IID', nil)
+ stub_env('TOP_UPSTREAM_MERGE_REQUEST_IID', nil)
+ stub_env('QA_RUN_TYPE', run_type)
+ end
+
+ context 'with reliable spec' do
+ let(:reliable) { 'true' }
+ let(:quarantined) { 'false' }
+
+ it 'exports data to influxdb' do
+ run_spec do
+ it('exports data', :reliable) {}
+ end
+
+ expect(influx_write_api).to have_received(:write).with(data: [data])
+ end
+ end
+
+ context 'with quarantined spec' do
+ let(:reliable) { 'false' }
+ let(:quarantined) { 'true' }
+
+ it 'exports data to influxdb' do
+ run_spec do
+ it('exports data', :quarantine) {}
+ end
+
+ expect(influx_write_api).to have_received(:write).with(data: [data])
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/resource_label_events_spec.rb b/spec/features/issues/resource_label_events_spec.rb
index 33edf2f0b63..e08410efc0b 100644
--- a/spec/features/issues/resource_label_events_spec.rb
+++ b/spec/features/issues/resource_label_events_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe 'List issue resource label events', :js do
click_on 'Edit'
wait_for_requests
- labels.each { |label| click_link label }
+ labels.each { |label| click_on label }
send_keys(:escape)
wait_for_requests
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 3153bef966e..454fd50ac24 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -53,6 +53,19 @@ RSpec.describe 'User edit profile' do
expect(page).to have_content('Profile was successfully updated')
end
+ it 'does not set secondary emails without user input' do
+ fill_in 'user_organization', with: 'GitLab'
+ submit_settings
+
+ user.reload
+ expect(page).to have_field('user_commit_email', with: '')
+ expect(page).to have_field('user_public_email', with: '')
+
+ User::SECONDARY_EMAIL_ATTRIBUTES.each do |attribute|
+ expect(user.read_attribute(attribute)).to be_blank
+ end
+ end
+
it 'shows an error if the full name contains an emoji', :js do
simulate_input('#user_name', 'Martin 😀')
submit_settings
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js
index 23810339833..6e8841411a2 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/label_item_spec.js
@@ -1,4 +1,3 @@
-import { GlIcon, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import LabelItem from '~/vue_shared/components/sidebar/labels_select_widget/label_item.vue';
@@ -6,16 +5,10 @@ import { mockRegularLabel } from './mock_data';
const mockLabel = { ...mockRegularLabel, set: true };
-const createComponent = ({
- label = mockLabel,
- isLabelSet = mockLabel.set,
- highlight = true,
-} = {}) =>
+const createComponent = ({ label = mockLabel } = {}) =>
shallowMount(LabelItem, {
propsData: {
label,
- isLabelSet,
- highlight,
},
});
@@ -31,45 +24,6 @@ describe('LabelItem', () => {
});
describe('template', () => {
- it('renders gl-link component', () => {
- expect(wrapper.find(GlLink).exists()).toBe(true);
- });
-
- it('renders component root with class `is-focused` when `highlight` prop is true', () => {
- const wrapperTemp = createComponent({
- highlight: true,
- });
-
- expect(wrapperTemp.classes()).toContain('is-focused');
-
- wrapperTemp.destroy();
- });
-
- it('renders visible gl-icon component when `isLabelSet` prop is true', () => {
- const wrapperTemp = createComponent({
- isLabelSet: true,
- });
-
- const iconEl = wrapperTemp.find(GlIcon);
-
- expect(iconEl.isVisible()).toBe(true);
- expect(iconEl.props('name')).toBe('mobile-issue-close');
-
- wrapperTemp.destroy();
- });
-
- it('renders visible span element as placeholder instead of gl-icon when `isLabelSet` prop is false', () => {
- const wrapperTemp = createComponent({
- isLabelSet: false,
- });
-
- const placeholderEl = wrapperTemp.find('[data-testid="no-icon"]');
-
- expect(placeholderEl.isVisible()).toBe(true);
-
- wrapperTemp.destroy();
- });
-
it('renders label color element', () => {
const colorEl = wrapper.find('[data-testid="label-color-box"]');
diff --git a/spec/helpers/profiles_helper_spec.rb b/spec/helpers/profiles_helper_spec.rb
index c015937c299..c3a3c2a0178 100644
--- a/spec/helpers/profiles_helper_spec.rb
+++ b/spec/helpers/profiles_helper_spec.rb
@@ -24,20 +24,6 @@ RSpec.describe ProfilesHelper do
end
end
- describe '#selected_commit_email' do
- let(:user) { create(:user) }
-
- it 'returns main email when commit email attribute is nil' do
- expect(helper.selected_commit_email(user)).to eq(user.email)
- end
-
- it 'returns DB stored commit_email' do
- user.update!(commit_email: Gitlab::PrivateCommitEmail::TOKEN)
-
- expect(helper.selected_commit_email(user)).to eq(Gitlab::PrivateCommitEmail::TOKEN)
- end
- end
-
describe '#email_provider_label' do
it "returns nil for users without external email" do
user = create(:user)
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 0c28515b574..3aa6b2e3c05 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -440,17 +440,30 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
context 'when the environment name is invalid' do
let(:attributes) { { name: 'deploy', ref: 'master', environment: '!!!' } }
- it_behaves_like 'non-deployment job'
- it_behaves_like 'ensures environment inexistence'
+ it 'fails the job with a failure reason and does not create an environment' do
+ expect(subject).to be_failed
+ expect(subject).to be_environment_creation_failure
+ expect(subject.metadata.expanded_environment_name).to be_nil
+ expect(Environment.exists?(name: expected_environment_name)).to eq(false)
+ end
+
+ context 'when surface_environment_creation_failure feature flag is disabled' do
+ before do
+ stub_feature_flags(surface_environment_creation_failure: false)
+ end
- it 'tracks an exception' do
- expect(Gitlab::ErrorTracking).to receive(:track_exception)
- .with(an_instance_of(described_class::EnvironmentCreationFailure),
- project_id: project.id,
- reason: %q{Name can contain only letters, digits, '-', '_', '/', '$', '{', '}', '.', and spaces, but it cannot start or end with '/'})
- .once
+ it_behaves_like 'non-deployment job'
+ it_behaves_like 'ensures environment inexistence'
- subject
+ it 'tracks an exception' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(an_instance_of(described_class::EnvironmentCreationFailure),
+ project_id: project.id,
+ reason: %q{Name can contain only letters, digits, '-', '_', '/', '$', '{', '}', '.', and spaces, but it cannot start or end with '/'})
+ .once
+
+ subject
+ end
end
end
end
diff --git a/spec/presenters/commit_status_presenter_spec.rb b/spec/presenters/commit_status_presenter_spec.rb
index 4b2441d656e..f0bf1b860e4 100644
--- a/spec/presenters/commit_status_presenter_spec.rb
+++ b/spec/presenters/commit_status_presenter_spec.rb
@@ -15,6 +15,25 @@ RSpec.describe CommitStatusPresenter do
expect(described_class.superclass).to eq(Gitlab::View::Presenter::Delegated)
end
+ describe '#callout_failure_message' do
+ subject { presenter.callout_failure_message }
+
+ context 'when troubleshooting doc is available' do
+ let(:failure_reason) { :environment_creation_failure }
+
+ before do
+ build.failure_reason = failure_reason
+ end
+
+ it 'appends the troubleshooting link' do
+ doc = described_class::TROUBLESHOOTING_DOC[failure_reason]
+
+ expect(subject).to eq("#{described_class.callout_failure_messages[failure_reason]} " \
+ "<a href=\"#{presenter.help_page_path(doc[:path], anchor: doc[:anchor])}\">How do I fix it?</a>")
+ end
+ end
+ end
+
describe 'covers all failure reasons' do
let(:message) { presenter.callout_failure_message }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 6ccfabca101..4f393aed377 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -308,6 +308,10 @@ RSpec.configure do |config|
# For more information check https://gitlab.com/gitlab-org/gitlab/-/issues/339348
stub_feature_flags(new_header_search: false)
+ # Disable the override flag in order to enable the feature by default.
+ # See https://docs.gitlab.com/ee/development/feature_flags/#selectively-disable-by-actor
+ stub_feature_flags(surface_environment_creation_failure_override: false)
+
allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged)
else
unstub_all_feature_flags