Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
92849dc177
commit
80b22a4413
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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">{{
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -38,9 +38,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
showAiActions() {
|
||||
return (
|
||||
this.resourceGlobalId && this.glFeatures.aiGlobalSwitch && this.glFeatures.summarizeNotes
|
||||
);
|
||||
return this.resourceGlobalId && this.glFeatures.summarizeNotes;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.project-clone-holder {
|
||||
.project-code-holder {
|
||||
display: inline-block;
|
||||
margin: $gl-padding 0 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
6b8021c293e630af13479afe8f49ef3d28861d963942b481a3113266ff59fccf
|
||||
|
|
@ -0,0 +1 @@
|
|||
cf9a4cbefa65c11d5066134ff82615453aaf63af3f6f871d532038439ada6d22
|
||||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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**.
|
||||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}" }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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"]')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue