Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-04-14 15:08:59 +00:00
parent 9769ccf613
commit 9b762f50fe
79 changed files with 1629 additions and 385 deletions

View file

@ -203,20 +203,20 @@ ee/lib/ee/gitlab/checks/** @proglottis @toon @zj-gitlab
lib/gitlab/checks/** @proglottis @toon @zj-gitlab
^[Documentation Directories]
.markdownlint.yml @marcel.amirault @eread @aqualls @cnorris
/doc/.markdownlint @marcel.amirault @eread @aqualls @cnorris
.markdownlint.yml @marcel.amirault @eread @aqualls @dianalogan
/doc/.markdownlint @marcel.amirault @eread @aqualls @dianalogan
/doc/ @gl-docsteam
/doc/.vale/ @marcel.amirault @eread @aqualls @cnorris
/doc/.vale/ @marcel.amirault @eread @aqualls @dianalogan
^[Documentation Pages]
/doc/administration/application_settings_cache.md @marcel.amirault
/doc/administration/application_settings_cache.md @marcia
/doc/administration/audit_event_streaming.md @eread
/doc/administration/audit_events.md @eread
/doc/administration/audit_reports.md @eread
/doc/administration/auditor_users.md @axil
/doc/administration/auditor_users.md @eread
/doc/administration/auth/ @eread
/doc/administration/cicd.md @marcel.amirault
/doc/administration/clusters/kas.md @sselhorn
/doc/administration/clusters/kas.md @marcia
/doc/administration/compliance.md @eread
/doc/administration/configure.md @axil
/doc/administration/consul.md @axil
@ -240,15 +240,15 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/integration/terminal.md @kpaizee
/doc/administration/invalidate_markdown_cache.md @msedlakjakubowski
/doc/administration/issue_closing_pattern.md @aqualls
/doc/administration/job_artifacts.md @eread
/doc/administration/job_artifacts.md @marcel.amirault
/doc/administration/job_logs.md @sselhorn
/doc/administration/lfs/index.md @aqualls
/doc/administration/libravatar.md @axil
/doc/administration/load_balancer.md @axil
/doc/administration/logs.md @ngaskill
/doc/administration/logs.md @msedlakjakubowski
/doc/administration/maintenance_mode/index.md @axil
/doc/administration/merge_request_diffs.md @aqualls
/doc/administration/monitoring/ @ngaskill
/doc/administration/monitoring/ @msedlakjakubowski
/doc/administration/monitoring/prometheus/index.md @axil
/doc/administration/nfs.md @axil
/doc/administration/object_storage.md @axil
@ -256,11 +256,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/operations/moving_repositories.md @eread
/doc/administration/operations/sidekiq_memory_killer.md @marcel.amirault
/doc/administration/package_information/ @axil
/doc/administration/packages/ @ngaskill
/doc/administration/packages/ @claytoncornell
/doc/administration/pages/index.md @aqualls
/doc/administration/pages/source.md @aqualls
/doc/administration/polling.md @axil
/doc/administration/postgresql/ @aqualls
/doc/administration/postgresql/ @marcia
/doc/administration/pseudonymizer.md @axil
/doc/administration/raketasks/ @axil
/doc/administration/raketasks/praefect.md @eread
@ -280,17 +280,18 @@ 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 @sselhorn
/doc/administration/terraform_state.md @marcia
/doc/administration/timezone.md @axil
/doc/administration/troubleshooting/ @axil
/doc/administration/troubleshooting/elasticsearch.md @rdickenson
/doc/administration/troubleshooting/postgresql.md @aqualls
/doc/administration/troubleshooting/postgresql.md @marcia
/doc/administration/uploads.md @axil
/doc/administration/user_settings.md @eread
/doc/administration/whats-new.md @kpaizee
/doc/administration/wikis/index.md @aqualls
/doc/api/access_requests.md @eread
/doc/api/admin_sidekiq_queues.md @axil
/doc/api/alert_management_alerts.md @msedlakjakubowski
/doc/api/api_resources.md @kpaizee
/doc/api/appearance.md @eread
/doc/api/applications.md @eread
@ -300,12 +301,13 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/boards.md @msedlakjakubowski
/doc/api/branches.md @aqualls
/doc/api/broadcast_messages.md @kpaizee
/doc/api/bulk_imports.md @ngaskill
/doc/api/bulk_imports.md @eread
/doc/api/cluster_agents.md @marcia
/doc/api/commits.md @aqualls
/doc/api/container_registry.md @ngaskill
/doc/api/container_registry.md @claytoncornell
/doc/api/custom_attributes.md @kpaizee
/doc/api/dependencies.md @rdickenson
/doc/api/dependency_proxy.md @ngaskill
/doc/api/dependency_proxy.md @claytoncornell
/doc/api/deploy_keys.md @rdickenson
/doc/api/deploy_tokens.md @rdickenson
/doc/api/deployments.md @rdickenson
@ -316,12 +318,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/epic_issues.md @msedlakjakubowski
/doc/api/epic_links.md @msedlakjakubowski
/doc/api/epics.md @msedlakjakubowski
/doc/api/error_tracking.md @ngaskill
/doc/api/error_tracking.md @msedlakjakubowski
/doc/api/events.md @eread
/doc/api/experiments.md @kpaizee
/doc/api/feature_flag_specs.md @rdickenson
/doc/api/feature_flag_user_lists.md @rdickenson
/doc/api/feature_flags_legacy.md @rdickenson
/doc/api/feature_flags.md @rdickenson
/doc/api/features.md @rdickenson
/doc/api/freeze_periods.md @rdickenson
@ -333,20 +334,21 @@ 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 @sselhorn
/doc/api/group_import_export.md @ngaskill
/doc/api/group_clusters.md @marcia
/doc/api/group_import_export.md @eread
/doc/api/group_iterations.md @msedlakjakubowski
/doc/api/group_labels.md @msedlakjakubowski
/doc/api/group_level_variables.md @marcel.amirault
/doc/api/group_milestones.md @msedlakjakubowski
/doc/api/group_protected_environments.md @rdickenson
/doc/api/group_relations_export.md @ngaskill
/doc/api/group_relations_export.md @eread
/doc/api/group_releases.md @rdickenson
/doc/api/group_repository_storage_moves.md @aqualls
/doc/api/group_wikis.md @aqualls
/doc/api/groups.md @fneill
/doc/api/import.md @ngaskill
/doc/api/import.md @eread
/doc/api/index.md @kpaizee
/doc/api/instance_clusters.md @sselhorn
/doc/api/instance_clusters.md @marcia
/doc/api/instance_level_ci_variables.md @marcel.amirault
/doc/api/integrations.md @kpaizee
/doc/api/invitations.md @kpaizee
@ -354,7 +356,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/issues_statistics.md @msedlakjakubowski
/doc/api/issues.md @msedlakjakubowski
/doc/api/iterations.md @msedlakjakubowski
/doc/api/job_artifacts.md @eread
/doc/api/job_artifacts.md @marcel.amirault
/doc/api/jobs.md @marcel.amirault
/doc/api/keys.md @aqualls
/doc/api/labels.md @msedlakjakubowski
@ -368,31 +370,30 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/merge_request_context_commits.md @aqualls
/doc/api/merge_requests.md @aqualls
/doc/api/merge_trains.md @marcel.amirault
/doc/api/metrics_dashboard_annotations.md @ngaskill
/doc/api/metrics_user_starred_dashboards.md @ngaskill
/doc/api/metrics_dashboard_annotations.md @msedlakjakubowski
/doc/api/metrics_user_starred_dashboards.md @msedlakjakubowski
/doc/api/milestones.md @msedlakjakubowski
/doc/api/namespaces.md @eread
/doc/api/notes.md @msedlakjakubowski
/doc/api/notification_settings.md @msedlakjakubowski
/doc/api/oauth2.md @eread
/doc/api/openapi/openapi_interactive.md @kpaizee
/doc/api/packages.md @ngaskill
/doc/api/packages/ @ngaskill
/doc/api/packages.md @claytoncornell
/doc/api/packages/ @claytoncornell
/doc/api/pages_domains.md @aqualls
/doc/api/pages.md @aqualls
/doc/api/personal_access_tokens.md @eread
/doc/api/pipeline_schedules.md @marcel.amirault
/doc/api/pipeline_triggers.md @marcel.amirault
/doc/api/pipelines.md @marcel.amirault
/doc/api/scim.md @eread
/doc/api/plan_limits.md @eread
/doc/api/project_access_tokens.md @eread
/doc/api/project_aliases.md @aqualls
/doc/api/project_badges.md @aqualls
/doc/api/project_clusters.md @sselhorn
/doc/api/project_clusters.md @marcia
/doc/api/project_import_export.md @aqualls
/doc/api/project_level_variables.md @marcel.amirault
/doc/api/project_relations_export.md @ngaskill
/doc/api/project_relations_export.md @eread
/doc/api/project_repository_storage_moves.md @eread
/doc/api/project_snippets.md @aqualls
/doc/api/project_statistics.md @aqualls
@ -415,7 +416,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/resource_state_events.md @msedlakjakubowski
/doc/api/resource_weight_events.md @msedlakjakubowski
/doc/api/runners.md @sselhorn
/doc/api/plan_limits.md @eread
/doc/api/scim.md @eread
/doc/api/search.md @aqualls
/doc/api/secure_files.md @marcel.amirault
/doc/api/settings.md @eread
@ -433,19 +434,19 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/templates/licenses.md @rdickenson
/doc/api/todos.md @msedlakjakubowski
/doc/api/topics.md @fneill
/doc/api/usage_data.md @fneill
/doc/api/usage_data.md @claytoncornell
/doc/api/users.md @eread
/doc/api/version.md @kpaizee
/doc/api/visual_review_discussions.md @eread
/doc/api/vulnerabilities.md @fneill
/doc/api/vulnerability_exports.md @fneill
/doc/api/vulnerability_findings.md @fneill
/doc/api/visual_review_discussions.md @marcel.amirault
/doc/api/vulnerabilities.md @claytoncornell
/doc/api/vulnerability_exports.md @claytoncornell
/doc/api/vulnerability_findings.md @claytoncornell
/doc/api/wikis.md @aqualls
/doc/architecture/blueprints/container_registry_metadata_database/index.md @ngaskill
/doc/architecture/blueprints/database/scalability/patterns/ @aqualls
/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md @sselhorn
/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/ci/caching/index.md @marcel.amirault
/doc/ci/chatops/index.md @sselhorn
/doc/ci/chatops/index.md @marcia
/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
@ -460,11 +461,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/ci/examples/authenticating-with-hashicorp-vault/index.md @marcel.amirault
/doc/ci/examples/deployment/composer-npm-deploy.md @rdickenson
/doc/ci/examples/deployment/index.md @rdickenson
/doc/ci/examples/end_to_end_testing_webdriverio/index.md @eread
/doc/ci/examples/end_to_end_testing_webdriverio/index.md @marcel.amirault
/doc/ci/examples/index.md @marcel.amirault
/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md @marcel.amirault
/doc/ci/examples/php.md @marcel.amirault
/doc/ci/examples/semantic-release.md @ngaskill
/doc/ci/examples/semantic-release.md @claytoncornell
/doc/ci/git_submodules.md @marcel.amirault
/doc/ci/index.md @marcel.amirault
/doc/ci/interactive_web_terminal/index.md @sselhorn
@ -474,16 +475,14 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/ci/jobs/job_control.md @marcel.amirault
/doc/ci/large_repositories/index.md @sselhorn
/doc/ci/lint.md @marcel.amirault
/doc/ci/metrics_reports.md @eread
/doc/ci/metrics_reports.md @marcel.amirault
/doc/ci/migration/circleci.md @marcel.amirault
/doc/ci/migration/jenkins.md @marcel.amirault
/doc/ci/pipeline_editor/index.md @marcel.amirault
/doc/ci/pipelines/ @marcel.amirault
/doc/ci/pipelines/job_artifacts.md @eread
/doc/ci/pipelines/pipeline_artifacts.md @eread
/doc/ci/quick_start/index.md @marcel.amirault
/doc/ci/resource_groups/index.md @rdickenson
/doc/ci/review_apps/index.md @eread
/doc/ci/review_apps/index.md @marcel.amirault
/doc/ci/runners/ @sselhorn
/doc/ci/secrets/index.md @marcel.amirault
/doc/ci/services/ @sselhorn
@ -491,23 +490,22 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/ci/test_cases/index.md @msedlakjakubowski
/doc/ci/triggers/index.md @marcel.amirault
/doc/ci/troubleshooting.md @marcel.amirault
/doc/ci/unit_test_reports.md @eread
/doc/ci/unit_test_reports.md @marcel.amirault
/doc/ci/variables/ @marcel.amirault
/doc/ci/yaml/ @marcel.amirault
/doc/ci/yaml/artifacts_reports.md @eread
/doc/development/adding_database_indexes.md @aqualls
/doc/development/adding_database_indexes.md @marcia
/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 @sselhorn
/doc/development/avoiding_downtime_in_migrations.md @aqualls
/doc/development/auto_devops.md @marcia
/doc/development/backend/create_source_code_be/index.md @aqualls
/doc/development/backend/ruby_style_guide.md @marcia
/doc/development/background_migrations.md @marcia
/doc/development/batched_background_migrations.md @marcia
/doc/development/build_test_package.md @axil
/doc/development/bulk_import.md @ngaskill
/doc/development/bulk_import.md @eread
/doc/development/cached_queries.md @marcia
/doc/development/cascading_settings.md @eread
/doc/development/chatops_on_gitlabcom.md @sselhorn
/doc/development/chatops_on_gitlabcom.md @marcia
/doc/development/cicd/cicd_reference_documentation_guide.md @marcel.amirault
/doc/development/cicd/index.md @marcel.amirault
/doc/development/cicd/schema.md @marcel.amirault
@ -515,27 +513,28 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/code_intelligence/index.md @aqualls
/doc/development/contributing/ @marcia
/doc/development/contributing/merge_request_workflow.md @aqualls
/doc/development/creating_enums.md @aqualls
/doc/development/database_debugging.md @aqualls
/doc/development/database_query_comments.md @aqualls
/doc/development/database_review.md @aqualls
/doc/development/database/client_side_connection_pool.md @marcia
/doc/development/database/ @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 @aqualls
/doc/development/db_dump.md @marcia
/doc/development/developing_with_solargraph.md @aqualls
/doc/development/distributed_tracing.md @ngaskill
/doc/development/diffs.md @aqualls
/doc/development/distributed_tracing.md @msedlakjakubowski
/doc/development/documentation/feature_flags.md @sselhorn
/doc/development/documentation/graphql_styleguide.md @sselhorn
/doc/development/documentation/index.md @cnorris
/doc/development/documentation/redirects.md @cnorris
/doc/development/documentation/review_apps.md @cnorris
/doc/development/documentation/index.md @dianalogan
/doc/development/documentation/redirects.md @dianalogan
/doc/development/documentation/review_apps.md @dianalogan
/doc/development/documentation/structure.md @sselhorn
/doc/development/documentation/styleguide/ @sselhorn
/doc/development/documentation/testing.md @cnorris
/doc/development/elasticsearch.md @rdickenson
/doc/development/experiment_guide/ @kpaizee
/doc/development/export_csv.md @ngaskill
/doc/development/documentation/testing.md @dianalogan
/doc/development/elasticsearch.md @marcia
/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
@ -544,70 +543,70 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/feature_flags/controls.md @marcia
/doc/development/feature_flags/index.md @marcia
/doc/development/filtering_by_label.md @msedlakjakubowski
/doc/development/foreign_keys.md @aqualls
/doc/development/foreign_keys.md @marcia
/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 @aqualls
/doc/development/hash_indexes.md @aqualls
/doc/development/i18n/ @ngaskill
/doc/development/image_scaling.md @marcel.amirault
/doc/development/import_export.md @ngaskill
/doc/development/graphql_guide/batchloader.md @marcia
/doc/development/hash_indexes.md @marcia
/doc/development/i18n/ @eread
/doc/development/image_scaling.md @marcia
/doc/development/import_export.md @eread
/doc/development/index.md @marcia
/doc/development/insert_into_tables_in_batches.md @aqualls
/doc/development/insert_into_tables_in_batches.md @marcia
/doc/development/integrations/ @kpaizee
/doc/development/integrations/codesandbox.md @marcia
/doc/development/integrations/jira_connect.md @kpaizee
/doc/development/integrations/secure_partner_integration.md @rdickenson
/doc/development/integrations/secure.md @ngaskill
/doc/development/integrations/secure.md @claytoncornell
/doc/development/internal_api/ @aqualls
/doc/development/internal_users.md @marcia
/doc/development/issuable-like-models.md @msedlakjakubowski
/doc/development/issue_types.md @msedlakjakubowski
/doc/development/iterating_tables_in_batches.md @aqualls
/doc/development/kubernetes.md @sselhorn
/doc/development/iterating_tables_in_batches.md @marcia
/doc/development/kubernetes.md @marcia
/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/omnibus.md @axil
/doc/development/ordering_table_columns.md @aqualls
/doc/development/packages.md @ngaskill
/doc/development/ordering_table_columns.md @marcia
/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/query_performance.md @aqualls
/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/real_time.md @msedlakjakubowski
/doc/development/secure_coding_guidelines.md @marcia
/doc/development/serializing_data.md @aqualls
/doc/development/service_ping/ @fneill
/doc/development/snowplow/ @fneill
/doc/development/serializing_data.md @marcia
/doc/development/service_ping/ @claytoncornell
/doc/development/single_table_inheritance.md @marcia
/doc/development/snowplow/ @claytoncornell
/doc/development/spam_protection_and_captcha/ @eread
/doc/development/sql.md @aqualls
/doc/development/swapping_tables.md @aqualls
/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 @aqualls
/doc/development/understanding_explain_plans.md @marcia
/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 @aqualls
/doc/development/verifying_database_capabilities.md @marcia
/doc/development/wikis.md @aqualls
/doc/development/work_items.md @msedlakjakubowski
/doc/development/work_items_widgets.md @msedlakjakubowski
/doc/development/work_items.md @msedlakjakubowski
/doc/development/workhorse/ @aqualls
/doc/development/workspace/index.md @marcia
/doc/downgrade_ee_to_ce/index.md @axil
/doc/gitlab-basics/add-file.md @aqualls
/doc/gitlab-basics/command-line-commands.md @aqualls
/doc/gitlab-basics/create-branch.md @aqualls
/doc/gitlab-basics/feature_branch_workflow.md @aqualls
/doc/gitlab-basics/index.md @aqualls
/doc/gitlab-basics/start-using-git.md @aqualls
/doc/gitlab-basics/ @aqualls
/doc/install/ @axil
/doc/integration/ @kpaizee
/doc/integration/elasticsearch.md @rdickenson
/doc/integration/elasticsearch.md @marcia
/doc/integration/gitpod.md @aqualls
/doc/integration/kerberos.md @eread
/doc/integration/mattermost/index.md @axil
@ -615,45 +614,40 @@ 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 @sselhorn
/doc/operations/ @ngaskill
/doc/integration/vault.md @marcia
/doc/operations/ @msedlakjakubowski
/doc/operations/feature_flags.md @rdickenson
/doc/operations/product_analytics.md @fneill
/doc/operations/product_analytics.md @claytoncornell
/doc/policy/ @axil
/doc/public_access/public_access.md @fneill
/doc/raketasks/ @axil
/doc/raketasks/generate_sample_prometheus_data.md @ngaskill
/doc/raketasks/generate_sample_prometheus_data.md @msedlakjakubowski
/doc/raketasks/migrate_snippets.md @aqualls
/doc/raketasks/spdx.md @rdickenson
/doc/raketasks/user_management.md @axil
/doc/raketasks/web_hooks.md @axil
/doc/raketasks/x509_signatures.md @aqualls
/doc/security/ @eread
/doc/ssh/index.md @eread
/doc/subscriptions/ @fneill
/doc/system_hooks/system_hooks.md @kpaizee
/doc/subscriptions/ @sselhorn
/doc/topics/authentication/index.md @eread
/doc/topics/autodevops/ @sselhorn
/doc/topics/autodevops/ @marcia
/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 @aqualls
/doc/update/upgrading_postgresql_using_slony.md @aqualls
/doc/update/mysql_to_postgresql.md @marcia
/doc/update/upgrading_postgresql_using_slony.md @marcia
/doc/user/admin_area/analytics/ @fneill
/doc/user/admin_area/broadcast_messages.md @kpaizee
/doc/user/admin_area/credentials_inventory.md @eread
/doc/user/admin_area/custom_project_templates.md @ngaskill
/doc/user/admin_area/custom_project_templates.md @eread
/doc/user/admin_area/diff_limits.md @aqualls
/doc/user/admin_area/geo_nodes.md @axil
/doc/user/admin_area/labels.md @msedlakjakubowski
/doc/user/admin_area/license.md @kpaizee
/doc/user/admin_area/license_file.md @sselhorn
/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 @aqualls
/doc/user/admin_area/monitoring/health_check.md @ngaskill
/doc/user/admin_area/monitoring/background_migrations.md @marcia
/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
/doc/user/admin_area/settings/account_and_limit_settings.md @aqualls
@ -664,10 +658,10 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/admin_area/settings/files_api_rate_limits.md @aqualls
/doc/user/admin_area/settings/git_lfs_rate_limits.md @aqualls
/doc/user/admin_area/settings/gitaly_timeouts.md @eread
/doc/user/admin_area/settings/import_export_rate_limits.md @ngaskill
/doc/user/admin_area/settings/import_export_rate_limits.md @eread
/doc/user/admin_area/settings/index.md @aqualls
/doc/user/admin_area/settings/instance_template_repository.md @aqualls
/doc/user/admin_area/settings/package_registry_rate_limits.md @ngaskill
/doc/user/admin_area/settings/package_registry_rate_limits.md @claytoncornell
/doc/user/admin_area/settings/project_integration_management.md @kpaizee
/doc/user/admin_area/settings/push_event_activities_limit.md @aqualls
/doc/user/admin_area/settings/rate_limit_on_issues_creation.md @msedlakjakubowski
@ -678,69 +672,71 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/analytics/ @fneill
/doc/user/analytics/ci_cd_analytics.md @rdickenson
/doc/user/application_security/ @rdickenson
/doc/user/application_security/cluster_image_scanning/index.md @ngaskill
/doc/user/application_security/container_scanning/index.md @ngaskill
/doc/user/application_security/cve_id_request.md @fneill
/doc/user/application_security/policies/ @ngaskill
/doc/user/application_security/security_dashboard/index.md @fneill
/doc/user/application_security/threat_monitoring/index.md @ngaskill
/doc/user/application_security/vulnerabilities/ @fneill
/doc/user/application_security/vulnerability_report/index.md @fneill
/doc/user/application_security/cluster_image_scanning/index.md @claytoncornell
/doc/user/application_security/container_scanning/index.md @claytoncornell
/doc/user/application_security/coverage_fuzzing/index.md @rdickenson
/doc/user/application_security/cve_id_request.md @claytoncornell
/doc/user/application_security/policies/ @claytoncornell
/doc/user/application_security/security_dashboard/index.md @claytoncornell
/doc/user/application_security/threat_monitoring/index.md @claytoncornell
/doc/user/application_security/vulnerabilities/index.md @claytoncornell
/doc/user/application_security/vulnerabilities/severities.md @claytoncornell
/doc/user/application_security/vulnerability_report/index.md @claytoncornell
/doc/user/asciidoc.md @aqualls
/doc/user/award_emojis.md @msedlakjakubowski
/doc/user/clusters/ @sselhorn
/doc/user/clusters/ @marcia
/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/index.md @eread
/doc/user/group/clusters/index.md @sselhorn
/doc/user/group/clusters/index.md @marcia
/doc/user/group/contribution_analytics/index.md @fneill
/doc/user/group/custom_project_templates.md @ngaskill
/doc/user/group/custom_project_templates.md @eread
/doc/user/group/devops_adoption/index.md @fneill
/doc/user/group/epics/epic_boards.md @msedlakjakubowski
/doc/user/group/epics/index.md @msedlakjakubowski
/doc/user/group/epics/linked_epics.md @msedlakjakubowski
/doc/user/group/epics/manage_epics.md @msedlakjakubowski
/doc/user/group/import/index.md @eread
/doc/user/group/index.md @fneill
/doc/user/group/import/index.md @ngaskill
/doc/user/group/insights/index.md @fneill
/doc/user/group/issues_analytics/index.md @msedlakjakubowski
/doc/user/group/iterations/index.md @msedlakjakubowski
/doc/user/group/planning_hierarchy/index.md @msedlakjakubowski
/doc/user/group/repositories_analytics/index.md @eread
/doc/user/group/repositories_analytics/index.md @marcel.amirault
/doc/user/group/roadmap/index.md @msedlakjakubowski
/doc/user/group/saml_sso/group_managed_accounts.md @eread
/doc/user/group/saml_sso/index.md @eread
/doc/user/group/saml_sso/scim_setup.md @eread
/doc/user/group/settings/group_access_tokens.md @eread
/doc/user/group/settings/import_export.md @ngaskill
/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/ @sselhorn
/doc/user/infrastructure/clusters/manage/management_project_applications/apparmor.md @ngaskill
/doc/user/infrastructure/clusters/manage/management_project_applications/cilium.md @ngaskill
/doc/user/infrastructure/clusters/manage/management_project_applications/elasticstack.md @ngaskill
/doc/user/infrastructure/clusters/manage/management_project_applications/falco.md @ngaskill
/doc/user/infrastructure/clusters/manage/management_project_applications/fluentd.md @ngaskill
/doc/user/infrastructure/iac/ @sselhorn
/doc/user/infrastructure/index.md @sselhorn
/doc/user/infrastructure/clusters/ @marcia
/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/sentry.md @msedlakjakubowski
/doc/user/infrastructure/iac/ @marcia
/doc/user/infrastructure/index.md @marcia
/doc/user/markdown.md @aqualls
/doc/user/packages/ @ngaskill
/doc/user/packages/infrastructure_registry/index.md @sselhorn
/doc/user/packages/terraform_module_registry/index.md @sselhorn
/doc/user/packages/workflows/project_registry.md @ngaskill
/doc/user/packages/workflows/working_with_monorepos.md @ngaskill
/doc/user/packages/ @claytoncornell
/doc/user/packages/infrastructure_registry/index.md @marcia
/doc/user/packages/terraform_module_registry/index.md @marcia
/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/ @sselhorn
/doc/user/project/clusters/kubernetes_pod_logs.md @ngaskill
/doc/user/project/clusters/protect/ @ngaskill
/doc/user/project/clusters/ @marcia
/doc/user/project/clusters/kubernetes_pod_logs.md @msedlakjakubowski
/doc/user/project/clusters/protect/ @claytoncornell
/doc/user/project/code_intelligence.md @aqualls
/doc/user/project/code_owners.md @aqualls
/doc/user/project/deploy_boards.md @rdickenson
@ -750,33 +746,31 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/project/file_lock.md @aqualls
/doc/user/project/git_attributes.md @aqualls
/doc/user/project/highlighting.md @aqualls
/doc/user/project/import/ @ngaskill
/doc/user/project/import/ @eread
/doc/user/project/import/jira.md @msedlakjakubowski
/doc/user/project/index.md @fneill
/doc/user/project/integrations/ @kpaizee
/doc/user/project/integrations/prometheus_library/ @ngaskill
/doc/user/project/integrations/prometheus.md @ngaskill
/doc/user/project/integrations/prometheus_library/ @msedlakjakubowski
/doc/user/project/integrations/prometheus.md @msedlakjakubowski
/doc/user/project/issue_board.md @msedlakjakubowski
/doc/user/project/issues/ @msedlakjakubowski
/doc/user/project/issues/csv_import.md @ngaskill
/doc/user/project/issues/csv_import.md @eread
/doc/user/project/labels.md @msedlakjakubowski
/doc/user/project/members/index.md @fneill
/doc/user/project/members/share_project_with_groups.md @fneill
/doc/user/project/merge_requests/ @aqualls
/doc/user/project/merge_requests/accessibility_testing.md @eread
/doc/user/project/merge_requests/browser_performance_testing.md @eread
/doc/user/project/merge_requests/accessibility_testing.md @marcel.amirault
/doc/user/project/merge_requests/browser_performance_testing.md @marcel.amirault
/doc/user/project/merge_requests/code_quality.md @rdickenson
/doc/user/project/merge_requests/csv_export.md @eread
/doc/user/project/merge_requests/fail_fast_testing.md @eread
/doc/user/project/merge_requests/load_performance_testing.md @eread
/doc/user/project/merge_requests/fail_fast_testing.md @marcel.amirault
/doc/user/project/merge_requests/load_performance_testing.md @marcel.amirault
/doc/user/project/merge_requests/reviews/index.md @aqualls
/doc/user/project/merge_requests/status_checks.md @eread
/doc/user/project/merge_requests/test_coverage_visualization.md @eread
/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md @eread
/doc/user/project/merge_requests/test_coverage_visualization.md @marcel.amirault
/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md @marcel.amirault
/doc/user/project/milestones/ @msedlakjakubowski
/doc/user/project/pages/ @aqualls
/doc/user/project/protected_branches.md @aqualls
/doc/user/project/protected_tags.md @aqualls
/doc/user/project/push_options.md @aqualls
/doc/user/project/quick_actions.md @msedlakjakubowski
/doc/user/project/releases/index.md @rdickenson
/doc/user/project/releases/release_cli.md @rdickenson
@ -784,7 +778,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/project/repository/reducing_the_repo_size_using_git.md @eread
/doc/user/project/requirements/index.md @msedlakjakubowski
/doc/user/project/service_desk.md @msedlakjakubowski
/doc/user/project/settings/import_export.md @ngaskill
/doc/user/project/settings/import_export.md @eread
/doc/user/project/settings/index.md @fneill
/doc/user/project/settings/project_access_tokens.md @eread
/doc/user/project/static_site_editor/index.md @aqualls
@ -795,13 +789,14 @@ 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 @rdickenson
/doc/user/search/advanced_search.md @marcia
/doc/user/search/index.md @aqualls
/doc/user/shortcuts.md @aqualls
/doc/user/snippets.md @aqualls
/doc/user/ssh.md @eread
/doc/user/tasks.md @msedlakjakubowski
/doc/user/todos.md @msedlakjakubowski
/doc/user/usage_quotas.md @fneill
/doc/user/usage_quotas.md @sselhorn
/doc/user/workspace/index.md @fneill
[Authentication and Authorization]

View file

@ -1 +1 @@
9413ca591ebe30dcb133c86d0ec53f6bc2fc30bb
c311109b15c26e1981c855bfa5c87aef02d27560

View file

@ -41,6 +41,7 @@ gem 'omniauth-azure-activedirectory-v2', '~> 1.0'
gem 'omniauth-azure-oauth2', '~> 0.0.9' # Deprecated v1 version
gem 'omniauth-cas3', '~> 1.1.4'
gem 'omniauth-dingtalk-oauth2', '~> 1.0'
gem 'omniauth-alicloud', '~> 1.0.1'
gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-github', '~> 1.4'
gem 'omniauth-gitlab', '~> 1.0.2'

View file

@ -829,6 +829,8 @@ GEM
omniauth (1.9.1)
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
omniauth-alicloud (1.0.1)
omniauth-oauth2 (~> 1.7.1)
omniauth-atlassian-oauth2 (0.2.0)
omniauth (>= 1.1.1)
omniauth-oauth2 (>= 1.5)
@ -1570,6 +1572,7 @@ DEPENDENCIES
ohai (~> 16.10)
oj (~> 3.10.6)
omniauth (~> 1.8)
omniauth-alicloud (~> 1.0.1)
omniauth-atlassian-oauth2 (~> 0.2.0)
omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.3)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -1,32 +0,0 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { NodeViewWrapper } from '@tiptap/vue-2';
export default {
name: 'ImageWrapper',
components: {
NodeViewWrapper,
GlLoadingIcon,
},
props: {
node: {
type: Object,
required: true,
},
},
};
</script>
<template>
<node-view-wrapper class="gl-display-inline-block">
<span class="gl-relative">
<img
data-testid="image"
class="gl-max-w-full gl-h-auto"
:title="node.attrs.title"
:class="{ 'gl-opacity-5': node.attrs.uploading }"
:src="node.attrs.src"
/>
<gl-loading-icon v-if="node.attrs.uploading" class="gl-absolute gl-left-50p gl-top-half" />
</span>
</node-view-wrapper>
</template>

View file

@ -0,0 +1,51 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { NodeViewWrapper } from '@tiptap/vue-2';
const tagNameMap = {
image: 'img',
video: 'video',
audio: 'audio',
};
export default {
name: 'MediaWrapper',
components: {
NodeViewWrapper,
GlLoadingIcon,
},
props: {
node: {
type: Object,
required: true,
},
},
computed: {
tagName() {
return tagNameMap[this.node.type.name] || 'img';
},
},
};
</script>
<template>
<node-view-wrapper class="gl-display-inline-block">
<span class="gl-relative" :class="{ [`media-container ${tagName}-container`]: true }">
<gl-loading-icon v-if="node.attrs.uploading" class="gl-absolute gl-left-50p gl-top-half" />
<component
:is="tagName"
data-testid="media"
:class="{
'gl-max-w-full gl-h-auto': tagName !== 'audio',
'gl-opacity-5': node.attrs.uploading,
}"
:title="node.attrs.title || node.attrs.alt"
:alt="node.attrs.alt"
:src="node.attrs.src"
controls="true"
/>
<a v-if="tagName !== 'img'" :href="node.attrs.canonicalSrc || node.attrs.src" @click.prevent>
{{ node.attrs.title || node.attrs.alt }}
</a>
</span>
</node-view-wrapper>
</template>

View file

@ -1,6 +1,6 @@
import { Image } from '@tiptap/extension-image';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import ImageWrapper from '../components/wrappers/image.vue';
import MediaWrapper from '../components/wrappers/media.vue';
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
const resolveImageEl = (element) =>
@ -78,6 +78,6 @@ export default Image.extend({
];
},
addNodeView() {
return VueNodeViewRenderer(ImageWrapper);
return VueNodeViewRenderer(MediaWrapper);
},
});

View file

@ -1,6 +1,8 @@
/* eslint-disable @gitlab/require-i18n-strings */
import { Node } from '@tiptap/core';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import MediaWrapper from '../components/wrappers/media.vue';
const queryPlayableElement = (element, mediaType) => element.querySelector(mediaType);
@ -11,6 +13,9 @@ export default Node.create({
addAttributes() {
return {
uploading: {
default: false,
},
src: {
default: null,
parseHTML: (element) => {
@ -60,7 +65,11 @@ export default Node.create({
...this.extraElementAttrs,
},
],
['a', { href: node.attrs.src }, node.attrs.alt],
['a', { href: node.attrs.src }, node.attrs.title || node.attrs.alt || ''],
];
},
addNodeView() {
return VueNodeViewRenderer(MediaWrapper);
},
});

View file

@ -5,6 +5,16 @@ import { extractFilename, readFileAsDataURL } from './utils';
export const acceptedMimes = {
image: ['image/jpeg', 'image/png', 'image/gif', 'image/jpg'],
audio: [
'audio/basic',
'audio/mid',
'audio/mpeg',
'audio/x-aiff',
'audio/ogg',
'audio/vorbis',
'audio/vnd.wav',
],
video: ['video/mp4', 'video/quicktime'],
};
const extractAttachmentLinkUrl = (html) => {
@ -50,11 +60,11 @@ export const uploadFile = async ({ uploadsPath, renderMarkdown, file }) => {
return extractAttachmentLinkUrl(rendered);
};
const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown, eventHub }) => {
const uploadContent = async ({ type, editor, file, uploadsPath, renderMarkdown, eventHub }) => {
const encodedSrc = await readFileAsDataURL(file);
const { view } = editor;
editor.commands.setImage({ uploading: true, src: encodedSrc });
editor.commands.insertContent({ type, attrs: { uploading: true, src: encodedSrc } });
const { state } = view;
const position = state.selection.from - 1;
@ -74,7 +84,7 @@ const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown, eventHub
} catch (e) {
editor.commands.deleteRange({ from: position, to: position + 1 });
eventHub.$emit('alert', {
message: __('An error occurred while uploading the image. Please try again.'),
message: __('An error occurred while uploading the file. Please try again.'),
variant: VARIANT_DANGER,
});
}
@ -114,10 +124,12 @@ const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown, eve
export const handleFileEvent = ({ editor, file, uploadsPath, renderMarkdown, eventHub }) => {
if (!file) return false;
if (acceptedMimes.image.includes(file?.type)) {
uploadImage({ editor, file, uploadsPath, renderMarkdown, eventHub });
for (const [type, mimes] of Object.entries(acceptedMimes)) {
if (mimes.includes(file?.type)) {
uploadContent({ type, editor, file, uploadsPath, renderMarkdown, eventHub });
return true;
return true;
}
}
uploadAttachment({ editor, file, uploadsPath, renderMarkdown, eventHub });

View file

@ -308,8 +308,7 @@ module WikiActions
end
def load_content?
return false if %w[history destroy diff].include?(params[:action])
return false if params[:action] == 'show' && Feature.enabled?(:wiki_async_load, container, default_enabled: :yaml)
return false if %w[history destroy diff show].include?(params[:action])
true
end

View file

@ -169,15 +169,17 @@ class SearchController < ApplicationController
search_allowed = case params[:scope]
when 'blobs'
Feature.enabled?(:global_search_code_tab, current_user, type: :ops, default_enabled: true)
Feature.enabled?(:global_search_code_tab, current_user, type: :ops, default_enabled: :yaml)
when 'commits'
Feature.enabled?(:global_search_commits_tab, current_user, type: :ops, default_enabled: true)
Feature.enabled?(:global_search_commits_tab, current_user, type: :ops, default_enabled: :yaml)
when 'issues'
Feature.enabled?(:global_search_issues_tab, current_user, type: :ops, default_enabled: true)
Feature.enabled?(:global_search_issues_tab, current_user, type: :ops, default_enabled: :yaml)
when 'merge_requests'
Feature.enabled?(:global_search_merge_requests_tab, current_user, type: :ops, default_enabled: true)
Feature.enabled?(:global_search_merge_requests_tab, current_user, type: :ops, default_enabled: :yaml)
when 'wiki_blobs'
Feature.enabled?(:global_search_wiki_tab, current_user, type: :ops, default_enabled: true)
Feature.enabled?(:global_search_wiki_tab, current_user, type: :ops, default_enabled: :yaml)
when 'users'
Feature.enabled?(:global_search_users_tab, current_user, type: :ops, default_enabled: :yaml)
else
true
end

View file

@ -14,7 +14,8 @@ class UploadsController < ApplicationController
"appearance" => Appearance,
"personal_snippet" => PersonalSnippet,
"projects/topic" => Projects::Topic,
nil => PersonalSnippet
'alert_management_metric_image' => ::AlertManagement::MetricImage,
nil => PersonalSnippet
}.freeze
rescue_from UnknownUploadModelError, with: :render_404
@ -56,6 +57,8 @@ class UploadsController < ApplicationController
true
when Projects::Topic
true
when ::AlertManagement::MetricImage
can?(current_user, :read_alert_management_metric_image, model.alert)
else
can?(current_user, "read_#{model.class.underscore}".to_sym, model)
end

View file

@ -2,6 +2,7 @@
module AuthHelper
PROVIDERS_WITH_ICONS = %w(
alicloud
atlassian_oauth2
auth0
authentiq

View file

@ -436,11 +436,11 @@ module SearchHelper
end
def show_user_search_tab?
if @project
project_search_tabs?(:members)
else
can?(current_user, :read_users_list)
end
return project_search_tabs?(:members) if @project
return false unless can?(current_user, :read_users_list)
return true if @group
Feature.enabled?(:global_search_users_tab, current_user, type: :ops, default_enabled: :yaml)
end
def issuable_state_to_badge_class(issuable)

View file

@ -27,6 +27,7 @@ module AlertManagement
has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :ordered_notes, -> { fresh }, as: :noteable, class_name: 'Note'
has_many :user_mentions, class_name: 'AlertManagement::AlertUserMention', foreign_key: :alert_management_alert_id
has_many :metric_images, class_name: '::AlertManagement::MetricImage'
has_internal_id :iid, scope: :project
@ -142,6 +143,10 @@ module AlertManagement
reference.to_i > 0 && reference.to_i <= Gitlab::Database::MAX_INT_VALUE
end
def metric_images_available?
::AlertManagement::MetricImage.available_for?(project)
end
def prometheus?
monitoring_tool == Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
end

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
module AlertManagement
class MetricImage < ApplicationRecord
include MetricImageUploading
self.table_name = 'alert_management_alert_metric_images'
belongs_to :alert, class_name: 'AlertManagement::Alert', foreign_key: 'alert_id', inverse_of: :metric_images
def self.available_for?(project)
true
end
private
def local_path
Gitlab::Routing.url_helpers.alert_metric_image_upload_path(
filename: file.filename,
id: file.upload.model_id,
model: model_name.param_key,
mounted_as: 'file'
)
end
end
end

View file

@ -621,6 +621,8 @@ class ApplicationSetting < ApplicationRecord
attr_encrypted :external_pipeline_validation_service_token, encryption_options_base_32_aes_256_gcm
attr_encrypted :mailgun_signing_key, encryption_options_base_32_aes_256_gcm.merge(encode: false)
attr_encrypted :database_grafana_api_key, encryption_options_base_32_aes_256_gcm.merge(encode: false, encode_iv: false)
attr_encrypted :arkose_labs_public_api_key, encryption_options_base_32_aes_256_gcm.merge(encode: false, encode_iv: false)
attr_encrypted :arkose_labs_private_api_key, encryption_options_base_32_aes_256_gcm.merge(encode: false, encode_iv: false)
validates :disable_feed_token,
inclusion: { in: [true, false], message: _('must be a boolean value') }

View file

@ -504,7 +504,11 @@ module Ci
if metadata&.expanded_environment_name.present?
metadata.expanded_environment_name
else
ExpandVariables.expand(environment, -> { simple_variables })
if ::Feature.enabled?(:ci_expand_environment_name_and_url, project, default_enabled: :yaml)
ExpandVariables.expand(environment, -> { simple_variables.sort_and_expand_all })
else
ExpandVariables.expand(environment, -> { simple_variables })
end
end
end
end

View file

@ -0,0 +1,54 @@
# frozen_string_literal: true
module MetricImageUploading
extend ActiveSupport::Concern
MAX_FILE_SIZE = 1.megabyte.freeze
included do
include Gitlab::FileTypeDetection
include FileStoreMounter
include WithUploads
validates :file, presence: true
validate :validate_file_is_image
validates :url, length: { maximum: 255 }, public_url: { allow_blank: true }
validates :url_text, length: { maximum: 128 }
scope :order_created_at_asc, -> { order(created_at: :asc) }
attribute :file_store, :integer, default: -> { MetricImageUploader.default_store }
mount_file_store_uploader MetricImageUploader
end
def filename
@filename ||= file&.filename
end
def file_path
@file_path ||= begin
return file&.url unless file&.upload
# If we're using a CDN, we need to use the full URL
asset_host = ActionController::Base.asset_host || Gitlab.config.gitlab.base_url
Gitlab::Utils.append_path(asset_host, local_path)
end
end
private
def valid_file_extensions
Gitlab::FileTypeDetection::SAFE_IMAGE_EXT
end
def validate_file_is_image
unless image?
message = _('does not have a supported extension. Only %{extension_list} are supported') % {
extension_list: valid_file_extensions.to_sentence
}
errors.add(:file, message)
end
end
end

View file

@ -282,7 +282,7 @@ class User < ApplicationRecord
after_update :username_changed_hook, if: :saved_change_to_username?
after_destroy :post_destroy_hook
after_destroy :remove_key_cache
after_save if: -> { saved_change_to_email? && confirmed? } do
after_save if: -> { (saved_change_to_email? || saved_change_to_confirmed_at?) && confirmed? } do
email_to_confirm = self.emails.find_by(email: self.email)
if email_to_confirm.present?

View file

@ -3,7 +3,15 @@
module AlertManagement
class AlertPolicy < ::BasePolicy
delegate { @subject.project }
rule { can?(:read_alert_management_alert) }.policy do
enable :read_alert_management_metric_image
end
rule { can?(:update_alert_management_alert) }.policy do
enable :upload_alert_management_metric_image
enable :update_alert_management_metric_image
enable :destroy_alert_management_metric_image
end
end
end
AlertManagement::AlertPolicy.prepend_mod

View file

@ -0,0 +1,46 @@
# frozen_string_literal: true
module AlertManagement
module MetricImages
class UploadService < BaseService
attr_reader :alert, :file, :url, :url_text, :metric
def initialize(alert, current_user, params = {})
super
@alert = alert
@file = params.fetch(:file)
@url = params.fetch(:url, nil)
@url_text = params.fetch(:url_text, nil)
end
def execute
unless can_upload_metrics?
return ServiceResponse.error(
message: _("You are not authorized to upload metric images"),
http_status: :forbidden
)
end
metric = AlertManagement::MetricImage.new(
alert: alert,
file: file,
url: url,
url_text: url_text
)
if metric.save
ServiceResponse.success(payload: { metric: metric, alert: alert })
else
ServiceResponse.error(message: metric.errors.full_messages.join(', '), http_status: :bad_request)
end
end
private
def can_upload_metrics?
alert.metric_images_available? && current_user&.can?(:upload_alert_management_metric_image, alert)
end
end
end
end

View file

@ -56,7 +56,13 @@ module Deployments
end
def expanded_environment_url
ExpandVariables.expand(environment_url, -> { variables }) if environment_url
return unless environment_url
if ::Feature.enabled?(:ci_expand_environment_name_and_url, deployment.project, default_enabled: :yaml)
ExpandVariables.expand(environment_url, -> { variables.sort_and_expand_all })
else
ExpandVariables.expand(environment_url, -> { variables })
end
end
def environment_url

View file

@ -105,7 +105,8 @@ module Projects
end
@project.track_project_repository
@project.create_project_setting unless @project.project_setting
create_project_settings
yield if block_given?
@ -122,6 +123,14 @@ module Projects
create_sast_commit if @initialize_with_sast
end
def create_project_settings
if Feature.enabled?(:create_project_settings, default_enabled: :yaml)
@project.project_setting.save if @project.project_setting.changed?
else
@project.create_project_setting unless @project.project_setting
end
end
# Add an authorization for the current user authorizations inline
# (so they can access the project immediately after this request
# completes), and any other affected users in the background

View file

@ -0,0 +1,20 @@
# frozen_string_literal: true
class MetricImageUploader < GitlabUploader # rubocop:disable Gitlab/NamespacedClass
include RecordsUploads::Concern
include ObjectStorage::Concern
prepend ObjectStorage::Extension::RecordsUploads
include UploaderHelper
private
def dynamic_segment
File.join(model.class.underscore, mounted_as.to_s, model.id.to_s)
end
class << self
def default_store
object_store_enabled? ? ObjectStorage::Store::REMOTE : ObjectStorage::Store::LOCAL
end
end
end

View file

@ -3,5 +3,4 @@
.gl-mb-3.js-email-opt-in.hidden
.gl-font-weight-bold.gl-mb-3
= _('Email updates (optional)')
= f.check_box :email_opted_in
= f.label :email_opted_in, _("I'd like to receive updates about GitLab via email"), class: 'gl-font-weight-normal'
= f.gitlab_ui_checkbox_component :email_opted_in, _("I'd like to receive updates about GitLab via email")

View file

@ -14,7 +14,7 @@
.settings-content
- if mirror_settings_enabled
= form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f|
= gitlab_ui_form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f|
.panel.panel-default
.panel-body
%div= form_errors(@project)

View file

@ -17,7 +17,7 @@
%p.gl-text-center= html_escape(_('%{gitlab_experience_text}. We won\'t share this information with anyone.')) % { gitlab_experience_text: gitlab_experience_text }
- else
%p.gl-text-center= html_escape(_('%{gitlab_experience_text}. Don\'t worry, this information isn\'t shared outside of your self-managed GitLab instance.')) % { gitlab_experience_text: gitlab_experience_text }
= form_for(current_user, url: users_sign_up_welcome_path, html: { class: 'card gl-w-full! gl-p-5', 'aria-live' => 'assertive' }) do |f|
= gitlab_ui_form_for(current_user, url: users_sign_up_welcome_path, html: { class: 'card gl-w-full! gl-p-5', 'aria-live' => 'assertive' }) do |f|
.devise-errors
= render 'devise/shared/error_messages', resource: current_user
.row

View file

@ -27,9 +27,6 @@
- if can?(current_user, :create_wiki, @wiki.container) && @page.latest? && @valid_encoding
= link_to sprite_icon('pencil', css_class: 'gl-icon'), wiki_page_path(@wiki, @page, action: :edit), title: 'Edit', role: "button", class: 'btn gl-button btn-icon btn-default js-wiki-edit', data: { qa_selector: 'edit_page_button', testid: 'wiki_edit_button' }
- if Feature.enabled?(:wiki_async_load, @wiki.container, default_enabled: :yaml)
.js-async-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content', tracking_context: wiki_page_tracking_context(@page).to_json, get_wiki_content_url: wiki_page_render_api_endpoint(@page) } }
- else
= render 'shared/wikis/wiki_content'
.js-async-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content', tracking_context: wiki_page_tracking_context(@page).to_json, get_wiki_content_url: wiki_page_render_api_endpoint(@page) } }
= render 'shared/wikis/sidebar'

View file

@ -0,0 +1,8 @@
---
name: ci_expand_environment_name_and_url
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84969
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/358831
milestone: '14.10'
type: development
group: group::pipeline authoring
default_enabled: false

View file

@ -0,0 +1,8 @@
---
name: create_project_settings
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84502
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/358136
milestone: '14.10'
type: development
group: group::authentication and authorization
default_enabled: false

View file

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323743
milestone: '13.10'
type: development
group: group::project management
default_enabled: false
default_enabled: true

View file

@ -1,8 +0,0 @@
---
name: wiki_async_load
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82394
rollout_issue_url:
milestone: '14.9'
type: development
group: group::editor
default_enabled: false

View file

@ -0,0 +1,8 @@
---
name: global_search_users_tab
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84186
rollout_issue_url:
milestone: '14.10'
type: ops
group: group::global search
default_enabled: true

View file

@ -1035,6 +1035,9 @@ production: &base
# arguments, followed by optional 'args' which can be either a hash or an array.
# Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html
providers:
# - { name: 'alicloud',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET' }
# See omniauth-cas3 for more configuration details
# - { name: 'cas3',
# label: 'cas3',
@ -1562,6 +1565,9 @@ test:
external_providers: []
providers:
- { name: 'alicloud',
app_id: 'YOUR_APP_ID',
app_secret: 'YOUR_APP_SECRET' }
- { name: 'cas3',
label: 'cas3',
args: { url: 'https://sso.example.com',

View file

@ -1001,14 +1001,6 @@ Settings['prometheus'] ||= Settingslogic.new({})
Settings.prometheus['enabled'] ||= false
Settings.prometheus['server_address'] ||= nil
#
# Arkose settings
#
Settings['arkose'] ||= Settingslogic.new({})
Settings.arkose['public_key'] ||= ENV['ARKOSE_LABS_PUBLIC_KEY']
Settings.arkose['private_key'] ||= ENV['ARKOSE_LABS_PRIVATE_KEY']
Settings.arkose['verify_url'] ||= ENV['ARKOSE_LABS_VERIFY_URL']
#
# Shutdown settings
#

View file

@ -38,6 +38,12 @@ scope path: :uploads do
post ':model/authorize',
to: 'uploads#authorize',
constraints: { model: /personal_snippet|user/ }
# Alert Metric Images
get "-/system/:model/:mounted_as/:id/:filename",
to: "uploads#show",
constraints: { model: /alert_management_metric_image/, mounted_as: /file/, filename: %r{[^/]+} },
as: 'alert_metric_image_upload'
end
# Redirect old note attachments path to new uploads path.

View file

@ -0,0 +1,26 @@
# frozen_string_literal: true
class AddArkoseSettingsToApplicationSettings < Gitlab::Database::Migration[1.0]
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20220405203843_add_text_limit_to_arkose_verify_url_application_settings.rb
def up
add_column :application_settings, :encrypted_arkose_labs_public_api_key, :binary
add_column :application_settings, :encrypted_arkose_labs_public_api_key_iv, :binary
add_column :application_settings, :encrypted_arkose_labs_private_api_key, :binary
add_column :application_settings, :encrypted_arkose_labs_private_api_key_iv, :binary
add_column :application_settings, :arkose_labs_verify_api_url, :text
end
# rubocop:enable Migration/AddLimitToTextColumns
def down
remove_column :application_settings, :encrypted_arkose_labs_public_api_key
remove_column :application_settings, :encrypted_arkose_labs_public_api_key_iv
remove_column :application_settings, :encrypted_arkose_labs_private_api_key
remove_column :application_settings, :encrypted_arkose_labs_private_api_key_iv
remove_column :application_settings, :arkose_labs_verify_api_url
end
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddTextLimitToArkoseVerifyUrlApplicationSettings < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_text_limit :application_settings, :arkose_labs_verify_api_url, 255
end
def down
remove_text_limit :application_settings, :arkose_labs_verify_api_url
end
end

View file

@ -0,0 +1 @@
0835eaaf3e355f98783a11098a37b894b581176d98c39cdfd3be44e2447fe232

View file

@ -0,0 +1 @@
ac1892c5f2131e41774cadc8799cb5fb2c7d36fe567850fc1251a23c2d454695

View file

@ -11265,6 +11265,11 @@ CREATE TABLE application_settings (
database_grafana_api_url text,
database_grafana_tag text,
public_runner_releases_url text DEFAULT 'https://gitlab.com/api/v4/projects/gitlab-org%2Fgitlab-runner/releases'::text NOT NULL,
encrypted_arkose_labs_public_api_key bytea,
encrypted_arkose_labs_public_api_key_iv bytea,
encrypted_arkose_labs_private_api_key bytea,
encrypted_arkose_labs_private_api_key_iv bytea,
arkose_labs_verify_api_url text,
delete_inactive_projects boolean DEFAULT false NOT NULL,
inactive_projects_delete_after_months integer DEFAULT 2 NOT NULL,
inactive_projects_min_size_mb integer DEFAULT 0 NOT NULL,
@ -11299,7 +11304,8 @@ CREATE TABLE application_settings (
CONSTRAINT check_d820146492 CHECK ((char_length(spam_check_endpoint_url) <= 255)),
CONSTRAINT check_e5024c8801 CHECK ((char_length(elasticsearch_username) <= 255)),
CONSTRAINT check_e5aba18f02 CHECK ((char_length(container_registry_version) <= 255)),
CONSTRAINT check_ef6176834f CHECK ((char_length(encrypted_cloud_license_auth_token_iv) <= 255))
CONSTRAINT check_ef6176834f CHECK ((char_length(encrypted_cloud_license_auth_token_iv) <= 255)),
CONSTRAINT check_f6563bc000 CHECK ((char_length(arkose_labs_verify_api_url) <= 255))
);
COMMENT ON COLUMN application_settings.content_validation_endpoint_url IS 'JiHu-specific column';

View file

@ -10023,6 +10023,7 @@ Input type for DastSiteProfile authentication.
| <a id="dastsiteprofileauthenabled"></a>`enabled` | [`Boolean`](#boolean) | Indicates whether authentication is enabled. |
| <a id="dastsiteprofileauthpassword"></a>`password` | [`String`](#string) | Redacted password to authenticate with on the target website. |
| <a id="dastsiteprofileauthpasswordfield"></a>`passwordField` | [`String`](#string) | Name of password field at the sign-in HTML form. |
| <a id="dastsiteprofileauthsubmitfield"></a>`submitField` | [`String`](#string) | Name or ID of sign-in submit button at the sign-in HTML form. |
| <a id="dastsiteprofileauthurl"></a>`url` | [`String`](#string) | The URL of the page containing the sign-in HTML form on the target website. |
| <a id="dastsiteprofileauthusername"></a>`username` | [`String`](#string) | Username to authenticate with on the target website. |
| <a id="dastsiteprofileauthusernamefield"></a>`usernameField` | [`String`](#string) | Name of username field at the sign-in HTML form. |
@ -20608,6 +20609,7 @@ Input type for DastSiteProfile authentication.
| <a id="dastsiteprofileauthinputenabled"></a>`enabled` | [`Boolean`](#boolean) | Indicates whether authentication is enabled. |
| <a id="dastsiteprofileauthinputpassword"></a>`password` | [`String`](#string) | Password to authenticate with on the target website. |
| <a id="dastsiteprofileauthinputpasswordfield"></a>`passwordField` | [`String`](#string) | Name of password field at the sign-in HTML form. |
| <a id="dastsiteprofileauthinputsubmitfield"></a>`submitField` | [`String`](#string) | Name or ID of sign-in submit button at the sign-in HTML form. |
| <a id="dastsiteprofileauthinputurl"></a>`url` | [`String`](#string) | The URL of the page containing the sign-in HTML form on the target website. |
| <a id="dastsiteprofileauthinputusername"></a>`username` | [`String`](#string) | Username to authenticate with on the target website. |
| <a id="dastsiteprofileauthinputusernamefield"></a>`usernameField` | [`String`](#string) | Name of username field at the sign-in HTML form. |

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View file

@ -21,6 +21,56 @@ GitLab supports the following security policies:
- [Scan Result Policy](scan-result-policies.md)
- [Container Network Policy](#container-network-policy) (DEPRECATED)
## Security policy project
All security policies are stored as YAML in a separate security policy project that gets linked to
the development project. This association can be a one-to-many relationship, allowing one security
policy project to apply to multiple development projects. Linked projects are not required to be in
the same group as the development projects to which they are linked.
![Security Policy Project Linking Diagram](img/association_diagram.png)
Although it is possible to have one project linked to itself and to serve as both the development
project and the security policy project, this is not recommended. Keeping the security policy
project separate from the development project allows for complete separation of duties between
security/compliance teams and development teams.
All security policies are stored in the `.gitlab/security-policies/policy.yml` YAML file inside the
linked security policy project. The format for this YAML is specific to the type of policy that is
stored there. Examples and schema information are available for the following policy types:
- [Scan execution policy](scan-execution-policies.md#example-security-policies-project)
- [Scan result policy](scan-result-policies.md#example-security-scan-result-policies-project)
Policies created in this project are applied through a background job that runs once every 10
minutes. Allow up to 10 minutes for any policy changes committed to this project to take effect.
### Managing the linked security policy project
NOTE:
Only project Owners have the [permissions](../../permissions.md#project-members-permissions)
to select, edit, and unlink a security policy project.
As a project owner, take the following steps to create or edit an association between your current
project and a project that you would like to designate as the security policy project:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Policies**.
1. Select **Edit Policy Project**, and search for and select the
project you would like to link from the dropdown menu.
1. Select **Save**.
To unlink a security policy project, follow the same steps but instead select the trash can icon in
the modal.
![Security Policy Project](img/security_policy_project_v14_6.png)
### Viewing the linked security policy project
All users who have access to the project policy page and are not project owners will instead view a
button linking out to the associated security policy project. If no security policy project has been
associated then the linking button does not appear.
## Policy management
The Policies page displays deployed
@ -57,6 +107,7 @@ You can use the policy editor to create, edit, and delete policies:
1. On the top bar, select **Menu > Projects** and find your group.
1. On the left sidebar, select **Security & Compliance > Policies**.
- To create a new policy, select **New policy** which is located in the **Policies** page's header.
You can then select which type of policy to create.
- To edit an existing policy, select **Edit policy** in the selected policy drawer.
The policy editor has two modes:
@ -78,44 +129,12 @@ by the Rule mode, Rule mode is automatically
disabled. If the YAML is incorrect, you must use YAML
mode to fix your policy before Rule mode is available again.
## Security Policies project
NOTE:
We recommend using the [Security Policies project](#security-policies-project)
exclusively for managing policies for the project. Do not add your application's source code to such
projects.
The Security Policies feature is a repository to store policies. All security policies are stored in
the `.gitlab/security-policies/policy.yml` YAML file. The format for this YAML is specific to the type of policy that is being stored there. Examples and schema information are available for the following policy types:
- [Scan execution policy](scan-execution-policies.md#example-security-policies-project)
- [Scan result policy](scan-result-policies.md#example-security-scan-result-policies-project)
Policies created in this project are applied through a background job that runs once every 10
minutes. Allow up to 10 minutes for any policy changes committed to this project to take effect.
## Security Policy project selection
NOTE:
Only project Owners have the [permissions](../../permissions.md#project-members-permissions)
to select Security Policy Project.
When the Security Policy project is created and policies are created within that repository, you
must create an association between that project and the project you want to apply policies to:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Policies**.
1. Select **Edit Policy Project**, and search for and select the
project you would like to link from the dropdown menu.
1. Select **Save**.
![Security Policy Project](img/security_policy_project_v14_6.png)
### Unlink Security Policy projects
Project owners can unlink Security Policy projects from development projects. To do this, follow
the steps described in [Security Policy project selection](#security-policy-project-selection),
but select the trash can icon in the modal.
When you finish creating or editing your policy, save and apply it by selecting the
**Configure with a merge request** button and then merging the resulting merge request. When you
press this button, the policy YAML is validated and any resulting errors are displayed.
Additionally, if you are a project owner and a security policy project has not been previously
associated with this project, then a new project is created and associated automatically at the same
time that the first policy merge request is created.
## Scan execution policies

View file

@ -132,8 +132,8 @@ Note the following:
## Example security policies project
You can use this example in a `.gitlab/security-policies/policy.yml`, as described in
[Security policies project](index.md#security-policies-project).
You can use this example in a `.gitlab/security-policies/policy.yml` file stored in a
[security policy project](index.md#security-policy-project):
```yaml
---

View file

@ -90,8 +90,8 @@ Requirements and limitations:
## Example security scan result policies project
You can use this example in a `.gitlab/security-policies/policy.yml`, as described in
[Security policies project](index.md#security-policies-project):
You can use this example in a `.gitlab/security-policies/policy.yml` file stored in a
[security policy project](index.md#security-policy-project):
```yaml
---

View file

@ -26,8 +26,8 @@ when searching in:
- Comments
- Code
- Commits
- Wiki (except [group wikis](../project/wiki/group.md))
- Users
- Wiki (except [group wikis](../project/wiki/group.md))
The Advanced Search can be useful in various scenarios:
@ -76,6 +76,7 @@ its performance:
| Commits | `global_search_commits_tab` | When enabled, the global search includes commits as part of the search. |
| Issues | `global_search_issues_tab` | When enabled, the global search includes issues as part of the search. |
| Merge Requests | `global_search_merge_requests_tab` | When enabled, the global search includes merge requests as part of the search. |
| Users | `global_search_users_tab` | When enabled, the global search includes users as part of the search. |
| Wiki | `global_search_wiki_tab` | When enabled, the global search includes wiki as part of the search. [Group wikis](../project/wiki/group.md) are not included. |
## Global Search validation

View file

@ -41,6 +41,7 @@ in the search field in the upper right corner:
> - Filtering by iterations was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6.
> - Filtering by iterations was moved from GitLab Ultimate to GitLab Premium in 13.9.
> - Filtering by type was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/322755) in GitLab 13.10 [with a flag](../../administration/feature_flags.md) named `vue_issues_list`. Disabled by default.
> - Filtering by type was [enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/322755) in GitLab 14.10.
> - Filtering by attention request was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343528) in GitLab 14.10 [with a flag](../../administration/feature_flags.md) named `mr_attention_requests`. Disabled by default.
Follow these steps to filter the **Issues** and **Merge requests** list pages in projects and
@ -59,12 +60,6 @@ groups:
- My-reaction
- Release
- Type
FLAG:
On self-managed GitLab, by default filtering by type is not available.
To make it available per group, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `vue_issues_list`.
On GitLab.com, this feature is not available.
- Weight
- Search for this text
1. Select or type the operator to use for filtering the attribute. The following operators are

View file

@ -0,0 +1,137 @@
# frozen_string_literal: true
module API
class AlertManagementAlerts < ::API::Base
feature_category :incident_management
params do
requires :id, type: String, desc: 'The ID of a project'
requires :alert_iid, type: Integer, desc: 'The IID of the Alert'
end
resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/alert_management_alerts/:alert_iid/metric_images' do
post 'authorize' do
authorize!(:upload_alert_management_metric_image, find_project_alert(request.params[:alert_iid]))
require_gitlab_workhorse!
::Gitlab::Workhorse.verify_api_request!(request.headers)
status 200
content_type ::Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
params = {
has_length: false,
maximum_size: ::AlertManagement::MetricImage::MAX_FILE_SIZE.to_i
}
::MetricImageUploader.workhorse_authorize(**params)
end
desc 'Upload a metric image for an alert' do
success Entities::MetricImage
end
params do
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The image file to be uploaded'
optional :url, type: String, desc: 'The url to view more metric info'
optional :url_text, type: String, desc: 'A description of the image or URL'
end
post do
require_gitlab_workhorse!
bad_request!('File is too large') if max_file_size_exceeded?
alert = find_project_alert(params[:alert_iid])
authorize!(:upload_alert_management_metric_image, alert)
upload = ::AlertManagement::MetricImages::UploadService.new(
alert,
current_user,
params.slice(:file, :url, :url_text)
).execute
if upload.success?
present upload.payload[:metric],
with: Entities::MetricImage,
current_user: current_user,
project: user_project
else
render_api_error!(upload.message, upload.http_status)
end
end
desc 'Metric Images for alert'
get do
alert = find_project_alert(params[:alert_iid])
if can?(current_user, :read_alert_management_metric_image, alert)
present alert.metric_images.order_created_at_asc, with: Entities::MetricImage
else
render_api_error!('Alert not found', 404)
end
end
desc 'Update a metric image for an alert' do
success Entities::MetricImage
end
params do
requires :metric_image_id, type: Integer, desc: 'The ID of metric image'
optional :url, type: String, desc: 'The url to view more metric info'
optional :url_text, type: String, desc: 'A description of the image or URL'
end
put ':metric_image_id' do
alert = find_project_alert(params[:alert_iid])
authorize!(:update_alert_management_metric_image, alert)
render_api_error!('Feature not available', 403) unless alert.metric_images_available?
metric_image = alert.metric_images.find_by_id(params[:metric_image_id])
render_api_error!('Metric image not found', 404) unless metric_image
if metric_image.update(params.slice(:url, :url_text))
present metric_image, with: Entities::MetricImage, current_user: current_user, project: user_project
else
unprocessable_entity!('Metric image could not be updated')
end
end
desc 'Remove a metric image for an alert' do
success Entities::MetricImage
end
params do
requires :metric_image_id, type: Integer, desc: 'The ID of metric image'
end
delete ':metric_image_id' do
alert = find_project_alert(params[:alert_iid])
authorize!(:destroy_alert_management_metric_image, alert)
render_api_error!('Feature not available', 403) unless alert.metric_images_available?
metric_image = alert.metric_images.find_by_id(params[:metric_image_id])
render_api_error!('Metric image not found', 404) unless metric_image
if metric_image.destroy
no_content!
else
unprocessable_entity!('Metric image could not be deleted')
end
end
end
end
helpers do
def find_project_alert(iid, project_id = nil)
project = project_id ? find_project!(project_id) : user_project
::AlertManagement::AlertsFinder.new(current_user, project, { iid: [iid] }).execute.first
end
def max_file_size_exceeded?
params[:file].size > ::AlertManagement::MetricImage::MAX_FILE_SIZE
end
end
end
end

View file

@ -163,6 +163,7 @@ module API
mount ::API::Admin::InstanceClusters
mount ::API::Admin::PlanLimits
mount ::API::Admin::Sidekiq
mount ::API::AlertManagementAlerts
mount ::API::Appearance
mount ::API::Applications
mount ::API::Avatar

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
module API
module Entities
class MetricImage < Grape::Entity
expose :id, :created_at, :filename, :file_path, :url, :url_text
end
end
end

View file

@ -5,6 +5,7 @@ module Gitlab
module OAuth
class Provider
LABELS = {
"alicloud" => "AliCloud",
"dingtalk" => "DingTalk",
"github" => "GitHub",
"gitlab" => "GitLab.com",

View file

@ -27,12 +27,12 @@ variables:
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
#
FUZZAPI_VERSION: "1"
FUZZAPI_VERSION_TAG: ""
FUZZAPI_IMAGE_SUFFIX: ""
FUZZAPI_IMAGE: api-fuzzing
apifuzzer_fuzz:
stage: fuzz
image: $SECURE_ANALYZERS_PREFIX/$FUZZAPI_IMAGE:$FUZZAPI_VERSION$FUZZAPI_VERSION_TAG
image: $SECURE_ANALYZERS_PREFIX/$FUZZAPI_IMAGE:$FUZZAPI_VERSION$FUZZAPI_IMAGE_SUFFIX
allow_failure: true
rules:
- if: $API_FUZZING_DISABLED
@ -43,7 +43,7 @@ apifuzzer_fuzz:
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
FUZZAPI_VERSION_TAG: "-fips"
FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH
script:
- /peach/analyzer-fuzz-api

View file

@ -27,12 +27,12 @@ variables:
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
#
DAST_API_VERSION: "1"
DAST_API_VERSION_TAG: ""
DAST_API_IMAGE_SUFFIX: ""
DAST_API_IMAGE: api-fuzzing
dast_api:
stage: dast
image: $SECURE_ANALYZERS_PREFIX/$DAST_API_IMAGE:$DAST_API_VERSION$DAST_API_VERSION_TAG
image: $SECURE_ANALYZERS_PREFIX/$DAST_API_IMAGE:$DAST_API_VERSION$DAST_API_IMAGE_SUFFIX
allow_failure: true
rules:
- if: $DAST_API_DISABLED
@ -43,7 +43,7 @@ dast_api:
- if: $CI_COMMIT_BRANCH &&
$CI_GITLAB_FIPS_MODE == "true"
variables:
DAST_API_VERSION_TAG: "-fips"
DAST_API_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH
script:
- /peach/analyzer-dast-api

View file

@ -22,7 +22,7 @@ module Gitlab
'frame_src' => ContentSecurityPolicy::Directives.frame_src,
'img_src' => "'self' data: blob: http: https:",
'manifest_src' => "'self'",
'media_src' => "'self'",
'media_src' => "'self' data:",
'script_src' => ContentSecurityPolicy::Directives.script_src,
'style_src' => "'self' 'unsafe-inline'",
'worker_src' => "#{Gitlab::Utils.append_path(Gitlab.config.gitlab.url, 'assets/')} blob: data:",

View file

@ -6,6 +6,14 @@ module Gitlab
include Gitlab::Utils::StrongMemoize
class DiffFile < Gitlab::Diff::File
RENDERED_TIMEOUT_BACKGROUND = 10.seconds
RENDERED_TIMEOUT_FOREGROUND = 1.5.seconds
BACKGROUND_EXECUTION = 'background'
FOREGROUND_EXECUTION = 'foreground'
LOG_IPYNBDIFF_GENERATED = 'IPYNB_DIFF_GENERATED'
LOG_IPYNBDIFF_TIMEOUT = 'IPYNB_DIFF_TIMEOUT'
LOG_IPYNBDIFF_INVALID = 'IPYNB_DIFF_INVALID'
attr_reader :source_diff
delegate :repository, :diff_refs, :fallback_diff_refs, :unfolded, :unique_identifier,
@ -52,14 +60,17 @@ module Gitlab
def notebook_diff
strong_memoize(:notebook_diff) do
Gitlab::AppLogger.info({ message: 'IPYNB_DIFF_GENERATED' })
IpynbDiff.diff(source_diff.old_blob&.data, source_diff.new_blob&.data,
raise_if_invalid_nb: true,
diffy_opts: { include_diff_info: true })
Timeout.timeout(timeout_time) do
IpynbDiff.diff(source_diff.old_blob&.data, source_diff.new_blob&.data,
raise_if_invalid_nb: true, diffy_opts: { include_diff_info: true })&.tap do
log_event(LOG_IPYNBDIFF_GENERATED)
end
end
rescue Timeout::Error => e
rendered_timeout.increment(source: Gitlab::Runtime.sidekiq? ? BACKGROUND_EXECUTION : FOREGROUND_EXECUTION)
log_event(LOG_IPYNBDIFF_TIMEOUT, e)
rescue IpynbDiff::InvalidNotebookError, IpynbDiff::InvalidTokenError => e
Gitlab::ErrorTracking.log_exception(e)
nil
log_event(LOG_IPYNBDIFF_INVALID, e)
end
end
@ -116,6 +127,23 @@ module Gitlab
[removals, additions]
end
def rendered_timeout
@rendered_timeout ||= Gitlab::Metrics.counter(
:ipynb_semantic_diff_timeouts_total,
'Counts the times notebook diff rendering timed out'
)
end
def timeout_time
Gitlab::Runtime.sidekiq? ? RENDERED_TIMEOUT_BACKGROUND : RENDERED_TIMEOUT_FOREGROUND
end
def log_event(message, error = nil)
Gitlab::AppLogger.info({ message: message })
Gitlab::ErrorTracking.track_exception(error) if error
nil
end
end
end
end

View file

@ -4178,9 +4178,6 @@ msgstr ""
msgid "An error occurred while uploading the file. Please try again."
msgstr ""
msgid "An error occurred while uploading the image. Please try again."
msgstr ""
msgid "An error occurred while validating group path"
msgstr ""
@ -6559,9 +6556,6 @@ msgstr ""
msgid "CI variables"
msgstr ""
msgid "CI will run using the credentials assigned above."
msgstr ""
msgid "CI/CD"
msgstr ""
@ -18881,7 +18875,7 @@ msgstr ""
msgid "If checked, new group memberships and permissions can only be added via LDAP synchronization"
msgstr ""
msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "If disabled, only administrators can configure repository mirroring."
@ -39907,7 +39901,7 @@ msgstr ""
msgid "Trigger pipelines for mirror updates"
msgstr ""
msgid "Trigger pipelines when branches or tags are updated in the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load."
msgid "Trigger pipelines when branches or tags are updated in the upstream repository. Depending on the activity of the upstream repository, this may greatly increase the load on your CI runners. Only enable this if you know they can handle the load. %{strong_start}CI will run using the credentials assigned above.%{strong_end} %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Trigger removed."

View file

@ -31,7 +31,7 @@ RSpec.describe Profiles::AccountsController do
end
end
[:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :authentiq, :dingtalk].each do |provider|
[:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :authentiq, :dingtalk, :alicloud].each do |provider|
describe "#{provider} provider" do
let(:user) { create(:omniauth_user, provider: provider.to_s) }

View file

@ -211,6 +211,7 @@ RSpec.describe SearchController do
:global_search_merge_requests_tab | 'merge_requests'
:global_search_wiki_tab | 'wiki_blobs'
:global_search_commits_tab | 'commits'
:global_search_users_tab | 'users'
end
with_them do

View file

@ -701,6 +701,24 @@ RSpec.describe UploadsController do
end
end
end
context 'when viewing alert metric images' do
let!(:user) { create(:user) }
let!(:project) { create(:project) }
let(:alert) { create(:alert_management_alert, project: project) }
let(:metric_image) { create(:alert_metric_image, alert: alert) }
before do
project.add_developer(user)
sign_in(user)
end
it "responds with status 200" do
get :show, params: { model: "alert_management_metric_image", mounted_as: 'file', id: metric_image.id, filename: metric_image.filename }
expect(response).to have_gitlab_http_status(:ok)
end
end
end
def post_authorize(verified: true)

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
FactoryBot.define do
factory :alert_metric_image, class: 'AlertManagement::MetricImage' do
association :alert, factory: :alert_management_alert
url { generate(:url) }
trait :local do
file_store { ObjectStorage::Store::LOCAL }
end
after(:build) do |image|
image.file = fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg')
end
end
end

View file

@ -16,7 +16,7 @@ RSpec.describe 'OAuth Login', :js, :allow_forgery_protection do
end
providers = [:github, :twitter, :bitbucket, :gitlab, :google_oauth2,
:facebook, :cas3, :auth0, :authentiq, :salesforce, :dingtalk]
:facebook, :cas3, :auth0, :authentiq, :salesforce, :dingtalk, :alicloud]
around do |example|
with_omniauth_full_host { example.run }

View file

@ -8,26 +8,14 @@ RSpec.describe 'Project wikis', :js do
let(:wiki) { create(:project_wiki, user: user, project: project) }
let(:project) { create(:project, namespace: user.namespace, creator: user) }
shared_examples 'wiki feature tests' do
it_behaves_like 'User creates wiki page'
it_behaves_like 'User deletes wiki page'
it_behaves_like 'User previews wiki changes'
it_behaves_like 'User updates wiki page'
it_behaves_like 'User uses wiki shortcuts'
it_behaves_like 'User views AsciiDoc page with includes'
it_behaves_like 'User views a wiki page'
it_behaves_like 'User views wiki pages'
it_behaves_like 'User views wiki sidebar'
it_behaves_like 'User views Git access wiki page'
end
it_behaves_like 'wiki feature tests'
context 'when feature flag :wiki_async_load is disabled' do
before do
stub_feature_flags(wiki_async_load: false)
end
it_behaves_like 'wiki feature tests'
end
it_behaves_like 'User creates wiki page'
it_behaves_like 'User deletes wiki page'
it_behaves_like 'User previews wiki changes'
it_behaves_like 'User updates wiki page'
it_behaves_like 'User uses wiki shortcuts'
it_behaves_like 'User views AsciiDoc page with includes'
it_behaves_like 'User views a wiki page'
it_behaves_like 'User views wiki pages'
it_behaves_like 'User views wiki sidebar'
it_behaves_like 'User views Git access wiki page'
end

View file

@ -1,21 +1,24 @@
import { GlLoadingIcon } from '@gitlab/ui';
import { NodeViewWrapper } from '@tiptap/vue-2';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ImageWrapper from '~/content_editor/components/wrappers/image.vue';
import MediaWrapper from '~/content_editor/components/wrappers/media.vue';
describe('content/components/wrappers/image', () => {
describe('content/components/wrappers/media', () => {
let wrapper;
const createWrapper = async (nodeAttrs = {}) => {
wrapper = shallowMountExtended(ImageWrapper, {
wrapper = shallowMountExtended(MediaWrapper, {
propsData: {
node: {
attrs: nodeAttrs,
type: {
name: 'image',
},
},
},
});
};
const findImage = () => wrapper.findByTestId('image');
const findMedia = () => wrapper.findByTestId('media');
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
afterEach(() => {
@ -33,7 +36,7 @@ describe('content/components/wrappers/image', () => {
createWrapper({ src });
expect(findImage().attributes().src).toBe(src);
expect(findMedia().attributes().src).toBe(src);
});
describe('when uploading', () => {
@ -45,8 +48,8 @@ describe('content/components/wrappers/image', () => {
expect(findLoadingIcon().exists()).toBe(true);
});
it('adds gl-opacity-5 class selector to image', () => {
expect(findImage().classes()).toContain('gl-opacity-5');
it('adds gl-opacity-5 class selector to the media tag', () => {
expect(findMedia().classes()).toContain('gl-opacity-5');
});
});
@ -59,8 +62,8 @@ describe('content/components/wrappers/image', () => {
expect(findLoadingIcon().exists()).toBe(false);
});
it('does not add gl-opacity-5 class selector to image', () => {
expect(findImage().classes()).not.toContain('gl-opacity-5');
it('does not add gl-opacity-5 class selector to the media tag', () => {
expect(findMedia().classes()).not.toContain('gl-opacity-5');
});
});
});

View file

@ -1,7 +1,10 @@
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
import Attachment from '~/content_editor/extensions/attachment';
import Image from '~/content_editor/extensions/image';
import Audio from '~/content_editor/extensions/audio';
import Video from '~/content_editor/extensions/video';
import Link from '~/content_editor/extensions/link';
import Loading from '~/content_editor/extensions/loading';
import { VARIANT_DANGER } from '~/flash';
@ -14,6 +17,23 @@ const PROJECT_WIKI_ATTACHMENT_IMAGE_HTML = `<p data-sourcepos="1:1-1:27" dir="au
<img alt="test-file" class="lazy" data-src="/group1/project1/-/wikis/test-file.png" data-canonical-src="test-file.png">
</a>
</p>`;
const PROJECT_WIKI_ATTACHMENT_VIDEO_HTML = `<p data-sourcepos="1:1-1:132" dir="auto">
<span class="media-container video-container">
<video src="/group1/project1/-/wikis/test-file.mp4" controls="true" data-setup="{}" data-title="test-file" width="400" preload="metadata" data-canonical-src="test-file.mp4">
</video>
<a href="/himkp/test/-/wikis/test-file.mp4" target="_blank" rel="noopener noreferrer" title="Download 'test-file'" data-canonical-src="test-file.mp4">test-file</a>
</span>
</p>`;
const PROJECT_WIKI_ATTACHMENT_AUDIO_HTML = `<p data-sourcepos="3:1-3:74" dir="auto">
<span class="media-container audio-container">
<audio src="/himkp/test/-/wikis/test-file.mp3" controls="true" data-setup="{}" data-title="test-file" data-canonical-src="test-file.mp3">
</audio>
<a href="/himkp/test/-/wikis/test-file.mp3" target="_blank" rel="noopener noreferrer" title="Download 'test-file'" data-canonical-src="test-file.mp3">test-file</a>
</span>
</p>`;
const PROJECT_WIKI_ATTACHMENT_LINK_HTML = `<p data-sourcepos="1:1-1:26" dir="auto">
<a href="/group1/project1/-/wikis/test-file.zip" data-canonical-src="test-file.zip">test-file</a>
</p>`;
@ -23,6 +43,8 @@ describe('content_editor/extensions/attachment', () => {
let doc;
let p;
let image;
let audio;
let video;
let loading;
let link;
let renderMarkdown;
@ -31,15 +53,18 @@ describe('content_editor/extensions/attachment', () => {
const uploadsPath = '/uploads/';
const imageFile = new File(['foo'], 'test-file.png', { type: 'image/png' });
const audioFile = new File(['foo'], 'test-file.mp3', { type: 'audio/mpeg' });
const videoFile = new File(['foo'], 'test-file.mp4', { type: 'video/mp4' });
const attachmentFile = new File(['foo'], 'test-file.zip', { type: 'application/zip' });
const expectDocumentAfterTransaction = ({ number, expectedDoc, action }) => {
return new Promise((resolve) => {
let counter = 1;
const handleTransaction = () => {
const handleTransaction = async () => {
if (counter === number) {
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON());
tiptapEditor.off('update', handleTransaction);
await waitForPromises();
resolve();
}
@ -60,18 +85,22 @@ describe('content_editor/extensions/attachment', () => {
Loading,
Link,
Image,
Audio,
Video,
Attachment.configure({ renderMarkdown, uploadsPath, eventHub }),
],
});
({
builders: { doc, p, image, loading, link },
builders: { doc, p, image, audio, video, loading, link },
} = createDocBuilder({
tiptapEditor,
names: {
loading: { markType: Loading.name },
image: { nodeType: Image.name },
link: { nodeType: Link.name },
audio: { nodeType: Audio.name },
video: { nodeType: Video.name },
},
}));
@ -103,17 +132,22 @@ describe('content_editor/extensions/attachment', () => {
tiptapEditor.commands.setContent(initialDoc.toJSON());
});
describe('when the file has image mime type', () => {
const base64EncodedFile = 'data:image/png;base64,Zm9v';
describe.each`
nodeType | mimeType | html | file | mediaType
${'image'} | ${'image/png'} | ${PROJECT_WIKI_ATTACHMENT_IMAGE_HTML} | ${imageFile} | ${(attrs) => image(attrs)}
${'audio'} | ${'audio/mpeg'} | ${PROJECT_WIKI_ATTACHMENT_AUDIO_HTML} | ${audioFile} | ${(attrs) => audio(attrs)}
${'video'} | ${'video/mp4'} | ${PROJECT_WIKI_ATTACHMENT_VIDEO_HTML} | ${videoFile} | ${(attrs) => video(attrs)}
`('when the file has $nodeType mime type', ({ mimeType, html, file, mediaType }) => {
const base64EncodedFile = `data:${mimeType};base64,Zm9v`;
beforeEach(() => {
renderMarkdown.mockResolvedValue(PROJECT_WIKI_ATTACHMENT_IMAGE_HTML);
renderMarkdown.mockResolvedValue(html);
});
describe('when uploading succeeds', () => {
const successResponse = {
link: {
markdown: '![test-file](test-file.png)',
markdown: `![test-file](${file.name})`,
},
};
@ -121,21 +155,21 @@ describe('content_editor/extensions/attachment', () => {
mock.onPost().reply(httpStatus.OK, successResponse);
});
it('inserts an image with src set to the encoded image file and uploading true', async () => {
const expectedDoc = doc(p(image({ uploading: true, src: base64EncodedFile })));
it('inserts a media content with src set to the encoded content and uploading true', async () => {
const expectedDoc = doc(p(mediaType({ uploading: true, src: base64EncodedFile })));
await expectDocumentAfterTransaction({
number: 1,
expectedDoc,
action: () => tiptapEditor.commands.uploadAttachment({ file: imageFile }),
action: () => tiptapEditor.commands.uploadAttachment({ file }),
});
});
it('updates the inserted image with canonicalSrc when upload is successful', async () => {
it('updates the inserted content with canonicalSrc when upload is successful', async () => {
const expectedDoc = doc(
p(
image({
canonicalSrc: 'test-file.png',
mediaType({
canonicalSrc: file.name,
src: base64EncodedFile,
alt: 'test-file',
uploading: false,
@ -146,7 +180,7 @@ describe('content_editor/extensions/attachment', () => {
await expectDocumentAfterTransaction({
number: 2,
expectedDoc,
action: () => tiptapEditor.commands.uploadAttachment({ file: imageFile }),
action: () => tiptapEditor.commands.uploadAttachment({ file }),
});
});
});
@ -162,16 +196,16 @@ describe('content_editor/extensions/attachment', () => {
await expectDocumentAfterTransaction({
number: 2,
expectedDoc,
action: () => tiptapEditor.commands.uploadAttachment({ file: imageFile }),
action: () => tiptapEditor.commands.uploadAttachment({ file }),
});
});
it('emits an alert event that includes an error message', (done) => {
tiptapEditor.commands.uploadAttachment({ file: imageFile });
tiptapEditor.commands.uploadAttachment({ file });
eventHub.$on('alert', ({ message, variant }) => {
expect(variant).toBe(VARIANT_DANGER);
expect(message).toBe('An error occurred while uploading the image. Please try again.');
expect(message).toBe('An error occurred while uploading the file. Please try again.');
done();
});
});

View file

@ -467,6 +467,12 @@ RSpec.describe SearchHelper do
describe '#show_user_search_tab?' do
subject { show_user_search_tab? }
let(:current_user) { build(:user) }
before do
allow(self).to receive(:current_user).and_return(current_user)
end
context 'when project search' do
before do
@project = :some_project
@ -481,11 +487,14 @@ RSpec.describe SearchHelper do
end
end
context 'when not project search' do
context 'when group search' do
before do
@group = :some_group
end
context 'when current_user can read_users_list' do
before do
allow(self).to receive(:current_user).and_return(:the_current_user)
allow(self).to receive(:can?).with(:the_current_user, :read_users_list).and_return(true)
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(true)
end
it { is_expected.to eq(true) }
@ -493,8 +502,33 @@ RSpec.describe SearchHelper do
context 'when current_user cannot read_users_list' do
before do
allow(self).to receive(:current_user).and_return(:the_current_user)
allow(self).to receive(:can?).with(:the_current_user, :read_users_list).and_return(false)
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(false)
end
it { is_expected.to eq(false) }
end
end
context 'when global search' do
context 'when current_user can read_users_list' do
before do
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(true)
end
it { is_expected.to eq(true) }
context 'when global_search_user_tab feature flag is disabled' do
before do
stub_feature_flags(global_search_users_tab: false)
end
it { is_expected.to eq(false) }
end
end
context 'when current_user cannot read_users_list' do
before do
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(false)
end
it { is_expected.to eq(false) }

View file

@ -63,6 +63,28 @@ RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFile do
expect(nb_file.diff).to be_nil
end
end
context 'timeout' do
it 'utilizes timeout for web' do
expect(Timeout).to receive(:timeout).with(described_class::RENDERED_TIMEOUT_FOREGROUND).and_call_original
nb_file.diff
end
it 'falls back to nil on timeout' do
allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
expect(Timeout).to receive(:timeout).and_raise(Timeout::Error)
expect(nb_file.diff).to be_nil
end
it 'utilizes longer timeout for sidekiq' do
allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
expect(Timeout).to receive(:timeout).with(described_class::RENDERED_TIMEOUT_BACKGROUND).and_call_original
nb_file.diff
end
end
end
describe '#has_renderable?' do

View file

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe AlertManagement::MetricImage do
subject { build(:alert_metric_image) }
describe 'associations' do
it { is_expected.to belong_to(:alert) }
end
describe 'validations' do
it { is_expected.to be_valid }
it { is_expected.to validate_presence_of(:file) }
it { is_expected.to validate_length_of(:url).is_at_most(255) }
it { is_expected.to validate_length_of(:url_text).is_at_most(128) }
end
describe '.available_for?' do
subject { described_class.available_for?(issue.project) }
let_it_be_with_refind(:issue) { create(:issue) }
it { is_expected.to eq(true) }
end
end

View file

@ -1585,6 +1585,31 @@ RSpec.describe Ci::Build do
it { is_expected.to eq('review/x') }
end
context 'when environment name uses a nested variable' do
let(:yaml_variables) do
[
{ key: 'ENVIRONMENT_NAME', value: '${CI_COMMIT_REF_NAME}' }
]
end
let(:build) do
create(:ci_build,
ref: 'master',
yaml_variables: yaml_variables,
environment: 'review/$ENVIRONMENT_NAME')
end
it { is_expected.to eq('review/master') }
context 'when the FF ci_expand_environment_name_and_url is disabled' do
before do
stub_feature_flags(ci_expand_environment_name_and_url: false)
end
it { is_expected.to eq('review/${CI_COMMIT_REF_NAME}') }
end
end
end
describe '#expanded_kubernetes_namespace' do

View file

@ -1560,7 +1560,7 @@ RSpec.describe User do
end
it 'adds the confirmed primary email to emails' do
expect(user.emails.confirmed.map(&:email)).not_to include(user.email)
expect(user.emails.confirmed.map(&:email)).not_to include(user.unconfirmed_email)
user.confirm
@ -1619,14 +1619,23 @@ RSpec.describe User do
context 'when the email is changed but not confirmed' do
let(:user) { create(:user, email: 'primary@example.com') }
it 'does not add the new email to emails yet' do
before do
user.update!(email: 'new_primary@example.com')
end
it 'does not add the new email to emails yet' do
expect(user.unconfirmed_email).to eq('new_primary@example.com')
expect(user.email).to eq('primary@example.com')
expect(user).to be_confirmed
expect(user.emails.pluck(:email)).not_to include('new_primary@example.com')
end
it 'adds the new email to emails upon confirmation' do
user.confirm
expect(user.email).to eq('new_primary@example.com')
expect(user).to be_confirmed
expect(user.emails.pluck(:email)).to include('new_primary@example.com')
end
end
context 'when the user is created as not confirmed' do
@ -1636,6 +1645,11 @@ RSpec.describe User do
expect(user).not_to be_confirmed
expect(user.emails.pluck(:email)).not_to include('primary@example.com')
end
it 'adds the email to emails upon confirmation' do
user.confirm
expect(user.emails.pluck(:email)).to include('primary@example.com')
end
end
context 'when the user is created as confirmed' do

View file

@ -3,9 +3,10 @@
require 'spec_helper'
RSpec.describe AlertManagement::AlertPolicy, :models do
let(:alert) { create(:alert_management_alert) }
let(:project) { alert.project }
let(:user) { create(:user) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:alert) { create(:alert_management_alert, project: project, issue: incident) }
let_it_be(:incident) { nil }
subject(:policy) { described_class.new(user, alert) }
@ -21,5 +22,50 @@ RSpec.describe AlertManagement::AlertPolicy, :models do
it { is_expected.to be_allowed :read_alert_management_alert }
it { is_expected.to be_allowed :update_alert_management_alert }
end
shared_examples 'does not allow metric image reads' do
it { expect(policy).to be_disallowed(:read_alert_management_metric_image) }
end
shared_examples 'does not allow metric image updates' do
specify do
expect(policy).to be_disallowed(:upload_alert_management_metric_image)
expect(policy).to be_disallowed(:destroy_alert_management_metric_image)
end
end
shared_examples 'allows metric image reads' do
it { expect(policy).to be_allowed(:read_alert_management_metric_image) }
end
shared_examples 'allows metric image updates' do
specify do
expect(policy).to be_allowed(:upload_alert_management_metric_image)
expect(policy).to be_allowed(:destroy_alert_management_metric_image)
end
end
context 'when user is not a member' do
include_examples 'does not allow metric image reads'
include_examples 'does not allow metric image updates'
end
context 'when user is a guest' do
before do
project.add_guest(user)
end
include_examples 'does not allow metric image reads'
include_examples 'does not allow metric image updates'
end
context 'when user is a developer' do
before do
project.add_developer(user)
end
include_examples 'allows metric image reads'
include_examples 'allows metric image updates'
end
end
end

View file

@ -0,0 +1,411 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::AlertManagementAlerts do
let_it_be(:creator) { create(:user) }
let_it_be(:project) do
create(:project, :public, creator_id: creator.id, namespace: creator.namespace)
end
let_it_be(:user) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert, project: project) }
describe 'PUT /projects/:id/alert_management_alerts/:alert_iid/metric_images/authorize' do
include_context 'workhorse headers'
before do
project.add_developer(user)
end
subject do
post api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/authorize", user),
headers: workhorse_headers
end
it 'authorizes uploading with workhorse header' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
it 'rejects requests that bypassed gitlab-workhorse' do
workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
subject
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'when using remote storage' do
context 'when direct upload is enabled' do
before do
stub_uploads_object_storage(MetricImageUploader, enabled: true, direct_upload: true)
end
it 'responds with status 200, location of file remote store and object details' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response).not_to have_key('TempPath')
expect(json_response['RemoteObject']).to have_key('ID')
expect(json_response['RemoteObject']).to have_key('GetURL')
expect(json_response['RemoteObject']).to have_key('StoreURL')
expect(json_response['RemoteObject']).to have_key('DeleteURL')
expect(json_response['RemoteObject']).to have_key('MultipartUpload')
end
end
context 'when direct upload is disabled' do
before do
stub_uploads_object_storage(MetricImageUploader, enabled: true, direct_upload: false)
end
it 'handles as a local file' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response['TempPath']).to eq(MetricImageUploader.workhorse_local_upload_path)
expect(json_response['RemoteObject']).to be_nil
end
end
end
end
describe 'POST /projects/:id/alert_management_alerts/:alert_iid/metric_images' do
include WorkhorseHelpers
using RSpec::Parameterized::TableSyntax
include_context 'workhorse headers'
let(:file) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') }
let(:file_name) { 'rails_sample.jpg' }
let(:url) { 'http://gitlab.com' }
let(:url_text) { 'GitLab' }
let(:params) { { url: url, url_text: url_text } }
subject do
workhorse_finalize(
api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images", user),
method: :post,
file_key: :file,
params: params.merge(file: file),
headers: workhorse_headers,
send_rewritten_field: true
)
end
shared_examples 'can_upload_metric_image' do
it 'creates a new metric image' do
subject
expect(response).to have_gitlab_http_status(:created)
expect(json_response['filename']).to eq(file_name)
expect(json_response['url']).to eq(url)
expect(json_response['url_text']).to eq(url_text)
expect(json_response['created_at']).not_to be_nil
expect(json_response['id']).not_to be_nil
file_path_regex = %r{/uploads/-/system/alert_management_metric_image/file/\d+/#{file_name}}
expect(json_response['file_path']).to match(file_path_regex)
end
end
shared_examples 'unauthorized_upload' do
it 'disallows the upload' do
subject
expect(response).to have_gitlab_http_status(:forbidden)
expect(json_response['message']).to eq('403 Forbidden')
end
end
where(:user_role, :expected_status) do
:guest | :unauthorized_upload
:reporter | :unauthorized_upload
:developer | :can_upload_metric_image
end
with_them do
before do
# Local storage
stub_uploads_object_storage(MetricImageUploader, enabled: false)
allow_next_instance_of(MetricImageUploader) do |uploader|
allow(uploader).to receive(:file_storage?).and_return(true)
end
project.send("add_#{user_role}", user)
end
it_behaves_like "#{params[:expected_status]}"
end
context 'file size too large' do
before do
allow_next_instance_of(UploadedFile) do |upload_file|
allow(upload_file).to receive(:size).and_return(AlertManagement::MetricImage::MAX_FILE_SIZE + 1)
end
end
it 'returns an error' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to match(/File is too large/)
end
end
context 'error when saving' do
before do
project.add_developer(user)
allow_next_instance_of(::AlertManagement::MetricImages::UploadService) do |service|
error = instance_double(ServiceResponse, success?: false, message: 'some error', http_status: :bad_request)
allow(service).to receive(:execute).and_return(error)
end
end
it 'returns an error' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.body).to match(/some error/)
end
end
context 'object storage enabled' do
before do
# Object storage
stub_uploads_object_storage(MetricImageUploader)
allow_next_instance_of(MetricImageUploader) do |uploader|
allow(uploader).to receive(:file_storage?).and_return(true)
end
project.add_developer(user)
end
it_behaves_like 'can_upload_metric_image'
it 'uploads to remote storage' do
subject
last_upload = AlertManagement::MetricImage.last.uploads.last
expect(last_upload.store).to eq(::ObjectStorage::Store::REMOTE)
end
end
end
describe 'GET /projects/:id/alert_management_alerts/:alert_iid/metric_images' do
using RSpec::Parameterized::TableSyntax
let!(:image) { create(:alert_metric_image, alert: alert) }
subject { get api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images", user) }
shared_examples 'can_read_metric_image' do
it 'can read the metric images' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.first).to match(
{
id: image.id,
created_at: image.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
filename: image.filename,
file_path: image.file_path,
url: image.url,
url_text: nil
}.with_indifferent_access
)
end
end
shared_examples 'unauthorized_read' do
it 'cannot read the metric images' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
where(:user_role, :public_project, :expected_status) do
:not_member | false | :unauthorized_read
:not_member | true | :unauthorized_read
:guest | false | :unauthorized_read
:reporter | false | :unauthorized_read
:developer | false | :can_read_metric_image
end
with_them do
before do
project.send("add_#{user_role}", user) unless user_role == :not_member
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) unless public_project
end
it_behaves_like "#{params[:expected_status]}"
end
end
describe 'PUT /projects/:id/alert_management_alerts/:alert_iid/metric_images/:metric_image_id' do
using RSpec::Parameterized::TableSyntax
let!(:image) { create(:alert_metric_image, alert: alert) }
let(:params) { { url: 'http://test.example.com', url_text: 'Example website 123' } }
subject do
put api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/#{image.id}", user),
params: params
end
shared_examples 'can_update_metric_image' do
it 'can update the metric images' do
subject
expect(response).to have_gitlab_http_status(:success)
expect(json_response['url']).to eq(params[:url])
expect(json_response['url_text']).to eq(params[:url_text])
end
end
shared_examples 'unauthorized_update' do
it 'cannot update the metric image' do
subject
expect(response).to have_gitlab_http_status(:forbidden)
expect(image.reload).to eq(image)
end
end
where(:user_role, :public_project, :expected_status) do
:not_member | false | :unauthorized_update
:not_member | true | :unauthorized_update
:guest | false | :unauthorized_update
:reporter | false | :unauthorized_update
:developer | false | :can_update_metric_image
end
with_them do
before do
project.send("add_#{user_role}", user) unless user_role == :not_member
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) unless public_project
end
it_behaves_like "#{params[:expected_status]}"
end
context 'when user has access' do
before do
project.add_developer(user)
end
context 'and metric image not found' do
subject do
put api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/#{non_existing_record_id}", user) # rubocop: disable Layout/LineLength
end
it 'returns an error' do
subject
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('Metric image not found')
end
end
context 'metric image cannot be updated' do
let(:params) { { url_text: 'something_long' * 100 } }
it 'returns an error' do
subject
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response['message']).to eq('Metric image could not be updated')
end
end
end
end
describe 'DELETE /projects/:id/alert_management_alerts/:alert_iid/metric_images/:metric_image_id' do
using RSpec::Parameterized::TableSyntax
let!(:image) { create(:alert_metric_image, alert: alert) }
subject do
delete api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/#{image.id}", user)
end
shared_examples 'can delete metric image successfully' do
it 'can delete the metric images' do
subject
expect(response).to have_gitlab_http_status(:no_content)
expect { image.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
shared_examples 'unauthorized delete' do
it 'cannot delete the metric image' do
subject
expect(response).to have_gitlab_http_status(:forbidden)
expect(image.reload).to eq(image)
end
end
where(:user_role, :public_project, :expected_status) do
:not_member | false | 'unauthorized delete'
:not_member | true | 'unauthorized delete'
:guest | false | 'unauthorized delete'
:reporter | false | 'unauthorized delete'
:developer | false | 'can delete metric image successfully'
end
with_them do
before do
project.send("add_#{user_role}", user) unless user_role == :not_member
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) unless public_project
end
it_behaves_like "#{params[:expected_status]}"
end
context 'when user has access' do
before do
project.add_developer(user)
end
context 'when metric image not found' do
subject do
delete api("/projects/#{project.id}/alert_management_alerts/#{alert.iid}/metric_images/#{non_existing_record_id}", user) # rubocop: disable Layout/LineLength
end
it 'returns an error' do
subject
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('Metric image not found')
end
end
context 'when error when deleting' do
before do
allow_next_instance_of(AlertManagement::AlertsFinder) do |finder|
allow(finder).to receive(:execute).and_return([alert])
end
allow(alert).to receive_message_chain('metric_images.find_by_id') { image }
allow(image).to receive(:destroy).and_return(false)
end
it 'returns an error' do
subject
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response['message']).to eq('Metric image could not be deleted')
end
end
end
end
end

View file

@ -47,7 +47,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures do
it 'executes a limited number of queries' do
control_count = ActiveRecord::QueryRecorder.new { subject }.count
expect(control_count).to be <= 105
expect(control_count).to be <= 108
end
it 'schedules an import using a namespace' do

View file

@ -21,6 +21,17 @@ RSpec.describe 'Uploads', 'routing' do
)
end
it 'allows fetching alert metric metric images' do
expect(get('/uploads/-/system/alert_management_metric_image/file/1/test.jpg')).to route_to(
controller: 'uploads',
action: 'show',
model: 'alert_management_metric_image',
id: '1',
filename: 'test.jpg',
mounted_as: 'file'
)
end
it 'does not allow creating uploads for other models' do
unroutable_models = UploadsController::MODEL_CLASSES.keys.compact - %w(personal_snippet user)

View file

@ -0,0 +1,79 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe AlertManagement::MetricImages::UploadService do
subject(:service) { described_class.new(alert, current_user, params) }
let_it_be_with_refind(:project) { create(:project) }
let_it_be_with_refind(:alert) { create(:alert_management_alert, project: project) }
let_it_be_with_refind(:current_user) { create(:user) }
let(:params) do
{
file: fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg'),
url: 'https://www.gitlab.com'
}
end
describe '#execute' do
subject { service.execute }
shared_examples 'uploads the metric' do
it 'uploads the metric and returns a success' do
expect { subject }.to change(AlertManagement::MetricImage, :count).by(1)
expect(subject.success?).to eq(true)
expect(subject.payload).to match({ metric: instance_of(AlertManagement::MetricImage), alert: alert })
end
end
shared_examples 'no metric saved, an error given' do |message|
it 'returns an error and does not upload', :aggregate_failures do
expect(subject.success?).to eq(false)
expect(subject.message).to match(a_string_matching(message))
expect(AlertManagement::MetricImage.count).to eq(0)
end
end
context 'user does not have permissions' do
it_behaves_like 'no metric saved, an error given', 'You are not authorized to upload metric images'
end
context 'user has permissions' do
before_all do
project.add_developer(current_user)
end
it_behaves_like 'uploads the metric'
context 'no url given' do
let(:params) do
{
file: fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg')
}
end
it_behaves_like 'uploads the metric'
end
context 'record invalid' do
let(:params) do
{
file: fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain'),
url: 'https://www.gitlab.com'
}
end
it_behaves_like 'no metric saved, an error given', /File does not have a supported extension. Only png, jpg, jpeg, gif, bmp, tiff, ico, and webp are supported/ # rubocop: disable Layout/LineLength
end
context 'user is guest' do
before_all do
project.add_guest(current_user)
end
it_behaves_like 'no metric saved, an error given', 'You are not authorized to upload metric images'
end
end
end
end

View file

@ -286,6 +286,37 @@ RSpec.describe Deployments::UpdateEnvironmentService do
end
end
context 'when environment url uses a nested variable' do
let(:yaml_variables) do
[
{ key: 'MAIN_DOMAIN', value: '${STACK_NAME}.example.com' },
{ key: 'STACK_NAME', value: 'appname-${ENVIRONMENT_NAME}' },
{ key: 'ENVIRONMENT_NAME', value: '${CI_COMMIT_REF_SLUG}' }
]
end
let(:job) do
create(:ci_build,
:with_deployment,
pipeline: pipeline,
ref: 'master',
environment: 'production',
project: project,
yaml_variables: yaml_variables,
options: { environment: { name: 'production', url: 'http://$MAIN_DOMAIN' } })
end
it { is_expected.to eq('http://appname-master.example.com') }
context 'when the FF ci_expand_environment_name_and_url is disabled' do
before do
stub_feature_flags(ci_expand_environment_name_and_url: false)
end
it { is_expected.to eq('http://${STACK_NAME}.example.com') }
end
end
context 'when yaml environment does not have url' do
let(:job) { create(:ci_build, :with_deployment, pipeline: pipeline, environment: 'staging', project: project) }

View file

@ -135,10 +135,22 @@ RSpec.describe Projects::CreateService, '#execute' do
create_project(user, opts)
end
it 'builds associated project settings' do
it 'creates associated project settings' do
project = create_project(user, opts)
expect(project.project_setting).to be_new_record
expect(project.project_setting).to be_persisted
end
context 'create_project_settings feature flag is disabled' do
before do
stub_feature_flags(create_project_settings: false)
end
it 'builds associated project settings' do
project = create_project(user, opts)
expect(project.project_setting).to be_new_record
end
end
it_behaves_like 'storing arguments in the application context' do