Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
4136fdda4c
commit
eab22d334f
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 || ''
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'SignInGitlabMultiversion',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- TODO -->
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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(() => {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ class Groups::UploadsController < Groups::ApplicationController
|
|||
before_action :verify_workhorse_api!, only: [:authorize]
|
||||
|
||||
feature_category :subgroups
|
||||
urgency :low, [:show]
|
||||
|
||||
private
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ class Profiles::AccountsController < Profiles::ApplicationController
|
|||
include AuthHelper
|
||||
|
||||
feature_category :users
|
||||
urgency :low, [:show]
|
||||
|
||||
def show
|
||||
render(locals: show_view_variables)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
class Profiles::KeysController < Profiles::ApplicationController
|
||||
feature_category :users
|
||||
urgency :low, [:create]
|
||||
|
||||
def index
|
||||
@keys = current_user.keys.order_id_desc
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController
|
|||
before_action :user
|
||||
|
||||
feature_category :users
|
||||
urgency :medium, [:update]
|
||||
|
||||
def show
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class ProfilesController < Profiles::ApplicationController
|
|||
end
|
||||
|
||||
feature_category :users
|
||||
urgency :low, [:update]
|
||||
|
||||
def show
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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|
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BuildIosAppGuideEmailExperiment < ApplicationExperiment
|
||||
control { false }
|
||||
candidate { true }
|
||||
end
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
@ -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')
|
||||
|
|
@ -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!')
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
= render 'shared/runners/shared_runners_description'
|
||||
|
||||
#update-shared-runners-form{ data: group_shared_runners_settings_data(@group) }
|
||||
|
|
@ -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' }
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<%= @message.title %>
|
||||
|
||||
<%= @message.body_line1 %>
|
||||
|
||||
<%= @message.cta_link %>
|
||||
|
||||
<%= @message.cta2_link %>
|
||||
|
||||
<%= @message.footer_links %>
|
||||
|
||||
<%= @message.address %>
|
||||
|
||||
<%= @message.unsubscribe %>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
fa1651c066191279fe922b311be3e112b87648c52b1af7a81d7b73ebfe2f7177
|
||||
|
|
@ -0,0 +1 @@
|
|||
0bb23775f65b9997e04dffe701ba609d26dde89796406941fbd27bf810b174ae
|
||||
|
|
@ -0,0 +1 @@
|
|||
8ce9e197aa590d01755541a9f1c53d6835a9d4ae389e011c5050778d19e80f00
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue