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:
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue24
-rw-r--r--app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue11
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue2
-rw-r--r--app/assets/javascripts/set_status_modal/components/user_availability_status.vue26
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue3
-rw-r--r--app/assets/javascripts/set_status_modal/utils.js6
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue54
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.vue1
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue14
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue37
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue40
-rw-r--r--app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue12
-rw-r--r--app/models/packages/composer/cache_file.rb2
-rw-r--r--app/services/ci/pipeline_trigger_service.rb17
-rw-r--r--app/workers/all_queues.yml8
-rw-r--r--app/workers/packages/composer/cache_cleanup_worker.rb30
-rw-r--r--changelogs/unreleased/263452-sidebar-display-busy-status.yml5
-rw-r--r--changelogs/unreleased/292820-update-user-api-docs-and-enable-ff.yml5
-rw-r--r--changelogs/unreleased/299403-usage_data_unique_users_committing_ciconfigfile.yml5
-rw-r--r--changelogs/unreleased/300524-create-worker-to-clean-up-composer-cache-pages.yml5
-rw-r--r--changelogs/unreleased/feature-flag-enable-ci-config-merged-tab.yml5
-rw-r--r--config/feature_flags/development/ci_config_merged_tab.yml2
-rw-r--r--config/feature_flags/development/ci_trigger_payload_into_pipeline.yml8
-rw-r--r--config/feature_flags/development/clear_status_with_quick_options.yml2
-rw-r--r--config/feature_flags/development/group_wiki_import_export.yml8
-rw-r--r--config/feature_flags/development/usage_data_unique_users_committing_ciconfigfile.yml2
-rw-r--r--config/initializers/1_settings.rb3
-rw-r--r--config/metrics/counts_28d/20210201124930_deployments.yml (renamed from config/metrics/counts_28d/deployments.yml)16
-rw-r--r--config/metrics/counts_7d/20210201124931_g_project_management_issue_title_changed_weekly.yml (renamed from config/metrics/counts_7d/g_project_management_issue_title_changed_weekly.yml)17
-rw-r--r--config/metrics/counts_all/20210201124934_deployments.yml (renamed from config/metrics/counts_all/deployments.yml)15
-rw-r--r--config/metrics/counts_all/20210204124930_servers.yml14
-rw-r--r--config/metrics/counts_all/20210204124932_clusters.yml14
-rw-r--r--config/metrics/license/20210201124932_recorded_at.yml (renamed from config/metrics/license/recorded_at.yml)17
-rw-r--r--config/metrics/license/20210201124933_uuid.yml (renamed from config/metrics/license/uuid.yml)17
-rw-r--r--config/metrics/license/20210204124827_hostname.yml19
-rw-r--r--config/metrics/license/20210204124829_active_user_count.yml19
-rw-r--r--config/metrics/license/20210204124854_license_management_jobs.yml16
-rw-r--r--config/metrics/license/20210204124926_license_trial_ends_on.yml14
-rw-r--r--config/metrics/license/20210204124928_version.yml14
-rw-r--r--config/metrics/license/20210204124936_version.yml14
-rw-r--r--config/metrics/license/20210204124938_recording_ce_finished_at.yml16
-rw-r--r--config/metrics/settings/20210201124935_database_adapter.yml (renamed from config/metrics/settings/database_adapter.yml)17
-rw-r--r--config/metrics/settings/20210204124856_instance_auto_devops_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124858_container_registry_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124900_dependency_proxy_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124904_gravatar_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124906_ldap_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124908_mattermost_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124910_omniauth_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124912_prometheus_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124914_prometheus_metrics_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124916_reply_by_email_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124918_signup_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124920_web_ide_clientside_preview_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124922_grafana_link_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124924_elasticsearch_enabled.yml14
-rw-r--r--config/metrics/settings/20210204124934_enabled.yml14
-rw-r--r--db/migrate/20210203222620_add_expired_index_to_composer_cache_files.rb19
-rw-r--r--db/migrate/20210203223551_add_orphan_index_to_composer_cache_files.rb19
-rw-r--r--db/schema_migrations/202102032226201
-rw-r--r--db/schema_migrations/202102032235511
-rw-r--r--db/structure.sql4
-rw-r--r--doc/administration/maintenance_mode/index.md57
-rw-r--r--doc/api/users.md22
-rw-r--r--doc/ci/triggers/README.md35
-rw-r--r--doc/ci/variables/predefined_variables.md1
-rw-r--r--doc/development/maintenance_mode.md6
-rw-r--r--doc/development/usage_ping/dictionary.md803
-rw-r--r--doc/development/usage_ping/metrics_dictionary.md9
-rw-r--r--doc/user/project/autocomplete_characters.md7
-rw-r--r--generator_templates/usage_metric_definition/metric_definition.yml13
-rw-r--r--lib/api/users.rb2
-rw-r--r--lib/bulk_imports/pipeline/runner.rb18
-rw-r--r--lib/generators/gitlab/usage_metric_definition_generator.rb10
-rw-r--r--lib/gitlab/usage/docs/helper.rb2
-rw-r--r--lib/gitlab/usage/docs/value_formatter.rb4
-rw-r--r--locale/gitlab.pot12
-rw-r--r--package.json11
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb174
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb1
-rw-r--r--spec/frontend/notes/components/note_header_spec.js9
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_column_spec.js109
-rw-r--r--spec/frontend/set_status_modal/user_availability_status_spec.js31
-rw-r--r--spec/frontend/set_status_modal/utils_spec.js15
-rw-r--r--spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js30
-rw-r--r--spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js22
-rw-r--r--spec/frontend/sidebar/components/assignees/collapsed_assignee_spec.js4
-rw-r--r--spec/frontend/sidebar/components/assignees/user_name_with_status_spec.js51
-rw-r--r--spec/frontend/vue_shared/components/user_popover/user_popover_spec.js8
-rw-r--r--spec/javascripts/matchers.js42
-rw-r--r--spec/javascripts/test_bundle.js2
-rw-r--r--spec/lib/bulk_imports/pipeline/runner_spec.rb9
-rw-r--r--spec/lib/gitlab/usage/docs/renderer_spec.rb1
-rw-r--r--spec/lib/gitlab/usage/docs/value_formatter_spec.rb2
-rw-r--r--spec/models/packages/composer/cache_file_spec.rb2
-rw-r--r--spec/requests/api/triggers_spec.rb11
-rw-r--r--spec/services/ci/pipeline_trigger_service_spec.rb29
-rw-r--r--spec/workers/packages/composer/cache_cleanup_worker_spec.rb29
-rw-r--r--yarn.lock89
101 files changed, 2118 insertions, 415 deletions
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index 101421523d0..6932af61c69 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -1,9 +1,9 @@
<script>
/* eslint-disable vue/no-v-html */
-import { GlIcon, GlLoadingIcon, GlTooltipDirective, GlSprintf } from '@gitlab/ui';
+import { GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { mapActions } from 'vuex';
-import { isUserBusy } from '~/set_status_modal/utils';
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import UserNameWithStatus from '../../sidebar/components/assignees/user_name_with_status.vue';
export default {
components: {
@@ -12,7 +12,7 @@ export default {
import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'),
GlIcon,
GlLoadingIcon,
- GlSprintf,
+ UserNameWithStatus,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -90,10 +90,6 @@ export default {
}
return false;
},
- authorIsBusy() {
- const { status } = this.author;
- return status?.availability && isUserBusy(status.availability);
- },
emojiElement() {
return this.$refs?.authorStatus?.querySelector('gl-emoji');
},
@@ -133,6 +129,9 @@ export default {
this.$refs.authorNameLink.dispatchEvent(new Event('mouseleave'));
this.isUsernameLinkHovered = false;
},
+ userAvailability(selectedAuthor) {
+ return selectedAuthor?.availability || '';
+ },
},
};
</script>
@@ -158,12 +157,11 @@ export default {
:data-username="author.username"
>
<slot name="note-header-info"></slot>
- <span class="note-header-author-name gl-font-weight-bold">
- <gl-sprintf v-if="authorIsBusy" :message="s__('UserAvailability|%{author} (Busy)')">
- <template #author>{{ authorName }}</template>
- </gl-sprintf>
- <template v-else>{{ authorName }}</template>
- </span>
+ <user-name-with-status
+ :name="authorName"
+ :availability="userAvailability(author)"
+ container-classes="note-header-author-name gl-font-weight-bold"
+ />
</a>
<span
v-if="authorStatus"
diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
index 30d64a58675..3ce77a1c60a 100644
--- a/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
+++ b/app/assets/javascripts/pipelines/components/graph/linked_pipelines_column.vue
@@ -61,6 +61,9 @@ export default {
isUpstream() {
return this.type === UPSTREAM;
},
+ minWidth() {
+ return this.isUpstream ? 0 : this.$options.minWidth;
+ },
},
methods: {
getPipelineData(pipeline) {
@@ -132,8 +135,8 @@ export default {
this.$emit('pipelineExpandToggle', jobName, expanded);
},
- showDownstreamContainer(id) {
- return !this.isUpstream && (this.isExpanded(id) || this.isLoadingPipeline(id));
+ showContainer(id) {
+ return this.isExpanded(id) || this.isLoadingPipeline(id);
},
},
};
@@ -164,8 +167,8 @@ export default {
@pipelineExpandToggle="onPipelineExpandToggle"
/>
<div
- v-if="showDownstreamContainer(pipeline.id)"
- :style="{ minWidth: $options.minWidth }"
+ v-if="showContainer(pipeline.id)"
+ :style="{ minWidth }"
class="gl-display-inline-block"
>
<pipeline-graph
diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
index 9438c117346..0a762563114 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -84,7 +84,7 @@ export default {
};
</script>
<template>
- <main-graph-wrapper class="gl-px-6">
+ <main-graph-wrapper class="gl-px-6" data-testid="stage-column">
<template #stages>
<div
data-testid="stage-column-title"
diff --git a/app/assets/javascripts/set_status_modal/components/user_availability_status.vue b/app/assets/javascripts/set_status_modal/components/user_availability_status.vue
deleted file mode 100644
index e86d94f86c6..00000000000
--- a/app/assets/javascripts/set_status_modal/components/user_availability_status.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-<script>
-import { AVAILABILITY_STATUS, isUserBusy, isValidAvailibility } from '../utils';
-
-export default {
- name: 'UserAvailabilityStatus',
- props: {
- availability: {
- type: String,
- required: true,
- validator: isValidAvailibility,
- },
- },
- computed: {
- isBusy() {
- const { availability = AVAILABILITY_STATUS.NOT_SET } = this;
- return isUserBusy(availability);
- },
- },
-};
-</script>
-
-<template>
- <span v-if="isBusy" class="gl-font-weight-normal gl-text-gray-500">{{
- s__('UserAvailability|(Busy)')
- }}</span>
-</template>
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
index 639cc8a72ef..bed264341a5 100644
--- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
@@ -10,7 +10,7 @@ import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
import { __, s__ } from '~/locale';
import { updateUserStatus } from '~/rest_api';
import EmojiMenuInModal from './emoji_menu_in_modal';
-import { isUserBusy, isValidAvailibility } from './utils';
+import { isUserBusy } from './utils';
const emojiMenuClass = 'js-modal-status-emoji-menu';
export const AVAILABILITY_STATUS = {
@@ -46,7 +46,6 @@ export default {
currentAvailability: {
type: String,
required: false,
- validator: isValidAvailibility,
default: '',
},
canSetUserAvailability: {
diff --git a/app/assets/javascripts/set_status_modal/utils.js b/app/assets/javascripts/set_status_modal/utils.js
index faee4012ef4..e17d95adb25 100644
--- a/app/assets/javascripts/set_status_modal/utils.js
+++ b/app/assets/javascripts/set_status_modal/utils.js
@@ -3,7 +3,5 @@ export const AVAILABILITY_STATUS = {
NOT_SET: 'not_set',
};
-export const isUserBusy = (status) => status === AVAILABILITY_STATUS.BUSY;
-
-export const isValidAvailibility = (availability) =>
- availability.length ? Object.values(AVAILABILITY_STATUS).includes(availability) : true;
+export const isUserBusy = (status = '') =>
+ Boolean(status.length && status.toLowerCase().trim() === AVAILABILITY_STATUS.BUSY);
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
index fbbe2e341a7..d0a65b48522 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
@@ -1,8 +1,46 @@
<script>
import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
+import { isUserBusy } from '~/set_status_modal/utils';
import AssigneeAvatar from './assignee_avatar.vue';
+const I18N = {
+ BUSY: __('Busy'),
+ CANNOT_MERGE: __('Cannot merge'),
+ LC_CANNOT_MERGE: __('cannot merge'),
+};
+
+const paranthesize = (str) => `(${str})`;
+
+const generateAssigneeTooltip = ({
+ name,
+ availability,
+ cannotMerge = true,
+ tooltipHasName = false,
+}) => {
+ if (!tooltipHasName) {
+ return cannotMerge ? I18N.CANNOT_MERGE : '';
+ }
+
+ const statusInformation = [];
+ if (availability && isUserBusy(availability)) {
+ statusInformation.push(I18N.BUSY);
+ }
+
+ if (cannotMerge) {
+ statusInformation.push(I18N.LC_CANNOT_MERGE);
+ }
+
+ if (tooltipHasName && statusInformation.length) {
+ return sprintf(__('%{name} %{status}'), {
+ name,
+ status: statusInformation.map(paranthesize).join(' '),
+ });
+ }
+
+ return name;
+};
+
export default {
components: {
AssigneeAvatar,
@@ -37,15 +75,13 @@ export default {
return this.issuableType === 'merge_request' && !this.user.can_merge;
},
tooltipTitle() {
- if (this.cannotMerge && this.tooltipHasName) {
- return sprintf(__('%{userName} (cannot merge)'), { userName: this.user.name });
- } else if (this.cannotMerge) {
- return __('Cannot merge');
- } else if (this.tooltipHasName) {
- return this.user.name;
- }
-
- return '';
+ const { name = '', availability = '' } = this.user;
+ return generateAssigneeTooltip({
+ name,
+ availability,
+ cannotMerge: this.cannotMerge,
+ tooltipHasName: this.tooltipHasName,
+ });
},
tooltipOption() {
return {
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
index 84e7110e2b2..5dc1ab90e00 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
@@ -36,7 +36,6 @@ export default {
sortedAssigness() {
const canMergeUsers = this.users.filter((user) => user.can_merge);
const canNotMergeUsers = this.users.filter((user) => !user.can_merge);
-
return [...canMergeUsers, ...canNotMergeUsers];
},
},
diff --git a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue
index 2f654409561..af4227fa48d 100644
--- a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue
@@ -1,9 +1,11 @@
<script>
import AssigneeAvatar from './assignee_avatar.vue';
+import UserNameWithStatus from './user_name_with_status.vue';
export default {
components: {
AssigneeAvatar,
+ UserNameWithStatus,
},
props: {
user: {
@@ -16,12 +18,20 @@ export default {
default: 'issue',
},
},
+ computed: {
+ availability() {
+ return this.user?.availability || '';
+ },
+ },
};
</script>
-
<template>
<button type="button" class="btn-link">
<assignee-avatar :user="user" :img-size="24" :issuable-type="issuableType" />
- <span class="author"> {{ user.name }} </span>
+ <user-name-with-status
+ :name="user.name"
+ :availability="availability"
+ container-classes="author"
+ />
</button>
</template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
index b713b0f960c..20667e695ce 100644
--- a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
@@ -1,11 +1,30 @@
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
+import { isUserBusy } from '~/set_status_modal/utils';
import CollapsedAssignee from './collapsed_assignee.vue';
const DEFAULT_MAX_COUNTER = 99;
const DEFAULT_RENDER_COUNT = 5;
+const generateCollapsedAssigneeTooltip = ({ renderUsers, allUsers, tooltipTitleMergeStatus }) => {
+ const names = renderUsers.map(({ name, availability }) => {
+ if (availability && isUserBusy(availability)) {
+ return sprintf(__('%{name} (Busy)'), { name });
+ }
+ return name;
+ });
+
+ if (!allUsers.length) {
+ return __('Assignee(s)');
+ }
+ if (allUsers.length > names.length) {
+ names.push(sprintf(__('+ %{amount} more'), { amount: allUsers.length - names.length }));
+ }
+ const text = names.join(', ');
+ return tooltipTitleMergeStatus ? `${text} (${tooltipTitleMergeStatus})` : text;
+};
+
export default {
directives: {
GlTooltip: GlTooltipDirective,
@@ -74,19 +93,11 @@ export default {
tooltipTitle() {
const maxRender = Math.min(DEFAULT_RENDER_COUNT, this.users.length);
const renderUsers = this.users.slice(0, maxRender);
- const names = renderUsers.map((u) => u.name);
-
- if (!this.users.length) {
- return __('Assignee(s)');
- }
-
- if (this.users.length > names.length) {
- names.push(sprintf(__('+ %{amount} more'), { amount: this.users.length - names.length }));
- }
-
- const text = names.join(', ');
-
- return this.tooltipTitleMergeStatus ? `${text} (${this.tooltipTitleMergeStatus})` : text;
+ return generateCollapsedAssigneeTooltip({
+ renderUsers,
+ allUsers: this.users,
+ tooltipTitleMergeStatus: this.tooltipTitleMergeStatus,
+ });
},
tooltipOptions() {
diff --git a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
index 31d5d7c0077..36775648809 100644
--- a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
@@ -1,12 +1,14 @@
<script>
import { __, sprintf } from '~/locale';
import AssigneeAvatarLink from './assignee_avatar_link.vue';
+import UserNameWithStatus from './user_name_with_status.vue';
const DEFAULT_RENDER_COUNT = 5;
export default {
components: {
AssigneeAvatarLink,
+ UserNameWithStatus,
},
props: {
users: {
@@ -55,6 +57,9 @@ export default {
toggleShowLess() {
this.showLess = !this.showLess;
},
+ userAvailability(u) {
+ return u?.availability || '';
+ },
},
};
</script>
@@ -68,7 +73,7 @@ export default {
:issuable-type="issuableType"
>
<div class="ml-2 gl-line-height-normal">
- <div>{{ firstUser.name }}</div>
+ <user-name-with-status :name="firstUser.name" :availability="userAvailability(firstUser)" />
<div>{{ username }}</div>
</div>
</assignee-avatar-link>
diff --git a/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue b/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue
new file mode 100644
index 00000000000..41b3b6c9a45
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/assignees/user_name_with_status.vue
@@ -0,0 +1,40 @@
+<script>
+import { GlSprintf } from '@gitlab/ui';
+import { isUserBusy } from '~/set_status_modal/utils';
+
+export default {
+ name: 'UserNameWithStatus',
+ components: {
+ GlSprintf,
+ },
+ props: {
+ name: {
+ type: String,
+ required: true,
+ },
+ containerClasses: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ availability: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ isBusy() {
+ return isUserBusy(this.availability);
+ },
+ },
+};
+</script>
+<template>
+ <span :class="containerClasses">
+ <gl-sprintf v-if="isBusy" :message="s__('UserAvailability|%{author} (Busy)')">
+ <template #author>{{ name }}</template>
+ </gl-sprintf>
+ <template v-else>{{ name }}</template>
+ </span>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
index f121036540e..37bde089de8 100644
--- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
+++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
@@ -6,7 +6,7 @@ import {
GlDeprecatedSkeletonLoading as GlSkeletonLoading,
GlIcon,
} from '@gitlab/ui';
-import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue';
+import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
import { glEmojiTag } from '../../../emoji';
import UserAvatarImage from '../user_avatar/user_avatar_image.vue';
@@ -26,7 +26,7 @@ export default {
GlPopover,
GlSkeletonLoading,
UserAvatarImage,
- UserAvailabilityStatus,
+ UserNameWithStatus,
},
props: {
target: {
@@ -66,7 +66,7 @@ export default {
);
},
availabilityStatus() {
- return this.user?.status?.availability || null;
+ return this.user?.status?.availability || '';
},
},
};
@@ -93,11 +93,7 @@ export default {
<template v-else>
<div class="gl-mb-3">
<h5 class="gl-m-0">
- {{ user.name }}
- <user-availability-status
- v-if="availabilityStatus"
- :availability="availabilityStatus"
- />
+ <user-name-with-status :name="user.name" :availability="availabilityStatus" />
</h5>
<span class="gl-text-gray-500">@{{ user.username }}</span>
</div>
diff --git a/app/models/packages/composer/cache_file.rb b/app/models/packages/composer/cache_file.rb
index 92659644b06..ecd7596b989 100644
--- a/app/models/packages/composer/cache_file.rb
+++ b/app/models/packages/composer/cache_file.rb
@@ -16,6 +16,8 @@ module Packages
scope :with_namespace, ->(namespace) { where(namespace: namespace) }
scope :with_sha, ->(sha) { where(file_sha256: sha) }
+ scope :expired, -> { where("delete_at <= ?", Time.current) }
+ scope :without_namespace, -> { where(namespace_id: nil) }
end
end
end
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
index a31f5e9056e..dbbaefb2b2f 100644
--- a/app/services/ci/pipeline_trigger_service.rb
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -17,6 +17,9 @@ module Ci
private
+ PAYLOAD_VARIABLE_KEY = 'TRIGGER_PAYLOAD'
+ PAYLOAD_VARIABLE_HIDDEN_PARAMS = %i(token).freeze
+
def create_pipeline_from_trigger(trigger)
# this check is to not leak the presence of the project if user cannot read it
return unless trigger.project == project
@@ -70,9 +73,23 @@ module Ci
end
def variables
+ if ::Feature.enabled?(:ci_trigger_payload_into_pipeline, project, default_enabled: :yaml)
+ param_variables + [payload_variable]
+ else
+ param_variables
+ end
+ end
+
+ def param_variables
params[:variables].to_h.map do |key, value|
{ key: key, value: value }
end
end
+
+ def payload_variable
+ { key: PAYLOAD_VARIABLE_KEY,
+ value: params.except(*PAYLOAD_VARIABLE_HIDDEN_PARAMS).to_json,
+ variable_type: :file }
+ end
end
end
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 6d4e6de30d9..59ab0a4d05b 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -267,6 +267,14 @@
:weight: 1
:idempotent:
:tags: []
+- :name: cronjob:packages_composer_cache_cleanup
+ :feature_category: :package_registry
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: cronjob:pages_domain_removal_cron
:feature_category: :pages
:has_external_dependencies:
diff --git a/app/workers/packages/composer/cache_cleanup_worker.rb b/app/workers/packages/composer/cache_cleanup_worker.rb
new file mode 100644
index 00000000000..638e50e18c4
--- /dev/null
+++ b/app/workers/packages/composer/cache_cleanup_worker.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Packages
+ module Composer
+ class CacheCleanupWorker
+ include ApplicationWorker
+ include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
+
+ feature_category :package_registry
+
+ idempotent!
+
+ def perform
+ ::Packages::Composer::CacheFile.without_namespace.find_in_batches do |cache_files|
+ cache_files.each(&:destroy)
+ rescue ActiveRecord::RecordNotFound
+ # ignore. likely due to object already being deleted.
+ end
+
+ ::Packages::Composer::CacheFile.expired.find_in_batches do |cache_files|
+ cache_files.each(&:destroy)
+ rescue ActiveRecord::RecordNotFound
+ # ignore. likely due to object already being deleted.
+ end
+ rescue => e
+ Gitlab::ErrorTracking.log_exception(e)
+ end
+ end
+ end
+end
diff --git a/changelogs/unreleased/263452-sidebar-display-busy-status.yml b/changelogs/unreleased/263452-sidebar-display-busy-status.yml
new file mode 100644
index 00000000000..db1ecfa7590
--- /dev/null
+++ b/changelogs/unreleased/263452-sidebar-display-busy-status.yml
@@ -0,0 +1,5 @@
+---
+title: Display the user busy status in the MR sidebar
+merge_request: 47769
+author:
+type: changed
diff --git a/changelogs/unreleased/292820-update-user-api-docs-and-enable-ff.yml b/changelogs/unreleased/292820-update-user-api-docs-and-enable-ff.yml
new file mode 100644
index 00000000000..7c8824ed38a
--- /dev/null
+++ b/changelogs/unreleased/292820-update-user-api-docs-and-enable-ff.yml
@@ -0,0 +1,5 @@
+---
+title: Schedule user status cleanup after a given time interval via the REST API
+merge_request: 54207
+author:
+type: added
diff --git a/changelogs/unreleased/299403-usage_data_unique_users_committing_ciconfigfile.yml b/changelogs/unreleased/299403-usage_data_unique_users_committing_ciconfigfile.yml
new file mode 100644
index 00000000000..f62b78d1b8d
--- /dev/null
+++ b/changelogs/unreleased/299403-usage_data_unique_users_committing_ciconfigfile.yml
@@ -0,0 +1,5 @@
+---
+title: Add tracking to measure the number of unique users committing CI config
+merge_request: 54192
+author:
+type: other
diff --git a/changelogs/unreleased/300524-create-worker-to-clean-up-composer-cache-pages.yml b/changelogs/unreleased/300524-create-worker-to-clean-up-composer-cache-pages.yml
new file mode 100644
index 00000000000..4acbc9dae00
--- /dev/null
+++ b/changelogs/unreleased/300524-create-worker-to-clean-up-composer-cache-pages.yml
@@ -0,0 +1,5 @@
+---
+title: Cleanup composer cache pages
+merge_request: 53005
+author:
+type: other
diff --git a/changelogs/unreleased/feature-flag-enable-ci-config-merged-tab.yml b/changelogs/unreleased/feature-flag-enable-ci-config-merged-tab.yml
new file mode 100644
index 00000000000..3febd18659a
--- /dev/null
+++ b/changelogs/unreleased/feature-flag-enable-ci-config-merged-tab.yml
@@ -0,0 +1,5 @@
+---
+title: Add merged Yaml tab feature to Pipeline Editor
+merge_request: 54223
+author:
+type: added
diff --git a/config/feature_flags/development/ci_config_merged_tab.yml b/config/feature_flags/development/ci_config_merged_tab.yml
index 5cee3429af8..8ccdf105951 100644
--- a/config/feature_flags/development/ci_config_merged_tab.yml
+++ b/config/feature_flags/development/ci_config_merged_tab.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/301103
milestone: '13.9'
type: development
group: group::pipeline authoring
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/ci_trigger_payload_into_pipeline.yml b/config/feature_flags/development/ci_trigger_payload_into_pipeline.yml
new file mode 100644
index 00000000000..93dd7113f2a
--- /dev/null
+++ b/config/feature_flags/development/ci_trigger_payload_into_pipeline.yml
@@ -0,0 +1,8 @@
+---
+name: ci_trigger_payload_into_pipeline
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53837
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321027
+milestone: '13.9'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/clear_status_with_quick_options.yml b/config/feature_flags/development/clear_status_with_quick_options.yml
index b4ce306fae0..3a8af1569c2 100644
--- a/config/feature_flags/development/clear_status_with_quick_options.yml
+++ b/config/feature_flags/development/clear_status_with_quick_options.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/320777
milestone: '13.9'
type: development
group: group::optimize
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/group_wiki_import_export.yml b/config/feature_flags/development/group_wiki_import_export.yml
deleted file mode 100644
index f9806ef0c28..00000000000
--- a/config/feature_flags/development/group_wiki_import_export.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: group_wiki_import_export
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51873
-rollout_issue_url:
-milestone: '13.9'
-type: development
-group: group::editor
-default_enabled: false
diff --git a/config/feature_flags/development/usage_data_unique_users_committing_ciconfigfile.yml b/config/feature_flags/development/usage_data_unique_users_committing_ciconfigfile.yml
index ebe8125aa65..1d3092ed615 100644
--- a/config/feature_flags/development/usage_data_unique_users_committing_ciconfigfile.yml
+++ b/config/feature_flags/development/usage_data_unique_users_committing_ciconfigfile.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/299403
milestone: '13.9'
type: development
group: group::pipeline authoring
-default_enabled: false
+default_enabled: true
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 557ad272d72..c478123f4eb 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -454,6 +454,9 @@ Settings.cron_jobs['personal_access_tokens_expired_notification_worker']['job_cl
Settings.cron_jobs['repository_archive_cache_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['repository_archive_cache_worker']['cron'] ||= '0 * * * *'
Settings.cron_jobs['repository_archive_cache_worker']['job_class'] = 'RepositoryArchiveCacheWorker'
+Settings.cron_jobs['packages_composer_cache_cleanup_worker'] ||= Settingslogic.new({})
+Settings.cron_jobs['packages_composer_cache_cleanup_worker']['cron'] ||= '30 * * * *'
+Settings.cron_jobs['packages_composer_cache_cleanup_worker']['job_class'] = 'Packages::Composer::CacheCleanupWorker'
Settings.cron_jobs['import_export_project_cleanup_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['import_export_project_cleanup_worker']['cron'] ||= '0 * * * *'
Settings.cron_jobs['import_export_project_cleanup_worker']['job_class'] = 'ImportExportProjectCleanupWorker'
diff --git a/config/metrics/counts_28d/deployments.yml b/config/metrics/counts_28d/20210201124930_deployments.yml
index 2f07db2b1d1..e03785d62ba 100644
--- a/config/metrics/counts_28d/deployments.yml
+++ b/config/metrics/counts_28d/20210201124930_deployments.yml
@@ -1,12 +1,20 @@
+---
key_path: counts_monthly.deployments
description: Total deployments count for recent 28 days
-value_type: integer
+product_section: ops
product_stage: release
+product_group: group::ops release
+product_category:
+value_type: number
status: data_available
milestone: 13.2
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35493
-product_group: 'group::ops release'
time_frame: 28d
data_source: database
-distribution: [ee, ce]
-tier: ['free', 'starter', 'premium', 'ultimate', 'bronze', 'silver', 'gold']
+distribution:
+- ee
+- ce
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/g_project_management_issue_title_changed_weekly.yml b/config/metrics/counts_7d/20210201124931_g_project_management_issue_title_changed_weekly.yml
index 3f67789131f..07b20fd2938 100644
--- a/config/metrics/counts_7d/g_project_management_issue_title_changed_weekly.yml
+++ b/config/metrics/counts_7d/20210201124931_g_project_management_issue_title_changed_weekly.yml
@@ -1,13 +1,20 @@
+---
key_path: redis_hll_counters.issues_edit.g_project_management_issue_title_changed_weekly
description: Distinct users count that changed issue title in a group for last recent week
-value_type: integer
-product_category: issue_tracking
product_stage: plan
+product_group: group::project management
+product_category: issue_tracking
+value_type: number
status: data_available
milestone: 13.6
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/229918
-product_group: 'group::project management'
time_frame: 7d
data_source: redis_hll
-distribution: [ee, ce]
-tier: ['free', 'starter', 'premium', 'ultimate', 'bronze', 'silver', 'gold']
+distribution:
+- ee
+- ce
+tier:
+- free
+- premium
+- ultimate
+
diff --git a/config/metrics/counts_all/deployments.yml b/config/metrics/counts_all/20210201124934_deployments.yml
index a6c25cda085..8b93eaf989d 100644
--- a/config/metrics/counts_all/deployments.yml
+++ b/config/metrics/counts_all/20210201124934_deployments.yml
@@ -1,12 +1,19 @@
+---
key_path: counts.deployments
description: Total deployments count
-value_type: integer
+product_section: ops
product_stage: release
+product_group: group::ops release
+value_type: number
status: data_available
milestone: 8.12
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/735
-product_group: 'group::ops release'
time_frame: all
data_source: database
-distribution: [ee, ce]
-tier: ['free', 'starter', 'premium', 'ultimate', 'bronze', 'silver', 'gold']
+distribution:
+- ee
+- ce
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20210204124930_servers.yml b/config/metrics/counts_all/20210204124930_servers.yml
new file mode 100644
index 00000000000..3103797ccce
--- /dev/null
+++ b/config/metrics/counts_all/20210204124930_servers.yml
@@ -0,0 +1,14 @@
+---
+key_path: gitaly.servers
+description: Total Gitalty Servers
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: number
+status: data_available
+time_frame: all
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/counts_all/20210204124932_clusters.yml b/config/metrics/counts_all/20210204124932_clusters.yml
new file mode 100644
index 00000000000..1bb86905502
--- /dev/null
+++ b/config/metrics/counts_all/20210204124932_clusters.yml
@@ -0,0 +1,14 @@
+---
+key_path: gitaly.clusters
+description: Total GitLab Managed clusters both enabled and disabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: number
+status: data_available
+time_frame: all
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/license/recorded_at.yml b/config/metrics/license/20210201124932_recorded_at.yml
index 529e700b684..af60dcb329e 100644
--- a/config/metrics/license/recorded_at.yml
+++ b/config/metrics/license/20210201124932_recorded_at.yml
@@ -1,13 +1,20 @@
+---
key_path: recorded_at
description: When the Usage Ping computation was started
-value_type: string
-product_category: collection
+product_section: growth
product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: string
status: data_available
milestone: 8.10
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/557
-product_group: group::product intelligence
time_frame: none
data_source: ruby
-distribution: [ee, ce]
-tier: ['free', 'starter', 'premium', 'ultimate', 'bronze', 'silver', 'gold']
+distribution:
+- ee
+- ce
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/license/uuid.yml b/config/metrics/license/20210201124933_uuid.yml
index 6ecf7f7ef11..6cd4ca58ef8 100644
--- a/config/metrics/license/uuid.yml
+++ b/config/metrics/license/20210201124933_uuid.yml
@@ -1,13 +1,20 @@
+---
key_path: uuid
description: GitLab instance unique identifier
-value_type: string
-product_category: collection
+product_section: growth
product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: string
status: data_available
milestone: 9.1
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1521
-product_group: group::product intelligence
time_frame: none
data_source: database
-distribution: [ee, ce]
-tier: ['free', 'starter', 'premium', 'ultimate', 'bronze', 'silver', 'gold']
+distribution:
+- ee
+- ce
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/license/20210204124827_hostname.yml b/config/metrics/license/20210204124827_hostname.yml
new file mode 100644
index 00000000000..6f878f04863
--- /dev/null
+++ b/config/metrics/license/20210204124827_hostname.yml
@@ -0,0 +1,19 @@
+---
+key_path: hostname
+description: Host name of GitLab instance
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: string
+status: data_available
+time_frame: none
+data_source:
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/license/20210204124829_active_user_count.yml b/config/metrics/license/20210204124829_active_user_count.yml
new file mode 100644
index 00000000000..21dacb43d92
--- /dev/null
+++ b/config/metrics/license/20210204124829_active_user_count.yml
@@ -0,0 +1,19 @@
+---
+key_path: active_user_count
+description: This is named the instance_user_count in the Versions application.
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: string
+status: data_available
+time_frame: none
+data_source: database
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/license/20210204124854_license_management_jobs.yml b/config/metrics/license/20210204124854_license_management_jobs.yml
new file mode 100644
index 00000000000..f89df165662
--- /dev/null
+++ b/config/metrics/license/20210204124854_license_management_jobs.yml
@@ -0,0 +1,16 @@
+---
+key_path: counts.license_management_jobs
+description: Name on the GitLab license
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: number
+status: data_available
+time_frame: none
+data_source: database
+distribution: []
+tier:
+- premium
+- ultimate
+skip_validation: true
diff --git a/config/metrics/license/20210204124926_license_trial_ends_on.yml b/config/metrics/license/20210204124926_license_trial_ends_on.yml
new file mode 100644
index 00000000000..6e78c6239e9
--- /dev/null
+++ b/config/metrics/license/20210204124926_license_trial_ends_on.yml
@@ -0,0 +1,14 @@
+---
+key_path: license_trial_ends_on
+description: Date the license ends on
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: string
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/license/20210204124928_version.yml b/config/metrics/license/20210204124928_version.yml
new file mode 100644
index 00000000000..a5e8acb1eaa
--- /dev/null
+++ b/config/metrics/license/20210204124928_version.yml
@@ -0,0 +1,14 @@
+---
+key_path: gitaly.version
+description: Version of Gitaly
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: string
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/license/20210204124936_version.yml b/config/metrics/license/20210204124936_version.yml
new file mode 100644
index 00000000000..f9c28d40d3d
--- /dev/null
+++ b/config/metrics/license/20210204124936_version.yml
@@ -0,0 +1,14 @@
+---
+key_path: gitlab_pages.version
+description: The version number of GitLab Pages
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: string
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/license/20210204124938_recording_ce_finished_at.yml b/config/metrics/license/20210204124938_recording_ce_finished_at.yml
new file mode 100644
index 00000000000..bc8a3e57b45
--- /dev/null
+++ b/config/metrics/license/20210204124938_recording_ce_finished_at.yml
@@ -0,0 +1,16 @@
+---
+key_path: recording_ce_finished_at
+description: When the core features were computed
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: string
+status: data_available
+time_frame: none
+data_source:
+distribution:
+- ce
+- ee
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/database_adapter.yml b/config/metrics/settings/20210201124935_database_adapter.yml
index 428559a05f0..020325995b1 100644
--- a/config/metrics/settings/database_adapter.yml
+++ b/config/metrics/settings/20210201124935_database_adapter.yml
@@ -1,11 +1,18 @@
+---
key_path: database.adapter
description: This metric only returns a value of PostgreSQL in supported versions of GitLab. It could be removed from the usage ping. Historically MySQL was also supported.
-value_type: string
+product_section: enablement
+product_stage: enablement
+product_group: group::enablement distribution
product_category: collection
-product_stage: growth
+value_type: string
status: data_available
-product_group: group::enablement distribution
time_frame: none
data_source: database
-distribution: [ee, ce]
-tier: ['free', 'starter', 'premium', 'ultimate', 'bronze', 'silver', 'gold']
+distribution:
+- ee
+- ce
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/settings/20210204124856_instance_auto_devops_enabled.yml b/config/metrics/settings/20210204124856_instance_auto_devops_enabled.yml
new file mode 100644
index 00000000000..41f6d432b78
--- /dev/null
+++ b/config/metrics/settings/20210204124856_instance_auto_devops_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: instance_auto_devops_enabled
+description: Whether auto DevOps is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124858_container_registry_enabled.yml b/config/metrics/settings/20210204124858_container_registry_enabled.yml
new file mode 100644
index 00000000000..53567998a76
--- /dev/null
+++ b/config/metrics/settings/20210204124858_container_registry_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: container_registry_enabled
+description: Whether container registry is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124900_dependency_proxy_enabled.yml b/config/metrics/settings/20210204124900_dependency_proxy_enabled.yml
new file mode 100644
index 00000000000..cecb9035dc6
--- /dev/null
+++ b/config/metrics/settings/20210204124900_dependency_proxy_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: dependency_proxy_enabled
+description: Whether dependency proxy is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml b/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml
new file mode 100644
index 00000000000..5cdb62237e4
--- /dev/null
+++ b/config/metrics/settings/20210204124902_gitlab_shared_runners_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: gitlab_shared_runners_enabled
+description: Whether shared runners is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124904_gravatar_enabled.yml b/config/metrics/settings/20210204124904_gravatar_enabled.yml
new file mode 100644
index 00000000000..7102c96332a
--- /dev/null
+++ b/config/metrics/settings/20210204124904_gravatar_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: gravatar_enabled
+description: Whether gravatar is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124906_ldap_enabled.yml b/config/metrics/settings/20210204124906_ldap_enabled.yml
new file mode 100644
index 00000000000..b0bdeded7d9
--- /dev/null
+++ b/config/metrics/settings/20210204124906_ldap_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: ldap_enabled
+description: Whether LDAP is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124908_mattermost_enabled.yml b/config/metrics/settings/20210204124908_mattermost_enabled.yml
new file mode 100644
index 00000000000..7082b6c4ec5
--- /dev/null
+++ b/config/metrics/settings/20210204124908_mattermost_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: mattermost_enabled
+description: Whether Mattermost is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124910_omniauth_enabled.yml b/config/metrics/settings/20210204124910_omniauth_enabled.yml
new file mode 100644
index 00000000000..10483bd977b
--- /dev/null
+++ b/config/metrics/settings/20210204124910_omniauth_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: omniauth_enabled
+description: Whether OmniAuth is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124912_prometheus_enabled.yml b/config/metrics/settings/20210204124912_prometheus_enabled.yml
new file mode 100644
index 00000000000..0e6199e9976
--- /dev/null
+++ b/config/metrics/settings/20210204124912_prometheus_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: prometheus_enabled
+description: Whether the bundled Prometheus is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124914_prometheus_metrics_enabled.yml b/config/metrics/settings/20210204124914_prometheus_metrics_enabled.yml
new file mode 100644
index 00000000000..50b08c15919
--- /dev/null
+++ b/config/metrics/settings/20210204124914_prometheus_metrics_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: prometheus_metrics_enabled
+description: Whether Prometheus Metrics endpoint is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124916_reply_by_email_enabled.yml b/config/metrics/settings/20210204124916_reply_by_email_enabled.yml
new file mode 100644
index 00000000000..82defcf4014
--- /dev/null
+++ b/config/metrics/settings/20210204124916_reply_by_email_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: reply_by_email_enabled
+description: Whether incoming email is setup
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124918_signup_enabled.yml b/config/metrics/settings/20210204124918_signup_enabled.yml
new file mode 100644
index 00000000000..49d997bd2a8
--- /dev/null
+++ b/config/metrics/settings/20210204124918_signup_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: signup_enabled
+description: Whether public signup is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124920_web_ide_clientside_preview_enabled.yml b/config/metrics/settings/20210204124920_web_ide_clientside_preview_enabled.yml
new file mode 100644
index 00000000000..d3eb20e93fd
--- /dev/null
+++ b/config/metrics/settings/20210204124920_web_ide_clientside_preview_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: web_ide_clientside_preview_enabled
+description: Whether web ide clientside preview is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124922_grafana_link_enabled.yml b/config/metrics/settings/20210204124922_grafana_link_enabled.yml
new file mode 100644
index 00000000000..a78936d3324
--- /dev/null
+++ b/config/metrics/settings/20210204124922_grafana_link_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: grafana_link_enabled
+description: Whether Grafana is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124924_elasticsearch_enabled.yml b/config/metrics/settings/20210204124924_elasticsearch_enabled.yml
new file mode 100644
index 00000000000..4b7d560b4ef
--- /dev/null
+++ b/config/metrics/settings/20210204124924_elasticsearch_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: elasticsearch_enabled
+description: Whether Elasticsearch is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/config/metrics/settings/20210204124934_enabled.yml b/config/metrics/settings/20210204124934_enabled.yml
new file mode 100644
index 00000000000..3f4c8653dd1
--- /dev/null
+++ b/config/metrics/settings/20210204124934_enabled.yml
@@ -0,0 +1,14 @@
+---
+key_path: gitlab_pages.enabled
+description: Whether GitLab Pages is enabled
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: boolean
+status: data_available
+time_frame: none
+data_source:
+distribution: []
+tier: []
+skip_validation: true
diff --git a/db/migrate/20210203222620_add_expired_index_to_composer_cache_files.rb b/db/migrate/20210203222620_add_expired_index_to_composer_cache_files.rb
new file mode 100644
index 00000000000..9c6a27812a5
--- /dev/null
+++ b/db/migrate/20210203222620_add_expired_index_to_composer_cache_files.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddExpiredIndexToComposerCacheFiles < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'composer_cache_files_index_on_deleted_at'
+
+ def up
+ add_concurrent_index :packages_composer_cache_files, [:delete_at, :id], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :packages_composer_cache_files, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20210203223551_add_orphan_index_to_composer_cache_files.rb b/db/migrate/20210203223551_add_orphan_index_to_composer_cache_files.rb
new file mode 100644
index 00000000000..e2853977e5f
--- /dev/null
+++ b/db/migrate/20210203223551_add_orphan_index_to_composer_cache_files.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddOrphanIndexToComposerCacheFiles < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_composer_cache_files_where_namespace_id_is_null'
+
+ def up
+ add_concurrent_index :packages_composer_cache_files, :id, name: INDEX_NAME, where: 'namespace_id IS NULL'
+ end
+
+ def down
+ remove_concurrent_index_by_name :packages_composer_cache_files, INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20210203222620 b/db/schema_migrations/20210203222620
new file mode 100644
index 00000000000..2c8c3ffc111
--- /dev/null
+++ b/db/schema_migrations/20210203222620
@@ -0,0 +1 @@
+6d3250533b72c6aa307570d386725fa3ebe1ec49c36edc0f7d6dc8a1d5092826 \ No newline at end of file
diff --git a/db/schema_migrations/20210203223551 b/db/schema_migrations/20210203223551
new file mode 100644
index 00000000000..53b3a9f65aa
--- /dev/null
+++ b/db/schema_migrations/20210203223551
@@ -0,0 +1 @@
+4ccf450bbc9061edae81cabcfafd9360f1f57cfd25af3ad016fbbb344f9fe694 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 5d27baaab02..386fbe4ee3f 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -21202,6 +21202,8 @@ CREATE INDEX code_owner_approval_required ON protected_branches USING btree (pro
CREATE INDEX commit_id_and_note_id_index ON commit_user_mentions USING btree (commit_id, note_id);
+CREATE INDEX composer_cache_files_index_on_deleted_at ON packages_composer_cache_files USING btree (delete_at, id);
+
CREATE UNIQUE INDEX design_management_designs_versions_uniqueness ON design_management_designs_versions USING btree (design_id, version_id);
CREATE INDEX design_user_mentions_on_design_id_and_note_id_index ON design_user_mentions USING btree (design_id, note_id);
@@ -21860,6 +21862,8 @@ CREATE INDEX index_clusters_on_user_id ON clusters USING btree (user_id);
CREATE UNIQUE INDEX index_commit_user_mentions_on_note_id ON commit_user_mentions USING btree (note_id);
+CREATE INDEX index_composer_cache_files_where_namespace_id_is_null ON packages_composer_cache_files USING btree (id) WHERE (namespace_id IS NULL);
+
CREATE INDEX index_container_expiration_policies_on_next_run_at_and_enabled ON container_expiration_policies USING btree (next_run_at, enabled);
CREATE INDEX index_container_repositories_on_project_id ON container_repositories USING btree (project_id);
diff --git a/doc/administration/maintenance_mode/index.md b/doc/administration/maintenance_mode/index.md
index c80088a9dda..61d321f2176 100644
--- a/doc/administration/maintenance_mode/index.md
+++ b/doc/administration/maintenance_mode/index.md
@@ -4,24 +4,24 @@ group: Geo
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# GitLab in maintenance mode **(PREMIUM SELF)**
+# GitLab Maintenance Mode **(PREMIUM SELF)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2149) in GitLab Premium 13.9.
-Maintenance mode allows administrators to reduce write operations to a minimum while maintenance tasks are performed. The main goal is to block all external actions that change the internal state, including the PostgreSQL database, but especially files, Git repositories, Container repositories, etc.
+Maintenance Mode allows administrators to reduce write operations to a minimum while maintenance tasks are performed. The main goal is to block all external actions that change the internal state, including the PostgreSQL database, but especially files, Git repositories, Container repositories, etc.
-Once maintenance mode is enabled, in-progress actions will finish relatively quickly since no new actions are coming in, and internal state changes will be minimal.
+Once Maintenance Mode is enabled, in-progress actions will finish relatively quickly since no new actions are coming in, and internal state changes will be minimal.
In that state, various maintenance tasks are easier, and services can be stopped completely or be
further degraded for a much shorter period of time than might otherwise be needed, for example stopping cron jobs and draining queues should be fairly quick.
-Maintenance mode allows most external actions that do not change internal state. On a high-level, HTTP POST, PUT, PATCH, and DELETE requests are blocked and a detailed overview of [how special cases are handled](#rest-api) is available.
+Maintenance Mode allows most external actions that do not change internal state. On a high-level, HTTP POST, PUT, PATCH, and DELETE requests are blocked and a detailed overview of [how special cases are handled](#rest-api) is available.
-## Enable maintenance mode
+## Enable Maintenance Mode
-There are three ways to enable maintenance mode as an administrator:
+There are three ways to enable Maintenance Mode as an administrator:
- **Web UI**:
- 1. Go to **Admin Area > Settings > General**, expand **Maintenance mode**, and toggle **Enable maintenance mode**.
+ 1. Go to **Admin Area > Settings > General**, expand **Maintenance Mode**, and toggle **Enable Maintenance Mode**.
You can optionally add a message for the banner as well.
1. Click **Save** for the changes to take effect.
@@ -39,12 +39,12 @@ There are three ways to enable maintenance mode as an administrator:
::Gitlab::CurrentSettings.update_attributes!(maintenance_mode_message: "New message")
```
-## Disable maintenance mode
+## Disable Maintenance Mode
-There are three ways to disable maintenance mode:
+There are three ways to disable Maintenance Mode:
- **Web UI**:
- 1. Go to **Admin Area > Settings > General**, expand **Maintenance mode**, and toggle **Enable maintenance mode**.
+ 1. Go to **Admin Area > Settings > General**, expand **Maintenance Mode**, and toggle **Enable Maintenance Mode**.
1. Click **Save** for the changes to take effect.
@@ -60,14 +60,14 @@ There are three ways to disable maintenance mode:
::Gitlab::CurrentSettings.update_attributes!(maintenance_mode: false)
```
-## Behavior of GitLab features in maintenance mode
+## Behavior of GitLab features in Maintenance Mode
-When maintenance mode is enabled, a banner is displayed at the top of the page.
+When Maintenance Mode is enabled, a banner is displayed at the top of the page.
The banner can be customized with a specific message.
An error is displayed when a user tries to perform a write operation that isn't allowed.
-![Maintenance mode banner and error message](maintenance_mode_error_message.png)
+![Maintenance Mode banner and error message](maintenance_mode_error_message.png)
NOTE:
In some cases, the visual feedback from an action could be misleading, for example when starring a project, the **Star** button changes to show the **Unstar** action, however, this is only the frontend update, and it doesn't take into account the failed status of the POST request. These visual bugs are to be fixed [in follow-up iterations](https://gitlab.com/gitlab-org/gitlab/-/issues/295197).
@@ -75,7 +75,7 @@ In some cases, the visual feedback from an action could be misleading, for examp
### Admin functions
Systems administrators can edit the application settings. This will allow
-them to disable maintenance mode after it's been enabled.
+them to disable Maintenance Mode after it's been enabled.
### Authentication
@@ -127,24 +127,25 @@ For most JSON requests, POST, PUT, PATCH, and DELETE are blocked, and the API re
### Continuous Integration
-In maintenance mode:
+- No new jobs or pipelines start, scheduled or otherwise.
+- Jobs that were already running continue to have a `running` status in the GitLab UI,
+ even if they finish running on the GitLab Runner.
+- Jobs in the `running` state for longer than the project's time limit do not time out.
+- Pipelines cannot be started, retried or canceled. No new jobs can be created either.
-- No new jobs or pipelines, scheduled or otherwise, will start in maintenance mode.
-- Those jobs that were already running, will continue to show status as 'running' in the Web UI, even if they finish running on GitLab Runner.
-**Note** It is recommended that you restart already running pipelines after maintenance mode is turned off.
-- If the job has been in 'running' state for longer than the project's time limit,
- it will **not** time out.
-- Pipelines cannot be started, retried or canceled in maintenance mode.
- No new jobs can be created either.
+After Maintenance Mode is disabled, new jobs are picked up again. Jobs that were
+in the `running` state before enabling Maintenance Mode resume and their logs start
+updating again.
-Once maintenance mode is disabled, new jobs are picked up again. The jobs that were in the running state before enabling maintenance mode, will resume, and their logs will start getting updated again.
+NOTE:
+It is recommended that you restart previously `running` pipelines after Maintenance Mode
+is turned off.
### Deployments
Deployments won't go through because pipelines will be unfinished.
-It is recommended to disable auto deploys during maintenance mode, and enable
-them once maintenance mode is disabled.
+It is recommended to disable auto deploys during Maintenance Mode, and enable them once it is disabled.
#### Terraform integration
@@ -178,9 +179,9 @@ You can monitor queues and disable jobs in **Admin Area > Monitoring > Backgroun
### Geo secondaries
-When primary is in maintenance mode, secondary will also automatically go into maintenance mode.
+When primary is in Maintenance Mode, secondary will also automatically go into Maintenance Mode.
-It is important that you do not disable replication before enabling maintenance mode.
+It is important that you do not disable replication before enabling Maintenance Mode.
Replication and verification will continue to work but proxied Git pushes to primary will not work.
@@ -198,7 +199,7 @@ SAST and Secret Detection cannot be initiated because they depend on passing CI
In the use case of [a planned failover](../geo/disaster_recovery/planned_failover.md), a few writes in the primary database are acceptable, since they will be replicated quickly and are not significant in number.
-For the same reason we don't automatically block background jobs when maintenance mode is enabled.
+For the same reason we don't automatically block background jobs when Maintenance Mode is enabled.
The resulting database writes are acceptable. Here, the trade-off is between more service degradation and the completion of replication.
diff --git a/doc/api/users.md b/doc/api/users.md
index 60db3be5e88..76b0bb3491c 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -628,7 +628,8 @@ Example response:
{
"emoji":"coffee",
"message":"I crave coffee :coffee:",
- "message_html": "I crave coffee <gl-emoji title=\"hot beverage\" data-name=\"coffee\" data-unicode-version=\"4.0\">☕</gl-emoji>"
+ "message_html": "I crave coffee <gl-emoji title=\"hot beverage\" data-name=\"coffee\" data-unicode-version=\"4.0\">☕</gl-emoji>",
+ "clear_status_at": null
}
```
@@ -654,7 +655,8 @@ Example response:
{
"emoji":"coffee",
"message":"I crave coffee :coffee:",
- "message_html": "I crave coffee <gl-emoji title=\"hot beverage\" data-name=\"coffee\" data-unicode-version=\"4.0\">☕</gl-emoji>"
+ "message_html": "I crave coffee <gl-emoji title=\"hot beverage\" data-name=\"coffee\" data-unicode-version=\"4.0\">☕</gl-emoji>",
+ "clear_status_at": null
}
```
@@ -666,15 +668,16 @@ Set the status of the current user.
PUT /user/status
```
-| Attribute | Type | Required | Description |
-| --------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `emoji` | string | no | The name of the emoji to use as status. If omitted `speech_balloon` is used. Emoji name can be one of the specified names in the [Gemojione index](https://github.com/bonusly/gemojione/blob/master/config/index.json). |
-| `message` | string | no | The message to set as a status. It can also contain emoji codes. |
+| Attribute | Type | Required | Description |
+| -------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `emoji` | string | no | The name of the emoji to use as status. If omitted `speech_balloon` is used. Emoji name can be one of the specified names in the [Gemojione index](https://github.com/bonusly/gemojione/blob/master/config/index.json). |
+| `message` | string | no | The message to set as a status. It can also contain emoji codes. |
+| `clear_status_after` | string | no | Automatically clean up the status after a given time interval, allowed values: `30_minutes`, `3_hours`, `8_hours`, `1_day`, `3_days`, `7_days`, `30_days`
-When both parameters `emoji` and `message` are empty, the status is cleared.
+When both parameters `emoji` and `message` are empty, the status is cleared. When the `clear_status_after` parameter is missing from the request, the previously set value for `"clear_status_after` is cleared.
```shell
-curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --data "emoji=coffee" --data "message=I crave coffee" "https://gitlab.example.com/api/v4/user/status"
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --data "clear_status_after=1_day" --data "emoji=coffee" --data "message=I crave coffee" "https://gitlab.example.com/api/v4/user/status"
```
Example responses
@@ -683,7 +686,8 @@ Example responses
{
"emoji":"coffee",
"message":"I crave coffee",
- "message_html": "I crave coffee"
+ "message_html": "I crave coffee",
+ "clear_status_at":"2021-02-15T10:49:01.311Z"
}
```
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index 9a6c6c14ea2..b4cea48a362 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -185,6 +185,41 @@ You should pass `ref` as part of the URL, to take precedence over `ref` from
the webhook body that designates the branch ref that fired the trigger in the
source repository. Be sure to URL-encode `ref` if it contains slashes.
+### Using webhook payload in the triggered pipeline
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31197) in GitLab 13.9.
+> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
+> - It's disabled on GitLab.com.
+> - It's not recommended for production use.
+> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-the-trigger_payload-variable). **(FREE SELF)**
+
+WARNING:
+This feature might not be available to you. Check the **version history** note above for details.
+
+If you trigger a pipeline by using a webhook, you can access the webhook payload with
+the `TRIGGER_PAYLOAD` [predefined CI/CD variable](../variables/predefined_variables.md).
+The payload is exposed as a [file-type variable](../variables/README.md#custom-cicd-variables-of-type-file),
+so you can access the data with `cat $TRIGGER_PAYLOAD` or a similar command.
+
+#### Enable or disable the `TRIGGER_PAYLOAD` variable
+
+The `TRIGGER_PAYLOAD` CI/CD variable is under development and not ready for production use. It is
+deployed behind a feature flag that is **disabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
+can enable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:ci_trigger_payload_into_pipeline)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:ci_trigger_payload_into_pipeline)
+```
+
## Making use of trigger variables
You can pass any number of arbitrary variables in the trigger API call and they
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 57546f4c764..8d2df82a212 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -145,3 +145,4 @@ Kubernetes-specific variables are detailed in the
| `GITLAB_USER_ID` | 8.12 | all | The ID of the user who started the job. |
| `GITLAB_USER_LOGIN` | 10.0 | all | The login username of the user who started the job. |
| `GITLAB_USER_NAME` | 10.0 | all | The real name of the user who started the job. |
+| `TRIGGER_PAYLOAD` | 13.9 | all | This variable is available when a pipeline is [triggered with a webhook](../triggers/README.md#using-webhook-payload-in-the-triggered-pipeline) |
diff --git a/doc/development/maintenance_mode.md b/doc/development/maintenance_mode.md
index 64dc3a9f994..6b5a6045bb9 100644
--- a/doc/development/maintenance_mode.md
+++ b/doc/development/maintenance_mode.md
@@ -4,11 +4,11 @@ group: Geo
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Internal workings of maintenance mode **(PREMIUM SELF)**
+# Internal workings of GitLab Maintenance Mode **(PREMIUM SELF)**
-## Where is maintenance mode enforced?
+## Where is Maintenance Mode enforced?
-Maintenance mode **only** blocks writes from HTTP and SSH requests at the application level in a few key places within the rails application.
+GitLab Maintenance Mode **only** blocks writes from HTTP and SSH requests at the application level in a few key places within the rails application.
[Search the codebase for `maintenance_mode?`.](https://gitlab.com/search?utf8=%E2%9C%93&search=maintenance_mode%3F&group_id=9970&project_id=278964&scope=blobs&search_code=false&snippets=false&repository_ref=)
- [the read-only database method](https://gitlab.com/gitlab-org/gitlab/blob/2425e9de50c678413ceaad6ee3bf66f42b7e228c/ee/lib/ee/gitlab/database.rb#L13), which toggles special behavior when we are not allowed to write to the database. [Search the codebase for `Gitlab::Database.read_only?`.](https://gitlab.com/search?utf8=%E2%9C%93&search=Gitlab%3A%3ADatabase.read_only%3F&group_id=9970&project_id=278964&scope=blobs&search_code=false&snippets=false&repository_ref=)
diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md
index 42df05304b4..ca3e0f1aae6 100644
--- a/doc/development/usage_ping/dictionary.md
+++ b/doc/development/usage_ping/dictionary.md
@@ -29,130 +29,859 @@ The Metrics Dictionary is based on the following metrics definition YAML files:
Each table includes a `milestone`, which corresponds to the GitLab version when the metric
was released.
-## counts.deployments
+## `active_user_count`
+
+This is named the instance_user_count in the Versions application.
+
+| field | value |
+| --- | --- |
+| `key_path` | **`active_user_count`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | Database |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+| `skip_validation` | true |
+
+## `container_registry_enabled`
+
+Whether container registry is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`container_registry_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `counts.deployments`
Total deployments count
| field | value |
| --- | --- |
-| `key_path` | **counts.deployments** |
-| `value_type` | integer |
+| `key_path` | **`counts.deployments`** |
+| `product_section` | ops |
| `product_stage` | release |
+| `product_group` | `group::ops release` |
+| `value_type` | number |
| `status` | data_available |
| `milestone` | 8.12 |
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/735) |
-| `product_group` | `group::ops release` |
| `time_frame` | all |
| `data_source` | Database |
| `distribution` | ee, ce |
-| `tier` | free, starter, premium, ultimate, bronze, silver, gold |
+| `tier` | free, premium, ultimate |
-## counts.geo_nodes
+## `counts.geo_nodes`
Total number of sites in a Geo deployment
| field | value |
| --- | --- |
-| `key_path` | **counts.geo_nodes** |
-| `value_type` | integer |
-| `product_category` | disaster_recovery |
+| `key_path` | **`counts.geo_nodes`** |
+| `product_section` | enablement |
| `product_stage` | enablement |
+| `product_group` | `group::geo` |
+| `product_category` | disaster_recovery |
+| `value_type` | integer |
| `status` | data_available |
| `milestone` | 11.2 |
-| `product_group` | `group::geo` |
| `time_frame` | all |
| `data_source` | Database |
| `distribution` | ee |
| `tier` | premium, ultimate |
-## counts_monthly.deployments
+## `counts.license_management_jobs`
+
+Name on the GitLab license
+
+| field | value |
+| --- | --- |
+| `key_path` | **`counts.license_management_jobs`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | number |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | Database |
+| `distribution` | |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `counts_monthly.deployments`
Total deployments count for recent 28 days
| field | value |
| --- | --- |
-| `key_path` | **counts_monthly.deployments** |
-| `value_type` | integer |
+| `key_path` | **`counts_monthly.deployments`** |
+| `product_section` | ops |
| `product_stage` | release |
+| `product_group` | `group::ops release` |
+| `product_category` | |
+| `value_type` | number |
| `status` | data_available |
| `milestone` | 13.2 |
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35493) |
-| `product_group` | `group::ops release` |
| `time_frame` | 28d |
| `data_source` | Database |
| `distribution` | ee, ce |
-| `tier` | free, starter, premium, ultimate, bronze, silver, gold |
+| `tier` | free, premium, ultimate |
-## database.adapter
+## `database.adapter`
This metric only returns a value of PostgreSQL in supported versions of GitLab. It could be removed from the usage ping. Historically MySQL was also supported.
| field | value |
| --- | --- |
-| `key_path` | **database.adapter** |
+| `key_path` | **`database.adapter`** |
+| `product_section` | enablement |
+| `product_stage` | enablement |
+| `product_group` | `group::enablement distribution` |
+| `product_category` | collection |
| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | Database |
+| `distribution` | ee, ce |
+| `tier` | free, premium, ultimate |
+
+## `dependency_proxy_enabled`
+
+Whether dependency proxy is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`dependency_proxy_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `elasticsearch_enabled`
+
+Whether Elasticsearch is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`elasticsearch_enabled`** |
+| `product_section` | growth |
| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `gitaly.clusters`
+
+Total GitLab Managed clusters both enabled and disabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`gitaly.clusters`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | number |
+| `status` | data_available |
+| `time_frame` | all |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `gitaly.servers`
+
+Total Gitalty Servers
+
+| field | value |
+| --- | --- |
+| `key_path` | **`gitaly.servers`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | number |
+| `status` | data_available |
+| `time_frame` | all |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `gitaly.version`
+
+Version of Gitaly
+
+| field | value |
+| --- | --- |
+| `key_path` | **`gitaly.version`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `gitlab_pages.enabled`
+
+Whether GitLab Pages is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`gitlab_pages.enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `gitlab_pages.version`
+
+The version number of GitLab Pages
+
+| field | value |
+| --- | --- |
+| `key_path` | **`gitlab_pages.version`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `gitlab_shared_runners_enabled`
+
+Whether shared runners is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`gitlab_shared_runners_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `grafana_link_enabled`
+
+Whether Grafana is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`grafana_link_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `gravatar_enabled`
+
+Whether gravatar is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`gravatar_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `historical_max_users`
+
+The maximum active user count. Active is defined in UsersStatistics model.
+
+| field | value |
+| --- | --- |
+| `key_path` | **`historical_max_users`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `hostname`
+
+Host name of GitLab instance
+
+| field | value |
+| --- | --- |
+| `key_path` | **`hostname`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ce, ee |
+| `tier` | free, premium, ultimate |
+| `skip_validation` | true |
+
+## `instance_auto_devops_enabled`
+
+Whether auto DevOps is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`instance_auto_devops_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `ldap_enabled`
+
+Whether LDAP is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`ldap_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `license_expires_at`
+
+The date the license ends
+
+| field | value |
+| --- | --- |
+| `key_path` | **`license_expires_at`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `license_id`
+
+The ID of the license
+
+| field | value |
+| --- | --- |
+| `key_path` | **`license_id`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `license_md5`
+
+The license key of the GitLab instance
+
+| field | value |
+| --- | --- |
+| `key_path` | **`license_md5`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | free, premium, ultimate |
+| `skip_validation` | true |
+
+## `license_plan`
+
+The plan of the GitLab license
+
+| field | value |
+| --- | --- |
+| `key_path` | **`license_plan`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `license_starts_at`
+
+The date the license starts
+
+| field | value |
+| --- | --- |
+| `key_path` | **`license_starts_at`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `license_subscription_id`
+
+Licese zuora_subscription_id
+
+| field | value |
+| --- | --- |
+| `key_path` | **`license_subscription_id`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `license_trial`
+
+Whether this is a trial license or not
+
+| field | value |
+| --- | --- |
+| `key_path` | **`license_trial`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `license_trial_ends_on`
+
+Date the license ends on
+
+| field | value |
+| --- | --- |
+| `key_path` | **`license_trial_ends_on`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `license_user_count`
+
+The number of users included in the license
+
+| field | value |
+| --- | --- |
+| `key_path` | **`license_user_count`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
| `status` | data_available |
-| `product_group` | `group::enablement distribution` |
| `time_frame` | none |
| `data_source` | Database |
-| `distribution` | ee, ce |
-| `tier` | free, starter, premium, ultimate, bronze, silver, gold |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
-## recorded_at
+## `licensee.Company`
-When the Usage Ping computation was started
+Company on the GitLab license
| field | value |
| --- | --- |
-| `key_path` | **recorded_at** |
+| `key_path` | **`licensee.Company`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `licensee.Email`
+
+Email on the GitLab license
+
+| field | value |
+| --- | --- |
+| `key_path` | **`licensee.Email`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `licensee.Name`
+
+Name on the GitLab license
+
+| field | value |
+| --- | --- |
+| `key_path` | **`licensee.Name`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | premium, ultimate |
+| `skip_validation` | true |
+
+## `mattermost_enabled`
+
+Whether Mattermost is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`mattermost_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `omniauth_enabled`
+
+Whether OmniAuth is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`omniauth_enabled`** |
+| `product_section` | growth |
| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `prometheus_enabled`
+
+Whether the bundled Prometheus is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`prometheus_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `prometheus_metrics_enabled`
+
+Whether Prometheus Metrics endpoint is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`prometheus_metrics_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `recorded_at`
+
+When the Usage Ping computation was started
+
+| field | value |
+| --- | --- |
+| `key_path` | **`recorded_at`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
| `status` | data_available |
| `milestone` | 8.1 |
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/557) |
-| `product_group` | `group::product intelligence` |
| `time_frame` | none |
| `data_source` | Ruby |
| `distribution` | ee, ce |
-| `tier` | free, starter, premium, ultimate, bronze, silver, gold |
+| `tier` | free, premium, ultimate |
+
+## `recording_ce_finished_at`
+
+When the core features were computed
+
+| field | value |
+| --- | --- |
+| `key_path` | **`recording_ce_finished_at`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ce, ee |
+| `tier` | |
+| `skip_validation` | true |
-## redis_hll_counters.issues_edit.g_project_management_issue_title_changed_weekly
+## `recording_ee_finished_at`
+
+When the EE-specific features were computed
+
+| field | value |
+| --- | --- |
+| `key_path` | **`recording_ee_finished_at`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | ee |
+| `tier` | |
+| `skip_validation` | true |
+
+## `redis_hll_counters.issues_edit.g_project_management_issue_title_changed_weekly`
Distinct users count that changed issue title in a group for last recent week
| field | value |
| --- | --- |
-| `key_path` | **redis_hll_counters.issues_edit.g_project_management_issue_title_changed_weekly** |
-| `value_type` | integer |
-| `product_category` | issue_tracking |
+| `key_path` | **`redis_hll_counters.issues_edit.g_project_management_issue_title_changed_weekly`** |
| `product_stage` | plan |
+| `product_group` | `group::project management` |
+| `product_category` | issue_tracking |
+| `value_type` | number |
| `status` | data_available |
| `milestone` | 13.6 |
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/issues/229918) |
-| `product_group` | `group::project management` |
| `time_frame` | 7d |
| `data_source` | Redis_hll |
| `distribution` | ee, ce |
-| `tier` | free, starter, premium, ultimate, bronze, silver, gold |
+| `tier` | free, premium, ultimate |
-## uuid
+## `reply_by_email_enabled`
-GitLab instance unique identifier
+Whether incoming email is setup
| field | value |
| --- | --- |
-| `key_path` | **uuid** |
-| `value_type` | string |
+| `key_path` | **`reply_by_email_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `signup_enabled`
+
+Whether public signup is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`signup_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
+
+## `uuid`
+
+GitLab instance unique identifier
+
+| field | value |
+| --- | --- |
+| `key_path` | **`uuid`** |
+| `product_section` | growth |
| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | string |
| `status` | data_available |
| `milestone` | 9.1 |
| `introduced_by_url` | [Introduced by](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1521) |
-| `product_group` | `group::product intelligence` |
| `time_frame` | none |
| `data_source` | Database |
| `distribution` | ee, ce |
-| `tier` | free, starter, premium, ultimate, bronze, silver, gold |
+| `tier` | free, premium, ultimate |
+
+## `web_ide_clientside_preview_enabled`
+
+Whether web ide clientside preview is enabled
+
+| field | value |
+| --- | --- |
+| `key_path` | **`web_ide_clientside_preview_enabled`** |
+| `product_section` | growth |
+| `product_stage` | growth |
+| `product_group` | `group::product intelligence` |
+| `product_category` | collection |
+| `value_type` | boolean |
+| `status` | data_available |
+| `time_frame` | none |
+| `data_source` | |
+| `distribution` | |
+| `tier` | |
+| `skip_validation` | true |
diff --git a/doc/development/usage_ping/metrics_dictionary.md b/doc/development/usage_ping/metrics_dictionary.md
index 7a541c55f90..406a223b204 100644
--- a/doc/development/usage_ping/metrics_dictionary.md
+++ b/doc/development/usage_ping/metrics_dictionary.md
@@ -59,8 +59,13 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1521
product_group: group::product intelligence
time_frame: none
data_source: database
-distribution: [ee, ce]
-tier: ['free', 'starter', 'premium', 'ultimate', 'bronze', 'silver', 'gold']
+distribution:
+- ee
+- ce
+tier:
+- free
+- premium
+- ultimate
```
## Create a new metric definition
diff --git a/doc/user/project/autocomplete_characters.md b/doc/user/project/autocomplete_characters.md
index 0d234aff643..8d8ff942d05 100644
--- a/doc/user/project/autocomplete_characters.md
+++ b/doc/user/project/autocomplete_characters.md
@@ -3,11 +3,13 @@ stage: Create
group: Source Code
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
type: reference
-description: "Autocomplete chars in Markdown fields."
+description: "Autocomplete characters in Markdown fields."
---
# Autocomplete characters **(FREE)**
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/36705) in GitLab 13.9: you can search using the full name in user autocomplete.
+
The autocomplete characters provide a quick way of entering field values into
Markdown fields. When you start typing a word in a Markdown field with one of
the following characters, GitLab progressively autocompletes against a set of
@@ -58,3 +60,6 @@ popup now only includes users where `le` appears in their username, or a word in
their name.
![Popup list which includes users whose username or name contains the string](img/autocomplete_characters_example2_v12_0.png)
+
+You can also search across the full name to find a user.
+To find `Rosy Grant`, even if their username is for example `hunter2`, you can type their full name without spaces like `@rosygrant`.
diff --git a/generator_templates/usage_metric_definition/metric_definition.yml b/generator_templates/usage_metric_definition/metric_definition.yml
index a2550c3bf66..abe9a266dd6 100644
--- a/generator_templates/usage_metric_definition/metric_definition.yml
+++ b/generator_templates/usage_metric_definition/metric_definition.yml
@@ -1,3 +1,4 @@
+---
# See Usage Ping metrics dictionary docs https://docs.gitlab.com/ee/development/usage_ping/metrics_dictionary.html
key_path: <%= key_path %>
description:
@@ -7,10 +8,14 @@ product_group:
product_category:
value_type: <%= value_type %>
status: implemented
-milestone:
+milestone: <%= milestone %>
introduced_by_url:
time_frame: <%= time_frame %>
data_source:
-distribution: <%= distribution %>
-# tier: ['free', 'premium', 'ultimate']
-tier:
+distribution:
+<%= distribution %>
+# Add here corresponding tiers
+# tier:
+# - free
+# - premium
+# - ultimate
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 28274d5afb3..f91e3c34ef2 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -1072,7 +1072,7 @@ module API
forbidden! unless can?(current_user, :update_user_status, current_user)
update_params = declared_params
- update_params.delete(:clear_status_after) if Feature.disabled?(:clear_status_with_quick_options, current_user)
+ update_params.delete(:clear_status_after) if Feature.disabled?(:clear_status_with_quick_options, current_user, default_enabled: :yaml)
if ::Users::SetStatusService.new(current_user, update_params).execute
present current_user.status, with: Entities::UserStatus
diff --git a/lib/bulk_imports/pipeline/runner.rb b/lib/bulk_imports/pipeline/runner.rb
index c9841a51d9b..d39f4121b51 100644
--- a/lib/bulk_imports/pipeline/runner.rb
+++ b/lib/bulk_imports/pipeline/runner.rb
@@ -26,7 +26,11 @@ module BulkImports
end
end
- after_run(extracted_data) if respond_to?(:after_run)
+ if respond_to?(:after_run)
+ run_pipeline_step(:after_run) do
+ after_run(extracted_data)
+ end
+ end
info(message: 'Pipeline finished')
rescue MarkedAsFailedError
@@ -35,7 +39,7 @@ module BulkImports
private # rubocop:disable Lint/UselessAccessModifier
- def run_pipeline_step(step, class_name)
+ def run_pipeline_step(step, class_name = nil)
raise MarkedAsFailedError if marked_as_failed?
info(pipeline_step: step, step_class: class_name)
@@ -92,19 +96,21 @@ module BulkImports
end
def warn(extra = {})
- logger.warn(log_base_params.merge(extra))
+ logger.warn(log_params(extra))
end
def info(extra = {})
- logger.info(log_base_params.merge(extra))
+ logger.info(log_params(extra))
end
- def log_base_params
- {
+ def log_params(extra)
+ defaults = {
bulk_import_entity_id: context.entity.id,
bulk_import_entity_type: context.entity.source_type,
pipeline_class: pipeline
}
+
+ defaults.merge(extra).compact
end
def logger
diff --git a/lib/generators/gitlab/usage_metric_definition_generator.rb b/lib/generators/gitlab/usage_metric_definition_generator.rb
index 59f31eeb6c8..d3fac4c74f3 100644
--- a/lib/generators/gitlab/usage_metric_definition_generator.rb
+++ b/lib/generators/gitlab/usage_metric_definition_generator.rb
@@ -45,9 +45,13 @@ module Gitlab
end
def distribution
- value = ['ce']
- value << 'ee' if ee?
- value
+ value = ['- ce']
+ value << '- ee' if ee?
+ value.join("\n")
+ end
+
+ def milestone
+ Gitlab::VERSION.match('(\d+\.\d+)').captures.first
end
private
diff --git a/lib/gitlab/usage/docs/helper.rb b/lib/gitlab/usage/docs/helper.rb
index aa778f7f26f..8483334800b 100644
--- a/lib/gitlab/usage/docs/helper.rb
+++ b/lib/gitlab/usage/docs/helper.rb
@@ -27,7 +27,7 @@ module Gitlab
end
def render_name(name)
- "## #{name}\n"
+ "## `#{name}`\n"
end
def render_description(object)
diff --git a/lib/gitlab/usage/docs/value_formatter.rb b/lib/gitlab/usage/docs/value_formatter.rb
index 6ec20e89427..a2dc9b081f8 100644
--- a/lib/gitlab/usage/docs/value_formatter.rb
+++ b/lib/gitlab/usage/docs/value_formatter.rb
@@ -7,9 +7,9 @@ module Gitlab
def self.format(key, value)
case key
when :key_path
- "**#{value}**"
+ "**`#{value}`**"
when :data_source
- value.capitalize
+ value.to_s.capitalize
when :product_group
"`#{value}`"
when :introduced_by_url
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index aaad0f13d45..3847742c1b5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -653,6 +653,12 @@ msgstr ""
msgid "%{name_with_link} has run out of Shared Runner Pipeline minutes so no new jobs or pipelines in its projects will run."
msgstr ""
+msgid "%{name} %{status}"
+msgstr ""
+
+msgid "%{name} (Busy)"
+msgstr ""
+
msgid "%{name} contained %{resultsString}"
msgstr ""
@@ -5075,6 +5081,9 @@ msgstr ""
msgid "Business metrics (Custom)"
msgstr ""
+msgid "Busy"
+msgstr ""
+
msgid "Buy License"
msgstr ""
@@ -14044,6 +14053,9 @@ msgstr ""
msgid "Group URL"
msgstr ""
+msgid "Group Wikis"
+msgstr ""
+
msgid "Group avatar"
msgstr ""
diff --git a/package.json b/package.json
index b3e31bcfee3..85339e7ce73 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,6 @@
"babel-loader": "^8.2.2",
"babel-plugin-lodash": "^3.3.4",
"bootstrap": "4.4.1",
- "brace-expansion": "^1.1.8",
"cache-loader": "^4.1.0",
"clipboard": "^1.7.1",
"codemirror": "^5.48.4",
@@ -90,15 +89,12 @@
"exports-loader": "^0.7.0",
"fast-mersenne-twister": "1.0.2",
"file-loader": "^5.1.0",
- "font-awesome": "4.7.0",
"fuzzaldrin-plus": "^0.6.0",
- "glob": "^7.1.6",
"graphql": "^15.4.0",
"graphql-tag": "^2.11.0",
"immer": "^7.0.7",
"ipaddr.js": "^1.9.1",
"jed": "^1.1.1",
- "jest-transform-graphql": "^2.1.0",
"jquery": "^3.5.0",
"jquery.caret": "^0.3.1",
"jquery.waitforimages": "^2.2.0",
@@ -112,8 +108,6 @@
"mathjax": "3",
"mermaid": "^8.9.0",
"minimatch": "^3.0.4",
- "miragejs": "^0.1.40",
- "mock-apollo-client": "^0.5.0",
"monaco-editor": "^0.20.0",
"monaco-editor-webpack-plugin": "^1.9.0",
"monaco-yaml": "^2.5.1",
@@ -184,6 +178,7 @@
"istanbul-lib-coverage": "^3.0.0",
"istanbul-lib-report": "^3.0.0",
"istanbul-reports": "^3.0.0",
+ "glob": "^7.1.6",
"jasmine-core": "^2.9.0",
"jasmine-diff": "^0.1.3",
"jasmine-jquery": "^2.1.1",
@@ -192,6 +187,7 @@
"jest-environment-jsdom": "^26.5.2",
"jest-junit": "^12.0.0",
"jest-raw-loader": "^1.0.1",
+ "jest-transform-graphql": "^2.1.0",
"jest-util": "^26.5.2",
"jsdoc": "^3.5.5",
"jsdoc-vue": "^1.0.0",
@@ -205,9 +201,10 @@
"karma-webpack": "^4.0.2",
"markdownlint-cli": "0.24.0",
"md5": "^2.2.1",
+ "miragejs": "^0.1.40",
+ "mock-apollo-client": "^0.5.0",
"node-sass": "^4.14.1",
"nodemon": "^2.0.4",
- "pixelmatch": "^4.0.2",
"postcss": "^7.0.14",
"prettier": "2.2.1",
"readdir-enhanced": "^2.2.4",
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 30c7c401338..94800717677 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -14,8 +14,6 @@ RSpec.describe 'Pipeline', :js do
before do
sign_in(user)
project.add_role(user, role)
- stub_feature_flags(graphql_pipeline_details: false)
- stub_feature_flags(graphql_pipeline_details_users: false)
end
shared_context 'pipeline builds' do
@@ -70,7 +68,7 @@ RSpec.describe 'Pipeline', :js do
it 'shows the pipeline graph' do
visit_pipeline
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
expect(page).to have_content('Build')
expect(page).to have_content('Test')
expect(page).to have_content('Deploy')
@@ -644,7 +642,7 @@ RSpec.describe 'Pipeline', :js do
end
it 'shows the pipeline graph' do
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
expect(page).to have_content('Build')
expect(page).to have_content('Test')
expect(page).to have_content('Deploy')
@@ -684,7 +682,7 @@ RSpec.describe 'Pipeline', :js do
end
it 'shows the pipeline with a bridge job' do
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
expect(page).to have_content('cross-build')
end
@@ -757,6 +755,61 @@ RSpec.describe 'Pipeline', :js do
describe 'GET /:project/-/pipelines/:id' do
subject { visit project_pipeline_path(project, pipeline) }
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+
+ it 'shows deploy job as created' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('pending')
+ end
+
+ within('.js-pipeline-graph') do
+ within '.stage-column:nth-child(1)' do
+ expect(page).to have_content('test')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-created')
+ end
+ end
+ end
+
+ context 'when test job succeeded' do
+ before do
+ test_job.success!
+ end
+
+ it 'shows deploy job as pending' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('running')
+ end
+
+ within('.pipeline-graph') do
+ within '.stage-column:nth-child(1)' do
+ expect(page).to have_content('test')
+ expect(page).to have_css('.ci-status-icon-success')
+ end
+
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+ end
+ end
+ end
+ end
+
it 'shows deploy job as created' do
subject
@@ -764,13 +817,13 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('pending')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(1)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[0]) do
expect(page).to have_content('test')
expect(page).to have_css('.ci-status-icon-pending')
end
- within '.stage-column:nth-child(2)' do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-created')
end
@@ -789,13 +842,13 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('running')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(1)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[0]) do
expect(page).to have_content('test')
expect(page).to have_css('.ci-status-icon-success')
end
- within '.stage-column:nth-child(2)' do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-pending')
end
@@ -818,14 +871,37 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('waiting')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(2)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-waiting-for-resource')
end
end
end
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.pipeline-graph') do
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
+ end
+
context 'when resource is released from another job' do
before do
another_job.success!
@@ -838,13 +914,36 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('running')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(2)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-pending')
end
end
end
+
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+ it 'shows deploy job as pending' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('running')
+ end
+
+ within('.pipeline-graph') do
+ within '.stage-column:nth-child(2)' do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-pending')
+ end
+ end
+ end
+ end
end
context 'when deploy job is a bridge to trigger a downstream pipeline' do
@@ -860,8 +959,30 @@ RSpec.describe 'Pipeline', :js do
expect(page).to have_content('waiting')
end
- within('.pipeline-graph') do
- within '.stage-column:nth-child(2)' do
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
+ expect(page).to have_content('deploy')
+ expect(page).to have_css('.ci-status-icon-waiting-for-resource')
+ end
+ end
+ end
+ end
+
+ context 'when deploy job is a bridge to trigger a downstream pipeline' do
+ let!(:deploy_job) do
+ create(:ci_bridge, :created, stage: 'deploy', name: 'deploy',
+ stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
+ end
+
+ it 'shows deploy job as waiting for resource' do
+ subject
+
+ within('.pipeline-header-container') do
+ expect(page).to have_content('waiting')
+ end
+
+ within('.js-pipeline-graph') do
+ within(all('[data-testid="stage-column"]')[1]) do
expect(page).to have_content('deploy')
expect(page).to have_css('.ci-status-icon-waiting-for-resource')
end
@@ -1087,7 +1208,24 @@ RSpec.describe 'Pipeline', :js do
expect(current_path).to eq(pipeline_path(pipeline))
expect(page).not_to have_content('Failed Jobs')
- expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_selector('.js-pipeline-graph')
+ end
+
+ # remove when :graphql_pipeline_details flag is removed
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/299112
+ context 'when :graphql_pipeline_details flag is off' do
+ before do
+ stub_feature_flags(graphql_pipeline_details: false)
+ stub_feature_flags(graphql_pipeline_details_users: false)
+ end
+
+ it 'displays the pipeline graph' do
+ subject
+
+ expect(current_path).to eq(pipeline_path(pipeline))
+ expect(page).not_to have_content('Failed Jobs')
+ expect(page).to have_selector('.pipeline-visualization')
+ end
end
end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index cc0a528aaa3..6421d3db2cd 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe 'Pipelines', :js do
sign_in(user)
stub_feature_flags(graphql_pipeline_details: false)
stub_feature_flags(graphql_pipeline_details_users: false)
+
project.add_developer(user)
project.update!(auto_devops_attributes: { enabled: false })
end
diff --git a/spec/frontend/notes/components/note_header_spec.js b/spec/frontend/notes/components/note_header_spec.js
index 4760e2b9a0a..774d5aaa7d3 100644
--- a/spec/frontend/notes/components/note_header_spec.js
+++ b/spec/frontend/notes/components/note_header_spec.js
@@ -4,6 +4,7 @@ import { nextTick } from 'vue';
import Vuex from 'vuex';
import NoteHeader from '~/notes/components/note_header.vue';
import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
+import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -36,9 +37,7 @@ describe('NoteHeader component', () => {
username: 'root',
show_status: true,
status_tooltip_html: statusHtml,
- status: {
- availability: '',
- },
+ availability: '',
};
const createComponent = (props) => {
@@ -48,7 +47,7 @@ describe('NoteHeader component', () => {
actions,
}),
propsData: { ...props },
- stubs: { GlSprintf },
+ stubs: { GlSprintf, UserNameWithStatus },
});
};
@@ -110,7 +109,7 @@ describe('NoteHeader component', () => {
});
it('renders busy status if author availability is set', () => {
- createComponent({ author: { ...author, status: { availability: AVAILABILITY_STATUS.BUSY } } });
+ createComponent({ author: { ...author, availability: AVAILABILITY_STATUS.BUSY } });
expect(wrapper.find('.js-user-link').text()).toContain('(Busy)');
});
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
index 504d1953c0c..8f01accccc1 100644
--- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
@@ -2,7 +2,7 @@ import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
-import { DOWNSTREAM, GRAPHQL } from '~/pipelines/components/graph/constants';
+import { DOWNSTREAM, GRAPHQL, UPSTREAM } from '~/pipelines/components/graph/constants';
import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue';
import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
@@ -17,7 +17,7 @@ const processedPipeline = pipelineWithUpstreamDownstream(mockPipelineResponse);
describe('Linked Pipelines Column', () => {
const defaultProps = {
- columnTitle: 'Upstream',
+ columnTitle: 'Downstream',
linkedPipelines: processedPipeline.downstream,
type: DOWNSTREAM,
};
@@ -45,14 +45,15 @@ describe('Linked Pipelines Column', () => {
});
};
- const createComponentWithApollo = (
+ const createComponentWithApollo = ({
mountFn = shallowMount,
getPipelineDetailsHandler = jest.fn().mockResolvedValue(wrappedPipelineReturn),
- ) => {
+ props = {},
+ } = {}) => {
const requestHandlers = [[getPipelineDetails, getPipelineDetailsHandler]];
const apolloProvider = createMockApollo(requestHandlers);
- createComponent({ apolloProvider, mountFn });
+ createComponent({ apolloProvider, mountFn, props });
};
afterEach(() => {
@@ -86,34 +87,90 @@ describe('Linked Pipelines Column', () => {
await wrapper.vm.$nextTick();
};
- describe('when successful', () => {
- beforeEach(() => {
- createComponentWithApollo(mount);
+ describe('downstream', () => {
+ describe('when successful', () => {
+ beforeEach(() => {
+ createComponentWithApollo({ mountFn: mount });
+ });
+
+ it('toggles the pipeline visibility', async () => {
+ expect(findPipelineGraph().exists()).toBe(false);
+ await clickExpandButtonAndAwaitTimers();
+ expect(findPipelineGraph().exists()).toBe(true);
+ await clickExpandButton();
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
});
- it('toggles the pipeline visibility', async () => {
- expect(findPipelineGraph().exists()).toBe(false);
- await clickExpandButtonAndAwaitTimers();
- expect(findPipelineGraph().exists()).toBe(true);
- await clickExpandButton();
- expect(findPipelineGraph().exists()).toBe(false);
+ describe('on error', () => {
+ beforeEach(() => {
+ createComponentWithApollo({
+ mountFn: mount,
+ getPipelineDetailsHandler: jest.fn().mockRejectedValue(new Error('GraphQL error')),
+ });
+ });
+
+ it('emits the error', async () => {
+ await clickExpandButton();
+ expect(wrapper.emitted().error).toEqual([[LOAD_FAILURE]]);
+ });
+
+ it('does not show the pipeline', async () => {
+ expect(findPipelineGraph().exists()).toBe(false);
+ await clickExpandButtonAndAwaitTimers();
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
});
});
- describe('on error', () => {
- beforeEach(() => {
- createComponentWithApollo(mount, jest.fn().mockRejectedValue(new Error('GraphQL error')));
+ describe('upstream', () => {
+ const upstreamProps = {
+ columnTitle: 'Upstream',
+ /*
+ Because the IDs need to match to work, rather
+ than make new mock data, we are representing
+ the upstream pipeline with the downstream data.
+ */
+ linkedPipelines: processedPipeline.downstream,
+ type: UPSTREAM,
+ };
+
+ describe('when successful', () => {
+ beforeEach(() => {
+ createComponentWithApollo({
+ mountFn: mount,
+ props: upstreamProps,
+ });
+ });
+
+ it('toggles the pipeline visibility', async () => {
+ expect(findPipelineGraph().exists()).toBe(false);
+ await clickExpandButtonAndAwaitTimers();
+ expect(findPipelineGraph().exists()).toBe(true);
+ await clickExpandButton();
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
});
- it('emits the error', async () => {
- await clickExpandButton();
- expect(wrapper.emitted().error).toEqual([[LOAD_FAILURE]]);
- });
-
- it('does not show the pipeline', async () => {
- expect(findPipelineGraph().exists()).toBe(false);
- await clickExpandButtonAndAwaitTimers();
- expect(findPipelineGraph().exists()).toBe(false);
+ describe('on error', () => {
+ beforeEach(() => {
+ createComponentWithApollo({
+ mountFn: mount,
+ getPipelineDetailsHandler: jest.fn().mockRejectedValue(new Error('GraphQL error')),
+ props: upstreamProps,
+ });
+ });
+
+ it('emits the error', async () => {
+ await clickExpandButton();
+ expect(wrapper.emitted().error).toEqual([[LOAD_FAILURE]]);
+ });
+
+ it('does not show the pipeline', async () => {
+ expect(findPipelineGraph().exists()).toBe(false);
+ await clickExpandButtonAndAwaitTimers();
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
});
});
});
diff --git a/spec/frontend/set_status_modal/user_availability_status_spec.js b/spec/frontend/set_status_modal/user_availability_status_spec.js
deleted file mode 100644
index 95ca0251ce0..00000000000
--- a/spec/frontend/set_status_modal/user_availability_status_spec.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue';
-import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
-
-describe('UserAvailabilityStatus', () => {
- let wrapper;
-
- const createComponent = (props = {}) => {
- return shallowMount(UserAvailabilityStatus, {
- propsData: {
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('with availability status', () => {
- it(`set to ${AVAILABILITY_STATUS.BUSY}`, () => {
- wrapper = createComponent({ availability: AVAILABILITY_STATUS.BUSY });
- expect(wrapper.text()).toContain('(Busy)');
- });
-
- it(`set to ${AVAILABILITY_STATUS.NOT_SET}`, () => {
- wrapper = createComponent({ availability: AVAILABILITY_STATUS.NOT_SET });
- expect(wrapper.html()).toBe('');
- });
- });
-});
diff --git a/spec/frontend/set_status_modal/utils_spec.js b/spec/frontend/set_status_modal/utils_spec.js
new file mode 100644
index 00000000000..273f30f8311
--- /dev/null
+++ b/spec/frontend/set_status_modal/utils_spec.js
@@ -0,0 +1,15 @@
+import { AVAILABILITY_STATUS, isUserBusy } from '~/set_status_modal/utils';
+
+describe('Set status modal utils', () => {
+ describe('isUserBusy', () => {
+ it.each`
+ value | result
+ ${''} | ${false}
+ ${'fake status'} | ${false}
+ ${AVAILABILITY_STATUS.NOT_SET} | ${false}
+ ${AVAILABILITY_STATUS.BUSY} | ${true}
+ `('with $value returns $result', ({ value, result }) => {
+ expect(isUserBusy(value)).toBe(result);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js b/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js
index d7dc0aaae78..5a3a152d201 100644
--- a/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js
+++ b/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js
@@ -79,4 +79,34 @@ describe('AssigneeAvatarLink component', () => {
});
},
);
+
+ describe.each`
+ tooltipHasName | availability | canMerge | expected
+ ${true} | ${'Busy'} | ${false} | ${'Root (Busy) (cannot merge)'}
+ ${true} | ${'Busy'} | ${true} | ${'Root (Busy)'}
+ ${true} | ${''} | ${false} | ${'Root (cannot merge)'}
+ ${true} | ${''} | ${true} | ${'Root'}
+ ${false} | ${'Busy'} | ${false} | ${'Cannot merge'}
+ ${false} | ${'Busy'} | ${true} | ${''}
+ ${false} | ${''} | ${false} | ${'Cannot merge'}
+ ${false} | ${''} | ${true} | ${''}
+ `(
+ "with tooltipHasName=$tooltipHasName and availability='$availability' and canMerge=$canMerge",
+ ({ tooltipHasName, availability, canMerge, expected }) => {
+ beforeEach(() => {
+ createComponent({
+ tooltipHasName,
+ user: {
+ ...userDataMock(),
+ can_merge: canMerge,
+ availability,
+ },
+ });
+ });
+
+ it('sets tooltip to $expected', () => {
+ expect(findTooltipText()).toBe(expected);
+ });
+ },
+ );
});
diff --git a/spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js b/spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js
index 4365dc79272..5aa8264b98c 100644
--- a/spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js
+++ b/spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js
@@ -187,4 +187,26 @@ describe('CollapsedAssigneeList component', () => {
expect(findAvatarCounter().text()).toEqual(`${DEFAULT_MAX_COUNTER}+`);
});
});
+
+ const [busyUser] = UsersMockHelper.createNumberRandomUsers(1);
+ const [canMergeUser] = UsersMockHelper.createNumberRandomUsers(1);
+ busyUser.availability = 'busy';
+ canMergeUser.can_merge = true;
+
+ describe.each`
+ users | busy | canMerge | expected
+ ${[busyUser, canMergeUser]} | ${1} | ${1} | ${`${busyUser.name} (Busy), ${canMergeUser.name} (1/2 can merge)`}
+ ${[busyUser]} | ${1} | ${0} | ${`${busyUser.name} (Busy) (cannot merge)`}
+ ${[canMergeUser]} | ${0} | ${1} | ${`${canMergeUser.name}`}
+ ${[]} | ${0} | ${0} | ${'Assignee(s)'}
+ `(
+ 'with $users.length users, $busy is busy and $canMerge that can merge',
+ ({ users, expected }) => {
+ it('generates the tooltip text', () => {
+ createComponent({ users });
+
+ expect(getTooltipTitle()).toEqual(expected);
+ });
+ },
+ );
});
diff --git a/spec/frontend/sidebar/components/assignees/collapsed_assignee_spec.js b/spec/frontend/sidebar/components/assignees/collapsed_assignee_spec.js
index 51c245d24ce..b49e6255923 100644
--- a/spec/frontend/sidebar/components/assignees/collapsed_assignee_spec.js
+++ b/spec/frontend/sidebar/components/assignees/collapsed_assignee_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import AssigneeAvatar from '~/sidebar/components/assignees/assignee_avatar.vue';
import CollapsedAssignee from '~/sidebar/components/assignees/collapsed_assignee.vue';
+import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
import userDataMock from '../../user_data_mock';
const TEST_USER = userDataMock();
@@ -18,6 +19,9 @@ describe('CollapsedAssignee assignee component', () => {
wrapper = shallowMount(CollapsedAssignee, {
propsData,
+ stubs: {
+ UserNameWithStatus,
+ },
});
}
diff --git a/spec/frontend/sidebar/components/assignees/user_name_with_status_spec.js b/spec/frontend/sidebar/components/assignees/user_name_with_status_spec.js
new file mode 100644
index 00000000000..9483c6624c5
--- /dev/null
+++ b/spec/frontend/sidebar/components/assignees/user_name_with_status_spec.js
@@ -0,0 +1,51 @@
+import { GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
+import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
+
+const name = 'Goku';
+const containerClasses = 'gl-cool-class gl-over-9000';
+
+describe('UserNameWithStatus', () => {
+ let wrapper;
+
+ function createComponent(props = {}) {
+ return shallowMount(UserNameWithStatus, {
+ propsData: { name, containerClasses, ...props },
+ stubs: {
+ GlSprintf,
+ },
+ });
+ }
+
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('will render the users name', () => {
+ expect(wrapper.html()).toContain(name);
+ });
+
+ it('will not render "Busy"', () => {
+ expect(wrapper.html()).not.toContain('Busy');
+ });
+
+ it('will render all relevant containerClasses', () => {
+ const classes = wrapper.find('span').classes().join(' ');
+ expect(classes).toBe(containerClasses);
+ });
+
+ describe(`with availability="${AVAILABILITY_STATUS.BUSY}"`, () => {
+ beforeEach(() => {
+ wrapper = createComponent({ availability: AVAILABILITY_STATUS.BUSY });
+ });
+
+ it('will render "Busy"', () => {
+ expect(wrapper.html()).toContain('Goku (Busy)');
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
index 302f54257ab..a6c5e23ae14 100644
--- a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
+++ b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
@@ -1,7 +1,7 @@
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlSprintf, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue';
import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
+import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue';
const DEFAULT_PROPS = {
@@ -36,7 +36,7 @@ describe('User Popover Component', () => {
const findByTestId = (testid) => wrapper.find(`[data-testid="${testid}"]`);
const findUserStatus = () => wrapper.find('.js-user-status');
const findTarget = () => document.querySelector('.js-user-link');
- const findAvailabilityStatus = () => wrapper.find(UserAvailabilityStatus);
+ const findUserName = () => wrapper.find(UserNameWithStatus);
const createWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(UserPopover, {
@@ -47,7 +47,7 @@ describe('User Popover Component', () => {
},
stubs: {
GlSprintf,
- UserAvailabilityStatus,
+ UserNameWithStatus,
},
...options,
});
@@ -213,7 +213,7 @@ describe('User Popover Component', () => {
createWrapper({ user });
- expect(findAvailabilityStatus().exists()).toBe(true);
+ expect(findUserName().exists()).toBe(true);
expect(wrapper.text()).toContain(user.name);
expect(wrapper.text()).toContain('(Busy)');
});
diff --git a/spec/javascripts/matchers.js b/spec/javascripts/matchers.js
deleted file mode 100644
index ae005e152ed..00000000000
--- a/spec/javascripts/matchers.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import pixelmatch from 'pixelmatch';
-
-export default {
- toImageDiffEqual: () => {
- const getImageData = (img) => {
- const canvas = document.createElement('canvas');
- canvas.width = img.width;
- canvas.height = img.height;
- canvas.getContext('2d').drawImage(img, 0, 0);
- return canvas.getContext('2d').getImageData(0, 0, img.width, img.height).data;
- };
-
- return {
- compare(actual, expected, threshold = 0.1) {
- if (actual.height !== expected.height || actual.width !== expected.width) {
- return {
- pass: false,
- message: `Expected image dimensions (h x w) of ${expected.height}x${expected.width}.
- Received an image with ${actual.height}x${actual.width}`,
- };
- }
-
- const { width, height } = actual;
- const differentPixels = pixelmatch(
- getImageData(actual),
- getImageData(expected),
- null,
- width,
- height,
- { threshold },
- );
-
- return {
- pass: differentPixels < 20,
- message: `${differentPixels} pixels differ more than ${
- threshold * 100
- } percent between input and output.`,
- };
- },
- };
- },
-};
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index bf56a50b67f..be14d2ee7e7 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -12,7 +12,6 @@ import Vue from 'vue';
import { getDefaultAdapter } from '~/lib/utils/axios_utils';
import Translate from '~/vue_shared/translate';
-import customMatchers from './matchers';
import { FIXTURES_PATH, TEST_HOST } from './test_constants';
// Tech debt issue TBD
@@ -57,7 +56,6 @@ beforeAll(() => {
inline: window.__karma__.config.color,
}),
);
- jasmine.addMatchers(customMatchers);
});
// globalize common libraries
diff --git a/spec/lib/bulk_imports/pipeline/runner_spec.rb b/spec/lib/bulk_imports/pipeline/runner_spec.rb
index b3a6833de02..76e4e64a7d6 100644
--- a/spec/lib/bulk_imports/pipeline/runner_spec.rb
+++ b/spec/lib/bulk_imports/pipeline/runner_spec.rb
@@ -39,6 +39,8 @@ RSpec.describe BulkImports::Pipeline::Runner do
extractor BulkImports::Extractor
transformer BulkImports::Transformer
loader BulkImports::Loader
+
+ def after_run(_); end
end
stub_const('BulkImports::MyPipeline', pipeline)
@@ -107,6 +109,13 @@ RSpec.describe BulkImports::Pipeline::Runner do
.with(
bulk_import_entity_id: entity.id,
bulk_import_entity_type: 'group_entity',
+ pipeline_class: 'BulkImports::MyPipeline',
+ pipeline_step: :after_run
+ )
+ expect(logger).to receive(:info)
+ .with(
+ bulk_import_entity_id: entity.id,
+ bulk_import_entity_type: 'group_entity',
message: 'Pipeline finished',
pipeline_class: 'BulkImports::MyPipeline'
)
diff --git a/spec/lib/gitlab/usage/docs/renderer_spec.rb b/spec/lib/gitlab/usage/docs/renderer_spec.rb
index e62861cd677..0677aa2d9d7 100644
--- a/spec/lib/gitlab/usage/docs/renderer_spec.rb
+++ b/spec/lib/gitlab/usage/docs/renderer_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe Gitlab::Usage::Docs::Renderer do
.table_of_contents
.select { |metric_doc| metric_doc.level == 2 && !metric_doc.text.start_with?('info:') }
.map(&:text)
+ .map { |text| text.sub('<code>', '').sub('</code>', '') }
expect(generated_dictionary_keys).to match_array(items.keys)
end
diff --git a/spec/lib/gitlab/usage/docs/value_formatter_spec.rb b/spec/lib/gitlab/usage/docs/value_formatter_spec.rb
index 18560cbc10f..7002c76a7cf 100644
--- a/spec/lib/gitlab/usage/docs/value_formatter_spec.rb
+++ b/spec/lib/gitlab/usage/docs/value_formatter_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Gitlab::Usage::Docs::ValueFormatter do
:introduced_by_url | 'http://test.com' | '[Introduced by](http://test.com)'
:tier | %w(gold premium) | 'gold, premium'
:distribution | %w(ce ee) | 'ce, ee'
- :key_path | 'key.path' | '**key.path**'
+ :key_path | 'key.path' | '**`key.path`**'
:milestone | '13.4' | '13.4'
:status | 'data_available' | 'data_available'
end
diff --git a/spec/models/packages/composer/cache_file_spec.rb b/spec/models/packages/composer/cache_file_spec.rb
index ef9818f0930..a03b89ca2f5 100644
--- a/spec/models/packages/composer/cache_file_spec.rb
+++ b/spec/models/packages/composer/cache_file_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Packages::Composer::CacheFile, type: :model do
let_it_be(:group1) { create(:group) }
let_it_be(:group2) { create(:group) }
let_it_be(:cache_file1) { create(:composer_cache_file, file_sha256: '123456', group: group1) }
- let_it_be(:cache_file2) { create(:composer_cache_file, file_sha256: '456778', group: group2) }
+ let_it_be(:cache_file2) { create(:composer_cache_file, delete_at: 2.days.from_now, file_sha256: '456778', group: group2) }
describe '.with_namespace' do
subject { described_class.with_namespace(group1) }
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb
index c51358bf659..55d17fabc9a 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/triggers_spec.rb
@@ -54,6 +54,15 @@ RSpec.describe API::Triggers do
expect(pipeline.builds.size).to eq(5)
end
+ it 'stores payload as a variable' do
+ post api("/projects/#{project.id}/trigger/pipeline"), params: options.merge(ref: 'master')
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(pipeline.variables.find { |v| v.key == 'TRIGGER_PAYLOAD' }.value).to eq(
+ "{\"ref\":\"master\",\"id\":\"#{project.id}\",\"variables\":{}}"
+ )
+ end
+
it 'returns bad request with no pipeline created if there\'s no commit for that ref' do
post api("/projects/#{project.id}/trigger/pipeline"), params: options.merge(ref: 'other-branch')
@@ -84,7 +93,7 @@ RSpec.describe API::Triggers do
post api("/projects/#{project.id}/trigger/pipeline"), params: options.merge(variables: variables, ref: 'master')
expect(response).to have_gitlab_http_status(:created)
- expect(pipeline.variables.map { |v| { v.key => v.value } }.last).to eq(variables)
+ expect(pipeline.variables.find { |v| v.key == 'TRIGGER_KEY' }.value).to eq('TRIGGER_VALUE')
end
end
end
diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb
index 0cc66e67b91..89d3da89011 100644
--- a/spec/services/ci/pipeline_trigger_service_spec.rb
+++ b/spec/services/ci/pipeline_trigger_service_spec.rb
@@ -45,6 +45,27 @@ RSpec.describe Ci::PipelineTriggerService do
expect(result[:status]).to eq(:success)
end
+ it 'stores the payload as a variable' do
+ expect { result }.to change { Ci::PipelineVariable.count }.by(1)
+
+ var = result[:pipeline].variables.first
+
+ expect(var.key).to eq('TRIGGER_PAYLOAD')
+ expect(var.value).to eq('{"ref":"master","variables":null}')
+ expect(var.variable_type).to eq('file')
+ end
+
+ context 'when FF ci_trigger_payload_into_pipeline is disabled' do
+ before do
+ stub_feature_flags(ci_trigger_payload_into_pipeline: false)
+ end
+
+ it 'does not store the payload as a variable' do
+ expect { result }.not_to change { Ci::PipelineVariable.count }
+ expect(result[:pipeline].variables).to be_empty
+ end
+ end
+
context 'when commit message has [ci skip]' do
before do
allow_next(Ci::Pipeline).to receive(:git_commit_message) { '[ci skip]' }
@@ -60,8 +81,8 @@ RSpec.describe Ci::PipelineTriggerService do
let(:params) { { token: trigger.token, ref: 'master', variables: variables } }
let(:variables) { { 'AAA' => 'AAA123' } }
- it 'has a variable' do
- expect { result }.to change { Ci::PipelineVariable.count }.by(1)
+ it 'has variables' do
+ expect { result }.to change { Ci::PipelineVariable.count }.by(2)
.and change { Ci::TriggerRequest.count }.by(1)
expect(result[:pipeline].variables.map { |v| { v.key => v.value } }.first).to eq(variables)
expect(result[:pipeline].trigger_requests.last.variables).to be_nil
@@ -155,8 +176,8 @@ RSpec.describe Ci::PipelineTriggerService do
let(:params) { { token: job.token, ref: 'master', variables: variables } }
let(:variables) { { 'AAA' => 'AAA123' } }
- it 'has a variable' do
- expect { result }.to change { Ci::PipelineVariable.count }.by(1)
+ it 'has variables' do
+ expect { result }.to change { Ci::PipelineVariable.count }.by(2)
.and change { Ci::Sources::Pipeline.count }.by(1)
expect(result[:pipeline].variables.map { |v| { v.key => v.value } }.first).to eq(variables)
expect(job.sourced_pipelines.last.pipeline_id).to eq(result[:pipeline].id)
diff --git a/spec/workers/packages/composer/cache_cleanup_worker_spec.rb b/spec/workers/packages/composer/cache_cleanup_worker_spec.rb
new file mode 100644
index 00000000000..e69fe55acc2
--- /dev/null
+++ b/spec/workers/packages/composer/cache_cleanup_worker_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Composer::CacheCleanupWorker, type: :worker do
+ describe '#perform' do
+ let_it_be(:group) { create(:group) }
+
+ let!(:cache_file1) { create(:composer_cache_file, delete_at: nil, group: group, file_sha256: '124') }
+ let!(:cache_file2) { create(:composer_cache_file, delete_at: 2.days.from_now, group: group, file_sha256: '3456') }
+ let!(:cache_file3) { create(:composer_cache_file, delete_at: 1.day.ago, group: group, file_sha256: '5346') }
+ let!(:cache_file4) { create(:composer_cache_file, delete_at: nil, group: group, file_sha256: '56889') }
+
+ subject { described_class.new.perform }
+
+ before do
+ # emulate group deletion
+ cache_file4.update_columns(namespace_id: nil)
+ end
+
+ it 'deletes expired packages' do
+ expect { subject }.to change { Packages::Composer::CacheFile.count }.by(-2)
+ expect { cache_file1.reload }.not_to raise_error ActiveRecord::RecordNotFound
+ expect { cache_file2.reload }.not_to raise_error ActiveRecord::RecordNotFound
+ expect { cache_file3.reload }.to raise_error ActiveRecord::RecordNotFound
+ expect { cache_file4.reload }.to raise_error ActiveRecord::RecordNotFound
+ end
+ end
+end
diff --git a/yarn.lock b/yarn.lock
index 9f7fa94af24..699bf42395b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2369,10 +2369,10 @@ bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.5.1, bluebird@^3.5.5, bluebird@~3.
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
-bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
- version "4.11.8"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
- integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.9:
+ version "4.11.9"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
+ integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
body-parser@1.19.0, body-parser@^1.16.1:
version "1.19.0"
@@ -2432,7 +2432,7 @@ boxen@^4.2.0:
type-fest "^0.8.1"
widest-line "^3.1.0"
-brace-expansion@^1.1.7, brace-expansion@^1.1.8:
+brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
@@ -2463,7 +2463,7 @@ braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
-brorand@^1.0.1:
+brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
@@ -4235,9 +4235,9 @@ domutils@^1.5.1:
domelementtype "1"
dot-prop@^4.1.1:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
- integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4"
+ integrity sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==
dependencies:
is-obj "^1.0.0"
@@ -4319,17 +4319,17 @@ electron-to-chromium@^1.3.634:
integrity sha512-cev+jOrz/Zm1i+Yh334Hed6lQVOkkemk2wRozfMF4MtTR7pxf3r3L5Rbd7uX1zMcEqVJ7alJBnJL7+JffkC6FQ==
elliptic@^6.0.0:
- version "6.4.0"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
- integrity sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=
+ version "6.5.4"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
+ integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
dependencies:
- bn.js "^4.4.0"
- brorand "^1.0.1"
+ bn.js "^4.11.9"
+ brorand "^1.1.0"
hash.js "^1.0.0"
- hmac-drbg "^1.0.0"
- inherits "^2.0.1"
- minimalistic-assert "^1.0.0"
- minimalistic-crypto-utils "^1.0.0"
+ hmac-drbg "^1.0.1"
+ inherits "^2.0.4"
+ minimalistic-assert "^1.0.1"
+ minimalistic-crypto-utils "^1.0.1"
emittery@^0.7.1:
version "0.7.1"
@@ -5265,11 +5265,6 @@ follow-redirects@^1.0.0, follow-redirects@^1.10.0:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
-font-awesome@4.7.0:
- version "4.7.0"
- resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
- integrity sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=
-
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -5862,7 +5857,7 @@ highlight.js@^9.13.1, highlight.js@~9.13.0:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e"
integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==
-hmac-drbg@^1.0.0:
+hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
@@ -6000,9 +5995,9 @@ http-proxy-middleware@0.19.1:
micromatch "^3.1.10"
http-proxy@^1.13.0, http-proxy@^1.17.0:
- version "1.18.0"
- resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a"
- integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==
+ version "1.18.1"
+ resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
+ integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
dependencies:
eventemitter3 "^4.0.0"
follow-redirects "^1.0.0"
@@ -6207,9 +6202,9 @@ inherits@2.0.3:
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
- version "1.3.5"
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
- integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+ integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
internal-ip@^4.3.0:
version "4.3.0"
@@ -7560,9 +7555,9 @@ kind-of@^5.0.0:
integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
kind-of@^6.0.0, kind-of@^6.0.2:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
- integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
klaw@~2.0.0:
version "2.0.0"
@@ -8314,12 +8309,12 @@ minify@^4.1.1:
try-catch "^2.0.0"
try-to-catch "^1.0.2"
-minimalistic-assert@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
- integrity sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=
+minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+ integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
@@ -8582,9 +8577,9 @@ node-ensure@^0.0.0:
integrity sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc=
node-fetch@^2.6.0:
- version "2.6.0"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
- integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
+ integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-forge@^0.10.0:
version "0.10.0"
@@ -9351,13 +9346,6 @@ pirates@^4.0.1:
dependencies:
node-modules-regexp "^1.0.0"
-pixelmatch@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854"
- integrity sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=
- dependencies:
- pngjs "^3.0.0"
-
pkg-dir@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
@@ -9379,11 +9367,6 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
-pngjs@^3.0.0:
- version "3.3.3"
- resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b"
- integrity sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==
-
pofile@1.0.x:
version "1.0.11"
resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.0.11.tgz#35aff58c17491d127a07336d5522ebc9df57c954"