Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-04-22 18:09:22 +00:00
parent 4136fdda4c
commit eab22d334f
139 changed files with 1897 additions and 1912 deletions

View File

@ -138,4 +138,4 @@ variables:
include:
- local: .gitlab/ci/*.gitlab-ci.yml
- remote: 'https://gitlab.com/gitlab-org/frontend/untamper-my-lockfile/-/raw/main/.gitlab-ci-template.yml'
- remote: 'https://gitlab.com/gitlab-org/frontend/untamper-my-lockfile/-/raw/main/templates/merge_request_pipelines.yml'

View File

@ -209,14 +209,14 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/.vale/ @marcel.amirault @eread @aqualls @dianalogan
^[Documentation Pages]
/doc/administration/application_settings_cache.md @marcia
/doc/administration/application_settings_cache.md @sselhorn
/doc/administration/audit_event_streaming.md @eread
/doc/administration/audit_events.md @eread
/doc/administration/audit_reports.md @eread
/doc/administration/auditor_users.md @eread
/doc/administration/auth/ @eread
/doc/administration/cicd.md @marcel.amirault
/doc/administration/clusters/kas.md @marcia
/doc/administration/clusters/kas.md @sselhorn
/doc/administration/compliance.md @eread
/doc/administration/configure.md @axil
/doc/administration/consul.md @axil
@ -254,13 +254,13 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/object_storage.md @axil
/doc/administration/operations/ @axil
/doc/administration/operations/moving_repositories.md @eread
/doc/administration/operations/sidekiq_memory_killer.md @marcia
/doc/administration/operations/sidekiq_memory_killer.md @sselhorn
/doc/administration/package_information/ @axil
/doc/administration/packages/ @claytoncornell
/doc/administration/pages/index.md @aqualls
/doc/administration/pages/source.md @aqualls
/doc/administration/polling.md @axil
/doc/administration/postgresql/ @marcia
/doc/administration/postgresql/ @sselhorn
/doc/administration/pseudonymizer.md @axil
/doc/administration/raketasks/ @axil
/doc/administration/raketasks/praefect.md @eread
@ -280,11 +280,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/snippets/index.md @aqualls
/doc/administration/static_objects_external_storage.md @aqualls
/doc/administration/system_hooks.md @kpaizee
/doc/administration/terraform_state.md @marcia
/doc/administration/terraform_state.md @sselhorn
/doc/administration/timezone.md @axil
/doc/administration/troubleshooting/ @axil
/doc/administration/troubleshooting/elasticsearch.md @rdickenson
/doc/administration/troubleshooting/postgresql.md @marcia
/doc/administration/troubleshooting/postgresql.md @sselhorn
/doc/administration/uploads.md @axil
/doc/administration/user_settings.md @eread
/doc/administration/whats-new.md @kpaizee
@ -302,7 +302,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/branches.md @aqualls
/doc/api/broadcast_messages.md @kpaizee
/doc/api/bulk_imports.md @eread
/doc/api/cluster_agents.md @marcia
/doc/api/cluster_agents.md @sselhorn
/doc/api/commits.md @aqualls
/doc/api/container_registry.md @claytoncornell
/doc/api/custom_attributes.md @kpaizee
@ -334,7 +334,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/group_activity_analytics.md @fneill
/doc/api/group_badges.md @fneill
/doc/api/group_boards.md @msedlakjakubowski
/doc/api/group_clusters.md @marcia
/doc/api/group_clusters.md @sselhorn
/doc/api/group_import_export.md @eread
/doc/api/group_iterations.md @msedlakjakubowski
/doc/api/group_labels.md @msedlakjakubowski
@ -348,7 +348,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/groups.md @fneill
/doc/api/import.md @eread
/doc/api/index.md @kpaizee
/doc/api/instance_clusters.md @marcia
/doc/api/instance_clusters.md @sselhorn
/doc/api/instance_level_ci_variables.md @marcel.amirault
/doc/api/integrations.md @kpaizee
/doc/api/invitations.md @kpaizee
@ -390,7 +390,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/project_access_tokens.md @eread
/doc/api/project_aliases.md @aqualls
/doc/api/project_badges.md @aqualls
/doc/api/project_clusters.md @marcia
/doc/api/project_clusters.md @sselhorn
/doc/api/project_import_export.md @aqualls
/doc/api/project_level_variables.md @marcel.amirault
/doc/api/project_relations_export.md @eread
@ -443,10 +443,10 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/vulnerability_findings.md @claytoncornell
/doc/api/wikis.md @aqualls
/doc/architecture/blueprints/container_registry_metadata_database/index.md @claytoncornell
/doc/architecture/blueprints/database/scalability/patterns/ @marcia
/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md @marcia
/doc/architecture/blueprints/database/scalability/patterns/ @sselhorn
/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md @sselhorn
/doc/ci/caching/index.md @marcel.amirault
/doc/ci/chatops/index.md @marcia
/doc/ci/chatops/index.md @sselhorn
/doc/ci/ci_cd_for_external_repos/ @marcel.amirault
/doc/ci/cloud_deployment/ecs/quick_start_guide.md @rdickenson
/doc/ci/cloud_deployment/index.md @rdickenson
@ -493,33 +493,33 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/ci/unit_test_reports.md @marcel.amirault
/doc/ci/variables/ @marcel.amirault
/doc/ci/yaml/ @marcel.amirault
/doc/development/adding_database_indexes.md @marcia
/doc/development/adding_database_indexes.md @sselhorn
/doc/development/application_limits.md @axil
/doc/development/approval_rules.md @aqualls
/doc/development/audit_event_guide/index.md @eread
/doc/development/auto_devops.md @marcia
/doc/development/auto_devops.md @sselhorn
/doc/development/backend/create_source_code_be/index.md @aqualls
/doc/development/backend/ruby_style_guide.md @marcia
/doc/development/batched_background_migrations.md @marcia
/doc/development/backend/ruby_style_guide.md @sselhorn
/doc/development/batched_background_migrations.md @sselhorn
/doc/development/build_test_package.md @axil
/doc/development/bulk_import.md @eread
/doc/development/cached_queries.md @marcia
/doc/development/cached_queries.md @sselhorn
/doc/development/cascading_settings.md @eread
/doc/development/chatops_on_gitlabcom.md @marcia
/doc/development/chatops_on_gitlabcom.md @sselhorn
/doc/development/cicd/cicd_reference_documentation_guide.md @marcel.amirault
/doc/development/cicd/index.md @marcel.amirault
/doc/development/cicd/schema.md @marcel.amirault
/doc/development/cicd/templates.md @marcel.amirault
/doc/development/code_intelligence/index.md @aqualls
/doc/development/contributing/ @marcia
/doc/development/contributing/ @sselhorn
/doc/development/contributing/merge_request_workflow.md @aqualls
/doc/development/creating_enums.md @marcia
/doc/development/database_debugging.md @marcia
/doc/development/database_query_comments.md @marcia
/doc/development/database_review.md @marcia
/doc/development/database/ @marcia
/doc/development/database/multiple_databases.md @marcia
/doc/development/db_dump.md @marcia
/doc/development/creating_enums.md @sselhorn
/doc/development/database_debugging.md @sselhorn
/doc/development/database_query_comments.md @sselhorn
/doc/development/database_review.md @sselhorn
/doc/development/database/ @sselhorn
/doc/development/database/multiple_databases.md @sselhorn
/doc/development/db_dump.md @sselhorn
/doc/development/developing_with_solargraph.md @aqualls
/doc/development/diffs.md @aqualls
/doc/development/distributed_tracing.md @msedlakjakubowski
@ -531,82 +531,82 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/documentation/structure.md @sselhorn
/doc/development/documentation/styleguide/ @sselhorn
/doc/development/documentation/testing.md @dianalogan
/doc/development/elasticsearch.md @marcia
/doc/development/elasticsearch.md @sselhorn
/doc/development/experiment_guide/gitlab_experiment.md @kpaizee
/doc/development/experiment_guide/index.md @kpaizee
/doc/development/export_csv.md @eread
/doc/development/fe_guide/content_editor.md @aqualls
/doc/development/fe_guide/dark_mode.md @marcia
/doc/development/fe_guide/graphql.md @marcia
/doc/development/fe_guide/dark_mode.md @sselhorn
/doc/development/fe_guide/graphql.md @sselhorn
/doc/development/fe_guide/source_editor.md @aqualls
/doc/development/feature_categorization/index.md @marcia
/doc/development/feature_flags/controls.md @marcia
/doc/development/feature_flags/index.md @marcia
/doc/development/feature_categorization/index.md @sselhorn
/doc/development/feature_flags/controls.md @sselhorn
/doc/development/feature_flags/index.md @sselhorn
/doc/development/filtering_by_label.md @msedlakjakubowski
/doc/development/foreign_keys.md @marcia
/doc/development/foreign_keys.md @sselhorn
/doc/development/geo.md @axil
/doc/development/geo/framework.md @axil
/doc/development/git_object_deduplication.md @eread
/doc/development/gitaly.md @eread
/doc/development/graphql_guide/ @kpaizee
/doc/development/graphql_guide/batchloader.md @marcia
/doc/development/hash_indexes.md @marcia
/doc/development/graphql_guide/batchloader.md @sselhorn
/doc/development/hash_indexes.md @sselhorn
/doc/development/i18n/ @eread
/doc/development/image_scaling.md @marcia
/doc/development/image_scaling.md @sselhorn
/doc/development/import_export.md @eread
/doc/development/index.md @marcia
/doc/development/insert_into_tables_in_batches.md @marcia
/doc/development/index.md @sselhorn
/doc/development/insert_into_tables_in_batches.md @sselhorn
/doc/development/integrations/ @kpaizee
/doc/development/integrations/codesandbox.md @marcia
/doc/development/integrations/codesandbox.md @sselhorn
/doc/development/integrations/secure_partner_integration.md @rdickenson
/doc/development/integrations/secure.md @claytoncornell
/doc/development/internal_api/ @aqualls
/doc/development/internal_users.md @marcia
/doc/development/internal_users.md @sselhorn
/doc/development/issuable-like-models.md @msedlakjakubowski
/doc/development/issue_types.md @msedlakjakubowski
/doc/development/iterating_tables_in_batches.md @marcia
/doc/development/kubernetes.md @marcia
/doc/development/iterating_tables_in_batches.md @sselhorn
/doc/development/kubernetes.md @sselhorn
/doc/development/lfs.md @aqualls
/doc/development/licensed_feature_availability.md @sselhorn
/doc/development/logging.md @msedlakjakubowski
/doc/development/maintenance_mode.md @axil
/doc/development/new_fe_guide/modules/widget_extensions.md @aqualls
/doc/development/new_fe_guide/tips.md @marcia
/doc/development/new_fe_guide/tips.md @sselhorn
/doc/development/omnibus.md @axil
/doc/development/ordering_table_columns.md @marcia
/doc/development/ordering_table_columns.md @sselhorn
/doc/development/packages.md @claytoncornell
/doc/development/permissions.md @eread
/doc/development/policies.md @eread
/doc/development/product_qualified_lead_guide/index.md @kpaizee
/doc/development/project_templates.md @fneill
/doc/development/prometheus_metrics.md @msedlakjakubowski
/doc/development/query_performance.md @marcia
/doc/development/query_recorder.md @marcia
/doc/development/query_performance.md @sselhorn
/doc/development/query_recorder.md @sselhorn
/doc/development/real_time.md @msedlakjakubowski
/doc/development/secure_coding_guidelines.md @marcia
/doc/development/serializing_data.md @marcia
/doc/development/secure_coding_guidelines.md @sselhorn
/doc/development/serializing_data.md @sselhorn
/doc/development/service_ping/ @claytoncornell
/doc/development/single_table_inheritance.md @marcia
/doc/development/single_table_inheritance.md @sselhorn
/doc/development/snowplow/ @claytoncornell
/doc/development/spam_protection_and_captcha/ @eread
/doc/development/sql.md @marcia
/doc/development/swapping_tables.md @marcia
/doc/development/testing_guide/best_practices.md @marcia
/doc/development/testing_guide/end_to_end/best_practices.md @marcia
/doc/development/understanding_explain_plans.md @marcia
/doc/development/sql.md @sselhorn
/doc/development/swapping_tables.md @sselhorn
/doc/development/testing_guide/best_practices.md @sselhorn
/doc/development/testing_guide/end_to_end/best_practices.md @sselhorn
/doc/development/understanding_explain_plans.md @sselhorn
/doc/development/value_stream_analytics.md @fneill
/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md @fneill
/doc/development/verifying_database_capabilities.md @marcia
/doc/development/verifying_database_capabilities.md @sselhorn
/doc/development/wikis.md @aqualls
/doc/development/work_items_widgets.md @msedlakjakubowski
/doc/development/work_items.md @msedlakjakubowski
/doc/development/workhorse/ @aqualls
/doc/development/workspace/index.md @marcia
/doc/development/workspace/index.md @sselhorn
/doc/downgrade_ee_to_ce/index.md @axil
/doc/gitlab-basics/ @aqualls
/doc/install/ @axil
/doc/integration/ @kpaizee
/doc/integration/elasticsearch.md @marcia
/doc/integration/elasticsearch.md @sselhorn
/doc/integration/gitpod.md @aqualls
/doc/integration/kerberos.md @eread
/doc/integration/mattermost/index.md @axil
@ -614,7 +614,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/integration/saml.md @eread
/doc/integration/security_partners/index.md @rdickenson
/doc/integration/sourcegraph.md @aqualls
/doc/integration/vault.md @marcia
/doc/integration/vault.md @sselhorn
/doc/operations/ @msedlakjakubowski
/doc/operations/feature_flags.md @rdickenson
/doc/operations/product_analytics.md @claytoncornell
@ -627,14 +627,14 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/security/ @eread
/doc/subscriptions/ @sselhorn
/doc/topics/authentication/index.md @eread
/doc/topics/autodevops/ @marcia
/doc/topics/autodevops/ @sselhorn
/doc/topics/git/ @aqualls
/doc/topics/gitlab_flow.md @aqualls
/doc/topics/offline/ @axil
/doc/topics/plan_and_track.md @msedlakjakubowski
/doc/update/ @axil
/doc/update/mysql_to_postgresql.md @marcia
/doc/update/upgrading_postgresql_using_slony.md @marcia
/doc/update/mysql_to_postgresql.md @sselhorn
/doc/update/upgrading_postgresql_using_slony.md @sselhorn
/doc/user/admin_area/analytics/ @fneill
/doc/user/admin_area/broadcast_messages.md @kpaizee
/doc/user/admin_area/credentials_inventory.md @eread
@ -646,7 +646,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/admin_area/license.md @kpaizee
/doc/user/admin_area/merge_requests_approvals.md @aqualls
/doc/user/admin_area/moderate_users.md @eread
/doc/user/admin_area/monitoring/background_migrations.md @marcia
/doc/user/admin_area/monitoring/background_migrations.md @sselhorn
/doc/user/admin_area/monitoring/health_check.md @msedlakjakubowski
/doc/user/admin_area/reporting/spamcheck.md @axil
/doc/user/admin_area/review_abuse_reports.md @eread
@ -684,14 +684,14 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/application_security/vulnerability_report/index.md @claytoncornell
/doc/user/asciidoc.md @aqualls
/doc/user/award_emojis.md @msedlakjakubowski
/doc/user/clusters/ @marcia
/doc/user/clusters/ @sselhorn
/doc/user/compliance/compliance_report/index.md @eread
/doc/user/compliance/index.md @eread
/doc/user/compliance/license_compliance/index.md @rdickenson
/doc/user/crm/index.md @msedlakjakubowski
/doc/user/discussions/index.md @aqualls
/doc/user/feature_flags.md @marcia
/doc/user/group/clusters/index.md @marcia
/doc/user/feature_flags.md @sselhorn
/doc/user/group/clusters/index.md @sselhorn
/doc/user/group/contribution_analytics/index.md @fneill
/doc/user/group/custom_project_templates.md @eread
/doc/user/group/devops_adoption/index.md @fneill
@ -714,27 +714,27 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/group/settings/import_export.md @eread
/doc/user/group/subgroups/index.md @fneill
/doc/user/group/value_stream_analytics/index.md @fneill
/doc/user/infrastructure/clusters/ @marcia
/doc/user/infrastructure/clusters/ @sselhorn
/doc/user/infrastructure/clusters/manage/management_project_applications/apparmor.md @claytoncornell
/doc/user/infrastructure/clusters/manage/management_project_applications/cilium.md @claytoncornell
/doc/user/infrastructure/clusters/manage/management_project_applications/elasticstack.md @msedlakjakubowski
/doc/user/infrastructure/clusters/manage/management_project_applications/falco.md @claytoncornell
/doc/user/infrastructure/clusters/manage/management_project_applications/fluentd.md @claytoncornell
/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md @msedlakjakubowski
/doc/user/infrastructure/clusters/manage/management_project_applications/runner.md @marcia
/doc/user/infrastructure/clusters/manage/management_project_applications/runner.md @sselhorn
/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md @msedlakjakubowski
/doc/user/infrastructure/iac/ @marcia
/doc/user/infrastructure/index.md @marcia
/doc/user/infrastructure/iac/ @sselhorn
/doc/user/infrastructure/index.md @sselhorn
/doc/user/markdown.md @aqualls
/doc/user/packages/ @claytoncornell
/doc/user/packages/infrastructure_registry/index.md @marcia
/doc/user/packages/terraform_module_registry/index.md @marcia
/doc/user/packages/infrastructure_registry/index.md @sselhorn
/doc/user/packages/terraform_module_registry/index.md @sselhorn
/doc/user/permissions.md @eread
/doc/user/profile/ @eread
/doc/user/profile/notifications.md @msedlakjakubowski
/doc/user/project/autocomplete_characters.md @aqualls
/doc/user/project/badges.md @aqualls
/doc/user/project/clusters/ @marcia
/doc/user/project/clusters/ @sselhorn
/doc/user/project/clusters/kubernetes_pod_logs.md @msedlakjakubowski
/doc/user/project/clusters/protect/ @claytoncornell
/doc/user/project/code_intelligence.md @aqualls
@ -789,7 +789,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/project/working_with_projects.md @fneill
/doc/user/public_access.md @fneill
/doc/user/reserved_names.md @fneill
/doc/user/search/advanced_search.md @marcia
/doc/user/search/advanced_search.md @sselhorn
/doc/user/search/index.md @aqualls
/doc/user/shortcuts.md @aqualls
/doc/user/snippets.md @aqualls

View File

@ -7,6 +7,7 @@ import {
GlDropdownSectionHeader,
GlDropdownItem,
GlSearchBoxByType,
GlTruncate,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import { filterBySearchTerm } from '~/analytics/shared/utils';
@ -28,6 +29,7 @@ export default {
GlDropdownSectionHeader,
GlDropdownItem,
GlSearchBoxByType,
GlTruncate,
},
props: {
groupId: {
@ -212,30 +214,29 @@ export default {
<gl-dropdown
ref="projectsDropdown"
class="dropdown dropdown-projects"
toggle-class="gl-shadow-none"
toggle-class="gl-shadow-none gl-mb-0"
:loading="loadingDefaultProjects"
:show-clear-all="hasSelectedProjects"
show-highlighted-items-title
highlighted-items-title-class="gl-p-3"
block
@clear-all.stop="onClearAll"
@hide="onHide"
>
<template #button-content>
<gl-loading-icon v-if="loadingDefaultProjects" class="gl-mr-2" />
<div class="gl-display-flex gl-flex-grow-1">
<gl-avatar
v-if="isOnlyOneProjectSelected"
:src="selectedProjects[0].avatarUrl"
:entity-id="getEntityId(selectedProjects[0])"
:entity-name="selectedProjects[0].name"
:size="16"
:shape="$options.AVATAR_SHAPE_OPTION_RECT"
:alt="selectedProjects[0].name"
class="gl-display-inline-flex gl-vertical-align-middle gl-mr-2"
/>
{{ selectedProjectsLabel }}
</div>
<gl-icon class="gl-ml-2" name="chevron-down" />
<gl-loading-icon v-if="loadingDefaultProjects" class="gl-mr-2 gl-flex-shrink-0" />
<gl-avatar
v-if="isOnlyOneProjectSelected"
:src="selectedProjects[0].avatarUrl"
:entity-id="getEntityId(selectedProjects[0])"
:entity-name="selectedProjects[0].name"
:size="16"
:shape="$options.AVATAR_SHAPE_OPTION_RECT"
:alt="selectedProjects[0].name"
class="gl-display-inline-flex gl-vertical-align-middle gl-mr-2 gl-flex-shrink-0"
/>
<gl-truncate :text="selectedProjectsLabel" class="gl-min-w-0 gl-flex-grow-1" />
<gl-icon class="gl-ml-2 gl-flex-shrink-0" name="chevron-down" />
</template>
<template #header>
<gl-dropdown-section-header>{{ __('Projects') }}</gl-dropdown-section-header>

View File

@ -1,18 +1,15 @@
import { sortMilestonesByDueDate } from '~/milestones/utils';
import { mergeUrlParams } from '../lib/utils/url_utility';
import DropdownAjaxFilter from './dropdown_ajax_filter';
import DropdownEmoji from './dropdown_emoji';
import DropdownHint from './dropdown_hint';
import DropdownNonUser from './dropdown_non_user';
import DropdownOperator from './dropdown_operator';
import DropdownUser from './dropdown_user';
import DropdownUtils from './dropdown_utils';
import NullDropdown from './null_dropdown';
export default class AvailableDropdownMappings {
constructor({
container,
runnerTagsEndpoint,
labelsEndpoint,
milestonesEndpoint,
releasesEndpoint,
@ -22,7 +19,6 @@ export default class AvailableDropdownMappings {
includeDescendantGroups,
}) {
this.container = container;
this.runnerTagsEndpoint = runnerTagsEndpoint;
this.labelsEndpoint = labelsEndpoint;
this.milestonesEndpoint = milestonesEndpoint;
this.releasesEndpoint = releasesEndpoint;
@ -135,25 +131,6 @@ export default class AvailableDropdownMappings {
gl: DropdownNonUser,
element: this.container.querySelector('#js-dropdown-confidential'),
},
status: {
reference: null,
gl: NullDropdown,
element: this.container.querySelector('#js-dropdown-admin-runner-status'),
},
type: {
reference: null,
gl: NullDropdown,
element: this.container.querySelector('#js-dropdown-admin-runner-type'),
},
tag: {
reference: null,
gl: DropdownAjaxFilter,
extraArguments: {
endpoint: this.getRunnerTagsEndpoint(),
symbol: '~',
},
element: this.container.querySelector('#js-dropdown-runner-tag'),
},
'target-branch': {
reference: null,
gl: DropdownNonUser,
@ -202,10 +179,6 @@ export default class AvailableDropdownMappings {
return endpoint;
}
getRunnerTagsEndpoint() {
return `${this.runnerTagsEndpoint}.json`;
}
getMergeRequestTargetBranchesEndpoint() {
const endpoint = `${
gon.relative_url_root || ''

View File

@ -15,6 +15,4 @@ export const MAX_HISTORY_SIZE = 5;
export const FILTERED_SEARCH = {
MERGE_REQUESTS: 'merge_requests',
ISSUES: 'issues',
ADMIN_RUNNERS: 'admin/runners',
GROUP_RUNNERS_ANCHOR: 'runners-settings',
};

View File

@ -9,7 +9,6 @@ import FilteredSearchVisualTokens from './filtered_search_visual_tokens';
export default class FilteredSearchDropdownManager {
constructor({
runnerTagsEndpoint = '',
labelsEndpoint = '',
milestonesEndpoint = '',
iterationsEndpoint = '',
@ -26,7 +25,6 @@ export default class FilteredSearchDropdownManager {
const removeTrailingSlash = (url) => url.replace(/\/$/, '');
this.container = FilteredSearchContainer.container;
this.runnerTagsEndpoint = removeTrailingSlash(runnerTagsEndpoint);
this.labelsEndpoint = removeTrailingSlash(labelsEndpoint);
this.milestonesEndpoint = removeTrailingSlash(milestonesEndpoint);
this.iterationsEndpoint = removeTrailingSlash(iterationsEndpoint);

View File

@ -114,7 +114,6 @@ export default class FilteredSearchManager {
this.tokenizer = FilteredSearchTokenizer;
const {
runnerTagsEndpoint = '',
labelsEndpoint = '',
milestonesEndpoint = '',
releasesEndpoint = '',
@ -124,7 +123,6 @@ export default class FilteredSearchManager {
} = this.filteredSearchInput.dataset;
this.dropdownManager = new FilteredSearchDropdownManager({
runnerTagsEndpoint,
labelsEndpoint,
milestonesEndpoint,
releasesEndpoint,

View File

@ -1,27 +0,0 @@
import { __ } from '~/locale';
import FilteredSearchTokenKeys from './filtered_search_token_keys';
const tokenKeys = [
{
formattedKey: __('Status'),
key: 'status',
type: 'string',
param: 'status',
symbol: '',
icon: 'messages',
tag: 'status',
},
{
formattedKey: __('Type'),
key: 'type',
type: 'string',
param: 'type',
symbol: '',
icon: 'cube',
tag: 'type',
},
];
const GroupRunnersFilteredSearchTokenKeys = new FilteredSearchTokenKeys(tokenKeys);
export default GroupRunnersFilteredSearchTokenKeys;

View File

@ -1,9 +0,0 @@
import FilteredSearchDropdown from './filtered_search_dropdown';
export default class NullDropdown extends FilteredSearchDropdown {
renderContent(forceShowList = false) {
this.droplab.changeHookList(this.hookId, this.dropdown, [], this.config);
super.renderContent(forceShowList);
}
}

View File

@ -7,8 +7,8 @@ import AccessorUtilities from '~/lib/utils/accessor';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { I18N_DEFAULT_SIGN_IN_ERROR_MESSAGE } from '../constants';
import { SET_ALERT } from '../store/mutation_types';
import SignInPage from '../pages/sign_in.vue';
import SubscriptionsPage from '../pages/subscriptions.vue';
import SignInPage from '../pages/sign_in/sign_in_page.vue';
import SubscriptionsPage from '../pages/subscriptions_page.vue';
import UserLink from './user_link.vue';
import CompatibilityAlert from './compatibility_alert.vue';
import BrowserSupportAlert from './browser_support_alert.vue';
@ -111,7 +111,6 @@ export default {
<user-link :user-signed-in="userSignedIn" :has-subscriptions="hasSubscriptions" :user="user" />
<h2 class="gl-text-center gl-mb-7">{{ s__('JiraService|GitLab for Jira Configuration') }}</h2>
<div class="gl-layout-w-limited gl-mx-auto gl-px-5 gl-mb-7">
<sign-in-page
v-if="!userSignedIn"

View File

@ -1,65 +0,0 @@
<script>
import { s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import SubscriptionsList from '../components/subscriptions_list.vue';
export default {
name: 'SignInPage',
components: {
SubscriptionsList,
SignInLegacyButton: () => import('../components/sign_in_legacy_button.vue'),
SignInOauthButton: () => import('../components/sign_in_oauth_button.vue'),
},
mixins: [glFeatureFlagMixin()],
inject: ['usersPath'],
props: {
hasSubscriptions: {
type: Boolean,
required: true,
},
},
computed: {
useSignInOauthButton() {
return this.glFeatures.jiraConnectOauth;
},
},
i18n: {
signInButtonTextWithSubscriptions: s__('Integrations|Sign in to add namespaces'),
signInText: s__('JiraService|Sign in to GitLab.com to get started.'),
},
methods: {
onSignInError() {
this.$emit('error');
},
},
};
</script>
<template>
<div v-if="hasSubscriptions">
<div class="gl-display-flex gl-justify-content-end">
<sign-in-oauth-button
v-if="useSignInOauthButton"
@sign-in="$emit('sign-in-oauth', $event)"
@error="onSignInError"
>
{{ $options.i18n.signInButtonTextWithSubscriptions }}
</sign-in-oauth-button>
<sign-in-legacy-button v-else :users-path="usersPath">
{{ $options.i18n.signInButtonTextWithSubscriptions }}
</sign-in-legacy-button>
</div>
<subscriptions-list />
</div>
<div v-else class="gl-text-center">
<p class="gl-mb-7">{{ $options.i18n.signInText }}</p>
<sign-in-oauth-button
v-if="useSignInOauthButton"
@sign-in="$emit('sign-in-oauth', $event)"
@error="onSignInError"
/>
<sign-in-legacy-button v-else class="gl-mb-7" :users-path="usersPath" />
</div>
</template>

View File

@ -0,0 +1,68 @@
<script>
import { s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import SubscriptionsList from '../../components/subscriptions_list.vue';
export default {
name: 'SignInGitlabCom',
components: {
SubscriptionsList,
SignInLegacyButton: () => import('../../components/sign_in_legacy_button.vue'),
SignInOauthButton: () => import('../../components/sign_in_oauth_button.vue'),
},
mixins: [glFeatureFlagMixin()],
inject: ['usersPath'],
props: {
hasSubscriptions: {
type: Boolean,
required: true,
},
},
computed: {
useSignInOauthButton() {
return this.glFeatures.jiraConnectOauth;
},
},
i18n: {
signInButtonTextWithSubscriptions: s__('Integrations|Sign in to add namespaces'),
signInText: s__('JiraService|Sign in to GitLab.com to get started.'),
},
methods: {
onSignInError() {
this.$emit('error');
},
},
};
</script>
<template>
<div>
<h2 class="gl-text-center gl-mb-7">{{ s__('JiraService|GitLab for Jira Configuration') }}</h2>
<div v-if="hasSubscriptions">
<div class="gl-display-flex gl-justify-content-end">
<sign-in-oauth-button
v-if="useSignInOauthButton"
@sign-in="$emit('sign-in-oauth', $event)"
@error="onSignInError"
>
{{ $options.i18n.signInButtonTextWithSubscriptions }}
</sign-in-oauth-button>
<sign-in-legacy-button v-else :users-path="usersPath">
{{ $options.i18n.signInButtonTextWithSubscriptions }}
</sign-in-legacy-button>
</div>
<subscriptions-list />
</div>
<div v-else class="gl-text-center">
<p class="gl-mb-7">{{ $options.i18n.signInText }}</p>
<sign-in-oauth-button
v-if="useSignInOauthButton"
@sign-in="$emit('sign-in-oauth', $event)"
@error="onSignInError"
/>
<sign-in-legacy-button v-else class="gl-mb-7" :users-path="usersPath" />
</div>
</div>
</template>

View File

@ -0,0 +1,11 @@
<script>
export default {
name: 'SignInGitlabMultiversion',
};
</script>
<template>
<div>
<!-- TODO -->
</div>
</template>

View File

@ -0,0 +1,31 @@
<script>
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import SignInGitlabCom from './sign_in_gitlab_com.vue';
import SignInGitlabMultiversion from './sign_in_gitlab_multiversion.vue';
export default {
name: 'SignInPage',
components: { SignInGitlabCom, SignInGitlabMultiversion },
mixins: [glFeatureFlagMixin()],
props: {
hasSubscriptions: {
type: Boolean,
required: true,
},
},
computed: {
isOauthSelfManagedEnabled() {
return this.glFeatures.jiraConnectOauth && this.glFeatures.jiraConnectOauthSelfManaged;
},
},
};
</script>
<template>
<sign-in-gitlab-multiversion v-if="isOauthSelfManagedEnabled" />
<sign-in-gitlab-com
v-else
:has-subscriptions="hasSubscriptions"
@sign-in-oauth="$emit('sign-in-oauth', $event)"
@error="$emit('error', $event)"
/>
</template>

View File

@ -1,43 +0,0 @@
<script>
import { GlEmptyState } from '@gitlab/ui';
import SubscriptionsList from '../components/subscriptions_list.vue';
import AddNamespaceButton from '../components/add_namespace_button.vue';
export default {
name: 'SubscriptionsPage',
components: {
GlEmptyState,
SubscriptionsList,
AddNamespaceButton,
},
props: {
hasSubscriptions: {
type: Boolean,
required: true,
},
},
};
</script>
<template>
<div v-if="hasSubscriptions">
<div class="gl-display-flex gl-justify-content-end">
<add-namespace-button />
</div>
<subscriptions-list />
</div>
<gl-empty-state
v-else
:title="s__('Integrations|No linked namespaces')"
:description="
s__(
'Integrations|Namespaces are the GitLab groups and subgroups you link to this Jira instance.',
)
"
>
<template #actions>
<add-namespace-button />
</template>
</gl-empty-state>
</template>

View File

@ -0,0 +1,47 @@
<script>
import { GlEmptyState } from '@gitlab/ui';
import SubscriptionsList from '../components/subscriptions_list.vue';
import AddNamespaceButton from '../components/add_namespace_button.vue';
export default {
name: 'SubscriptionsPage',
components: {
GlEmptyState,
SubscriptionsList,
AddNamespaceButton,
},
props: {
hasSubscriptions: {
type: Boolean,
required: true,
},
},
};
</script>
<template>
<div>
<h2 class="gl-text-center gl-mb-7">{{ s__('JiraService|GitLab for Jira Configuration') }}</h2>
<div v-if="hasSubscriptions">
<div class="gl-display-flex gl-justify-content-end">
<add-namespace-button />
</div>
<subscriptions-list />
</div>
<gl-empty-state
v-else
:title="s__('Integrations|No linked namespaces')"
:description="
s__(
'Integrations|Namespaces are the GitLab groups and subgroups you link to this Jira instance.',
)
"
>
<template #actions>
<add-namespace-button />
</template>
</gl-empty-state>
</div>
</template>

View File

@ -1,7 +1,7 @@
import { isEmpty, isString } from 'lodash';
import { isEmpty } from 'lodash';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
export const headerTime = (state) => (state.job.started ? state.job.started : state.job.created_at);
export const headerTime = (state) => state.job.started_at || state.job.created_at;
export const hasForwardDeploymentFailure = (state) =>
state?.job?.failure_reason === 'forward_deployment_failure';
@ -13,10 +13,10 @@ export const shouldRenderCalloutMessage = (state) =>
!isEmpty(state.job.status) && !isEmpty(state.job.callout_message);
/**
* When job has not started the key will be null
* When job started the key will be a string with a date.
* When the job has not started the value of job.started_at will be null
* When job has started the value of job.started_at will be a string with a date.
*/
export const shouldRenderTriggeredLabel = (state) => isString(state.job.started);
export const shouldRenderTriggeredLabel = (state) => Boolean(state.job.started_at);
export const hasEnvironment = (state) => !isEmpty(state.job.deployment_status);

View File

@ -1,24 +1,9 @@
import initVariableList from '~/ci_variable_list';
import GroupRunnersFilteredSearchTokenKeys from '~/filtered_search/group_runners_filtered_search_token_keys';
import initSharedRunnersForm from '~/group_settings/mount_shared_runners';
import { FILTERED_SEARCH } from '~/filtered_search/constants';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { initRunnerAwsDeployments } from '~/pages/shared/mount_runner_aws_deployments';
import { initInstallRunner } from '~/pages/shared/mount_runner_instructions';
import initSettingsPanels from '~/settings_panels';
// Initialize expandable settings panels
initSettingsPanels();
initFilteredSearch({
page: FILTERED_SEARCH.ADMIN_RUNNERS,
filteredSearchTokenKeys: GroupRunnersFilteredSearchTokenKeys,
anchor: FILTERED_SEARCH.GROUP_RUNNERS_ANCHOR,
useDefaultState: false,
});
initSharedRunnersForm();
initVariableList();
initInstallRunner();
initRunnerAwsDeployments();

View File

@ -65,6 +65,7 @@ export default class Project {
const fieldName = $dropdown.data('fieldName');
const shouldVisit = Boolean($dropdown.data('visit'));
const $form = $dropdown.closest('form');
const path = $form.find('#path').val();
const action = $form.attr('action');
const linkTarget = mergeUrlParams(serializeForm($form[0]), action);
@ -116,20 +117,21 @@ export default class Project {
},
clicked(options) {
const { e } = options;
e.preventDefault();
// Since this page does not reload when changing directories in a repo
// the rendered links do not have the path to the current directory.
// This updates the path based on the current url and then opens
// the the url with the updated path parameter.
if (shouldVisit) {
if (!shouldVisit) {
e.preventDefault();
}
// Some pages need to dynamically get the current path
// so they can opt-in to JS getting the path from the
// current URL by not setting a path in the dropdown form
if (shouldVisit && path === undefined) {
e.preventDefault();
const selectedUrl = new URL(e.target.href);
const loc = window.location.href;
if (loc.includes('/-/')) {
// Since the current ref in renderRow is outdated on page changes
// (To be addressed in: https://gitlab.com/gitlab-org/gitlab/-/issues/327085)
// We are deciphering the current ref from the dropdown data instead
const currentRef = $dropdown.data('ref');
// The split and startWith is to ensure an exact word match
// and avoid partial match ie. currentRef is "dev" and loc is "development"

View File

@ -1,8 +1,5 @@
import initTree from 'ee_else_ce/repository';
import Activities from '~/activities';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import { BlobViewer } from '~/blob/viewer';
import { initUploadForm } from '~/blob_edit/blob_bundle';
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
import leaveByUrl from '~/namespaces/leave_by_url';
@ -10,33 +7,38 @@ import initVueNotificationsDropdown from '~/notifications';
import Star from '~/projects/star';
import { initUploadFileTrigger } from '~/projects/upload_file';
import initReadMore from '~/read_more';
import UserCallout from '~/user_callout';
initReadMore();
new Star(); // eslint-disable-line no-new
// eslint-disable-next-line no-new
new UserCallout({
setCalloutPerProject: false,
className: 'js-autodevops-banner',
});
// Project show page loads different overview content based on user preferences
if (document.querySelector('.js-upload-blob-form')) {
initUploadForm();
import(/* webpackChunkName: 'blobBundle' */ '~/blob_edit/blob_bundle')
.then(({ initUploadForm }) => {
initUploadForm();
})
.catch(() => {});
}
if (document.getElementById('js-tree-list')) {
initTree();
import(/* webpackChunkName: 'treeList' */ 'ee_else_ce/repository')
.then(({ default: initTree }) => {
initTree();
})
.catch(() => {});
}
if (document.querySelector('.blob-viewer')) {
new BlobViewer(); // eslint-disable-line no-new
import(/* webpackChunkName: 'blobViewer' */ '~/blob/viewer')
.then(({ BlobViewer }) => {
new BlobViewer(); // eslint-disable-line no-new
})
.catch(() => {});
}
if (document.querySelector('.project-show-activity')) {
new Activities(); // eslint-disable-line no-new
import(/* webpackChunkName: 'activitiesList' */ '~/activities')
.then(({ default: Activities }) => {
new Activities(); // eslint-disable-line no-new
})
.catch(() => {});
}
leaveByUrl('project');
@ -48,3 +50,18 @@ new ShortcutsNavigation(); // eslint-disable-line no-new
initUploadFileTrigger();
initInviteMembersModal();
initInviteMembersTrigger();
initReadMore();
new Star(); // eslint-disable-line no-new
if (document.querySelector('.js-autodevops-banner')) {
import(/* webpackChunkName: 'userCallOut' */ '~/user_callout')
.then(({ default: UserCallout }) => {
// eslint-disable-next-line no-new
new UserCallout({
setCalloutPerProject: false,
className: 'js-autodevops-banner',
});
})
.catch(() => {});
}

View File

@ -19,6 +19,11 @@ export default {
type: String,
required: true,
},
runnerUrl: {
type: String,
required: false,
default: null,
},
},
data() {
return {
@ -40,6 +45,11 @@ export default {
},
},
},
computed: {
loading() {
return this.$apollo.queries.runner.loading;
},
},
errorCaptured(error) {
this.reportToSentry(error);
},
@ -53,6 +63,11 @@ export default {
<template>
<div>
<runner-header v-if="runner" :runner="runner" />
<runner-update-form :runner="runner" class="gl-my-5" />
<runner-update-form
:loading="loading"
:runner="runner"
:runner-url="runnerUrl"
class="gl-my-5"
/>
</div>
</template>

View File

@ -12,7 +12,7 @@ export const initAdminRunnerEdit = (selector = '#js-admin-runner-edit') => {
return null;
}
const { runnerId } = el.dataset;
const { runnerId, runnerUrl } = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
@ -25,6 +25,7 @@ export const initAdminRunnerEdit = (selector = '#js-admin-runner-edit') => {
return h(AdminRunnerEditApp, {
props: {
runnerId,
runnerUrl,
},
});
},

View File

@ -1,10 +1,12 @@
<script>
import {
GlButton,
GlIcon,
GlForm,
GlFormCheckbox,
GlFormGroup,
GlFormInputGroup,
GlSkeletonLoader,
GlTooltipDirective,
} from '@gitlab/ui';
import {
@ -21,10 +23,12 @@ export default {
name: 'RunnerUpdateForm',
components: {
GlButton,
GlIcon,
GlForm,
GlFormCheckbox,
GlFormGroup,
GlFormInputGroup,
GlSkeletonLoader,
RunnerUpdateCostFactorFields: () =>
import('ee_component/runner/components/runner_update_cost_factor_fields.vue'),
},
@ -37,6 +41,16 @@ export default {
required: false,
default: null,
},
loading: {
type: Boolean,
required: false,
default: false,
},
runnerUrl: {
type: String,
required: false,
default: null,
},
},
data() {
return {
@ -48,9 +62,6 @@ export default {
canBeLockedToProject() {
return this.runner?.runnerType === PROJECT_TYPE;
},
readonlyIpAddress() {
return this.runner?.ipAddress;
},
},
watch: {
runner(newVal, oldVal) {
@ -100,104 +111,108 @@ export default {
</script>
<template>
<gl-form @submit.prevent="onSubmit">
<gl-form-checkbox
v-model="model.active"
data-testid="runner-field-paused"
:value="false"
:unchecked-value="true"
>
{{ __('Paused') }}
<template #help>
{{ s__('Runners|Stop the runner from accepting new jobs.') }}
</template>
</gl-form-checkbox>
<h4 class="gl-font-lg gl-my-5">{{ s__('Runners|Details') }}</h4>
<gl-form-checkbox
v-model="model.accessLevel"
data-testid="runner-field-protected"
:value="$options.ACCESS_LEVEL_REF_PROTECTED"
:unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED"
>
{{ __('Protected') }}
<template #help>
{{ s__('Runners|Use the runner on pipelines for protected branches only.') }}
</template>
</gl-form-checkbox>
<gl-form-checkbox v-model="model.runUntagged" data-testid="runner-field-run-untagged">
{{ __('Run untagged jobs') }}
<template #help>
{{ s__('Runners|Use the runner for jobs without tags, in addition to tagged jobs.') }}
</template>
</gl-form-checkbox>
<gl-form-checkbox
v-if="canBeLockedToProject"
v-model="model.locked"
data-testid="runner-field-locked"
>
{{ __('Lock to current projects') }}
<template #help>
{{
s__(
'Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects.',
)
}}
</template>
</gl-form-checkbox>
<gl-form-group :label="__('IP Address')" data-testid="runner-field-ip-address">
<gl-form-input-group :value="readonlyIpAddress" readonly select-on-click>
<template #append>
<gl-button
v-gl-tooltip.hover
:title="__('Copy IP Address')"
:aria-label="__('Copy IP Address')"
:data-clipboard-text="readonlyIpAddress"
icon="copy-to-clipboard"
class="d-inline-flex"
/>
</template>
</gl-form-input-group>
</gl-form-group>
<gl-form-group :label="__('Description')" data-testid="runner-field-description">
<gl-skeleton-loader v-if="loading" />
<gl-form-group v-else :label="__('Description')" data-testid="runner-field-description">
<gl-form-input-group v-model="model.description" />
</gl-form-group>
<gl-form-group
data-testid="runner-field-max-timeout"
:label="__('Maximum job timeout')"
:description="
s__(
'Runners|Enter the number of seconds. This timeout takes precedence over lower timeouts set for the project.',
)
"
>
<gl-form-input-group v-model.number="model.maximumTimeout" type="number" />
</gl-form-group>
<hr />
<gl-form-group
data-testid="runner-field-tags"
:label="__('Tags')"
:description="
__('You can set up jobs to only use runners with specific tags. Separate tags with commas.')
"
>
<gl-form-input-group v-model="model.tagList" />
</gl-form-group>
<h4 class="gl-font-lg gl-my-5">{{ s__('Runners|Configuration') }}</h4>
<runner-update-cost-factor-fields v-model="model" />
<template v-if="loading">
<gl-skeleton-loader v-for="i in 3" :key="i" />
</template>
<template v-else>
<div class="gl-mb-5">
<gl-form-checkbox
v-model="model.active"
data-testid="runner-field-paused"
:value="false"
:unchecked-value="true"
>
{{ __('Paused') }}
<template #help>
{{ s__('Runners|Stop the runner from accepting new jobs.') }}
</template>
</gl-form-checkbox>
<div class="form-actions">
<gl-form-checkbox
v-model="model.accessLevel"
data-testid="runner-field-protected"
:value="$options.ACCESS_LEVEL_REF_PROTECTED"
:unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED"
>
{{ __('Protected') }}
<template #help>
{{ s__('Runners|Use the runner on pipelines for protected branches only.') }}
</template>
</gl-form-checkbox>
<gl-form-checkbox v-model="model.runUntagged" data-testid="runner-field-run-untagged">
{{ __('Run untagged jobs') }}
<template #help>
{{ s__('Runners|Use the runner for jobs without tags, in addition to tagged jobs.') }}
</template>
</gl-form-checkbox>
<gl-form-checkbox
v-if="canBeLockedToProject"
v-model="model.locked"
data-testid="runner-field-locked"
>
{{ __('Lock to current projects') }} <gl-icon name="lock" />
<template #help>
{{
s__(
'Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects.',
)
}}
</template>
</gl-form-checkbox>
</div>
<gl-form-group
data-testid="runner-field-max-timeout"
:label="__('Maximum job timeout')"
:description="
s__(
'Runners|Enter the number of seconds. This timeout takes precedence over lower timeouts set for the project.',
)
"
>
<gl-form-input-group v-model.number="model.maximumTimeout" type="number" />
</gl-form-group>
<gl-form-group
data-testid="runner-field-tags"
:label="__('Tags')"
:description="
__(
'You can set up jobs to only use runners with specific tags. Separate tags with commas.',
)
"
>
<gl-form-input-group v-model="model.tagList" />
</gl-form-group>
<runner-update-cost-factor-fields v-model="model" />
</template>
<div class="gl-mt-6">
<gl-button
type="submit"
variant="confirm"
class="js-no-auto-disable"
:loading="saving || !runner"
:loading="loading || saving"
>
{{ __('Save changes') }}
</gl-button>
<gl-button :href="runnerUrl">
{{ __('Cancel') }}
</gl-button>
</div>
</gl-form>
</template>

View File

@ -15,6 +15,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
skip_cross_project_access_check :index, :starred
feature_category :projects
urgency :low, [:starred]
def index
respond_to do |format|

View File

@ -26,7 +26,7 @@ class Explore::ProjectsController < Explore::ApplicationController
feature_category :projects
# TODO: Set higher urgency after addressing https://gitlab.com/gitlab-org/gitlab/-/issues/357913
# and https://gitlab.com/gitlab-org/gitlab/-/issues/358945
urgency :low, [:index, :topics, :trending]
urgency :low, [:index, :topics, :trending, :starred, :topic]
def index
show_alert_if_search_is_disabled

View File

@ -3,7 +3,6 @@
class Groups::RunnersController < Groups::ApplicationController
before_action :authorize_read_group_runners!, only: [:index, :show]
before_action :authorize_admin_group_runners!, only: [:edit, :update, :destroy, :pause, :resume]
before_action :runner_list_group_view_vue_ui_enabled, only: [:index]
before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
feature_category :runner
@ -15,10 +14,6 @@ class Groups::RunnersController < Groups::ApplicationController
Gitlab::Tracking.event(self.class.name, 'index', user: current_user, namespace: @group)
end
def runner_list_group_view_vue_ui_enabled
render_404 unless Feature.enabled?(:runner_list_group_view_vue_ui, group, default_enabled: :yaml)
end
def show
end
@ -33,32 +28,6 @@ class Groups::RunnersController < Groups::ApplicationController
end
end
def destroy
if can?(current_user, :delete_runner, @runner)
Ci::Runners::UnregisterRunnerService.new(@runner, current_user).execute
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found
else
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found, alert: _('Runner cannot be deleted, please contact your administrator.')
end
end
def resume
if Ci::Runners::UpdateRunnerService.new(@runner).update(active: true)
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), notice: _('Runner was successfully updated.')
else
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), alert: _('Runner was not updated.')
end
end
def pause
if Ci::Runners::UpdateRunnerService.new(@runner).update(active: false)
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), notice: _('Runner was successfully updated.')
else
redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), alert: _('Runner was not updated.')
end
end
private
def runner

View File

@ -3,8 +3,6 @@
module Groups
module Settings
class CiCdController < Groups::ApplicationController
include RunnerSetupScripts
layout 'group_settings'
skip_cross_project_access_check :show
before_action :authorize_admin_group!
@ -14,15 +12,7 @@ module Groups
feature_category :continuous_integration
NUMBER_OF_RUNNERS_PER_PAGE = 4
def show
runners_finder = Ci::RunnersFinder.new(current_user: current_user, params: params.merge({ group: @group }))
# We need all runners for count
@all_group_runners = runners_finder.execute.except(:limit, :offset)
@group_runners = runners_finder.execute.page(params[:page]).per(NUMBER_OF_RUNNERS_PER_PAGE)
@sort = runners_finder.sort_key
end
def update
@ -35,13 +25,6 @@ module Groups
redirect_to group_settings_ci_cd_path
end
def reset_registration_token
::Ci::Runners::ResetRegistrationTokenService.new(@group, current_user).execute
flash[:notice] = _('GroupSettings|New runners registration token has been generated!')
redirect_to group_settings_ci_cd_path
end
def update_auto_devops
if auto_devops_service.execute
flash[:notice] = s_('GroupSettings|Auto DevOps pipeline was updated for the group')
@ -52,10 +35,6 @@ module Groups
redirect_to group_settings_ci_cd_path
end
def runner_setup_scripts
private_runner_setup_scripts
end
private
def define_variables

View File

@ -10,6 +10,7 @@ class Groups::UploadsController < Groups::ApplicationController
before_action :verify_workhorse_api!, only: [:authorize]
feature_category :subgroups
urgency :low, [:show]
private

View File

@ -61,7 +61,7 @@ class GroupsController < Groups::ApplicationController
urgency :high, [:unfoldered_environment_names]
# TODO: Set #show to higher urgency after resolving https://gitlab.com/gitlab-org/gitlab/-/issues/334795
urgency :low, [:merge_requests, :show, :create, :new, :update, :projects, :destroy]
urgency :low, [:merge_requests, :show, :create, :new, :update, :projects, :destroy, :edit]
def index
redirect_to(current_user ? dashboard_groups_path : explore_groups_path)

View File

@ -20,6 +20,7 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
before_action do
push_frontend_feature_flag(:jira_connect_oauth, @user, default_enabled: :yaml)
push_frontend_feature_flag(:jira_connect_oauth_self_managed, @user, default_enabled: :yaml)
end
before_action :allow_rendering_in_iframe, only: :index

View File

@ -4,6 +4,7 @@ class Profiles::AccountsController < Profiles::ApplicationController
include AuthHelper
feature_category :users
urgency :low, [:show]
def show
render(locals: show_view_variables)

View File

@ -2,6 +2,7 @@
class Profiles::KeysController < Profiles::ApplicationController
feature_category :users
urgency :low, [:create]
def index
@keys = current_user.keys.order_id_desc

View File

@ -4,6 +4,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController
before_action :user
feature_category :users
urgency :medium, [:update]
def show
end

View File

@ -15,6 +15,7 @@ class ProfilesController < Profiles::ApplicationController
end
feature_category :users
urgency :low, [:update]
def show
end

View File

@ -57,7 +57,7 @@ class ProjectsController < Projects::ApplicationController
feature_category :portfolio_management, [:planning_hierarchy]
# TODO: Set high urgency for #show https://gitlab.com/gitlab-org/gitlab/-/issues/334444
urgency :low, [:refs, :show, :toggle_star, :transfer, :archive, :destroy]
urgency :low, [:refs, :show, :toggle_star, :transfer, :archive, :destroy, :update]
urgency :high, [:unfoldered_environment_names]
def index

View File

@ -35,6 +35,7 @@ class UsersController < ApplicationController
# TODO: Set higher urgency after resolving https://gitlab.com/gitlab-org/gitlab/-/issues/357914
urgency :low, [:show, :calendar_activities]
urgency :high, [:exists]
def show
respond_to do |format|

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
class BuildIosAppGuideEmailExperiment < ApplicationExperiment
control { false }
candidate { true }
end

View File

@ -374,8 +374,6 @@ module SearchHelper
autocomplete: 'off'
}
opts[:data]['runner-tags-endpoint'] = tag_list_admin_runners_path
if @project.present?
opts[:data]['project-id'] = @project.id
opts[:data]['labels-endpoint'] = project_labels_path(@project)

View File

@ -21,6 +21,12 @@ module Emails
mail_to(to: email, subject: @message.subject_line)
end
def build_ios_app_guide_email(recipient_email)
@message = ::Gitlab::Email::Message::BuildIosAppGuide.new
mail_to(to: recipient_email, subject: @message.subject_line)
end
private
def mail_to(to:, subject:)

View File

@ -4,15 +4,28 @@ module Users
class InProductMarketingEmail < ApplicationRecord
include BulkInsertSafe
BUILD_IOS_APP_GUIDE = 'build_ios_app_guide'
CAMPAIGNS = [BUILD_IOS_APP_GUIDE].freeze
belongs_to :user
validates :user, presence: true
validates :track, presence: true
validates :series, presence: true
validates :track, :series, presence: true, if: -> { campaign.blank? }
validates :campaign, presence: true, if: -> { track.blank? && series.blank? }
validates :campaign, inclusion: { in: CAMPAIGNS }, allow_nil: true
validates :user_id, uniqueness: {
scope: [:track, :series],
message: 'has already been sent'
}
message: 'track series email has already been sent'
}, if: -> { track.present? }
validates :user_id, uniqueness: {
scope: :campaign,
message: 'campaign email has already been sent'
}, if: -> { campaign.present? }
validate :campaign_or_track_series
enum track: {
create: 0,
@ -31,29 +44,61 @@ module Users
INACTIVE_TRACK_NAMES = %w(invite_team).freeze
ACTIVE_TRACKS = tracks.except(*INACTIVE_TRACK_NAMES)
scope :without_track_and_series, -> (track, series) do
users = User.arel_table
product_emails = arel_table
join_condition = users[:id].eq(product_emails[:user_id])
.and(product_emails[:track]).eq(ACTIVE_TRACKS[track])
.and(product_emails[:series]).eq(series)
arel_join = users.join(product_emails, Arel::Nodes::OuterJoin).on(join_condition)
joins(arel_join.join_sources)
.where(in_product_marketing_emails: { id: nil })
.select(Arel.sql("DISTINCT ON(#{users.table_name}.id) #{users.table_name}.*"))
end
scope :for_user_with_track_and_series, -> (user, track, series) do
where(user: user, track: track, series: series)
end
scope :without_track_and_series, -> (track, series) do
join_condition = for_user.and(for_track_and_series(track, series))
users_without_records(join_condition)
end
scope :without_campaign, -> (campaign) do
join_condition = for_user.and(for_campaign(campaign))
users_without_records(join_condition)
end
def self.users_table
User.arel_table
end
def self.distinct_users_sql
name = users_table.table_name
Arel.sql("DISTINCT ON(#{name}.id) #{name}.*")
end
def self.users_without_records(condition)
arel_join = users_table.join(arel_table, Arel::Nodes::OuterJoin).on(condition)
joins(arel_join.join_sources)
.where(in_product_marketing_emails: { id: nil })
.select(distinct_users_sql)
end
def self.for_user
arel_table[:user_id].eq(users_table[:id])
end
def self.for_campaign(campaign)
arel_table[:campaign].eq(campaign)
end
def self.for_track_and_series(track, series)
arel_table[:track].eq(ACTIVE_TRACKS[track])
.and(arel_table[:series]).eq(series)
end
def self.save_cta_click(user, track, series)
email = for_user_with_track_and_series(user, track, series).take
email.update(cta_clicked_at: Time.zone.now) if email && email.cta_clicked_at.blank?
end
private
def campaign_or_track_series
if campaign.present? && (track.present? || series.present?)
errors.add(:campaign, 'should be a campaign or a track and series but not both')
end
end
end
end

View File

@ -61,7 +61,7 @@ module Namespaces
def initialize(track, interval)
@track = track
@interval = interval
@sent_email_records = InProductMarketingEmailRecords.new
@sent_email_records = ::Users::InProductMarketingEmailRecords.new
end
def execute
@ -86,7 +86,7 @@ module Namespaces
users_for_group(group).each do |user|
if can_perform_action?(user, group)
send_email(user, group)
sent_email_records.add(user, track, series)
sent_email_records.add(user, track: track, series: series)
end
end

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
module Projects
class InProductMarketingCampaignEmailsService
include Gitlab::Experiment::Dsl
def initialize(project, campaign)
@project = project
@campaign = campaign
@sent_email_records = ::Users::InProductMarketingEmailRecords.new
end
def execute
send_emails
end
private
attr_reader :project, :campaign, :sent_email_records
def send_emails
project_users.each do |user|
send_email(user)
end
sent_email_records.save!
end
# rubocop: disable CodeReuse/ActiveRecord
def project_users
@project_users ||= project.users
.where(email_opted_in: true)
.merge(Users::InProductMarketingEmail.without_campaign(campaign))
end
# rubocop: enable CodeReuse/ActiveRecord
def project_users_max_access_levels
ids = project_users.map(&:id)
@project_users_max_access_levels ||= project.team.max_member_access_for_user_ids(ids)
end
def send_email(user)
return unless user.can?(:receive_notifications)
return unless target_user?(user)
Notify.build_ios_app_guide_email(user.notification_email_or_default).deliver_later
sent_email_records.add(user, campaign: campaign)
experiment(:build_ios_app_guide_email, project: project).track(:email_sent)
end
def target_user?(user)
max_access_level = project_users_max_access_levels[user.id]
max_access_level >= Gitlab::Access::DEVELOPER
end
end
end

View File

@ -19,11 +19,27 @@ module Projects
def record_target_platforms
return unless target_platforms.present?
setting = ::ProjectSetting.find_or_initialize_by(project: project) # rubocop:disable CodeReuse/ActiveRecord
setting.target_platforms = target_platforms
setting.save
project_setting.target_platforms = target_platforms
project_setting.save
setting.target_platforms
send_build_ios_app_guide_email
project_setting.target_platforms
end
def project_setting
@project_setting ||= ::ProjectSetting.find_or_initialize_by(project: project) # rubocop:disable CodeReuse/ActiveRecord
end
def experiment_candidate?
experiment(:build_ios_app_guide_email, project: project).run
end
def send_build_ios_app_guide_email
return unless experiment_candidate?
campaign = Users::InProductMarketingEmail::BUILD_IOS_APP_GUIDE
Projects::InProductMarketingCampaignEmailsService.new(project, campaign).execute
end
end
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
module Namespaces
module Users
class InProductMarketingEmailRecords
attr_reader :records
@ -13,9 +13,10 @@ module Namespaces
@records = []
end
def add(user, track, series)
def add(user, campaign: nil, track: nil, series: nil)
@records << Users::InProductMarketingEmail.new(
user: user,
campaign: campaign,
track: track,
series: series,
created_at: Time.zone.now,

View File

@ -13,7 +13,7 @@
.form-group
- can_be_configured = @application_setting.usage_ping_can_be_configured?
- service_ping_link_start = link_start % { url: help_page_path('development/service_ping/index') }
- deactivating_service_ping_link_start = link_start % { url: help_page_path('development/service_ping/index', anchor: 'disable-service-ping-using-the-configuration-file') }
- deactivating_service_ping_link_start = link_start % { url: help_page_path('user/admin_area/settings/usage_statistics', anchor: 'disable-usage-statistics-with-the-configuration-file') }
- usage_ping_help_text = s_('AdminSettings|To help improve GitLab and its user experience, GitLab periodically collects usage information. %{link_start}What information is shared with GitLab Inc.?%{link_end}').html_safe % { link_start: service_ping_link_start, link_end: link_end }
- disabled_help_text = s_('AdminSettings|Service ping is disabled in your configuration file, and cannot be enabled through this form. For more information, see the documentation on %{link_start}deactivating service ping%{link_end}.').html_safe % { link_start: deactivating_service_ping_link_start, link_end: link_end }
= f.gitlab_ui_checkbox_component :usage_ping_enabled, s_('AdminSettings|Enable Service Ping'),
@ -28,7 +28,7 @@
.form-group
- usage_ping_enabled = @application_setting.usage_ping_enabled?
- label = s_('AdminSettings|Enable Registration Features')
- label_link = link_to sprite_icon('question-o'), help_page_path('development/service_ping/index', anchor: 'registration-features-program')
- label_link = link_to sprite_icon('question-o'), help_page_path('user/admin_area/settings/usage_statistics', anchor: 'registration-features-program')
- help_text = usage_ping_enabled ? s_('AdminSettings|You can enable Registration Features because Service Ping is enabled. To continue using Registration Features in the future, you will also need to register with GitLab via a new cloud licensing service.') : s_('AdminSettings|To enable Registration Features, first enable Service Ping.')
= f.gitlab_ui_checkbox_component :usage_ping_features_enabled?, '%{label} %{label_link}'.html_safe % { label: label, label_link: label_link },
help_text: '<span id="service_ping_features_helper_text">%{help_text}</span>'.html_safe % { help_text: help_text },

View File

@ -8,10 +8,10 @@
- breadcrumb_title runner_name
- page_title runner_name
#js-admin-runner-edit{ data: {runner_id: @runner.id} }
#js-admin-runner-edit{ data: {runner_id: @runner.id, runner_url: admin_runner_path(@runner) } }
.gl-overflow-auto
%h4= _('Restrict projects for this runner')
%h4.gl-font-lg.gl-my-5= _('Restrict projects for this runner')
- if @runner.runner_projects.any?
%table.table{ data: { testid: 'assigned-projects' } }
@ -32,7 +32,7 @@
%table.table{ data: { testid: 'unassigned-projects' } }
%thead
%tr
%th= _('Project')
%th= s_('Runners|Select projects to assign to this runner')
%th
%tr

View File

@ -1,29 +0,0 @@
- link = link_to _('Runner API'), help_page_path('api/runners.md')
%h4
= _('Group runners')
-# Proper policies should be implemented per
-# https://gitlab.com/gitlab-org/gitlab-foss/issues/45894
.bs-callout.help-callout
%p
= _('These runners are shared across projects in this group.')
= _('Group runners can be managed with the %{link}.').html_safe % { link: link }
- if can?(current_user, :register_group_runners, @group)
- if params[:ci_runner_templates]
%hr
= render partial: 'ci/runner/setup_runner_in_aws',
locals: { registration_token: @group.runners_token }
%hr
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: @group.runners_token,
type: 'group',
reset_token_url: reset_registration_token_group_settings_ci_cd_path,
project_path: '',
group_path: @group.full_path }
%br
- else
= _('Please contact an admin to register runners.')
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'prevent-users-from-registering-runners'), target: '_blank', rel: 'noopener noreferrer'

View File

@ -1,80 +0,0 @@
.gl-responsive-table-row{ id: dom_id(runner) }
.table-section.section-10.section-wrap
.table-mobile-header{ role: 'rowheader' }= _('Type')
.table-mobile-content
- if runner.group_type?
= gl_badge_tag s_('Runners|group'), variant: :success, size: :sm
- else
= gl_badge_tag s_('Runners|specific'), variant: :info, size: :sm
- if runner.locked?
= gl_badge_tag s_('Runners|locked'), variant: :warning, size: :sm
- unless runner.active?
= gl_badge_tag s_('Runners|paused'), { variant: :danger, size: :sm }, { title: s_('Runners|Not accepting jobs'), data: { toggle: 'tooltip', container: 'body' } }
.table-section.section-30
.table-mobile-header{ role: 'rowheader' }= s_('Runners|Runner')
.table-mobile-content
= link_to("##{runner.id} (#{runner.short_sha})", group_runner_path(@group, runner))
.gl-text-truncate
%span{ title: runner.description, data: { toggle: 'tooltip', container: 'body' } }
= runner.description
.table-section.section-10
.table-mobile-header{ role: 'rowheader' }= _('Version')
.table-mobile-content.str-truncated.has-tooltip{ title: runner.version }
= runner.version
.table-section.section-10
.table-mobile-header{ role: 'rowheader' }= _('IP Address')
.table-mobile-content.str-truncated.has-tooltip{ title: runner.ip_address }
= runner.ip_address
.table-section.section-5
.table-mobile-header{ role: 'rowheader' }= _('Projects')
.table-mobile-content
- if runner.group_type?
\-
- else
= runner.runner_projects.count(:all)
.table-section.section-5
.table-mobile-header{ role: 'rowheader' }= _('Jobs')
.table-mobile-content
= limited_counter_with_delimiter(runner.builds)
.table-section.section-10.section-wrap
.table-mobile-header{ role: 'rowheader' }= _('Tags')
.table-mobile-content
- runner.tags.map(&:name).sort.each do |tag|
= gl_badge_tag tag, { variant: :info }, { class: 'str-truncated has-tooltip', title: tag }
.table-section.section-10
.table-mobile-header{ role: 'rowheader' }= _('Last contact')
.table-mobile-content
- contacted_at = runner_contacted_at(runner)
- if contacted_at
= time_ago_with_tooltip contacted_at
- else
= _('Never')
.table-section.table-button-footer.section-10
.btn-group.table-action-buttons
.btn-group
= link_to edit_group_runner_path(@group, runner), class: 'gl-button btn btn-default btn-icon has-tooltip', title: _('Edit'), ref: 'tooltip', aria: { label: _('Edit') }, data: { placement: 'top', container: 'body'} do
= sprite_icon('pencil', css_class: 'gl-icon')
.btn-group
- if runner.active?
= link_to pause_group_runner_path(@group, runner), method: :post, class: 'gl-button btn btn-default btn-icon', title: s_('Runners|Pause from accepting jobs'), ref: 'tooltip', aria: { label: _('Pause') }, data: { toggle: 'tooltip', container: 'body', confirm: _('Are you sure?') } do
= sprite_icon('pause', css_class: 'gl-icon')
- else
= link_to resume_group_runner_path(@group, runner), method: :post, class: 'gl-button btn btn-default btn-icon', title: s_('Runners|Resume accepting jobs'), ref: 'tooltip', aria: { label: _('Resume') }, data: { toggle: 'tooltip', container: 'body'} do
= sprite_icon('play', css_class: 'gl-icon')
- if runner.belongs_to_more_than_one_project?
- delete_runner_tooltip = _('Multi-project Runners cannot be removed')
.btn-group.has-tooltip{ data: { container: 'body', placement: 'top' }, title: delete_runner_tooltip }
.gl-button.btn.btn-danger.btn-icon{ 'aria-label' => delete_runner_tooltip, disabled: 'disabled' }
= sprite_icon('close', css_class: 'gl-icon')
- else
.btn-group
= link_to group_runner_path(@group, runner), method: :delete, class: 'gl-button btn btn-danger btn-icon has-tooltip', title: _('Remove'), ref: 'tooltip', aria: { label: _('Remove') }, data: { placement: 'top', container: 'body', confirm: _('Are you sure?'), confirm_btn_variant: "danger" } do
= sprite_icon('close', css_class: 'gl-icon')

View File

@ -1,119 +1,14 @@
- if Feature.enabled?(:runner_list_group_view_vue_ui, @group, default_enabled: :yaml)
.gl-mb-6
#update-shared-runners-form{ data: group_shared_runners_settings_data(@group) }
.gl-card.gl-px-8.gl-py-6.gl-line-height-20
.gl-card-body.gl-display-flex{ :class => "gl-p-0!" }
.gl-banner-illustration
= image_tag('illustrations/rocket-launch-md.svg', alt: s_('Runners|Rocket launch illustration'))
.gl-banner-content
%h1.gl-banner-title
= s_('Runners|New group runners view')
%p
= s_('Runners|The new view gives you more space and better visibility into your fleet of runners.')
%a.btn.btn-confirm.btn-md.gl-button{ :href => group_runners_path(@group) }
%span.gl-button-text
= s_('Runners|Take me there!')
- else
= render 'shared/runners/runner_description'
%hr
.row
.col-sm-6
= render 'groups/runners/group_runners'
.col-sm-6
= render 'groups/runners/shared_runners'
%h4.underlined-title
= _('Available runners: %{runners}').html_safe % { runners: limited_counter_with_delimiter(@all_group_runners) }
-# haml-lint:disable NoPlainNodes
.row
.col-sm-9
= form_tag group_settings_ci_cd_path, id: 'runners-search', method: :get, class: 'filter-form js-filter-form' do
.filtered-search-wrapper.d-flex
.filtered-search-box
= dropdown_tag(_('Recent searches'),
options: { wrapper_class: 'filtered-search-history-dropdown-wrapper',
toggle_class: 'gl-button btn btn-default filtered-search-history-dropdown-toggle-button',
dropdown_class: 'filtered-search-history-dropdown',
content_class: 'filtered-search-history-dropdown-content' }) do
.js-filtered-search-history-dropdown{ data: { full_path: group_settings_ci_cd_path } }
.filtered-search-box-input-container.droplab-dropdown
.scroll-container
%ul.tokens-container.list-unstyled
%li.input-token
%input.form-control.filtered-search{ search_filter_input_options('runners') }
#js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item{ data: {hint: "#{'{{hint}}'}", tag: "#{'{{tag}}'}", action: "#{'{{hint === \'search\' ? \'submit\' : \'\' }}'}" } }
= button_tag class: 'gl-button btn btn-link' do
-# Encapsulate static class name `{{icon}}` inside #{} to bypass
-# haml lint's ClassAttributeWithStaticValue
%svg
%use{ 'xlink:href': "#{'{{icon}}'}" }
%span.js-filter-hint
{{formattedKey}}
#js-dropdown-operator.filtered-search-input-dropdown-menu.dropdown-menu
%ul.filter-dropdown{ data: { dropdown: true, dynamic: true } }
%li.filter-dropdown-item{ data: { value: "{{ title }}" } }
= button_tag class: 'gl-button btn btn-link' do
{{ title }}
%span.btn-helptext
{{ help }}
#js-dropdown-admin-runner-status.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
- Ci::Runner::AVAILABLE_STATUSES.each do |status|
%li.filter-dropdown-item{ data: { value: status } }
= button_tag class: 'gl-button btn btn-link' do
= status.titleize
#js-dropdown-admin-runner-type.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
- Ci::Runner::AVAILABLE_TYPES.each do |runner_type|
- next if runner_type == 'instance_type'
%li.filter-dropdown-item{ data: { value: runner_type } }
= button_tag class: 'gl-button btn btn-link' do
= runner_type.titleize
#js-dropdown-runner-tag.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
= button_tag class: 'gl-button btn btn-link' do
= _('No Tag')
%li.divider.droplab-item-ignore
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
= button_tag class: 'gl-button btn btn-link js-data-value' do
%span.dropdown-light-content
{{name}}
= button_tag class: 'clear-search hidden' do
= sprite_icon('close', size: 16, css_class: 'clear-search-icon')
.filter-dropdown-container
= render 'groups/runners/sort_dropdown'
.col-sm-3.text-right-lg
= _('Runners currently online: %{active_runners_count}') % { active_runners_count: limited_counter_with_delimiter(@all_group_runners.online) }
- if @group_runners.any?
.content-list{ data: { testid: 'runners-table' } }
.table-holder
.gl-responsive-table-row.table-row-header{ role: 'row' }
.table-section.section-10{ role: 'rowheader' }= _('Type/State')
.table-section.section-30{ role: 'rowheader' }= s_('Runners|Runner')
.table-section.section-10{ role: 'rowheader' }= _('Version')
.table-section.section-10{ role: 'rowheader' }= _('IP Address')
.table-section.section-5{ role: 'rowheader' }= _('Projects')
.table-section.section-5{ role: 'rowheader' }= _('Jobs')
.table-section.section-10{ role: 'rowheader' }= _('Tags')
.table-section.section-10{ role: 'rowheader' }= _('Last contact')
.table-section.section-10{ role: 'rowheader' }
- @group_runners.each do |runner|
- runner = runner.present(current_user: current_user)
= render 'groups/runners/runner', runner: runner
= paginate @group_runners, theme: 'gitlab', :params => { :anchor => 'runners-settings' }
- else
.nothing-here-block= _('No runners found')
.gl-mb-6
#update-shared-runners-form{ data: group_shared_runners_settings_data(@group) }
.gl-card.gl-px-8.gl-py-6.gl-line-height-20
.gl-card-body.gl-display-flex{ :class => "gl-p-0!" }
.gl-banner-illustration
= image_tag('illustrations/rocket-launch-md.svg', alt: s_('Runners|Rocket launch illustration'))
.gl-banner-content
%h1.gl-banner-title
= s_('Runners|New group runners view')
%p
= s_('Runners|The new view gives you more space and better visibility into your fleet of runners.')
%a.btn.btn-confirm.btn-md.gl-button{ :href => group_runners_path(@group) }
%span.gl-button-text
= s_('Runners|Take me there!')

View File

@ -1,3 +0,0 @@
= render 'shared/runners/shared_runners_description'
#update-shared-runners-form{ data: group_shared_runners_settings_data(@group) }

View File

@ -1,3 +0,0 @@
- runners_sort_options = runners_sort_options_hash.map { |value, text| { value: value, text: text, href: page_filter_path(sort: value) } }
= gl_redirect_listbox_tag runners_sort_options, @sort, class: 'gl-ml-3', data: { display: 'static' }

View File

@ -1,11 +1,7 @@
- breadcrumb_title _('Edit')
- page_title _('Edit'), "##{@runner.id} (#{@runner.short_sha})"
- if Feature.enabled?(:runner_list_group_view_vue_ui, @group, default_enabled: :yaml)
- add_to_breadcrumbs _('Runners'), group_runners_path(@group)
- else
- add_to_breadcrumbs _('CI/CD Settings'), group_settings_ci_cd_path(@group)
- add_to_breadcrumbs _('Runners'), group_runners_path(@group)
- add_to_breadcrumbs "#{@runner.short_sha}", group_runner_path(@group, @runner)

View File

@ -1,6 +1,3 @@
- if Feature.enabled?(:runner_list_group_view_vue_ui, @group, default_enabled: :yaml)
- add_to_breadcrumbs _('Runners'), group_runners_path(@group)
- else
- add_to_breadcrumbs _('CI/CD Settings'), group_settings_ci_cd_path(@group)
- add_to_breadcrumbs _('Runners'), group_runners_path(@group)
= render 'shared/runners/runner_details', runner: @runner

View File

@ -0,0 +1,13 @@
%tr
%td{ bgcolor: "#ffffff", height: "auto", style: "max-width: 600px; width: 100%; text-align: center; height: 200px; padding: 25px 15px; mso-line-height-rule: exactly; min-height: 40px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;", valign: "middle", width: "100%" }
= inline_image_link(@message.logo_path, { width: '150', style: 'width: 150px;' })
%h1{ style: "font-size: 40px; line-height: 46x; color: #000000; padding: 20px 0 0 0; font-weight: normal;" }
= @message.title
%tr
%td{ style: "padding: 10px 20px 30px 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif; color:#000000; font-size: 18px; line-height: 24px;" }
%p{ style: "margin: 0 0 20px 0;" }
= @message.body_line1.html_safe
%tr
%td{ align: "center", style: "padding: 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;" }
.cta_link.cta_link_primary= @message.cta_link
.cta_link.cta_link_secondary= @message.cta2_link

View File

@ -0,0 +1,13 @@
<%= @message.title %>
<%= @message.body_line1 %>
<%= @message.cta_link %>
<%= @message.cta2_link %>
<%= @message.footer_links %>
<%= @message.address %>
<%= @message.unsubscribe %>

View File

@ -2,7 +2,7 @@
.nav-block
.tree-ref-container
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'blob', path: @path
= render 'shared/ref_switcher', destination: 'blob'
%ul.breadcrumb.repo-breadcrumb
%li.breadcrumb-item

View File

@ -46,7 +46,7 @@
.col-md-6
.tree-ref-container
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'graphs_commits'
= render 'shared/ref_switcher', destination: 'graphs_commits', path: @path
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs

View File

@ -28,10 +28,7 @@
= _('This group does not have any group runners yet.')
- if can?(current_user, :admin_group_runners, @project.group)
- if Feature.enabled?(:runner_list_group_view_vue_ui, @group, default_enabled: :yaml)
- register_runners_path = group_runners_path(@project.group)
- else
- register_runners_path = group_settings_ci_cd_path(@project.group)
- register_runners_path = group_runners_path(@project.group)
- group_link = link_to _("group's CI/CD settings."), register_runners_path
= _('Group owners can register group runners in the %{link}').html_safe % { link: group_link }
- else

View File

@ -1,6 +1,6 @@
.tree-ref-container.gl-display-flex.mb-2.mb-md-0
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'tree', path: @path, show_create: true
= render 'shared/ref_switcher', destination: 'tree', show_create: true
#js-repo-breadcrumb{ data: breadcrumb_data_attributes }

View File

@ -1,5 +1,5 @@
- feature_title = local_assigns.fetch(:feature_title, s_('RegistrationFeatures|use this feature'))
- registration_features_docs_path = help_page_path('development/service_ping/index.md', anchor: 'registration-features-program')
- registration_features_docs_path = help_page_path('user/admin_area/settings/usage_statistics.md', anchor: 'registration-features-program')
- registration_features_link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: registration_features_docs_path }
%div

View File

@ -1,8 +1,8 @@
---
name: runner_list_group_view_vue_ui
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66376
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/336405
milestone: '14.2'
name: jira_connect_oauth_self_managed
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85483
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359940
milestone: '15.0'
type: development
group: group::runner
default_enabled: true
group: group::integrations
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: build_ios_app_guide_email
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83817
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/357803
milestone: '15.0'
type: experiment
group: group::activation
default_enabled: false

View File

@ -3,7 +3,7 @@ table_name: atlassian_identities
classes:
- Atlassian::Identity
feature_categories:
- importers
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9af97ee69a36de1dc4e73f4030d6316d3f0a82c5
- authentication_and_authorization
description: Stores Atlassian credentials that are used to integrate with Atlassian API
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40176
milestone: '13.4'

View File

@ -4,6 +4,6 @@ classes:
- BulkImports::Configuration
feature_categories:
- importers
description: TODO
description: Used to store the configuration details of a bulk import of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42978
milestone: '13.5'

View File

@ -4,6 +4,6 @@ classes:
- BulkImports::Entity
feature_categories:
- importers
description: TODO
description: Used to store and track the status of the migration of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42978
milestone: '13.5'

View File

@ -4,6 +4,6 @@ classes:
- BulkImports::ExportUpload
feature_categories:
- importers
description: TODO
description: Used to store information of the exported files containing the data of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59976
milestone: '13.12'

View File

@ -4,6 +4,6 @@ classes:
- BulkImports::Export
feature_categories:
- importers
description: TODO
description: Used to track the generation status of export files for groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59976
milestone: '13.12'

View File

@ -4,6 +4,6 @@ classes:
- BulkImports::Failure
feature_categories:
- importers
description: TODO
description: Used to store failures that occur during the migration of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47526
milestone: '13.7'

View File

@ -4,6 +4,6 @@ classes:
- BulkImports::Tracker
feature_categories:
- importers
description: TODO
description: Used to store and track the status of each pipeline associated with the migration of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47009
milestone: '13.6'

View File

@ -4,6 +4,6 @@ classes:
- BulkImport
feature_categories:
- importers
description: TODO
description: Used to store and track the status of a bulk import request of groups or projects
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42978
milestone: '13.5'

View File

@ -4,6 +4,6 @@ classes:
- Issues::CsvImport
feature_categories:
- importers
description: TODO
description: Used to report the unique user usage of the CSV Issue Import feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44742
milestone: '13.6'

View File

@ -4,6 +4,6 @@ classes:
- GroupImportState
feature_categories:
- importers
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/9af97ee69a36de1dc4e73f4030d6316d3f0a82c5
description: Used to store and track the group import status when using the Import/Export feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29588
milestone: '13.0'

View File

@ -4,6 +4,6 @@ classes:
- ImportExportUpload
feature_categories:
- importers
description: TODO
description: Used to store the location of the imported or exported archives files of groups or projects when using the feature Import/Export
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/a2bf1641546a1d3eeb3e9f44734854f655c0adef
milestone: '11.1'

View File

@ -4,6 +4,6 @@ classes:
- ImportFailure
feature_categories:
- importers
description: TODO
description: Used to store group or project import failures that occur when using the Import/Export feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20727
milestone: '12.6'

View File

@ -4,6 +4,6 @@ classes:
- ProjectExportJob
feature_categories:
- importers
description: TODO
description: Used to track and control project export status
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23664
milestone: '12.9'

View File

@ -4,6 +4,6 @@ classes:
- ProjectImportData
feature_categories:
- importers
description: TODO
description: Used to store credentials and configuration of external projects when using the Import/Export feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/7d98c8842d6bc9b14fb410f028db7ab651961b42
milestone: '7.10'

View File

@ -4,6 +4,6 @@ classes:
- ProjectImportState
feature_categories:
- importers
description: TODO
description: Used to store and track the project import status when using the Import/Export feature
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/0ca479d1ce0eadfcdc0e29d0e18136f5790d5b2f
milestone: '9.3'

View File

@ -3,7 +3,7 @@ table_name: system_note_metadata
classes:
- SystemNoteMetadata
feature_categories:
- importers
description: TODO
- team_planning
description: Used to store notes metadata
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/1c3c7fb25d972fc19d5b4bb371cb21094d81e478
milestone: '9.1'

View File

@ -0,0 +1,35 @@
# frozen_string_literal: true
class AddCampaignToInProductMarketingEmail < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
TARGET_TABLE = :in_product_marketing_emails
UNIQUE_INDEX_NAME = :index_in_product_marketing_emails_on_user_campaign
CONSTRAINT_NAME = :in_product_marketing_emails_track_and_series_or_campaign
TRACK_AND_SERIES_NOT_NULL_CONSTRAINT = 'track IS NOT NULL AND series IS NOT NULL AND campaign IS NULL'
CAMPAIGN_NOT_NULL_CONSTRAINT = 'track IS NULL AND series IS NULL AND campaign IS NOT NULL'
def up
change_column_null TARGET_TABLE, :track, true
change_column_null TARGET_TABLE, :series, true
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20220420034519_add_text_limit_to_in_product_marketing_email_campaign.rb
add_column :in_product_marketing_emails, :campaign, :text, if_not_exists: true
# rubocop:enable Migration/AddLimitToTextColumns
add_concurrent_index TARGET_TABLE, [:user_id, :campaign], unique: true, name: UNIQUE_INDEX_NAME
add_check_constraint TARGET_TABLE,
"(#{TRACK_AND_SERIES_NOT_NULL_CONSTRAINT}) OR (#{CAMPAIGN_NOT_NULL_CONSTRAINT})",
CONSTRAINT_NAME
end
def down
remove_check_constraint TARGET_TABLE, CONSTRAINT_NAME
remove_concurrent_index TARGET_TABLE, [:user_id, :campaign], name: UNIQUE_INDEX_NAME
remove_column :in_product_marketing_emails, :campaign, if_exists: true
# Records that previously had a value for campaign column will have NULL
# values for track and series columns so we can't reverse
# change_column_null.
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddTextLimitToInProductMarketingEmailCampaign < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
def up
add_text_limit :in_product_marketing_emails, :campaign, 255
end
def down
remove_text_limit :in_product_marketing_emails, :campaign
end
end

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class CleanupOrphansApprovalProjectRules < Gitlab::Database::Migration[1.0]
class ApprovalProjectRule < ActiveRecord::Base
self.table_name = 'approval_project_rules'
end
def up
return unless Gitlab.ee?
ApprovalProjectRule.reset_column_information
logger = ::Gitlab::BackgroundMigration::Logger.build
records_ids = []
# Related enum: report_type: { vulnerability: 1, license_scanning: 2, code_coverage: 3, scan_finding: 4 }
ApprovalProjectRule.where(report_type: 4)
.joins("LEFT JOIN security_orchestration_policy_configurations
ON approval_project_rules.project_id = security_orchestration_policy_configurations.project_id")
.where(security_orchestration_policy_configurations: { project_id: nil }).each do |record|
records_ids << record.id
logger.info(
message: "CleanupOrphansApprovalProjectRules with record id: #{record.id}",
class: ApprovalProjectRule.name,
attributes: record.attributes
)
end
ApprovalProjectRule.where(id: records_ids).delete_all
end
def down
# no-op
end
end

View File

@ -0,0 +1 @@
fa1651c066191279fe922b311be3e112b87648c52b1af7a81d7b73ebfe2f7177

View File

@ -0,0 +1 @@
0bb23775f65b9997e04dffe701ba609d26dde89796406941fbd27bf810b174ae

View File

@ -0,0 +1 @@
8ce9e197aa590d01755541a9f1c53d6835a9d4ae389e011c5050778d19e80f00

View File

@ -15888,10 +15888,13 @@ CREATE TABLE in_product_marketing_emails (
id bigint NOT NULL,
user_id bigint NOT NULL,
cta_clicked_at timestamp with time zone,
track smallint NOT NULL,
series smallint NOT NULL,
track smallint,
series smallint,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL
updated_at timestamp with time zone NOT NULL,
campaign text,
CONSTRAINT check_9d8b29f74f CHECK ((char_length(campaign) <= 255)),
CONSTRAINT in_product_marketing_emails_track_and_series_or_campaign CHECK ((((track IS NOT NULL) AND (series IS NOT NULL) AND (campaign IS NULL)) OR ((track IS NULL) AND (series IS NULL) AND (campaign IS NOT NULL))))
);
CREATE SEQUENCE in_product_marketing_emails_id_seq
@ -27910,6 +27913,8 @@ CREATE INDEX index_imported_projects_on_import_type_creator_id_created_at ON pro
CREATE INDEX index_imported_projects_on_import_type_id ON projects USING btree (import_type, id) WHERE (import_type IS NOT NULL);
CREATE UNIQUE INDEX index_in_product_marketing_emails_on_user_campaign ON in_product_marketing_emails USING btree (user_id, campaign);
CREATE INDEX index_in_product_marketing_emails_on_user_id ON in_product_marketing_emails USING btree (user_id);
CREATE UNIQUE INDEX index_in_product_marketing_emails_on_user_track_series ON in_product_marketing_emails USING btree (user_id, track, series);

View File

@ -74,7 +74,14 @@ in GitLab 14.1. This feature is still under development, and is not ready for pr
### Configure single database
By default, GDK is configured to run with multiple databases. To configure GDK to use a single database:
By default, GDK is configured to run with multiple databases.
WARNING:
Switching back-and-forth between single and multiple databases in
the same development instance is discouraged. Any data in the `ci`
database will not be accessible in single database mode. For single database, you should use a separate development instance.
To configure GDK to use a single database:
1. On the GDK root directory, run:

View File

@ -22,7 +22,7 @@ and sales teams understand how GitLab is used. The data helps to:
Service Ping information is not anonymous. It's linked to the instance's hostname, but does
not contain project names, usernames, or any other specific data.
Sending a Service Ping payload is optional and you can [disable](#disable-service-ping) it on any
Sending a Service Ping payload is optional and you can [disable](../../user/admin_area/settings/usage_statistics.md#enable-or-disable-usage-statistics) it on any
self-managed instance. When Service Ping is enabled, GitLab gathers data from the other instances
and can show your instance's usage statistics to your users.
@ -38,23 +38,6 @@ We use the following terminology to describe the Service Ping components:
- **MAU**: monthly active users.
- **WAU**: weekly active users.
### Why enable Service Ping?
The main purpose of Service Ping is to build a better GitLab. We collect data about how GitLab is used
to understand feature or stage adoption and usage. This data gives an insight into how GitLab adds
value and helps our team understand the reasons why people use GitLab, and with this knowledge we're able to
make better product decisions.
There are several other benefits to enabling Service Ping:
- As a benefit of having Service Ping active, GitLab lets you analyze the users' activities over time of your GitLab installation.
- As a benefit of having Service Ping active, GitLab provides you with [DevOps Score](../../user/admin_area/analytics/dev_ops_reports.md#devops-score), which gives you an overview of your entire instance's adoption of Concurrent DevOps from planning to monitoring.
- You get better, more proactive support (assuming that our TAMs and support organization used the data to deliver more value).
- You get insight and advice into how to get the most value out of your investment in GitLab. Wouldn't you want to know that a number of features or values are not being adopted in your organization?
- You get a report that illustrates how you compare against other similar organizations (anonymized), with specific advice and recommendations on how to improve your DevOps processes.
- Service Ping is enabled by default. To disable it, see [Disable Service Ping](#disable-service-ping).
- When Service Ping is enabled, you have the option to participate in our [Registration Features Program](#registration-features-program) and receive free paid features.
### Limitations
- Service Ping does not track frontend events things like page views, link clicks, or user sessions.
@ -65,107 +48,6 @@ Because of these limitations we recommend you:
- Instrument your products with Snowplow for more detailed analytics on GitLab.com.
- Use Service Ping to track aggregated backend events on self-managed instances.
### Registration Features Program
> Introduced in GitLab 14.1.
In GitLab versions 14.1 and later, GitLab Free customers with a self-managed instance running
[GitLab EE](../ee_features.md) can receive paid features by registering with GitLab and sending us
activity data through Service Ping. Features introduced here do not remove the feature from its paid
tier. Users can continue to access the features in a paid tier without sharing usage data.
#### Features available in 14.1 and later
1. [Email from GitLab](../../user/admin_area/email_from_gitlab.md).
#### Features available in 14.4 and later
1. [Repository size limit](../../user/admin_area/settings/account_and_limit_settings.md#repository-size-limit).
1. [Restrict group access by IP address](../../user/group/index.md#restrict-group-access-by-ip-address).
NOTE:
Registration is not yet required for participation, but will be added in a future milestone.
#### Enable Registration Features
1. Sign in as a user with administrator access.
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand the **Usage statistics** section.
1. If not enabled, select the **Enable Service Ping** checkbox.
1. Select the **Enable Registration Features** checkbox.
1. Select **Save changes**.
## View the Service Ping payload **(FREE SELF)**
You can view the exact JSON payload sent to GitLab Inc. in the Admin Area. To view the payload:
1. Sign in as a user with administrator access.
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand the **Usage statistics** section.
1. Select **Preview payload**.
For an example payload, see [Example Service Ping payload](#example-service-ping-payload).
## Disable Service Ping **(FREE SELF)**
NOTE:
The method to disable Service Ping in the GitLab configuration file does not work in
GitLab versions 9.3 to 13.12.3. See the [troubleshooting section](#cannot-disable-service-ping-using-the-configuration-file)
on how to disable it.
You can disable Service Ping either using the GitLab UI, or editing the GitLab
configuration file.
### Disable Service Ping using the UI
To disable Service Ping in the GitLab UI:
1. Sign in as a user with administrator access.
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand the **Usage statistics** section.
1. Clear the **Enable Service Ping** checkbox.
1. Select **Save changes**.
### Disable Service Ping using the configuration file
To disable Service Ping and prevent it from being configured in the future through
the Admin Area:
**For installations using the Linux package:**
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['usage_ping_enabled'] = false
```
1. Reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
**For installations from source:**
1. Edit `/home/git/gitlab/config/gitlab.yml`:
```yaml
production: &base
# ...
gitlab:
# ...
usage_ping_enabled: false
```
1. Restart GitLab:
```shell
sudo service gitlab restart
```
## Service Ping request flow
The following example shows a basic request/response flow between a GitLab instance, the Versions Application, the License Application, Salesforce, the GitLab S3 Bucket, the GitLab Snowflake Data Warehouse, and Sisense:
@ -580,28 +462,6 @@ skip_db_write:
ServicePing::SubmitService.new(skip_db_write: true).execute
```
## Manually upload Service Ping payload
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/7388) in GitLab 14.8 with a flag named `admin_application_settings_service_usage_data_center`. Disabled by default.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83265) in GitLab 14.10.
Service Ping payload can be uploaded to GitLab even if your application instance doesn't have access to the internet,
or you don't have Service Ping [cron job](#how-service-ping-works) enabled.
To upload payload manually:
1. Sign in as a user with administrator access.
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Service** usage data.
1. Select **Download payload**.
1. Save the JSON file.
1. Visit [Service usage data center](https://version.gitlab.com/usage_data/new).
1. Select **Choose file** and choose the file from p5.
1. Select **Upload**.
The uploaded file is encrypted and sent using secure [HTTPS protocol](https://en.wikipedia.org/wiki/HTTPS). HTTPS creates a secure
communication channel between web browser and the server, and protects transmitted data against man-in-the-middle attacks.
## Monitoring
Service Ping reporting process state is monitored with [internal SiSense dashboard](https://app.periscopedata.com/app/gitlab/968489/Product-Intelligence---Service-Ping-Health).

View File

@ -131,7 +131,7 @@ which has a related schema in `/config/metrics/objects_schemas/topology_schema.j
We use the following categories to classify a metric:
- `operational`: Required data for operational purposes.
- `optional`: Default value for a metric. Data that is optional to collect. This can be [enabled or disabled](../service_ping/index.md#disable-service-ping) in the Admin Area.
- `optional`: Default value for a metric. Data that is optional to collect. This can be [enabled or disabled](../../user/admin_area/settings/usage_statistics.md#enable-or-disable-usage-statistics) in the Admin Area.
- `subscription`: Data related to licensing.
- `standard`: Standard set of identifiers that are included when collecting data.

View File

@ -1,8 +1,7 @@
---
stage: none
group: unassigned
stage: Growth
group: Product Intelligence
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference
---
# Usage statistics **(FREE SELF)**
@ -15,14 +14,55 @@ All usage statistics are [opt-out](#enable-or-disable-usage-statistics).
## Service Ping
Service Ping is a process that collects and sends a weekly payload to GitLab Inc.
For more information, see the [Service Ping guide](../../../development/service_ping/index.md).
### Instance-level analytics availability
When Service Ping is enabled, GitLab gathers data from other instances and
enables certain [instance-level analytics features](../analytics/index.md)
For more information, see the [Service Ping guide](../../../development/service_ping/index.md). When Service Ping is enabled, GitLab gathers data from other instances and enables certain [instance-level analytics features](../analytics/index.md)
that are dependent on Service Ping.
### Why enable Service Ping?
The main purpose of Service Ping is to build a better GitLab. We collect data about how GitLab is used
to understand feature or stage adoption and usage. This data gives an insight into how GitLab adds
value and helps our team understand the reasons why people use GitLab, and with this knowledge we're able to make better product decisions.
There are several other benefits to enabling Service Ping:
- Analyze the users' activities over time of your GitLab installation.
- A [DevOps Score](../analytics/dev_ops_reports.md#devops-score) to give you an overview of your entire instance's adoption of concurrent DevOps from planning to monitoring.
- More proactive support (assuming that our TAMs and support organization used the data to deliver more value).
- Insight and advice into how to get the most value out of your investment in GitLab.
- Reports that show how you compare against other similar organizations (anonymized), with specific advice and recommendations on how to improve your DevOps processes.
- Participation in our [Registration Features Program](#registration-features-program) to receive free paid features.
## Registration Features Program
> Introduced in GitLab 14.1.
In GitLab versions 14.1 and later, GitLab Free customers with a self-managed instance running
GitLab Enterprise Edition can receive paid features by registering with GitLab and sending us
activity data through Service Ping. Features introduced here do not remove the feature from its paid
tier. Users can continue to access the features in a paid tier without sharing usage data.
### Features available in 14.1 and later
- [Email from GitLab](../email_from_gitlab.md).
#### Features available in 14.4 and later
- [Repository size limit](../settings/account_and_limit_settings.md#repository-size-limit).
- [Restrict group access by IP address](../../group/index.md#restrict-group-access-by-ip-address).
NOTE:
Registration is not yet required for participation, but may be added in a future milestone.
#### Enable Registration Features
1. Sign in as a user with administrator access.
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand the **Usage statistics** section.
1. If not enabled, select the **Enable Service Ping** checkbox.
1. Select the **Enable Registration Features** checkbox.
1. Select **Save changes**.
## Version check
If enabled, version check informs you if a new version is available and the
@ -79,6 +119,81 @@ To enable or disable Service Ping and version check:
1. Select or clear the **Enable version check** and **Enable Service Ping** checkboxes.
1. Select **Save changes**.
## Disable usage statistics with the configuration file
NOTE:
The method to disable Service Ping in the GitLab configuration file does not work in
GitLab versions 9.3 to 13.12.3. For more information about how to disable it, see [troubleshooting](../../../development/service_ping/index.md#cannot-disable-service-ping-using-the-configuration-file).
To disable Service Ping and prevent it from being configured in the future through
the Admin Area:
**For installations using the Linux package:**
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['usage_ping_enabled'] = false
```
1. Reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
**For installations from source:**
1. Edit `/home/git/gitlab/config/gitlab.yml`:
```yaml
production: &base
# ...
gitlab:
# ...
usage_ping_enabled: false
```
1. Restart GitLab:
```shell
sudo service gitlab restart
```
## View the Service Ping payload
You can view the exact JSON payload sent to GitLab Inc. in the Admin Area. To view the payload:
1. Sign in as a user with administrator access.
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand the **Usage statistics** section.
1. Select **Preview payload**.
For an example payload, see [Example Service Ping payload](../../../development/service_ping/index.md#example-service-ping-payload).
## Manually upload Service Ping payload
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/7388) in GitLab 14.8 with a flag named `admin_application_settings_service_usage_data_center`. Disabled by default.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83265) in GitLab 14.10.
You can upload the Service Ping payload to GitLab even if your instance doesn't have internet access,
or if the Service Ping [cron job](../../../development/service_ping/index.md#how-service-ping-works) is not enabled.
To upload the payload manually:
1. Sign in as a user with administrator access.
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Service** usage data.
1. Select **Download payload**.
1. Save the JSON file.
1. Visit [Service usage data center](https://version.gitlab.com/usage_data/new).
1. Select **Choose file** and choose the file from p5.
1. Select **Upload**.
The uploaded file is encrypted and sent using secure HTTPS protocol. HTTPS creates a secure
communication channel between web browser and the server, and protects transmitted data against man-in-the-middle attacks.
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues

View File

@ -32,7 +32,7 @@ module API
params do
use :pagination
end
get ":id/badges" do
get ":id/badges", urgency: :default do
source = find_source(source_type, params[:id])
badges = source.badges

View File

@ -196,7 +196,7 @@ module API
use :optional_params
end
post feature_category: :subgroups do
post feature_category: :subgroups, urgency: :low do
parent_group = find_group!(params[:parent_id]) if params[:parent_id].present?
if parent_group
authorize! :create_subgroup, parent_group
@ -229,7 +229,7 @@ module API
use :optional_update_params
use :optional_update_params_ee
end
put ':id', feature_category: :subgroups do
put ':id', feature_category: :subgroups, urgency: :low do
group = find_group!(params[:id])
group.preload_shared_group_links

View File

@ -30,7 +30,7 @@ module API
use :pagination
use :optional_list_params_ee
end
get feature_category: :subgroups do
get feature_category: :subgroups, urgency: :low do
owned_only = params[:owned_only] == true
namespaces = current_user.admin ? Namespace.all : current_user.namespaces(owned_only: owned_only)

View File

@ -267,7 +267,7 @@ module API
use :optional_create_project_params
use :create_params
end
post do
post urgency: :low do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/21139')
attrs = declared_params(include_missing: false)
attrs = translate_params_for_compatibility(attrs)

View File

@ -1080,7 +1080,7 @@ module API
params do
use :pagination
end
get "emails", feature_category: :users do
get "emails", feature_category: :users, urgency: :high do
present paginate(current_user.emails), with: Entities::Email
end

View File

@ -88,11 +88,7 @@ class Feature
feature = get(key)
# If we're not default enabling the flag or the feature has been set, always evaluate.
# `persisted?` can potentially generate DB queries and also checks for inclusion
# in an array of feature names (177 at last count), possibly reducing performance by half.
# So we only perform the `persisted` check if `default_enabled: true`
feature_value = !default_enabled || Feature.persisted_name?(feature.name) ? feature.enabled?(thing) : true
feature_value = current_feature_value(feature, thing, default_enabled: default_enabled)
# If we don't filter out this flag here we will enter an infinite loop
log_feature_flag_state(key, feature_value) if log_feature_flag_states?(key)
@ -181,6 +177,16 @@ class Feature
private
# Evaluate if `default enabled: false` or the feature has been persisted.
# `persisted_name?` can potentially generate DB queries and also checks for inclusion
# in an array of feature names (177 at last count), possibly reducing performance by half.
# So we only perform the `persisted` check if `default_enabled: true`
def current_feature_value(feature, thing, default_enabled:)
return true if default_enabled && !Feature.persisted_name?(feature.name)
feature.enabled?(thing)
end
def flipper
if Gitlab::SafeRequestStore.active?
Gitlab::SafeRequestStore[:flipper] ||= build_flipper_instance(memoize: true)

View File

@ -3,34 +3,19 @@
module Gitlab
module BackgroundMigration
# Backfill group_features for an array of groups
class BackfillGroupFeatures < ::Gitlab::BackgroundMigration::BaseJob
include Gitlab::Database::DynamicModelHelpers
def perform(start_id, end_id, batch_table, batch_column, sub_batch_size, pause_ms, batch_size)
pause_ms = 0 if pause_ms < 0
parent_batch_relation = relation_scoped_to_range(batch_table, batch_column, start_id, end_id)
parent_batch_relation.each_batch(column: batch_column, of: sub_batch_size, order_hint: :type) do |sub_batch|
batch_metrics.time_operation(:upsert_group_features) do
upsert_group_features(sub_batch, batch_size)
end
sleep(pause_ms * 0.001)
class BackfillGroupFeatures < ::Gitlab::BackgroundMigration::BatchedMigrationJob
def perform(batch_size)
each_sub_batch(
operation_name: :upsert_group_features,
batching_arguments: { order_hint: :type },
batching_scope: ->(relation) { relation.where(type: 'Group') }
) do |sub_batch|
upsert_group_features(sub_batch, batch_size)
end
end
def batch_metrics
@batch_metrics ||= Gitlab::Database::BackgroundMigration::BatchMetrics.new
end
private
def relation_scoped_to_range(source_table, source_key_column, start_id, stop_id)
define_batchable_model(source_table, connection: connection)
.where(source_key_column => start_id..stop_id)
.where(type: 'Group')
end
def upsert_group_features(relation, batch_size)
connection.execute(
<<~SQL

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Base class for batched background migrations. Subclasses should implement the `#perform`
# method as the entry point for the job's execution, which will be called with the migration
# arguments (if any).
class BatchedMigrationJob
include Gitlab::Database::DynamicModelHelpers
def initialize(start_id:, end_id:, batch_table:, batch_column:, sub_batch_size:, pause_ms:, connection:)
@start_id = start_id
@end_id = end_id
@batch_table = batch_table
@batch_column = batch_column
@sub_batch_size = sub_batch_size
@pause_ms = pause_ms
@connection = connection
end
def perform(*job_arguments)
raise NotImplementedError, "subclasses of #{self.class.name} must implement #{__method__}"
end
def batch_metrics
@batch_metrics ||= Gitlab::Database::BackgroundMigration::BatchMetrics.new
end
private
attr_reader :start_id, :end_id, :batch_table, :batch_column, :sub_batch_size, :pause_ms, :connection
def each_sub_batch(operation_name: :default, batching_arguments: {}, batching_scope: nil)
all_batching_arguments = { column: batch_column, of: sub_batch_size }.merge(batching_arguments)
parent_relation = parent_batch_relation(batching_scope)
parent_relation.each_batch(**all_batching_arguments) do |relation|
batch_metrics.instrument_operation(operation_name) do
yield relation
end
sleep([pause_ms, 0].max * 0.001)
end
end
def parent_batch_relation(batching_scope)
parent_relation = define_batchable_model(batch_table, connection: connection)
.where(batch_column => start_id..end_id)
return parent_relation unless batching_scope
batching_scope.call(parent_relation)
end
end
end
end

View File

@ -13,50 +13,25 @@ module Gitlab
# - We skip the NULL checks as they may result in not using an index scan
# - The table that is migrated does _not_ need `id` as the primary key
# We use the provided primary_key column to perform the update.
class CopyColumnUsingBackgroundMigrationJob < BaseJob
include Gitlab::Database::DynamicModelHelpers
class CopyColumnUsingBackgroundMigrationJob < BatchedMigrationJob
def perform(copy_from, copy_to)
assignment_clauses = build_assignment_clauses(copy_from, copy_to)
# start_id - The start ID of the range of rows to update.
# end_id - The end ID of the range of rows to update.
# batch_table - The name of the table that contains the columns.
# batch_column - The name of the column we use to batch over the table.
# sub_batch_size - We don't want updates to take more than ~100ms
# This allows us to run multiple smaller batches during
# the minimum 2.minute interval that we can schedule jobs
# pause_ms - The number of milliseconds to sleep between each subbatch execution.
# copy_from - List of columns containing the data to copy.
# copy_to - List of columns to copy the data to. Order must match the order in `copy_from`.
def perform(start_id, end_id, batch_table, batch_column, sub_batch_size, pause_ms, copy_from, copy_to)
copy_from = Array.wrap(copy_from)
copy_to = Array.wrap(copy_to)
raise ArgumentError, 'number of source and destination columns must match' unless copy_from.count == copy_to.count
assignment_clauses = column_assignment_clauses(copy_from, copy_to)
parent_batch_relation = relation_scoped_to_range(batch_table, batch_column, start_id, end_id)
parent_batch_relation.each_batch(column: batch_column, of: sub_batch_size) do |sub_batch|
batch_metrics.time_operation(:update_all) do
sub_batch.update_all(assignment_clauses)
end
pause_ms = 0 if pause_ms < 0
sleep(pause_ms * 0.001)
each_sub_batch(operation_name: :update_all) do |relation|
relation.update_all(assignment_clauses)
end
end
def batch_metrics
@batch_metrics ||= Gitlab::Database::BackgroundMigration::BatchMetrics.new
end
private
def relation_scoped_to_range(source_table, source_key_column, start_id, stop_id)
define_batchable_model(source_table, connection: connection).where(source_key_column => start_id..stop_id)
end
def build_assignment_clauses(copy_from, copy_to)
copy_from = Array.wrap(copy_from)
copy_to = Array.wrap(copy_to)
unless copy_from.count == copy_to.count
raise ArgumentError, 'number of source and destination columns must match'
end
def column_assignment_clauses(copy_from, copy_to)
assignments = copy_from.zip(copy_to).map do |from_column, to_column|
from_column = connection.quote_column_name(from_column)
to_column = connection.quote_column_name(to_column)

View File

@ -39,7 +39,40 @@ module Gitlab
end
def execute_batch(tracking_record)
job_instance = migration_instance_for(tracking_record.migration_job_class)
job_instance = execute_job(tracking_record)
if job_instance.respond_to?(:batch_metrics)
tracking_record.metrics = job_instance.batch_metrics
end
end
def execute_job(tracking_record)
job_class = tracking_record.migration_job_class
if job_class < Gitlab::BackgroundMigration::BatchedMigrationJob
execute_batched_migration_job(job_class, tracking_record)
else
execute_legacy_job(job_class, tracking_record)
end
end
def execute_batched_migration_job(job_class, tracking_record)
job_instance = job_class.new(
start_id: tracking_record.min_value,
end_id: tracking_record.max_value,
batch_table: tracking_record.migration_table_name,
batch_column: tracking_record.migration_column_name,
sub_batch_size: tracking_record.sub_batch_size,
pause_ms: tracking_record.pause_ms,
connection: connection)
job_instance.perform(*tracking_record.migration_job_arguments)
job_instance
end
def execute_legacy_job(job_class, tracking_record)
job_instance = job_class.new
job_instance.perform(
tracking_record.min_value,
@ -50,17 +83,7 @@ module Gitlab
tracking_record.pause_ms,
*tracking_record.migration_job_arguments)
if job_instance.respond_to?(:batch_metrics)
tracking_record.metrics = job_instance.batch_metrics
end
end
def migration_instance_for(job_class)
if job_class < Gitlab::BackgroundMigration::BaseJob
job_class.new(connection: connection)
else
job_class.new
end
job_instance
end
end
end

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
module Gitlab
module Email
module Message
class BuildIosAppGuide
include Gitlab::Email::Message::InProductMarketing::Helper
include Gitlab::Routing
attr_accessor :format
def initialize(format: :html)
@format = format
end
def subject_line
s_('InProductMarketing|Get set up to build for iOS')
end
def title
s_("InProductMarketing|Building for iOS? We've got you covered.")
end
def body_line1
s_(
'InProductMarketing|Want to get your iOS app up and running, including publishing all the way to ' \
'TestFlight? Follow our guide to set up GitLab and fastlane to publish iOS apps to the App Store.'
)
end
def cta_text
s_('InProductMarketing|Learn how to build for iOS')
end
def cta_link
action_link(cta_text, 'https://about.gitlab.com/blog/2019/03/06/ios-publishing-with-gitlab-and-fastlane/')
end
def cta2_text
s_('InProductMarketing|Watch iOS building in action.')
end
def cta2_link
action_link(cta2_text, 'https://www.youtube.com/watch?v=325FyJt7ZG8')
end
def logo_path
'mailers/in_product_marketing/create-0.png'
end
def unsubscribe
unsubscribe_message
end
end
end
end
end

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