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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-10-04 18:12:14 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-10-04 18:12:14 +0300
commite9ab4187093f05b873b32045295eeb580c97cdce (patch)
tree74f41f843e6addc0e17d4da877366f90e7f2fc68
parent0327ce54a7e315b3aa6f80cc4e540fbb6792f2e3 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/api/bulk_imports_api.js7
-rw-r--r--app/assets/javascripts/import_entities/components/pagination_bar.vue90
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/usage_statistics.js2
-rw-r--r--app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue176
-rw-r--r--app/assets/javascripts/pages/import/bulk_imports/history/index.js15
-rw-r--r--app/assets/javascripts/pages/import/bulk_imports/history/utils/error_messages.js3
-rw-r--r--app/assets/javascripts/performance_bar/components/performance_bar_app.vue22
-rw-r--r--app/assets/javascripts/rest_api.js1
-rw-r--r--app/controllers/concerns/issuable_actions.rb2
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/models/ci/build_trace_metadata.rb6
-rw-r--r--app/views/admin/application_settings/_usage.html.haml16
-rw-r--r--app/views/admin/application_settings/metrics_and_profiling.html.haml2
-rw-r--r--app/views/groups/_import_group_from_another_instance_panel.html.haml13
-rw-r--r--app/views/import/bulk_imports/history.html.haml6
-rw-r--r--config/feature_flags/development/merge_request_discussion_cache.yml8
-rw-r--r--config/feature_flags/development/sort_by_project_users_by_project_authorizations_user_id.yml8
-rw-r--r--config/feature_flags/ops/show_terraform_banner.yml8
-rw-r--r--config/routes/import.rb1
-rw-r--r--doc/administration/monitoring/performance/img/performance_bar_v14_4.pngbin74349 -> 77571 bytes
-rw-r--r--doc/administration/monitoring/performance/performance_bar.md6
-rw-r--r--doc/development/profiling.md6
-rw-r--r--doc/development/service_ping/index.md4
-rw-r--r--doc/development/service_ping/metrics_lifecycle.md5
-rw-r--r--doc/user/admin_area/settings/usage_statistics.md2
-rw-r--r--lib/api/projects.rb5
-rw-r--r--lib/gitlab/ci/trace.rb30
-rw-r--r--lib/gitlab/ci/trace/archive.rb57
-rw-r--r--lib/gitlab/middleware/speedscope.rb18
-rw-r--r--lib/gitlab/stack_prof.rb12
-rw-r--r--locale/gitlab.pot65
-rw-r--r--spec/features/groups/import_export/migration_history_spec.rb30
-rw-r--r--spec/frontend/import_entities/components/pagination_bar_spec.js92
-rw-r--r--spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js175
-rw-r--r--spec/helpers/projects_helper_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/trace/archive_spec.rb24
-rw-r--r--spec/lib/gitlab/middleware/speedscope_spec.rb52
-rw-r--r--spec/models/ci/build_trace_metadata_spec.rb4
-rw-r--r--spec/requests/api/projects_spec.rb23
-rw-r--r--spec/requests/projects/merge_requests_discussions_spec.rb10
40 files changed, 876 insertions, 140 deletions
diff --git a/app/assets/javascripts/api/bulk_imports_api.js b/app/assets/javascripts/api/bulk_imports_api.js
new file mode 100644
index 00000000000..d636cfdff0b
--- /dev/null
+++ b/app/assets/javascripts/api/bulk_imports_api.js
@@ -0,0 +1,7 @@
+import { buildApiUrl } from '~/api/api_utils';
+import axios from '~/lib/utils/axios_utils';
+
+const BULK_IMPORT_ENTITIES_PATH = '/api/:version/bulk_imports/entities';
+
+export const getBulkImportsHistory = (params) =>
+ axios.get(buildApiUrl(BULK_IMPORT_ENTITIES_PATH), { params });
diff --git a/app/assets/javascripts/import_entities/components/pagination_bar.vue b/app/assets/javascripts/import_entities/components/pagination_bar.vue
new file mode 100644
index 00000000000..33bd3e08bb1
--- /dev/null
+++ b/app/assets/javascripts/import_entities/components/pagination_bar.vue
@@ -0,0 +1,90 @@
+<script>
+import { GlDropdown, GlDropdownItem, GlIcon, GlSprintf } from '@gitlab/ui';
+import { __ } from '~/locale';
+import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
+
+const DEFAULT_PAGE_SIZES = [20, 50, 100];
+
+export default {
+ components: {
+ PaginationLinks,
+ GlDropdown,
+ GlDropdownItem,
+ GlIcon,
+ GlSprintf,
+ },
+ props: {
+ pageInfo: {
+ required: true,
+ type: Object,
+ },
+ pageSizes: {
+ required: false,
+ type: Array,
+ default: () => DEFAULT_PAGE_SIZES,
+ },
+ itemsCount: {
+ required: true,
+ type: Number,
+ },
+ },
+
+ computed: {
+ humanizedTotal() {
+ return this.pageInfo.total >= 1000 ? __('1000+') : this.pageInfo.total;
+ },
+
+ paginationInfo() {
+ const { page, perPage } = this.pageInfo;
+ const start = (page - 1) * perPage + 1;
+ const end = start + this.itemsCount - 1;
+
+ return { start, end };
+ },
+ },
+
+ methods: {
+ setPage(page) {
+ this.$emit('set-page', page);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-display-flex gl-align-items-center">
+ <pagination-links :change="setPage" :page-info="pageInfo" class="gl-m-0" />
+ <gl-dropdown category="tertiary" class="gl-ml-auto">
+ <template #button-content>
+ <span class="gl-font-weight-bold">
+ <gl-sprintf :message="__('%{count} items per page')">
+ <template #count>
+ {{ pageInfo.perPage }}
+ </template>
+ </gl-sprintf>
+ </span>
+ <gl-icon class="gl-button-icon dropdown-chevron" name="chevron-down" />
+ </template>
+ <gl-dropdown-item v-for="size in pageSizes" :key="size" @click="$emit('set-page-size', size)">
+ <gl-sprintf :message="__('%{count} items per page')">
+ <template #count>
+ {{ size }}
+ </template>
+ </gl-sprintf>
+ </gl-dropdown-item>
+ </gl-dropdown>
+ <div class="gl-ml-2" data-testid="information">
+ <gl-sprintf :message="s__('BulkImport|Showing %{start}-%{end} of %{total}')">
+ <template #start>
+ {{ paginationInfo.start }}
+ </template>
+ <template #end>
+ {{ paginationInfo.end }}
+ </template>
+ <template #total>
+ {{ humanizedTotal }}
+ </template>
+ </gl-sprintf>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/usage_statistics.js b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/usage_statistics.js
index 4c312a008cb..68849857d0f 100644
--- a/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/usage_statistics.js
+++ b/app/assets/javascripts/pages/admin/application_settings/metrics_and_profiling/usage_statistics.js
@@ -1,7 +1,7 @@
import { __ } from '~/locale';
export const HELPER_TEXT_SERVICE_PING_DISABLED = __(
- 'To enable Registration Features, make sure "Enable service ping" is checked.',
+ 'To enable Registration Features, first enable Service Ping.',
);
export const HELPER_TEXT_SERVICE_PING_ENABLED = __(
diff --git a/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue b/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue
new file mode 100644
index 00000000000..ec3cf4a8a92
--- /dev/null
+++ b/app/assets/javascripts/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue
@@ -0,0 +1,176 @@
+<script>
+import { GlButton, GlEmptyState, GlLink, GlLoadingIcon, GlTable } from '@gitlab/ui';
+
+import { s__, __ } from '~/locale';
+import createFlash from '~/flash';
+import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
+import { joinPaths } from '~/lib/utils/url_utility';
+import { getBulkImportsHistory } from '~/rest_api';
+import ImportStatus from '~/import_entities/components/import_status.vue';
+import PaginationBar from '~/import_entities/components/pagination_bar.vue';
+import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+
+import { DEFAULT_ERROR } from '../utils/error_messages';
+
+const DEFAULT_PER_PAGE = 20;
+const DEFAULT_TH_CLASSES =
+ 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-200! gl-border-b-1! gl-p-5!';
+
+const tableCell = (config) => ({
+ thClass: `${DEFAULT_TH_CLASSES}`,
+ tdClass: (value, key, item) => {
+ return {
+ // eslint-disable-next-line no-underscore-dangle
+ 'gl-border-b-0!': item._showDetails,
+ };
+ },
+ ...config,
+});
+
+export default {
+ components: {
+ GlButton,
+ GlEmptyState,
+ GlLink,
+ GlLoadingIcon,
+ GlTable,
+ PaginationBar,
+ ImportStatus,
+ TimeAgo,
+ },
+
+ data() {
+ return {
+ loading: true,
+ historyItems: [],
+ paginationConfig: {
+ page: 1,
+ perPage: DEFAULT_PER_PAGE,
+ },
+ pageInfo: {},
+ };
+ },
+
+ fields: [
+ tableCell({
+ key: 'source_full_path',
+ label: s__('BulkImport|Source group'),
+ thClass: `${DEFAULT_TH_CLASSES} gl-w-30p`,
+ }),
+ tableCell({
+ key: 'destination_name',
+ label: s__('BulkImport|New group'),
+ thClass: `${DEFAULT_TH_CLASSES} gl-w-40p`,
+ }),
+ tableCell({
+ key: 'created_at',
+ label: __('Date'),
+ }),
+ tableCell({
+ key: 'status',
+ label: __('Status'),
+ tdAttr: { 'data-qa-selector': 'import_status_indicator' },
+ }),
+ ],
+
+ computed: {
+ hasHistoryItems() {
+ return this.historyItems.length > 0;
+ },
+ },
+
+ watch: {
+ paginationConfig: {
+ handler() {
+ this.loadHistoryItems();
+ },
+ deep: true,
+ immediate: true,
+ },
+ },
+
+ methods: {
+ async loadHistoryItems() {
+ try {
+ this.loading = true;
+ const { data: historyItems, headers } = await getBulkImportsHistory({
+ page: this.paginationConfig.page,
+ per_page: this.paginationConfig.perPage,
+ });
+ this.pageInfo = parseIntPagination(normalizeHeaders(headers));
+ this.historyItems = historyItems;
+ } catch (e) {
+ createFlash({ message: DEFAULT_ERROR, captureError: true, error: e });
+ } finally {
+ this.loading = false;
+ }
+ },
+
+ getDestinationUrl({ destination_name: name, destination_namespace: namespace }) {
+ return [namespace, name].filter(Boolean).join('/');
+ },
+
+ getFullDestinationUrl(params) {
+ return joinPaths(gon.relative_url_root || '', this.getDestinationUrl(params));
+ },
+ },
+
+ gitlabLogo: window.gon.gitlab_logo,
+};
+</script>
+
+<template>
+ <div>
+ <div
+ class="gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-display-flex gl-align-items-center"
+ >
+ <h1 class="gl-my-0 gl-py-4 gl-font-size-h1">
+ <img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6 gl-mb-2 gl-display-inline gl-mr-2" />
+ {{ s__('BulkImport|Group import history') }}
+ </h1>
+ </div>
+ <gl-loading-icon v-if="loading" size="md" class="gl-mt-5" />
+ <gl-empty-state
+ v-else-if="!hasHistoryItems"
+ :title="s__('BulkImport|No history is available')"
+ :description="s__('BulkImport|Your imported groups will appear here.')"
+ />
+ <template v-else>
+ <gl-table
+ :fields="$options.fields"
+ :items="historyItems"
+ data-qa-selector="import_history_table"
+ class="gl-w-full"
+ >
+ <template #cell(destination_name)="{ item }">
+ <gl-link :href="getFullDestinationUrl(item)" target="_blank">
+ {{ getDestinationUrl(item) }}
+ </gl-link>
+ </template>
+ <template #cell(created_at)="{ value }">
+ <time-ago :time="value" />
+ </template>
+ <template #cell(status)="{ value, item, toggleDetails, detailsShowing }">
+ <import-status :status="value" class="gl-display-inline-block gl-w-13" />
+ <gl-button
+ v-if="item.failures.length"
+ class="gl-ml-3"
+ :selected="detailsShowing"
+ @click="toggleDetails"
+ >{{ __('Details') }}</gl-button
+ >
+ </template>
+ <template #row-details="{ item }">
+ <pre>{{ item.failures }}</pre>
+ </template>
+ </gl-table>
+ <pagination-bar
+ :page-info="pageInfo"
+ :items-count="historyItems.length"
+ class="gl-m-0 gl-mt-3"
+ @set-page="paginationConfig.page = $event"
+ @set-page-size="paginationConfig.perPage = $event"
+ />
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pages/import/bulk_imports/history/index.js b/app/assets/javascripts/pages/import/bulk_imports/history/index.js
new file mode 100644
index 00000000000..5a67aa99baa
--- /dev/null
+++ b/app/assets/javascripts/pages/import/bulk_imports/history/index.js
@@ -0,0 +1,15 @@
+import Vue from 'vue';
+import BulkImportHistoryApp from './components/bulk_imports_history_app.vue';
+
+function mountImportHistoryApp(mountElement) {
+ if (!mountElement) return undefined;
+
+ return new Vue({
+ el: mountElement,
+ render(createElement) {
+ return createElement(BulkImportHistoryApp);
+ },
+ });
+}
+
+mountImportHistoryApp(document.querySelector('#import-history-mount-element'));
diff --git a/app/assets/javascripts/pages/import/bulk_imports/history/utils/error_messages.js b/app/assets/javascripts/pages/import/bulk_imports/history/utils/error_messages.js
new file mode 100644
index 00000000000..24669e22ade
--- /dev/null
+++ b/app/assets/javascripts/pages/import/bulk_imports/history/utils/error_messages.js
@@ -0,0 +1,3 @@
+import { __ } from '~/locale';
+
+export const DEFAULT_ERROR = __('Something went wrong on our end.');
diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
index 7fd63acbede..ed30198244f 100644
--- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
+++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
@@ -124,9 +124,6 @@ export default {
const fileName = this.requests[0].truncatedUrl;
return `${fileName}_perf_bar_${Date.now()}.json`;
},
- flamegraphPath() {
- return mergeUrlParams({ performance_bar: 'flamegraph' }, window.location.href);
- },
},
mounted() {
this.currentRequest = this.requestId;
@@ -135,6 +132,12 @@ export default {
changeCurrentRequest(newRequestId) {
this.currentRequest = newRequestId;
},
+ flamegraphPath(mode) {
+ return mergeUrlParams(
+ { performance_bar: 'flamegraph', stackprof_mode: mode },
+ window.location.href,
+ );
+ },
},
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
};
@@ -180,8 +183,17 @@ export default {
}}</a>
</div>
<div v-if="currentRequest.details" id="peek-flamegraph" class="view">
- <a class="gl-text-blue-200" :href="flamegraphPath">{{
- s__('PerformanceBar|Flamegraph')
+ <span class="gl-text-white-200">{{ s__('PerformanceBar|Flamegraph with mode:') }}</span>
+ <a class="gl-text-blue-200" :href="flamegraphPath('wall')">{{
+ s__('PerformanceBar|wall')
+ }}</a>
+ /
+ <a class="gl-text-blue-200" :href="flamegraphPath('cpu')">{{
+ s__('PerformanceBar|cpu')
+ }}</a>
+ /
+ <a class="gl-text-blue-200" :href="flamegraphPath('object')">{{
+ s__('PerformanceBar|object')
}}</a>
</div>
<a v-if="statsUrl" class="gl-text-blue-200 view" :href="statsUrl">{{
diff --git a/app/assets/javascripts/rest_api.js b/app/assets/javascripts/rest_api.js
index 61fe89f4f7e..29642b6633f 100644
--- a/app/assets/javascripts/rest_api.js
+++ b/app/assets/javascripts/rest_api.js
@@ -2,6 +2,7 @@ export * from './api/groups_api';
export * from './api/projects_api';
export * from './api/user_api';
export * from './api/markdown_api';
+export * from './api/bulk_imports_api';
// Note: It's not possible to spy on methods imported from this file in
// Jest tests.
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index 2462df77884..e1e662a1968 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -158,7 +158,7 @@ module IssuableActions
discussions = Discussion.build_collection(notes, issuable)
- if issuable.is_a?(MergeRequest) && Feature.enabled?(:merge_request_discussion_cache, issuable.target_project, default_enabled: :yaml)
+ if issuable.is_a?(MergeRequest)
cache_context = [current_user&.cache_key, project.team.human_max_access(current_user&.id)].join(':')
render_cached(discussions, with: discussion_serializer, cache_context: -> (_) { cache_context }, context: self)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index d7f1cd505e9..b11541b46cc 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -351,7 +351,7 @@ module ProjectsHelper
end
def show_terraform_banner?(project)
- project.repository_languages.with_programming_language('HCL').exists? && project.terraform_states.empty?
+ Feature.enabled?(:show_terraform_banner, type: :ops, default_enabled: true) && project.repository_languages.with_programming_language('HCL').exists? && project.terraform_states.empty?
end
def project_permissions_panel_data(project)
diff --git a/app/models/ci/build_trace_metadata.rb b/app/models/ci/build_trace_metadata.rb
index 901b84ceec6..4819029ea33 100644
--- a/app/models/ci/build_trace_metadata.rb
+++ b/app/models/ci/build_trace_metadata.rb
@@ -37,8 +37,10 @@ module Ci
increment!(:archival_attempts, touch: :last_archival_attempt_at)
end
- def track_archival!(trace_artifact_id)
- update!(trace_artifact_id: trace_artifact_id, archived_at: Time.current)
+ def track_archival!(trace_artifact_id, checksum)
+ update!(trace_artifact_id: trace_artifact_id,
+ checksum: checksum,
+ archived_at: Time.current)
end
def archival_attempts_message
diff --git a/app/views/admin/application_settings/_usage.html.haml b/app/views/admin/application_settings/_usage.html.haml
index 934c0643043..5bdad50c161 100644
--- a/app/views/admin/application_settings/_usage.html.haml
+++ b/app/views/admin/application_settings/_usage.html.haml
@@ -10,21 +10,21 @@
= f.label :version_check_enabled, class: 'form-check-label' do
= _("Enable version check")
.form-text.text-muted
- = _("GitLab will inform you if a new version is available.")
- = _("%{link_start}Learn more%{link_end} about what information is shared with GitLab Inc.").html_safe % { link_start: "<a href='#{help_page_path("user/admin_area/settings/usage_statistics", anchor: "version-check")}'>".html_safe, link_end: '</a>'.html_safe }
+ = _("GitLab informs you if a new version is available.")
+ = _("%{link_start}What information does GitLab Inc. collect?%{link_end}").html_safe % { link_start: "<a href='#{help_page_path("user/admin_area/settings/usage_statistics", anchor: "version-check")}'>".html_safe, link_end: '</a>'.html_safe }
.form-group
- can_be_configured = @application_setting.usage_ping_can_be_configured?
.form-check
= f.check_box :usage_ping_enabled, disabled: !can_be_configured, class: 'form-check-input'
= f.label :usage_ping_enabled, class: 'form-check-label' do
- = _('Enable service ping')
+ = _('Enable Service Ping')
.form-text.text-muted
- if can_be_configured
- %p.mb-2= _('To help improve GitLab and its user experience, GitLab will periodically collect usage information.')
+ %p.mb-2= _('To help improve GitLab and its user experience, GitLab periodically collects usage information.')
- - service_ping_path = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'service-ping')
+ - service_ping_path = help_page_path('development/service_ping/index.md')
- service_ping_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: service_ping_path }
- %p.mb-2= s_('%{service_ping_link_start}Learn more%{service_ping_link_end} about what information is shared with GitLab Inc.').html_safe % { service_ping_link_start: service_ping_link_start, service_ping_link_end: '</a>'.html_safe }
+ %p.mb-2= s_('%{service_ping_link_start}What information is shared with GitLab Inc.?%{service_ping_link_end}').html_safe % { service_ping_link_start: service_ping_link_start, service_ping_link_end: '</a>'.html_safe }
%button.gl-button.btn.btn-default.js-payload-preview-trigger{ type: 'button', data: { payload_selector: ".#{payload_class}" } }
.gl-spinner.js-spinner.gl-display-none.gl-mr-2
@@ -46,7 +46,7 @@
- if usage_ping_enabled
%p.gl-mb-3.text-muted{ id: 'service_ping_features_helper_text' }= _('You can enable Registration Features because Service Ping is enabled. To continue using Registration Features in the future, you will also need to register with GitLab via a new cloud licensing service.')
- else
- %p.gl-mb-3.text-muted{ id: 'service_ping_features_helper_text' }= _('To enable Registration Features, make sure "Enable service ping" is checked.')
+ %p.gl-mb-3.text-muted{ id: 'service_ping_features_helper_text' }= _('To enable Registration Features, first enable Service Ping.')
%p.gl-mb-3.text-muted= _('Registration Features include:')
.form-text
@@ -61,7 +61,7 @@
%li
= _('Email from GitLab - email users right from the Admin Area. %{link_start}Learn more%{link_end}.').html_safe % { link_start: email_from_gitlab_link, link_end: link_end }
%li
- = _('Limit project size at a global, group and project level. %{link_start}Learn more%{link_end}.').html_safe % { link_start: repo_size_limit_link, link_end: link_end }
+ = _('Limit project size at a global, group, and project level. %{link_start}Learn more%{link_end}.').html_safe % { link_start: repo_size_limit_link, link_end: link_end }
%li
= _('Restrict group access by IP address. %{link_start}Learn more%{link_end}.').html_safe % { link_start: restrict_ip_link, link_end: link_end }
diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml
index f1e37c76130..6087551d7c7 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -49,7 +49,7 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Enable or disable version check and service ping.')
+ = _('Enable or disable version check and Service Ping.')
.settings-content
= render 'usage'
diff --git a/app/views/groups/_import_group_from_another_instance_panel.html.haml b/app/views/groups/_import_group_from_another_instance_panel.html.haml
index 2b9277c67e9..06a86c2465f 100644
--- a/app/views/groups/_import_group_from_another_instance_panel.html.haml
+++ b/app/views/groups/_import_group_from_another_instance_panel.html.haml
@@ -1,16 +1,15 @@
= form_with url: configure_import_bulk_imports_path, class: 'group-form gl-show-field-errors' do |f|
.gl-border-l-solid.gl-border-r-solid.gl-border-gray-100.gl-border-1.gl-p-5
- %h4.gl-display-flex
- = s_('GroupsNew|Import groups from another instance of GitLab')
- %span.badge.badge-info.badge-pill.gl-badge.md.gl-ml-3
- = _('Beta')
+ .gl-display-flex.gl-align-items-center
+ %h4.gl-display-flex
+ = s_('GroupsNew|Import groups from another instance of GitLab')
+ = link_to _('History'), history_import_bulk_imports_path, class: 'gl-link gl-ml-auto'
.gl-alert.gl-alert-warning{ role: 'alert' }
= sprite_icon('warning', css_class: 'gl-icon s16 gl-alert-icon gl-alert-icon-no-title')
.gl-alert-body
- docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index.md') }
- - feedback_link_start = '<a href="https://gitlab.com/gitlab-org/gitlab/-/issues/284495" target="_blank" rel="noopener noreferrer">'.html_safe
- - link_end = '</a>'.html_safe
- = s_('GroupsNew|Not all related objects are migrated, as %{docs_link_start}described here%{docs_link_end}. Please %{feedback_link_start}leave feedback%{feedback_link_end} on this feature.').html_safe % { docs_link_start: docs_link_start, docs_link_end: link_end, feedback_link_start: feedback_link_start, feedback_link_end: link_end }
+ - docs_link_end = '</a>'.html_safe
+ = s_('GroupsNew|Not all related objects are migrated. %{docs_link_start}More info%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: docs_link_end }
%p.gl-mt-3
= s_('GroupsNew|Provide credentials for another instance of GitLab to import your groups directly.')
.form-group.gl-display-flex.gl-flex-direction-column
diff --git a/app/views/import/bulk_imports/history.html.haml b/app/views/import/bulk_imports/history.html.haml
new file mode 100644
index 00000000000..80eb0c7a764
--- /dev/null
+++ b/app/views/import/bulk_imports/history.html.haml
@@ -0,0 +1,6 @@
+- add_to_breadcrumbs _('New group'), new_group_path
+- add_to_breadcrumbs _('Import group'), new_group_path(anchor: 'import-group-pane')
+- add_page_specific_style 'page_bundles/import'
+- page_title _('Import history')
+
+#import-history-mount-element
diff --git a/config/feature_flags/development/merge_request_discussion_cache.yml b/config/feature_flags/development/merge_request_discussion_cache.yml
deleted file mode 100644
index e90887fc2b3..00000000000
--- a/config/feature_flags/development/merge_request_discussion_cache.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: merge_request_discussion_cache
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64688
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/335799
-milestone: '14.1'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/feature_flags/development/sort_by_project_users_by_project_authorizations_user_id.yml b/config/feature_flags/development/sort_by_project_users_by_project_authorizations_user_id.yml
deleted file mode 100644
index 88a4e0b0472..00000000000
--- a/config/feature_flags/development/sort_by_project_users_by_project_authorizations_user_id.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: sort_by_project_users_by_project_authorizations_user_id
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64528
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334167
-milestone: '14.1'
-type: development
-group: group::optimize
-default_enabled: true
diff --git a/config/feature_flags/ops/show_terraform_banner.yml b/config/feature_flags/ops/show_terraform_banner.yml
new file mode 100644
index 00000000000..a4ec831f4e4
--- /dev/null
+++ b/config/feature_flags/ops/show_terraform_banner.yml
@@ -0,0 +1,8 @@
+---
+name: show_terraform_banner
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71462
+rollout_issue_url:
+milestone: '14.4'
+type: ops
+group: group::configure
+default_enabled: true
diff --git a/config/routes/import.rb b/config/routes/import.rb
index c35b67497da..9c76c4435ff 100644
--- a/config/routes/import.rb
+++ b/config/routes/import.rb
@@ -70,6 +70,7 @@ namespace :import do
post :configure
get :status
get :realtime_changes
+ get :history
end
resource :manifest, only: [:create, :new], controller: :manifest do
diff --git a/doc/administration/monitoring/performance/img/performance_bar_v14_4.png b/doc/administration/monitoring/performance/img/performance_bar_v14_4.png
index 1d3cefeb574..9ddc9825202 100644
--- a/doc/administration/monitoring/performance/img/performance_bar_v14_4.png
+++ b/doc/administration/monitoring/performance/img/performance_bar_v14_4.png
Binary files differ
diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md
index 9e27ea5697b..3da44e26855 100644
--- a/doc/administration/monitoring/performance/performance_bar.md
+++ b/doc/administration/monitoring/performance/performance_bar.md
@@ -65,7 +65,11 @@ From left to right, the performance bar displays:
can be added by its full URL (authenticated as the current user), or by the value of
its `X-Request-Id` header.
- **Download**: a link to download the raw JSON used to generate the Performance Bar reports.
-- **Flamegraph**: a link to generate a [flamegraph](../../../development/profiling.md#speedscope-flamegraphs) of the current URL.
+- **Flamegraph** with mode: a link to generate a [flamegraph](../../../development/profiling.md#speedscope-flamegraphs)
+ of the current URL with the selected [Stackprof mode](https://github.com/tmm1/stackprof#sampling):
+ - The **Wall** mode samples every *interval* of the time on a clock on a wall. The interval is set to `10100` microseconds.
+ - The **CPU** mode samples every *interval* of CPU activity. The interval is set to `10100` microseconds.
+ - The **Object** mode samples every *interval*. The interval is set to `100` allocations.
- **Request Selector**: a select box displayed on the right-hand side of the
Performance Bar which enables you to view these metrics for any requests made while
the current page was open. Only the first two requests per unique URL are captured.
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index 4753ce81821..656b30402a6 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -98,11 +98,13 @@ profile and log output to S3.
## Speedscope flamegraphs
-You can generate a flamegraph for a particular URL by selecting the flamegraph button in the performance bar or by adding the `performance_bar=flamegraph` parameter to the request.
+You can generate a flamegraph for a particular URL by selecting a flamegraph sampling mode button in the performance bar or by adding the `performance_bar=flamegraph` parameter to the request.
![Speedscope](img/speedscope_v13_12.png)
-More information about the views can be found in the [Speedscope docs](https://github.com/jlfwong/speedscope#views)
+Find more information about the views in the [Speedscope docs](https://github.com/jlfwong/speedscope#views).
+
+Find more information about different sampling modes in the [Stackprof docs](https://github.com/tmm1/stackprof#sampling).
This is enabled for all users that can access the performance bar.
diff --git a/doc/development/service_ping/index.md b/doc/development/service_ping/index.md
index 0a94fa2ff6c..791013db88e 100644
--- a/doc/development/service_ping/index.md
+++ b/doc/development/service_ping/index.md
@@ -110,7 +110,7 @@ To disable Service Ping in the GitLab UI:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand the **Usage statistics** section.
-1. Clear the **Enable service ping** checkbox.
+1. Clear the **Enable Service Ping** checkbox.
1. Select **Save changes**.
### Disable Service Ping using the configuration file
@@ -554,5 +554,5 @@ To work around this bug, you have two options:
1. In GitLab, on the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand **Usage Statistics**.
- 1. Clear the **Enable service ping** checkbox.
+ 1. Clear the **Enable Service Ping** checkbox.
1. Select **Save Changes**.
diff --git a/doc/development/service_ping/metrics_lifecycle.md b/doc/development/service_ping/metrics_lifecycle.md
index c0446aece8b..46040146de2 100644
--- a/doc/development/service_ping/metrics_lifecycle.md
+++ b/doc/development/service_ping/metrics_lifecycle.md
@@ -136,7 +136,10 @@ Product Intelligence team as inactive and is assigned to the group owner for rev
We are working on automating this process. See [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/338466) for details.
-Only deprecated metrics can be removed from Service Ping.
+Metrics can be removed from Service Ping if they:
+
+- Were previously [deprecated](#deprecate-a-metric).
+- Are not used in any Sisense dashboard.
For an example of the metric removal process take a look at this [example issue](https://gitlab.com/gitlab-org/gitlab/-/issues/297029)
diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md
index 330a25087ef..8ea38e14054 100644
--- a/doc/user/admin_area/settings/usage_statistics.md
+++ b/doc/user/admin_area/settings/usage_statistics.md
@@ -76,7 +76,7 @@ To enable or disable Service Ping and version check:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand **Usage statistics**.
-1. Select or clear the **Enable version check** and **Enable service ping** checkboxes.
+1. Select or clear the **Enable version check** and **Enable Service Ping** checkboxes.
1. Select **Save changes**.
<!-- ## Troubleshooting
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 34e0b528ced..b937c5e26cb 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -658,10 +658,7 @@ module API
users = DeclarativePolicy.subject_scope { user_project.team.users }
users = users.search(params[:search]) if params[:search].present?
users = users.where_not_in(params[:skip_users]) if params[:skip_users].present?
-
- if Feature.enabled?(:sort_by_project_users_by_project_authorizations_user_id, user_project, default_enabled: :yaml)
- users = users.order('project_authorizations.user_id' => :asc) # rubocop: disable CodeReuse/ActiveRecord
- end
+ users = users.order('project_authorizations.user_id' => :asc) # rubocop: disable CodeReuse/ActiveRecord
present paginate(users), with: Entities::UserBasic
end
diff --git a/lib/gitlab/ci/trace.rb b/lib/gitlab/ci/trace.rb
index efdbe7deae3..6816d216015 100644
--- a/lib/gitlab/ci/trace.rb
+++ b/lib/gitlab/ci/trace.rb
@@ -236,35 +236,7 @@ module Gitlab
end
def archive_stream!(stream)
- clone_file!(stream, JobArtifactUploader.workhorse_upload_path) do |clone_path|
- create_build_trace!(job, clone_path)
- end
- end
-
- def clone_file!(src_stream, temp_dir)
- FileUtils.mkdir_p(temp_dir)
- Dir.mktmpdir("tmp-trace-#{job.id}", temp_dir) do |dir_path|
- temp_path = File.join(dir_path, "job.log")
- FileUtils.touch(temp_path)
- size = IO.copy_stream(src_stream, temp_path)
- raise ArchiveError, 'Failed to copy stream' unless size == src_stream.size
-
- yield(temp_path)
- end
- end
-
- def create_build_trace!(job, path)
- File.open(path) do |stream|
- # TODO: Set `file_format: :raw` after we've cleaned up legacy traces migration
- # https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20307
- trace_artifact = job.create_job_artifacts_trace!(
- project: job.project,
- file_type: :trace,
- file: stream,
- file_sha256: self.class.sha256_hexdigest(path))
-
- trace_metadata.track_archival!(trace_artifact.id)
- end
+ ::Gitlab::Ci::Trace::Archive.new(job, trace_metadata).execute!(stream)
end
def trace_metadata
diff --git a/lib/gitlab/ci/trace/archive.rb b/lib/gitlab/ci/trace/archive.rb
new file mode 100644
index 00000000000..45d4ce92e71
--- /dev/null
+++ b/lib/gitlab/ci/trace/archive.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Trace
+ class Archive
+ include ::Gitlab::Utils::StrongMemoize
+ include Checksummable
+
+ def initialize(job, trace_metadata)
+ @job = job
+ @trace_metadata = trace_metadata
+ end
+
+ def execute!(stream)
+ clone_file!(stream, JobArtifactUploader.workhorse_upload_path) do |clone_path|
+ md5_checksum = self.class.md5_hexdigest(clone_path)
+ sha256_checksum = self.class.sha256_hexdigest(clone_path)
+
+ job.transaction do
+ trace_artifact = create_build_trace!(clone_path, sha256_checksum)
+ trace_metadata.track_archival!(trace_artifact.id, md5_checksum)
+ end
+ end
+ end
+
+ private
+
+ attr_reader :job, :trace_metadata
+
+ def clone_file!(src_stream, temp_dir)
+ FileUtils.mkdir_p(temp_dir)
+ Dir.mktmpdir("tmp-trace-#{job.id}", temp_dir) do |dir_path|
+ temp_path = File.join(dir_path, "job.log")
+ FileUtils.touch(temp_path)
+ size = IO.copy_stream(src_stream, temp_path)
+ raise ::Gitlab::Ci::Trace::ArchiveError, 'Failed to copy stream' unless size == src_stream.size
+
+ yield(temp_path)
+ end
+ end
+
+ def create_build_trace!(path, file_sha256)
+ File.open(path) do |stream|
+ # TODO: Set `file_format: :raw` after we've cleaned up legacy traces migration
+ # https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20307
+ job.create_job_artifacts_trace!(
+ project: job.project,
+ file_type: :trace,
+ file: stream,
+ file_sha256: file_sha256)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/middleware/speedscope.rb b/lib/gitlab/middleware/speedscope.rb
index 74f334d9ab3..6992ac9b720 100644
--- a/lib/gitlab/middleware/speedscope.rb
+++ b/lib/gitlab/middleware/speedscope.rb
@@ -19,11 +19,12 @@ module Gitlab
require 'stackprof'
begin
+ mode = stackprof_mode(request)
flamegraph = ::StackProf.run(
- mode: :wall,
+ mode: mode,
raw: true,
aggregate: false,
- interval: ::Gitlab::StackProf::DEFAULT_INTERVAL_US
+ interval: ::Gitlab::StackProf.interval(mode)
) do
_, _, body = @app.call(env)
end
@@ -64,7 +65,7 @@ module Gitlab
var iframe = document.createElement('IFRAME');
iframe.setAttribute('id', 'speedscope-iframe');
document.body.appendChild(iframe);
- var iframeUrl = '#{speedscope_path}#profileURL=' + objUrl + '&title=' + 'Flamegraph for #{CGI.escape(path)}';
+ var iframeUrl = '#{speedscope_path}#profileURL=' + objUrl + '&title=' + 'Flamegraph for #{CGI.escape(path)} in #{stackprof_mode(request)} mode';
iframe.setAttribute('src', iframeUrl);
</script>
</body>
@@ -73,6 +74,17 @@ module Gitlab
[200, headers, [html]]
end
+
+ def stackprof_mode(request)
+ case request.params['stackprof_mode']&.to_sym
+ when :cpu
+ :cpu
+ when :object
+ :object
+ else
+ :wall
+ end
+ end
end
end
end
diff --git a/lib/gitlab/stack_prof.rb b/lib/gitlab/stack_prof.rb
index 97f52491e9e..9fc4798ffdc 100644
--- a/lib/gitlab/stack_prof.rb
+++ b/lib/gitlab/stack_prof.rb
@@ -75,20 +75,20 @@ module Gitlab
current_timeout_s = nil
else
mode = ENV['STACKPROF_MODE']&.to_sym || DEFAULT_MODE
- interval = ENV['STACKPROF_INTERVAL']&.to_i
- interval ||= (mode == :object ? DEFAULT_INTERVAL_EVENTS : DEFAULT_INTERVAL_US)
+ stackprof_interval = ENV['STACKPROF_INTERVAL']&.to_i
+ stackprof_interval ||= interval(mode)
log_event(
'starting profile',
profile_mode: mode,
- profile_interval: interval,
+ profile_interval: stackprof_interval,
profile_timeout: timeout_s
)
::StackProf.start(
mode: mode,
raw: Gitlab::Utils.to_boolean(ENV['STACKPROF_RAW'] || 'true'),
- interval: interval
+ interval: stackprof_interval
)
current_timeout_s = timeout_s
end
@@ -131,5 +131,9 @@ module Gitlab
pid: Process.pid
}.merge(labels.compact))
end
+
+ def self.interval(mode)
+ mode == :object ? DEFAULT_INTERVAL_EVENTS : DEFAULT_INTERVAL_US
+ end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 32337f729ff..6f48a77f9be 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -715,15 +715,15 @@ msgstr ""
msgid "%{link_start}Learn more%{link_end} about roles."
msgstr ""
-msgid "%{link_start}Learn more%{link_end} about what information is shared with GitLab Inc."
-msgstr ""
-
msgid "%{link_start}Remove the %{draft_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it's ready."
msgstr ""
msgid "%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request that is a work in progress from being merged before it's ready."
msgstr ""
+msgid "%{link_start}What information does GitLab Inc. collect?%{link_end}"
+msgstr ""
+
msgid "%{listToShow}, and %{awardsListLength} more"
msgstr ""
@@ -876,7 +876,7 @@ msgid_plural "%{securityScanner} results are not available because a pipeline ha
msgstr[0] ""
msgstr[1] ""
-msgid "%{service_ping_link_start}Learn more%{service_ping_link_end} about what information is shared with GitLab Inc."
+msgid "%{service_ping_link_start}What information is shared with GitLab Inc.?%{service_ping_link_end}"
msgstr ""
msgid "%{size} %{unit}"
@@ -5179,9 +5179,6 @@ msgstr ""
msgid "Below you will find all the groups that are public."
msgstr ""
-msgid "Beta"
-msgstr ""
-
msgid "Bi-weekly code coverage"
msgstr ""
@@ -5860,6 +5857,9 @@ msgstr ""
msgid "BulkImport|From source group"
msgstr ""
+msgid "BulkImport|Group import history"
+msgstr ""
+
msgid "BulkImport|Import failed: Destination cannot be a subgroup of the source group. Change the destination and try again."
msgstr ""
@@ -5878,6 +5878,12 @@ msgstr ""
msgid "BulkImport|Name already exists."
msgstr ""
+msgid "BulkImport|New group"
+msgstr ""
+
+msgid "BulkImport|No history is available"
+msgstr ""
+
msgid "BulkImport|No parent"
msgstr ""
@@ -5890,6 +5896,9 @@ msgstr ""
msgid "BulkImport|Showing %{start}-%{end} of %{total} matching filter \"%{filter}\" from %{link}"
msgstr ""
+msgid "BulkImport|Source group"
+msgstr ""
+
msgid "BulkImport|To new group"
msgstr ""
@@ -5899,6 +5908,9 @@ msgstr ""
msgid "BulkImport|You have no groups to import"
msgstr ""
+msgid "BulkImport|Your imported groups will appear here."
+msgstr ""
+
msgid "BulkImport|expected an associated Group but has an associated Project"
msgstr ""
@@ -12580,6 +12592,9 @@ msgstr ""
msgid "Enable SSL verification"
msgstr ""
+msgid "Enable Service Ping"
+msgstr ""
+
msgid "Enable Snowplow tracking"
msgstr ""
@@ -12673,7 +12688,7 @@ msgstr ""
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
-msgid "Enable or disable version check and service ping."
+msgid "Enable or disable version check and Service Ping."
msgstr ""
msgid "Enable protected paths rate limit"
@@ -12691,9 +12706,6 @@ msgstr ""
msgid "Enable repository checks"
msgstr ""
-msgid "Enable service ping"
-msgstr ""
-
msgid "Enable shared runners for all projects and subgroups in this group."
msgstr ""
@@ -15514,6 +15526,9 @@ msgstr ""
msgid "GitLab group: %{source_link}"
msgstr ""
+msgid "GitLab informs you if a new version is available."
+msgstr ""
+
msgid "GitLab is a complete DevOps platform, delivered as a single application, fundamentally changing the way Development, Security, and Ops teams collaborate"
msgstr ""
@@ -15556,9 +15571,6 @@ msgstr ""
msgid "GitLab version"
msgstr ""
-msgid "GitLab will inform you if a new version is available."
-msgstr ""
-
msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
msgstr ""
@@ -16603,7 +16615,7 @@ msgstr ""
msgid "GroupsNew|No import options available"
msgstr ""
-msgid "GroupsNew|Not all related objects are migrated, as %{docs_link_start}described here%{docs_link_end}. Please %{feedback_link_start}leave feedback%{feedback_link_end} on this feature."
+msgid "GroupsNew|Not all related objects are migrated. %{docs_link_start}More info%{docs_link_end}."
msgstr ""
msgid "GroupsNew|Personal access token"
@@ -17208,12 +17220,18 @@ msgstr ""
msgid "Import from Jira"
msgstr ""
+msgid "Import group"
+msgstr ""
+
msgid "Import group from file"
msgstr ""
msgid "Import groups"
msgstr ""
+msgid "Import history"
+msgstr ""
+
msgid "Import in progress"
msgstr ""
@@ -20397,7 +20415,7 @@ msgstr ""
msgid "Limit namespaces and projects that can be indexed"
msgstr ""
-msgid "Limit project size at a global, group and project level. %{link_start}Learn more%{link_end}."
+msgid "Limit project size at a global, group, and project level. %{link_start}Learn more%{link_end}."
msgstr ""
msgid "Limit sign in from multiple IP addresses"
@@ -24649,7 +24667,7 @@ msgstr ""
msgid "PerformanceBar|First Contentful Paint"
msgstr ""
-msgid "PerformanceBar|Flamegraph"
+msgid "PerformanceBar|Flamegraph with mode:"
msgstr ""
msgid "PerformanceBar|Frontend resources"
@@ -24685,6 +24703,15 @@ msgstr ""
msgid "PerformanceBar|Trace"
msgstr ""
+msgid "PerformanceBar|cpu"
+msgstr ""
+
+msgid "PerformanceBar|object"
+msgstr ""
+
+msgid "PerformanceBar|wall"
+msgstr ""
+
msgid "Period in seconds"
msgstr ""
@@ -35451,7 +35478,7 @@ msgstr ""
msgid "To define internal users, first enable new users set to external"
msgstr ""
-msgid "To enable Registration Features, make sure \"Enable service ping\" is checked."
+msgid "To enable Registration Features, first enable Service Ping."
msgstr ""
msgid "To ensure no loss of personal content, this account should only be used for matters related to %{group_name}."
@@ -35478,7 +35505,7 @@ msgstr ""
msgid "To get started, use the link below to confirm your account."
msgstr ""
-msgid "To help improve GitLab and its user experience, GitLab will periodically collect usage information."
+msgid "To help improve GitLab and its user experience, GitLab periodically collects usage information."
msgstr ""
msgid "To help improve GitLab, we would like to periodically %{docs_link}. This can be changed at any time in %{settings_link}."
diff --git a/spec/features/groups/import_export/migration_history_spec.rb b/spec/features/groups/import_export/migration_history_spec.rb
new file mode 100644
index 00000000000..243bdcc13a9
--- /dev/null
+++ b/spec/features/groups/import_export/migration_history_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Import/Export - GitLab migration history', :js do
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:user_import_1) { create(:bulk_import, user: user) }
+ let_it_be(:finished_entity_1) { create(:bulk_import_entity, :finished, bulk_import: user_import_1) }
+
+ let_it_be(:user_import_2) { create(:bulk_import, user: user) }
+ let_it_be(:failed_entity_2) { create(:bulk_import_entity, :failed, bulk_import: user_import_2) }
+
+ before do
+ gitlab_sign_in(user)
+
+ visit new_group_path
+
+ click_link 'Import group'
+ end
+
+ it 'successfully displays import history' do
+ click_link 'History'
+
+ wait_for_requests
+
+ expect(page).to have_content 'Group import history'
+ expect(page.find('tbody')).to have_css('tr', count: 2)
+ end
+end
diff --git a/spec/frontend/import_entities/components/pagination_bar_spec.js b/spec/frontend/import_entities/components/pagination_bar_spec.js
new file mode 100644
index 00000000000..163ce11a8db
--- /dev/null
+++ b/spec/frontend/import_entities/components/pagination_bar_spec.js
@@ -0,0 +1,92 @@
+import { GlPagination, GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import PaginationBar from '~/import_entities/components/pagination_bar.vue';
+import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
+
+describe('Pagination bar', () => {
+ const DEFAULT_PROPS = {
+ pageInfo: {
+ total: 50,
+ page: 1,
+ perPage: 20,
+ },
+ itemsCount: 17,
+ };
+ let wrapper;
+
+ const createComponent = (propsData) => {
+ wrapper = mount(PaginationBar, {
+ propsData: {
+ ...DEFAULT_PROPS,
+ ...propsData,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('events', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('emits set-page event when page is selected', () => {
+ const NEXT_PAGE = 3;
+ // PaginationLinks uses prop instead of event for handling page change
+ // So we go one level deep to test this
+ wrapper
+ .findComponent(PaginationLinks)
+ .findComponent(GlPagination)
+ .vm.$emit('input', NEXT_PAGE);
+ expect(wrapper.emitted('set-page')).toEqual([[NEXT_PAGE]]);
+ });
+
+ it('emits set-page-size event when page size is selected', () => {
+ const firstItemInPageSizeDropdown = wrapper.findComponent(GlDropdownItem);
+ firstItemInPageSizeDropdown.vm.$emit('click');
+
+ const [emittedPageSizeChange] = wrapper.emitted('set-page-size')[0];
+ expect(firstItemInPageSizeDropdown.text()).toMatchInterpolatedText(
+ `${emittedPageSizeChange} items per page`,
+ );
+ });
+ });
+
+ it('renders current page size', () => {
+ const CURRENT_PAGE_SIZE = 40;
+
+ createComponent({
+ pageInfo: {
+ ...DEFAULT_PROPS.pageInfo,
+ perPage: CURRENT_PAGE_SIZE,
+ },
+ });
+
+ expect(wrapper.find(GlDropdown).find('button').text()).toMatchInterpolatedText(
+ `${CURRENT_PAGE_SIZE} items per page`,
+ );
+ });
+
+ it('renders current page information', () => {
+ createComponent();
+
+ expect(wrapper.find('[data-testid="information"]').text()).toMatchInterpolatedText(
+ 'Showing 1 - 17 of 50',
+ );
+ });
+
+ it('renders current page information when total count is over 1000', () => {
+ createComponent({
+ pageInfo: {
+ ...DEFAULT_PROPS.pageInfo,
+ total: 1200,
+ },
+ });
+
+ expect(wrapper.find('[data-testid="information"]').text()).toMatchInterpolatedText(
+ 'Showing 1 - 17 of 1000+',
+ );
+ });
+});
diff --git a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
new file mode 100644
index 00000000000..d6b394a42c6
--- /dev/null
+++ b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
@@ -0,0 +1,175 @@
+import { GlEmptyState, GlLoadingIcon, GlTable } from '@gitlab/ui';
+import { mount, shallowMount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import PaginationBar from '~/import_entities/components/pagination_bar.vue';
+import BulkImportsHistoryApp from '~/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+
+describe('BulkImportsHistoryApp', () => {
+ const API_URL = '/api/v4/bulk_imports/entities';
+
+ const DEFAULT_HEADERS = {
+ 'x-page': 1,
+ 'x-per-page': 20,
+ 'x-next-page': 2,
+ 'x-total': 22,
+ 'x-total-pages': 2,
+ 'x-prev-page': null,
+ };
+ const DUMMY_RESPONSE = [
+ {
+ id: 1,
+ bulk_import_id: 1,
+ status: 'finished',
+ source_full_path: 'top-level-group-12',
+ destination_name: 'top-level-group-12',
+ destination_namespace: 'h5bp',
+ created_at: '2021-07-08T10:03:44.743Z',
+ failures: [],
+ },
+ {
+ id: 2,
+ bulk_import_id: 2,
+ status: 'failed',
+ source_full_path: 'autodevops-demo',
+ destination_name: 'autodevops-demo',
+ destination_namespace: 'flightjs',
+ parent_id: null,
+ namespace_id: null,
+ project_id: null,
+ created_at: '2021-07-13T12:52:26.664Z',
+ updated_at: '2021-07-13T13:34:49.403Z',
+ failures: [
+ {
+ pipeline_class: 'BulkImports::Groups::Pipelines::GroupPipeline',
+ pipeline_step: 'loader',
+ exception_class: 'ActiveRecord::RecordNotUnique',
+ correlation_id_value: '01FAFYSYZ7XPF3P9NSMTS693SZ',
+ created_at: '2021-07-13T13:34:49.344Z',
+ },
+ ],
+ },
+ ];
+
+ let wrapper;
+ let mock;
+
+ function createComponent({ shallow = true } = {}) {
+ const mountFn = shallow ? shallowMount : mount;
+ wrapper = mountFn(BulkImportsHistoryApp);
+ }
+
+ const originalApiVersion = gon.api_version;
+ beforeAll(() => {
+ gon.api_version = 'v4';
+ });
+
+ afterAll(() => {
+ gon.api_version = originalApiVersion;
+ });
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ wrapper.destroy();
+ });
+
+ describe('general behavior', () => {
+ it('renders loading state when loading', () => {
+ createComponent();
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('renders empty state when no data is available', async () => {
+ mock.onGet(API_URL).reply(200, [], DEFAULT_HEADERS);
+ createComponent();
+ await axios.waitForAll();
+
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.find(GlEmptyState).exists()).toBe(true);
+ });
+
+ it('renders table with data when history is available', async () => {
+ mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS);
+ createComponent();
+ await axios.waitForAll();
+
+ const table = wrapper.find(GlTable);
+ expect(table.exists()).toBe(true);
+ // can't use .props() or .attributes() here
+ expect(table.vm.$attrs.items).toHaveLength(DUMMY_RESPONSE.length);
+ });
+
+ it('changes page when requested by pagination bar', async () => {
+ const NEW_PAGE = 4;
+
+ mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS);
+ createComponent();
+ await axios.waitForAll();
+ mock.resetHistory();
+
+ wrapper.findComponent(PaginationBar).vm.$emit('set-page', NEW_PAGE);
+ await axios.waitForAll();
+
+ expect(mock.history.get.length).toBe(1);
+ expect(mock.history.get[0].params).toStrictEqual(expect.objectContaining({ page: NEW_PAGE }));
+ });
+ });
+
+ it('changes page size when requested by pagination bar', async () => {
+ const NEW_PAGE_SIZE = 4;
+
+ mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS);
+ createComponent();
+ await axios.waitForAll();
+ mock.resetHistory();
+
+ wrapper.findComponent(PaginationBar).vm.$emit('set-page-size', NEW_PAGE_SIZE);
+ await axios.waitForAll();
+
+ expect(mock.history.get.length).toBe(1);
+ expect(mock.history.get[0].params).toStrictEqual(
+ expect.objectContaining({ per_page: NEW_PAGE_SIZE }),
+ );
+ });
+
+ describe('details button', () => {
+ beforeEach(() => {
+ mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS);
+ createComponent({ shallow: false });
+ return axios.waitForAll();
+ });
+
+ it('renders details button if relevant item has failures', async () => {
+ expect(
+ extendedWrapper(wrapper.find('tbody').findAll('tr').at(1)).findByText('Details').exists(),
+ ).toBe(true);
+ });
+
+ it('does not render details button if relevant item has no failures', () => {
+ expect(
+ extendedWrapper(wrapper.find('tbody').findAll('tr').at(0)).findByText('Details').exists(),
+ ).toBe(false);
+ });
+
+ it('expands details when details button is clicked', async () => {
+ const ORIGINAL_ROW_INDEX = 1;
+ await extendedWrapper(wrapper.find('tbody').findAll('tr').at(ORIGINAL_ROW_INDEX))
+ .findByText('Details')
+ .trigger('click');
+
+ const detailsRowContent = wrapper
+ .find('tbody')
+ .findAll('tr')
+ .at(ORIGINAL_ROW_INDEX + 1)
+ .find('pre');
+
+ expect(detailsRowContent.exists()).toBe(true);
+ expect(JSON.parse(detailsRowContent.text())).toStrictEqual(DUMMY_RESPONSE[1].failures);
+ });
+ });
+});
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 85b572d3f68..944ed621929 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -904,6 +904,14 @@ RSpec.describe ProjectsHelper do
it { is_expected.to be_falsey }
end
+
+ context 'the :show_terraform_banner feature flag is disabled' do
+ before do
+ stub_feature_flags(show_terraform_banner: false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
end
end
diff --git a/spec/lib/gitlab/ci/trace/archive_spec.rb b/spec/lib/gitlab/ci/trace/archive_spec.rb
new file mode 100644
index 00000000000..1e63d7faea0
--- /dev/null
+++ b/spec/lib/gitlab/ci/trace/archive_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Trace::Archive do
+ let_it_be(:job) { create(:ci_build, :success, :trace_live) }
+ let_it_be(:trace_metadata) { create(:ci_build_trace_metadata, build: job) }
+ let_it_be(:src_checksum) do
+ job.trace.read { |stream| Digest::MD5.hexdigest(stream.raw) }
+ end
+
+ describe '#execute' do
+ subject { described_class.new(job, trace_metadata) }
+
+ it 'computes and assigns checksum' do
+ Gitlab::Ci::Trace::ChunkedIO.new(job) do |stream|
+ expect { subject.execute!(stream) }.to change { Ci::JobArtifact.count }.by(1)
+ end
+
+ expect(trace_metadata.checksum).to eq(src_checksum)
+ expect(trace_metadata.trace_artifact).to eq(job.job_artifacts_trace)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/middleware/speedscope_spec.rb b/spec/lib/gitlab/middleware/speedscope_spec.rb
index bb830a2fbda..c1d452f69f8 100644
--- a/spec/lib/gitlab/middleware/speedscope_spec.rb
+++ b/spec/lib/gitlab/middleware/speedscope_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::Middleware::Speedscope do
allow(env).to receive(:[]).with('warden').and_return(double('Warden', user: create(:admin)))
end
- it 'runs StackProf and returns a flamegraph' do
+ it 'returns a flamegraph' do
expect(StackProf).to receive(:run).and_call_original
status, headers, body = middleware.call(env)
@@ -55,6 +55,56 @@ RSpec.describe Gitlab::Middleware::Speedscope do
expect(headers).to eq({ 'Content-Type' => 'text/html' })
expect(body.first).to include('speedscope-iframe')
end
+
+ context 'when the stackprof_mode parameter is set and valid' do
+ let(:env) { Rack::MockRequest.env_for('/', params: { 'performance_bar' => 'flamegraph', 'stackprof_mode' => 'cpu' }) }
+
+ it 'runs StackProf in the mode specified in the stackprof_mode parameter' do
+ expect(StackProf).to receive(:run).with(hash_including(mode: :cpu))
+
+ middleware.call(env)
+ end
+ end
+
+ context 'when the stackprof_mode parameter is not set' do
+ let(:env) { Rack::MockRequest.env_for('/', params: { 'performance_bar' => 'flamegraph' }) }
+
+ it 'runs StackProf in wall mode' do
+ expect(StackProf).to receive(:run).with(hash_including(mode: :wall))
+
+ middleware.call(env)
+ end
+ end
+
+ context 'when the stackprof_mode parameter is invalid' do
+ let(:env) { Rack::MockRequest.env_for('/', params: { 'performance_bar' => 'flamegraph', 'stackprof_mode' => 'invalid' }) }
+
+ it 'runs StackProf in wall mode' do
+ expect(StackProf).to receive(:run).with(hash_including(mode: :wall))
+
+ middleware.call(env)
+ end
+ end
+
+ context 'when the stackprof_mode parameter is set to object mode' do
+ let(:env) { Rack::MockRequest.env_for('/', params: { 'performance_bar' => 'flamegraph', 'stackprof_mode' => 'object' }) }
+
+ it 'runs StackProf with an interval of 100' do
+ expect(StackProf).to receive(:run).with(hash_including(interval: 100))
+
+ middleware.call(env)
+ end
+ end
+
+ context 'when the stackprof_mode parameter is not set to object mode' do
+ let(:env) { Rack::MockRequest.env_for('/', params: { 'performance_bar' => 'flamegraph', 'stackprof_mode' => 'wall' }) }
+
+ it 'runs StackProf with an interval of 10_100' do
+ expect(StackProf).to receive(:run).with(hash_including(interval: 10_100))
+
+ middleware.call(env)
+ end
+ end
end
end
end
diff --git a/spec/models/ci/build_trace_metadata_spec.rb b/spec/models/ci/build_trace_metadata_spec.rb
index 5e4645c5dc4..5d6f1878388 100644
--- a/spec/models/ci/build_trace_metadata_spec.rb
+++ b/spec/models/ci/build_trace_metadata_spec.rb
@@ -88,14 +88,16 @@ RSpec.describe Ci::BuildTraceMetadata do
describe '#track_archival!' do
let(:trace_artifact) { create(:ci_job_artifact) }
let(:metadata) { create(:ci_build_trace_metadata) }
+ let(:checksum) { SecureRandom.hex }
it 'stores the artifact id and timestamp' do
expect(metadata.trace_artifact_id).to be_nil
- metadata.track_archival!(trace_artifact.id)
+ metadata.track_archival!(trace_artifact.id, checksum)
metadata.reload
expect(metadata.trace_artifact_id).to eq(trace_artifact.id)
+ expect(metadata.checksum).to eq(checksum)
expect(metadata.archived_at).to be_like_time(Time.current)
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index be8a6c7bdcf..0febc769729 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -2684,26 +2684,9 @@ RSpec.describe API::Projects do
context 'when authenticated' do
context 'valid request' do
- context 'when sort_by_project_authorizations_user_id FF is off' do
- before do
- stub_feature_flags(sort_by_project_users_by_project_authorizations_user_id: false)
- end
-
- it_behaves_like 'project users response' do
- let(:project) { project4 }
- let(:current_user) { user4 }
- end
- end
-
- context 'when sort_by_project_authorizations_user_id FF is on' do
- before do
- stub_feature_flags(sort_by_project_users_by_project_authorizations_user_id: true)
- end
-
- it_behaves_like 'project users response' do
- let(:project) { project4 }
- let(:current_user) { user4 }
- end
+ it_behaves_like 'project users response' do
+ let(:project) { project4 }
+ let(:current_user) { user4 }
end
end
diff --git a/spec/requests/projects/merge_requests_discussions_spec.rb b/spec/requests/projects/merge_requests_discussions_spec.rb
index 26402bad8aa..4921a43ab8b 100644
--- a/spec/requests/projects/merge_requests_discussions_spec.rb
+++ b/spec/requests/projects/merge_requests_discussions_spec.rb
@@ -251,16 +251,6 @@ RSpec.describe 'merge requests discussions' do
let(:changed_notes) { [first_note, second_note] }
end
end
-
- context 'when merge_request_discussion_cache is disabled' do
- before do
- stub_feature_flags(merge_request_discussion_cache: false)
- end
-
- it_behaves_like 'cache miss' do
- let(:changed_notes) { [first_note, second_note] }
- end
- end
end
end
end