Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-11-24 18:09:57 +00:00
parent 92849dc177
commit 80b22a4413
119 changed files with 1216 additions and 620 deletions

View File

@ -1,26 +1,11 @@
---
Capybara/TestidFinders:
Exclude:
- 'spec/features/dashboard/projects_spec.rb'
- 'spec/features/dashboard/todos/todos_spec.rb'
- 'spec/features/groups/board_sidebar_spec.rb'
- 'spec/features/groups/board_spec.rb'
- 'spec/features/groups/clusters/user_spec.rb'
- 'spec/features/groups/dependency_proxy_spec.rb'
- 'spec/features/groups/group_settings_spec.rb'
- 'spec/features/groups/members/leave_group_spec.rb'
- 'spec/features/groups/members/manage_groups_spec.rb'
- 'spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb'
- 'spec/features/groups/members/search_members_spec.rb'
- 'spec/features/groups/members/sort_members_spec.rb'
- 'spec/features/groups/members/tabs_spec.rb'
- 'spec/features/groups/merge_requests_spec.rb'
- 'spec/features/issues/issue_sidebar_spec.rb'
- 'spec/features/issues/issue_state_spec.rb'
- 'spec/features/issues/user_creates_issue_spec.rb'
- 'spec/features/issues/user_edits_issue_spec.rb'
- 'spec/features/issues/user_resets_their_incoming_email_token_spec.rb'
- 'spec/features/issues/user_sees_live_update_spec.rb'
- 'spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb'
- 'spec/features/issues/user_toggles_subscription_spec.rb'
- 'spec/features/labels_hierarchy_spec.rb'
@ -38,7 +23,6 @@ Capybara/TestidFinders:
- 'spec/features/merge_request/user_merges_immediately_spec.rb'
- 'spec/features/merge_request/user_posts_notes_spec.rb'
- 'spec/features/merge_request/user_resolves_conflicts_spec.rb'
- 'spec/features/merge_request/user_reverts_merge_request_spec.rb'
- 'spec/features/merge_request/user_sees_merge_widget_spec.rb'
- 'spec/features/merge_request/user_sees_pipelines_spec.rb'
- 'spec/features/merge_request/user_sees_suggest_pipeline_spec.rb'
@ -47,7 +31,6 @@ Capybara/TestidFinders:
- 'spec/features/merge_request/user_toggles_whitespace_changes_spec.rb'
- 'spec/features/merge_request/user_views_open_merge_request_spec.rb'
- 'spec/features/milestone_spec.rb'
- 'spec/features/nav/new_nav_callout_spec.rb'
- 'spec/features/nav/pinned_nav_items_spec.rb'
- 'spec/features/populate_new_pipeline_vars_with_params_spec.rb'
- 'spec/features/profile_spec.rb'
@ -61,7 +44,6 @@ Capybara/TestidFinders:
- 'spec/features/profiles/user_edit_profile_spec.rb'
- 'spec/features/profiles/user_updates_comment_template_spec.rb'
- 'spec/features/project_group_variables_spec.rb'
- 'spec/features/project_variables_spec.rb'
- 'spec/features/projects/blobs/blame_spec.rb'
- 'spec/features/projects/branches/user_deletes_branch_spec.rb'
- 'spec/features/projects/branches_spec.rb'
@ -76,7 +58,6 @@ Capybara/TestidFinders:
- 'spec/features/projects/environments/environment_spec.rb'
- 'spec/features/projects/environments/environments_spec.rb'
- 'spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb'
- 'spec/features/projects/features_visibility_spec.rb'
- 'spec/features/projects/fork_spec.rb'
- 'spec/features/projects/integrations/user_activates_jira_spec.rb'
- 'spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb'
@ -111,7 +92,6 @@ Capybara/TestidFinders:
- 'spec/features/projects/terraform_spec.rb'
- 'spec/features/projects/tree/create_directory_spec.rb'
- 'spec/features/projects/tree/create_file_spec.rb'
- 'spec/features/projects/user_uses_shortcuts_spec.rb'
- 'spec/features/projects/work_items/work_item_children_spec.rb'
- 'spec/features/projects/work_items/work_item_spec.rb'
- 'spec/features/protected_branches_spec.rb'
@ -121,10 +101,8 @@ Capybara/TestidFinders:
- 'spec/features/search/user_searches_for_merge_requests_spec.rb'
- 'spec/features/search/user_searches_for_milestones_spec.rb'
- 'spec/features/search/user_searches_for_wiki_pages_spec.rb'
- 'spec/features/search/user_uses_header_search_field_spec.rb'
- 'spec/features/search/user_uses_search_filters_spec.rb'
- 'spec/features/tags/developer_deletes_tag_spec.rb'
- 'spec/features/tags/maintainer_deletes_protected_tag_spec.rb'
- 'spec/features/triggers_spec.rb'
- 'spec/features/uploads/user_uploads_avatar_to_profile_spec.rb'
- 'spec/features/user_sees_revert_modal_spec.rb'

View File

@ -250,7 +250,6 @@ Gitlab/NamespacedClass:
- 'app/models/plan.rb'
- 'app/models/plan_limits.rb'
- 'app/models/pool_repository.rb'
- 'app/models/product_analytics_event.rb'
- 'app/models/programming_language.rb'
- 'app/models/project.rb'
- 'app/models/project_authorization.rb'

View File

@ -581,7 +581,6 @@ Layout/EmptyLineAfterMagicComment:
- 'spec/models/packages/rpm/repository_file_spec.rb'
- 'spec/models/packages/rubygems/metadatum_spec.rb'
- 'spec/models/packages/tag_spec.rb'
- 'spec/models/product_analytics_event_spec.rb'
- 'spec/policies/design_management/design_policy_spec.rb'
- 'spec/requests/api/composer_packages_spec.rb'
- 'spec/requests/api/conan_project_packages_spec.rb'

View File

@ -1994,7 +1994,6 @@ RSpec/ContextWording:
- 'spec/lib/object_storage/direct_upload_spec.rb'
- 'spec/lib/omni_auth/strategies/jwt_spec.rb'
- 'spec/lib/peek/views/bullet_detailed_spec.rb'
- 'spec/lib/product_analytics/event_params_spec.rb'
- 'spec/lib/safe_zip/entry_spec.rb'
- 'spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb'
- 'spec/lib/security/ci_configuration/sast_build_action_spec.rb'

View File

@ -4223,7 +4223,6 @@ RSpec/FeatureCategory:
- 'spec/lib/peek/views/external_http_spec.rb'
- 'spec/lib/peek/views/memory_spec.rb'
- 'spec/lib/peek/views/redis_detailed_spec.rb'
- 'spec/lib/product_analytics/event_params_spec.rb'
- 'spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb'
- 'spec/lib/prometheus/pid_provider_spec.rb'
- 'spec/lib/quality/seeders/issues_spec.rb'
@ -4709,7 +4708,6 @@ RSpec/FeatureCategory:
- 'spec/models/preloaders/project_root_ancestor_preloader_spec.rb'
- 'spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb'
- 'spec/models/preloaders/user_max_access_level_in_projects_preloader_spec.rb'
- 'spec/models/product_analytics_event_spec.rb'
- 'spec/models/programming_language_spec.rb'
- 'spec/models/project_authorization_spec.rb'
- 'spec/models/project_auto_devops_spec.rb'

View File

@ -473,7 +473,6 @@ RSpec/NamedSubject:
- 'ee/spec/lib/gitlab/llm/chat_message_spec.rb'
- 'ee/spec/lib/gitlab/llm/chat_storage_spec.rb'
- 'ee/spec/lib/gitlab/llm/completions/chat_spec.rb'
- 'ee/spec/lib/gitlab/llm/concerns/circuit_breaker_spec.rb'
- 'ee/spec/lib/gitlab/llm/concerns/exponential_backoff_spec.rb'
- 'ee/spec/lib/gitlab/llm/graphql_subscription_response_service_spec.rb'
- 'ee/spec/lib/gitlab/llm/templates/categorize_question_spec.rb'
@ -2503,7 +2502,6 @@ RSpec/NamedSubject:
- 'spec/lib/peek/views/external_http_spec.rb'
- 'spec/lib/peek/views/memory_spec.rb'
- 'spec/lib/peek/views/redis_detailed_spec.rb'
- 'spec/lib/product_analytics/event_params_spec.rb'
- 'spec/lib/quality/seeders/issues_spec.rb'
- 'spec/lib/release_highlights/validator/entry_spec.rb'
- 'spec/lib/release_highlights/validator_spec.rb'
@ -3437,8 +3435,6 @@ RSpec/NamedSubject:
- 'spec/services/personal_access_tokens/last_used_service_spec.rb'
- 'spec/services/personal_access_tokens/revoke_service_spec.rb'
- 'spec/services/post_receive_service_spec.rb'
- 'spec/services/product_analytics/build_activity_graph_service_spec.rb'
- 'spec/services/product_analytics/build_graph_service_spec.rb'
- 'spec/services/projects/alerting/notify_service_spec.rb'
- 'spec/services/projects/all_issues_count_service_spec.rb'
- 'spec/services/projects/all_merge_requests_count_service_spec.rb'

View File

@ -2081,7 +2081,6 @@ Style/InlineDisableAnnotation:
- 'ee/spec/lib/gitlab/import_export/project/relation_factory_spec.rb'
- 'ee/spec/lib/gitlab/llm/chain/agents/zero_shot/executor_real_requests_spec.rb'
- 'ee/spec/lib/gitlab/llm/chain/tools/epic_identifier/executor_spec.rb'
- 'ee/spec/lib/gitlab/llm/concerns/circuit_breaker_spec.rb'
- 'ee/spec/lib/gitlab/mirror_spec.rb'
- 'ee/spec/lib/gitlab/patch/database_config_spec.rb'
- 'ee/spec/lib/gitlab/sitemaps/sitemap_file_spec.rb'

View File

@ -2,6 +2,7 @@
import { GlBanner, GlLink } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import BetaBadge from '~/vue_shared/components/badges/beta_badge.vue';
import { CATALOG_FEEDBACK_DISMISSED_KEY } from '../../constants';
const defaultTitle = __('CI/CD Catalog');
@ -11,6 +12,7 @@ const defaultDescription = s__(
export default {
components: {
BetaBadge,
GlBanner,
GlLink,
},
@ -58,7 +60,10 @@ export default {
{{ $options.i18n.banner.description }}
</p>
</gl-banner>
<h1 class="page-title gl-font-size-h-display">{{ pageTitle }}</h1>
<div class="gl-my-4 gl-display-flex gl-align-items-center">
<h1 class="gl-m-0 gl-font-size-h-display">{{ pageTitle }}</h1>
<beta-badge class="gl-ml-3" />
</div>
<p>
<span data-testid="page-description">{{ pageDescription }}</span>
<gl-link :href="$options.learnMorePath" target="_blank">{{

View File

@ -14,7 +14,7 @@ export default function initClonePanel() {
$(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
}
$('a', $cloneOptions).on('click', (e) => {
$('.js-clone-links a', $cloneOptions).on('click', (e) => {
const $this = $(e.currentTarget);
const url = $this.attr('href');
if (

View File

@ -0,0 +1,21 @@
<script>
import { s__ } from '~/locale';
export default {
props: {
folderName: {
type: String,
required: true,
},
},
i18n: {
pageTitle: s__('Environments|Environments'),
},
};
</script>
<template>
<h4 class="gl-font-weight-normal" data-testid="folder-name">
{{ $options.i18n.pageTitle }} /
<b>{{ folderName }}</b>
</h4>
</template>

View File

@ -2,7 +2,8 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import Translate from '~/vue_shared/translate';
import EnvironmentsFolderApp from './environments_folder_view.vue';
import EnvironmentsFolderView from './environments_folder_view.vue';
import EnvironmentsFolderApp from './environments_folder_app.vue';
Vue.use(Translate);
Vue.use(VueApollo);
@ -13,19 +14,35 @@ const apolloProvider = new VueApollo({
export default () => {
const el = document.getElementById('environments-folder-list-view');
const environmentsData = el.dataset;
if (gon.features.environmentsFolderNewLook) {
const folderName = environmentsData.environmentsDataFolderName;
return new Vue({
el,
components: {
EnvironmentsFolderApp,
},
render(createElement) {
return createElement('environments-folder-app', {
props: {
folderName,
},
});
},
});
}
return new Vue({
el,
components: {
EnvironmentsFolderApp,
EnvironmentsFolderView,
},
apolloProvider,
provide: {
projectPath: el.dataset.projectPath,
},
data() {
const environmentsData = el.dataset;
return {
endpoint: environmentsData.environmentsDataEndpoint,
folderName: environmentsData.environmentsDataFolderName,
@ -33,7 +50,7 @@ export default () => {
};
},
render(createElement) {
return createElement('environments-folder-app', {
return createElement('environments-folder-view', {
props: {
endpoint: this.endpoint,
folderName: this.folderName,

View File

@ -10,6 +10,7 @@ import produce from 'immer';
import {
getK8sPods,
handleClusterError,
buildWatchPath,
} from '~/kubernetes_dashboard/graphql/helpers/resolver_helpers';
import { humanizeClusterErrors } from '../../helpers/k8s_integration_helper';
import k8sPodsQuery from '../queries/k8s_pods.query.graphql';
@ -62,9 +63,7 @@ const mapWorkloadItems = (items, kind) => {
const watchWorkloadItems = ({ kind, apiVersion, configuration, namespace, client }) => {
const itemKind = kind.toLowerCase().replace('list', 's');
const path = namespace
? `/apis/${apiVersion}/namespaces/${namespace}/${itemKind}`
: `/apis/${apiVersion}/${itemKind}`;
const path = buildWatchPath({ resource: itemKind, api: `apis/${apiVersion}`, namespace });
const config = new Configuration(configuration);
const watcherApi = new WatchApi(config);
@ -113,7 +112,7 @@ const mapServicesItems = (items) => {
};
const watchServices = ({ configuration, namespace, client }) => {
const path = namespace ? `/api/v1/namespaces/${namespace}/services` : '/api/v1/services';
const path = buildWatchPath({ resource: 'services', namespace });
const config = new Configuration(configuration);
const watcherApi = new WatchApi(config);

View File

@ -262,6 +262,38 @@ class GfmAutoComplete {
});
}
// eslint-disable-next-line class-methods-use-this
setSubmitReviewStates($input) {
if (!window.gon.features?.mrRequestChanges) return;
const REVIEW_STATES = {
reviewed: {
header: __('Comment'),
description: __('Submit general feedback without explicit approval.'),
},
approve: {
header: __('Approve'),
description: __('Submit feedback and approve these changes.'),
},
requested_changes: {
header: __('Request changes'),
description: __('Submit feedback that should be addressed before merging.'),
},
};
$input.filter('[data-supports-quick-actions="true"]').atwho({
// Always keep the trailing space otherwise the command won't display correctly
at: '/submit_review ',
alias: 'submit_review',
data: Object.keys(REVIEW_STATES),
displayTpl({ name }) {
const reviewState = REVIEW_STATES[name];
return `<li><span class="gl-font-weight-bold gl-display-block">${reviewState.header}</span><small class="description gl-display-block gl-w-full gl-float-left! gl-px-0!">${reviewState.description}</small></li>`;
},
});
}
setupEmoji($input) {
const fetchData = this.fetchData.bind(this);
@ -851,6 +883,9 @@ class GfmAutoComplete {
} else if (dataSource) {
AjaxCache.retrieve(dataSource, true)
.then((data) => {
if (data.some((c) => c.name === 'submit_review')) {
this.setSubmitReviewStates($input);
}
this.loadData($input, at, data);
})
.catch(() => {

View File

@ -9,8 +9,12 @@ export const handleClusterError = async (err) => {
throw errorData;
};
export const buildWatchPath = ({ resource, api = 'api/v1', namespace = '' }) => {
return namespace ? `/${api}/namespaces/${namespace}/${resource}` : `/${api}/${resource}`;
};
export const watchPods = ({ client, query, configuration, namespace }) => {
const path = namespace ? `/api/v1/namespaces/${namespace}/pods` : '/api/v1/pods';
const path = buildWatchPath({ resource: 'pods', namespace });
const config = new Configuration(configuration);
const watcherApi = new WatchApi(config);

View File

@ -38,9 +38,7 @@ export default {
},
computed: {
showAiActions() {
return (
this.resourceGlobalId && this.glFeatures.aiGlobalSwitch && this.glFeatures.summarizeNotes
);
return this.resourceGlobalId && this.glFeatures.summarizeNotes;
},
},
};

View File

@ -67,10 +67,7 @@ export default {
return this.currentScope === SCOPE_MILESTONES;
},
showWikiBlobsFilters() {
return (
this.currentScope === SCOPE_WIKI_BLOBS &&
this.glFeatures?.searchProjectWikisHideArchivedProjects
);
return this.currentScope === SCOPE_WIKI_BLOBS;
},
},
methods: {

View File

@ -53,7 +53,7 @@
}
}
.project-clone-holder {
.project-code-holder {
display: inline-block;
margin: $gl-padding 0 0;

View File

@ -14,6 +14,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController
push_frontend_feature_flag(:k8s_watch_api, project)
end
before_action only: [:folder] do
push_frontend_feature_flag(:environments_folder_new_look, project)
end
before_action :authorize_read_environment!
before_action :authorize_create_environment!, only: [:new, :create]
before_action :authorize_stop_environment!, only: [:stop]

View File

@ -1,37 +0,0 @@
# frozen_string_literal: true
class ProductAnalyticsEvent < ApplicationRecord
self.table_name = 'product_analytics_events_experimental'
# Ignore that the partition key :project_id is part of the formal primary key
self.primary_key = :id
belongs_to :project
validates :event_id, :project_id, :v_collector, :v_etl, presence: true
# There is no default Rails timestamps in the table.
# collector_tstamp is a timestamp when a collector recorded an event.
scope :order_by_time, -> { order(collector_tstamp: :desc) }
# If we decide to change this scope to use date_trunc('day', collector_tstamp),
# we should remember that a btree index on collector_tstamp will be no longer effective.
scope :timerange, ->(duration, today = Time.zone.today) {
where('collector_tstamp BETWEEN ? AND ? ', today - duration + 1, today + 1)
}
def self.count_by_graph(graph, days)
group(graph).timerange(days).count
end
def self.count_collector_tstamp_by_day(days)
group("DATE_TRUNC('day', collector_tstamp)")
.reorder('date_trunc_day_collector_tstamp')
.timerange(days)
.count
end
def as_json_wo_empty
as_json.compact
end
end

View File

@ -468,10 +468,6 @@ class Project < ApplicationRecord
# rubocop:enable Cop/ActiveRecordDependent
has_many :active_pages_deployments, -> { active }, class_name: 'PagesDeployment', inverse_of: :project
# Can be too many records. We need to implement delete_all in batches.
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/228637
has_many :product_analytics_events, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :operations_feature_flags, class_name: 'Operations::FeatureFlag'
has_one :operations_feature_flags_client, class_name: 'Operations::FeatureFlagsClient'
has_many :operations_feature_flags_user_lists, class_name: 'Operations::FeatureFlags::UserList'

View File

@ -33,6 +33,7 @@ class UserPolicy < BasePolicy
enable :read_saved_replies
enable :read_user_email_address
enable :admin_user_email_address
enable :make_profile_private
end
rule { default }.enable :read_user_profile

View File

@ -1,13 +0,0 @@
# frozen_string_literal: true
module ProductAnalytics
class BuildActivityGraphService < BuildGraphService
def execute
timerange = @params[:timerange].days
results = product_analytics_events.count_collector_tstamp_by_day(timerange)
format_results('collector_tstamp', results.transform_keys(&:to_date))
end
end
end

View File

@ -1,33 +0,0 @@
# frozen_string_literal: true
module ProductAnalytics
class BuildGraphService
def initialize(project, params)
@project = project
@params = params
end
def execute
graph = @params[:graph].to_sym
timerange = @params[:timerange].days
results = product_analytics_events.count_by_graph(graph, timerange)
format_results(graph, results)
end
private
def format_results(name, results)
{
id: name,
keys: results.keys,
values: results.values
}
end
def product_analytics_events
@project.product_analytics_events
end
end
end

View File

@ -5,8 +5,8 @@
.controls.gl-display-flex
= link_button_to nil, project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'd-none d-sm-inline-flex has-tooltip', icon: 'rss'
- if is_project_overview && can?(current_user, :download_code, @project)
.project-clone-holder.d-none.d-md-inline-flex.gl-ml-2
= render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right'
.project-code-holder.d-none.d-md-inline-flex.gl-ml-2
= render "projects/buttons/code", dropdown_class: 'dropdown-menu-right', ref: @ref
.content_list.project-activity{ :"data-href" => activity_project_path(@project) }
.loading

View File

@ -1,15 +1,16 @@
- project = project || @project
- dropdown_class = local_assigns.fetch(:dropdown_class, '')
- ref = local_assigns.fetch(:ref)
- if can?(current_user, :download_code, @project)
.git-clone-holder.js-git-clone-holder
= render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { id: 'clone-dropdown', class: 'clone-dropdown-btn', data: { toggle: 'dropdown', qa_selector: 'clone_dropdown' } }) do
%span.gl-mr-2.js-clone-dropdown-label
= _('Clone')
%span.js-clone-dropdown-label
= _('Code')
= sprite_icon("chevron-down", css_class: "icon")
%ul.dropdown-menu.dropdown-menu-large.dropdown-menu-selectable.clone-options-dropdown{ class: dropdown_class, data: { qa_selector: 'clone_dropdown_content' } }
%ul.dropdown-menu.dropdown-menu-large.clone-options-dropdown{ class: dropdown_class, data: { qa_selector: 'clone_dropdown_content' } }
- if ssh_enabled?
%li{ class: 'gl-px-4!' }
%li.gl-dropdown-item.js-clone-links{ class: 'gl-px-4!' }
%label.label-bold
= _('Clone with SSH')
.input-group.btn-group
@ -18,7 +19,7 @@
= clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), category: :primary, size: :medium)
= render_if_exists 'projects/buttons/geo'
- if http_enabled?
%li.pt-2{ class: 'gl-px-4!' }
%li.pt-2.gl-dropdown-item.js-clone-links{ class: 'gl-px-4!' }
%label.label-bold
= _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase }
.input-group.btn-group
@ -28,7 +29,7 @@
= render_if_exists 'projects/buttons/geo'
= render_if_exists 'projects/buttons/kerberos_clone_field'
%li.divider.mt-2
%li.pt-2.gl-dropdown-item
%li.pt-2.gl-dropdown-item.js-clone-links
%label.label-bold{ class: 'gl-px-4!' }
= _('Open in your IDE')
- if ssh_enabled?
@ -53,3 +54,6 @@
%a.dropdown-item.open-with-link{ href: xcode_uri_to_repo(@project) }
.gl-dropdown-item-text-wrapper
= _("Xcode")
- if !project.empty_repo? && can?(current_user, :download_code, project)
%li.divider.mt-2
= render 'projects/buttons/download_menu_items', project: project, ref: ref

View File

@ -1,27 +1,13 @@
- project = local_assigns.fetch(:project)
- ref = local_assigns.fetch(:ref)
- pipeline = local_assigns.fetch(:pipeline) { project.latest_successful_pipeline_for(ref) }
- pipeline = local_assigns.fetch(:pipeline, nil)
- css_class = local_assigns.fetch(:css_class, '')
- if !project.empty_repo? && can?(current_user, :download_code, project)
- archive_prefix = "#{project.path}-#{ref.tr('/', '-')}"
.project-action-button.dropdown.gl-dropdown.inline{ class: css_class }>
= render Pajamas::ButtonComponent.new(button_options: { class: 'dropdown-toggle gl-dropdown-toggle dropdown-icon-only has-tooltip', title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static', data: { testid: 'download-source-code-button' } }) do
= sprite_icon('download', css_class: 'gl-icon dropdown-icon')
%span.sr-only= _('Select Archive Format')
= sprite_icon('chevron-down', css_class: 'gl-icon dropdown-chevron')
.dropdown-menu.dropdown-menu-right{ role: 'menu' }
%section
%h5.m-0.dropdown-bold-header= _('Download source code')
.dropdown-menu-content
= render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: nil
.js-directory-downloads{ data: { links: directory_download_links(project, ref, archive_prefix).to_json } }
- if pipeline && pipeline.latest_builds_with_artifacts.any?
%section.border-top.pt-1.mt-1
%h5.m-0.dropdown-bold-header= _('Download artifacts')
- unless pipeline.latest?
%span.unclickable= ci_status_for_statuseable(project.latest_pipeline(ref))
%h6.m-0.dropdown-header= _('Previous Artifacts')
%ul
- pipeline.latest_builds_with_artifacts.each do |job|
%li= link_to job.name, latest_succeeded_project_artifacts_path(project, "#{ref}/download", job: job.name), rel: 'nofollow', download: ''
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
= render 'projects/buttons/download_menu_items', project: project, ref: ref, pipeline: pipeline

View File

@ -1,4 +1,5 @@
.btn-group.ml-0.w-100
- Gitlab::Workhorse::ARCHIVE_FORMATS.each_with_index do |fmt, index|
- archive_path = project_archive_path(project, id: tree_join(ref, archive_prefix), path: path, format: fmt)
= link_button_to fmt, external_storage_url_or_path(archive_path), rel: 'nofollow', download: '', variant: index == 0 ? :confirm : :default, size: :small
- Gitlab::Workhorse::ARCHIVE_FORMATS.each_with_index do |fmt, index|
- archive_path = project_archive_path(project, id: tree_join(ref, archive_prefix), path: path, format: fmt)
%a.dropdown-item.open-with-link{ href: external_storage_url_or_path(archive_path), rel: 'nofollow', download: '' }
.gl-dropdown-item-text-wrapper= fmt

View File

@ -0,0 +1,21 @@
- project = local_assigns.fetch(:project)
- ref = local_assigns.fetch(:ref)
- pipeline = local_assigns.fetch(:pipeline) { project.latest_successful_pipeline_for(ref) }
- archive_prefix = "#{project.path}-#{ref.tr('/', '-')}"
%li.gl-dropdown-item
%h5.m-0.dropdown-bold-header= _('Download source code')
= render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: nil
.js-directory-downloads{ data: { links: directory_download_links(project, ref, archive_prefix).to_json } }
- if pipeline && pipeline.latest_builds_with_artifacts.any?
%li.divider.mt-2
%li.gl-dropdown-item
%h5.m-0.dropdown-bold-header= _('Download artifacts')
- unless pipeline.latest?
%span.gl-ml-3.unclickable= ci_status_for_statuseable(project.latest_pipeline(ref))
%li.divider.mt-2
%li.gl-dropdown-item
%h5.m-0.dropdown-bold-header= _('Previous Artifacts')
- pipeline.latest_builds_with_artifacts.each do |job|
= link_to latest_succeeded_project_artifacts_path(project, "#{ref}/download", job: job.name), class: 'dropdown-item open-with-link', rel: 'nofollow', download: '' do
.gl-dropdown-item-text-wrapper= job.name

View File

@ -19,11 +19,11 @@
= _('You can get started by cloning the repository or start adding files to it with one of the following options.')
.project-buttons{ data: { testid: 'quick-actions-container' } }
.project-clone-holder.d-block.d-md-none.gl-mt-3.gl-mr-3
.project-code-holder.d-block.d-md-none.gl-mt-3.gl-mr-3
= render "shared/mobile_clone_panel"
.project-clone-holder.d-none.d-md-inline-block.gl-mb-3.gl-mr-3.float-left
= render "projects/buttons/clone"
.project-code-holder.d-none.d-md-inline-block.gl-mb-3.gl-mr-3.float-left
= render "projects/buttons/code", ref: @ref
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons, project_buttons: true
- if can?(current_user, :push_code, @project)

View File

@ -16,10 +16,10 @@
= render 'projects/find_file_link'
= render 'shared/web_ide_button', blob: nil
.project-code-holder.d-none.d-md-inline-block>
= render "projects/buttons/code", dropdown_class: 'dropdown-menu-right', ref: @ref
.project-code-holder.d-block.d-md-none.mt-sm-2.mt-md-0.ml-md-2>
= render 'projects/buttons/download', project: @project, ref: @ref
.project-clone-holder.d-none.d-md-inline-block>
= render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right'
.project-clone-holder.d-block.d-md-none.mt-sm-2.mt-md-0.ml-md-2>
= render "shared/mobile_clone_panel"
= render "shared/mobile_clone_panel", ref: @ref

View File

@ -10,9 +10,9 @@
= default_clone_protocol.upcase
= sprite_icon('chevron-down', css_class: 'gl-icon')
%ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown{ data: { testid: 'clone-dropdown-content' } }
%li
%li.js-clone-links
= ssh_clone_button(container)
%li
%li.js-clone-links
= http_clone_button(container)
= render_if_exists 'shared/kerberos_clone_button', container: container

View File

@ -8,9 +8,9 @@
= sprite_icon("chevron-down", css_class: "dropdown-btn-icon icon")
%ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown{ data: { dropdown: true } }
- if ssh_enabled?
%li
%li.js-clone-links
= dropdown_item_with_description(ssh_copy_label, ssh_clone_url_to_repo(project), href: ssh_clone_url_to_repo(project), data: { clone_type: 'ssh' }, default: true)
- if http_enabled?
%li
%li.js-clone-links
= dropdown_item_with_description(http_copy_label, http_clone_url_to_repo(project), href: http_clone_url_to_repo(project), data: { clone_type: 'http' })
= render_if_exists 'shared/mobile_kerberos_clone'

View File

@ -0,0 +1,8 @@
---
name: ci_retry_on_exit_codes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135430
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/430037
milestone: '16.7'
type: development
group: group::pipeline authoring
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: environments_folder_new_look
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137046
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431928
milestone: '16.7'
type: development
group: group::environments
default_enabled: false

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
Gitlab.ee do
Circuitbox.configure do |config|
config.default_circuit_store = Gitlab::CircuitBreaker::Store.new
config.default_notifier = Gitlab::CircuitBreaker::Notifier.new
end
Circuitbox.configure do |config|
config.default_circuit_store = Gitlab::CircuitBreaker::Store.new
config.default_notifier = Gitlab::CircuitBreaker::Notifier.new
end

View File

@ -1,56 +0,0 @@
# frozen_string_literal: true
Gitlab::Seeder.quiet do
# The data set takes approximately 2 minutes to load,
# so its put behind the flag. To seed this data use the flag and the filter:
# SEED_PRODUCT_ANALYTICS_EVENTS=1 FILTER=product_analytics_events rake db:seed_fu
flag = 'SEED_PRODUCT_ANALYTICS_EVENTS'
if ENV[flag]
Project.all.sample(2).each do |project|
# Let's generate approx a week of events from now into the past with 1 minute step.
# To add some differentiation we add a random offset of up to 45 seconds.
10000.times do |i|
dvce_created_tstamp = DateTime.now - i.minute - rand(45).seconds
# Add a random delay to collector timestamp. Up to 2 seconds.
collector_tstamp = dvce_created_tstamp + rand(3).second
ProductAnalyticsEvent.create!(
project_id: project.id,
platform: ["web", "mob", "mob", "app"].sample,
collector_tstamp: collector_tstamp,
dvce_created_tstamp: dvce_created_tstamp,
event: nil,
event_id: SecureRandom.uuid,
name_tracker: "sp",
v_tracker: "js-2.14.0",
v_collector: Gitlab::VERSION,
v_etl: Gitlab::VERSION,
domain_userid: SecureRandom.uuid,
domain_sessionidx: 4,
page_url: "#{project.web_url}/-/product_analytics/test",
page_title: 'Test page',
page_referrer: "#{project.web_url}/-/product_analytics/test",
br_lang: ["en-US", "en-US", "en-GB", "nl", "fi"].sample, # https://www.andiamo.co.uk/resources/iso-language-codes/
br_features_pdf: true,
br_cookies: [true, true, true, false].sample,
br_colordepth: ["24", "24", "16", "8"].sample,
os_timezone: ["America/Los_Angeles", "America/Los_Angeles", "America/Lima", "Asia/Dubai", "Africa/Bangui"].sample, # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
doc_charset: ["UTF-8", "UTF-8", "UTF-8", "DOS", "EUC"].sample,
domain_sessionid: SecureRandom.uuid
)
end
unless Feature.enabled?(:product_analytics, project)
if Feature.enable(:product_analytics, project)
puts "Product analytics feature was enabled for #{project.full_path}"
end
end
puts "10K events added to #{project.full_path}"
end
else
puts "Skipped. Use the `#{flag}` environment variable to enable."
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddMakeProfilePrivateApplicationSetting < Gitlab::Database::Migration[2.2]
milestone '16.7'
def change
add_column(:application_settings, :make_profile_private, :boolean, default: true, null: false)
end
end

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
class RemoveCustomEmailSmtpColumnsFromServiceDeskSettings < Gitlab::Database::Migration[2.2]
MAXIMUM_LIMIT = 255
milestone '16.7'
disable_ddl_transaction!
def up
with_lock_retries do
remove_column :service_desk_settings, :custom_email_smtp_address
remove_column :service_desk_settings, :custom_email_smtp_port
remove_column :service_desk_settings, :custom_email_smtp_username
remove_column :service_desk_settings, :encrypted_custom_email_smtp_password
remove_column :service_desk_settings, :encrypted_custom_email_smtp_password_iv
end
end
def down
with_lock_retries do
add_column :service_desk_settings, :custom_email_smtp_address, :text
add_column :service_desk_settings, :custom_email_smtp_port, :integer
add_column :service_desk_settings, :custom_email_smtp_username, :text
add_column :service_desk_settings, :encrypted_custom_email_smtp_password, :binary
add_column :service_desk_settings, :encrypted_custom_email_smtp_password_iv, :binary
end
add_text_limit :service_desk_settings, :custom_email_smtp_address, MAXIMUM_LIMIT
add_text_limit :service_desk_settings, :custom_email_smtp_username, MAXIMUM_LIMIT
end
end

View File

@ -0,0 +1 @@
6b8021c293e630af13479afe8f49ef3d28861d963942b481a3113266ff59fccf

View File

@ -0,0 +1 @@
cf9a4cbefa65c11d5066134ff82615453aaf63af3f6f871d532038439ada6d22

View File

@ -12196,6 +12196,7 @@ CREATE TABLE application_settings (
project_jobs_api_rate_limit integer DEFAULT 600 NOT NULL,
math_rendering_limits_enabled boolean DEFAULT true NOT NULL,
service_access_tokens_expiration_enforced boolean DEFAULT true NOT NULL,
make_profile_private boolean DEFAULT true NOT NULL,
enable_artifact_external_redirect_warning_page boolean DEFAULT true NOT NULL,
allow_project_creation_for_guest_and_below boolean DEFAULT true NOT NULL,
update_namespace_name_rate_limit smallint DEFAULT 120 NOT NULL,
@ -23437,16 +23438,9 @@ CREATE TABLE service_desk_settings (
file_template_project_id bigint,
custom_email_enabled boolean DEFAULT false NOT NULL,
custom_email text,
custom_email_smtp_address text,
custom_email_smtp_port integer,
custom_email_smtp_username text,
encrypted_custom_email_smtp_password bytea,
encrypted_custom_email_smtp_password_iv bytea,
service_desk_enabled boolean DEFAULT true NOT NULL,
add_external_participants_from_cc boolean DEFAULT false NOT NULL,
CONSTRAINT check_57a79552e1 CHECK ((char_length(custom_email) <= 255)),
CONSTRAINT check_b283637a9e CHECK ((char_length(custom_email_smtp_address) <= 255)),
CONSTRAINT check_e3535d46ee CHECK ((char_length(custom_email_smtp_username) <= 255))
CONSTRAINT check_57a79552e1 CHECK ((char_length(custom_email) <= 255))
);
CREATE TABLE shards (

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,6 +1,6 @@
---
type: reference
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -4,12 +4,11 @@ group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Group SSH certificates API **(PREMIUM ALL)**
# Group SSH certificates API **(PREMIUM SAAS)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/421915) in GitLab 16.4 [with a flag](../user/feature_flags.md) named `ssh_certificates_rest_endpoints`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../administration/feature_flags.md) named `ssh_certificates_rest_endpoints`.
On GitLab.com, this feature is not available.
Use this API to create, read and delete SSH certificates for a group.

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
type: concepts, howto

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
type: concepts, howto

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
---

View File

@ -1019,6 +1019,10 @@ end
## Dropping a database table
NOTE:
After a table has been dropped, it should be added to the database dictionary, following the
steps in the [database dictionary guide](database/database_dictionary.md#dropping-tables).
Dropping a database table is uncommon, and the `drop_table` method
provided by Rails is generally considered safe. Before dropping the table,
please consider the following:
@ -1105,9 +1109,6 @@ class DroppingTableMigrationClass < Gitlab::Database::Migration[2.1]
end
```
After a table has been dropped, it should be added to the database dictionary, following the
steps in the [database dictionary guide](database/database_dictionary.md#dropping-tables).
## Dropping a sequence
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88387) in GitLab 15.1.

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
---

View File

@ -59,6 +59,7 @@ To setup ClickHouse as the GitLab data storage:
1. [Run ClickHouse Cluster and configure database](#run-and-configure-clickhouse).
1. [Configure GitLab connection to Clickhouse](#configure-the-gitlab-connection-to-clickhouse).
1. [Run ClickHouse migrations](#run-clickhouse-migrations).
1. [Enable the feature flags](#enable-feature-flags).
### Run and configure ClickHouse

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -0,0 +1,83 @@
---
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/product/ux/technical-writing/#assignments
---
# Manage group's SSH certificates **(PREMIUM SAAS)**
Manage Git access to the projects by sharing public Certified Authority (`CA`) files in your organization's top-level group.
Git access control options on GitLab SaaS (SSH, HTTPS) rely on credentials (such as access tokens and SSH keys)
setup in the user profile and are out of control of the organization.
To temporarily grant Git access to your projects, you can use SSH certificates.
## Add a CA certificate to a top-level group
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/421915) in GitLab 16.4 [with a flag](../feature_flags.md) named `ssh_certificates_rest_endpoints`. Disabled by default.
FLAG:
On GitLab.com, this feature is not available.
Prerequisites:
- You must have the Owner role for the group.
- The group must be a top-level group, not a subgroup.
To add a CA certificate to a group:
1. Generate an SSH key pair to be used as a Certified Authority file:
```plaintext
ssh-keygen -f CA
```
1. Add the public key to the top-level group using [Group SSH certificates API](../../api/group_ssh_certificates.md#create-ssh-certificate)
to grant access to the projects of the group and its subgroups.
## Issue CA certificates for users
Prerequisites:
- You must have the Owner role for the group.
- The user certificates can only be used to access the projects within the top-level group and its subgroups.
- A user's username or primary email (`user` or `user@example.com`) must be specified to associate a
GitLab user with the user certificate.
- The user must be an [Enterprise User](../enterprise_user/index.md).
To issue user certificates, use the private key from the pair you [created earlier](#add-a-ca-certificate-to-a-top-level-group):
```shell
ssh-keygen -s CA -I user@example.com -V +1d user-key.pub
```
The (`user-key.pub`) key is the public key from an SSH key pair that is used by a user for SSH authentication.
The SSH key pair is either generated by a user or provisioned by the group owner infrastructure along with the SSH certificate.
The expiration date (`+1d`) identifies how long the SSH certificate can be used to access the group projects.
The user certificates can only be used to access the projects within the top-level group.
## Enforce SSH certificates
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/421915) in GitLab 16.7 [with a flag](../feature_flags.md) named `enforce_ssh_certificates_via_settings`. Disabled by default.
FLAG:
On GitLab.com, this feature is not available.
You can enforce usage of SSH certificates and forbid users from authenticating using SSH
keys and access tokens.
When SSH certificates are enforced, only individual user accounts are affected.
It does not apply to service accounts, deploy keys, and other types of internal accounts.
Prerequisites:
- You must have the Owner role for the group.
To enforce using SSH certificates:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > General**.
1. Expand the **Permissions and group features** section.
1. Select the **Enforce SSH Certificates** checkbox.
1. Select **Save changes**.

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -1,5 +1,5 @@
---
stage: Monitor
stage: Service Management
group: Respond
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -35,8 +35,8 @@ module Gitlab
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
ALLOWED_KEYS = %i[max when].freeze
attributes :max, :when
ALLOWED_KEYS = %i[max when exit_codes].freeze
attributes ALLOWED_KEYS
validations do
validates :config, allowed_keys: ALLOWED_KEYS
@ -53,6 +53,7 @@ module Gitlab
validates :when,
inclusion: { in: FullRetry.possible_retry_when_values },
if: -> (config) { config.when.is_a?(String) }
validates :exit_codes, array_of_integers_or_integer: true
end
end
@ -62,9 +63,14 @@ module Gitlab
def value
super.tap do |config|
# make sure that `when` is an array, because we allow it to
# be passed as a String in config for simplicity
# make sure that `when` and `exit_codes` are arrays, because we allow them to
# be passed as a String/Integer in config for simplicity
config[:when] = Array.wrap(config[:when]) if config[:when]
if config[:exit_codes] && Feature.enabled?(:ci_retry_on_exit_codes, Feature.current_request)
config[:exit_codes] = Array.wrap(config[:exit_codes])
else
config.delete(:exit_codes)
end
end
end

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
# A configurable circuit breaker to protect the application from external service failures.
# The circuit measures the amount of failures and if the threshold is exceeded, stops sending requests.
module Gitlab
module CircuitBreaker
InternalServerError = Class.new(StandardError)
DEFAULT_ERROR_THRESHOLD = 50
DEFAULT_VOLUME_THRESHOLD = 10
class << self
include ::Gitlab::Utils::StrongMemoize
# @param [String] unique name for the circuit
# @param options [Hash] an options hash setting optional values per circuit
def run_with_circuit(service_name, options = {}, &block)
circuit(service_name, options).run(exception: false, &block)
end
private
def circuit(service_name, options)
strong_memoize_with(:circuit, service_name, options) do
circuit_options = {
exceptions: [InternalServerError],
error_threshold: DEFAULT_ERROR_THRESHOLD,
volume_threshold: DEFAULT_VOLUME_THRESHOLD
}.merge(options)
Circuitbox.circuit(service_name, circuit_options)
end
end
end
end
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
module Gitlab
module CircuitBreaker
class Notifier
CircuitBreakerError = Class.new(RuntimeError)
def notify(service_name, event)
return unless event == 'failure'
exception = CircuitBreakerError.new("Service #{service_name}: #{event}")
exception.set_backtrace(Gitlab::BacktraceCleaner.clean_backtrace(caller))
Gitlab::ErrorTracking.track_exception(exception)
end
def notify_warning(_service_name, _message)
# no-op
end
def notify_run(_service_name, &_block)
# This gets called by Circuitbox::CircuitBreaker#run to actually execute
# the block passed.
yield
end
end
end
end

View File

@ -0,0 +1,51 @@
# frozen_string_literal: true
module Gitlab
module CircuitBreaker
class Store
def key?(key)
with { |redis| redis.exists?(key) }
end
def store(key, value, opts = {})
with do |redis|
redis.set(key, value, ex: opts[:expires])
value
end
end
def increment(key, amount = 1, opts = {})
expires = opts[:expires]
with do |redis|
redis.multi do |multi|
multi.incrby(key, amount)
multi.expire(key, expires) if expires
end
end
end
def load(key, _opts = {})
with { |redis| redis.get(key) }
end
def values_at(*keys, **_opts)
keys.map! { |key| load(key) }
end
def delete(key)
with { |redis| redis.del(key) }
end
private
def with(&block)
Gitlab::Redis::RateLimiting.with(&block)
rescue ::Redis::BaseConnectionError
# Do not raise an error if we cannot connect to Redis. If
# Redis::RateLimiting is unavailable it should not take the site down.
nil
end
end
end
end

View File

@ -161,10 +161,25 @@ module Gitlab
condition do
quick_action_target.persisted?
end
command :submit_review do
command :submit_review do |state = "reviewed"|
next if params[:review_id]
result = DraftNotes::PublishService.new(quick_action_target, current_user).execute
if Feature.enabled?(:mr_request_changes, current_user)
reviewer_state = state.strip.presence
if reviewer_state === 'approve'
::MergeRequests::ApprovalService
.new(project: quick_action_target.project, current_user: current_user)
.execute(quick_action_target)
elsif MergeRequestReviewer.states.key?(reviewer_state)
::MergeRequests::UpdateReviewerStateService
.new(project: quick_action_target.project, current_user: current_user)
.execute(quick_action_target, reviewer_state)
end
end
@execution_message[:submit_review] = if result[:status] == :success
_('Submitted the current review.')
else

View File

@ -1,56 +0,0 @@
# frozen_string_literal: true
module ProductAnalytics
# Converts params from Snowplow tracker to one compatible with
# GitLab ProductAnalyticsEvent model. The field naming corresponds
# with snowplow event model. Only project_id is GitLab specific.
#
# For information on what each field is you can check next resources:
# * Snowplow tracker protocol: https://github.com/snowplow/snowplow/wiki/snowplow-tracker-protocol
# * Canonical event model: https://github.com/snowplow/snowplow/wiki/canonical-event-model
class EventParams
def self.parse_event_params(params)
{
project_id: params['aid'],
platform: params['p'],
collector_tstamp: Time.zone.now,
event_id: params['eid'],
v_tracker: params['tv'],
v_collector: Gitlab::VERSION,
v_etl: Gitlab::VERSION,
os_timezone: params['tz'],
name_tracker: params['tna'],
br_lang: params['lang'],
doc_charset: params['cs'],
br_features_pdf: Gitlab::Utils.to_boolean(params['f_pdf']),
br_features_flash: Gitlab::Utils.to_boolean(params['f_fla']),
br_features_java: Gitlab::Utils.to_boolean(params['f_java']),
br_features_director: Gitlab::Utils.to_boolean(params['f_dir']),
br_features_quicktime: Gitlab::Utils.to_boolean(params['f_qt']),
br_features_realplayer: Gitlab::Utils.to_boolean(params['f_realp']),
br_features_windowsmedia: Gitlab::Utils.to_boolean(params['f_wma']),
br_features_gears: Gitlab::Utils.to_boolean(params['f_gears']),
br_features_silverlight: Gitlab::Utils.to_boolean(params['f_ag']),
br_colordepth: params['cd'],
br_cookies: Gitlab::Utils.to_boolean(params['cookie']),
dvce_created_tstamp: params['dtm'],
br_viewheight: params['vp'],
domain_sessionidx: params['vid'],
domain_sessionid: params['sid'],
domain_userid: params['duid'],
user_fingerprint: params['fp'],
page_referrer: params['refr'],
page_url: params['url'],
se_category: params['se_ca'],
se_action: params['se_ac'],
se_label: params['se_la'],
se_property: params['se_pr'],
se_value: params['se_va']
}
end
def self.has_required_params?(params)
params['aid'].present? && params['eid'].present?
end
end
end

View File

@ -9,7 +9,7 @@ module QA
def self.included(base)
super
base.view 'app/views/projects/buttons/_clone.html.haml' do
base.view 'app/views/projects/buttons/_code.html.haml' do
element :clone_dropdown
element :clone_dropdown_content
element :ssh_clone_url_content

View File

@ -26,6 +26,7 @@ CodeReuse/ActiveRecord:
- lib/banzai/**/*.rb
- lib/click_house/migration_support/**/*.rb
- lib/gitlab/background_migration/**/*.rb
- lib/gitlab/circuit_breaker/store.rb
- lib/gitlab/cycle_analytics/**/*.rb
- lib/gitlab/counters/**/*.rb
- lib/gitlab/database/**/*.rb

View File

@ -35,6 +35,14 @@ FactoryBot.define do
name { 'development' }
end
trait :with_folders do |environment|
sequence(:name) { |n| "#{folder}/environment#{n}" }
transient do
folder { 'folder' }
end
end
trait :with_review_app do |environment|
sequence(:name) { |n| "review/#{n}" }

View File

@ -1,24 +0,0 @@
# frozen_string_literal: true
FactoryBot.define do
factory :product_analytics_event do
project
platform { 'web' }
collector_tstamp { DateTime.now }
dvce_created_tstamp { DateTime.now }
event_id { SecureRandom.uuid }
name_tracker { 'sp' }
v_tracker { 'js-2.14.0' }
v_collector { 'GitLab 13.1.0-pre' }
v_etl { 'GitLab 13.1.0-pre' }
domain_userid { SecureRandom.uuid }
domain_sessionidx { 4 }
page_url { 'http://localhost:3333/products/123' }
br_lang { 'en-US' }
br_cookies { true }
br_colordepth { '24' }
os_timezone { 'America/Los_Angeles' }
doc_charset { 'UTF-8' }
domain_sessionid { SecureRandom.uuid }
end
end

View File

@ -151,7 +151,7 @@ RSpec.describe 'Dashboard Projects', :js, feature_category: :groups_and_projects
it 'shows that the last pipeline passed' do
visit dashboard_projects_path
page.within('[data-testid="project_controls"]') do
within_testid('project_controls') do
expect(page).to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']")
expect(page).to have_css("[data-testid='ci-icon']")
expect(page).to have_css('[data-testid="status_success_borderless-icon"]')
@ -163,7 +163,7 @@ RSpec.describe 'Dashboard Projects', :js, feature_category: :groups_and_projects
it 'does not show the pipeline status' do
visit dashboard_projects_path
page.within('[data-testid="project_controls"]') do
within_testid('project_controls') do
expect(page).not_to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']")
expect(page).not_to have_css("[data-testid='ci-icon']")
expect(page).not_to have_css('[data-testid="status_success_borderless-icon"]')

View File

@ -95,7 +95,7 @@ RSpec.describe 'Dashboard Todos', :js, feature_category: :team_planning do
shared_examples 'deleting the todo' do
before do
within first('.todo') do
find('[data-testid="check-icon"]').click
find_by_testid('check-icon').click
end
end
@ -121,9 +121,9 @@ RSpec.describe 'Dashboard Todos', :js, feature_category: :team_planning do
shared_examples 'deleting and restoring the todo' do
before do
within first('.todo') do
find('[data-testid="check-icon"]').click
find_by_testid('check-icon').click
wait_for_requests
find('[data-testid="redo-icon"]').click
find_by_testid('redo-icon').click
end
end
@ -301,7 +301,7 @@ RSpec.describe 'Dashboard Todos', :js, feature_category: :team_planning do
describe 'restoring the todo' do
before do
within first('.todo') do
find('[data-testid="todo-add-icon"]').click
find_by_testid('todo-add-icon').click
end
end
@ -407,7 +407,7 @@ RSpec.describe 'Dashboard Todos', :js, feature_category: :team_planning do
context 'User has deleted a todo' do
before do
within first('.todo') do
find('[data-testid="check-icon"]').click
find_by_testid('check-icon').click
end
end

View File

@ -0,0 +1,46 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Environments Folder page', :js, feature_category: :environment_management do
let(:folder_name) { 'folder' }
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let!(:envs) { create_list(:environment, 4, :with_folders, project: project, folder: folder_name) }
before_all do
project.add_role(user, :developer)
end
before do
create(:environment, :production, project: project)
end
describe 'new folders page' do
before do
sign_in(user)
visit folder_project_environments_path(project, folder_name)
wait_for_requests
end
it 'renders the header with a folder name' do
expect(page).to have_content("Environments / #{folder_name}")
end
end
describe 'legacy folders page' do
before do
stub_feature_flags(environments_folder_new_look: false)
sign_in(user)
visit folder_project_environments_path(project, folder_name)
wait_for_requests
end
it 'user opens folder view' do
expect(page).to have_content("Environments / #{folder_name}")
expect(page).not_to have_content('production')
envs.each { |env| expect(page).to have_content(env.name.split('/').last) }
end
end
end

View File

@ -34,7 +34,7 @@ RSpec.describe 'Group Issue Boards', :js, feature_category: :groups_and_projects
wait_for_requests
page.within('[data-testid="dropdown-content"]') do
within_testid('dropdown-content') do
expect(page).to have_content(project_1_label.title)
expect(page).to have_content(group_label.title)
expect(page).not_to have_content(project_2_label.title)

View File

@ -32,7 +32,7 @@ RSpec.describe 'Group Boards', feature_category: :team_planning do
fill_in 'issue_title', with: issue_title
page.within("[data-testid='project-select-dropdown']") do
within_testid('project-select-dropdown') do
find('button.gl-new-dropdown-toggle').click
find('.gl-new-dropdown-item').click

View File

@ -112,9 +112,9 @@ RSpec.describe 'User Cluster', :js, feature_category: :user_profile do
context 'when user destroys the cluster' do
before do
click_link 'Advanced Settings'
find('[data-testid="remove-integration-button"]').click
find_by_testid('remove-integration-button').click
fill_in 'confirm_cluster_name_input', with: cluster.name
find('[data-testid="remove-integration-modal-button"]').click
find_by_testid('remove-integration-modal-button').click
end
it 'user sees creation form with the successful message' do

View File

@ -62,7 +62,7 @@ RSpec.describe 'Group Dependency Proxy', feature_category: :dependency_proxy do
visit settings_path
wait_for_requests
proxy_toggle = find('[data-testid="dependency-proxy-setting-toggle"]')
proxy_toggle = find_by_testid('dependency-proxy-setting-toggle')
proxy_toggle_button = proxy_toggle.find('button')
expect(proxy_toggle).to have_css("button.is-checked")

View File

@ -141,8 +141,8 @@ RSpec.describe 'Edit group settings', feature_category: :groups_and_projects do
end
describe 'transfer group', :js do
let(:namespace_select) { page.find('[data-testid="transfer-group-namespace-select"]') }
let(:confirm_modal) { page.find('[data-testid="confirm-danger-modal"]') }
let(:namespace_select) { find_by_testid('transfer-group-namespace-select') }
let(:confirm_modal) { find_by_testid('confirm-danger-modal') }
shared_examples 'can transfer the group' do
before do
@ -154,7 +154,7 @@ RSpec.describe 'Edit group settings', feature_category: :groups_and_projects do
visit edit_group_path(selected_group)
page.within('[data-testid="transfer-locations-dropdown"]') do
within_testid('transfer-locations-dropdown') do
click_button _('Select parent group')
fill_in _('Search'), with: target_group&.name || ''
wait_for_requests
@ -170,7 +170,7 @@ RSpec.describe 'Edit group settings', feature_category: :groups_and_projects do
click_button 'Confirm'
end
within('[data-testid="breadcrumb-links"]') do
within_testid('breadcrumb-links') do
expect(page).to have_content(target_group.name) if target_group
expect(page).to have_content(selected_group.name)
end

View File

@ -79,7 +79,7 @@ RSpec.describe 'Groups > Members > Leave group', feature_category: :groups_and_p
visit group_path(group, leave: 1)
expect(find('[data-testid="alert-danger"]')).to have_content 'You do not have permission to leave this group'
expect(find_by_testid('alert-danger')).to have_content 'You do not have permission to leave this group'
end
def left_group_message(group)

View File

@ -106,7 +106,7 @@ RSpec.describe 'Groups > Members > Manage groups', :js, feature_category: :group
page.within first_row do
expect(page).to have_field('Expiration date', with: expiration_date)
find('[data-testid="clear-button"]').click
find_by_testid('clear-button').click
wait_for_requests

View File

@ -48,7 +48,7 @@ RSpec.describe 'Groups > Members > Owner adds member with expiration date', :js,
page.within second_row do
expect(page).to have_field('Expiration date', with: expiration_date)
find('[data-testid="clear-button"]').click
find_by_testid('clear-button').click
wait_for_requests

View File

@ -21,7 +21,7 @@ RSpec.describe 'Search group member', :js, feature_category: :groups_and_project
end
it 'renders member users' do
page.within '[data-testid="members-filtered-search-bar"]' do
within_testid('members-filtered-search-bar') do
find_field('Filter members').click
find('input').native.send_keys(member.name)
click_button 'Search'

View File

@ -17,7 +17,7 @@ RSpec.describe 'Groups > Members > Sort members', :js, feature_category: :groups
end
def expect_sort_by(text, sort_direction)
within('[data-testid="members-sort-dropdown"]') do
within_testid('members-sort-dropdown') do
expect(page).to have_css('button[aria-haspopup="menu"]', text: text)
expect(page).to have_button("Sort direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
end

View File

@ -62,7 +62,7 @@ RSpec.describe 'Groups > Members > Tabs', :js, feature_category: :groups_and_pro
click_link 'Invited'
page.within '[data-testid="members-filtered-search-bar"]' do
within_testid('members-filtered-search-bar') do
find_field('Search invited').click
find('input').native.send_keys('email')
click_button 'Search'
@ -75,7 +75,7 @@ RSpec.describe 'Groups > Members > Tabs', :js, feature_category: :groups_and_pro
before do
click_link 'Members'
page.within '[data-testid="members-filtered-search-bar"]' do
within_testid 'members-filtered-search-bar' do
find_field('Filter members').click
find('input').native.send_keys('test')
click_button 'Search'

View File

@ -81,7 +81,7 @@ RSpec.describe 'Group merge requests page', feature_category: :code_review_workf
it 'shows projects only with merge requests feature enabled', :js do
click_button 'Select project to create merge request'
page.within('[data-testid="new-resource-dropdown"]') do
within_testid('new-resource-dropdown') do
expect(page).to have_content(project.name_with_namespace)
expect(page).not_to have_content(project_with_merge_requests_disabled.name_with_namespace)
end

Some files were not shown because too many files have changed in this diff Show More