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>2022-03-15 21:08:10 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-03-15 21:08:10 +0300
commit7f08e6916d8259a8ed1549cb54460f0b746d9d8b (patch)
tree40853e994af97de42bd68076bd0bffa6be5c2814
parentcb7f766437db0c483adf3c128e35c64237a2ef53 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock12
-rw-r--r--app/assets/javascripts/environments/components/new_environment_folder.vue4
-rw-r--r--app/assets/javascripts/environments/components/new_environments_app.vue18
-rw-r--r--app/assets/javascripts/environments/constants.js10
-rw-r--r--app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue41
-rw-r--r--app/assets/javascripts/members/constants.js37
-rw-r--r--app/assets/javascripts/members/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/group_members/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue4
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js1
-rw-r--r--app/assets/stylesheets/framework/mixins.scss6
-rw-r--r--app/assets/stylesheets/notify.scss24
-rw-r--r--app/assets/stylesheets/notify_base.scss25
-rw-r--r--app/assets/stylesheets/notify_enhanced.scss68
-rw-r--r--app/controllers/projects/environments_controller.rb12
-rw-r--r--app/controllers/projects/redirect_controller.rb20
-rw-r--r--app/controllers/projects_controller.rb18
-rw-r--r--app/graphql/resolvers/concerns/group_issuable_resolver.rb23
-rw-r--r--app/graphql/resolvers/group_issues_resolver.rb6
-rw-r--r--app/graphql/resolvers/group_merge_requests_resolver.rb5
-rw-r--r--app/helpers/learn_gitlab_helper.rb38
-rw-r--r--app/models/project.rb4
-rw-r--r--app/views/layouts/notify.html.haml5
-rw-r--r--app/views/layouts/service_desk.html.haml5
-rw-r--r--app/views/notify/_note_email.html.haml4
-rw-r--r--app/views/notify/issue_due_email.html.haml4
-rw-r--r--app/views/notify/new_issue_email.html.haml4
-rw-r--r--app/views/notify/new_merge_request_email.html.haml2
-rw-r--r--app/views/notify/new_release_email.html.haml2
-rw-r--r--app/views/notify/service_desk_new_note_email.html.haml2
-rw-r--r--config/application.rb1
-rw-r--r--config/feature_flags/development/enhanced_notify_css.yml (renamed from config/feature_flags/development/block_project_serialization.yml)8
-rw-r--r--config/feature_flags/experiment/change_continuous_onboarding_link_urls.yml8
-rw-r--r--config/routes.rb2
-rw-r--r--doc/.vale/gitlab/HeadingContent.yml18
-rw-r--r--doc/administration/geo/index.md2
-rw-r--r--doc/api/graphql/reference/index.md2
-rw-r--r--doc/api/group_labels.md2
-rw-r--r--doc/api/issues.md22
-rw-r--r--doc/development/documentation/index.md20
-rw-r--r--doc/development/service_ping/index.md5
-rw-r--r--doc/user/application_security/cluster_image_scanning/index.md2
-rw-r--r--doc/user/application_security/container_scanning/index.md2
-rw-r--r--doc/user/group/index.md1
-rw-r--r--doc/user/project/img/labels_sort_label_priority.pngbin42263 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_sort_priority.pngbin41486 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_subscriptions_v13_5.pngbin11375 -> 0 bytes
-rw-r--r--doc/user/project/issue_board.md2
-rw-r--r--doc/user/project/issues/sorting_issue_lists.md6
-rw-r--r--doc/user/project/labels.md441
-rw-r--r--doc/user/search/index.md2
-rw-r--r--lib/backup/manager.rb59
-rw-r--r--lib/gitlab/ci/config/entry/reports.rb16
-rw-r--r--lib/gitlab/ci/config/entry/reports/coverage_report.rb31
-rw-r--r--lib/gitlab/config/entry/validators.rb11
-rw-r--r--lib/learn_gitlab/onboarding.rb16
-rwxr-xr-xscripts/merge-simplecov24
-rw-r--r--spec/controllers/projects_controller_spec.rb53
-rw-r--r--spec/frontend/environments/new_environments_app_spec.js18
-rw-r--r--spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js54
-rw-r--r--spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap14
-rw-r--r--spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js8
-rw-r--r--spec/frontend/pages/projects/learn_gitlab/components/mock_data.js1
-rw-r--r--spec/graphql/resolvers/base_resolver_spec.rb10
-rw-r--r--spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb6
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/namespace_projects_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/project_resolver_spec.rb4
-rw-r--r--spec/graphql/types/base_enum_spec.rb2
-rw-r--r--spec/graphql/types/base_field_spec.rb28
-rw-r--r--spec/graphql/types/global_id_type_spec.rb8
-rw-r--r--spec/helpers/learn_gitlab_helper_spec.rb79
-rw-r--r--spec/lib/backup/manager_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb57
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports_spec.rb47
-rw-r--r--spec/lib/gitlab/config/entry/validators_spec.rb43
-rw-r--r--spec/lib/gitlab/graphql/markdown_field_spec.rb12
-rw-r--r--spec/lib/gitlab/graphql/mount_mutation_spec.rb8
-rw-r--r--spec/lib/learn_gitlab/onboarding_spec.rb2
-rw-r--r--spec/models/project_spec.rb8
-rw-r--r--spec/requests/api/graphql/group/issues_spec.rb25
-rw-r--r--spec/requests/api/graphql/group/merge_requests_spec.rb21
-rw-r--r--spec/requests/projects/redirect_controller_spec.rb66
-rw-r--r--spec/routing/project_routing_spec.rb6
-rw-r--r--spec/services/ci/create_pipeline_service/artifacts_spec.rb67
-rw-r--r--spec/services/notification_service_spec.rb1
88 files changed, 1159 insertions, 622 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 804e2152697..200d09d4331 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-d0d5fa790767c12eeadb40a1ecfbc00fde2a4768
+4ef97df05e54269d90fdbd4d2f59fcc29b1afcdf
diff --git a/Gemfile b/Gemfile
index 5e7b3738185..66b2ad17397 100644
--- a/Gemfile
+++ b/Gemfile
@@ -153,7 +153,7 @@ gem 'html-pipeline', '~> 2.13.2'
gem 'deckar01-task_list', '2.3.1'
gem 'gitlab-markup', '~> 1.8.0'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
-gem 'commonmarker', '~> 0.23.2'
+gem 'commonmarker', '~> 0.23.4'
gem 'kramdown', '~> 2.3.1'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 6.3.2'
@@ -405,7 +405,7 @@ group :development, :test, :danger do
end
group :development, :test, :coverage do
- gem 'simplecov', '~> 0.18.5', require: false
+ gem 'simplecov', '~> 0.21', require: false
gem 'simplecov-lcov', '~> 0.8.0', require: false
gem 'simplecov-cobertura', '~> 1.3.1', require: false
gem 'undercover', '~> 0.4.4', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index d2e2d1e6438..00d16ddbfd5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -202,7 +202,7 @@ GEM
open4 (~> 1.3)
coderay (1.1.3)
colored2 (3.1.2)
- commonmarker (0.23.2)
+ commonmarker (0.23.4)
concurrent-ruby (1.1.9)
connection_pool (2.2.5)
contracts (0.11.0)
@@ -281,7 +281,7 @@ GEM
diffy (3.3.0)
discordrb-webhooks (3.4.2)
rest-client (>= 2.0.0)
- docile (1.3.2)
+ docile (1.4.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.5.0.rc2)
@@ -1207,13 +1207,15 @@ GEM
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simple_po_parser (1.1.2)
- simplecov (0.18.5)
+ simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
+ simplecov_json_formatter (~> 0.1)
simplecov-cobertura (1.3.1)
simplecov (~> 0.8)
simplecov-html (0.12.3)
simplecov-lcov (0.8.0)
+ simplecov_json_formatter (0.1.4)
sixarm_ruby_unaccent (1.2.0)
slack-messenger (2.3.4)
snowplow-tracker (0.6.1)
@@ -1438,7 +1440,7 @@ DEPENDENCIES
capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3)
charlock_holmes (~> 0.7.7)
- commonmarker (~> 0.23.2)
+ commonmarker (~> 0.23.4)
concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
countries (~> 3.0)
@@ -1648,7 +1650,7 @@ DEPENDENCIES
sidekiq-cron (~> 1.2)
sigdump (~> 0.2.4)
simple_po_parser (~> 1.1.2)
- simplecov (~> 0.18.5)
+ simplecov (~> 0.21)
simplecov-cobertura (~> 1.3.1)
simplecov-lcov (~> 0.8.0)
slack-messenger (~> 2.3.4)
diff --git a/app/assets/javascripts/environments/components/new_environment_folder.vue b/app/assets/javascripts/environments/components/new_environment_folder.vue
index 30a178db5cc..d5c6d26cfd0 100644
--- a/app/assets/javascripts/environments/components/new_environment_folder.vue
+++ b/app/assets/javascripts/environments/components/new_environment_folder.vue
@@ -3,6 +3,7 @@ import { GlButton, GlCollapse, GlIcon, GlBadge, GlLink } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import pollIntervalQuery from '../graphql/queries/poll_interval.query.graphql';
import folderQuery from '../graphql/queries/folder.query.graphql';
+import { ENVIRONMENT_COUNT_BY_SCOPE } from '../constants';
import EnvironmentItem from './new_environment_item.vue';
export default {
@@ -56,7 +57,8 @@ export default {
return this.visible ? this.$options.i18n.collapse : this.$options.i18n.expand;
},
count() {
- return this.folder?.[`${this.scope}Count`] ?? 0;
+ const count = ENVIRONMENT_COUNT_BY_SCOPE[this.scope];
+ return this.folder?.[count] ?? 0;
},
folderClass() {
return { 'gl-font-weight-bold': this.visible };
diff --git a/app/assets/javascripts/environments/components/new_environments_app.vue b/app/assets/javascripts/environments/components/new_environments_app.vue
index 8e6457ed918..087e9cd2fcd 100644
--- a/app/assets/javascripts/environments/components/new_environments_app.vue
+++ b/app/assets/javascripts/environments/components/new_environments_app.vue
@@ -9,6 +9,7 @@ import environmentToDeleteQuery from '../graphql/queries/environment_to_delete.q
import environmentToRollbackQuery from '../graphql/queries/environment_to_rollback.query.graphql';
import environmentToStopQuery from '../graphql/queries/environment_to_stop.query.graphql';
import environmentToChangeCanaryQuery from '../graphql/queries/environment_to_change_canary.query.graphql';
+import { ENVIRONMENTS_SCOPE } from '../constants';
import EnvironmentFolder from './new_environment_folder.vue';
import EnableReviewAppModal from './enable_review_app_modal.vue';
import StopEnvironmentModal from './stop_environment_modal.vue';
@@ -82,12 +83,14 @@ export default {
},
modalId: 'enable-review-app-info',
data() {
- const { page = '1', scope = 'available' } = queryToObject(window.location.search);
+ const { page = '1', scope } = queryToObject(window.location.search);
return {
interval: undefined,
isReviewAppModalVisible: false,
page: parseInt(page, 10),
- scope,
+ scope: Object.values(ENVIRONMENTS_SCOPE).includes(scope)
+ ? scope
+ : ENVIRONMENTS_SCOPE.AVAILABLE,
environmentToDelete: {},
environmentToRollback: {},
environmentToStop: {},
@@ -188,6 +191,7 @@ export default {
});
},
},
+ ENVIRONMENTS_SCOPE,
};
</script>
<template>
@@ -209,7 +213,10 @@ export default {
query-param-name="scope"
@primary="showReviewAppModal"
>
- <gl-tab query-param-value="available" @click="setScope('available')">
+ <gl-tab
+ :query-param-value="$options.ENVIRONMENTS_SCOPE.AVAILABLE"
+ @click="setScope($options.ENVIRONMENTS_SCOPE.AVAILABLE)"
+ >
<template #title>
<span>{{ $options.i18n.available }}</span>
<gl-badge size="sm" class="gl-tab-counter-badge">
@@ -217,7 +224,10 @@ export default {
</gl-badge>
</template>
</gl-tab>
- <gl-tab query-param-value="stopped" @click="setScope('stopped')">
+ <gl-tab
+ :query-param-value="$options.ENVIRONMENTS_SCOPE.STOPPED"
+ @click="setScope($options.ENVIRONMENTS_SCOPE.STOPPED)"
+ >
<template #title>
<span>{{ $options.i18n.stopped }}</span>
<gl-badge size="sm" class="gl-tab-counter-badge">
diff --git a/app/assets/javascripts/environments/constants.js b/app/assets/javascripts/environments/constants.js
index 6d427bef4e6..942491039d6 100644
--- a/app/assets/javascripts/environments/constants.js
+++ b/app/assets/javascripts/environments/constants.js
@@ -38,3 +38,13 @@ export const CANARY_STATUS = {
};
export const CANARY_UPDATE_MODAL = 'confirm-canary-change';
+
+export const ENVIRONMENTS_SCOPE = {
+ AVAILABLE: 'available',
+ STOPPED: 'stopped',
+};
+
+export const ENVIRONMENT_COUNT_BY_SCOPE = {
+ [ENVIRONMENTS_SCOPE.AVAILABLE]: 'availableCount',
+ [ENVIRONMENTS_SCOPE.STOPPED]: 'stoppedCount',
+};
diff --git a/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
index 633dee75237..ca60f876c6f 100644
--- a/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
+++ b/app/assets/javascripts/members/components/filter_sort/members_filtered_search_bar.vue
@@ -1,5 +1,4 @@
<script>
-import { GlFilteredSearchToken } from '@gitlab/ui';
import { mapState } from 'vuex';
import {
getParameterByName,
@@ -7,46 +6,24 @@ import {
queryToObject,
redirectTo,
} from '~/lib/utils/url_utility';
-import { s__ } from '~/locale';
import {
SEARCH_TOKEN_TYPE,
SORT_QUERY_PARAM_NAME,
ACTIVE_TAB_QUERY_PARAM_NAME,
-} from '~/members/constants';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+ AVAILABLE_FILTERED_SEARCH_TOKENS,
+} from 'ee_else_ce/members/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
export default {
name: 'MembersFilteredSearchBar',
components: { FilteredSearchBar },
- availableTokens: [
- {
- type: 'two_factor',
- icon: 'lock',
- title: s__('Members|2FA'),
- token: GlFilteredSearchToken,
- unique: true,
- operators: OPERATOR_IS_ONLY,
- options: [
- { value: 'enabled', title: s__('Members|Enabled') },
- { value: 'disabled', title: s__('Members|Disabled') },
- ],
- requiredPermissions: 'canManageMembers',
- },
- {
- type: 'with_inherited_permissions',
- icon: 'group',
- title: s__('Members|Membership'),
- token: GlFilteredSearchToken,
- unique: true,
- operators: OPERATOR_IS_ONLY,
- options: [
- { value: 'exclude', title: s__('Members|Direct') },
- { value: 'only', title: s__('Members|Inherited') },
- ],
- },
- ],
- inject: ['namespace', 'sourceId', 'canManageMembers'],
+ availableTokens: AVAILABLE_FILTERED_SEARCH_TOKENS,
+ inject: {
+ namespace: {},
+ sourceId: {},
+ canManageMembers: {},
+ canFilterByEnterprise: { default: false },
+ },
data() {
return {
initialFilterValue: [],
diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js
index 273f1acebc7..49ce00a1689 100644
--- a/app/assets/javascripts/members/constants.js
+++ b/app/assets/javascripts/members/constants.js
@@ -1,4 +1,7 @@
-import { __ } from '~/locale';
+import { GlFilteredSearchToken } from '@gitlab/ui';
+
+import { __, s__ } from '~/locale';
+import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
export const FIELD_KEY_ACCOUNT = 'account';
export const FIELD_KEY_SOURCE = 'source';
@@ -82,6 +85,38 @@ export const DEFAULT_SORT = {
sortDesc: false,
};
+export const FILTERED_SEARCH_TOKEN_TWO_FACTOR = {
+ type: 'two_factor',
+ icon: 'lock',
+ title: s__('Members|2FA'),
+ token: GlFilteredSearchToken,
+ unique: true,
+ operators: OPERATOR_IS_ONLY,
+ options: [
+ { value: 'enabled', title: s__('Members|Enabled') },
+ { value: 'disabled', title: s__('Members|Disabled') },
+ ],
+ requiredPermissions: 'canManageMembers',
+};
+
+export const FILTERED_SEARCH_TOKEN_WITH_INHERITED_PERMISSIONS = {
+ type: 'with_inherited_permissions',
+ icon: 'group',
+ title: s__('Members|Membership'),
+ token: GlFilteredSearchToken,
+ unique: true,
+ operators: OPERATOR_IS_ONLY,
+ options: [
+ { value: 'exclude', title: s__('Members|Direct') },
+ { value: 'only', title: s__('Members|Inherited') },
+ ],
+};
+
+export const AVAILABLE_FILTERED_SEARCH_TOKENS = [
+ FILTERED_SEARCH_TOKEN_TWO_FACTOR,
+ FILTERED_SEARCH_TOKEN_WITH_INHERITED_PERMISSIONS,
+];
+
export const AVATAR_SIZE = 48;
export const MEMBER_TYPES = {
diff --git a/app/assets/javascripts/members/index.js b/app/assets/javascripts/members/index.js
index 510e89240f4..0df876cabd7 100644
--- a/app/assets/javascripts/members/index.js
+++ b/app/assets/javascripts/members/index.js
@@ -18,6 +18,7 @@ export const initMembersApp = (el, options) => {
sourceId,
canManageMembers,
canExportMembers,
+ canFilterByEnterprise,
exportCsvPath,
...vuexStoreAttributes
} = parseDataAttributes(el);
@@ -60,6 +61,7 @@ export const initMembersApp = (el, options) => {
currentUserId: gon.current_user_id || null,
sourceId,
canManageMembers,
+ canFilterByEnterprise,
canExportMembers,
exportCsvPath,
},
diff --git a/app/assets/javascripts/pages/groups/group_members/index.js b/app/assets/javascripts/pages/groups/group_members/index.js
index 36f37dab40d..be12ca6b015 100644
--- a/app/assets/javascripts/pages/groups/group_members/index.js
+++ b/app/assets/javascripts/pages/groups/group_members/index.js
@@ -21,7 +21,7 @@ initMembersApp(document.querySelector('.js-group-members-list-app'), {
requestFormatter: groupMemberRequestFormatter,
filteredSearchBar: {
show: true,
- tokens: ['two_factor', 'with_inherited_permissions'],
+ tokens: ['two_factor', 'with_inherited_permissions', 'enterprise'],
searchParam: 'search',
placeholder: s__('Members|Filter members'),
recentSearchesStorageKey: 'group_members',
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
index d0ec02bbd0c..573f996a254 100644
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
@@ -32,7 +32,7 @@ export default {
);
},
openInNewTab() {
- return ACTION_LABELS[this.action]?.openInNewTab === true;
+ return ACTION_LABELS[this.action]?.openInNewTab === true || this.value.openInNewTab === true;
},
},
methods: {
@@ -65,8 +65,6 @@ export default {
data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link"
:data-track-label="$options.i18n.ACTION_LABELS[action].title"
- data-track-property="Growth::Conversion::Experiment::LearnGitLab"
- data-track-experiment="change_continuous_onboarding_link_urls"
>
{{ $options.i18n.ACTION_LABELS[action].title }}
</gl-link>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
index 880cf699e5e..1887c48dd1b 100644
--- a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
@@ -62,7 +62,6 @@ export const ACTION_LABELS = {
description: s__('LearnGitLab|Scan your code to uncover vulnerabilities before deploying.'),
section: 'deploy',
position: 1,
- openInNewTab: true,
},
issueCreated: {
title: s__('LearnGitLab|Create an issue'),
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 1e51bf3d974..1caf02937d5 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -439,6 +439,12 @@
.na {
color: inherit;
}
+
+ // Rouge `Comment` token (quoted text in email body)
+ .c {
+ color: $gl-grayish-blue;
+ font-style: italic;
+ }
}
}
}
diff --git a/app/assets/stylesheets/notify.scss b/app/assets/stylesheets/notify.scss
index ca8c358d97f..2d501781119 100644
--- a/app/assets/stylesheets/notify.scss
+++ b/app/assets/stylesheets/notify.scss
@@ -1,24 +1,4 @@
-@import 'framework/mixins';
-@import 'framework/variables';
-
-img {
- max-width: 100%;
- height: auto;
-}
-
-p.details {
- font-style: italic;
- color: $gray-500;
-}
-
-.footer > p {
- font-size: small;
- color: $gray-500;
-}
-
-pre.commit-message {
- white-space: pre-wrap;
-}
+@import 'notify_base';
.gl-label-scoped {
border: 2px solid currentColor;
@@ -52,6 +32,4 @@ pre.commit-message {
border: 1px solid $gray-100;
border-radius: $border-radius-small;
}
-
- @include email-code-block;
}
diff --git a/app/assets/stylesheets/notify_base.scss b/app/assets/stylesheets/notify_base.scss
new file mode 100644
index 00000000000..8c6f9a27077
--- /dev/null
+++ b/app/assets/stylesheets/notify_base.scss
@@ -0,0 +1,25 @@
+@import 'framework/mixins';
+@import 'framework/variables';
+
+img {
+ max-width: 100%;
+ height: auto;
+}
+
+p.details {
+ font-style: italic;
+ color: $gray-500;
+}
+
+.footer > p {
+ font-size: small;
+ color: $gray-500;
+}
+
+pre.commit-message {
+ white-space: pre-wrap;
+}
+
+.content {
+ @include email-code-block;
+}
diff --git a/app/assets/stylesheets/notify_enhanced.scss b/app/assets/stylesheets/notify_enhanced.scss
new file mode 100644
index 00000000000..5df5a8592bf
--- /dev/null
+++ b/app/assets/stylesheets/notify_enhanced.scss
@@ -0,0 +1,68 @@
+// Import a subset of the GitLab UI framework:
+// keep parts that affect elements that can appear in emails;
+// omit Bootstrap Reboot since it adds unnecessary styles to every element.
+@import 'notify_base';
+@import 'bootstrap/scss/functions';
+@import 'bootstrap/scss/variables';
+@import 'bootstrap/scss/mixins';
+@import 'bootstrap/scss/code';
+@import '@gitlab/ui/src/scss/variables';
+@import '@gitlab/ui/src/scss/utility-mixins/index';
+@import '@gitlab/ui/src/scss/components';
+@import 'bootstrap_migration';
+@import 'framework/common';
+@import 'framework/gfm';
+@import 'framework/kbd';
+@import 'framework/tables';
+@import 'framework/typography';
+@import 'framework/emojis';
+
+body {
+ font-family: $regular-font;
+ font-size: inherit;
+}
+
+a {
+ text-decoration: none;
+}
+
+.content {
+ .md {
+ padding: 1rem 0;
+ }
+
+ hr {
+ border: 1px solid #e1e1e1;
+ }
+
+ blockquote {
+ border-top-width: 0;
+ border-bottom-width: 0;
+ border-right-width: 0;
+
+ &:dir(rtl) {
+ border-left-width: 0;
+ border-right-width: inherit;
+ }
+ }
+
+ table {
+ border-collapse: collapse;
+ }
+
+ .diff-table.code,
+ table.code {
+ width: auto;
+
+ td {
+ padding: inherit;
+
+ pre {
+ background-color: inherit;
+ margin: 0;
+ padding: 0;
+ border: inherit;
+ }
+ }
+ }
+}
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 84ebdcd9364..eabc048e341 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -29,13 +29,14 @@ class Projects::EnvironmentsController < Projects::ApplicationController
feature_category :continuous_delivery
def index
- @environments = project.environments
- .with_state(params[:scope] || :available)
@project = ProjectPresenter.new(project, current_user: current_user)
respond_to do |format|
format.html
format.json do
+ @environments = project.environments
+ .with_state(params[:scope] || :available)
+
Gitlab::PollingInterval.set_header(response, interval: 3_000)
environments_count_by_state = project.environments.count_by_state
@@ -52,14 +53,15 @@ class Projects::EnvironmentsController < Projects::ApplicationController
# Returns all environments for a given folder
# rubocop: disable CodeReuse/ActiveRecord
def folder
- folder_environments = project.environments.where(environment_type: params[:id])
- @environments = folder_environments.with_state(params[:scope] || :available)
- .order(:name)
@folder = params[:id]
respond_to do |format|
format.html
format.json do
+ folder_environments = project.environments.where(environment_type: params[:id])
+ @environments = folder_environments.with_state(params[:scope] || :available)
+ .order(:name)
+
render json: {
environments: serialize_environments(request, response),
available_count: folder_environments.available.count,
diff --git a/app/controllers/projects/redirect_controller.rb b/app/controllers/projects/redirect_controller.rb
new file mode 100644
index 00000000000..6bcbe87ee42
--- /dev/null
+++ b/app/controllers/projects/redirect_controller.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+# Projects::RedirectController is used to resolve the route projects/:id.
+# It's helpful for this to be in its own controller so that the
+# ProjectsController can assume that :namespace_id exists
+class Projects::RedirectController < ::ApplicationController
+ skip_before_action :authenticate_user!
+
+ feature_category :projects
+
+ def redirect_from_id
+ project = Project.find(params[:id])
+
+ if can?(current_user, :read_project, project)
+ redirect_to project
+ else
+ render_404
+ end
+ end
+end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 7b45bed426b..9f971800ff5 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -17,10 +17,10 @@ class ProjectsController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
before_action :disable_query_limiting, only: [:show, :create]
- before_action :authenticate_user!, except: [:index, :show, :activity, :refs, :resolve, :unfoldered_environment_names]
+ before_action :authenticate_user!, except: [:index, :show, :activity, :refs, :unfoldered_environment_names]
before_action :redirect_git_extension, only: [:show]
- before_action :project, except: [:index, :new, :create, :resolve]
- before_action :repository, except: [:index, :new, :create, :resolve]
+ before_action :project, except: [:index, :new, :create]
+ before_action :repository, except: [:index, :new, :create]
before_action :verify_git_import_enabled, only: [:create]
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
before_action :present_project, only: [:edit]
@@ -48,7 +48,7 @@ class ProjectsController < Projects::ApplicationController
feature_category :projects, [
:index, :show, :new, :create, :edit, :update, :transfer,
- :destroy, :resolve, :archive, :unarchive, :toggle_star, :activity
+ :destroy, :archive, :unarchive, :toggle_star, :activity
]
feature_category :source_code_management, [:remove_fork, :housekeeping, :refs]
@@ -324,16 +324,6 @@ class ProjectsController < Projects::ApplicationController
end
# rubocop: enable CodeReuse/ActiveRecord
- def resolve
- @project = Project.find(params[:id])
-
- if can?(current_user, :read_project, @project)
- redirect_to @project
- else
- render_404
- end
- end
-
def unfoldered_environment_names
respond_to do |format|
format.json do
diff --git a/app/graphql/resolvers/concerns/group_issuable_resolver.rb b/app/graphql/resolvers/concerns/group_issuable_resolver.rb
index 542ff5374ff..92d22409ff2 100644
--- a/app/graphql/resolvers/concerns/group_issuable_resolver.rb
+++ b/app/graphql/resolvers/concerns/group_issuable_resolver.rb
@@ -3,12 +3,21 @@
module GroupIssuableResolver
extend ActiveSupport::Concern
- class_methods do
- def include_subgroups(name_of_things)
- argument :include_subgroups, GraphQL::Types::Boolean,
- required: false,
- default_value: false,
- description: "Include #{name_of_things} belonging to subgroups"
- end
+ included do
+ argument :include_subgroups, GraphQL::Types::Boolean,
+ required: false,
+ default_value: false,
+ description: "Include #{issuable_collection_name} belonging to subgroups"
+
+ argument :include_archived, GraphQL::Types::Boolean,
+ required: false,
+ default_value: false,
+ description: "Return #{issuable_collection_name} from archived projects"
+ end
+
+ def resolve(**args)
+ args[:non_archived] = !args.delete(:include_archived)
+
+ super
end
end
diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb
index 28f9266974f..05c5e803539 100644
--- a/app/graphql/resolvers/group_issues_resolver.rb
+++ b/app/graphql/resolvers/group_issues_resolver.rb
@@ -3,9 +3,11 @@
module Resolvers
class GroupIssuesResolver < BaseIssuesResolver
- include GroupIssuableResolver
+ def self.issuable_collection_name
+ 'issues'
+ end
- include_subgroups 'issues'
+ include GroupIssuableResolver
def ready?(**args)
if args.dig(:not, :release_tag).present?
diff --git a/app/graphql/resolvers/group_merge_requests_resolver.rb b/app/graphql/resolvers/group_merge_requests_resolver.rb
index 34a4c67bc56..da1b6169c07 100644
--- a/app/graphql/resolvers/group_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/group_merge_requests_resolver.rb
@@ -2,13 +2,16 @@
module Resolvers
class GroupMergeRequestsResolver < MergeRequestsResolver
+ def self.issuable_collection_name
+ 'merge requests'
+ end
+
include GroupIssuableResolver
alias_method :group, :object
type Types::MergeRequestType.connection_type, null: true
- include_subgroups 'merge requests'
accept_assignee
accept_author
diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb
index 7dfd9ed47e3..60f3b12d736 100644
--- a/app/helpers/learn_gitlab_helper.rb
+++ b/app/helpers/learn_gitlab_helper.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
module LearnGitlabHelper
+ IMAGE_PATH_PLAN = "learn_gitlab/section_plan.svg"
+ IMAGE_PATH_DEPLOY = "learn_gitlab/section_deploy.svg"
+ IMAGE_PATH_WORKSPACE = "learn_gitlab/section_workspace.svg"
+
def learn_gitlab_enabled?(project)
return false unless current_user
@@ -25,19 +29,7 @@ module LearnGitlabHelper
def onboarding_actions_data(project)
attributes = onboarding_progress(project).attributes.symbolize_keys
- urls_to_use = nil
-
- experiment(
- :change_continuous_onboarding_link_urls,
- namespace: project.namespace,
- actor: current_user,
- sticky_to: project.namespace
- ) do |e|
- e.control { urls_to_use = action_urls }
- e.candidate { urls_to_use = new_action_urls(project) }
- end
-
- urls_to_use.to_h do |action, url|
+ action_urls(project).to_h do |action, url|
[
action,
url: url,
@@ -50,13 +42,13 @@ module LearnGitlabHelper
def onboarding_sections_data
{
workspace: {
- svg: image_path("learn_gitlab/section_workspace.svg")
+ svg: image_path(IMAGE_PATH_WORKSPACE)
},
plan: {
- svg: image_path("learn_gitlab/section_plan.svg")
+ svg: image_path(IMAGE_PATH_PLAN)
},
deploy: {
- svg: image_path("learn_gitlab/section_deploy.svg")
+ svg: image_path(IMAGE_PATH_DEPLOY)
}
}
end
@@ -65,22 +57,20 @@ module LearnGitlabHelper
{ name: project.name }
end
- def action_urls
- LearnGitlab::Onboarding::ACTION_ISSUE_IDS.transform_values { |id| project_issue_url(learn_gitlab_project, id) }
- .merge(LearnGitlab::Onboarding::ACTION_DOC_URLS)
- end
-
- def new_action_urls(project)
- action_urls.merge(
+ def action_urls(project)
+ action_issue_urls.merge(
issue_created: project_issues_path(project),
git_write: project_path(project),
- pipeline_created: project_pipelines_path(project),
merge_request_created: project_merge_requests_path(project),
user_added: project_members_url(project),
security_scan_enabled: project_security_configuration_path(project)
)
end
+ def action_issue_urls
+ LearnGitlab::Onboarding::ACTION_ISSUE_IDS.transform_values { |id| project_issue_url(learn_gitlab_project, id) }
+ end
+
def learn_gitlab_project
@learn_gitlab_project ||= LearnGitlab::Project.new(current_user).project
end
diff --git a/app/models/project.rb b/app/models/project.rb
index e55395b32e7..3778db48ff2 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -3046,10 +3046,6 @@ class Project < ApplicationRecord
Projects::SyncEvent.enqueue_worker
end
end
-
- def allow_serialization?(options = nil)
- Feature.disabled?(:block_project_serialization, self, default_enabled: :yaml) || super
- end
end
Project.prepend_mod_with('Project')
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index e922b505be8..3b979f69cac 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -3,7 +3,10 @@
%meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" }
%title
GitLab
- = stylesheet_link_tag 'notify'
+ - if Feature.enabled?(:enhanced_notify_css)
+ = stylesheet_link_tag 'notify_enhanced'
+ - else
+ = stylesheet_link_tag 'notify'
= yield :head
%body
.content
diff --git a/app/views/layouts/service_desk.html.haml b/app/views/layouts/service_desk.html.haml
index 26d15a74403..a838ba91d26 100644
--- a/app/views/layouts/service_desk.html.haml
+++ b/app/views/layouts/service_desk.html.haml
@@ -5,7 +5,10 @@
%title
GitLab
-# haml-lint:enable NoPlainNodes
- = stylesheet_link_tag 'notify'
+ - if Feature.enabled?(:enhanced_notify_css)
+ = stylesheet_link_tag 'notify_enhanced'
+ - else
+ = stylesheet_link_tag 'notify'
= yield :head
%body
.content
diff --git a/app/views/notify/_note_email.html.haml b/app/views/notify/_note_email.html.haml
index ad0c873bf56..55984472047 100644
--- a/app/views/notify/_note_email.html.haml
+++ b/app/views/notify/_note_email.html.haml
@@ -25,11 +25,11 @@
= content_for :head do
= stylesheet_link_tag 'mailers/highlighted_diff_email'
- %table
+ %table.code
= render partial: "projects/diffs/email_line",
collection: discussion.truncated_diff_lines(diff_limit: diff_limit),
as: :line,
locals: { diff_file: discussion.diff_file }
-%div{ style: note_style }
+.md{ style: note_style }
= markdown(note.note, pipeline: :email, author: note.author, current_user: @recipient, issuable_reference_expansion_enabled: true)
diff --git a/app/views/notify/issue_due_email.html.haml b/app/views/notify/issue_due_email.html.haml
index c9cd9c32b54..e512d7732e2 100644
--- a/app/views/notify/issue_due_email.html.haml
+++ b/app/views/notify/issue_due_email.html.haml
@@ -8,5 +8,5 @@
This issue is due on: #{@issue.due_date.to_s(:medium)}
- if @issue.description
- %div
- = markdown(@issue.description, pipeline: :email, author: @issue.author, current_user: @recipient, issuable_reference_expansion_enabled: true)
+ .md
+ = markdown(@issue.description, pipeline: :email, author: @issue.author, current_user: @recipient, issuable_reference_expansion_enabled: true)
diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml
index 439604a950a..592b3f453af 100644
--- a/app/views/notify/new_issue_email.html.haml
+++ b/app/views/notify/new_issue_email.html.haml
@@ -7,5 +7,5 @@
= assignees_label(@issue)
- if @issue.description
- %div
- = markdown(@issue.description, pipeline: :email, author: @issue.author, current_user: @recipient, issuable_reference_expansion_enabled: true)
+ .md
+ = markdown(@issue.description, pipeline: :email, author: @issue.author, current_user: @recipient, issuable_reference_expansion_enabled: true)
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index 54fb6573c26..f67ac5f8fb2 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -15,5 +15,5 @@
= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter
- if @merge_request.description
- %div
+ .md
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author, current_user: @recipient, issuable_reference_expansion_enabled: true)
diff --git a/app/views/notify/new_release_email.html.haml b/app/views/notify/new_release_email.html.haml
index 1cd3a2340c6..09c0e7a8abd 100644
--- a/app/views/notify/new_release_email.html.haml
+++ b/app/views/notify/new_release_email.html.haml
@@ -1,7 +1,7 @@
- release_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: @target_url }
- description_details = { tag: @release.tag, name: @project.name, release_link_start: release_link_start, release_link_end: '</a>'.html_safe }
-%div{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+.md{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%p
= _("A new Release %{tag} for %{name} was published. Visit the %{release_link_start}Releases page%{release_link_end} to read more about it.").html_safe % description_details
diff --git a/app/views/notify/service_desk_new_note_email.html.haml b/app/views/notify/service_desk_new_note_email.html.haml
index 186bdf133e3..0c16cf3315f 100644
--- a/app/views/notify/service_desk_new_note_email.html.haml
+++ b/app/views/notify/service_desk_new_note_email.html.haml
@@ -1,5 +1,5 @@
- if Gitlab::CurrentSettings.email_author_in_body
%div
= _("%{author_link} wrote:").html_safe % { author_link: link_to(@note.author_name, user_url(@note.author)) }
-%div
+.md
= markdown(@note.note, pipeline: :email, author: @note.author, issuable_reference_expansion_enabled: true)
diff --git a/config/application.rb b/config/application.rb
index 8d795e6bc4e..e66f64ce791 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -247,6 +247,7 @@ module Gitlab
config.assets.precompile << "mailer.css"
config.assets.precompile << "mailer_client_specific.css"
config.assets.precompile << "notify.css"
+ config.assets.precompile << "notify_enhanced.css"
config.assets.precompile << "mailers/*.css"
config.assets.precompile << "page_bundles/_mixins_and_variables_and_functions.css"
config.assets.precompile << "page_bundles/admin/application_settings_metrics_and_profiling.css"
diff --git a/config/feature_flags/development/block_project_serialization.yml b/config/feature_flags/development/enhanced_notify_css.yml
index c4ef7145fcb..e47db3ba435 100644
--- a/config/feature_flags/development/block_project_serialization.yml
+++ b/config/feature_flags/development/enhanced_notify_css.yml
@@ -1,8 +1,8 @@
---
-name: block_project_serialization
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81900
+name: enhanced_notify_css
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78604
rollout_issue_url:
-milestone: '14.9'
+milestone: '14.8'
type: development
-group: group::workspace
+group: group::project management
default_enabled: false
diff --git a/config/feature_flags/experiment/change_continuous_onboarding_link_urls.yml b/config/feature_flags/experiment/change_continuous_onboarding_link_urls.yml
deleted file mode 100644
index e65d7cd8d94..00000000000
--- a/config/feature_flags/experiment/change_continuous_onboarding_link_urls.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: change_continuous_onboarding_link_urls
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71408
-rollout_issue_url:
-milestone: '14.5'
-type: experiment
-group: group::conversion
-default_enabled: false
diff --git a/config/routes.rb b/config/routes.rb
index 910ddb2e571..4811e59d3e6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -269,7 +269,7 @@ Rails.application.routes.draw do
resources :projects, only: [:index, :new, :create]
- get '/projects/:id' => 'projects#resolve'
+ get '/projects/:id' => 'projects/redirect#redirect_from_id'
draw :git_http
draw :api
diff --git a/doc/.vale/gitlab/HeadingContent.yml b/doc/.vale/gitlab/HeadingContent.yml
new file mode 100644
index 00000000000..a8dc596f2a2
--- /dev/null
+++ b/doc/.vale/gitlab/HeadingContent.yml
@@ -0,0 +1,18 @@
+---
+# Error: gitlab.HeadingContent
+#
+# Checks for generic, unhelpful subheadings.
+#
+# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
+extends: existence
+message: 'Rename the subheading "%s", or re-purpose the content elsewhere.'
+level: warning
+scope: heading
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/#headings-1
+ignorecase: false
+tokens:
+ - How it works
+ - Limitations
+ - Overview
+ - Use cases?
+ - Important notes?
diff --git a/doc/administration/geo/index.md b/doc/administration/geo/index.md
index c0a5b2e0ff0..1b80e91c9c4 100644
--- a/doc/administration/geo/index.md
+++ b/doc/administration/geo/index.md
@@ -200,7 +200,7 @@ This list of limitations only reflects the latest version of GitLab. If you are
- Pushing directly to a **secondary** site redirects (for HTTP) or proxies (for SSH) the request to the **primary** site instead of [handling it directly](https://gitlab.com/gitlab-org/gitlab/-/issues/1381), except when using Git over HTTP with credentials embedded in the URI. For example, `https://user:password@secondary.tld`.
- The **primary** site has to be online for OAuth login to happen. Existing sessions and Git are not affected. Support for the **secondary** site to use an OAuth provider independent from the primary is [being planned](https://gitlab.com/gitlab-org/gitlab/-/issues/208465).
-- The installation takes multiple manual steps that together can take about an hour depending on circumstances. We are working on improving this experience. See [Omnibus GitLab issue #2978](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/2978) for details.
+- The installation takes multiple manual steps that together can take about an hour depending on circumstances. Consider using [the GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) to deploy and operate production GitLab instances based on our [Reference Architectures](../reference_architectures/index.md), including automation of common daily tasks. We are planning to [improve Geo's installation even further](https://gitlab.com/groups/gitlab-org/-/epics/1465).
- Real-time updates of issues/merge requests (for example, via long polling) doesn't work on the **secondary** site.
- GitLab Runners cannot register with a **secondary** site. Support for this is [planned for the future](https://gitlab.com/gitlab-org/gitlab/-/issues/3294).
- [Selective synchronization](replication/configuration.md#selective-synchronization) only limits what repositories and files are replicated. The entire PostgreSQL data is still replicated. Selective synchronization is not built to accommodate compliance / export control use cases.
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 3c7be07227d..dab2609bfbd 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -11473,6 +11473,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupissuesepicid"></a>`epicId` | [`String`](#string) | ID of an epic associated with the issues, "none" and "any" values are supported. |
| <a id="groupissuesiid"></a>`iid` | [`String`](#string) | IID of the issue. For example, "1". |
| <a id="groupissuesiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of issues. For example, `["1", "2"]`. |
+| <a id="groupissuesincludearchived"></a>`includeArchived` | [`Boolean`](#boolean) | Return issues from archived projects. |
| <a id="groupissuesincludesubepics"></a>`includeSubepics` | [`Boolean`](#boolean) | Whether to include subepics when filtering issues by epicId. |
| <a id="groupissuesincludesubgroups"></a>`includeSubgroups` | [`Boolean`](#boolean) | Include issues belonging to subgroups. |
| <a id="groupissuesiterationid"></a>`iterationId` | [`[ID]`](#id) | List of iteration Global IDs applied to the issue. |
@@ -11606,6 +11607,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupmergerequestscreatedbefore"></a>`createdBefore` | [`Time`](#time) | Merge requests created before this timestamp. |
| <a id="groupmergerequestsdraft"></a>`draft` | [`Boolean`](#boolean) | Limit result to draft merge requests. |
| <a id="groupmergerequestsiids"></a>`iids` | [`[String!]`](#string) | Array of IIDs of merge requests, for example `[1, 2]`. |
+| <a id="groupmergerequestsincludearchived"></a>`includeArchived` | [`Boolean`](#boolean) | Return merge requests from archived projects. |
| <a id="groupmergerequestsincludesubgroups"></a>`includeSubgroups` | [`Boolean`](#boolean) | Include merge requests belonging to subgroups. |
| <a id="groupmergerequestslabels"></a>`labels` | [`[String!]`](#string) | Array of label names. All resolved merge requests will have all of these labels. |
| <a id="groupmergerequestsmergedafter"></a>`mergedAfter` | [`Time`](#time) | Merge requests merged after this date. |
diff --git a/doc/api/group_labels.md b/doc/api/group_labels.md
index 96b8a162e34..e0f1b451231 100644
--- a/doc/api/group_labels.md
+++ b/doc/api/group_labels.md
@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/21368) in GitLab 11.8.
-This API supports managing [group labels](../user/project/labels.md#project-labels-and-group-labels).
+This API supports managing [group labels](../user/project/labels.md#types-of-labels).
It allows users to list, create, update, and delete group labels. Furthermore, users can subscribe to and
unsubscribe from group labels.
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 5801f072062..ef0727e1c13 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -210,6 +210,28 @@ Issues created by users on GitLab Premium or higher include the `epic` property:
}
```
+Issues created by users on GitLab Premium or higher include the `iteration` property:
+
+```json
+{
+ "iteration": {
+ "id":90,
+ "iid":4,
+ "sequence":2,
+ "group_id":162,
+ "title":null,
+ "description":null,
+ "state":2,
+ "created_at":"2022-03-14T05:21:11.929Z",
+ "updated_at":"2022-03-14T05:21:11.929Z",
+ "start_date":"2022-03-08",
+ "due_date":"2022-03-14",
+ "web_url":"http://gitlab.com/groups/my-group/-/iterations/90"
+ }
+ ...
+}
+```
+
Issues created by users on GitLab Ultimate include the `health_status` property:
```json
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 5cf7bb74549..66d6beb821f 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -7,7 +7,7 @@ description: Learn how to contribute to GitLab Documentation.
# GitLab Documentation guidelines
-The GitLab documentation is [intended as the single source of truth (SSOT)](https://about.gitlab.com/handbook/documentation/) for information about how to configure, use, and troubleshoot GitLab. The documentation contains use cases and usage instructions for every GitLab feature, organized by product area and subject. This includes topics and workflows that span multiple GitLab features, and the use of GitLab with other applications.
+The GitLab documentation is [intended as the single source of truth (SSOT)](https://about.gitlab.com/handbook/documentation/) for information about how to configure, use, and troubleshoot GitLab. The documentation contains use cases and usage instructions for every GitLab feature, organized by product area and subject. This includes topics and workflows that span multiple GitLab features and the use of GitLab with other applications.
In addition to this page, the following resources can help you craft and contribute to documentation:
@@ -55,9 +55,9 @@ docs-only merge requests using the following guide:
[Contributions to GitLab docs](workflow.md) are welcome from the entire GitLab community.
-To ensure that GitLab docs are current, there are special processes and responsibilities for all [feature changes](workflow.md), that is development work that impacts the appearance, usage, or administration of a feature.
+To ensure that the GitLab docs are current, there are special processes and responsibilities for all [feature changes](workflow.md), that is development work that impacts the appearance, usage, or administration of a feature.
-However, anyone can contribute [documentation improvements](workflow.md) that are not associated with a feature change. For example, adding a new doc on how to accomplish a use case that's already possible with GitLab or with third-party tools and GitLab.
+However, anyone can contribute [documentation improvements](workflow.md) that are not associated with a feature change. For example, adding a new document on how to accomplish a use case that's already possible with GitLab or with third-party tools and GitLab.
## Markdown and styles
@@ -87,8 +87,8 @@ belongs to, as well as an information block as described below:
- `group`: The [Group](https://about.gitlab.com/company/team/structure/#product-groups)
to which the majority of the page's content belongs.
- `info`: The following line, which provides direction to contributors regarding
- how to contact the Technical Writer associated with the page's Stage and
- Group:
+ how to contact the Technical Writer associated with the page's stage and
+ group:
```plaintext
To determine the technical writer assigned to the Stage/Group
@@ -116,7 +116,7 @@ The following metadata should be added when a page is moved to another location:
location to which visitors should be redirected for a moved page.
[Learn more](redirects.md).
- `disqus_identifier`: Identifier for Disqus commenting system. Used to keep
- comments with a page that's been moved to a new URL.
+ comments with a page that has been moved to a new URL.
[Learn more](redirects.md#redirections-for-pages-with-disqus-comments).
### Comments metadata
@@ -192,8 +192,8 @@ For example:
1. The change shows up in the 14.5 self-managed release, due to missing the release cutoff
for 14.4.
-The exact cutoff date for each release is flexible, and can be earlier or later
-than expected due to holidays, weekends, or other events. In general, MRs merged
+The exact cutoff date for each release is flexible, and can be sooner or later
+than expected due to holidays, weekends or other events. In general, MRs merged
by the 17th should be present in the release on the 22nd, though it is not guaranteed.
If it is important that a documentation update is present in that month's release,
merge it as early as possible.
@@ -209,7 +209,7 @@ with the following conventions:
- It's relative to the `doc/` directory in the GitLab repository.
- It omits the `.md` extension.
-- It doesn't end with a slash (`/`).
+- It doesn't end with a forward slash (`/`).
The help text follows the [Pajamas guidelines](https://design.gitlab.com/usability/helping-users/#formatting-help-content).
@@ -316,7 +316,7 @@ process. This is configured in the `Dangerfile` in the GitLab repository under
## Automatic screenshot generator
-You can now set up an automatic screenshot generator to take and compress screenshots, with the
+You can now set up an automatic screenshot generator to take and compress screenshots with the
help of a configuration file known as **screenshot generator**.
### Use the tool
diff --git a/doc/development/service_ping/index.md b/doc/development/service_ping/index.md
index ee3782dd5a9..e06ee648d34 100644
--- a/doc/development/service_ping/index.md
+++ b/doc/development/service_ping/index.md
@@ -68,7 +68,10 @@ Because of these limitations we recommend you:
> Introduced in GitLab 14.1.
-In GitLab versions 14.1 and later, free self-managed users running [GitLab EE](../ee_features.md) can receive paid features by registering with GitLab and sending us activity data through Service Ping. Features introduced here do not remove the feature from its paid tier. Users can continue to access the features in a paid tier without sharing usage data.
+In GitLab versions 14.1 and later, GitLab Free customers with a self-managed instance running
+[GitLab EE](../ee_features.md) can receive paid features by registering with GitLab and sending us
+activity data through Service Ping. Features introduced here do not remove the feature from its paid
+tier. Users can continue to access the features in a paid tier without sharing usage data.
#### Features available in 14.1 and later
diff --git a/doc/user/application_security/cluster_image_scanning/index.md b/doc/user/application_security/cluster_image_scanning/index.md
index 8bf0efd5261..293645b8de6 100644
--- a/doc/user/application_security/cluster_image_scanning/index.md
+++ b/doc/user/application_security/cluster_image_scanning/index.md
@@ -46,7 +46,7 @@ To enable cluster image scanning in your pipeline, you need the following:
- [GitLab Runner](https://docs.gitlab.com/runner/)
with the [`docker`](https://docs.gitlab.com/runner/executors/docker.html)
or [`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html)
- executor.
+ executor on Linux/amd64.
- Docker `18.09.03` or later installed on the same computer as the runner. If you're using the
shared runners on GitLab.com, then this is already the case.
- [Starboard Operator](https://aquasecurity.github.io/starboard/v0.10.3/operator/installation/kubectl/)
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index 7dcbaf9e649..f2d6cef669d 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -50,7 +50,7 @@ To enable container scanning in your pipeline, you need the following:
- Container Scanning runs in the `test` stage, which is available by default. If you redefine the stages in the `.gitlab-ci.yml` file, the `test` stage is required.
- [GitLab Runner](https://docs.gitlab.com/runner/) with the [`docker`](https://docs.gitlab.com/runner/executors/docker.html)
- or [`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html) executor.
+ or [`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html) executor on Linux/amd64.
- Docker `18.09.03` or higher installed on the same computer as the runner. If you're using the
shared runners on GitLab.com, then this is already the case.
- An image matching the [supported distributions](#supported-distributions).
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 86c33933144..4b9ff7f64e8 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -172,6 +172,7 @@ Filter a group to find members. By default, all members in the group and subgrou
- To view members in the group only, select **Membership = Direct**.
- To view members of the group and its subgroups, select **Membership = Inherited**.
- To view members with two-factor authentication enabled or disabled, select **2FA = Enabled** or **Disabled**.
+ - [In GitLab 14.0 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/349887), to view GitLab users created by [SAML SSO](saml_sso/index.md) or [SCIM provisioning](saml_sso/scim_setup.md) select **Enterprise = true**.
### Search a group
diff --git a/doc/user/project/img/labels_sort_label_priority.png b/doc/user/project/img/labels_sort_label_priority.png
deleted file mode 100644
index faf629ac61d..00000000000
--- a/doc/user/project/img/labels_sort_label_priority.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_sort_priority.png b/doc/user/project/img/labels_sort_priority.png
deleted file mode 100644
index a6b5fca26f4..00000000000
--- a/doc/user/project/img/labels_sort_priority.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_subscriptions_v13_5.png b/doc/user/project/img/labels_subscriptions_v13_5.png
deleted file mode 100644
index a2a4e9e50cc..00000000000
--- a/doc/user/project/img/labels_subscriptions_v13_5.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 428cb4a7f17..d731ceb5aca 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -536,7 +536,7 @@ changing a label.
A typical workflow of using an issue board would be:
-1. You have [created](labels.md#label-management) and [prioritized](labels.md#label-priority)
+1. You [create](labels.md#create-a-label) and [prioritize](labels.md#set-label-priority)
labels to categorize your issues.
1. You have a bunch of issues (ideally labeled).
1. You visit the issue board and start [creating lists](#create-a-new-list) to
diff --git a/doc/user/project/issues/sorting_issue_lists.md b/doc/user/project/issues/sorting_issue_lists.md
index 329f65bfacd..9311ef590df 100644
--- a/doc/user/project/issues/sorting_issue_lists.md
+++ b/doc/user/project/issues/sorting_issue_lists.md
@@ -49,7 +49,7 @@ Ties are broken arbitrarily. Only the highest prioritized label is checked,
and labels with a lower priority are ignored.
For more information, see [issue 14523](https://gitlab.com/gitlab-org/gitlab/-/issues/14523).
-To learn more about priority labels, read the [Labels](../labels.md#label-priority) documentation.
+To learn how to change label priority, see [Label priority](../labels.md#set-label-priority).
## Sorting by last updated
@@ -98,7 +98,9 @@ When you sort by **Priority**, the issue order changes to sort in this order:
1. Issues with a higher priority label.
1. Issues without a prioritized label.
-To learn more about priority, read the [Labels](../labels.md#label-priority) documentation.
+Ties are broken arbitrarily.
+
+To learn how to change label priority, see [Label priority](../labels.md#set-label-priority).
## Sorting by title
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index 45ed5960626..18197cd860f 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -6,10 +6,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Labels **(FREE)**
-As your count of issues, merge requests, and epics grows in GitLab, it's more and more challenging
+As your count of issues, merge requests, and epics grows in GitLab, it gets more challenging
to keep track of those items. Especially as your organization grows from just a few people to
-hundreds or thousands. This is where labels come in. They help you organize and tag your work
-so you can track and find the work items you're interested in.
+hundreds or thousands. With labels, you can organize and tag your work, and track the work items
+you're interested in.
Labels are a key part of [issue boards](issue_board.md). With labels you can:
@@ -19,132 +19,257 @@ Labels are a key part of [issue boards](issue_board.md). With labels you can:
- [Search lists of issues, merge requests, and epics](../search/index.md#search-issues-and-merge-requests),
as well as [issue boards](../search/index.md#issue-boards).
-## Project labels and group labels
+## Types of labels
-There are two types of labels in GitLab:
+You can use two types of labels in GitLab:
- **Project labels** can be assigned to issues and merge requests in that project only.
-- **Group labels** can be assigned to issues and merge requests in any project in
- the selected group or its subgroups.
- - They can also be assigned to [epics](../group/epics/index.md) in the selected group or its subgroups.
+- **Group labels** can be assigned to issues, merge requests, and [epics](../group/epics/index.md)
+ in any project in the selected group or its subgroups.
## Assign and unassign labels
> Unassigning labels with the **X** button [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216881) in GitLab 13.5.
-Every issue, merge request, and epic can be assigned any number of labels. The labels are
-managed in the right sidebar, where you can assign or unassign labels as needed.
+You can assign labels to any issue, merge request, or epic.
To assign or unassign a label:
-1. In the **Labels** section of the sidebar, click **Edit**.
+1. In the **Labels** section of the sidebar, select **Edit**.
1. In the **Assign labels** list, search for labels by typing their names.
You can search repeatedly to add more labels.
The selected labels are marked with a checkmark.
-1. Click the labels you want to assign or unassign.
+1. Select the labels you want to assign or unassign.
1. To apply your changes to labels, select **X** next to **Assign labels** or select any area
outside the label section.
-Alternatively, to unassign a label, click the **X** on the label you want to unassign.
+Alternatively, to unassign a label, select the **X** on the label you want to unassign.
-You can also assign a label with the `/label` [quick action](quick_actions.md),
-remove labels with `/unlabel`, and reassign labels (remove all and assign new ones) with `/relabel`.
+You can also assign and unassign labels with [quick actions](quick_actions.md):
-## Label management
+- Assign labels with `/label`.
+- Remove labels with `/unlabel`.
+- Remove all labels and assign new ones with `/relabel`.
-Users with a [permission level](../permissions.md) of Reporter or higher are able to create
-and edit labels.
+## View available labels
-### Project labels
+### View project labels
-> Showing all inherited labels [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241990) in GitLab 13.5.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241990) in GitLab 13.5: the label list in a project also shows all inherited labels.
-To view a project's available labels, in the project, go to **Project information > Labels**.
-Its list of labels includes both the labels defined at the project level, and
-all labels defined by its ancestor groups. For each label, you can see the
-project or group path from where it was created. You can filter the list by
-entering a search query in the **Filter** field, and then clicking its search
-icon (**{search}**).
+To view the **project's labels**:
-To create a new project label:
+1. On the top bar, select **Menu > Projects** and find your project.
+1. On the left sidebar, select **Project information > Labels**.
-1. In your project, go to **Project information > Labels**.
-1. Select the **New label** button.
+Or:
+
+1. View an issue or merge request.
+1. On the right sidebar, in the **Labels** section, select **Edit**.
+1. Select **Manage project labels**.
+
+The list of labels includes both the labels created in the project and
+all labels created in the project's ancestor groups. For each label, you can see the
+project or group path where it was created.
+
+### View group labels
+
+To view the **group's labels**:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+1. On the left sidebar, select **Group information > Labels**.
+
+Or:
+
+1. View an epic.
+1. On the right sidebar, in the **Labels** section, select **Edit**.
+1. Select **Manage group labels**.
+
+The list includes all labels created only in the group. It does not list any labels created in
+the group's projects.
+
+## Create a label
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project or group.
+
+### Create a project label
+
+To create a project label:
+
+1. On the top bar, select **Menu > Projects** and find your project.
+1. On the left sidebar, select **Project information > Labels**.
+1. Select **New label**.
+1. In the **Title** field, enter a short, descriptive name for the label. You
+ can also use this field to create [scoped, mutually exclusive labels](#scoped-labels).
+1. Optional. In the **Description** field, enter additional
+ information about how and when to use this label.
+1. Optional. Select a color by selecting from the available colors, or enter a hex color value for
+ a specific color in the **Background color** field.
+1. Select **Create label**.
+
+### Create a project label from an issue or merge request
+
+You can also create a new project label from an issue or merge request.
+Labels you create this way belong to the same project as the issue or merge request.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To do so:
+
+1. View an issue or merge request.
+1. On the right sidebar, in the **Labels** section, select **Edit**.
+1. Select **Create project label**.
+1. Fill in the name field. You can't specify a description if creating a label this way.
+ You can add a description later by [editing the label](#edit-a-label).
+1. Optional. Select a color by selecting from the available colors, or enter a hex color value for
+ a specific color.
+1. Select **Create**.
+
+### Create a group label
+
+To create a group label:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+1. On the left sidebar, select **Group information > Labels**.
+1. Select **New label**.
1. In the **Title** field, enter a short, descriptive name for the label. You
can also use this field to create [scoped, mutually exclusive labels](#scoped-labels).
-1. Optional. In the **Description** field, you can enter additional
+1. Optional. In the **Description** field, enter additional
information about how and when to use this label.
-1. Optional. Select a background color for the label by selecting one of the
- available colors, or by entering a hex color value in the **Background color**
- field.
+1. Optional. Select a color by selecting from the available colors, or enter a hex color value for
+ a specific color in the **Background color** field.
1. Select **Create label**.
-You can also create a new project label from within an issue or merge request. In the
-label section of the right sidebar of an issue or a merge request:
+### Create a group label from an epic **(PREMIUM)**
+
+You can also create a new group label from an epic.
+Labels you create this way belong to the same group as the epic.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the group.
-1. Click **Edit**.
-1. Click **Create project label**.
- - Fill in the name field. Note that you can't specify a description if creating a label
- this way. You can add a description later by editing the label (see below).
- - Optional. Select a color by clicking on the available colors, or input a hex
- color value for a specific color.
-1. Click **Create**.
+To do so:
-To edit a label after you create it, select (**{pencil}**).
+1. View an epic.
+1. On the right sidebar, in the **Labels** section, select **Edit**.
+1. Select **Create group label**.
+1. Fill in the name field. You can't specify a description if creating a label this way.
+ You can add a description later by [editing the label](#edit-a-label).
+1. Optional. Select a color by selecting from the available colors,enter input a hex color value
+ for a specific color.
+1. Select **Create**.
-To delete a project label, select (**{ellipsis_v}**) next to the **Subscribe** button
-and select **Delete** or select **Delete** when you edit a label.
+## Edit a label
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project or group.
+
+### Edit a project label
+
+To edit a **project** label:
+
+1. On the top bar, select **Menu > Projects** and find your project.
+1. On the left sidebar, select **Project information > Labels**.
+1. Next to the label you want to edit, select **Edit** (**{pencil}**).
+
+### Edit a group label
+
+To edit a **group** label:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+1. On the left sidebar, select **Group information > Labels**.
+1. Next to the label you want to edit, select **Edit** (**{pencil}**).
+
+## Delete a label
WARNING:
-If you delete a label, it is permanently deleted. All references to the label are removed from the system and you cannot undo the deletion.
+If you delete a label, it is permanently deleted. All references to the label are removed from the
+system and you cannot undo the deletion.
+
+Prerequisites:
-#### Promote a project label to a group label
+- You must have at least the Reporter role for the project.
+
+### Delete a project label
+
+To delete a **project** label:
+
+1. On the top bar, select **Menu > Projects** and find your project.
+1. On the left sidebar, select **Project information > Labels**.
+1. Either:
+
+ - Next to the **Subscribe** button, select (**{ellipsis_v}**).
+ - Next to the label you want to edit, select **Edit** (**{pencil}**).
+
+1. Select **Delete**.
+
+### Delete a group label
+
+To delete a **group** label:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+1. On the left sidebar, select **Group information > Labels**.
+1. Either:
+
+ - Next to the **Subscribe** button, select (**{ellipsis_v}**).
+ - Next to the label you want to edit, select **Edit** (**{pencil}**).
+
+1. Select **Delete**.
+
+## Promote a project label to a group label
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/231472) in GitLab 13.6: promoting a project label keeps that label's ID and changes it into a group label. Previously, promoting a project label created a new group label with a new ID and deleted the old label.
-If you previously created a project label and now want to make it available for other
-projects within the same group, you can promote it to a group label.
+You might want to make a project label available for other
+projects in the same group. Then, you can promote the label to a group label.
If other projects in the same group have a label with the same title, they are all
merged with the new group label. If a group label with the same title exists, it is
also merged.
-All issues, merge requests, issue board lists, issue board filters, and label subscriptions
-with the old labels are assigned to the new group label.
+WARNING:
+Promoting a label is a permanent action and cannot be reversed.
-The new group label has the same ID as the previous project label.
+Prerequisites:
-WARNING:
-Promoting a label is a permanent action, and cannot be reversed.
+- You must have at least the Reporter role for the project.
+- You must have at least the Reporter role for the project's parent group.
To promote a project label to a group label:
-1. Navigate to **Project information > Labels** in the project.
-1. Click on the three dots (**{ellipsis_v}**) next to the **Subscribe** button and
+1. On the top bar, select **Menu > Projects** and find your project.
+1. On the left sidebar, select **Project information > Labels**.
+1. Next to the **Subscribe** button, select the three dots (**{ellipsis_v}**) and
select **Promote to group label**.
-### Group labels
+All issues, merge requests, issue board lists, issue board filters, and label subscriptions
+with the old labels are assigned to the new group label.
+
+The new group label has the same ID as the previous project label.
-To view the group labels list, navigate to the group and click **Group information > Labels**.
-The list includes all labels that are defined at the group level only. It does not
-list any labels that are defined in projects. You can filter the list by entering
-a search query at the top and clicking search (**{search}**).
+## Generate default project labels
-To create a **group label**, navigate to **Group information > Labels** in the group and
-follow the same process as [creating a project label](#project-labels).
+If a project or its parent group has no labels, you can generate a default set of project
+labels from the label list page.
-#### Create group labels from epics **(ULTIMATE)**
+Prerequisites:
-You can create group labels from the epic sidebar. The labels you create
-belong to the immediate group to which the epic belongs. The process is the same as
-creating a [project label from an issue or merge request](#project-labels).
+- You must have at least the Reporter role for the project.
+- The project must have no labels present.
-### Generate default labels
+To add the default labels to the project:
-If a project or group has no labels, you can generate a default set of project or group
-labels from the label list page. The page shows a **Generate a default set of labels**
-button if the list is empty. Select the button to add the following default labels
-to the project:
+1. On the top bar, select **Menu > Projects** and find your project.
+1. On the left sidebar, select **Project information > Labels**.
+1. Select **Generate a default set of labels**.
+
+The following labels are created:
- `bug`
- `confirmed`
@@ -157,131 +282,143 @@ to the project:
## Scoped labels **(PREMIUM)**
-Scoped labels allow teams to use the label feature to annotate issues, merge requests
-and epics with mutually exclusive labels. This can enable more complicated workflows
-by preventing certain labels from being used together.
-
-A label is scoped when it uses a special double-colon (`::`) syntax in the label's
-title, for example:
+Teams can use scoped labels to annotate issues, merge requests, and epics with mutually exclusive
+labels. By preventing certain labels from being used together, you can create more complex workflows.
![Scoped labels](img/labels_key_value_v13_5.png)
-An issue, merge request or epic cannot have two scoped labels, of the form `key::value`,
-with the same `key`. Adding a new label with the same `key`, but a different `value`
-causes the previous `key` label to be replaced with the new label.
+A scoped label uses a double-colon (`::`) syntax in its title, for example: `workflow::in-review`.
-For example:
+An issue, merge request, or epic cannot have two scoped labels, of the form `key::value`,
+with the same `key`. If you add a new label with the same `key` but a different `value`,
+the previous `key` label is replaced with the new label.
-1. An issue is identified as being low priority, and a `priority::low` project
- label is added to it.
-1. After more review the issue priority is increased, and a `priority::high` label is
- added.
-1. GitLab automatically removes the `priority::low` label, as an issue should not
- have two priority labels at the same time.
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For a video overview, see [Scoped Labels Speed Run](https://www.youtube.com/watch?v=ebyCiKMFODg).
### Filter by scoped labels
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12285) in GitLab 14.4.
-To filter issue, merge request, or epic lists for ones with labels that belong to a given scope, enter
+To filter issue, merge request, or epic lists by a given scope, enter
`<scope>::*` in the searched label name.
For example, filtering by the `platform::*` label returns issues that have `platform::iOS`,
`platform::Android`, or `platform::Linux` labels.
NOTE:
-This is not available on the [issues or merge requests dashboard pages](../search/index.md#search-issues-and-merge-requests).
+Filtering by scoped labels not available on the [issues or merge requests dashboard pages](../search/index.md#search-issues-and-merge-requests).
+
+### Scoped labels examples
+
+**Example 1.** Updating issue priority:
+
+1. You decide that an issue is of low priority, and assign it the `priority::low` label.
+1. After more review, you realize the issue's priority is higher increased, and you assign it the
+ `priority::high` label.
+1. Because an issue shouldn't have two priority labels at the same time, GitLab removes the
+ `priority::low` label.
+
+**Example 2.** You want a custom field in issues to track the operating system platform
+that your features target, where each issue should only target one platform.
+
+You create three labels:
+
+- `platform::iOS`
+- `platform::Android`
+- `platform::Linux`
+
+If you assign any of these labels to an issue automatically removes any other existing label that
+starts with `platform::`.
+
+**Example 3.** You can use scoped labels to represent the workflow states of your teams.
-### Workflows with scoped labels
+Suppose you have the following labels:
-Suppose you wanted a custom field in issues to track the operating system platform
-that your features target, where each issue should only target one platform. You
-would then create three labels `platform::iOS`, `platform::Android`, `platform::Linux`.
-Applying any one of these labels on a given issue would automatically remove any other
-existing label that starts with `platform::`.
+- `workflow::development`
+- `workflow::review`
+- `workflow::deployed`
-The same pattern could be applied to represent the workflow states of your teams.
-Suppose you have the labels `workflow::development`, `workflow::review`, and
-`workflow::deployed`. If an issue already has the label `workflow::development`
-applied, and a developer wanted to advance the issue to `workflow::review`, they
-would simply apply that label, and the `workflow::development` label would
-automatically be removed. This behavior already exists when you move issues
-across label lists in an [issue board](issue_board.md#create-workflows), but
-now, team members who may not be working in an issue board directly would still
-be able to advance workflow states consistently in issues themselves.
+If an issue already has the label `workflow::development` and a developer wants to show that the
+issue is now under review, they assign the `workflow::review`, and the `workflow::development` label
+is removed.
-This functionality is demonstrated in a video regarding
-[using scoped labels for custom fields and workflows](https://www.youtube.com/watch?v=4BCBby6du3c).
+The same happens when you move issues across label lists in an
+[issue board](issue_board.md#create-workflows). With scoped labels, team members not working in an
+issue board can also advance workflow states consistently in issues themselves.
-### Scoped labels with nested scopes
+For a video explanation, see:
+
+<div class="video-fallback">
+ See the video: <a href="https://www.youtube.com/watch?v=4BCBby6du3c">Use scoped labels for custom fields and custom workflows</a>.
+</div>
+<figure class="video-container">
+ <iframe src="https://www.youtube.com/embed/4BCBby6du3c" frameborder="0" allowfullscreen="true"> </iframe>
+</figure>
+
+### Nested scopes
You can create a label with a nested scope by using multiple double colons `::` when creating
it. In this case, everything before the last `::` is the scope.
-For example, `workflow::backend::review` and `workflow::backend::development` are valid
-scoped labels, but they **can't** exist on the same issue at the same time, as they
-both share the same scope, `workflow::backend`.
+For example, if your project has these labels:
-Additionally, `workflow::backend::review` and `workflow::frontend::review` are valid
-scoped labels, and they **can** exist on the same issue at the same time, as they
-both have different scopes, `workflow::frontend` and `workflow::backend`.
+- `workflow::backend::review`
+- `workflow::backend::development`
+- `workflow::frontend::review`
-## Subscribing to labels
+An issue **can't** have both `workflow::backend::review` and `workflow::backend::development`
+labels at the same time, because they both share the same scope: `workflow::backend`.
-From the project label list page and the group label list page, you can click **Subscribe**
-to the right of any label to enable [notifications](../profile/notifications.md) for that
-label. You are notified whenever the label is assigned to an epic,
-issue, or merge request.
+On the other hand, an issue **can** have both `workflow::backend::review` and `workflow::frontend::review`
+labels at the same time, because they both have different scopes: `workflow::frontend` and `workflow::backend`.
-If you are subscribing to a group label from within a project, you can select to subscribe
-to label notifications for the project only, or the whole group.
+## Receive notifications when a label is used
-![Labels subscriptions](img/labels_subscriptions_v13_5.png)
+You can subscribe to a label to [receive notifications](../profile/notifications.md) whenever the
+label is assigned to an issue, merge request, or epic.
-## Label priority
+To subscribe to a label:
-Labels can have relative priorities, which are used in the **Label priority** and
-**Priority** sort orders of issues and merge request list pages. Prioritization
-for both group and project labels happens at the project level, and cannot be done
-from the group label list.
+1. [View the label list page.](#view-available-labels)
+1. To the right of any label, select **Subscribe**.
+1. Optional. If you are subscribing to a group label from a project, select either:
+ - **Subscribe at project level** to be notified about events in this project.
+ - **Subscribe at group level**: to be notified about events in the whole group.
-NOTE:
-Priority sorting is based on the highest priority label only. [This discussion](https://gitlab.com/gitlab-org/gitlab/-/issues/14523) considers changing this.
+## Set label priority
-From the project label list page, star a label to indicate that it has a priority.
-
-![Labels prioritized](img/labels_prioritized_v13_5.png)
+Labels can have relative priorities, which are used when you sort issue and merge request lists
+by [label priority](issues/sorting_issue_lists.md#sorting-by-label-priority) and [priority](issues/sorting_issue_lists.md#sorting-by-priority).
-Drag starred labels up and down the list to change their priority, where higher in the list
-means higher priority.
+When prioritizing labels, you must do it from a project.
+It's not possible to do it from the group label list.
-![Drag to change label priority](img/labels_drag_priority_v12_1.gif)
+NOTE:
+Priority sorting is based on the highest priority label only.
+[This discussion](https://gitlab.com/gitlab-org/gitlab/-/issues/14523) considers changing this.
-On the merge request and issue list pages (for both groups and projects) you
-can sort by `Label priority` or `Priority`.
+Prerequisites:
-If you sort by `Label priority`, GitLab uses this sort comparison order:
+- You must have at least the Reporter role for the project.
-1. Items with a higher priority label.
-1. Items without a prioritized label.
+To prioritize a label:
-Ties are broken arbitrarily. Note that only the highest prioritized label is checked,
-and labels with a lower priority are ignored. See this [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/14523)
-for more information.
+1. On the top bar, select **Menu > Projects** and find your project.
+1. On the left sidebar, select **Project information > Labels**.
+1. Next to a label you want to prioritize, select the star (**{star-o}**).
-![Labels sort label priority](img/labels_sort_label_priority.png)
+![Labels prioritized](img/labels_prioritized_v13_5.png)
-If you sort by `Priority`, GitLab uses this sort comparison order:
+This label now appears at the top of the label list, under **Prioritized Labels**.
-1. Items with milestones that have due dates, where the soonest assigned [milestone](milestones/index.md)
- is listed first.
-1. Items with milestones with no due dates.
-1. Items with a higher priority label.
-1. Items without a prioritized label.
+To change the relative priority of these labels, drag them up and down the list.
+The labels higher in the list get higher priority.
-Ties are broken arbitrarily.
+![Drag to change label priority](img/labels_drag_priority_v12_1.gif)
-![Labels sort priority](img/labels_sort_priority.png)
+To learn what happens when you sort by priority or label priority, see
+[Sorting and ordering issue lists](issues/sorting_issue_lists.md).
## Troubleshooting
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index c2e572a52e8..44327af380e 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -194,7 +194,7 @@ Some filters can be added multiple times. These include but are not limited to a
You can search your [To-Do List](../todos.md) by "to do" and "done".
You can filter to-do items per project, author, type, and action.
-Also, you can sort them by [**Label priority**](../../user/project/labels.md#label-priority),
+Also, you can sort them by [**Label priority**](../../user/project/labels.md#set-label-priority),
**Last created**, and **Oldest created**.
## Projects
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index cefa1c34ac3..a19fcd6fede 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -76,9 +76,9 @@ module Backup
run_create_task(task_name)
end
- write_info
+ write_backup_information
- if ENV['SKIP'] && ENV['SKIP'].include?('tar')
+ if skipped?('tar')
upload
else
pack
@@ -96,6 +96,7 @@ module Backup
def run_create_task(task_name)
definition = @definitions[task_name]
+ build_backup_information
puts_time "Dumping #{definition.task.human_name} ... ".color(:blue)
unless definition.task.enabled
@@ -103,7 +104,7 @@ module Backup
return
end
- if ENV["SKIP"] && ENV["SKIP"].include?(task_name)
+ if skipped?(task_name)
puts_time "[SKIPPED]".color(:cyan)
return
end
@@ -118,10 +119,11 @@ module Backup
def restore
cleanup_required = unpack
+ read_backup_information
verify_backup_version
@definitions.keys.each do |task_name|
- run_restore_task(task_name) unless skipped?(task_name)
+ run_restore_task(task_name) if !skipped?(task_name) && enabled_task?(task_name)
end
Rake::Task['gitlab:shell:setup'].invoke
@@ -141,6 +143,7 @@ module Backup
def run_restore_task(task_name)
definition = @definitions[task_name]
+ read_backup_information
puts_time "Restoring #{definition.task.human_name} ... ".color(:blue)
unless definition.task.enabled
@@ -171,7 +174,11 @@ module Backup
private
- def write_info
+ def read_backup_information
+ @backup_information ||= YAML.load_file(File.join(backup_path, MANIFEST_NAME))
+ end
+
+ def write_backup_information
# Make sure there is a connection
ActiveRecord::Base.connection.reconnect!
@@ -182,6 +189,23 @@ module Backup
end
end
+ def build_backup_information
+ @backup_information ||= {
+ db_version: ActiveRecord::Migrator.current_version.to_s,
+ backup_created_at: Time.now,
+ gitlab_version: Gitlab::VERSION,
+ tar_version: tar_version,
+ installation_type: Gitlab::INSTALLATION_TYPE,
+ skipped: ENV["SKIP"]
+ }
+ end
+
+ def backup_information
+ raise Backup::Error, "#{MANIFEST_NAME} not yet loaded" unless @backup_information
+
+ @backup_information
+ end
+
def pack
Dir.chdir(backup_path) do
# create archive
@@ -287,15 +311,15 @@ module Backup
def verify_backup_version
Dir.chdir(backup_path) do
# restoring mismatching backups can lead to unexpected problems
- if settings[:gitlab_version] != Gitlab::VERSION
+ if backup_information[:gitlab_version] != Gitlab::VERSION
progress.puts(<<~HEREDOC.color(:red))
GitLab version mismatch:
Your current GitLab version (#{Gitlab::VERSION}) differs from the GitLab version in the backup!
Please switch to the following version and try again:
- version: #{settings[:gitlab_version]}
+ version: #{backup_information[:gitlab_version]}
HEREDOC
progress.puts
- progress.puts "Hint: git checkout v#{settings[:gitlab_version]}"
+ progress.puts "Hint: git checkout v#{backup_information[:gitlab_version]}"
exit 1
end
end
@@ -351,7 +375,7 @@ module Backup
end
def skipped?(item)
- settings[:skipped] && settings[:skipped].include?(item) || !enabled_task?(item)
+ backup_information[:skipped] && backup_information[:skipped].include?(item)
end
def enabled_task?(task_name)
@@ -411,15 +435,11 @@ module Backup
def backup_contents
[MANIFEST_NAME] + @definitions.reject do |name, definition|
- skipped?(name) ||
+ skipped?(name) || !enabled_task?(name) ||
(definition.destination_optional && !File.exist?(File.join(backup_path, definition.destination_path)))
end.values.map(&:destination_path)
end
- def settings
- @settings ||= YAML.load_file(MANIFEST_NAME)
- end
-
def tar_file
@tar_file ||= if ENV['BACKUP'].present?
File.basename(ENV['BACKUP']) + FILE_NAME_SUFFIX
@@ -428,17 +448,6 @@ module Backup
end
end
- def backup_information
- @backup_information ||= {
- db_version: ActiveRecord::Migrator.current_version.to_s,
- backup_created_at: Time.now,
- gitlab_version: Gitlab::VERSION,
- tar_version: tar_version,
- installation_type: Gitlab::INSTALLATION_TYPE,
- skipped: ENV["SKIP"]
- }
- end
-
def create_attributes
attrs = {
key: remote_target,
diff --git a/lib/gitlab/ci/config/entry/reports.rb b/lib/gitlab/ci/config/entry/reports.rb
index e45dbfa243f..f8fce1abc06 100644
--- a/lib/gitlab/ci/config/entry/reports.rb
+++ b/lib/gitlab/ci/config/entry/reports.rb
@@ -8,6 +8,7 @@ module Gitlab
# Entry that represents a configuration of job artifacts.
#
class Reports < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Configurable
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
@@ -15,10 +16,13 @@ module Gitlab
%i[junit codequality sast secret_detection dependency_scanning container_scanning
dast performance browser_performance load_performance license_scanning metrics lsif
dotenv cobertura terraform accessibility cluster_applications
- requirements coverage_fuzzing api_fuzzing cluster_image_scanning].freeze
+ requirements coverage_fuzzing api_fuzzing cluster_image_scanning
+ coverage_report].freeze
attributes ALLOWED_KEYS
+ entry :coverage_report, Reports::CoverageReport, description: 'Coverage report configuration.'
+
validations do
validates :config, type: Hash
validates :config, allowed_keys: ALLOWED_KEYS
@@ -47,10 +51,18 @@ module Gitlab
validates :cluster_applications, array_of_strings_or_string: true # DEPRECATED: https://gitlab.com/gitlab-org/gitlab/-/issues/333441
validates :requirements, array_of_strings_or_string: true
end
+
+ validates :config, mutually_exclusive_keys: [:coverage_report, :cobertura]
end
def value
- @config.transform_values { |v| Array(v) }
+ @config.transform_values do |value|
+ if value.is_a?(Hash)
+ value
+ else
+ Array(value)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/reports/coverage_report.rb b/lib/gitlab/ci/config/entry/reports/coverage_report.rb
new file mode 100644
index 00000000000..98119c7fd53
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/reports/coverage_report.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ class Reports
+ class CoverageReport < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+ include ::Gitlab::Config::Entry::Attributable
+
+ ALLOWED_KEYS = %i[coverage_format path].freeze
+ SUPPORTED_COVERAGE = %w[cobertura].freeze
+
+ attributes ALLOWED_KEYS
+
+ validations do
+ validates :config, type: Hash
+ validates :config, allowed_keys: ALLOWED_KEYS
+
+ with_options(presence: true) do
+ validates :coverage_format, inclusion: { in: SUPPORTED_COVERAGE, message: "must be one of supported formats: #{SUPPORTED_COVERAGE.join(', ')}." }
+ validates :path, type: String
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/config/entry/validators.rb b/lib/gitlab/config/entry/validators.rb
index 6ebcc476e4b..cc24ae837f3 100644
--- a/lib/gitlab/config/entry/validators.rb
+++ b/lib/gitlab/config/entry/validators.rb
@@ -39,6 +39,17 @@ module Gitlab
end
end
+ class MutuallyExclusiveKeysValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ mutually_exclusive_keys = value.try(:keys).to_a & options[:in]
+
+ if mutually_exclusive_keys.length > 1
+ record.errors.add(attribute, "please use only one the following keys: " +
+ mutually_exclusive_keys.join(', '))
+ end
+ end
+ end
+
class AllowedValuesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless options[:in].include?(value.to_s)
diff --git a/lib/learn_gitlab/onboarding.rb b/lib/learn_gitlab/onboarding.rb
index 38ffa9eb2e6..42415aacbee 100644
--- a/lib/learn_gitlab/onboarding.rb
+++ b/lib/learn_gitlab/onboarding.rb
@@ -5,19 +5,19 @@ module LearnGitlab
include Gitlab::Utils::StrongMemoize
ACTION_ISSUE_IDS = {
- issue_created: 4,
- git_write: 6,
pipeline_created: 7,
- merge_request_created: 9,
- user_added: 8,
trial_started: 2,
required_mr_approvals_enabled: 11,
code_owners_enabled: 10
}.freeze
- ACTION_DOC_URLS = {
- security_scan_enabled: 'https://docs.gitlab.com/ee/user/application_security/security_dashboard/#gitlab-security-dashboard-security-center-and-vulnerability-reports'
- }.freeze
+ ACTION_PATHS = [
+ :issue_created,
+ :git_write,
+ :merge_request_created,
+ :user_added,
+ :security_scan_enabled
+ ].freeze
def initialize(namespace)
@namespace = namespace
@@ -49,7 +49,7 @@ module LearnGitlab
end
def tracked_actions
- ACTION_ISSUE_IDS.keys + ACTION_DOC_URLS.keys
+ ACTION_ISSUE_IDS.keys + ACTION_PATHS
end
attr_reader :namespace
diff --git a/scripts/merge-simplecov b/scripts/merge-simplecov
index b74a416a6e0..32a0cd86f82 100755
--- a/scripts/merge-simplecov
+++ b/scripts/merge-simplecov
@@ -5,26 +5,4 @@ require_relative '../spec/simplecov_env'
SimpleCovEnv.configure_profile
SimpleCovEnv.configure_formatter
-module SimpleCov
- module ResultMerger
- class << self
- def resultset_files
- Dir.glob(File.join(SimpleCov.coverage_path, '*', '.resultset.json'))
- end
-
- def resultset_hashes
- resultset_files.map do |path|
- JSON.parse(File.read(path))
- rescue StandardError
- {}
- end
- end
-
- def resultset
- resultset_hashes.reduce({}, :merge)
- end
- end
- end
-end
-
-SimpleCov::ResultMerger.merged_result.format!
+SimpleCov.collate Dir.glob(File.join(SimpleCov.coverage_path, '*', '.resultset.json'))
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index f21e00b5522..cf8f02ea17e 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -1602,59 +1602,6 @@ RSpec.describe ProjectsController do
end
end
- describe 'GET resolve' do
- shared_examples 'resolvable endpoint' do
- it 'redirects to the project page' do
- get :resolve, params: { id: project.id }
-
- expect(response).to have_gitlab_http_status(:found)
- expect(response).to redirect_to(project_path(project))
- end
- end
-
- context 'with an authenticated user' do
- before do
- sign_in(user)
- end
-
- context 'when user has access to the project' do
- before do
- project.add_developer(user)
- end
-
- it_behaves_like 'resolvable endpoint'
- end
-
- context 'when user has no access to the project' do
- it 'gives 404 for existing project' do
- get :resolve, params: { id: project.id }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- it 'gives 404 for non-existing project' do
- get :resolve, params: { id: '0' }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'non authenticated user' do
- context 'with a public project' do
- let(:project) { public_project }
-
- it_behaves_like 'resolvable endpoint'
- end
-
- it 'gives 404 for private project' do
- get :resolve, params: { id: project.id }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
it 'updates Service Desk attributes' do
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/frontend/environments/new_environments_app_spec.js b/spec/frontend/environments/new_environments_app_spec.js
index d903e1e2d56..cb9535da363 100644
--- a/spec/frontend/environments/new_environments_app_spec.js
+++ b/spec/frontend/environments/new_environments_app_spec.js
@@ -70,8 +70,9 @@ describe('~/environments/components/new_environments_app.vue', () => {
previousPage: 1,
__typename: 'LocalPageInfo',
},
+ location = '?scope=available&page=2',
}) => {
- setWindowLocation('?scope=available&page=2');
+ setWindowLocation(location);
environmentAppMock.mockReturnValue(environmentsApp);
environmentFolderMock.mockReturnValue(folder);
paginationMock.mockReturnValue(pageInfo);
@@ -98,6 +99,21 @@ describe('~/environments/components/new_environments_app.vue', () => {
wrapper.destroy();
});
+ it('should request available environments if the scope is invalid', async () => {
+ await createWrapperWithMocked({
+ environmentsApp: resolvedEnvironmentsApp,
+ folder: resolvedFolder,
+ location: '?scope=bad&page=2',
+ });
+
+ expect(environmentAppMock).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({ scope: 'available', page: 2 }),
+ expect.anything(),
+ expect.anything(),
+ );
+ });
+
it('should show all the folders that are fetched', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
diff --git a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
index ee2fbbe57b9..b692eea4aa5 100644
--- a/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
+++ b/spec/frontend/members/components/filter_sort/members_filtered_search_bar_spec.js
@@ -1,12 +1,14 @@
-import { GlFilteredSearchToken } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import setWindowLocation from 'helpers/set_window_location_helper';
import { redirectTo } from '~/lib/utils/url_utility';
import MembersFilteredSearchBar from '~/members/components/filter_sort/members_filtered_search_bar.vue';
-import { MEMBER_TYPES } from '~/members/constants';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
+import {
+ MEMBER_TYPES,
+ FILTERED_SEARCH_TOKEN_TWO_FACTOR,
+ FILTERED_SEARCH_TOKEN_WITH_INHERITED_PERMISSIONS,
+} from '~/members/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
jest.mock('~/lib/utils/url_utility', () => {
@@ -32,7 +34,7 @@ describe('MembersFilteredSearchBar', () => {
state: {
filteredSearchBar: {
show: true,
- tokens: ['two_factor'],
+ tokens: [FILTERED_SEARCH_TOKEN_TWO_FACTOR.type],
searchParam: 'search',
placeholder: 'Filter members',
recentSearchesStorageKey: 'group_members',
@@ -70,21 +72,7 @@ describe('MembersFilteredSearchBar', () => {
it('includes tokens set in `filteredSearchBar.tokens`', () => {
createComponent();
- expect(findFilteredSearchBar().props('tokens')).toEqual([
- {
- type: 'two_factor',
- icon: 'lock',
- title: '2FA',
- token: GlFilteredSearchToken,
- unique: true,
- operators: OPERATOR_IS_ONLY,
- options: [
- { value: 'enabled', title: 'Enabled' },
- { value: 'disabled', title: 'Disabled' },
- ],
- requiredPermissions: 'canManageMembers',
- },
- ]);
+ expect(findFilteredSearchBar().props('tokens')).toEqual([FILTERED_SEARCH_TOKEN_TWO_FACTOR]);
});
describe('when `canManageMembers` is false', () => {
@@ -93,7 +81,10 @@ describe('MembersFilteredSearchBar', () => {
state: {
filteredSearchBar: {
show: true,
- tokens: ['two_factor', 'with_inherited_permissions'],
+ tokens: [
+ FILTERED_SEARCH_TOKEN_TWO_FACTOR.type,
+ FILTERED_SEARCH_TOKEN_WITH_INHERITED_PERMISSIONS.type,
+ ],
searchParam: 'search',
placeholder: 'Filter members',
recentSearchesStorageKey: 'group_members',
@@ -105,18 +96,7 @@ describe('MembersFilteredSearchBar', () => {
});
expect(findFilteredSearchBar().props('tokens')).toEqual([
- {
- type: 'with_inherited_permissions',
- icon: 'group',
- title: 'Membership',
- token: GlFilteredSearchToken,
- unique: true,
- operators: OPERATOR_IS_ONLY,
- options: [
- { value: 'exclude', title: 'Direct' },
- { value: 'only', title: 'Inherited' },
- ],
- },
+ FILTERED_SEARCH_TOKEN_WITH_INHERITED_PERMISSIONS,
]);
});
});
@@ -134,7 +114,7 @@ describe('MembersFilteredSearchBar', () => {
expect(findFilteredSearchBar().props('initialFilterValue')).toEqual([
{
- type: 'two_factor',
+ type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type,
value: {
data: 'enabled',
operator: '=',
@@ -183,7 +163,7 @@ describe('MembersFilteredSearchBar', () => {
createComponent();
findFilteredSearchBar().vm.$emit('onFilter', [
- { type: 'two_factor', value: { data: 'enabled', operator: '=' } },
+ { type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
]);
expect(redirectTo).toHaveBeenCalledWith('https://localhost/?two_factor=enabled');
@@ -193,7 +173,7 @@ describe('MembersFilteredSearchBar', () => {
createComponent();
findFilteredSearchBar().vm.$emit('onFilter', [
- { type: 'two_factor', value: { data: 'enabled', operator: '=' } },
+ { type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
{ type: 'filtered-search-term', value: { data: 'foobar' } },
]);
@@ -206,7 +186,7 @@ describe('MembersFilteredSearchBar', () => {
createComponent();
findFilteredSearchBar().vm.$emit('onFilter', [
- { type: 'two_factor', value: { data: 'enabled', operator: '=' } },
+ { type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
{ type: 'filtered-search-term', value: { data: 'foo bar baz' } },
]);
@@ -221,7 +201,7 @@ describe('MembersFilteredSearchBar', () => {
createComponent();
findFilteredSearchBar().vm.$emit('onFilter', [
- { type: 'two_factor', value: { data: 'enabled', operator: '=' } },
+ { type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
{ type: 'filtered-search-term', value: { data: 'foobar' } },
]);
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap
index 86ccaa43786..62cf769cffd 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap
+++ b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_spec.js.snap
@@ -137,9 +137,7 @@ exports[`Learn GitLab renders correctly 1`] = `
class="gl-link"
data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link"
- data-track-experiment="change_continuous_onboarding_link_urls"
data-track-label="Set up CI/CD"
- data-track-property="Growth::Conversion::Experiment::LearnGitLab"
href="http://example.com/"
target="_self"
>
@@ -157,9 +155,7 @@ exports[`Learn GitLab renders correctly 1`] = `
class="gl-link"
data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link"
- data-track-experiment="change_continuous_onboarding_link_urls"
data-track-label="Start a free Ultimate trial"
- data-track-property="Growth::Conversion::Experiment::LearnGitLab"
href="http://example.com/"
target="_self"
>
@@ -177,9 +173,7 @@ exports[`Learn GitLab renders correctly 1`] = `
class="gl-link"
data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link"
- data-track-experiment="change_continuous_onboarding_link_urls"
data-track-label="Add code owners"
- data-track-property="Growth::Conversion::Experiment::LearnGitLab"
href="http://example.com/"
target="_self"
>
@@ -204,9 +198,7 @@ exports[`Learn GitLab renders correctly 1`] = `
class="gl-link"
data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link"
- data-track-experiment="change_continuous_onboarding_link_urls"
data-track-label="Add merge request approval"
- data-track-property="Growth::Conversion::Experiment::LearnGitLab"
href="http://example.com/"
target="_self"
>
@@ -267,9 +259,7 @@ exports[`Learn GitLab renders correctly 1`] = `
class="gl-link"
data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link"
- data-track-experiment="change_continuous_onboarding_link_urls"
data-track-label="Create an issue"
- data-track-property="Growth::Conversion::Experiment::LearnGitLab"
href="http://example.com/"
target="_self"
>
@@ -287,9 +277,7 @@ exports[`Learn GitLab renders correctly 1`] = `
class="gl-link"
data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link"
- data-track-experiment="change_continuous_onboarding_link_urls"
data-track-label="Submit a merge request"
- data-track-property="Growth::Conversion::Experiment::LearnGitLab"
href="http://example.com/"
target="_self"
>
@@ -343,9 +331,7 @@ exports[`Learn GitLab renders correctly 1`] = `
class="gl-link"
data-testid="uncompleted-learn-gitlab-link"
data-track-action="click_link"
- data-track-experiment="change_continuous_onboarding_link_urls"
data-track-label="Run a Security scan using CI/CD"
- data-track-property="Growth::Conversion::Experiment::LearnGitLab"
href="https://docs.gitlab.com/ee/foobar/"
rel="noopener noreferrer"
target="_blank"
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js
index 3b113f4dcd7..e21371123e8 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js
+++ b/spec/frontend/pages/projects/learn_gitlab/components/learn_gitlab_section_link_spec.js
@@ -12,8 +12,9 @@ const defaultProps = {
completed: false,
};
-const docLinkProps = {
+const openInNewTabProps = {
url: 'https://docs.gitlab.com/ee/user/application_security/security_dashboard/',
+ openInNewTab: true,
};
describe('Learn GitLab Section Link', () => {
@@ -59,9 +60,9 @@ describe('Learn GitLab Section Link', () => {
expect(wrapper.find('[data-testid="trial-only"]').exists()).toBe(true);
});
- describe('doc links', () => {
+ describe('links marked with openInNewTab', () => {
beforeEach(() => {
- createWrapper('securityScanEnabled', docLinkProps);
+ createWrapper('securityScanEnabled', openInNewTabProps);
});
it('renders links with blank target', () => {
@@ -78,7 +79,6 @@ describe('Learn GitLab Section Link', () => {
expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_link', {
label: 'Run a Security scan using CI/CD',
- property: 'Growth::Conversion::Experiment::LearnGitLab',
});
unmockTracking();
diff --git a/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js b/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js
index b21965e8f48..5dc64097d81 100644
--- a/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js
+++ b/spec/frontend/pages/projects/learn_gitlab/components/mock_data.js
@@ -38,6 +38,7 @@ export const testActions = {
url: 'https://docs.gitlab.com/ee/foobar/',
completed: false,
svg: 'http://example.com/images/illustration.svg',
+ openInNewTab: true,
},
issueCreated: {
url: 'http://example.com/',
diff --git a/spec/graphql/resolvers/base_resolver_spec.rb b/spec/graphql/resolvers/base_resolver_spec.rb
index d77a0b6242e..39b00c14161 100644
--- a/spec/graphql/resolvers/base_resolver_spec.rb
+++ b/spec/graphql/resolvers/base_resolver_spec.rb
@@ -239,16 +239,16 @@ RSpec.describe Resolvers::BaseResolver do
it 'increases complexity based on arguments' do
field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: described_class, null: false, max_page_size: 1)
- expect(field.to_graphql.complexity.call({}, { sort: 'foo' }, 1)).to eq 3
- expect(field.to_graphql.complexity.call({}, { search: 'foo' }, 1)).to eq 7
+ expect(field.complexity.call({}, { sort: 'foo' }, 1)).to eq 3
+ expect(field.complexity.call({}, { search: 'foo' }, 1)).to eq 7
end
it 'does not increase complexity when filtering by iids' do
field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
- expect(field.to_graphql.complexity.call({}, { sort: 'foo' }, 1)).to eq 6
- expect(field.to_graphql.complexity.call({}, { sort: 'foo', iid: 1 }, 1)).to eq 3
- expect(field.to_graphql.complexity.call({}, { sort: 'foo', iids: [1, 2, 3] }, 1)).to eq 3
+ expect(field.complexity.call({}, { sort: 'foo' }, 1)).to eq 6
+ expect(field.complexity.call({}, { sort: 'foo', iid: 1 }, 1)).to eq 3
+ expect(field.complexity.call({}, { sort: 'foo', iids: [1, 2, 3] }, 1)).to eq 3
end
end
diff --git a/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
index 38f5a644985..4c4aa4f53e1 100644
--- a/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
+++ b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
@@ -106,9 +106,9 @@ RSpec.describe ResolvesPipelines do
it 'increases field complexity based on arguments' do
field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: resolver, null: false, max_page_size: 1)
- expect(field.to_graphql.complexity.call({}, {}, 1)).to eq 2
- expect(field.to_graphql.complexity.call({}, { sha: 'foo' }, 1)).to eq 4
- expect(field.to_graphql.complexity.call({}, { sha: 'ref' }, 1)).to eq 4
+ expect(field.complexity.call({}, {}, 1)).to eq 2
+ expect(field.complexity.call({}, { sha: 'foo' }, 1)).to eq 4
+ expect(field.complexity.call({}, { sha: 'ref' }, 1)).to eq 4
end
def resolve_pipelines(args = {}, context = { current_user: current_user })
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index dc717b113c1..326c105a358 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -618,8 +618,8 @@ RSpec.describe Resolvers::IssuesResolver do
it 'increases field complexity based on arguments' do
field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
- expect(field.to_graphql.complexity.call({}, {}, 1)).to eq 4
- expect(field.to_graphql.complexity.call({}, { labelName: 'foo' }, 1)).to eq 8
+ expect(field.complexity.call({}, {}, 1)).to eq 4
+ expect(field.complexity.call({}, { labelName: 'foo' }, 1)).to eq 8
end
def create_issue_with_severity(project, severity:)
diff --git a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
index b1f50a4a4a5..eb4d0ab6f37 100644
--- a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
@@ -147,8 +147,8 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
it 'has an high complexity regardless of arguments' do
field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
- expect(field.to_graphql.complexity.call({}, {}, 1)).to eq 24
- expect(field.to_graphql.complexity.call({}, { include_subgroups: true }, 1)).to eq 24
+ expect(field.complexity.call({}, {}, 1)).to eq 24
+ expect(field.complexity.call({}, { include_subgroups: true }, 1)).to eq 24
end
def resolve_projects(args = { include_subgroups: false, sort: nil, search: nil, ids: nil }, context = { current_user: current_user })
diff --git a/spec/graphql/resolvers/project_resolver_spec.rb b/spec/graphql/resolvers/project_resolver_spec.rb
index cd3fdc788e6..dec9d4701e1 100644
--- a/spec/graphql/resolvers/project_resolver_spec.rb
+++ b/spec/graphql/resolvers/project_resolver_spec.rb
@@ -36,8 +36,8 @@ RSpec.describe Resolvers::ProjectResolver do
field1 = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: described_class, null: false, max_page_size: 100)
field2 = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: described_class, null: false, max_page_size: 1)
- expect(field1.to_graphql.complexity.call({}, {}, 1)).to eq 2
- expect(field2.to_graphql.complexity.call({}, {}, 1)).to eq 2
+ expect(field1.complexity.call({}, {}, 1)).to eq 2
+ expect(field2.complexity.call({}, {}, 1)).to eq 2
end
def resolve_project(full_path)
diff --git a/spec/graphql/types/base_enum_spec.rb b/spec/graphql/types/base_enum_spec.rb
index 64524f3ffcd..65a345052c7 100644
--- a/spec/graphql/types/base_enum_spec.rb
+++ b/spec/graphql/types/base_enum_spec.rb
@@ -136,7 +136,7 @@ RSpec.describe Types::BaseEnum do
value 'TEST_VALUE', **args
end
- enum.to_graphql.values['TEST_VALUE']
+ enum.values['TEST_VALUE']
end
end
end
diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb
index 31d07f701e8..9d02f061435 100644
--- a/spec/graphql/types/base_field_spec.rb
+++ b/spec/graphql/types/base_field_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Types::BaseField do
it 'defaults to 1' do
field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true)
- expect(field.to_graphql.complexity).to eq 1
+ expect(field.complexity).to eq 1
end
describe '#base_complexity' do
@@ -43,7 +43,7 @@ RSpec.describe Types::BaseField do
it 'has specified value' do
field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true, complexity: 12)
- expect(field.to_graphql.complexity).to eq 12
+ expect(field.complexity).to eq 12
end
context 'when field has a resolver' do
@@ -51,7 +51,7 @@ RSpec.describe Types::BaseField do
let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: resolver, complexity: 2, max_page_size: 100, null: true) }
it 'uses this complexity' do
- expect(field.to_graphql.complexity).to eq 2
+ expect(field.complexity).to eq 2
end
end
@@ -59,13 +59,13 @@ RSpec.describe Types::BaseField do
let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: resolver, max_page_size: 100, null: true) }
it 'sets complexity depending on arguments for resolvers' do
- expect(field.to_graphql.complexity.call({}, {}, 2)).to eq 4
- expect(field.to_graphql.complexity.call({}, { first: 50 }, 2)).to eq 3
+ expect(field.complexity.call({}, {}, 2)).to eq 4
+ expect(field.complexity.call({}, { first: 50 }, 2)).to eq 3
end
it 'sets complexity depending on number load limits for resolvers' do
- expect(field.to_graphql.complexity.call({}, { first: 1 }, 2)).to eq 2
- expect(field.to_graphql.complexity.call({}, { first: 1, foo: true }, 2)).to eq 4
+ expect(field.complexity.call({}, { first: 1 }, 2)).to eq 2
+ expect(field.complexity.call({}, { first: 1, foo: true }, 2)).to eq 4
end
end
@@ -73,8 +73,8 @@ RSpec.describe Types::BaseField do
it 'sets complexity as normal' do
field = described_class.new(name: 'test', type: GraphQL::Types::String, resolver_class: resolver, max_page_size: 100, null: true)
- expect(field.to_graphql.complexity.call({}, {}, 2)).to eq 2
- expect(field.to_graphql.complexity.call({}, { first: 50 }, 2)).to eq 2
+ expect(field.complexity.call({}, {}, 2)).to eq 2
+ expect(field.complexity.call({}, { first: 50 }, 2)).to eq 2
end
end
end
@@ -84,9 +84,9 @@ RSpec.describe Types::BaseField do
it 'adds 1 if true' do
with_gitaly_field = described_class.new(name: 'test', type: GraphQL::Types::String, resolver_class: resolver, null: true, calls_gitaly: true)
without_gitaly_field = described_class.new(name: 'test', type: GraphQL::Types::String, resolver_class: resolver, null: true)
- base_result = without_gitaly_field.to_graphql.complexity.call({}, {}, 2)
+ base_result = without_gitaly_field.complexity.call({}, {}, 2)
- expect(with_gitaly_field.to_graphql.complexity.call({}, {}, 2)).to eq base_result + 1
+ expect(with_gitaly_field.complexity.call({}, {}, 2)).to eq base_result + 1
end
end
@@ -94,7 +94,7 @@ RSpec.describe Types::BaseField do
it 'adds 1 if true' do
field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true, calls_gitaly: true)
- expect(field.to_graphql.complexity).to eq 2
+ expect(field.complexity).to eq 2
end
end
@@ -108,14 +108,14 @@ RSpec.describe Types::BaseField do
it 'has complexity set to that constant' do
field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true, complexity: 12)
- expect(field.to_graphql.complexity).to eq 12
+ expect(field.complexity).to eq 12
end
it 'does not raise an error even with Gitaly calls' do
allow(Gitlab::GitalyClient).to receive(:get_request_count).and_return([0, 1])
field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true, complexity: 12)
- expect(field.to_graphql.complexity).to eq 12
+ expect(field.complexity).to eq 12
end
end
end
diff --git a/spec/graphql/types/global_id_type_spec.rb b/spec/graphql/types/global_id_type_spec.rb
index f9391efdf08..8df92c818fc 100644
--- a/spec/graphql/types/global_id_type_spec.rb
+++ b/spec/graphql/types/global_id_type_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Types::GlobalIDType do
let(:gid) { project.to_global_id }
it 'is has the correct name' do
- expect(described_class.to_graphql.name).to eq('GlobalID')
+ expect(described_class.graphql_name).to eq('GlobalID')
end
describe '.coerce_result' do
@@ -63,7 +63,7 @@ RSpec.describe Types::GlobalIDType do
let(:type) { ::Types::GlobalIDType[::Project] }
it 'is has the correct name' do
- expect(type.to_graphql.name).to eq('ProjectID')
+ expect(type.graphql_name).to eq('ProjectID')
end
context 'the GID is appropriate' do
@@ -126,7 +126,7 @@ RSpec.describe Types::GlobalIDType do
let(:deprecating_gid) { Gitlab::GlobalId.build(model_name: 'Issue', id: issue.id) }
it 'appends the description with a deprecation notice for the old Global ID' do
- expect(type.to_graphql.description).to include('The older format `"gid://gitlab/OldIssue/1"` was deprecated in 10.0')
+ expect(type.description).to include('The older format `"gid://gitlab/OldIssue/1"` was deprecated in 10.0')
end
describe 'coercing input against the type (parsing the Global ID string when supplied as an argument)' do
@@ -242,7 +242,7 @@ RSpec.describe Types::GlobalIDType do
let(:type) { ::Types::GlobalIDType[::Ci::Build] }
it 'is has a valid GraphQL identifier for a name' do
- expect(type.to_graphql.name).to eq('CiBuildID')
+ expect(type.graphql_name).to eq('CiBuildID')
end
end
diff --git a/spec/helpers/learn_gitlab_helper_spec.rb b/spec/helpers/learn_gitlab_helper_spec.rb
index ffc2bb31b8f..9fce7495b5a 100644
--- a/spec/helpers/learn_gitlab_helper_spec.rb
+++ b/spec/helpers/learn_gitlab_helper_spec.rb
@@ -97,29 +97,29 @@ RSpec.describe LearnGitlabHelper do
trial_started: a_hash_including(
url: a_string_matching(%r{/learn_gitlab/-/issues/2\z})
),
- issue_created: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/issues/4\z})
- ),
- git_write: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/issues/6\z})
- ),
pipeline_created: a_hash_including(
url: a_string_matching(%r{/learn_gitlab/-/issues/7\z})
),
- user_added: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/issues/8\z})
- ),
- merge_request_created: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/issues/9\z})
- ),
code_owners_enabled: a_hash_including(
url: a_string_matching(%r{/learn_gitlab/-/issues/10\z})
),
required_mr_approvals_enabled: a_hash_including(
url: a_string_matching(%r{/learn_gitlab/-/issues/11\z})
),
+ issue_created: a_hash_including(
+ url: a_string_matching(%r{/learn_gitlab/-/issues\z})
+ ),
+ git_write: a_hash_including(
+ url: a_string_matching(%r{/learn_gitlab\z})
+ ),
+ user_added: a_hash_including(
+ url: a_string_matching(%r{/learn_gitlab/-/project_members\z})
+ ),
+ merge_request_created: a_hash_including(
+ url: a_string_matching(%r{/learn_gitlab/-/merge_requests\z})
+ ),
security_scan_enabled: a_hash_including(
- url: a_string_matching(%r{docs\.gitlab\.com/ee/user/application_security/security_dashboard/#gitlab-security-dashboard-security-center-and-vulnerability-reports\z})
+ url: a_string_matching(%r{/learn_gitlab/-/security/configuration\z})
)
})
end
@@ -137,58 +137,5 @@ RSpec.describe LearnGitlabHelper do
security_scan_enabled: a_hash_including(completed: false)
})
end
-
- context 'when in the new action URLs experiment' do
- before do
- stub_experiments(change_continuous_onboarding_link_urls: :candidate)
- end
-
- it_behaves_like 'has all data'
-
- it 'sets mostly new paths' do
- expect(onboarding_actions_data).to match({
- trial_started: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/issues/2\z})
- ),
- issue_created: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/issues\z})
- ),
- git_write: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab\z})
- ),
- pipeline_created: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/pipelines\z})
- ),
- user_added: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/project_members\z})
- ),
- merge_request_created: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/merge_requests\z})
- ),
- code_owners_enabled: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/issues/10\z})
- ),
- required_mr_approvals_enabled: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/issues/11\z})
- ),
- security_scan_enabled: a_hash_including(
- url: a_string_matching(%r{/learn_gitlab/-/security/configuration\z})
- )
- })
- end
-
- it 'calls experiment with expected context & options' do
- allow(helper).to receive(:current_user).and_return(user)
-
- expect(helper).to receive(:experiment).with(
- :change_continuous_onboarding_link_urls,
- namespace: namespace,
- actor: user,
- sticky_to: namespace
- )
-
- learn_gitlab_data
- end
- end
end
end
diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index bf1a039f354..9cf78a11bc7 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -71,7 +71,7 @@ RSpec.describe Backup::Manager do
end
before do
- allow(YAML).to receive(:load_file).with('backup_information.yml')
+ allow(YAML).to receive(:load_file).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
.and_return(backup_information)
end
@@ -171,7 +171,8 @@ RSpec.describe Backup::Manager do
before do
allow(ActiveRecord::Base.connection).to receive(:reconnect!)
allow(Kernel).to receive(:system).and_return(true)
- allow(YAML).to receive(:load_file).and_return(backup_information)
+ allow(YAML).to receive(:load_file).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
+ .and_return(backup_information)
allow(subject).to receive(:backup_information).and_return(backup_information)
allow(task1).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'task1.tar.gz'))
@@ -571,7 +572,8 @@ RSpec.describe Backup::Manager do
allow(task1).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'task1.tar.gz'))
allow(task2).to receive(:restore).with(File.join(Gitlab.config.backup.path, 'task2.tar.gz'))
- allow(YAML).to receive(:load_file).and_return(backup_information)
+ allow(YAML).to receive(:load_file).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
+ .and_return(backup_information)
allow(Rake::Task['gitlab:shell:setup']).to receive(:invoke)
allow(Rake::Task['cache:clear']).to receive(:invoke)
end
diff --git a/spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb b/spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb
new file mode 100644
index 00000000000..588f53150ff
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Entry::Reports::CoverageReport do
+ let(:entry) { described_class.new(config) }
+
+ describe 'validations' do
+ context 'when it is valid' do
+ let(:config) { { coverage_format: 'cobertura', path: 'cobertura-coverage.xml' } }
+
+ it { expect(entry).to be_valid }
+
+ it { expect(entry.value).to eq(config) }
+ end
+
+ context 'with unsupported coverage format' do
+ let(:config) { { coverage_format: 'jacoco', path: 'jacoco.xml' } }
+
+ it { expect(entry).not_to be_valid }
+
+ it { expect(entry.errors).to include /format must be one of supported formats/ }
+ end
+
+ context 'without coverage format' do
+ let(:config) { { path: 'cobertura-coverage.xml' } }
+
+ it { expect(entry).not_to be_valid }
+
+ it { expect(entry.errors).to include /format can't be blank/ }
+ end
+
+ context 'without path' do
+ let(:config) { { coverage_format: 'cobertura' } }
+
+ it { expect(entry).not_to be_valid }
+
+ it { expect(entry.errors).to include /path can't be blank/ }
+ end
+
+ context 'with invalid path' do
+ let(:config) { { coverage_format: 'cobertura', path: 123 } }
+
+ it { expect(entry).not_to be_valid }
+
+ it { expect(entry.errors).to include /path should be a string/ }
+ end
+
+ context 'with unknown keys' do
+ let(:config) { { coverage_format: 'cobertura', path: 'cobertura-coverage.xml', foo: :bar } }
+
+ it { expect(entry).not_to be_valid }
+
+ it { expect(entry.errors).to include /contains unknown keys/ }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
index 12b8960eb32..061d8f34c8d 100644
--- a/spec/lib/gitlab/ci/config/entry/reports_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
@@ -6,12 +6,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Reports do
let(:entry) { described_class.new(config) }
describe 'validates ALLOWED_KEYS' do
- let(:artifact_file_types) { Ci::JobArtifact.file_types }
-
- described_class::ALLOWED_KEYS.each do |keyword, _|
- it "expects #{keyword} to be an artifact file_type" do
- expect(artifact_file_types).to include(keyword)
- end
+ it "expects ALLOWED_KEYS to be an artifact file_type or coverage_report" do
+ expect(Ci::JobArtifact.file_types.keys.map(&:to_sym) + [:coverage_report]).to include(*described_class::ALLOWED_KEYS)
end
end
@@ -68,6 +64,45 @@ RSpec.describe Gitlab::Ci::Config::Entry::Reports do
it_behaves_like 'a valid entry', params[:keyword], params[:file]
end
end
+
+ context 'when coverage_report is specified' do
+ let(:coverage_format) { :cobertura }
+ let(:filename) { 'cobertura-coverage.xml' }
+ let(:coverage_report) { { path: filename, coverage_format: coverage_format } }
+ let(:config) { { coverage_report: coverage_report } }
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+
+ it 'returns artifacts configuration' do
+ expect(entry.value).to eq(config)
+ end
+
+ context 'and another report is specified' do
+ let(:config) { { coverage_report: coverage_report, dast: 'gl-dast-report.json' } }
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+
+ it 'returns artifacts configuration' do
+ expect(entry.value).to eq({ coverage_report: coverage_report, dast: ['gl-dast-report.json'] })
+ end
+ end
+
+ context 'and a direct coverage report format is specified' do
+ let(:config) { { coverage_report: coverage_report, cobertura: 'cobertura-coverage.xml' } }
+
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+
+ it 'reports error' do
+ expect(entry.errors).to include /please use only one the following keys: coverage_report, cobertura/
+ end
+ end
+ end
end
context 'when entry value is not correct' do
diff --git a/spec/lib/gitlab/config/entry/validators_spec.rb b/spec/lib/gitlab/config/entry/validators_spec.rb
new file mode 100644
index 00000000000..cbc09aac586
--- /dev/null
+++ b/spec/lib/gitlab/config/entry/validators_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Config::Entry::Validators do
+ let(:klass) do
+ Class.new do
+ include ActiveModel::Validations
+ include Gitlab::Config::Entry::Validators
+ end
+ end
+
+ let(:instance) { klass.new }
+
+ describe described_class::MutuallyExclusiveKeysValidator do
+ using RSpec::Parameterized::TableSyntax
+
+ before do
+ klass.instance_eval do
+ validates :config, mutually_exclusive_keys: [:foo, :bar]
+ end
+
+ allow(instance).to receive(:config).and_return(config)
+ end
+
+ where(:context, :config, :valid_result) do
+ 'with mutually exclusive keys' | { foo: 1, bar: 2 } | false
+ 'without mutually exclusive keys' | { foo: 1 } | true
+ 'without mutually exclusive keys' | { bar: 1 } | true
+ 'with other keys' | { foo: 1, baz: 2 } | true
+ end
+
+ with_them do
+ it 'validates the instance' do
+ expect(instance.valid?).to be(valid_result)
+
+ unless valid_result
+ expect(instance.errors.messages_for(:config)).to include /please use only one the following keys: foo, bar/
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/markdown_field_spec.rb b/spec/lib/gitlab/graphql/markdown_field_spec.rb
index c2253811e91..ed3f19d8cf2 100644
--- a/spec/lib/gitlab/graphql/markdown_field_spec.rb
+++ b/spec/lib/gitlab/graphql/markdown_field_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Gitlab::Graphql::MarkdownField do
expect(field.name).to eq('testHtml')
expect(field.description).to eq('The GitLab Flavored Markdown rendering of `hello`')
expect(field.type).to eq(GraphQL::Types::String)
- expect(field.to_graphql.complexity).to eq(5)
+ expect(field.complexity).to eq(5)
end
context 'developer warnings' do
@@ -43,7 +43,7 @@ RSpec.describe Gitlab::Graphql::MarkdownField do
let(:field) { type_class.fields['noteHtml'] }
it 'renders markdown from the same property as the field name without the `_html` suffix' do
- expect(field.to_graphql.resolve(type_instance, {}, context)).to eq(expected_markdown)
+ expect(field.resolve(type_instance, {}, context)).to eq(expected_markdown)
end
context 'when a `method` argument is passed' do
@@ -51,7 +51,7 @@ RSpec.describe Gitlab::Graphql::MarkdownField do
let(:field) { type_class.fields['testHtml'] }
it 'renders markdown from a specific property' do
- expect(field.to_graphql.resolve(type_instance, {}, context)).to eq(expected_markdown)
+ expect(field.resolve(type_instance, {}, context)).to eq(expected_markdown)
end
end
@@ -62,21 +62,21 @@ RSpec.describe Gitlab::Graphql::MarkdownField do
let(:note) { build(:note, note: "Referencing #{issue.to_reference(full: true)}") }
it 'renders markdown correctly' do
- expect(field.to_graphql.resolve(type_instance, {}, context)).to include(issue_path(issue))
+ expect(field.resolve(type_instance, {}, context)).to include(issue_path(issue))
end
context 'when the issue is not publicly accessible' do
let_it_be(:project) { create(:project, :private) }
it 'hides the references from users that are not allowed to see the reference' do
- expect(field.to_graphql.resolve(type_instance, {}, context)).not_to include(issue_path(issue))
+ expect(field.resolve(type_instance, {}, context)).not_to include(issue_path(issue))
end
it 'shows the reference to users that are allowed to see it' do
context = GraphQL::Query::Context.new(query: query, values: { current_user: project.first_owner }, object: nil)
type_instance = type_class.authorized_new(note, context)
- expect(field.to_graphql.resolve(type_instance, {}, context)).to include(issue_path(issue))
+ expect(field.resolve(type_instance, {}, context)).to include(issue_path(issue))
end
end
end
diff --git a/spec/lib/gitlab/graphql/mount_mutation_spec.rb b/spec/lib/gitlab/graphql/mount_mutation_spec.rb
index fe25e923506..09fd9eac714 100644
--- a/spec/lib/gitlab/graphql/mount_mutation_spec.rb
+++ b/spec/lib/gitlab/graphql/mount_mutation_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Gitlab::Graphql::MountMutation do
f.mount_mutation(mutation)
end
- mutation_type.get_field('testMutation').to_graphql
+ mutation_type.get_field('testMutation')
end
it 'mounts a mutation' do
@@ -31,7 +31,7 @@ RSpec.describe Gitlab::Graphql::MountMutation do
f.mount_aliased_mutation('MyAlias', mutation)
end
- mutation_type.get_field('myAlias').to_graphql
+ mutation_type.get_field('myAlias')
end
it 'mounts a mutation' do
@@ -43,11 +43,11 @@ RSpec.describe Gitlab::Graphql::MountMutation do
end
it 'has a correct type' do
- expect(field.type.name).to eq('MyAliasPayload')
+ expect(field.type.to_type_signature).to eq('MyAliasPayload')
end
it 'has a correct input argument' do
- expect(field.arguments['input'].type.unwrap.name).to eq('MyAliasInput')
+ expect(field.arguments['input'].type.unwrap.to_type_signature).to eq('MyAliasInput')
end
end
diff --git a/spec/lib/learn_gitlab/onboarding_spec.rb b/spec/lib/learn_gitlab/onboarding_spec.rb
index 6b4be65f3b2..8c7284ed7f5 100644
--- a/spec/lib/learn_gitlab/onboarding_spec.rb
+++ b/spec/lib/learn_gitlab/onboarding_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe LearnGitlab::Onboarding do
let(:namespace) { build(:namespace) }
let_it_be(:tracked_action_columns) do
- tracked_actions = described_class::ACTION_ISSUE_IDS.keys + described_class::ACTION_DOC_URLS.keys
+ tracked_actions = described_class::ACTION_ISSUE_IDS.keys + described_class::ACTION_PATHS
tracked_actions.map { |key| OnboardingProgress.column_name(key) }
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index d58b91e675a..f3c4f5a4f6f 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -8026,14 +8026,6 @@ RSpec.describe Project, factory_default: :keep do
let(:object) { build(:project) }
it_behaves_like 'blocks unsafe serialization'
-
- context 'when feature flag block_project_serialization is disabled' do
- before do
- stub_feature_flags(block_project_serialization: false)
- end
-
- it_behaves_like 'allows unsafe serialization'
- end
end
private
diff --git a/spec/requests/api/graphql/group/issues_spec.rb b/spec/requests/api/graphql/group/issues_spec.rb
index 332bf242e9c..26338f46611 100644
--- a/spec/requests/api/graphql/group/issues_spec.rb
+++ b/spec/requests/api/graphql/group/issues_spec.rb
@@ -44,6 +44,31 @@ RSpec.describe 'getting an issue list for a group' do
end
end
+ context 'when there are archived projects' do
+ let_it_be(:archived_project) { create(:project, :archived, group: group1) }
+ let_it_be(:archived_issue) { create(:issue, project: archived_project) }
+
+ before_all do
+ group1.add_developer(current_user)
+ end
+
+ it 'excludes issues from archived projects by default' do
+ post_graphql(query, current_user: current_user)
+
+ expect(issues_ids).to contain_exactly(issue1_gid, issue2_gid)
+ end
+
+ context 'when include_archived is true' do
+ let(:issue_filter_params) { { include_archived: true } }
+
+ it 'includes issues from archived projects' do
+ post_graphql(query, current_user: current_user)
+
+ expect(issues_ids).to contain_exactly(issue1_gid, issue2_gid, archived_issue.to_global_id.to_s)
+ end
+ end
+ end
+
context 'when there is a confidential issue' do
let_it_be(:confidential_issue1) { create(:issue, :confidential, project: project1) }
let_it_be(:confidential_issue2) { create(:issue, :confidential, project: project2) }
diff --git a/spec/requests/api/graphql/group/merge_requests_spec.rb b/spec/requests/api/graphql/group/merge_requests_spec.rb
index e9a5e558b1d..c0faff11c8d 100644
--- a/spec/requests/api/graphql/group/merge_requests_spec.rb
+++ b/spec/requests/api/graphql/group/merge_requests_spec.rb
@@ -16,6 +16,9 @@ RSpec.describe 'Query.group.mergeRequests' do
let_it_be(:project_x) { create(:project, :repository) }
let_it_be(:user) { create(:user, developer_projects: [project_x]) }
+ let_it_be(:archived_project) { create(:project, :archived, :repository, group: group) }
+ let_it_be(:archived_mr) { create(:merge_request, source_project: archived_project) }
+
let_it_be(:mr_attrs) do
{ target_branch: 'master' }
end
@@ -119,4 +122,22 @@ RSpec.describe 'Query.group.mergeRequests' do
expect(mrs_data).to match_array(expected_mrs(mrs_a + mrs_b + mrs_c))
end
end
+
+ describe 'passing include_archived: true' do
+ let(:query) do
+ <<~GQL
+ query($path: ID!) {
+ group(fullPath: $path) {
+ mergeRequests(includeArchived: true) { nodes { id } }
+ }
+ }
+ GQL
+ end
+
+ it 'can find all merge requests in the group, including from archived projects' do
+ post_graphql(query, current_user: user, variables: { path: group.full_path })
+
+ expect(mrs_data).to match_array(expected_mrs(mrs_a + mrs_b + [archived_mr]))
+ end
+ end
end
diff --git a/spec/requests/projects/redirect_controller_spec.rb b/spec/requests/projects/redirect_controller_spec.rb
new file mode 100644
index 00000000000..3bbca3ca32b
--- /dev/null
+++ b/spec/requests/projects/redirect_controller_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe "Projects::RedirectController requests" do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:private_project) { create(:project, :private) }
+ let_it_be(:public_project) { create(:project, :public) }
+ let_it_be(:user) { create(:user) }
+
+ before_all do
+ private_project.add_developer(user)
+ end
+
+ describe 'GET redirect_from_id' do
+ where(:authenticated, :project, :is_found) do
+ true | ref(:private_project) | true
+ false | ref(:private_project) | false
+ true | ref(:public_project) | true
+ false | ref(:public_project) | true
+ true | build(:project, id: 0) | false
+ end
+
+ with_them do
+ before do
+ sign_in(user) if authenticated
+
+ get "/projects/#{project.id}"
+ end
+
+ if params[:is_found]
+ it 'redirects to the project page' do
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response).to redirect_to(project_path(project))
+ end
+ else
+ it 'gives 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+
+ # This is a regression test for https://gitlab.com/gitlab-org/gitlab/-/issues/351058
+ context 'with sourcegraph enabled' do
+ let_it_be(:sourcegraph_url) { 'https://sourcegraph.test' }
+
+ before do
+ allow(Gitlab::CurrentSettings).to receive(:sourcegraph_url).and_return(sourcegraph_url)
+ allow(Gitlab::CurrentSettings).to receive(:sourcegraph_enabled).and_return(true)
+
+ sign_in(user)
+ end
+
+ context 'with projects/:id route' do
+ subject { get "/projects/#{public_project.id}" }
+
+ it 'redirects successfully' do
+ subject
+
+ expect(response).to redirect_to(project_path(public_project))
+ end
+ end
+ end
+end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 3a935ddbdd8..65772895826 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -70,9 +70,11 @@ RSpec.describe 'project routing' do
route_to('projects#preview_markdown', namespace_id: 'gitlab', id: 'gitlabhq')
)
end
+ end
- it 'to #resolve' do
- expect(get('/projects/1')).to route_to('projects#resolve', id: '1')
+ describe Projects::RedirectController, 'routing' do
+ it 'to #redirect_from_id' do
+ expect(get('/projects/1')).to route_to('projects/redirect#redirect_from_id', id: '1')
end
end
diff --git a/spec/services/ci/create_pipeline_service/artifacts_spec.rb b/spec/services/ci/create_pipeline_service/artifacts_spec.rb
new file mode 100644
index 00000000000..1ec30d68666
--- /dev/null
+++ b/spec/services/ci/create_pipeline_service/artifacts_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Ci::CreatePipelineService do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.first_owner }
+
+ let(:ref) { 'refs/heads/master' }
+ let(:source) { :push }
+
+ let(:service) { described_class.new(project, user, { ref: ref }) }
+ let(:pipeline) { service.execute(source).payload }
+
+ describe 'artifacts:' do
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ allow_next_instance_of(Ci::BuildScheduleWorker) do |instance|
+ allow(instance).to receive(:perform).and_return(true)
+ end
+ end
+
+ describe 'reports:' do
+ context 'with valid config' do
+ let(:config) do
+ <<~YAML
+ test-job:
+ script: "echo 'hello world' > cobertura.xml"
+ artifacts:
+ reports:
+ coverage_report:
+ coverage_format: 'cobertura'
+ path: 'cobertura.xml'
+
+ dependency-scanning-job:
+ script: "echo 'hello world' > gl-dependency-scanning-report.json"
+ artifacts:
+ reports:
+ dependency_scanning: 'gl-dependency-scanning-report.json'
+ YAML
+ end
+
+ it 'creates pipeline with builds' do
+ expect(pipeline).to be_persisted
+ expect(pipeline).not_to have_yaml_errors
+ expect(pipeline.builds.pluck(:name)).to contain_exactly('test-job', 'dependency-scanning-job')
+ end
+ end
+
+ context 'with invalid config' do
+ let(:config) do
+ <<~YAML
+ test-job:
+ script: "echo 'hello world' > cobertura.xml"
+ artifacts:
+ reports:
+ foo: 'bar'
+ YAML
+ end
+
+ it 'creates pipeline with yaml errors' do
+ expect(pipeline).to be_persisted
+ expect(pipeline).to have_yaml_errors
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index b895e9ba67e..399b2b4be2d 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1072,6 +1072,7 @@ RSpec.describe NotificationService, :mailer do
end
before do
+ project.reload
add_user_subscriptions(issue)
reset_delivered_emails!
update_custom_notification(:new_issue, @u_guest_custom, resource: project)