Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1dab074ef1
commit
fa4473a487
|
@ -784,6 +784,10 @@ Style/RegexpLiteralMixedPreserve:
|
|||
- mixed_preserve
|
||||
EnforcedStyle: mixed_preserve
|
||||
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94317#note_1139610896
|
||||
Style/Lambda:
|
||||
EnforcedStyle: literal
|
||||
|
||||
RSpec/TopLevelDescribePath:
|
||||
Exclude:
|
||||
- 'spec/fixtures/**/*.rb'
|
||||
|
|
|
@ -1,273 +1,81 @@
|
|||
---
|
||||
# Cop supports --auto-correct.
|
||||
# Cop supports --autocorrect.
|
||||
Style/Lambda:
|
||||
# Offense count: 653
|
||||
# Temporarily disabled due to too many offenses
|
||||
Enabled: false
|
||||
Details: grace period
|
||||
Exclude:
|
||||
- 'app/controllers/concerns/notes_actions.rb'
|
||||
- 'app/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support.rb'
|
||||
- 'app/controllers/projects/issues_controller.rb'
|
||||
- 'app/controllers/search_controller.rb'
|
||||
- 'app/graphql/mutations/container_repositories/destroy_tags.rb'
|
||||
- 'app/graphql/mutations/design_management/delete.rb'
|
||||
- 'app/graphql/types/permission_types/base_permission_type.rb'
|
||||
- 'app/models/analytics/cycle_analytics/issue_stage_event.rb'
|
||||
- 'app/models/analytics/cycle_analytics/merge_request_stage_event.rb'
|
||||
- 'app/models/bulk_imports/tracker.rb'
|
||||
- 'app/models/ci/build.rb'
|
||||
- 'app/models/ci/deleted_object.rb'
|
||||
- 'app/models/ci/instance_variable.rb'
|
||||
- 'app/models/ci/job_artifact.rb'
|
||||
- 'app/models/ci/namespace_mirror.rb'
|
||||
- 'app/models/ci/pending_build.rb'
|
||||
- 'app/models/ci/pipeline.rb'
|
||||
- 'app/models/ci/processable.rb'
|
||||
- 'app/models/ci/runner.rb'
|
||||
- 'app/models/clusters/cluster.rb'
|
||||
- 'app/models/clusters/concerns/application_status.rb'
|
||||
- 'app/models/commit_status.rb'
|
||||
- 'app/models/concerns/analytics/cycle_analytics/stage_event_model.rb'
|
||||
- 'app/models/concerns/approvable_base.rb'
|
||||
- 'app/models/concerns/atomic_internal_id.rb'
|
||||
- 'app/models/concerns/ci/has_status.rb'
|
||||
- 'app/models/concerns/clusters/agents/authorization_config_scopes.rb'
|
||||
- 'app/models/concerns/has_environment_scope.rb'
|
||||
- 'app/models/concerns/has_wiki_page_meta_attributes.rb'
|
||||
- 'app/models/concerns/id_in_ordered.rb'
|
||||
- 'app/models/concerns/integrations/has_issue_tracker_fields.rb'
|
||||
- 'app/models/concerns/issuable.rb'
|
||||
- 'app/models/concerns/issue_resource_event.rb'
|
||||
- 'app/models/concerns/milestoneable.rb'
|
||||
- 'app/models/concerns/mirror_authentication.rb'
|
||||
- 'app/models/concerns/packages/debian/component_file.rb'
|
||||
- 'app/models/concerns/reactive_caching.rb'
|
||||
- 'app/models/concerns/timebox.rb'
|
||||
- 'app/models/container_repository.rb'
|
||||
- 'app/models/custom_emoji.rb'
|
||||
- 'app/models/deployment.rb'
|
||||
- 'app/models/design_management/action.rb'
|
||||
- 'app/models/design_management/design.rb'
|
||||
- 'app/models/design_management/version.rb'
|
||||
- 'app/models/environment.rb'
|
||||
- 'app/models/event.rb'
|
||||
- 'app/models/group.rb'
|
||||
- 'app/models/group_deploy_key.rb'
|
||||
- 'app/models/group_group_link.rb'
|
||||
- 'app/models/hooks/web_hook.rb'
|
||||
- 'app/models/identity.rb'
|
||||
- 'app/models/import_failure.rb'
|
||||
- 'app/models/integrations/zentao_tracker_data.rb'
|
||||
- 'app/models/internal_id.rb'
|
||||
- 'app/models/issue.rb'
|
||||
- 'app/models/issue/metrics.rb'
|
||||
- 'app/models/jira_connect_installation.rb'
|
||||
- 'app/models/label.rb'
|
||||
- 'app/models/label_link.rb'
|
||||
- 'app/models/loose_foreign_keys/deleted_record.rb'
|
||||
- 'app/models/member.rb'
|
||||
- 'app/models/members/project_member.rb'
|
||||
- 'app/models/merge_request.rb'
|
||||
- 'app/models/merge_request/cleanup_schedule.rb'
|
||||
- 'app/models/merge_request_diff.rb'
|
||||
- 'app/models/merge_request_diff_file.rb'
|
||||
- 'app/models/merge_requests_closing_issues.rb'
|
||||
- 'app/models/milestone.rb'
|
||||
- 'app/models/namespace.rb'
|
||||
- 'app/models/note.rb'
|
||||
- 'app/models/note_diff_file.rb'
|
||||
- 'app/models/notification_setting.rb'
|
||||
- 'app/models/onboarding/progress.rb'
|
||||
- 'app/models/operations/feature_flags/user_list.rb'
|
||||
- 'app/models/packages/package.rb'
|
||||
- 'app/models/packages/package_file.rb'
|
||||
- 'app/models/pages_domain.rb'
|
||||
- 'app/models/product_analytics_event.rb'
|
||||
- 'app/models/programming_language.rb'
|
||||
- 'app/models/project.rb'
|
||||
- 'app/controllers/concerns/project_unauthorized.rb'
|
||||
- 'app/controllers/profiles/two_factor_auths_controller.rb'
|
||||
- 'app/models/concerns/featurable.rb'
|
||||
- 'app/models/project_feature.rb'
|
||||
- 'app/models/project_feature_usage.rb'
|
||||
- 'app/models/projects/topic.rb'
|
||||
- 'app/models/prometheus_alert_event.rb'
|
||||
- 'app/models/raw_usage_data.rb'
|
||||
- 'app/models/redirect_route.rb'
|
||||
- 'app/models/release.rb'
|
||||
- 'app/models/remote_mirror.rb'
|
||||
- 'app/models/repository_language.rb'
|
||||
- 'app/models/snippet.rb'
|
||||
- 'app/models/timelog.rb'
|
||||
- 'app/models/todo.rb'
|
||||
- 'app/models/user.rb'
|
||||
- 'app/models/users/in_product_marketing_email.rb'
|
||||
- 'app/serializers/ci/daily_build_group_report_result_entity.rb'
|
||||
- 'app/serializers/group_child_entity.rb'
|
||||
- 'app/serializers/issuable_sidebar_basic_entity.rb'
|
||||
- 'app/serializers/merge_request_sidebar_basic_entity.rb'
|
||||
- 'app/services/issues/referenced_merge_requests_service.rb'
|
||||
- 'config/initializers/deprecations.rb'
|
||||
- 'config/initializers/rspec_profiling.rb'
|
||||
- 'config/application.rb'
|
||||
- 'config/initializers/0_license.rb'
|
||||
- 'config/initializers/0_log_deprecations.rb'
|
||||
- 'config/initializers/action_cable.rb'
|
||||
- 'config/initializers/gitlab_experiment.rb'
|
||||
- 'config/initializers/lograge.rb'
|
||||
- 'config/routes.rb'
|
||||
- 'config/routes/dashboard.rb'
|
||||
- 'config/routes/group.rb'
|
||||
- 'config/routes/issues.rb'
|
||||
- 'db/post_migrate/20210303121224_update_gitlab_subscriptions_start_at_post_eoa.rb'
|
||||
- 'db/post_migrate/20210513155546_backfill_nuget_temporary_packages_to_processing_status.rb'
|
||||
- 'db/post_migrate/20210823132600_remove_duplicate_dast_site_tokens.rb'
|
||||
- 'db/post_migrate/20220425121435_backfill_integrations_enable_ssl_verification.rb'
|
||||
- 'ee/app/controllers/groups/analytics/productivity_analytics_controller.rb'
|
||||
- 'ee/app/models/analytics/devops_adoption/enabled_namespace.rb'
|
||||
- 'ee/app/models/analytics/devops_adoption/snapshot.rb'
|
||||
- 'ee/app/models/app_sec/fuzzing/coverage/corpus.rb'
|
||||
- 'ee/app/models/approval_merge_request_rule.rb'
|
||||
- 'ee/app/models/boards/epic_board_position.rb'
|
||||
- 'ee/app/models/boards/epic_user_preference.rb'
|
||||
- 'ee/app/models/ci/minutes/project_monthly_usage.rb'
|
||||
- 'ee/app/models/concerns/ee/protected_ref.rb'
|
||||
- 'ee/app/models/concerns/geo/replicable_model.rb'
|
||||
- 'ee/app/models/concerns/issue_widgets/acts_like_requirement.rb'
|
||||
- 'ee/app/models/dast/profile.rb'
|
||||
- 'ee/app/models/dast_site_validation.rb'
|
||||
- 'ee/app/models/dora/daily_metrics.rb'
|
||||
- 'ee/app/models/ee/ci/build.rb'
|
||||
- 'ee/app/models/ee/ci/daily_build_group_report_result.rb'
|
||||
- 'ee/app/models/ee/ci/job_artifact.rb'
|
||||
- 'ee/app/models/ee/ci/pipeline.rb'
|
||||
- 'ee/app/models/ee/environment.rb'
|
||||
- 'ee/app/models/ee/epic.rb'
|
||||
- 'ee/app/models/ee/group.rb'
|
||||
- 'ee/app/models/ee/group_member.rb'
|
||||
- 'ee/app/models/ee/identity.rb'
|
||||
- 'ee/app/models/ee/issue.rb'
|
||||
- 'ee/app/models/ee/iteration.rb'
|
||||
- 'ee/app/models/ee/label.rb'
|
||||
- 'ee/app/models/ee/member.rb'
|
||||
- 'ee/app/models/ee/merge_request.rb'
|
||||
- 'ee/app/models/ee/namespace.rb'
|
||||
- 'ee/app/models/ee/namespace_ci_cd_setting.rb'
|
||||
- 'ee/app/models/ee/note.rb'
|
||||
- 'ee/app/models/ee/project.rb'
|
||||
- 'ee/app/models/ee/user.rb'
|
||||
- 'ee/app/models/ee/vulnerability.rb'
|
||||
- 'ee/app/models/gitlab_subscription.rb'
|
||||
- 'ee/app/models/incident_management/oncall_rotation.rb'
|
||||
- 'ee/app/models/incident_management/oncall_shift.rb'
|
||||
- 'ee/app/models/iterations/cadence.rb'
|
||||
- 'ee/app/models/merge_request_block.rb'
|
||||
- 'ee/app/models/merge_requests/external_status_check.rb'
|
||||
- 'ee/app/models/merge_train.rb'
|
||||
- 'ee/app/models/protected_environment.rb'
|
||||
- 'ee/app/models/requirements_management/requirement.rb'
|
||||
- 'ee/app/models/security/finding.rb'
|
||||
- 'ee/app/models/security/orchestration_policy_configuration.rb'
|
||||
- 'ee/app/models/security/orchestration_policy_rule_schedule.rb'
|
||||
- 'ee/app/models/security/scan.rb'
|
||||
- 'ee/app/models/security/training_provider.rb'
|
||||
- 'ee/app/models/software_license_policy.rb'
|
||||
- 'ee/app/models/vulnerabilities/feedback.rb'
|
||||
- 'ee/app/models/vulnerabilities/finding.rb'
|
||||
- 'ee/app/models/vulnerabilities/historical_statistic.rb'
|
||||
- 'ee/app/models/vulnerabilities/read.rb'
|
||||
- 'ee/app/models/vulnerabilities/scanner.rb'
|
||||
- 'ee/app/controllers/concerns/ee/routable_actions/sso_enforcement_redirect.rb'
|
||||
- 'ee/app/serializers/ee/group_child_entity.rb'
|
||||
- 'ee/lib/ee/api/entities/application_setting.rb'
|
||||
- 'ee/lib/ee/api/entities/geo_node_status.rb'
|
||||
- 'ee/lib/ee/api/entities/group.rb'
|
||||
- 'ee/app/services/ee/issues/export_csv_service.rb'
|
||||
- 'ee/lib/ee/api/entities/group_detail.rb'
|
||||
- 'ee/lib/ee/api/entities/group_push_rule.rb'
|
||||
- 'ee/lib/ee/api/entities/project.rb'
|
||||
- 'ee/lib/ee/api/entities/vulnerability_issue_link.rb'
|
||||
- 'ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb'
|
||||
- 'ee/lib/ee/banzai/filter/sanitization_filter.rb'
|
||||
- 'ee/lib/ee/gitlab/checks/diff_check.rb'
|
||||
- 'ee/lib/elastic/latest/application_class_proxy.rb'
|
||||
- 'ee/lib/gem_extensions/elasticsearch/model/adapter/active_record/importing.rb'
|
||||
- 'ee/spec/migrations/backfill_delayed_group_deletion_spec.rb'
|
||||
- 'ee/spec/migrations/remove_schedule_and_status_null_constraints_from_pending_escalations_alert_spec.rb'
|
||||
- 'ee/spec/elastic_integration/global_search_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/geo/event_gap_tracking_spec.rb'
|
||||
- 'ee/spec/services/ee/groups/autocomplete_service_spec.rb'
|
||||
- 'ee/spec/services/ee/notes/create_service_spec.rb'
|
||||
- 'ee/spec/support/helpers/elasticsearch_helpers.rb'
|
||||
- 'ee/spec/support/shared_examples/lib/gitlab/middleware/maintenance_mode_gitlab_ee_instance_shared_examples.rb'
|
||||
- 'ee/spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_ee_instance_shared_examples.rb'
|
||||
- 'lib/api/ci/jobs.rb'
|
||||
- 'lib/api/ci/pipelines.rb'
|
||||
- 'lib/api/entities/group_detail.rb'
|
||||
- 'lib/api/entities/issue.rb'
|
||||
- 'lib/api/entities/label.rb'
|
||||
- 'lib/api/entities/merge_request.rb'
|
||||
- 'lib/api/entities/project.rb'
|
||||
- 'lib/api/entities/project_export_status.rb'
|
||||
- 'lib/api/feature_flags_user_lists.rb'
|
||||
- 'lib/container_registry/base_client.rb'
|
||||
- 'lib/container_registry/client.rb'
|
||||
- 'lib/csv_builder.rb'
|
||||
- 'lib/event_filter.rb'
|
||||
- 'lib/gitlab/background_migration/backfill_ci_namespace_mirrors.rb'
|
||||
- 'lib/gitlab/background_migration/backfill_ci_project_mirrors.rb'
|
||||
- 'lib/gitlab/background_migration/backfill_ci_queuing_tables.rb'
|
||||
- 'lib/gitlab/background_migration/fix_projects_without_prometheus_service.rb'
|
||||
- 'lib/gitlab/background_migration/populate_latest_pipeline_ids.rb'
|
||||
- 'lib/gitlab/ci/config/entry/includes.rb'
|
||||
- 'lib/gitlab/ci/config/entry/trigger.rb'
|
||||
- 'lib/gitlab/config/entry/validatable.rb'
|
||||
- 'lib/gitlab/database/background_migration/batched_migration.rb'
|
||||
- 'lib/gitlab/database/background_migration_job.rb'
|
||||
- 'lib/gitlab/database/postgres_foreign_key.rb'
|
||||
- 'lib/gitlab/database/postgres_index.rb'
|
||||
- 'lib/gitlab/database/postgres_partition.rb'
|
||||
- 'lib/gitlab/database/postgres_partitioned_table.rb'
|
||||
- 'lib/gitlab/gl_repository.rb'
|
||||
- 'lib/gitlab/import_export/import_failure_service.rb'
|
||||
- 'lib/gitlab/merge_requests/commit_message_generator.rb'
|
||||
- 'lib/gitlab/seeder.rb'
|
||||
- 'lib/api/validations/types/comma_separated_to_array.rb'
|
||||
- 'lib/api/validations/types/comma_separated_to_integer_array.rb'
|
||||
- 'lib/api/validations/types/hash_of_integer_values.rb'
|
||||
- 'lib/api/validations/validators/check_assignees_count.rb'
|
||||
- 'lib/banzai/filter/ascii_doc_sanitization_filter.rb'
|
||||
- 'lib/banzai/filter/base_sanitization_filter.rb'
|
||||
- 'lib/banzai/filter/sanitization_filter.rb'
|
||||
- 'lib/gitlab/action_cable/request_store_callbacks.rb'
|
||||
- 'lib/gitlab/checks/diff_check.rb'
|
||||
- 'lib/gitlab/database/load_balancing/action_cable_callbacks.rb'
|
||||
- 'lib/gitlab/middleware/rack_multipart_tempfile_factory.rb'
|
||||
- 'lib/gitlab/omniauth_initializer.rb'
|
||||
- 'lib/gitlab/prometheus/queries/query_additional_metrics.rb'
|
||||
- 'lib/gitlab/rack_attack.rb'
|
||||
- 'lib/gitlab/sidekiq_config/worker_matcher.rb'
|
||||
- 'lib/gitlab/sidekiq_signals.rb'
|
||||
- 'lib/gitlab/utils/measuring.rb'
|
||||
- 'lib/gitlab/visibility_level.rb'
|
||||
- 'rubocop/cop/rspec/modify_sidekiq_middleware.rb'
|
||||
- 'rubocop/cop/rspec/timecop_freeze.rb'
|
||||
- 'rubocop/cop/rspec/timecop_travel.rb'
|
||||
- 'lib/gitlab/sidekiq_middleware.rb'
|
||||
- 'lib/gitlab/utils/usage_data.rb'
|
||||
- 'qa/qa/page/base.rb'
|
||||
- 'qa/qa/runtime/allure_report.rb'
|
||||
- 'qa/qa/specs/features/api/1_manage/import/import_large_github_repo_spec.rb'
|
||||
- 'qa/qa/support/api.rb'
|
||||
- 'rubocop/cop/inject_enterprise_edition_module.rb'
|
||||
- 'rubocop/cop/rspec/have_gitlab_http_status.rb'
|
||||
- 'spec/controllers/concerns/routable_actions_spec.rb'
|
||||
- 'spec/deprecation_toolkit_env.rb'
|
||||
- 'spec/factories/design_management/designs.rb'
|
||||
- 'spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb'
|
||||
- 'spec/graphql/resolvers/concerns/resolves_groups_spec.rb'
|
||||
- 'spec/features/groups/dependency_proxy_for_containers_spec.rb'
|
||||
- 'spec/graphql/types/base_object_spec.rb'
|
||||
- 'spec/lib/gitlab/action_cable/request_store_callbacks_spec.rb'
|
||||
- 'spec/lib/gitlab/cross_project_access/class_methods_spec.rb'
|
||||
- 'spec/lib/gitlab/database/consistency_spec.rb'
|
||||
- 'spec/lib/gitlab/database/dynamic_model_helpers_spec.rb'
|
||||
- 'spec/lib/gitlab/database/load_balancing/action_cable_callbacks_spec.rb'
|
||||
- 'spec/lib/gitlab/database/load_balancing_spec.rb'
|
||||
- 'spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb'
|
||||
- 'spec/lib/gitlab/database/migration_helpers_spec.rb'
|
||||
- 'spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb'
|
||||
- 'spec/lib/gitlab/graphql/tracers/application_context_tracer_spec.rb'
|
||||
- 'spec/lib/gitlab/graphql/tracers/timer_tracer_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/members_mapper_spec.rb'
|
||||
- 'spec/lib/gitlab/sidekiq_middleware/size_limiter/validator_spec.rb'
|
||||
- 'spec/migrations/20210722150102_operations_feature_flags_correct_flexible_rollout_values_spec.rb'
|
||||
- 'spec/migrations/20210804150320_create_base_work_item_types_spec.rb'
|
||||
- 'spec/migrations/20210819145000_drop_temporary_columns_and_triggers_for_ci_builds_runner_session_spec.rb'
|
||||
- 'spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb'
|
||||
- 'spec/migrations/20210902144144_drop_temporary_columns_and_triggers_for_ci_build_needs_spec.rb'
|
||||
- 'spec/migrations/20210906100316_drop_temporary_columns_and_triggers_for_ci_build_trace_chunks_spec.rb'
|
||||
- 'spec/migrations/20210906130643_drop_temporary_columns_and_triggers_for_taggings_spec.rb'
|
||||
- 'spec/migrations/20210907013944_cleanup_bigint_conversion_for_ci_builds_metadata_spec.rb'
|
||||
- 'spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb'
|
||||
- 'spec/migrations/20210922021816_drop_int4_columns_for_ci_job_artifacts_spec.rb'
|
||||
- 'spec/migrations/20210922025631_drop_int4_column_for_ci_sources_pipelines_spec.rb'
|
||||
- 'spec/migrations/20210922082019_drop_int4_column_for_events_spec.rb'
|
||||
- 'spec/migrations/20210922091402_drop_int4_column_for_push_event_payloads_spec.rb'
|
||||
- 'spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb'
|
||||
- 'spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb'
|
||||
- 'spec/migrations/20220120094340_drop_position_from_security_findings_spec.rb'
|
||||
- 'spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb'
|
||||
- 'spec/migrations/20220305223212_add_security_training_providers_spec.rb'
|
||||
- 'spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb'
|
||||
- 'spec/migrations/generate_customers_dot_jwt_signing_key_spec.rb'
|
||||
- 'spec/migrations/insert_ci_daily_pipeline_schedule_triggers_plan_limits_spec.rb'
|
||||
- 'spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb'
|
||||
- 'spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb'
|
||||
- 'spec/migrations/remove_schedule_and_status_from_pending_alert_escalations_spec.rb'
|
||||
- 'spec/models/ability_spec.rb'
|
||||
- 'spec/models/broadcast_message_spec.rb'
|
||||
- 'spec/models/concerns/participable_spec.rb'
|
||||
- 'spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb'
|
||||
- 'spec/lib/gitlab/path_regex_spec.rb'
|
||||
- 'spec/services/groups/autocomplete_service_spec.rb'
|
||||
- 'spec/services/notes/create_service_spec.rb'
|
||||
- 'spec/services/issues/referenced_merge_requests_service_spec.rb'
|
||||
- 'spec/services/projects/autocomplete_service_spec.rb'
|
||||
- 'spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb'
|
||||
- 'spec/support/helpers/email_helpers.rb'
|
||||
- 'spec/support/helpers/reference_parser_helpers.rb'
|
||||
- 'spec/support/shared_examples/lib/gitlab/middleware/read_only_gitlab_instance_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb'
|
||||
- 'spec/workers/process_commit_worker_spec.rb'
|
||||
|
|
|
@ -270,6 +270,25 @@ export const secondsToMilliseconds = (seconds) => seconds * 1000;
|
|||
*/
|
||||
export const secondsToDays = (seconds) => Math.round(seconds / 86400);
|
||||
|
||||
/**
|
||||
* Returns the date `n` seconds after the date provided
|
||||
*
|
||||
* @param {Date} date the initial date
|
||||
* @param {Number} numberOfSeconds number of seconds after
|
||||
* @return {Date} A `Date` object `n` seconds after the provided `Date`
|
||||
*/
|
||||
export const nSecondsAfter = (date, numberOfSeconds) =>
|
||||
new Date(date.getTime() + numberOfSeconds * 1000);
|
||||
|
||||
/**
|
||||
* Returns the date `n` seconds before the date provided
|
||||
*
|
||||
* @param {Date} date the initial date
|
||||
* @param {Number} numberOfSeconds number of seconds before
|
||||
* @return {Date} A `Date` object `n` seconds before the provided `Date`
|
||||
*/
|
||||
export const nSecondsBefore = (date, numberOfSeconds) => nSecondsAfter(date, -numberOfSeconds);
|
||||
|
||||
/**
|
||||
* Returns the date `n` days after the date provided
|
||||
*
|
||||
|
|
|
@ -11,13 +11,10 @@ export default {
|
|||
render(h) {
|
||||
const { extensions } = registeredExtensions;
|
||||
|
||||
if (extensions.length === 0) return null;
|
||||
|
||||
return h(
|
||||
'section',
|
||||
{
|
||||
attrs: {
|
||||
class: 'mr-section-container mr-widget-workflow',
|
||||
role: 'region',
|
||||
'aria-label': __('Merge request reports'),
|
||||
},
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
hasChildren: false,
|
||||
};
|
||||
},
|
||||
updated() {
|
||||
this.hasChildren = this.$scopedSlots.default?.()?.some((c) => c.tag);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-show="hasChildren" class="mr-section-container mr-widget-workflow">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
|
@ -1,7 +1,10 @@
|
|||
<script>
|
||||
import { GlSafeHtmlDirective } from '@gitlab/ui';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
|
||||
import {
|
||||
registerExtension,
|
||||
registeredExtensions,
|
||||
} from '~/vue_merge_request_widget/components/extensions';
|
||||
import MrWidgetApprovals from 'ee_else_ce/vue_merge_request_widget/components/approvals/approvals.vue';
|
||||
import MRWidgetService from 'ee_else_ce/vue_merge_request_widget/services/mr_widget_service';
|
||||
import MRWidgetStore from 'ee_else_ce/vue_merge_request_widget/stores/mr_widget_store';
|
||||
|
@ -47,6 +50,7 @@ import terraformExtension from './extensions/terraform';
|
|||
import accessibilityExtension from './extensions/accessibility';
|
||||
import codeQualityExtension from './extensions/code_quality';
|
||||
import testReportExtension from './extensions/test_report';
|
||||
import ReportWidgetContainer from './components/report_widget_container.vue';
|
||||
|
||||
export default {
|
||||
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25
|
||||
|
@ -86,6 +90,7 @@ export default {
|
|||
SecurityReportsApp: () => import('~/vue_shared/security_reports/security_reports_app.vue'),
|
||||
MergeChecksFailed: () => import('./components/states/merge_checks_failed.vue'),
|
||||
ReadyToMerge: ReadyToMergeState,
|
||||
ReportWidgetContainer,
|
||||
},
|
||||
apollo: {
|
||||
state: {
|
||||
|
@ -216,6 +221,9 @@ export default {
|
|||
|
||||
return !this.mr.mergeDetailsCollapsed;
|
||||
},
|
||||
hasExtensions() {
|
||||
return registeredExtensions.extensions.length;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'mr.machineValue': {
|
||||
|
@ -553,7 +561,17 @@ export default {
|
|||
:mr="mr"
|
||||
:service="service"
|
||||
/>
|
||||
<extensions-container :mr="mr" />
|
||||
<report-widget-container>
|
||||
<extensions-container v-if="hasExtensions" :mr="mr" />
|
||||
<security-reports-app
|
||||
v-if="shouldRenderSecurityReport && !shouldShowSecurityExtension"
|
||||
:pipeline-id="mr.pipeline.id"
|
||||
:project-id="mr.sourceProjectId"
|
||||
:security-reports-docs-path="mr.securityReportsDocsPath"
|
||||
:target-project-full-path="mr.targetProjectFullPath"
|
||||
:mr-iid="mr.iid"
|
||||
/>
|
||||
</report-widget-container>
|
||||
<div class="mr-section-container mr-widget-workflow">
|
||||
<div v-if="hasAlerts" class="gl-overflow-hidden mr-widget-alert-container">
|
||||
<mr-widget-alert-message
|
||||
|
@ -582,15 +600,6 @@ export default {
|
|||
|
||||
<widget-container v-if="mr" :mr="mr" />
|
||||
|
||||
<security-reports-app
|
||||
v-if="shouldRenderSecurityReport && !shouldShowSecurityExtension"
|
||||
:pipeline-id="mr.pipeline.id"
|
||||
:project-id="mr.sourceProjectId"
|
||||
:security-reports-docs-path="mr.securityReportsDocsPath"
|
||||
:target-project-full-path="mr.targetProjectFullPath"
|
||||
:mr-iid="mr.iid"
|
||||
/>
|
||||
|
||||
<div class="mr-widget-section" data-qa-selector="mr_widget_content">
|
||||
<component :is="componentName" :mr="mr" :service="service" />
|
||||
<ready-to-merge
|
||||
|
|
|
@ -1,5 +1,2 @@
|
|||
.gl-spinner-container{ class: @class }
|
||||
- if @inline
|
||||
%span{ class: spinner_class, aria: {label: @label} }
|
||||
- else
|
||||
%div{ class: spinner_class, aria: {label: @label} }
|
||||
= content_tag (@inline ? :span : :div), **html_options do
|
||||
%span{ class: spinner_class, aria: {label: @label} }>
|
||||
|
|
|
@ -2,26 +2,31 @@
|
|||
|
||||
module Pajamas
|
||||
class SpinnerComponent < Pajamas::Component
|
||||
# @param [String] class
|
||||
# @param [Symbol] color
|
||||
# @param [Boolean] inline
|
||||
# @param [String] label
|
||||
# @param [Symbol] size
|
||||
def initialize(class: '', color: :dark, inline: false, label: _("Loading"), size: :sm)
|
||||
@class = binding.local_variable_get(:class)
|
||||
def initialize(color: :dark, inline: false, label: _("Loading"), size: :sm, **html_options)
|
||||
@color = filter_attribute(color.to_sym, COLOR_OPTIONS)
|
||||
@inline = inline
|
||||
@label = label.presence
|
||||
@size = filter_attribute(size.to_sym, SIZE_OPTIONS)
|
||||
@html_options = html_options
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def spinner_class
|
||||
["gl-spinner", "gl-spinner-#{@size}", "gl-spinner-#{@color}"]
|
||||
["gl-spinner", "gl-spinner-#{@size}", "gl-spinner-#{@color} gl-vertical-align-text-bottom!"]
|
||||
end
|
||||
|
||||
COLOR_OPTIONS = [:light, :dark].freeze
|
||||
SIZE_OPTIONS = [:sm, :md, :lg, :xl].freeze
|
||||
|
||||
def html_options
|
||||
options = format_options(options: @html_options, css_classes: "gl-spinner-container")
|
||||
options[:role] = "status"
|
||||
options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -71,18 +71,13 @@ module IconsHelper
|
|||
#
|
||||
# See also https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-loading-icon--default
|
||||
def gl_loading_icon(inline: false, color: 'dark', size: 'sm', css_class: nil, data: nil)
|
||||
spinner = content_tag(:span, "", {
|
||||
class: %[gl-spinner gl-spinner-#{color} gl-spinner-#{size} gl-vertical-align-text-bottom!],
|
||||
aria: { label: _('Loading') },
|
||||
render Pajamas::SpinnerComponent.new(
|
||||
inline: inline,
|
||||
color: color,
|
||||
size: size,
|
||||
class: css_class,
|
||||
data: data
|
||||
})
|
||||
|
||||
container_classes = ['gl-spinner-container']
|
||||
container_classes << css_class unless css_class.blank?
|
||||
content_tag(inline ? :span : :div, spinner, {
|
||||
class: container_classes,
|
||||
role: 'status'
|
||||
})
|
||||
)
|
||||
end
|
||||
|
||||
def external_snippet_icon(name)
|
||||
|
|
|
@ -11,6 +11,8 @@ module Emails
|
|||
)
|
||||
@recipient = User.find(user_id)
|
||||
|
||||
add_project_headers
|
||||
|
||||
mail_with_locale(
|
||||
to: @recipient.notification_email_for(@project.group),
|
||||
subject: subject(release_email_subject)
|
||||
|
|
|
@ -54,3 +54,4 @@
|
|||
.col-12
|
||||
.issuable-form-select-holder
|
||||
= form.gitlab_ui_datepicker :due_date, placeholder: _('Select due date'), autocomplete: 'off', id: "issuable-due-date"
|
||||
= render_if_exists "shared/issuable/form/iteration", form: form, group: project.group
|
||||
|
|
|
@ -566,7 +566,8 @@ module Gitlab
|
|||
# Used in app/services/web_hooks/log_execution_service.rb: log_execution
|
||||
ActiveSupport::TimeWithZone,
|
||||
ActiveSupport::TimeZone,
|
||||
Gitlab::Color # https://gitlab.com/gitlab-org/gitlab/-/issues/368844
|
||||
Gitlab::Color, # https://gitlab.com/gitlab-org/gitlab/-/issues/368844,
|
||||
Hashie::Array # https://gitlab.com/gitlab-org/gitlab/-/issues/378089
|
||||
]
|
||||
|
||||
# on_master_start yields immediately in unclustered environments and runs
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_variable_expansion_in_rules_exists
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101639
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381046
|
||||
milestone: '15.6'
|
||||
type: development
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -5,31 +5,10 @@ return unless Gitlab::Utils.to_boolean(ENV['GITLAB_MEMORY_WATCHDOG_ENABLED'])
|
|||
|
||||
Gitlab::Cluster::LifecycleEvents.on_worker_start do
|
||||
watchdog = Gitlab::Memory::Watchdog.new
|
||||
max_strikes = ENV.fetch('GITLAB_MEMWD_MAX_STRIKES', 5).to_i
|
||||
sleep_time_seconds = ENV.fetch('GITLAB_MEMWD_SLEEP_TIME_SEC', 60).to_i
|
||||
max_mem_growth = ENV.fetch('GITLAB_MEMWD_MAX_MEM_GROWTH', 3.0).to_f
|
||||
max_heap_frag = ENV.fetch('GITLAB_MEMWD_MAX_HEAP_FRAG', 0.5).to_f
|
||||
|
||||
watchdog.configure do |config|
|
||||
config.handler =
|
||||
if Gitlab::Runtime.puma?
|
||||
Gitlab::Memory::Watchdog::PumaHandler.new
|
||||
elsif Gitlab::Runtime.sidekiq?
|
||||
Gitlab::Memory::Watchdog::TermProcessHandler.new
|
||||
else
|
||||
Gitlab::Memory::Watchdog::NullHandler.instance
|
||||
end
|
||||
|
||||
config.logger = Gitlab::AppLogger
|
||||
config.sleep_time_seconds = sleep_time_seconds
|
||||
# config.monitor.use MonitorClass, args*, max_strikes:, kwargs**, &block
|
||||
config.monitors.use Gitlab::Memory::Watchdog::Monitor::HeapFragmentation,
|
||||
max_heap_fragmentation: max_heap_frag,
|
||||
max_strikes: max_strikes
|
||||
|
||||
config.monitors.use Gitlab::Memory::Watchdog::Monitor::UniqueMemoryGrowth,
|
||||
max_mem_growth: max_mem_growth,
|
||||
max_strikes: max_strikes
|
||||
if Gitlab::Runtime.puma?
|
||||
watchdog.configure(&Gitlab::Memory::Watchdog::Configurator.configure_for_puma)
|
||||
elsif Gitlab::Runtime.sidekiq?
|
||||
watchdog.configure(&Gitlab::Memory::Watchdog::Configurator.configure_for_sidekiq)
|
||||
end
|
||||
|
||||
Gitlab::BackgroundTask.new(watchdog).start
|
||||
|
|
|
@ -17,6 +17,8 @@ metadata:
|
|||
# Keep in alphabetical order
|
||||
- name: access_requests
|
||||
description: Operations related to access requests
|
||||
- name: ci_variables
|
||||
description: Operations related to CI/CD variables
|
||||
- name: cluster_agents
|
||||
description: Operations related to the GitLab agent for Kubernetes
|
||||
- name: ci_resource_groups
|
||||
|
@ -51,6 +53,8 @@ metadata:
|
|||
description: Operations related to import BitBucket projects
|
||||
- name: project_import_github
|
||||
description: Operations related to import GitHub projects
|
||||
- name: protected environments
|
||||
description: Operations related to protected environments
|
||||
- name: release_links
|
||||
description: Operations related to release assets (links)
|
||||
- name: releases
|
||||
|
@ -60,4 +64,4 @@ metadata:
|
|||
- name: system_hooks
|
||||
description: Operations related to system hooks
|
||||
- name: unleash_api
|
||||
description: Operations related to Unleash API
|
||||
description: Operations related to Unleash API
|
||||
|
|
|
@ -10333,7 +10333,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="boardepicchildreniidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
|
||||
| <a id="boardepicchildreniids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. |
|
||||
| <a id="boardepicchildrenin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
|
||||
| <a id="boardepicchildrenincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include epics from ancestor groups. |
|
||||
| <a id="boardepicchildrenincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include child epics from ancestor groups. |
|
||||
| <a id="boardepicchildrenincludedescendantgroups"></a>`includeDescendantGroups` | [`Boolean`](#boolean) | Include epics from descendant groups. |
|
||||
| <a id="boardepicchildrenlabelname"></a>`labelName` | [`[String!]`](#string) | Filter epics by labels. |
|
||||
| <a id="boardepicchildrenmilestonetitle"></a>`milestoneTitle` | [`String`](#string) | Filter epics by milestone title, computed from epic's issues. |
|
||||
|
@ -12247,7 +12247,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="epicchildreniidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
|
||||
| <a id="epicchildreniids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. |
|
||||
| <a id="epicchildrenin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
|
||||
| <a id="epicchildrenincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include epics from ancestor groups. |
|
||||
| <a id="epicchildrenincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include child epics from ancestor groups. |
|
||||
| <a id="epicchildrenincludedescendantgroups"></a>`includeDescendantGroups` | [`Boolean`](#boolean) | Include epics from descendant groups. |
|
||||
| <a id="epicchildrenlabelname"></a>`labelName` | [`[String!]`](#string) | Filter epics by labels. |
|
||||
| <a id="epicchildrenmilestonetitle"></a>`milestoneTitle` | [`String`](#string) | Filter epics by milestone title, computed from epic's issues. |
|
||||
|
@ -17693,7 +17693,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
| <a id="projectworkitemssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
|
||||
| <a id="projectworkitemssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by this criteria. |
|
||||
| <a id="projectworkitemsstate"></a>`state` | [`IssuableState`](#issuablestate) | Current state of this work item. |
|
||||
| <a id="projectworkitemsstatuswidget"></a>`statusWidget` | [`StatusFilterInput`](#statusfilterinput) | Input for status widget filter. |
|
||||
| <a id="projectworkitemsstatuswidget"></a>`statusWidget` | [`StatusFilterInput`](#statusfilterinput) | Input for status widget filter. Ignored if `work_items_mvc_2` is disabled. |
|
||||
| <a id="projectworkitemstypes"></a>`types` | [`[IssueType!]`](#issuetype) | Filter work items by the given work item types. |
|
||||
|
||||
### `ProjectCiCdSetting`
|
||||
|
|
|
@ -95,7 +95,7 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Protect an environment
|
||||
## Protect a single environment
|
||||
|
||||
Protects a single environment.
|
||||
|
||||
|
@ -136,7 +136,7 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Update an environment
|
||||
## Update a protected environment
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351854) in GitLab 15.4.
|
||||
|
||||
|
@ -304,7 +304,7 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Unprotect environment
|
||||
## Unprotect a single environment
|
||||
|
||||
Unprotects the given protected environment.
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Protect repository environments
|
||||
## Protect a single environment
|
||||
|
||||
Protects a single environment:
|
||||
|
||||
|
@ -343,7 +343,7 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Unprotect environment
|
||||
## Unprotect a single environment
|
||||
|
||||
Unprotects the given protected environment:
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ There are two ways to configure approvals for a protected environment:
|
|||
|
||||
1. Using the [UI](protected_environments.md#protecting-environments)
|
||||
1. Set the **Required approvals** field to 1 or more.
|
||||
1. Using the [REST API](../../api/protected_environments.md#protect-repository-environments)
|
||||
1. Using the [REST API](../../api/protected_environments.md#protect-a-single-environment)
|
||||
2. Set the `required_approval_count` field to 1 or more.
|
||||
|
||||
After this is configured, all jobs deploying to this environment automatically go into a blocked state and wait for approvals before running. Ensure that the number of required approvals is less than the number of users allowed to deploy.
|
||||
|
@ -89,7 +89,7 @@ Maintainer role.
|
|||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345678) in GitLab 14.10 with a flag named `deployment_approval_rules`. Disabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/345678) in GitLab 15.0. [Feature flag `deployment_approval_rules`](https://gitlab.com/gitlab-org/gitlab/-/issues/345678) removed.
|
||||
|
||||
1. Using the [REST API](../../api/group_protected_environments.md#protect-an-environment).
|
||||
1. Using the [REST API](../../api/group_protected_environments.md#protect-a-single-environment).
|
||||
1. `deploy_access_levels` represents which entity can execute the deployment job.
|
||||
1. `approval_rules` represents which entity can approve the deployment job.
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ There are two places defined variables can be used. On the:
|
|||
| [`include`](../yaml/index.md#include) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. <br/><br/>See [Use variables with include](../yaml/includes.md#use-variables-with-include) for more information on supported variables. |
|
||||
| [`only:variables`](../yaml/index.md#onlyvariables--exceptvariables) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- Variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`).<br/>- Any other variables related to environment (currently only `CI_ENVIRONMENT_URL`).<br/>- [Persisted variables](#persisted-variables). |
|
||||
| [`resource_group`](../yaml/index.md#resource_group) | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support the following:<br/>- `CI_ENVIRONMENT_URL`<br/>- [Persisted variables](#persisted-variables). |
|
||||
| [`rules:exists`](../yaml/index.md#rulesexists) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. |
|
||||
| [`rules:if`](../yaml/index.md#rulesif) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- Variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`).<br/>- Any other variables related to environment (currently only `CI_ENVIRONMENT_URL`).<br/>- [Persisted variables](#persisted-variables). |
|
||||
| [`script`](../yaml/index.md#script) | yes | Script execution shell | The variable expansion is made by the [execution shell environment](#execution-shell-environment). |
|
||||
| [`services:name`](../yaml/index.md#services) | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism). |
|
||||
|
|
|
@ -3437,7 +3437,8 @@ relative to `refs/heads/branch1` and the pipeline source is a merge request even
|
|||
|
||||
#### `rules:exists`
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24021) in GitLab 12.4.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24021) in GitLab 12.4.
|
||||
> - CI/CD variable support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/283881) in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_variable_expansion_in_rules_exists`. Disabled by default.
|
||||
|
||||
Use `exists` to run a job when certain files exist in the repository.
|
||||
|
||||
|
@ -3445,8 +3446,7 @@ Use `exists` to run a job when certain files exist in the repository.
|
|||
|
||||
**Possible inputs**:
|
||||
|
||||
- An array of file paths. Paths are relative to the project directory (`$CI_PROJECT_DIR`)
|
||||
and can't directly link outside it. File paths can use glob patterns.
|
||||
- An array of file paths. Paths are relative to the project directory (`$CI_PROJECT_DIR`) and can't directly link outside it. File paths can use glob patterns and [CI/CD variables](../variables/where_variables_can_be_used.md#gitlab-ciyml-file).
|
||||
|
||||
**Example of `rules:exists`**:
|
||||
|
||||
|
|
|
@ -159,3 +159,232 @@ export default {
|
|||
[In Vue 3](https://v3-migration.vuejs.org/breaking-changes/props-default-this.html),
|
||||
the props default value factory is passed the raw props as an argument, and can
|
||||
also access injections.
|
||||
|
||||
## Handling libraries that do not work with `@vue/compat`
|
||||
|
||||
**Problem**
|
||||
|
||||
Some libraries rely on Vue.js 2 internals. They might not work with `@vue/compat`, so we need a strategy to use an updated version with Vue.js 3 while maintaining compatibility with the current codebase.
|
||||
|
||||
**Goals**
|
||||
|
||||
- We should add as few changes as possible to existing code to support new libraries. Instead, we should **add*- new code, which will act as **facade**, making the new version compatible with the old one
|
||||
- Switching between new and old versions should be hidden inside tooling (webpack / jest) and should not be exposed to the code
|
||||
- All facades specific to migration should live in the same directory to simplify future migration steps
|
||||
|
||||
### Step-by-step migration
|
||||
|
||||
In the step-by-step guide, we will be migrating [VueApollo Demo](https://gitlab.com/gitlab-org/frontend/vue3-migration-vue-apollo/-/tree/main/src/vue3compat) project. It will allow us to focus on migration specifics while avoiding nuances of complex tooling setup in the GitLab project. The project intentionally uses the same tooling as GitLab:
|
||||
|
||||
- webpack
|
||||
- yarn
|
||||
- Vue.js + VueApollo
|
||||
|
||||
#### Initial state
|
||||
|
||||
Right after cloning, you could run [VueApollo Demo](https://gitlab.com/gitlab-org/frontend/vue3-migration-vue-apollo/-/tree/main/src/vue3compat) with Vue.js 2 using `yarn serve` or with Vue.js 3 (compat build) using `yarn serve:vue3`. However latter immediately crashes:
|
||||
|
||||
```javascript
|
||||
Uncaught TypeError: Cannot read properties of undefined (reading 'loading')
|
||||
```
|
||||
|
||||
VueApollo v3 (used for Vue.js 2) fails to initialize in Vue.js compat
|
||||
|
||||
NOTE:
|
||||
While stubbing `Vue.version` will solve VueApollo-related issues in the demo project, it will still lose reactivity on specific scenarios, so an upgrade is still needed
|
||||
|
||||
#### Step 1. Perform upgrade according to library docs
|
||||
|
||||
According to [VueApollo v4 installation guide](https://v4.apollo.vuejs.org/guide/installation.html), we need to install `@vue/apollo-option` (this package provides VueApollo support for Options API) and make changes to our application:
|
||||
|
||||
```diff
|
||||
--- a/src/index.js
|
||||
+++ b/src/index.js
|
||||
@@ -1,19 +1,17 @@
|
||||
-import Vue from "vue";
|
||||
-import VueApollo from "vue-apollo";
|
||||
+import { createApp, h } from "vue";
|
||||
+import { createApolloProvider } from "@vue/apollo-option";
|
||||
|
||||
import Demo from "./components/Demo.vue";
|
||||
import createDefaultClient from "./lib/graphql";
|
||||
|
||||
-Vue.use(VueApollo);
|
||||
-
|
||||
-const apolloProvider = new VueApollo({
|
||||
+const apolloProvider = createApolloProvider({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
-new Vue({
|
||||
- el: "#app",
|
||||
- apolloProvider,
|
||||
- render(h) {
|
||||
+const app = createApp({
|
||||
+ render() {
|
||||
return h(Demo);
|
||||
},
|
||||
});
|
||||
+app.use(apolloProvider);
|
||||
+app.mount("#app");
|
||||
```
|
||||
|
||||
You can view these changes in [01-upgrade-vue-apollo](https://gitlab.com/gitlab-org/frontend/vue3-migration-vue-apollo/-/compare/main...01-upgrade-vue-apollo) branch of demo project
|
||||
|
||||
#### Step 2. Addressing differences in augmenting applications in Vue.js 2 and 3
|
||||
|
||||
In Vue.js 2 tooling like `VueApollo` is initialized in a "lazy" fashion:
|
||||
|
||||
```javascript
|
||||
// We are registering VueApollo "handler" to handle some data LATER
|
||||
Vue.use(VueApollo)
|
||||
// ...
|
||||
// apolloProvider is provided at app instantiation,
|
||||
// previously registered VueApollo will handle that
|
||||
new Vue({ /- ... */, apolloProvider })
|
||||
```
|
||||
|
||||
In Vue.js 3 both steps were merged in one - we are immediately registering the handler and passing configuration:
|
||||
|
||||
```javascript
|
||||
app.use(apolloProvider)
|
||||
```
|
||||
|
||||
In order to backport this behavior, we need the following knowledge:
|
||||
|
||||
- We can access extra options provided to Vue instance via `$options`, so extra `apolloProvider` will be visible as `this.$options.apolloProvider`
|
||||
- We can access the current `app` (in Vue.js 3 meaning) on the Vue instance via `this.$.appContext.app`
|
||||
|
||||
NOTE:
|
||||
We're relying on non-public Vue.js 3 API in this case. However, since `@vue/compat` builds are expected to be available only for 3.2.x branch, we have reduced risks that this API will be changed
|
||||
|
||||
With this knowledge, we can move the initialization of our tooling as early as possible in Vue2 - in the `beforeCreate()` lifecycle hook:
|
||||
|
||||
```diff
|
||||
--- a/src/index.js
|
||||
+++ b/src/index.js
|
||||
@@ -1,4 +1,4 @@
|
||||
-import { createApp, h } from "vue";
|
||||
+import Vue from "vue";
|
||||
import { createApolloProvider } from "@vue/apollo-option";
|
||||
|
||||
import Demo from "./components/Demo.vue";
|
||||
@@ -8,10 +8,13 @@ const apolloProvider = createApolloProvider({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
-const app = createApp({
|
||||
- render() {
|
||||
+new Vue({
|
||||
+ el: "#app",
|
||||
+ apolloProvider,
|
||||
+ render(h) {
|
||||
return h(Demo);
|
||||
},
|
||||
+ beforeCreate() {
|
||||
+ this.$.appContext.app.use(this.$options.apolloProvider);
|
||||
+ },
|
||||
});
|
||||
-app.use(apolloProvider);
|
||||
-app.mount("#app");
|
||||
```
|
||||
|
||||
You can view these changes in [02-bring-back-new-vue](https://gitlab.com/gitlab-org/frontend/vue3-migration-vue-apollo/-/compare/01-upgrade-vue-apollo...02-bring-back-new-vue) branch of demo project
|
||||
|
||||
#### Step 3. Recreating `VueApollo` class
|
||||
|
||||
Vue.js 3 libraries (and Vue.js itself) have a preference for using factories like `createApp` instead of classes (previously `new Vue`)
|
||||
|
||||
`VueApollo` class served two purposes:
|
||||
|
||||
- constructor for creating `apolloProvider`
|
||||
- installation of apollo-related logic in components
|
||||
|
||||
We can utilize `Vue.use(VueApollo)` code, which existed in our codebase, to hide there our mixin and avoid modification of our app code:
|
||||
|
||||
```diff
|
||||
--- a/src/index.js
|
||||
+++ b/src/index.js
|
||||
@@ -4,7 +4,26 @@ import { createApolloProvider } from "@vue/apollo-option";
|
||||
import Demo from "./components/Demo.vue";
|
||||
import createDefaultClient from "./lib/graphql";
|
||||
|
||||
-const apolloProvider = createApolloProvider({
|
||||
+class VueApollo {
|
||||
+ constructor(...args) {
|
||||
+ return createApolloProvider(...args);
|
||||
+ }
|
||||
+
|
||||
+ // called by Vue.use
|
||||
+ static install() {
|
||||
+ Vue.mixin({
|
||||
+ beforeCreate() {
|
||||
+ if (this.$options.apolloProvider) {
|
||||
+ this.$.appContext.app.use(this.$options.apolloProvider);
|
||||
+ }
|
||||
+ },
|
||||
+ });
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+Vue.use(VueApollo);
|
||||
+
|
||||
+const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
@@ -14,7 +33,4 @@ new Vue({
|
||||
render(h) {
|
||||
return h(Demo);
|
||||
},
|
||||
- beforeCreate() {
|
||||
- this.$.appContext.app.use(this.$options.apolloProvider);
|
||||
- },
|
||||
});
|
||||
```
|
||||
|
||||
You can view these changes in [03-recreate-vue-apollo](https://gitlab.com/gitlab-org/frontend/vue3-migration-vue-apollo/-/compare/02-bring-back-new-vue...03-recreate-vue-apollo) branch of demo project
|
||||
|
||||
#### Step 4. Moving `VueApollo` class to a separate file and setting up an alias
|
||||
|
||||
Now, we have almost the same code (excluding import) as in Vue.js 2 version.
|
||||
We will move our facade to the separate file and set up `webpack` conditionally execute it if `vue-apollo` is imported when using Vue.js 3:
|
||||
|
||||
```diff
|
||||
--- a/src/index.js
|
||||
+++ b/src/index.js
|
||||
@@ -1,5 +1,5 @@
|
||||
import Vue from "vue";
|
||||
-import { createApolloProvider } from "@vue/apollo-option";
|
||||
+import VueApollo from "vue-apollo";
|
||||
|
||||
import Demo from "./components/Demo.vue";
|
||||
import createDefaultClient from "./lib/graphql";
|
||||
diff --git a/webpack.config.js b/webpack.config.js
|
||||
index 6160d3f..b8b955f 100644
|
||||
--- a/webpack.config.js
|
||||
+++ b/webpack.config.js
|
||||
@@ -12,6 +12,7 @@ if (USE_VUE3) {
|
||||
|
||||
VUE3_ALIASES = {
|
||||
vue: "@vue/compat",
|
||||
+ "vue-apollo": path.resolve("src/vue3compat/vue-apollo"),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
(moving `VueApollo` class from `index.js` to `vue3compat/vue-apollo.js` as default export is omitted for clarity)
|
||||
|
||||
You can view these changes in [04-add-webpack-alias](https://gitlab.com/gitlab-org/frontend/vue3-migration-vue-apollo/-/compare/03-recreate-vue-apollo...04-add-webpack-alias) branch of demo project
|
||||
|
||||
#### Step 5. Observe the results
|
||||
|
||||
At this point, you should be able again to run **both*- Vue.js 2 version with `yarn serve` and Vue.js 3 one with `yarn serve:vue3`
|
||||
[Final MR](https://gitlab.com/gitlab-org/frontend/vue3-migration-vue-apollo/-/merge_requests/1/diffs) with all changes from previous steps displays no changes to `index.js` (application code), which was our goal
|
||||
|
||||
### Applying this approach in the GitLab project
|
||||
|
||||
In [commit adding VueApollo v4 support](https://gitlab.com/gitlab-org/gitlab/-/commit/e0af7e6479695a28a4fe85a88f90815aa3ce2814) we can see additional nuances not covered by step-by-step guide:
|
||||
|
||||
- We might need to add additional imports to our facades (our code in GitLab uses `ApolloMutation` component)
|
||||
- We need to update aliases not only for webpack but also for jest so our tests could also consume our facade
|
||||
|
|
|
@ -103,31 +103,6 @@ When you upload an image, you can add text to the image and link it to the origi
|
|||
|
||||
If you add a link, it is shown above the uploaded image.
|
||||
|
||||
#### View an alert's logs
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/201846) in GitLab Ultimate 12.8.
|
||||
> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/217768) in GitLab 13.3.
|
||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25455) from GitLab Ultimate to GitLab Free in 12.9.
|
||||
|
||||
Viewing logs from a metrics panel can be useful if you're triaging an
|
||||
application incident and need to [explore logs](../metrics/dashboards/index.md#chart-context-menu)
|
||||
from across your application. These logs help you understand what's affecting
|
||||
your application's performance and how to resolve any problems.
|
||||
|
||||
Prerequisite:
|
||||
|
||||
- You must have at least the Developer role.
|
||||
|
||||
To view the logs for an alert:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Monitor > Alerts**.
|
||||
1. Select the alert you want to view.
|
||||
1. Below the title of the alert, select the **Metrics** tab.
|
||||
1. Select the [menu](../metrics/dashboards/index.md#chart-context-menu) of
|
||||
the metric chart to view options.
|
||||
1. Select **View logs**.
|
||||
|
||||
### Activity feed tab
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1.
|
||||
|
@ -177,6 +152,16 @@ To change an alert's status:
|
|||
To stop email notifications for alert reoccurrences in projects with [email notifications enabled](paging.md#email-notifications-for-alerts),
|
||||
change the alert's status away from **Triggered**.
|
||||
|
||||
#### Resolve an alert by closing the linked incident
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Reporter role.
|
||||
|
||||
When you close an [incident](incidents.md) that is linked to an alert,
|
||||
the linked alert's status changes to **Resolved**.
|
||||
You are then credited with the alert's status change.
|
||||
|
||||
#### As an on-call responder **(PREMIUM)**
|
||||
|
||||
On-call responders can respond to [alert pages](paging.md#escalating-an-alert)
|
||||
|
|
|
@ -207,7 +207,8 @@ the appropriate project and followed up from there.
|
|||
|
||||
### Fields in the new issue form
|
||||
|
||||
> Adding the new issue to an epic [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13847) in GitLab 13.1.
|
||||
> - Adding the new issue to an epic [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13847) in GitLab 13.1.
|
||||
> - Iteration field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/233517) in GitLab 15.6.
|
||||
|
||||
When you're creating a new issue, you can complete the following fields:
|
||||
|
||||
|
@ -222,6 +223,7 @@ When you're creating a new issue, you can complete the following fields:
|
|||
- [Due date](due_dates.md)
|
||||
- [Milestone](../milestones/index.md)
|
||||
- [Labels](../labels.md)
|
||||
- [Iteration](../../group/iterations/index.md)
|
||||
|
||||
## Edit an issue
|
||||
|
||||
|
|
|
@ -13,8 +13,9 @@ module API
|
|||
namespace 'admin' do
|
||||
namespace 'ci' do
|
||||
namespace 'variables' do
|
||||
desc 'Get instance-level variables' do
|
||||
desc 'List all instance-level variables' do
|
||||
success Entities::Ci::Variable
|
||||
tags %w[ci_variables]
|
||||
end
|
||||
params do
|
||||
use :pagination
|
||||
|
@ -25,11 +26,13 @@ module API
|
|||
present paginate(variables), with: Entities::Ci::Variable
|
||||
end
|
||||
|
||||
desc 'Get a specific variable from a group' do
|
||||
desc 'Get the details of a specific instance-level variable' do
|
||||
success Entities::Ci::Variable
|
||||
failure [{ code: 404, message: 'Instance Variable Not Found' }]
|
||||
tags %w[ci_variables]
|
||||
end
|
||||
params do
|
||||
requires :key, type: String, desc: 'The key of the variable'
|
||||
requires :key, type: String, desc: 'The key of a variable'
|
||||
end
|
||||
get ':key' do
|
||||
key = params[:key]
|
||||
|
@ -42,16 +45,18 @@ module API
|
|||
|
||||
desc 'Create a new instance-level variable' do
|
||||
success Entities::Ci::Variable
|
||||
failure [{ code: 400, message: '400 Bad Request' }]
|
||||
tags %w[ci_variables]
|
||||
end
|
||||
route_setting :log_safety, { safe: %w[key], unsafe: %w[value] }
|
||||
params do
|
||||
requires :key,
|
||||
type: String,
|
||||
desc: 'The key of the variable'
|
||||
desc: 'The key of the variable. Max 255 characters'
|
||||
|
||||
requires :value,
|
||||
type: String,
|
||||
desc: 'The value of the variable'
|
||||
desc: 'The value of a variable'
|
||||
|
||||
optional :protected,
|
||||
type: String,
|
||||
|
@ -64,7 +69,7 @@ module API
|
|||
optional :variable_type,
|
||||
type: String,
|
||||
values: ::Ci::InstanceVariable.variable_types.keys,
|
||||
desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
|
||||
desc: 'The type of a variable. Available types are: env_var (default) and file'
|
||||
end
|
||||
post '/' do
|
||||
variable_params = declared_params(include_missing: false)
|
||||
|
@ -78,18 +83,20 @@ module API
|
|||
end
|
||||
end
|
||||
|
||||
desc 'Update an existing instance-variable' do
|
||||
desc 'Update an instance-level variable' do
|
||||
success Entities::Ci::Variable
|
||||
failure [{ code: 404, message: 'Instance Variable Not Found' }]
|
||||
tags %w[ci_variables]
|
||||
end
|
||||
route_setting :log_safety, { safe: %w[key], unsafe: %w[value] }
|
||||
params do
|
||||
optional :key,
|
||||
type: String,
|
||||
desc: 'The key of the variable'
|
||||
desc: 'The key of a variable'
|
||||
|
||||
optional :value,
|
||||
type: String,
|
||||
desc: 'The value of the variable'
|
||||
desc: 'The value of a variable'
|
||||
|
||||
optional :protected,
|
||||
type: String,
|
||||
|
@ -102,7 +109,7 @@ module API
|
|||
optional :variable_type,
|
||||
type: String,
|
||||
values: ::Ci::InstanceVariable.variable_types.keys,
|
||||
desc: 'The type of variable, must be one of env_var or file'
|
||||
desc: 'The type of a variable. Available types are: env_var (default) and file'
|
||||
end
|
||||
put ':key' do
|
||||
variable = ::Ci::InstanceVariable.find_by_key(params[:key])
|
||||
|
@ -120,9 +127,11 @@ module API
|
|||
|
||||
desc 'Delete an existing instance-level variable' do
|
||||
success Entities::Ci::Variable
|
||||
failure [{ code: 404, message: 'Instance Variable Not Found' }]
|
||||
tags %w[ci_variables]
|
||||
end
|
||||
params do
|
||||
requires :key, type: String, desc: 'The key of the variable'
|
||||
requires :key, type: String, desc: 'The key of a variable'
|
||||
end
|
||||
delete ':key' do
|
||||
variable = ::Ci::InstanceVariable.find_by_key(params[:key])
|
||||
|
|
|
@ -171,6 +171,7 @@ module API
|
|||
namespace do
|
||||
# Keep in alphabetical order
|
||||
mount ::API::AccessRequests
|
||||
mount ::API::Admin::Ci::Variables
|
||||
mount ::API::Appearance
|
||||
mount ::API::Applications
|
||||
mount ::API::BroadcastMessages
|
||||
|
@ -180,6 +181,7 @@ module API
|
|||
mount ::API::Ci::Runners
|
||||
mount ::API::Clusters::AgentTokens
|
||||
mount ::API::Clusters::Agents
|
||||
mount ::API::CommitStatuses
|
||||
mount ::API::DeployKeys
|
||||
mount ::API::DeployTokens
|
||||
mount ::API::Deployments
|
||||
|
@ -217,7 +219,6 @@ module API
|
|||
|
||||
# Keep in alphabetical order
|
||||
mount ::API::Admin::BatchedBackgroundMigrations
|
||||
mount ::API::Admin::Ci::Variables
|
||||
mount ::API::Admin::InstanceClusters
|
||||
mount ::API::Admin::PlanLimits
|
||||
mount ::API::Admin::Sidekiq
|
||||
|
|
|
@ -16,14 +16,20 @@ module API
|
|||
before { authenticate! }
|
||||
|
||||
desc "Get a commit's statuses" do
|
||||
success Entities::CommitStatus
|
||||
success code: 200, model: Entities::CommitStatus
|
||||
failure [
|
||||
{ code: 401, message: 'Unauthorized' },
|
||||
{ code: 403, message: 'Forbidden' },
|
||||
{ code: 404, message: 'Not found' }
|
||||
]
|
||||
is_array true
|
||||
end
|
||||
params do
|
||||
requires :sha, type: String, desc: 'The commit hash'
|
||||
optional :ref, type: String, desc: 'The ref'
|
||||
optional :stage, type: String, desc: 'The stage'
|
||||
optional :name, type: String, desc: 'The name'
|
||||
optional :all, type: String, desc: 'Show all statuses, default: false'
|
||||
requires :sha, type: String, desc: 'The commit hash', documentation: { example: '18f3e63d05582537db6d183d9d557be09e1f90c8' }
|
||||
optional :ref, type: String, desc: 'The ref', documentation: { example: 'develop' }
|
||||
optional :stage, type: String, desc: 'The stage', documentation: { example: 'test' }
|
||||
optional :name, type: String, desc: 'The name', documentation: { example: 'bundler:audit' }
|
||||
optional :all, type: Boolean, desc: 'Show all statuses', documentation: { default: false }
|
||||
use :pagination
|
||||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
|
@ -43,19 +49,32 @@ module API
|
|||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
desc 'Post status to a commit' do
|
||||
success Entities::CommitStatus
|
||||
success code: 200, model: Entities::CommitStatus
|
||||
failure [
|
||||
{ code: 400, message: 'Bad request' },
|
||||
{ code: 401, message: 'Unauthorized' },
|
||||
{ code: 403, message: 'Forbidden' },
|
||||
{ code: 404, message: 'Not found' }
|
||||
]
|
||||
end
|
||||
params do
|
||||
requires :sha, type: String, desc: 'The commit hash'
|
||||
requires :state, type: String, desc: 'The state of the status',
|
||||
values: %w(pending running success failed canceled)
|
||||
optional :ref, type: String, desc: 'The ref'
|
||||
optional :target_url, type: String, desc: 'The target URL to associate with this status'
|
||||
optional :description, type: String, desc: 'A short description of the status'
|
||||
optional :name, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"', documentation: { default: 'default' }
|
||||
optional :context, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"', documentation: { default: 'default' }
|
||||
optional :coverage, type: Float, desc: 'The total code coverage'
|
||||
optional :pipeline_id, type: Integer, desc: 'An existing pipeline ID, when multiple pipelines on the same commit SHA have been triggered'
|
||||
requires :sha, type: String, desc: 'The commit hash',
|
||||
documentation: { example: '18f3e63d05582537db6d183d9d557be09e1f90c8' }
|
||||
requires :state, type: String, desc: 'The state of the status',
|
||||
values: %w(pending running success failed canceled),
|
||||
documentation: { example: 'pending' }
|
||||
optional :ref, type: String, desc: 'The ref',
|
||||
documentation: { example: 'develop' }
|
||||
optional :target_url, type: String, desc: 'The target URL to associate with this status',
|
||||
documentation: { example: 'https://gitlab.example.com/thedude/gitlab-foss/builds/91' }
|
||||
optional :description, type: String, desc: 'A short description of the status'
|
||||
optional :name, type: String, desc: 'A string label to differentiate this status from the status of other systems',
|
||||
documentation: { example: 'coverage', default: 'default' }
|
||||
optional :context, type: String, desc: 'A string label to differentiate this status from the status of other systems',
|
||||
documentation: { example: 'coverage', default: 'default' }
|
||||
optional :coverage, type: Float, desc: 'The total code coverage',
|
||||
documentation: { example: 100.0 }
|
||||
optional :pipeline_id, type: Integer, desc: 'An existing pipeline ID, when multiple pipelines on the same commit SHA have been triggered'
|
||||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
post ':id/statuses/:sha' do
|
||||
|
|
|
@ -4,10 +4,15 @@ module API
|
|||
module Entities
|
||||
module Ci
|
||||
class Variable < Grape::Entity
|
||||
expose :variable_type, :key, :value
|
||||
expose :protected?, as: :protected, if: -> (entity, _) { entity.respond_to?(:protected?) }
|
||||
expose :masked?, as: :masked, if: -> (entity, _) { entity.respond_to?(:masked?) }
|
||||
expose :environment_scope, if: -> (entity, _) { entity.respond_to?(:environment_scope) }
|
||||
expose :variable_type, documentation: { type: 'string', example: 'env_var' }
|
||||
expose :key, documentation: { type: 'string', example: 'TEST_VARIABLE_1' }
|
||||
expose :value, documentation: { type: 'string', example: 'TEST_1' }
|
||||
expose :protected?, as: :protected, if: -> (entity, _) { entity.respond_to?(:protected?) },
|
||||
documentation: { type: 'boolean' }
|
||||
expose :masked?, as: :masked, if: -> (entity, _) { entity.respond_to?(:masked?) },
|
||||
documentation: { type: 'boolean' }
|
||||
expose :environment_scope, if: -> (entity, _) { entity.respond_to?(:environment_scope) },
|
||||
documentation: { type: 'string', example: '*' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,8 +3,22 @@
|
|||
module API
|
||||
module Entities
|
||||
class CommitStatus < Grape::Entity
|
||||
expose :id, :sha, :ref, :status, :name, :target_url, :description,
|
||||
:created_at, :started_at, :finished_at, :allow_failure, :coverage
|
||||
expose :id, documentation: { type: 'integer', example: 93 }
|
||||
expose :sha, documentation: { type: 'string', example: '18f3e63d05582537db6d183d9d557be09e1f90c8' }
|
||||
expose :ref, documentation: { type: 'string', example: 'develop' }
|
||||
expose :status, documentation: { type: 'string', example: 'success' }
|
||||
expose :name, documentation: { type: 'string', example: 'default' }
|
||||
expose :target_url, documentation: {
|
||||
type: 'string',
|
||||
example: 'https://gitlab.example.com/thedude/gitlab-foss/builds/91'
|
||||
}
|
||||
expose :description, documentation: { type: 'string' }
|
||||
expose :created_at, documentation: { type: 'dateTime', example: '2016-01-19T09:05:50.355Z' }
|
||||
expose :started_at, documentation: { type: 'dateTime', example: '2016-01-20T08:40:25.832Z' }
|
||||
expose :finished_at, documentation: { type: 'dateTime', example: '2016-01-21T08:40:25.832Z' }
|
||||
expose :allow_failure, documentation: { type: 'boolean', example: false }
|
||||
expose :coverage, documentation: { type: 'number', format: 'float', example: 98.29 }
|
||||
|
||||
expose :author, using: Entities::UserBasic
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
module API
|
||||
module Entities
|
||||
class CustomAttribute < Grape::Entity
|
||||
expose :key
|
||||
expose :value
|
||||
expose :key, documentation: { type: 'string', example: 'foo' }
|
||||
expose :value, documentation: { type: 'string', example: 'bar' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,8 +9,17 @@ module API
|
|||
user.avatar_url(only_path: false)
|
||||
end
|
||||
|
||||
expose :avatar_path, if: ->(user, options) { options.fetch(:only_path, false) && user.avatar_path }
|
||||
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
|
||||
expose(
|
||||
:avatar_path,
|
||||
documentation: {
|
||||
type: 'string',
|
||||
example: '/user/avatar/28/The-Big-Lebowski-400-400.png'
|
||||
},
|
||||
if: ->(user, options) { options.fetch(:only_path, false) && user.avatar_path }
|
||||
)
|
||||
|
||||
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes,
|
||||
documentation: { is_array: true }
|
||||
|
||||
expose :web_url, documentation: { type: 'string', example: 'https://gitlab.example.com/root' } do |user, options|
|
||||
Gitlab::Routing.url_helpers.user_url(user)
|
||||
|
|
|
@ -9,20 +9,34 @@ module Gitlab
|
|||
MAX_PATTERN_COMPARISONS = 10_000
|
||||
|
||||
def initialize(globs)
|
||||
globs = Array(globs)
|
||||
|
||||
@top_level_only = globs.all?(&method(:top_level_glob?))
|
||||
@exact_globs, @pattern_globs = globs.partition(&method(:exact_glob?))
|
||||
@globs = Array(globs)
|
||||
@top_level_only = @globs.all?(&method(:top_level_glob?))
|
||||
end
|
||||
|
||||
def satisfied_by?(_pipeline, context)
|
||||
paths = worktree_paths(context)
|
||||
exact_globs, pattern_globs = separate_globs(context)
|
||||
|
||||
exact_matches?(paths) || pattern_matches?(paths)
|
||||
exact_matches?(paths, exact_globs) || pattern_matches?(paths, pattern_globs)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def separate_globs(context)
|
||||
if ::Feature.enabled?(:ci_variable_expansion_in_rules_exists, context.project)
|
||||
expanded_globs = expand_globs(context)
|
||||
expanded_globs.partition(&method(:exact_glob?))
|
||||
else
|
||||
@globs.partition(&method(:exact_glob?))
|
||||
end
|
||||
end
|
||||
|
||||
def expand_globs(context)
|
||||
@globs.map do |glob|
|
||||
ExpandVariables.expand_existing(glob, -> { context.variables_hash })
|
||||
end
|
||||
end
|
||||
|
||||
def worktree_paths(context)
|
||||
return [] unless context.project
|
||||
|
||||
|
@ -33,13 +47,16 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def exact_matches?(paths)
|
||||
@exact_globs.any? { |glob| paths.bsearch { |path| glob <=> path } }
|
||||
def exact_matches?(paths, exact_globs)
|
||||
exact_globs.any? do |glob|
|
||||
paths.bsearch { |path| glob <=> path }
|
||||
end
|
||||
end
|
||||
|
||||
def pattern_matches?(paths)
|
||||
def pattern_matches?(paths, pattern_globs)
|
||||
comparisons = 0
|
||||
@pattern_globs.any? do |glob|
|
||||
|
||||
pattern_globs.any? do |glob|
|
||||
paths.any? do |path|
|
||||
comparisons += 1
|
||||
comparisons > MAX_PATTERN_COMPARISONS || pattern_match?(glob, path)
|
||||
|
|
|
@ -54,6 +54,17 @@ module Gitlab
|
|||
init_prometheus_metrics
|
||||
end
|
||||
|
||||
##
|
||||
# Configuration for Watchdog, use like:
|
||||
#
|
||||
# watchdog.configure do |config|
|
||||
# config.handler = Gitlab::Memory::Watchdog::TermProcessHandler
|
||||
# config.sleep_time_seconds = 60
|
||||
# config.logger = Gitlab::AppLogger
|
||||
# config.monitors do |stack|
|
||||
# stack.push MyMonitorClass, args*, max_strikes:, kwargs**, &block
|
||||
# end
|
||||
# end
|
||||
def configure
|
||||
yield @configuration
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ module Gitlab
|
|||
@monitors = []
|
||||
end
|
||||
|
||||
def use(monitor_class, *args, **kwargs, &block)
|
||||
def push(monitor_class, *args, **kwargs, &block)
|
||||
remove(monitor_class)
|
||||
@monitors.push(build_monitor_state(monitor_class, *args, **kwargs, &block))
|
||||
end
|
||||
|
@ -39,11 +39,12 @@ module Gitlab
|
|||
|
||||
DEFAULT_SLEEP_TIME_SECONDS = 60
|
||||
|
||||
attr_reader :monitors
|
||||
attr_writer :logger, :handler, :sleep_time_seconds
|
||||
|
||||
def initialize
|
||||
@monitors = MonitorStack.new
|
||||
def monitors
|
||||
@monitor_stack ||= MonitorStack.new
|
||||
yield @monitor_stack if block_given?
|
||||
@monitor_stack
|
||||
end
|
||||
|
||||
def handler
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Memory
|
||||
class Watchdog
|
||||
class Configurator
|
||||
class << self
|
||||
def configure_for_puma
|
||||
lambda do |config|
|
||||
sleep_time_seconds = ENV.fetch('GITLAB_MEMWD_SLEEP_TIME_SEC', 60).to_i
|
||||
config.logger = Gitlab::AppLogger
|
||||
config.handler = Gitlab::Memory::Watchdog::PumaHandler.new
|
||||
config.sleep_time_seconds = sleep_time_seconds
|
||||
config.monitors(&configure_monitors_for_puma)
|
||||
end
|
||||
end
|
||||
|
||||
def configure_for_sidekiq
|
||||
lambda do |config|
|
||||
sleep_time_seconds = [ENV.fetch('SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL', 3).to_i, 2].max
|
||||
config.logger = Sidekiq.logger
|
||||
config.handler = Gitlab::Memory::Watchdog::TermProcessHandler.new
|
||||
config.sleep_time_seconds = sleep_time_seconds
|
||||
config.monitors(&configure_monitors_for_sidekiq)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def configure_monitors_for_puma
|
||||
lambda do |stack|
|
||||
max_strikes = ENV.fetch('GITLAB_MEMWD_MAX_STRIKES', 5).to_i
|
||||
|
||||
if Gitlab::Utils.to_boolean(ENV['DISABLE_PUMA_WORKER_KILLER'])
|
||||
max_heap_frag = ENV.fetch('GITLAB_MEMWD_MAX_HEAP_FRAG', 0.5).to_f
|
||||
max_mem_growth = ENV.fetch('GITLAB_MEMWD_MAX_MEM_GROWTH', 3.0).to_f
|
||||
|
||||
# stack.push MonitorClass, args*, max_strikes:, kwargs**, &block
|
||||
stack.push Gitlab::Memory::Watchdog::Monitor::HeapFragmentation,
|
||||
max_heap_fragmentation: max_heap_frag,
|
||||
max_strikes: max_strikes
|
||||
|
||||
stack.push Gitlab::Memory::Watchdog::Monitor::UniqueMemoryGrowth,
|
||||
max_mem_growth: max_mem_growth,
|
||||
max_strikes: max_strikes
|
||||
else
|
||||
memory_limit = ENV.fetch('PUMA_WORKER_MAX_MEMORY', 1200).to_i
|
||||
|
||||
stack.push Gitlab::Memory::Watchdog::Monitor::RssMemoryLimit,
|
||||
memory_limit: memory_limit,
|
||||
max_strikes: max_strikes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def configure_monitors_for_sidekiq
|
||||
# NOP - At the moment we don't run watchdog for Sidekiq
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,7 +22,7 @@ module Gitlab
|
|||
def call
|
||||
heap_fragmentation = Gitlab::Metrics::Memory.gc_heap_fragmentation
|
||||
|
||||
return { threshold_violated: false, payload: {} } unless heap_fragmentation > max_heap_fragmentation
|
||||
return { threshold_violated: false, payload: {} } if heap_fragmentation <= max_heap_fragmentation
|
||||
|
||||
{ threshold_violated: true, payload: payload(heap_fragmentation) }
|
||||
end
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Memory
|
||||
class Watchdog
|
||||
module Monitor
|
||||
class RssMemoryLimit
|
||||
attr_reader :memory_limit
|
||||
|
||||
def initialize(memory_limit:)
|
||||
@memory_limit = memory_limit
|
||||
end
|
||||
|
||||
def call
|
||||
worker_rss = Gitlab::Metrics::System.memory_usage_rss[:total]
|
||||
|
||||
return { threshold_violated: false, payload: {} } if worker_rss <= memory_limit
|
||||
|
||||
{ threshold_violated: true, payload: payload(worker_rss, memory_limit) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def payload(worker_rss, memory_limit)
|
||||
{
|
||||
message: 'rss memory limit exceeded',
|
||||
memwd_rss_bytes: worker_rss,
|
||||
memwd_max_rss_bytes: memory_limit
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,7 +16,7 @@ module Gitlab
|
|||
reference_uss = reference_mem[:uss]
|
||||
memory_limit = max_mem_growth * reference_uss
|
||||
|
||||
return { threshold_violated: false, payload: {} } unless worker_uss > memory_limit
|
||||
return { threshold_violated: false, payload: {} } if worker_uss <= memory_limit
|
||||
|
||||
{ threshold_violated: true, payload: payload(worker_uss, reference_uss, memory_limit) }
|
||||
end
|
||||
|
|
|
@ -7787,9 +7787,6 @@ msgstr ""
|
|||
msgid "Certificate Subject"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change Failure Rate"
|
||||
msgstr ""
|
||||
|
||||
|
@ -12008,9 +12005,6 @@ msgstr ""
|
|||
msgid "DORA4Metrics|%{startDate} - %{endDate}"
|
||||
msgstr ""
|
||||
|
||||
msgid "DORA4Metrics|30 days before that"
|
||||
msgstr ""
|
||||
|
||||
msgid "DORA4Metrics|Average (last %{days}d)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -12035,9 +12029,6 @@ msgstr ""
|
|||
msgid "DORA4Metrics|Deployment frequency"
|
||||
msgstr ""
|
||||
|
||||
msgid "DORA4Metrics|Last 30 days"
|
||||
msgstr ""
|
||||
|
||||
msgid "DORA4Metrics|Lead time for changes"
|
||||
msgstr ""
|
||||
|
||||
|
@ -12053,6 +12044,9 @@ msgstr ""
|
|||
msgid "DORA4Metrics|Median time an incident was open in a production environment over the given time period."
|
||||
msgstr ""
|
||||
|
||||
msgid "DORA4Metrics|Month to date"
|
||||
msgstr ""
|
||||
|
||||
msgid "DORA4Metrics|No incidents during this period"
|
||||
msgstr ""
|
||||
|
||||
|
@ -48511,9 +48505,6 @@ msgstr ""
|
|||
msgid "mrWidget|%{boldHeaderStart}Looks like there's no pipeline here.%{boldHeaderEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "mrWidget|%{linkStart}Set up now%{linkEnd} to analyze your source code for known security vulnerabilities."
|
||||
msgstr ""
|
||||
|
||||
msgid "mrWidget|%{mergeError}."
|
||||
msgstr ""
|
||||
|
||||
|
@ -48758,9 +48749,6 @@ msgstr ""
|
|||
msgid "mrWidget|Revoke approval"
|
||||
msgstr ""
|
||||
|
||||
msgid "mrWidget|SAST and Secret Detection is not enabled."
|
||||
msgstr ""
|
||||
|
||||
msgid "mrWidget|Set by %{merge_author} to be added to the merge train when the pipeline succeeds"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ end
|
|||
|
||||
def delete_hook
|
||||
FileUtils.rm(HOOK_PATH)
|
||||
system("git checkout master")
|
||||
puts "#{SHELL_YELLOW}Security harness removed -- you can now push to all remotes.#{SHELL_CLEAR}"
|
||||
end
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ RSpec.describe Pajamas::SpinnerComponent, type: :component do
|
|||
describe 'inline' do
|
||||
context 'by default' do
|
||||
it 'renders a div' do
|
||||
expect(page).to have_css 'div.gl-spinner'
|
||||
expect(page).to have_css 'div.gl-spinner-container'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -43,7 +43,7 @@ RSpec.describe Pajamas::SpinnerComponent, type: :component do
|
|||
let(:options) { { inline: true } }
|
||||
|
||||
it 'renders a span' do
|
||||
expect(page).to have_css 'span.gl-spinner'
|
||||
expect(page).to have_css 'span.gl-spinner-container'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,14 +9,28 @@ module Pajamas
|
|||
# @param label text
|
||||
# @param size select [[small, sm], [medium, md], [large, lg], [extra large, xl]]
|
||||
def default(inline: false, label: "Loading", size: :md)
|
||||
render(Pajamas::SpinnerComponent.new(inline: inline, label: label, size: size))
|
||||
render Pajamas::SpinnerComponent.new(
|
||||
inline: inline,
|
||||
label: label,
|
||||
size: size
|
||||
)
|
||||
end
|
||||
|
||||
# Use a light spinner on dark backgrounds
|
||||
# Use a light spinner on dark backgrounds.
|
||||
#
|
||||
# @display bg_color "#222"
|
||||
def light
|
||||
render(Pajamas::SpinnerComponent.new(color: :light))
|
||||
end
|
||||
|
||||
# Any extra HTML attributes like `class`, `data` or `id` get automatically applied to the spinner container element.
|
||||
#
|
||||
def extra_attributes
|
||||
render Pajamas::SpinnerComponent.new(
|
||||
class: "js-do-something",
|
||||
data: { foo: "bar" },
|
||||
id: "my-special-spinner"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { getDateWithUTC, newDateAsLocaleTime } from '~/lib/utils/datetime/date_calculation_utility';
|
||||
import {
|
||||
getDateWithUTC,
|
||||
newDateAsLocaleTime,
|
||||
nSecondsAfter,
|
||||
nSecondsBefore,
|
||||
} from '~/lib/utils/datetime/date_calculation_utility';
|
||||
|
||||
describe('newDateAsLocaleTime', () => {
|
||||
it.each`
|
||||
|
@ -31,3 +36,33 @@ describe('getDateWithUTC', () => {
|
|||
expect(getDateWithUTC(date)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('nSecondsAfter', () => {
|
||||
const start = new Date('2022-03-22T01:23:45.678Z');
|
||||
it.each`
|
||||
date | seconds | expected
|
||||
${start} | ${0} | ${start}
|
||||
${start} | ${1} | ${new Date('2022-03-22T01:23:46.678Z')}
|
||||
${start} | ${5} | ${new Date('2022-03-22T01:23:50.678Z')}
|
||||
${start} | ${60} | ${new Date('2022-03-22T01:24:45.678Z')}
|
||||
${start} | ${3600} | ${new Date('2022-03-22T02:23:45.678Z')}
|
||||
${start} | ${86400} | ${new Date('2022-03-23T01:23:45.678Z')}
|
||||
`('returns $expected given $string', ({ date, seconds, expected }) => {
|
||||
expect(nSecondsAfter(date, seconds)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('nSecondsBefore', () => {
|
||||
const start = new Date('2022-03-22T01:23:45.678Z');
|
||||
it.each`
|
||||
date | seconds | expected
|
||||
${start} | ${0} | ${start}
|
||||
${start} | ${1} | ${new Date('2022-03-22T01:23:44.678Z')}
|
||||
${start} | ${5} | ${new Date('2022-03-22T01:23:40.678Z')}
|
||||
${start} | ${60} | ${new Date('2022-03-22T01:22:45.678Z')}
|
||||
${start} | ${3600} | ${new Date('2022-03-22T00:23:45.678Z')}
|
||||
${start} | ${86400} | ${new Date('2022-03-21T01:23:45.678Z')}
|
||||
`('returns $expected given $string', ({ date, seconds, expected }) => {
|
||||
expect(nSecondsBefore(date, seconds)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -234,7 +234,7 @@ RSpec.describe IconsHelper do
|
|||
describe 'gl_loading_icon' do
|
||||
it 'returns the default spinner markup' do
|
||||
expect(gl_loading_icon.to_s)
|
||||
.to eq '<div class="gl-spinner-container" role="status"><span class="gl-spinner gl-spinner-dark gl-spinner-sm gl-vertical-align-text-bottom!" aria-label="Loading"></span></div>'
|
||||
.to eq '<div class="gl-spinner-container" role="status"><span aria-label="Loading" class="gl-spinner gl-spinner-sm gl-spinner-dark gl-vertical-align-text-bottom!"></span></div>'
|
||||
end
|
||||
|
||||
context 'when css_class is provided' do
|
||||
|
|
|
@ -29,28 +29,10 @@ RSpec.describe 'memory watchdog' do
|
|||
run_initializer
|
||||
end
|
||||
|
||||
shared_examples 'starts configured watchdog' do |handler_class|
|
||||
let(:configuration) { Gitlab::Memory::Watchdog::Configuration.new }
|
||||
let(:watchdog_monitors_params) do
|
||||
{
|
||||
Gitlab::Memory::Watchdog::Monitor::HeapFragmentation => {
|
||||
max_heap_fragmentation: max_heap_fragmentation,
|
||||
max_strikes: max_strikes
|
||||
},
|
||||
Gitlab::Memory::Watchdog::Monitor::UniqueMemoryGrowth => {
|
||||
max_mem_growth: max_mem_growth,
|
||||
max_strikes: max_strikes
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
shared_examples 'starts configured watchdog' do |configure_monitor_method|
|
||||
shared_examples 'configures and starts watchdog' do
|
||||
it "correctly configures and starts watchdog", :aggregate_failures do
|
||||
expect(watchdog).to receive(:configure).and_yield(configuration)
|
||||
|
||||
watchdog_monitors_params.each do |monitor_class, params|
|
||||
expect(configuration.monitors).to receive(:use).with(monitor_class, **params)
|
||||
end
|
||||
expect(Gitlab::Memory::Watchdog::Configurator).to receive(configure_monitor_method)
|
||||
|
||||
expect(Gitlab::Memory::Watchdog).to receive(:new).and_return(watchdog)
|
||||
expect(Gitlab::BackgroundTask).to receive(:new).with(watchdog).and_return(background_task)
|
||||
|
@ -58,71 +40,24 @@ RSpec.describe 'memory watchdog' do
|
|||
expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start).and_yield
|
||||
|
||||
run_initializer
|
||||
|
||||
expect(configuration.handler).to be_an_instance_of(handler_class)
|
||||
expect(configuration.logger).to eq(logger)
|
||||
expect(configuration.sleep_time_seconds).to eq(sleep_time_seconds)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when settings are not passed through the environment' do
|
||||
let(:max_strikes) { 5 }
|
||||
let(:max_heap_fragmentation) { 0.5 }
|
||||
let(:max_mem_growth) { 3.0 }
|
||||
let(:sleep_time_seconds) { 60 }
|
||||
|
||||
include_examples 'configures and starts watchdog'
|
||||
end
|
||||
|
||||
context 'when settings are passed through the environment' do
|
||||
let(:max_strikes) { 6 }
|
||||
let(:max_heap_fragmentation) { 0.4 }
|
||||
let(:max_mem_growth) { 2.0 }
|
||||
let(:sleep_time_seconds) { 50 }
|
||||
|
||||
before do
|
||||
stub_env('GITLAB_MEMWD_MAX_STRIKES', 6)
|
||||
stub_env('GITLAB_MEMWD_SLEEP_TIME_SEC', 50)
|
||||
stub_env('GITLAB_MEMWD_MAX_MEM_GROWTH', 2.0)
|
||||
stub_env('GITLAB_MEMWD_MAX_HEAP_FRAG', 0.4)
|
||||
end
|
||||
|
||||
include_examples 'configures and starts watchdog'
|
||||
end
|
||||
end
|
||||
|
||||
# In tests, the Puma constant does not exist so we cannot use a verified double.
|
||||
# rubocop: disable RSpec/VerifiedDoubles
|
||||
context 'when puma' do
|
||||
let(:puma) do
|
||||
Class.new do
|
||||
def self.cli_config
|
||||
Struct.new(:options).new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
stub_const('Puma', puma)
|
||||
stub_const('Puma::Cluster::WorkerHandle', double.as_null_object)
|
||||
|
||||
allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
|
||||
end
|
||||
|
||||
it_behaves_like 'starts configured watchdog', Gitlab::Memory::Watchdog::PumaHandler
|
||||
it_behaves_like 'starts configured watchdog', :configure_for_puma
|
||||
end
|
||||
# rubocop: enable RSpec/VerifiedDoubles
|
||||
|
||||
context 'when sidekiq' do
|
||||
before do
|
||||
allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
|
||||
end
|
||||
|
||||
it_behaves_like 'starts configured watchdog', Gitlab::Memory::Watchdog::TermProcessHandler
|
||||
end
|
||||
|
||||
context 'when other runtime' do
|
||||
it_behaves_like 'starts configured watchdog', Gitlab::Memory::Watchdog::NullHandler
|
||||
it_behaves_like 'starts configured watchdog', :configure_for_sidekiq
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Rails YAML safe load' do
|
||||
let(:unsafe_load) { false }
|
||||
|
||||
let(:klass) do
|
||||
Class.new(ActiveRecord::Base) do
|
||||
self.table_name = 'issues'
|
||||
|
||||
serialize :description
|
||||
end
|
||||
end
|
||||
|
||||
let(:instance) { klass.new(description: data) }
|
||||
|
||||
context 'with default permitted classes' do
|
||||
let(:data) do
|
||||
{
|
||||
'time' => Time.now,
|
||||
'date' => Date.today,
|
||||
'number' => 1,
|
||||
'hashie-array' => Hashie::Array.new([1, 2]),
|
||||
'array' => [5, 6]
|
||||
}
|
||||
end
|
||||
|
||||
it 'deserializes data' do
|
||||
instance.save!
|
||||
|
||||
expect(klass.find(instance.id).description).to eq(data)
|
||||
end
|
||||
|
||||
context 'with unpermitted classes' do
|
||||
let(:data) { { 'test' => create(:user) } }
|
||||
|
||||
it 'throws an exception' do
|
||||
expect { instance.save! }.to raise_error(Psych::DisallowedClass)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,11 +4,47 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do
|
||||
describe '#satisfied_by?' do
|
||||
shared_examples 'an exists rule with a context' do
|
||||
subject(:satisfied_by?) { described_class.new(globs).satisfied_by?(nil, context) }
|
||||
|
||||
shared_examples 'a rules:exists with a context' do
|
||||
it_behaves_like 'a glob matching rule' do
|
||||
let(:project) { create(:project, :custom_repo, files: files) }
|
||||
end
|
||||
|
||||
context 'when the rules:exists has a variable' do
|
||||
let_it_be(:project) { create(:project, :custom_repo, files: { 'helm/helm_file.txt' => '' }) }
|
||||
|
||||
let(:globs) { ['$HELM_DIR/**/*'] }
|
||||
|
||||
let(:variables_hash) do
|
||||
{ 'HELM_DIR' => 'helm' }
|
||||
end
|
||||
|
||||
before do
|
||||
allow(context).to receive(:variables_hash).and_return(variables_hash)
|
||||
end
|
||||
|
||||
context 'when the ci_variables_rules_exists FF is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_variable_expansion_in_rules_exists: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when the ci_variables_rules_exists FF is enabled' do
|
||||
context 'when the context has the specified variables' do
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when variable expansion does not match' do
|
||||
let(:variables_hash) { {} }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'after pattern comparision limit is reached' do
|
||||
let(:globs) { ['*definitely_not_a_matching_glob*'] }
|
||||
let(:project) { create(:project, :repository) }
|
||||
|
@ -22,26 +58,24 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do
|
|||
end
|
||||
end
|
||||
|
||||
subject(:satisfied_by?) { described_class.new(globs).satisfied_by?(nil, context) }
|
||||
|
||||
context 'when context is Build::Context::Build' do
|
||||
it_behaves_like 'an exists rule with a context' do
|
||||
context 'when the rules are being evaluated at job level' do
|
||||
it_behaves_like 'a rules:exists with a context' do
|
||||
let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) }
|
||||
let(:context) { Gitlab::Ci::Build::Context::Build.new(pipeline, sha: project.repository.commit.sha) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when context is Build::Context::Global' do
|
||||
it_behaves_like 'an exists rule with a context' do
|
||||
context 'when the rules are being evaluated for an entire pipeline' do
|
||||
it_behaves_like 'a rules:exists with a context' do
|
||||
let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) }
|
||||
let(:context) { Gitlab::Ci::Build::Context::Global.new(pipeline, yaml_variables: {}) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when context is Config::External::Context' do
|
||||
context 'when rules are being evaluated with `include`' do
|
||||
let(:context) { Gitlab::Ci::Config::External::Context.new(project: project, sha: sha) }
|
||||
|
||||
it_behaves_like 'an exists rule with a context' do
|
||||
it_behaves_like 'a rules:exists with a context' do
|
||||
let(:sha) { project.repository.commit.sha }
|
||||
end
|
||||
|
||||
|
|
|
@ -78,36 +78,53 @@ RSpec.describe Gitlab::Memory::Watchdog::Configuration do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when two monitors are configured to be used' do
|
||||
before do
|
||||
configuration.monitors.use monitor_class_1, false, { message: 'monitor_1_text' }, max_strikes: 5
|
||||
configuration.monitors.use monitor_class_2, true, { message: 'monitor_2_text' }, max_strikes: 0
|
||||
context 'when two different monitor class are configured' do
|
||||
shared_examples 'executes monitors and returns correct results' do
|
||||
it 'calls each monitor and returns correct results', :aggregate_failures do
|
||||
payloads = []
|
||||
thresholds = []
|
||||
strikes = []
|
||||
monitor_names = []
|
||||
|
||||
configuration.monitors.call_each do |result|
|
||||
payloads << result.payload
|
||||
thresholds << result.threshold_violated?
|
||||
strikes << result.strikes_exceeded?
|
||||
monitor_names << result.monitor_name
|
||||
end
|
||||
|
||||
expect(payloads).to eq([payload1, payload2])
|
||||
expect(thresholds).to eq([false, true])
|
||||
expect(strikes).to eq([false, true])
|
||||
expect(monitor_names).to eq([:monitor1, :monitor2])
|
||||
end
|
||||
end
|
||||
|
||||
it 'calls each monitor and returns correct results', :aggregate_failures do
|
||||
payloads = []
|
||||
thresholds = []
|
||||
strikes = []
|
||||
monitor_names = []
|
||||
|
||||
configuration.monitors.call_each do |result|
|
||||
payloads << result.payload
|
||||
thresholds << result.threshold_violated?
|
||||
strikes << result.strikes_exceeded?
|
||||
monitor_names << result.monitor_name
|
||||
context 'when monitors are configured inline' do
|
||||
before do
|
||||
configuration.monitors.push monitor_class_1, false, { message: 'monitor_1_text' }, max_strikes: 5
|
||||
configuration.monitors.push monitor_class_2, true, { message: 'monitor_2_text' }, max_strikes: 0
|
||||
end
|
||||
|
||||
expect(payloads).to eq([payload1, payload2])
|
||||
expect(thresholds).to eq([false, true])
|
||||
expect(strikes).to eq([false, true])
|
||||
expect(monitor_names).to eq([:monitor1, :monitor2])
|
||||
include_examples 'executes monitors and returns correct results'
|
||||
end
|
||||
|
||||
context 'when monitors are configured in a block' do
|
||||
before do
|
||||
configuration.monitors do |stack|
|
||||
stack.push monitor_class_1, false, { message: 'monitor_1_text' }, max_strikes: 5
|
||||
stack.push monitor_class_2, true, { message: 'monitor_2_text' }, max_strikes: 0
|
||||
end
|
||||
end
|
||||
|
||||
include_examples 'executes monitors and returns correct results'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when same monitor class is configured to be used twice' do
|
||||
context 'when same monitor class is configured twice' do
|
||||
before do
|
||||
configuration.monitors.use monitor_class_1, max_strikes: 1
|
||||
configuration.monitors.use monitor_class_1, max_strikes: 1
|
||||
configuration.monitors.push monitor_class_1, max_strikes: 1
|
||||
configuration.monitors.push monitor_class_1, max_strikes: 1
|
||||
end
|
||||
|
||||
it 'calls same monitor only once' do
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'prometheus/client'
|
||||
require_dependency 'gitlab/cluster/lifecycle_events'
|
||||
|
||||
RSpec.describe Gitlab::Memory::Watchdog::Configurator do
|
||||
shared_examples 'as configurator' do |handler_class, sleep_time_env, sleep_time|
|
||||
it 'configures the correct handler' do
|
||||
configurator.call(configuration)
|
||||
|
||||
expect(configuration.handler).to be_an_instance_of(handler_class)
|
||||
end
|
||||
|
||||
it 'configures the correct logger' do
|
||||
configurator.call(configuration)
|
||||
|
||||
expect(configuration.logger).to eq(logger)
|
||||
end
|
||||
|
||||
context 'when sleep_time_seconds is not passed through the environment' do
|
||||
let(:sleep_time_seconds) { sleep_time }
|
||||
|
||||
it 'configures the correct sleep time' do
|
||||
configurator.call(configuration)
|
||||
|
||||
expect(configuration.sleep_time_seconds).to eq(sleep_time_seconds)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sleep_time_seconds is passed through the environment' do
|
||||
let(:sleep_time_seconds) { sleep_time - 1 }
|
||||
|
||||
before do
|
||||
stub_env(sleep_time_env, sleep_time - 1)
|
||||
end
|
||||
|
||||
it 'configures the correct sleep time' do
|
||||
configurator.call(configuration)
|
||||
|
||||
expect(configuration.sleep_time_seconds).to eq(sleep_time_seconds)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'as monitor configurator' do
|
||||
it 'executes monitors and returns correct results' do
|
||||
configurator.call(configuration)
|
||||
|
||||
payloads = {}
|
||||
configuration.monitors.call_each do |result|
|
||||
payloads[result.monitor_name] = result.payload
|
||||
end
|
||||
|
||||
expect(payloads).to eq(expected_payloads)
|
||||
end
|
||||
end
|
||||
|
||||
let(:configuration) { Gitlab::Memory::Watchdog::Configuration.new }
|
||||
|
||||
# In tests, the Puma constant does not exist so we cannot use a verified double.
|
||||
# rubocop: disable RSpec/VerifiedDoubles
|
||||
describe '.configure_for_puma' do
|
||||
let(:logger) { Gitlab::AppLogger }
|
||||
let(:puma) do
|
||||
Class.new do
|
||||
def self.cli_config
|
||||
Struct.new(:options).new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
subject(:configurator) { described_class.configure_for_puma }
|
||||
|
||||
def stub_prometheus_metrics
|
||||
gauge = instance_double(::Prometheus::Client::Gauge)
|
||||
allow(Gitlab::Metrics).to receive(:gauge).and_return(gauge)
|
||||
allow(gauge).to receive(:set)
|
||||
end
|
||||
|
||||
before do
|
||||
stub_const('Puma', puma)
|
||||
stub_const('Puma::Cluster::WorkerHandle', double.as_null_object)
|
||||
stub_prometheus_metrics
|
||||
end
|
||||
|
||||
it_behaves_like 'as configurator',
|
||||
Gitlab::Memory::Watchdog::PumaHandler,
|
||||
'GITLAB_MEMWD_SLEEP_TIME_SEC',
|
||||
60
|
||||
|
||||
context 'with DISABLE_PUMA_WORKER_KILLER set to true' do
|
||||
let(:primary_memory) { 2048 }
|
||||
let(:worker_memory) { max_mem_growth * primary_memory + 1 }
|
||||
let(:expected_payloads) do
|
||||
{
|
||||
heap_fragmentation: {
|
||||
message: 'heap fragmentation limit exceeded',
|
||||
memwd_cur_heap_frag: max_heap_fragmentation + 0.1,
|
||||
memwd_max_heap_frag: max_heap_fragmentation,
|
||||
memwd_max_strikes: max_strikes,
|
||||
memwd_cur_strikes: 1
|
||||
|
||||
},
|
||||
unique_memory_growth: {
|
||||
message: 'memory limit exceeded',
|
||||
memwd_uss_bytes: worker_memory,
|
||||
memwd_ref_uss_bytes: primary_memory,
|
||||
memwd_max_uss_bytes: max_mem_growth * primary_memory,
|
||||
memwd_max_strikes: max_strikes,
|
||||
memwd_cur_strikes: 1
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_env('DISABLE_PUMA_WORKER_KILLER', true)
|
||||
allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(max_heap_fragmentation + 0.1)
|
||||
allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return({ uss: worker_memory })
|
||||
allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(
|
||||
pid: Gitlab::Cluster::PRIMARY_PID
|
||||
).and_return({ uss: primary_memory })
|
||||
end
|
||||
|
||||
context 'when settings are set via environment variables' do
|
||||
let(:max_heap_fragmentation) { 0.4 }
|
||||
let(:max_mem_growth) { 4.0 }
|
||||
let(:max_strikes) { 4 }
|
||||
|
||||
before do
|
||||
stub_env('GITLAB_MEMWD_MAX_HEAP_FRAG', 0.4)
|
||||
stub_env('GITLAB_MEMWD_MAX_MEM_GROWTH', 4.0)
|
||||
stub_env('GITLAB_MEMWD_MAX_STRIKES', 4)
|
||||
end
|
||||
|
||||
it_behaves_like 'as monitor configurator'
|
||||
end
|
||||
|
||||
context 'when settings are not set via environment variables' do
|
||||
let(:max_heap_fragmentation) { 0.5 }
|
||||
let(:max_mem_growth) { 3.0 }
|
||||
let(:max_strikes) { 5 }
|
||||
|
||||
it_behaves_like 'as monitor configurator'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with DISABLE_PUMA_WORKER_KILLER set to false' do
|
||||
let(:expected_payloads) do
|
||||
{
|
||||
rss_memory_limit: {
|
||||
message: 'rss memory limit exceeded',
|
||||
memwd_rss_bytes: memory_limit + 1,
|
||||
memwd_max_rss_bytes: memory_limit,
|
||||
memwd_max_strikes: max_strikes,
|
||||
memwd_cur_strikes: 1
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_env('DISABLE_PUMA_WORKER_KILLER', false)
|
||||
allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).and_return({ total: memory_limit + 1 })
|
||||
end
|
||||
|
||||
context 'when settings are set via environment variables' do
|
||||
let(:memory_limit) { 1300 }
|
||||
let(:max_strikes) { 4 }
|
||||
|
||||
before do
|
||||
stub_env('PUMA_WORKER_MAX_MEMORY', 1300)
|
||||
stub_env('GITLAB_MEMWD_MAX_STRIKES', 4)
|
||||
end
|
||||
|
||||
it_behaves_like 'as monitor configurator'
|
||||
end
|
||||
|
||||
context 'when settings are not set via environment variables' do
|
||||
let(:memory_limit) { 1200 }
|
||||
let(:max_strikes) { 5 }
|
||||
|
||||
it_behaves_like 'as monitor configurator'
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable RSpec/VerifiedDoubles
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'support/shared_examples/lib/gitlab/memory/watchdog/monitor_result_shared_examples'
|
||||
|
||||
RSpec.describe Gitlab::Memory::Watchdog::Monitor::RssMemoryLimit do
|
||||
let(:memory_limit) { 2048 }
|
||||
let(:worker_memory) { 1024 }
|
||||
|
||||
subject(:monitor) do
|
||||
described_class.new(memory_limit: memory_limit)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Gitlab::Metrics::System).to receive(:memory_usage_rss).and_return({ total: worker_memory })
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
context 'when process exceeds threshold' do
|
||||
let(:worker_memory) { memory_limit + 1 }
|
||||
let(:payload) do
|
||||
{
|
||||
message: 'rss memory limit exceeded',
|
||||
memwd_rss_bytes: worker_memory,
|
||||
memwd_max_rss_bytes: memory_limit
|
||||
}
|
||||
end
|
||||
|
||||
include_examples 'returns Watchdog Monitor result', threshold_violated: true
|
||||
end
|
||||
|
||||
context 'when process does not exceed threshold' do
|
||||
let(:worker_memory) { memory_limit - 1 }
|
||||
let(:payload) { {} }
|
||||
|
||||
include_examples 'returns Watchdog Monitor result', threshold_violated: false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -69,7 +69,7 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
|
|||
config.handler = handler
|
||||
config.logger = logger
|
||||
config.sleep_time_seconds = sleep_time_seconds
|
||||
config.monitors.use monitor_class, threshold_violated, payload, max_strikes: max_strikes
|
||||
config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
|
||||
end
|
||||
|
||||
allow(handler).to receive(:call).and_return(true)
|
||||
|
@ -205,8 +205,8 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures do
|
|||
config.handler = handler
|
||||
config.logger = logger
|
||||
config.sleep_time_seconds = sleep_time_seconds
|
||||
config.monitors.use monitor_class, threshold_violated, payload, max_strikes: max_strikes
|
||||
config.monitors.use monitor_class, threshold_violated, payload, max_strikes: max_strikes
|
||||
config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
|
||||
config.monitors.push monitor_class, threshold_violated, payload, max_strikes: max_strikes
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ RSpec.describe Emails::Releases do
|
|||
subject { Notify.new_release_email(user.id, release) }
|
||||
|
||||
it_behaves_like 'an email sent from GitLab'
|
||||
it_behaves_like 'an email with X-GitLab headers containing project details'
|
||||
|
||||
context 'when the release has a name' do
|
||||
it 'shows the correct subject' do
|
||||
|
|
Loading…
Reference in New Issue