diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index ddb4fc00e68..1b391bb163a 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -2647,113 +2647,6 @@ Style/FrozenStringLiteralComment: - 'app/views/projects/tags/index.atom.builder' - 'app/views/users/show.atom.builder' - 'bin/secpick' - - 'config.ru' - - 'config/boot.rb' - - 'config/environment.rb' - - 'config/environments/development.rb' - - 'config/environments/production.rb' - - 'config/environments/test.rb' - - 'config/initializers/01_secret_token.rb' - - 'config/initializers/0_acts_as_taggable.rb' - - 'config/initializers/0_inject_feature_flags.rb' - - 'config/initializers/0_post_deployment_migrations.rb' - - 'config/initializers/1_settings.rb' - - 'config/initializers/2_gitlab.rb' - - 'config/initializers/5_backend.rb' - - 'config/initializers/6_validations.rb' - - 'config/initializers/7_prometheus_metrics.rb' - - 'config/initializers/7_redis.rb' - - 'config/initializers/8_devise.rb' - - 'config/initializers/8_gitaly.rb' - - 'config/initializers/9_fast_gettext.rb' - - 'config/initializers/action_dispatch_http_mime_negotiation.rb' - - 'config/initializers/action_mailer_hooks.rb' - - 'config/initializers/active_record_data_types.rb' - - 'config/initializers/active_record_ping.rb' - - 'config/initializers/active_record_preloader.rb' - - 'config/initializers/active_record_schema_ignore_tables.rb' - - 'config/initializers/active_record_table_definition.rb' - - 'config/initializers/ar_speed_up_migration_checking.rb' - - 'config/initializers/asset_proxy_settings.rb' - - 'config/initializers/attr_encrypted_no_db_connection.rb' - - 'config/initializers/backtrace_silencers.rb' - - 'config/initializers/batch_loader.rb' - - 'config/initializers/bootstrap_form.rb' - - 'config/initializers/bullet.rb' - - 'config/initializers/cluster_events_before_phased_restart.rb' - - 'config/initializers/console_message.rb' - - 'config/initializers/cookies_serializer.rb' - - 'config/initializers/date_time_formats.rb' - - 'config/initializers/default_url_options.rb' - - 'config/initializers/deprecations.rb' - - 'config/initializers/direct_upload_support.rb' - - 'config/initializers/doorkeeper.rb' - - 'config/initializers/doorkeeper_openid_connect.rb' - - 'config/initializers/etag_caching.rb' - - 'config/initializers/fill_shards.rb' - - 'config/initializers/fix_local_cache_middleware.rb' - - 'config/initializers/fog_google_https_private_urls.rb' - - 'config/initializers/forbid_sidekiq_in_transactions.rb' - - 'config/initializers/gettext_rails_i18n_patch.rb' - - 'config/initializers/gitlab_kas_secret.rb' - - 'config/initializers/gitlab_shell_secret_token.rb' - - 'config/initializers/gitlab_workhorse_secret.rb' - - 'config/initializers/go_get.rb' - - 'config/initializers/grpc.rb' - - 'config/initializers/hamlit.rb' - - 'config/initializers/health_check.rb' - - 'config/initializers/http_hostname_override.rb' - - 'config/initializers/kaminari_active_record_relation_methods_with_limit.rb' - - 'config/initializers/kaminari_config.rb' - - 'config/initializers/lograge.rb' - - 'config/initializers/mail_encoding_patch.rb' - - 'config/initializers/mime_types.rb' - - 'config/initializers/mini_magick.rb' - - 'config/initializers/new_framework_defaults.rb' - - 'config/initializers/octokit.rb' - - 'config/initializers/omniauth.rb' - - 'config/initializers/peek.rb' - - 'config/initializers/postgresql_cte.rb' - - 'config/initializers/premailer.rb' - - 'config/initializers/query_limiting.rb' - - 'config/initializers/rack_lineprof.rb' - - 'config/initializers/relative_naming_ci_namespace.rb' - - 'config/initializers/request_context.rb' - - 'config/initializers/request_profiler.rb' - - 'config/initializers/routing_draw.rb' - - 'config/initializers/sentry.rb' - - 'config/initializers/server_uptime.rb' - - 'config/initializers/session_store.rb' - - 'config/initializers/sherlock.rb' - - 'config/initializers/sprockets.rb' - - 'config/initializers/static_files.rb' - - 'config/initializers/time_zone.rb' - - 'config/initializers/trusted_proxies.rb' - - 'config/initializers/warden.rb' - - 'config/initializers/workhorse_multipart.rb' - - 'config/initializers/wrap_parameters.rb' - - 'config/initializers/zz_metrics.rb' - - 'config/initializers_before_autoloader/000_inflections.rb' - - 'config/object_store_settings.rb' - - 'config/routes.rb' - - 'config/routes/admin.rb' - - 'config/routes/api.rb' - - 'config/routes/dashboard.rb' - - 'config/routes/development.rb' - - 'config/routes/explore.rb' - - 'config/routes/git_http.rb' - - 'config/routes/google_api.rb' - - 'config/routes/help.rb' - - 'config/routes/import.rb' - - 'config/routes/legacy_builds.rb' - - 'config/routes/repository.rb' - - 'config/routes/sherlock.rb' - - 'config/routes/sidekiq.rb' - - 'config/routes/snippets.rb' - - 'config/routes/uploads.rb' - - 'config/routes/wiki.rb' - - 'config/smime_signature_settings.rb' - - 'config/spring.rb' - 'danger/changes_size/Dangerfile' - 'danger/metadata/Dangerfile' - 'db/migrate/20190325080727_truncate_user_fullname.rb' diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue index d2d8784ce14..dc779609b86 100644 --- a/app/assets/javascripts/boards/components/board_list_header.vue +++ b/app/assets/javascripts/boards/components/board_list_header.vue @@ -86,16 +86,16 @@ export default { return !this.disabled && this.listType !== ListType.closed; }, showMilestoneListDetails() { - return ( - this.listType === ListType.milestone && - this.list.milestone && - (!this.list.collapsed || !this.isSwimlanesHeader) - ); + return this.listType === ListType.milestone && this.list.milestone && this.showListDetails; }, showAssigneeListDetails() { - return ( - this.listType === ListType.assignee && (!this.list.collapsed || !this.isSwimlanesHeader) - ); + return this.listType === ListType.assignee && this.showListDetails; + }, + showIterationListDetails() { + return this.listType === ListType.iteration && this.showListDetails; + }, + showListDetails() { + return !this.list.collapsed || !this.isSwimlanesHeader; }, issuesCount() { return this.list.issuesCount; @@ -218,6 +218,17 @@ export default { + + + + diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 4d748ac5bf8..49c49bb350d 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -203,6 +203,17 @@ module DiffHelper set_secure_cookie(:diff_view, params.delete(:view), type: CookiesHelper::COOKIE_TYPE_PERMANENT) if params[:view].present? end + def collapsed_diff_url(diff_file) + url_for( + safe_params.merge( + action: :diff_for_path, + old_path: diff_file.old_path, + new_path: diff_file.new_path, + file_identifier: diff_file.file_identifier + ) + ) + end + private def diff_btn(title, name, selected) diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb index ceefb6a8b8a..59403967753 100644 --- a/app/models/ci/build_trace_chunk.rb +++ b/app/models/ci/build_trace_chunk.rb @@ -76,6 +76,22 @@ module Ci end end + ## + # Sometime we need to ensure that the first read goes to a primary + # database, what is especially important in EE. This method does not + # change the behavior in CE. + # + def with_read_consistency(build, &block) + return yield unless consistent_reads_enabled?(build) + + ::Gitlab::Database::Consistency + .with_read_consistency(&block) + end + + def consistent_reads_enabled?(build) + Feature.enabled?(:gitlab_ci_trace_read_consistency, build.project, type: :development, default_enabled: false) + end + ## # Sometimes we do not want to read raw data. This method makes it easier # to find attributes that are just metadata excluding raw data. @@ -154,8 +170,8 @@ module Ci in_lock(lock_key, **lock_params) do # exclusive Redis lock is acquired first raise FailedToPersistDataError, 'Modifed build trace chunk detected' if has_changes_to_save? - self.reset.then do |chunk| # we ensure having latest lock_version - chunk.unsafe_persist_data! # we migrate the data and update data store + self.class.with_read_consistency(build) do + self.reset.then { |chunk| chunk.unsafe_persist_data! } end end rescue FailedToObtainLockError diff --git a/app/services/pages/migrate_from_legacy_storage_service.rb b/app/services/pages/migrate_from_legacy_storage_service.rb index 64b6a43436b..d805ae2418c 100644 --- a/app/services/pages/migrate_from_legacy_storage_service.rb +++ b/app/services/pages/migrate_from_legacy_storage_service.rb @@ -24,9 +24,7 @@ module Pages @queue.close @logger.info("Waiting for threads to finish...") - ActiveSupport::Dependencies.interlock.permit_concurrent_loads do - threads.each(&:join) - end + threads.each(&:join) { migrated: @migrated, errored: @errored } end @@ -34,8 +32,8 @@ module Pages def start_migration_threads Array.new(@migration_threads) do Thread.new do - Rails.application.executor.wrap do - while batch = @queue.pop + while batch = @queue.pop + Rails.application.executor.wrap do process_batch(batch) end end @@ -51,6 +49,11 @@ module Pages end @logger.info("#{@migrated} projects are migrated successfully, #{@errored} projects failed to be migrated") + rescue => e + # This method should never raise exception otherwise all threads might be killed + # and this will result in queue starving (and deadlock) + Gitlab::ErrorTracking.track_exception(e) + @logger.error("failed processing a batch: #{e.message}") end def migrate_project(project) diff --git a/app/views/projects/diffs/viewers/_collapsed.html.haml b/app/views/projects/diffs/viewers/_collapsed.html.haml index 94dcda38bd6..02f499144c0 100644 --- a/app/views/projects/diffs/viewers/_collapsed.html.haml +++ b/app/views/projects/diffs/viewers/_collapsed.html.haml @@ -1,5 +1,3 @@ -- diff_file = viewer.diff_file -- url = url_for(safe_params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier)) -.nothing-here-block.diff-collapsed{ data: { diff_for_path: url } } +.nothing-here-block.diff-collapsed{ data: { diff_for_path: collapsed_diff_url(viewer.diff_file) } } = _("This diff is collapsed.") %button.click-to-expand.btn.btn-link= _("Click to expand it.") diff --git a/app/views/projects/tags/releases/edit.html.haml b/app/views/projects/tags/releases/edit.html.haml index 896dbe454e6..d82c89a3f9f 100644 --- a/app/views/projects/tags/releases/edit.html.haml +++ b/app/views/projects/tags/releases/edit.html.haml @@ -15,5 +15,5 @@ = render 'shared/notes/hints' .error-alert .gl-mt-3 - = f.submit 'Save changes', class: 'btn btn-success' - = link_to "Cancel", project_tag_path(@project, @tag.name), class: "btn btn-default btn-cancel" + = f.submit 'Save changes', class: 'btn gl-button btn-success' + = link_to "Cancel", project_tag_path(@project, @tag.name), class: "btn gl-button btn-default btn-cancel" diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index d726d2ab233..b3a75494ccc 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -42,13 +42,13 @@ - if @tag.has_signature? = render partial: 'projects/commit/signature', object: @tag.signature - if can?(current_user, :admin_tag, @project) - = link_to edit_project_tag_release_path(@project, @tag.name), class: 'btn btn-icon btn-edit gl-button controls-item has-tooltip', title: s_('TagsPage|Edit release notes') do + = link_to edit_project_tag_release_path(@project, @tag.name), class: 'btn btn-icon btn-edit gl-button btn-default controls-item has-tooltip', title: s_('TagsPage|Edit release notes') do = sprite_icon("pencil", css_class: 'gl-icon') - = link_to project_tree_path(@project, @tag.name), class: 'btn btn-icon gl-button controls-item has-tooltip', title: s_('TagsPage|Browse files') do + = link_to project_tree_path(@project, @tag.name), class: 'btn btn-icon gl-button btn-default controls-item has-tooltip', title: s_('TagsPage|Browse files') do = sprite_icon('folder-open', css_class: 'gl-icon') - = link_to project_commits_path(@project, @tag.name), class: 'btn btn-icon gl-button controls-item has-tooltip', title: s_('TagsPage|Browse commits') do + = link_to project_commits_path(@project, @tag.name), class: 'btn btn-icon gl-button btn-default controls-item has-tooltip', title: s_('TagsPage|Browse commits') do = sprite_icon('history', css_class: 'gl-icon') - .btn-container.controls-item + .controls-item = render 'projects/buttons/download', project: @project, ref: @tag.name - if can?(current_user, :admin_tag, @project) .btn-container.controls-item-full diff --git a/changelogs/unreleased/diff-view-speed.yml b/changelogs/unreleased/diff-view-speed.yml new file mode 100644 index 00000000000..8ac020bc468 --- /dev/null +++ b/changelogs/unreleased/diff-view-speed.yml @@ -0,0 +1,5 @@ +--- +title: Lower allocations in _collapsed partial +merge_request: 53233 +author: +type: performance diff --git a/changelogs/unreleased/gl-button-fix-tag.yml b/changelogs/unreleased/gl-button-fix-tag.yml new file mode 100644 index 00000000000..a21519d02c7 --- /dev/null +++ b/changelogs/unreleased/gl-button-fix-tag.yml @@ -0,0 +1,5 @@ +--- +title: Add btn-default to buttons in tag action on the single tag page +merge_request: 52866 +author: Yogi (@yo) +type: other diff --git a/changelogs/unreleased/gl-button-single-tag.yml b/changelogs/unreleased/gl-button-single-tag.yml new file mode 100644 index 00000000000..02a38ead89e --- /dev/null +++ b/changelogs/unreleased/gl-button-single-tag.yml @@ -0,0 +1,5 @@ +--- +title: Apply new GitLab UI for buttons in the tag edit page +merge_request: 52863 +author: Yogi (@yo) +type: other diff --git a/config.ru b/config.ru index f53ff6f992c..0c50b3fdf6f 100644 --- a/config.ru +++ b/config.ru @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is used by Rack-based servers to start the application. if defined?(Unicorn) diff --git a/config/boot.rb b/config/boot.rb index da4e6b7290c..41bf7953737 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) # Set up gems listed in the Gemfile. diff --git a/config/environment.rb b/config/environment.rb index 426333bb469..d5abe55806c 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Load the Rails application. require_relative 'application' diff --git a/config/environments/development.rb b/config/environments/development.rb index 31a3af77ba1..146cdd4f5a7 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb diff --git a/config/environments/production.rb b/config/environments/production.rb index d9b3ee354b0..e1a7db8d860 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb diff --git a/config/environments/test.rb b/config/environments/test.rb index 4e359cd308c..2c6ab3bbc79 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'gitlab/testing/request_blocker_middleware' require 'gitlab/testing/robots_blocker_middleware' require 'gitlab/testing/request_inspector_middleware' diff --git a/config/feature_flags/development/gitlab_ci_trace_read_consistency.yml b/config/feature_flags/development/gitlab_ci_trace_read_consistency.yml new file mode 100644 index 00000000000..c9936a390dc --- /dev/null +++ b/config/feature_flags/development/gitlab_ci_trace_read_consistency.yml @@ -0,0 +1,8 @@ +--- +name: gitlab_ci_trace_read_consistency +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46976 +rollout_issue_url: +milestone: '13.9' +type: development +group: group::continuous integration +default_enabled: false diff --git a/config/feature_flags/development/usage_data_i_testing_load_performance_widget_total.yml b/config/feature_flags/development/usage_data_i_testing_load_performance_widget_total.yml new file mode 100644 index 00000000000..3ffb9bceb99 --- /dev/null +++ b/config/feature_flags/development/usage_data_i_testing_load_performance_widget_total.yml @@ -0,0 +1,8 @@ +--- +name: usage_data_i_testing_load_performance_widget_total +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52688 +rollout_issue_url: +milestone: '13.9' +type: development +group: group::testing +default_enabled: true diff --git a/config/initializers/01_secret_token.rb b/config/initializers/01_secret_token.rb index d7e725477eb..c4520b4b313 100644 --- a/config/initializers/01_secret_token.rb +++ b/config/initializers/01_secret_token.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # WARNING: Before you make a change to secrets.yml, read the development guide for GitLab secrets # doc/development/application_secrets.md. # diff --git a/config/initializers/0_acts_as_taggable.rb b/config/initializers/0_acts_as_taggable.rb index 50dc47673ab..9a92b8f2d18 100644 --- a/config/initializers/0_acts_as_taggable.rb +++ b/config/initializers/0_acts_as_taggable.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ActsAsTaggableOn.strict_case_match = true # tags_counter enables caching count of tags which results in an update whenever a tag is added or removed diff --git a/config/initializers/0_inject_feature_flags.rb b/config/initializers/0_inject_feature_flags.rb index 5b33b3bb4ea..daf208bd960 100644 --- a/config/initializers/0_inject_feature_flags.rb +++ b/config/initializers/0_inject_feature_flags.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This needs to be loaded after # config/initializers/0_inject_enterprise_edition_module.rb diff --git a/config/initializers/0_post_deployment_migrations.rb b/config/initializers/0_post_deployment_migrations.rb index 2d647f72840..dabb82c8525 100644 --- a/config/initializers/0_post_deployment_migrations.rb +++ b/config/initializers/0_post_deployment_migrations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Post deployment migrations are included by default. This file must be loaded # before other initializers as Rails may otherwise memoize a list of migrations # excluding the post deployment migrations. diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index e3c614b720b..2d8bab50635 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative '../settings' require_relative '../object_store_settings' require_relative '../smime_signature_settings' diff --git a/config/initializers/2_gitlab.rb b/config/initializers/2_gitlab.rb index 8b7f245b7b0..5b78a471f8a 100644 --- a/config/initializers/2_gitlab.rb +++ b/config/initializers/2_gitlab.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + require_dependency 'gitlab' diff --git a/config/initializers/5_backend.rb b/config/initializers/5_backend.rb index 46854af9b55..78910bb7675 100644 --- a/config/initializers/5_backend.rb +++ b/config/initializers/5_backend.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + unless Rails.env.test? required_version = Gitlab::VersionInfo.parse(Gitlab::Shell.version_required) current_version = Gitlab::VersionInfo.parse(Gitlab::Shell.version) diff --git a/config/initializers/6_validations.rb b/config/initializers/6_validations.rb index 827b15e5c8d..060b04be824 100644 --- a/config/initializers/6_validations.rb +++ b/config/initializers/6_validations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + def storage_validation_error(message) raise "#{message}. Please fix this in your gitlab.yml before starting GitLab." end diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb index 05487341610..a304f861db8 100644 --- a/config/initializers/7_prometheus_metrics.rb +++ b/config/initializers/7_prometheus_metrics.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'prometheus/client' # Keep separate directories for separate processes diff --git a/config/initializers/7_redis.rb b/config/initializers/7_redis.rb index af4967521b8..a6025a6dbf0 100644 --- a/config/initializers/7_redis.rb +++ b/config/initializers/7_redis.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Make sure we initialize a Redis connection pool before multi-threaded # execution starts by # 1. Sidekiq diff --git a/config/initializers/8_devise.rb b/config/initializers/8_devise.rb index a4841a11a00..3cac012c2f7 100644 --- a/config/initializers/8_devise.rb +++ b/config/initializers/8_devise.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Use this hook to configure devise mailer, warden hooks and so forth. The first # four configuration values can also be set straight in your models. Devise.setup do |config| diff --git a/config/initializers/8_gitaly.rb b/config/initializers/8_gitaly.rb index f4f116e67f7..d7b5bfbc269 100644 --- a/config/initializers/8_gitaly.rb +++ b/config/initializers/8_gitaly.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' Gitlab.config.repositories.storages.keys.each do |storage| diff --git a/config/initializers/9_fast_gettext.rb b/config/initializers/9_fast_gettext.rb index f836e6e971d..0c28a1b7ce8 100644 --- a/config/initializers/9_fast_gettext.rb +++ b/config/initializers/9_fast_gettext.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + FastGettext.default_available_locales = Gitlab::I18n.available_locales I18n.available_locales = Gitlab::I18n.available_locales diff --git a/config/initializers/action_dispatch_http_mime_negotiation.rb b/config/initializers/action_dispatch_http_mime_negotiation.rb index 6c31de2de55..2d6afb97a60 100644 --- a/config/initializers/action_dispatch_http_mime_negotiation.rb +++ b/config/initializers/action_dispatch_http_mime_negotiation.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Starting with Rails 5, Rails tries to determine the request format based on # the extension of the full URL path if no explicit `format` param or `Accept` # header is provided, like when simply browsing to a page in your browser. diff --git a/config/initializers/action_mailer_hooks.rb b/config/initializers/action_mailer_hooks.rb index 41e34b6eb20..46d5e387d9d 100644 --- a/config/initializers/action_mailer_hooks.rb +++ b/config/initializers/action_mailer_hooks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + unless Gitlab.config.gitlab.email_enabled ActionMailer::Base.register_interceptor(::Gitlab::Email::Hook::DisableEmailInterceptor) ActionMailer::Base.logger = nil diff --git a/config/initializers/active_record_data_types.rb b/config/initializers/active_record_data_types.rb index 3fa999e9908..f8f0a69622f 100644 --- a/config/initializers/active_record_data_types.rb +++ b/config/initializers/active_record_data_types.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # ActiveRecord custom data type for storing datetimes with timezone information. # See https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/11229 diff --git a/config/initializers/active_record_ping.rb b/config/initializers/active_record_ping.rb index 349a7e4a496..196f587f565 100644 --- a/config/initializers/active_record_ping.rb +++ b/config/initializers/active_record_ping.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # # frozen_string_literal: true if Gitlab::Utils.to_boolean(ENV['ENABLE_ACTIVERECORD_EMPTY_PING'], default: false) diff --git a/config/initializers/active_record_preloader.rb b/config/initializers/active_record_preloader.rb index d585ecda307..349ca6c4831 100644 --- a/config/initializers/active_record_preloader.rb +++ b/config/initializers/active_record_preloader.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module Associations class Preloader diff --git a/config/initializers/active_record_schema_ignore_tables.rb b/config/initializers/active_record_schema_ignore_tables.rb index 0a840bbf1d8..55c44e00c40 100644 --- a/config/initializers/active_record_schema_ignore_tables.rb +++ b/config/initializers/active_record_schema_ignore_tables.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + # Ignore dynamically managed partitions in static application schema ActiveRecord::SchemaDumper.ignore_tables += ["#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.*"] diff --git a/config/initializers/active_record_table_definition.rb b/config/initializers/active_record_table_definition.rb index 9220620da41..3c6311f034f 100644 --- a/config/initializers/active_record_table_definition.rb +++ b/config/initializers/active_record_table_definition.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # ActiveRecord custom method definitions with timezone information. # See https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/11229 diff --git a/config/initializers/ar_speed_up_migration_checking.rb b/config/initializers/ar_speed_up_migration_checking.rb index c4ffcc54cb2..778b2ee0cb1 100644 --- a/config/initializers/ar_speed_up_migration_checking.rb +++ b/config/initializers/ar_speed_up_migration_checking.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + if Rails.env.test? require 'active_record/migration' diff --git a/config/initializers/asset_proxy_settings.rb b/config/initializers/asset_proxy_settings.rb index 92247aba1b8..48eedcee681 100644 --- a/config/initializers/asset_proxy_settings.rb +++ b/config/initializers/asset_proxy_settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # # Asset proxy settings # diff --git a/config/initializers/attr_encrypted_no_db_connection.rb b/config/initializers/attr_encrypted_no_db_connection.rb index 7ad458929db..f8d0effbbad 100644 --- a/config/initializers/attr_encrypted_no_db_connection.rb +++ b/config/initializers/attr_encrypted_no_db_connection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module AttrEncrypted module Adapters module ActiveRecord diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb index 2f892f78112..f3e01d23448 100644 --- a/config/initializers/backtrace_silencers.rb +++ b/config/initializers/backtrace_silencers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.backtrace_cleaner.remove_silencers! # This allows us to see the proper caller of SQL calls in {development,test}.log diff --git a/config/initializers/batch_loader.rb b/config/initializers/batch_loader.rb index 2e2256b0eb9..d88b43fbcea 100644 --- a/config/initializers/batch_loader.rb +++ b/config/initializers/batch_loader.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + Rails.application.config.middleware.use(BatchLoader::Middleware) diff --git a/config/initializers/bootstrap_form.rb b/config/initializers/bootstrap_form.rb index bbc1d83a63f..8121bc8bf1d 100644 --- a/config/initializers/bootstrap_form.rb +++ b/config/initializers/bootstrap_form.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BootstrapFormBuilderCustomization def label_class "label-bold" diff --git a/config/initializers/bullet.rb b/config/initializers/bullet.rb index d1f72ca3ce7..2d21514b121 100644 --- a/config/initializers/bullet.rb +++ b/config/initializers/bullet.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + def bullet_enabled? Gitlab::Utils.to_boolean(ENV['ENABLE_BULLET'].to_s) end diff --git a/config/initializers/cluster_events_before_phased_restart.rb b/config/initializers/cluster_events_before_phased_restart.rb index aae5470d6ae..d029adbe363 100644 --- a/config/initializers/cluster_events_before_phased_restart.rb +++ b/config/initializers/cluster_events_before_phased_restart.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Technical debt, this should be ideally upstreamed. # # However, there's currently no way to hook before doing diff --git a/config/initializers/console_message.rb b/config/initializers/console_message.rb index fe47195062b..2c05c2c9a24 100644 --- a/config/initializers/console_message.rb +++ b/config/initializers/console_message.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # rubocop:disable Rails/Output if Gitlab::Runtime.console? # note that this will not print out when using `spring` diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb index e9a71f32581..375a23fdfd6 100644 --- a/config/initializers/cookies_serializer.rb +++ b/config/initializers/cookies_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. Rails.application.config.action_dispatch.use_cookies_with_metadata = true diff --git a/config/initializers/date_time_formats.rb b/config/initializers/date_time_formats.rb index 1939ced512d..44d07869033 100644 --- a/config/initializers/date_time_formats.rb +++ b/config/initializers/date_time_formats.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # :short - 10 Nov # :medium - Nov 10, 2007 # :long - November 10, 2007 diff --git a/config/initializers/default_url_options.rb b/config/initializers/default_url_options.rb index de2cdc6ecae..138a8e467c7 100644 --- a/config/initializers/default_url_options.rb +++ b/config/initializers/default_url_options.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + default_url_options = { host: Gitlab.config.gitlab.host, protocol: Gitlab.config.gitlab.protocol, diff --git a/config/initializers/deprecations.rb b/config/initializers/deprecations.rb index 2b07ca665e2..3d03efe67e0 100644 --- a/config/initializers/deprecations.rb +++ b/config/initializers/deprecations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + if Rails.env.development? || ENV['GITLAB_LEGACY_PATH_LOG_MESSAGE'] deprecator = ActiveSupport::Deprecation.new('11.0', 'GitLab') diff --git a/config/initializers/direct_upload_support.rb b/config/initializers/direct_upload_support.rb index 919b80b79c0..880aea7d114 100644 --- a/config/initializers/direct_upload_support.rb +++ b/config/initializers/direct_upload_support.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DirectUploadsValidator SUPPORTED_DIRECT_UPLOAD_PROVIDERS = [ObjectStorage::Config::GOOGLE_PROVIDER, ObjectStorage::Config::AWS_PROVIDER, diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 6b54b5074d5..1cf70909997 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Doorkeeper.configure do # Change the ORM that doorkeeper will use. # Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper diff --git a/config/initializers/doorkeeper_openid_connect.rb b/config/initializers/doorkeeper_openid_connect.rb index 3523776c4f7..12a963ce45d 100644 --- a/config/initializers/doorkeeper_openid_connect.rb +++ b/config/initializers/doorkeeper_openid_connect.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Doorkeeper::OpenidConnect.configure do issuer Gitlab.config.gitlab.url diff --git a/config/initializers/etag_caching.rb b/config/initializers/etag_caching.rb index eba88801141..2e5574d2602 100644 --- a/config/initializers/etag_caching.rb +++ b/config/initializers/etag_caching.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This middleware has to come after Gitlab::Metrics::RackMiddleware # in the middleware stack, because it tracks events with # GitLab Performance Monitoring diff --git a/config/initializers/fill_shards.rb b/config/initializers/fill_shards.rb index 90c0d63e4fe..e2889f59574 100644 --- a/config/initializers/fill_shards.rb +++ b/config/initializers/fill_shards.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # The explicit schema version check is needed because during our migration rollback testing, # `Shard.connected?` could be cached and return true even though the table doesn't exist return unless Shard.connected? diff --git a/config/initializers/fix_local_cache_middleware.rb b/config/initializers/fix_local_cache_middleware.rb index 2644ee6a7d3..94da6d9c0e0 100644 --- a/config/initializers/fix_local_cache_middleware.rb +++ b/config/initializers/fix_local_cache_middleware.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module LocalCacheRegistryCleanupWithEnsure LocalCacheRegistry = ActiveSupport::Cache::Strategy::LocalCache::LocalCacheRegistry diff --git a/config/initializers/fog_google_https_private_urls.rb b/config/initializers/fog_google_https_private_urls.rb index ef6afb2b686..78cbd41a609 100644 --- a/config/initializers/fog_google_https_private_urls.rb +++ b/config/initializers/fog_google_https_private_urls.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # # Monkey patching the https support for private urls # See https://gitlab.com/gitlab-org/gitlab/issues/4879 diff --git a/config/initializers/forbid_sidekiq_in_transactions.rb b/config/initializers/forbid_sidekiq_in_transactions.rb index f505fb5843a..85ec8cad0a3 100644 --- a/config/initializers/forbid_sidekiq_in_transactions.rb +++ b/config/initializers/forbid_sidekiq_in_transactions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Sidekiq module Worker EnqueueFromTransactionError = Class.new(StandardError) diff --git a/config/initializers/gettext_rails_i18n_patch.rb b/config/initializers/gettext_rails_i18n_patch.rb index 09c9b325a04..c23049e93c9 100644 --- a/config/initializers/gettext_rails_i18n_patch.rb +++ b/config/initializers/gettext_rails_i18n_patch.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'gettext_i18n_rails/haml_parser' require 'gettext_i18n_rails_js/parser/javascript' require 'json' diff --git a/config/initializers/gitlab_kas_secret.rb b/config/initializers/gitlab_kas_secret.rb index 5e86e954684..8955f71a597 100644 --- a/config/initializers/gitlab_kas_secret.rb +++ b/config/initializers/gitlab_kas_secret.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + Gitlab::Kas.ensure_secret! diff --git a/config/initializers/gitlab_shell_secret_token.rb b/config/initializers/gitlab_shell_secret_token.rb index 529dcdd4644..e11f65bb8ca 100644 --- a/config/initializers/gitlab_shell_secret_token.rb +++ b/config/initializers/gitlab_shell_secret_token.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + Gitlab::Shell.ensure_secret_token! diff --git a/config/initializers/gitlab_workhorse_secret.rb b/config/initializers/gitlab_workhorse_secret.rb index ed54dc11098..5c959a72bd1 100644 --- a/config/initializers/gitlab_workhorse_secret.rb +++ b/config/initializers/gitlab_workhorse_secret.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + begin Gitlab::Workhorse.secret rescue diff --git a/config/initializers/go_get.rb b/config/initializers/go_get.rb index 7e7896b4900..830579f4a8a 100644 --- a/config/initializers/go_get.rb +++ b/config/initializers/go_get.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + Rails.application.config.middleware.use(Gitlab::Middleware::Go) diff --git a/config/initializers/grpc.rb b/config/initializers/grpc.rb index b96962fe7db..66b212ef131 100644 --- a/config/initializers/grpc.rb +++ b/config/initializers/grpc.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'logger' GRPC_LOGGER = Logger.new(Rails.root.join('log/grpc.log')) diff --git a/config/initializers/hamlit.rb b/config/initializers/hamlit.rb index b5bcae4bbfc..dda38d2a61f 100644 --- a/config/initializers/hamlit.rb +++ b/config/initializers/hamlit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Hamlit::RailsTemplate.set_options(attr_quote: '"') Hamlit::Filters.remove_filter('coffee') diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb index 1496f20afc1..0b35aaf115b 100644 --- a/config/initializers/health_check.rb +++ b/config/initializers/health_check.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + HealthCheck.setup do |config| config.standard_checks = %w(database migrations cache) config.full_checks = %w(database migrations cache) diff --git a/config/initializers/http_hostname_override.rb b/config/initializers/http_hostname_override.rb index 58dd380326f..5d2739c1f58 100644 --- a/config/initializers/http_hostname_override.rb +++ b/config/initializers/http_hostname_override.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This override allows passing `@hostname_override` to the SNI protocol, # which is used to lookup the correct SSL certificate in the # request handshake process. diff --git a/config/initializers/kaminari_active_record_relation_methods_with_limit.rb b/config/initializers/kaminari_active_record_relation_methods_with_limit.rb index 55b98cda520..982cb69e532 100644 --- a/config/initializers/kaminari_active_record_relation_methods_with_limit.rb +++ b/config/initializers/kaminari_active_record_relation_methods_with_limit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Kaminari # Active Record specific page scope methods implementations module ActiveRecordRelationMethodsWithLimit diff --git a/config/initializers/kaminari_config.rb b/config/initializers/kaminari_config.rb index 3cbe9a058d7..8e10079ce36 100644 --- a/config/initializers/kaminari_config.rb +++ b/config/initializers/kaminari_config.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Kaminari.configure do |config| config.default_per_page = 20 config.max_per_page = 100 diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb index 5b068c15aad..e8479bc6aa4 100644 --- a/config/initializers/lograge.rb +++ b/config/initializers/lograge.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Only use Lograge for Rails unless Gitlab::Runtime.sidekiq? Rails.application.reloader.to_prepare do diff --git a/config/initializers/mail_encoding_patch.rb b/config/initializers/mail_encoding_patch.rb index d53b058ba75..11a96625e08 100644 --- a/config/initializers/mail_encoding_patch.rb +++ b/config/initializers/mail_encoding_patch.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Monkey patch mail 2.7.1 to fix quoted-printable issues with newlines # The issues upstream invalidate SMIME signatures under some conditions # This was working properly in 2.6.6 diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index acbdf8de5a6..5329232cc92 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: diff --git a/config/initializers/mini_magick.rb b/config/initializers/mini_magick.rb index db0e7bbaaa3..8e3e55279f7 100644 --- a/config/initializers/mini_magick.rb +++ b/config/initializers/mini_magick.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + MiniMagick.configure do |config| config.cli = :graphicsmagick end diff --git a/config/initializers/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb index 115ee08dbb6..3d3ee5299e2 100644 --- a/config/initializers/new_framework_defaults.rb +++ b/config/initializers/new_framework_defaults.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Remove this `if` condition when upgraded to rails 5.0. # The body must be kept. # Be sure to restart your server when you modify this file. diff --git a/config/initializers/octokit.rb b/config/initializers/octokit.rb index b3749258ec5..cd8c8ec24ee 100644 --- a/config/initializers/octokit.rb +++ b/config/initializers/octokit.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + Octokit.middleware.insert_after Octokit::Middleware::FollowRedirects, Gitlab::Octokit::Middleware diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index a2720ab9986..85984772d05 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + if Gitlab::Auth::Ldap::Config.enabled? module OmniAuth::Strategies Gitlab::Auth::Ldap::Config.available_servers.each do |server| diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index 7027ba69e13..85bfc4f0214 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'peek/adapters/redis' Peek::Adapters::Redis.prepend ::Gitlab::PerformanceBar::RedisAdapterWhenPeekEnabled diff --git a/config/initializers/postgresql_cte.rb b/config/initializers/postgresql_cte.rb index 68d53c4edbf..1ea0b4cfb58 100644 --- a/config/initializers/postgresql_cte.rb +++ b/config/initializers/postgresql_cte.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Adds support for WITH statements when using PostgreSQL. The code here is taken # from https://github.com/shmay/ctes_in_my_pg which at the time of writing has # not been pushed to RubyGems. The license of this repository is as follows: diff --git a/config/initializers/premailer.rb b/config/initializers/premailer.rb index 87f8e67ef1c..77077888af3 100644 --- a/config/initializers/premailer.rb +++ b/config/initializers/premailer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # See https://github.com/fphilipe/premailer-rails#configuration Premailer::Rails.config.merge!( generate_text_part: false, diff --git a/config/initializers/query_limiting.rb b/config/initializers/query_limiting.rb index 66864d1898e..66aefc97c6a 100644 --- a/config/initializers/query_limiting.rb +++ b/config/initializers/query_limiting.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + if Gitlab::QueryLimiting.enable? require_dependency 'gitlab/query_limiting/active_support_subscriber' require_dependency 'gitlab/query_limiting/transaction' diff --git a/config/initializers/rack_lineprof.rb b/config/initializers/rack_lineprof.rb index f7172fce9bc..e66bfd88907 100644 --- a/config/initializers/rack_lineprof.rb +++ b/config/initializers/rack_lineprof.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # The default colors of rack-lineprof can be very hard to look at in terminals # with darker backgrounds. This patch tweaks the colors a bit so the output is # actually readable. diff --git a/config/initializers/relative_naming_ci_namespace.rb b/config/initializers/relative_naming_ci_namespace.rb index d9d3034150f..7380597722a 100644 --- a/config/initializers/relative_naming_ci_namespace.rb +++ b/config/initializers/relative_naming_ci_namespace.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Description: https://coderwall.com/p/heed_q/rails-routing-and-namespaced-models # # This allows us to use CI ActiveRecord objects in all routes and use it: diff --git a/config/initializers/request_context.rb b/config/initializers/request_context.rb index f79f1f32d70..99e51ce498a 100644 --- a/config/initializers/request_context.rb +++ b/config/initializers/request_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do |config| config.middleware.insert_after RequestStore::Middleware, Gitlab::Middleware::RequestContext end diff --git a/config/initializers/request_profiler.rb b/config/initializers/request_profiler.rb index fb5a7b8372e..8e426772b9a 100644 --- a/config/initializers/request_profiler.rb +++ b/config/initializers/request_profiler.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do |config| config.middleware.use(Gitlab::RequestProfiler::Middleware) end diff --git a/config/initializers/routing_draw.rb b/config/initializers/routing_draw.rb index f0f74954eef..4978073633c 100644 --- a/config/initializers/routing_draw.rb +++ b/config/initializers/routing_draw.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Adds draw method into Rails routing # It allows us to keep routing split into files ActionDispatch::Routing::Mapper.prepend Gitlab::Patch::DrawRoute diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index a1eddd6a2c2..df80d4f7f73 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. require 'gitlab/current_settings' diff --git a/config/initializers/server_uptime.rb b/config/initializers/server_uptime.rb index 46bf242e143..9e58a848007 100644 --- a/config/initializers/server_uptime.rb +++ b/config/initializers/server_uptime.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + Rails.application.config.booted_at = Time.now diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index da24881885e..6e0cd33aa96 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. require 'gitlab/current_settings' diff --git a/config/initializers/sherlock.rb b/config/initializers/sherlock.rb index 8f2ababb712..ba33ffa13c5 100644 --- a/config/initializers/sherlock.rb +++ b/config/initializers/sherlock.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + if Gitlab::Sherlock.enabled? Rails.application.configure do |config| config.middleware.use(Gitlab::Sherlock::Middleware) diff --git a/config/initializers/sprockets.rb b/config/initializers/sprockets.rb index a20b7dc75e9..d0660574eb6 100644 --- a/config/initializers/sprockets.rb +++ b/config/initializers/sprockets.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + Sprockets.register_compressor 'application/javascript', :terser, Terser::Compressor diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb index 4e19fec084a..3cdb5a5abcf 100644 --- a/config/initializers/static_files.rb +++ b/config/initializers/static_files.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + app = Rails.application if app.config.public_file_server.enabled diff --git a/config/initializers/time_zone.rb b/config/initializers/time_zone.rb index bca7411ad63..8ad6b2d3447 100644 --- a/config/initializers/time_zone.rb +++ b/config/initializers/time_zone.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Time.zone = Gitlab.config.gitlab.time_zone || Time.zone # The default is normally set by Rails in the # active_support.initialize_time_zone Railtie, but we need to set it diff --git a/config/initializers/trusted_proxies.rb b/config/initializers/trusted_proxies.rb index a4528020c06..79e4b831c5e 100644 --- a/config/initializers/trusted_proxies.rb +++ b/config/initializers/trusted_proxies.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Override Rack::Request to make use of the same list of trusted_proxies # as the ActionDispatch::Request object. This is necessary for libraries # like rack_attack where they don't use ActionDispatch, and we want them diff --git a/config/initializers/warden.rb b/config/initializers/warden.rb index 3cfab52efd1..2517c0cf5c2 100644 --- a/config/initializers/warden.rb +++ b/config/initializers/warden.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do |config| Warden::Manager.after_set_user(scope: :user) do |user, auth, opts| Gitlab::Auth::UniqueIpsLimiter.limit_user!(user) diff --git a/config/initializers/workhorse_multipart.rb b/config/initializers/workhorse_multipart.rb index 9c170d1adfa..b68a6fc6ee5 100644 --- a/config/initializers/workhorse_multipart.rb +++ b/config/initializers/workhorse_multipart.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Rails.application.configure do |config| # ApolloUploadServer::Middleware expects to find uploaded files ready to use config.middleware.insert_before(ApolloUploadServer::Middleware, Gitlab::Middleware::Multipart) diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index 999df20181e..85b2d840618 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # # This file contains settings for ActionController::ParamsWrapper which diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb index a14239bf619..d0e22187454 100644 --- a/config/initializers/zz_metrics.rb +++ b/config/initializers/zz_metrics.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file was prefixed with zz_ because we want to load it the last! # See: https://gitlab.com/gitlab-org/gitlab-foss/issues/55611 diff --git a/config/initializers_before_autoloader/000_inflections.rb b/config/initializers_before_autoloader/000_inflections.rb index 85c53ab4320..9eb1ebe77bf 100644 --- a/config/initializers_before_autoloader/000_inflections.rb +++ b/config/initializers_before_autoloader/000_inflections.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format diff --git a/config/object_store_settings.rb b/config/object_store_settings.rb index f92bffd45db..938129757f1 100644 --- a/config/object_store_settings.rb +++ b/config/object_store_settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Set default values for object_store settings class ObjectStoreSettings SUPPORTED_TYPES = %w(artifacts external_diffs lfs uploads packages dependency_proxy terraform_state pages).freeze diff --git a/config/routes.rb b/config/routes.rb index 8caa924e113..90a26c0a20f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'sidekiq/web' require 'sidekiq/cron/web' require 'product_analytics/collector_app' diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 71a927f59b9..3e04f0d97cb 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + namespace :admin do resources :users, constraints: { id: %r{[a-zA-Z./0-9_\-]+} } do resources :keys, only: [:show, :destroy] diff --git a/config/routes/api.rb b/config/routes/api.rb index 5dbfcc98f0f..dcbc98991e2 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + post '/api/graphql', to: 'graphql#execute' mount GraphiQL::Rails::Engine, at: '/-/graphql-explorer', graphql_path: Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, '/api/graphql') diff --git a/config/routes/dashboard.rb b/config/routes/dashboard.rb index 7e29a36f020..6a3aa5ff0c1 100644 --- a/config/routes/dashboard.rb +++ b/config/routes/dashboard.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + resource :dashboard, controller: 'dashboard', only: [] do get :issues, action: :issues_calendar, constraints: lambda { |req| req.format == :ics } get :issues diff --git a/config/routes/development.rb b/config/routes/development.rb index 9b2b47c6a21..4ea53e14120 100644 --- a/config/routes/development.rb +++ b/config/routes/development.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + if Rails.env.development? # Make the built-in Rails routes available in development, otherwise they'd # get swallowed by the `namespace/project` route matcher below. diff --git a/config/routes/explore.rb b/config/routes/explore.rb index 59b53bdcf42..c6bf98b6fb3 100644 --- a/config/routes/explore.rb +++ b/config/routes/explore.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + namespace :explore do resources :projects, only: [:index] do collection do diff --git a/config/routes/git_http.rb b/config/routes/git_http.rb index 715d4b5cc59..6899a89cc7d 100644 --- a/config/routes/git_http.rb +++ b/config/routes/git_http.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + scope(path: '*repository_path', format: false) do constraints(repository_path: Gitlab::PathRegex.repository_git_route_regex) do scope(module: :repositories) do @@ -42,7 +44,7 @@ scope(path: '*repository_path', format: false) do wiki_redirect = redirect do |params, request| container_path = params[:repository_path].delete_suffix('.wiki.git') path = File.join(container_path, '-', 'wikis') - path << "?#{request.query_string}" unless request.query_string.blank? + path += "?#{request.query_string}" unless request.query_string.blank? path end @@ -54,7 +56,7 @@ scope(path: '*repository_path', format: false) do constraints(repository_path: Gitlab::PathRegex.repository_route_regex) do ref_redirect = redirect do |params, request| path = "#{params[:repository_path]}.git/info/refs" - path << "?#{request.query_string}" unless request.query_string.blank? + path += "?#{request.query_string}" unless request.query_string.blank? path end diff --git a/config/routes/google_api.rb b/config/routes/google_api.rb index a119b47c176..06f8d988072 100644 --- a/config/routes/google_api.rb +++ b/config/routes/google_api.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + scope '-' do namespace :google_api do resource :auth, only: [], controller: :authorizations do diff --git a/config/routes/help.rb b/config/routes/help.rb index 446310ba314..2a0aba8b632 100644 --- a/config/routes/help.rb +++ b/config/routes/help.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + get 'help' => 'help#index' get 'help/shortcuts' => 'help#shortcuts' get 'help/instance_configuration' => 'help#instance_configuration' diff --git a/config/routes/import.rb b/config/routes/import.rb index 5f94fb8d058..64830ef1e52 100644 --- a/config/routes/import.rb +++ b/config/routes/import.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Alias import callbacks under the /users/auth endpoint so that # the OAuth2 callback URL can be restricted under http://example.com/users/auth # instead of http://example.com. diff --git a/config/routes/legacy_builds.rb b/config/routes/legacy_builds.rb index 5ab2b953ce1..c08b581e101 100644 --- a/config/routes/legacy_builds.rb +++ b/config/routes/legacy_builds.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + resources :builds, only: [:index, :show], constraints: { id: /\d+/ } do collection do resources :artifacts, only: [], controller: 'build_artifacts' do diff --git a/config/routes/repository.rb b/config/routes/repository.rb index 43837f2ce34..61a407d5a35 100644 --- a/config/routes/repository.rb +++ b/config/routes/repository.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # All routing related to repository browsing resource :repository, only: [:create] do diff --git a/config/routes/sherlock.rb b/config/routes/sherlock.rb index c9969f91c36..a9be434dba7 100644 --- a/config/routes/sherlock.rb +++ b/config/routes/sherlock.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + if Gitlab::Sherlock.enabled? namespace :sherlock do resources :transactions, only: [:index, :show] do diff --git a/config/routes/sidekiq.rb b/config/routes/sidekiq.rb index 36ec8bc1d54..5f6755fc3b3 100644 --- a/config/routes/sidekiq.rb +++ b/config/routes/sidekiq.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + constraints ::Constraints::AdminConstrainer.new do mount Sidekiq::Web, at: '/admin/sidekiq', as: :sidekiq end diff --git a/config/routes/snippets.rb b/config/routes/snippets.rb index 9e0c42fa07d..5c162d0c37f 100644 --- a/config/routes/snippets.rb +++ b/config/routes/snippets.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + resources :snippets, except: [:create, :update, :destroy], concerns: :awardable, constraints: { id: /\d+/ } do member do get :raw diff --git a/config/routes/uploads.rb b/config/routes/uploads.rb index fb8af76397c..71a868175a9 100644 --- a/config/routes/uploads.rb +++ b/config/routes/uploads.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + scope path: :uploads do # Note attachments and User/Group/Project avatars get "-/system/:model/:mounted_as/:id/:filename", diff --git a/config/routes/wiki.rb b/config/routes/wiki.rb index 49ad39e8369..4d14743b3d3 100644 --- a/config/routes/wiki.rb +++ b/config/routes/wiki.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + scope(controller: :wikis) do scope(path: 'wikis', as: :wikis) do get :git_access diff --git a/config/smime_signature_settings.rb b/config/smime_signature_settings.rb index 4a8cf1a06f7..4b50e01dba0 100644 --- a/config/smime_signature_settings.rb +++ b/config/smime_signature_settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Set default values for email_smime settings class SmimeSignatureSettings def self.parse(email_smime) diff --git a/config/spring.rb b/config/spring.rb index 0092d0fd1b0..3f00e6ab23f 100644 --- a/config/spring.rb +++ b/config/spring.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + %w( .ruby-version .rbenv-vars diff --git a/db/migrate/20210203092540_remove_has_external_wiki_constraint.rb b/db/migrate/20210203092540_remove_has_external_wiki_constraint.rb index f76163b9f8a..80b0cc11685 100644 --- a/db/migrate/20210203092540_remove_has_external_wiki_constraint.rb +++ b/db/migrate/20210203092540_remove_has_external_wiki_constraint.rb @@ -9,7 +9,9 @@ class RemoveHasExternalWikiConstraint < ActiveRecord::Migration[6.0] def up # This reverts the following migration: add_not_null_constraint :projects, :has_external_wiki, validate: false - remove_not_null_constraint :projects, :has_external_wiki + if check_not_null_constraint_exists?(:projects, :has_external_wiki) + remove_not_null_constraint :projects, :has_external_wiki + end end def down diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt index a81895bc0f8..9e7fdef62c3 100644 --- a/doc/.vale/gitlab/spelling-exceptions.txt +++ b/doc/.vale/gitlab/spelling-exceptions.txt @@ -103,6 +103,7 @@ Conda Consul Contentful Corosync +Coursier cron crons crontab @@ -125,6 +126,10 @@ deduplicates deduplicating deduplication deliverables +denormalize +denormalized +denormalizes +denormalizing denylist denylisting denylists @@ -138,6 +143,7 @@ dequarantine dequarantined dequarantining DevOps +disambiguates discoverability dismissable Disqus @@ -145,6 +151,7 @@ Divio Dockerfile Dockerfiles Dockerize +Dockerized Dockerizing dogfood dogfooding @@ -169,6 +176,7 @@ failover failovers failsafe Falco +falsy fastlane favicon favorited @@ -181,6 +189,7 @@ Flawfinder Flowdock Fluentd Forgerock +formatters Fugit fuzzer Gantt @@ -206,6 +215,7 @@ goroutines Gosec Gradle Grafana +Grafonnet gravatar Gzip Haml @@ -253,6 +263,7 @@ Jira jq jQuery jsdom +Jsonnet JupyterHub kanban kanbans @@ -282,6 +293,7 @@ Libravatar liveness Lograge logrotate +Logrus Logstash lookahead lookaheads @@ -366,6 +378,7 @@ passwordless Patroni performant PgBouncer +pgLoader Phabricator phaser phasers @@ -474,6 +487,7 @@ rsync rsynced rsyncing rsyncs +Rubinius Rubix Rubocop Rubular @@ -521,6 +535,7 @@ spidering Splunk SpotBugs Stackdriver +Stackprof starrer starrers storable @@ -553,6 +568,8 @@ substring substrings subtask subtasks +subtest +subtests subtree subtrees sudo @@ -583,6 +600,8 @@ Tokenizers tokenizing toolchain toolchains +toolkit +toolkits tooltip tooltips transpile @@ -592,6 +611,7 @@ Trello triaged triages triaging +truthy Truststore Twilio Twitter @@ -605,6 +625,7 @@ unarchive unarchived unarchives unarchiving +unary unassign unassigning unassigns @@ -673,6 +694,7 @@ unstarted unstash unstashed unstashing +unsynced untarred untracked untrusted @@ -683,9 +705,11 @@ unverifying uploader uploaders upstreams +upvote upvoted upvotes URIs +Vagrantfile validator validators vendored diff --git a/doc/administration/user_settings.md b/doc/administration/user_settings.md index 9892d2a0764..681ce87edb6 100644 --- a/doc/administration/user_settings.md +++ b/doc/administration/user_settings.md @@ -1,6 +1,6 @@ --- -stage: Enablement -group: Distribution +stage: Manage +group: Access info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- @@ -10,7 +10,8 @@ GitLab administrators can modify user settings for the entire GitLab instance. ## Disallow users creating top-level groups -By default, new users can create top-level groups. To disable this, modify the appropriate configuration file. +By default, new users can create top-level groups. To disable this, modify the appropriate configuration file, +and then [reconfigure and restart GitLab](restart_gitlab.md). For Omnibus installations, add the following to `/etc/gitlab/gitlab.rb`: @@ -26,7 +27,8 @@ For source installations, uncomment the following line in `config/gitlab.yml`: ## Disallow users changing usernames -By default, new users can change their usernames. To disable this, modify the appropriate configuration file. +By default, new users can change their usernames. To disable this, modify the appropriate configuration file, +and then [reconfigure and restart GitLab](restart_gitlab.md). For Omnibus installations, add the following to `/etc/gitlab/gitlab.rb`: diff --git a/doc/api/group_iterations.md b/doc/api/group_iterations.md index d96f0881088..715f40e8325 100644 --- a/doc/api/group_iterations.md +++ b/doc/api/group_iterations.md @@ -49,7 +49,8 @@ Example response: "created_at": "2020-01-27T05:07:12.573Z", "updated_at": "2020-01-27T05:07:12.573Z", "due_date": "2020-02-01", - "start_date": "2020-02-14" + "start_date": "2020-02-14", + "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13" } ] ``` diff --git a/doc/api/iterations.md b/doc/api/iterations.md index 1ee489e4cf1..ff6e8e2542f 100644 --- a/doc/api/iterations.md +++ b/doc/api/iterations.md @@ -51,7 +51,8 @@ Example response: "created_at": "2020-01-27T05:07:12.573Z", "updated_at": "2020-01-27T05:07:12.573Z", "due_date": "2020-02-01", - "start_date": "2020-02-14" + "start_date": "2020-02-14", + "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13" } ] ``` diff --git a/doc/api/resource_access_tokens.md b/doc/api/resource_access_tokens.md index 44dfc243750..189f335fa21 100644 --- a/doc/api/resource_access_tokens.md +++ b/doc/api/resource_access_tokens.md @@ -15,7 +15,7 @@ You can read more about [project access tokens](../user/project/settings/project Get a list of project access tokens. ```plaintext -GET /:id/access_tokens +GET projects/:id/access_tokens ``` | Attribute | Type | required | Description | @@ -50,7 +50,7 @@ curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/a Create a project access token. ```plaintext -POST /:id/access_tokens +POST projects/:id/access_tokens ``` | Attribute | Type | required | Description | @@ -89,7 +89,7 @@ curl --request POST --header "PRIVATE-TOKEN: " \ Revoke a project access token. ```plaintext -DELETE /:id/access_tokens/:token_id +DELETE projects/:id/access_tokens/:token_id ``` | Attribute | Type | required | Description | diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md index 48179f3acc7..cda4f4c905c 100644 --- a/doc/development/feature_flags/controls.md +++ b/doc/development/feature_flags/controls.md @@ -11,12 +11,12 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo To be able to turn on/off features behind feature flags in any of the GitLab Inc. provided environments such as staging and production, you need to -have access to the [Chatops](../chatops_on_gitlabcom.md) bot. The Chatops bot +have access to the [ChatOps](../chatops_on_gitlabcom.md) bot. The ChatOps bot is currently running on the ops instance, which is different from or . -Follow the Chatops document to [request access](../chatops_on_gitlabcom.md#requesting-access). +Follow the ChatOps document to [request access](../chatops_on_gitlabcom.md#requesting-access). -Once you are added to the project test if your access propagated, +After you are added to the project test if your access propagated, run: ```shell @@ -37,7 +37,7 @@ easier to measure the impact of both separately. The GitLab feature library (using [Flipper](https://github.com/jnunemaker/flipper), and covered in the [Feature Flags process](process.md) guide) supports rolling out changes to a percentage of -time to users. This in turn can be controlled using [GitLab Chatops](../../ci/chatops/README.md). +time to users. This in turn can be controlled using [GitLab ChatOps](../../ci/chatops/README.md). For an up to date list of feature flag commands please see [the source code](https://gitlab.com/gitlab-com/chatops/blob/master/lib/chatops/commands/feature.rb). @@ -48,7 +48,7 @@ If you get an error "Whoops! This action is not allowed. This incident will be reported." that means your Slack account is not allowed to change feature flags or you do not [have access](#access). -### Enabling a feature for preproduction testing +### Enabling a feature for pre-production testing As a first step in a feature rollout, you should enable the feature on and . @@ -62,7 +62,7 @@ a (very) rough estimate of how your feature will look/behave on GitLab.com. Both of these instances are connected to Sentry so make sure you check the projects there for any exceptions while testing your feature after enabling the feature flag. -For these preproduction environments, the commands should be run in a +For these pre-production environments, the commands should be run in a Slack channel for the stage the feature is relevant to. For example, use the `#s_monitor` channel for features developed by the Monitor stage, Health group. @@ -77,7 +77,7 @@ To enable a feature for 25% of all users, run the following in Slack: ### Enabling a feature for GitLab.com When a feature has successfully been -[enabled on a preproduction](#enabling-a-feature-for-preproduction-testing) +[enabled on a pre-production](#enabling-a-feature-for-pre-production-testing) environment and verified as safe and working, you can roll out the change to GitLab.com (production). @@ -244,9 +244,9 @@ You cannot selectively disable feature flags for a specific project/group/user w ### Feature flag change logging -#### Chatops level +#### ChatOps level -Any feature flag change that affects GitLab.com (production) via [Chatops](https://gitlab.com/gitlab-com/chatops) +Any feature flag change that affects GitLab.com (production) via [ChatOps](https://gitlab.com/gitlab-com/chatops) is automatically logged in an issue. The issue is created in the @@ -259,7 +259,7 @@ The issue is then also posted to the GitLab internal marker to make the change even more visible. Changes to the issue format can be submitted in the -[Chatops project](https://gitlab.com/gitlab-com/chatops). +[ChatOps project](https://gitlab.com/gitlab-com/chatops). #### Instance level diff --git a/doc/development/feature_flags/process.md b/doc/development/feature_flags/process.md index 7e6299c193c..65a6d01e0a2 100644 --- a/doc/development/feature_flags/process.md +++ b/doc/development/feature_flags/process.md @@ -155,7 +155,7 @@ It may seem like feature flags are configuration, which goes against our [conven principle. However, configuration is by definition something that is user-manageable. Feature flags are not intended to be user-editable. Instead, they are intended as a tool for Engineers and Site Reliability Engineers to use to de-risk their changes. Feature flags are the shim that gets us -to Continuous Delivery with our mono repo and without having to deploy the entire codebase on every change. +to Continuous Delivery with our monorepo and without having to deploy the entire codebase on every change. Feature flags are created to ensure that we can safely rollout our work on our terms. If we use Feature Flags as a configuration, we are doing it wrong and are indeed in violation of our principles. If something needs to be configured, we should intentionally make it configuration from the @@ -173,5 +173,5 @@ Some of the benefits of using development-type feature flags are: 1. Improved throughput: when a change is less risky because a flag exists, theoretical tests about scalability can potentially become unnecessary or less important. This allows an engineer to potentially test a feature on a small project, monitor the impact, and proceed. The alternative might - be to build complex benchmarks locally, or on staging, or on another GitLab deployment, which has an - outsized impact on the time it can take to build and release a feature. + be to build complex benchmarks locally, or on staging, or on another GitLab deployment, which has a + large impact on the time it can take to build and release a feature. diff --git a/doc/development/filtering_by_label.md b/doc/development/filtering_by_label.md index b10e2f6e082..2b9c7efc087 100644 --- a/doc/development/filtering_by_label.md +++ b/doc/development/filtering_by_label.md @@ -137,7 +137,7 @@ object, so the number of combinations would balloon further. ### Attempt B2: store label titles for each object -From the perspective of updating the labelable object, this is the worst +From the perspective of updating the object, this is the worst option. We have to bulk update the objects when: 1. The objects are moved from one project to another. @@ -159,7 +159,7 @@ However, at present, the disadvantages outweigh the advantages. ## Conclusion -We have yet to find a method that is demonstratably better than the current +We have yet to find a method that is demonstrably better than the current method, when considering: 1. Query performance. diff --git a/doc/development/gemfile.md b/doc/development/gemfile.md index 8fd23f96329..1615c96309c 100644 --- a/doc/development/gemfile.md +++ b/doc/development/gemfile.md @@ -23,8 +23,8 @@ Refer to [licensing guidelines](licensing.md) for ensuring license compliance. When upgrading the Rails gem and its dependencies, you also should update the following: -- The [Gemfile in the `qa` directory](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/Gemfile). -- The [Gemfile in Gitaly Ruby](https://gitlab.com/gitlab-org/gitaly/-/blob/master/ruby/Gemfile), +- The [`Gemfile` in the `qa` directory](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/Gemfile). +- The [`Gemfile` in Gitaly Ruby](https://gitlab.com/gitlab-org/gitaly/-/blob/master/ruby/Gemfile), to ensure that we ship only one version of these gems. You should also update NPM packages that follow the current version of Rails: diff --git a/doc/development/geo.md b/doc/development/geo.md index fde7b475175..05fadcad08a 100644 --- a/doc/development/geo.md +++ b/doc/development/geo.md @@ -305,7 +305,7 @@ events include: - Repositories Changed event - Repository Created event - Hashed Storage Migrated event -- Lfs Object Deleted event +- LFS Object Deleted event - Hashed Storage Attachments event - Job Artifact Deleted event - Upload Deleted event diff --git a/doc/development/go_guide/dependencies.md b/doc/development/go_guide/dependencies.md index 72b3f82d86f..c5af21d0772 100644 --- a/doc/development/go_guide/dependencies.md +++ b/doc/development/go_guide/dependencies.md @@ -178,4 +178,4 @@ authenticate requests made over HTTP. Go rejects HTTP-only entries in `GOPROXY` that have embedded credentials. In a future version, Go may add support for arbitrary authentication headers. -Follow [golang/go#26232](https://github.com/golang/go/issues/26232) for details. +Follow [`golang/go#26232`](https://github.com/golang/go/issues/26232) for details. diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md index 68210c08a00..f352db918ed 100644 --- a/doc/development/go_guide/index.md +++ b/doc/development/go_guide/index.md @@ -63,10 +63,9 @@ of possible security breaches in our code: Remember to run [SAST](../../user/application_security/sast/index.md) and [Dependency Scanning](../../user/application_security/dependency_scanning/index.md) -**(ULTIMATE)** on your project (or at least the [gosec -analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/gosec)), -and to follow our [Security -requirements](../code_review.md#security-requirements). +**(ULTIMATE)** on your project (or at least the +[`gosec` analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/gosec)), +and to follow our [Security requirements](../code_review.md#security-requirements). Web servers can take advantages of middlewares like [Secure](https://github.com/unrolled/secure). @@ -92,9 +91,9 @@ projects: - Avoid global variables, even in packages. By doing so you introduce side effects if the package is included multiple times. - Use `goimports` before committing. - [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) + [`goimports`](https://godoc.org/golang.org/x/tools/cmd/goimports) is a tool that automatically formats Go source code using - [Gofmt](https://golang.org/cmd/gofmt/), in addition to formatting import lines, + [`Gofmt`](https://golang.org/cmd/gofmt/), in addition to formatting import lines, adding missing ones and removing unreferenced ones. Most editors/IDEs allow you to run commands before/after saving a file, you can set it @@ -162,8 +161,8 @@ be downloaded repeatedly, which can lead to intermittent problems due to rate limiting or network failures. In these circumstances, you should [cache the downloaded code between](../../ci/caching/index.md#caching-go-dependencies). -There was a [bug on modules -checksums](https://github.com/golang/go/issues/29278) in Go < v1.11.4, so make +There was a +[bug on modules checksums](https://github.com/golang/go/issues/29278) in Go versions earlier than v1.11.4, so make sure to use at least this version to avoid `checksum mismatch` errors. ### ORM @@ -171,7 +170,7 @@ sure to use at least this version to avoid `checksum mismatch` errors. We don't use object-relational mapping libraries (ORMs) at GitLab (except [ActiveRecord](https://guides.rubyonrails.org/active_record_basics.html) in Ruby on Rails). Projects can be structured with services to avoid them. -[pgx](https://github.com/jackc/pgx) should be enough to interact with PostgreSQL +[`pgx`](https://github.com/jackc/pgx) should be enough to interact with PostgreSQL databases. ### Migrations @@ -193,7 +192,7 @@ external dependencies might be worth considering in case we decide to use a spec library or framework: - [Testify](https://github.com/stretchr/testify) -- [httpexpect](https://github.com/gavv/httpexpect) +- [`httpexpect`](https://github.com/gavv/httpexpect) ### Subtests @@ -329,8 +328,8 @@ A few things to keep in mind when adding context: ## CLIs Every Go program is launched from the command line. -[cli](https://github.com/urfave/cli) is a convenient package to create command -line apps. It should be used whether the project is a daemon or a simple cli +[`cli`](https://github.com/urfave/cli) is a convenient package to create command +line apps. It should be used whether the project is a daemon or a simple CLI tool. Flags can be mapped to [environment variables](https://github.com/urfave/cli#values-from-the-environment) directly, which documents and centralizes at the same time all the possible command line @@ -389,7 +388,7 @@ functionality: This gives us a thin abstraction over underlying implementations that is consistent across Workhorse, Gitaly, and, in future, other Go servers. For example, in the case of `gitlab.com/gitlab-org/labkit/tracing` we can switch -from using Opentracing directly to using Zipkin or Gokit's own tracing wrapper +from using `Opentracing` directly to using `Zipkin` or Gokit's own tracing wrapper without changes to the application code, while still keeping the same consistent configuration mechanism (i.e. the `GITLAB_TRACING` environment variable). @@ -489,9 +488,9 @@ The following are some style guidelines that are specific to the Secure Team. ### Code style and format Use `goimports -local gitlab.com/gitlab-org` before committing. -[goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) +[`goimports`](https://godoc.org/golang.org/x/tools/cmd/goimports) is a tool that automatically formats Go source code using -[Gofmt](https://golang.org/cmd/gofmt/), in addition to formatting import lines, +[`Gofmt`](https://golang.org/cmd/gofmt/), in addition to formatting import lines, adding missing ones and removing unreferenced ones. By using the `-local gitlab.com/gitlab-org` option, `goimports` groups locally referenced packages separately from external ones. See diff --git a/doc/development/graphql_guide/batchloader.md b/doc/development/graphql_guide/batchloader.md index e965e678aba..1869c6e6f80 100644 --- a/doc/development/graphql_guide/batchloader.md +++ b/doc/development/graphql_guide/batchloader.md @@ -20,7 +20,7 @@ When implementing a new endpoint we should aim to minimise the number of SQL que Batch loading is useful when a series of queries for inputs `Qα, Qβ, ... Qω` can be combined to a single query for `Q[α, β, ... ω]`. An example of this is lookups by ID, where we can find two users by usernames as cheaply as one, but real-world examples can be more complex. -Batchloading is not suitable when the result sets have different sort-orders, grouping, aggregation or other non-composable features. +Batch loading is not suitable when the result sets have different sort-orders, grouping, aggregation or other non-composable features. There are two ways to use the batch-loader in your code. For simple ID lookups, use `::Gitlab::Graphql::Loaders::BatchModelLoader.new(model, id).find`. For more complex cases, you can use the batch API directly. diff --git a/doc/development/i18n/merging_translations.md b/doc/development/i18n/merging_translations.md index 582c1428bdd..e7d25942143 100644 --- a/doc/development/i18n/merging_translations.md +++ b/doc/development/i18n/merging_translations.md @@ -66,12 +66,14 @@ have been fixed on master. NOTE: These instructions work only for GitLab Team Members. -If for some reason the GitLab integration in CrowdIn does not exist, it can be -recreated by the following steps: +If for some reason the GitLab integration in CrowdIn doesn't exist, you can +recreate it with the following steps: -1. Sign in to GitLab as `gitlab-crowdin-bot` (If you're a GitLab Team Member, find credentials in the GitLab shared [1Password account](https://about.gitlab.com/handbook/security/#1password-for-teams) -1. Sign in to Crowdin with the GitLab integration -1. Navigate to Settings > Integrations > GitLab > Set Up Integration -1. Select `gitlab-org/gitlab` repository -1. On `Select Branches for Translation`, select `master` -1. Ensure the `Service Branch Name` is `master-i18n` +1. Sign in to GitLab as `gitlab-crowdin-bot`. (If you're a GitLab Team Member, + find credentials in the GitLab shared + [1Password account](https://about.gitlab.com/handbook/security/#1password-for-teams).) +1. Sign in to CrowdIn with the GitLab integration. +1. Go to **Settings > Integrations > GitLab > Set Up Integration**. +1. Select the `gitlab-org/gitlab` repository. +1. In `Select Branches for Translation`, select `master`. +1. Ensure the `Service Branch Name` is `master-i18n`. diff --git a/doc/development/integrations/codesandbox.md b/doc/development/integrations/codesandbox.md index 44a3778dc3e..3909991eed5 100644 --- a/doc/development/integrations/codesandbox.md +++ b/doc/development/integrations/codesandbox.md @@ -4,17 +4,17 @@ group: Development info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Set up local Codesandbox development environment +# Set up local CodeSandbox development environment -This guide walks through setting up a local [Codesandbox repository](https://github.com/codesandbox/codesandbox-client) and integrating it with a local GitLab instance. Codesandbox -is used to power the Web IDE's [Live Preview feature](../../user/project/web_ide/index.md#live-preview). Having a local Codesandbox setup is useful for debugging upstream issues or +This guide walks through setting up a local [CodeSandbox repository](https://github.com/codesandbox/codesandbox-client) and integrating it with a local GitLab instance. CodeSandbox +is used to power the Web IDE's [Live Preview feature](../../user/project/web_ide/index.md#live-preview). Having a local CodeSandbox setup is useful for debugging upstream issues or creating upstream contributions like [this one](https://github.com/codesandbox/codesandbox-client/pull/5137). ## Initial setup -Before using Codesandbox with your local GitLab instance, you must: +Before using CodeSandbox with your local GitLab instance, you must: -1. Enable HTTPS on your GDK. Codesandbox uses Service Workers that require `https`. +1. Enable HTTPS on your GDK. CodeSandbox uses Service Workers that require `https`. Follow the GDK [NGINX configuration instructions](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/doc/howto/nginx.md) to enable HTTPS for GDK. 1. Clone the [`codesandbox-client` project](https://github.com/codesandbox/codesandbox-client) locally. If you plan on contributing upstream, you might want to fork and clone first. @@ -38,16 +38,16 @@ Before using Codesandbox with your local GitLab instance, you must: You can run `yarn build:clean` to clean up the build assets. -## Use local GitLab instance with local Codesandbox +## Use local GitLab instance with local CodeSandbox -GitLab integrates with two parts of Codesandbox: +GitLab integrates with two parts of CodeSandbox: - An NPM package called `smooshpack` (called `sandpack` in the `codesandbox-client` project). This exposes an entrypoint for us to kick off Codesandbox's bundler. -- A server that houses Codesandbox assets for bundling and previewing. This is hosted +- A server that houses CodeSandbox assets for bundling and previewing. This is hosted on a separate server for security. -Each time you want to run GitLab and Codesandbox together, you need to perform the +Each time you want to run GitLab and CodeSandbox together, you need to perform the steps in the following sections. ### Use local `smooshpack` for GitLab @@ -94,7 +94,7 @@ mkdir node_modules ln -s $PATH_TO_LOCAL_GITLAB/node_modules/core-js ./node_modules/core-js ``` -### Start building codesandbox app assets +### Start building CodeSandbox app assets In the `codesandbox-client` project directory: @@ -104,7 +104,7 @@ cd packages/app yarn start:sandpack-sandbox ``` -### Create HTTPS proxy for Codesandbox `sandpack` assets +### Create HTTPS proxy for CodeSandbox `sandpack` assets Because we need `https`, we need to create a proxy to the webpack server. We can use [`http-server`](https://www.npmjs.com/package/http-server), which can do this proxying @@ -117,7 +117,7 @@ npx http-server --proxy http://localhost:3000 -S -C $PATH_TO_CERT_PEM -K $PATH_T ### Update `bundler_url` setting in GitLab We need to update our `application_setting_implementation.rb` to point to the server that hosts the -Codesandbox `sandpack` assets. For instance, if these assets are hosted by a server at `https://sandpack.local:8044`: +CodeSandbox `sandpack` assets. For instance, if these assets are hosted by a server at `https://sandpack.local:8044`: ```patch diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb @@ -139,7 +139,7 @@ index 6eed627b502..1824669e881 100644 NOTE: You can apply this patch by copying it to your clipboard and running `pbpaste | git apply`. -You'll might want to restart the GitLab Rails server after making this change: +You may want to restart the GitLab Rails server after making this change: ```shell gdk restart rails-web diff --git a/doc/development/packages.md b/doc/development/packages.md index 6740a3ae141..e1fa48cc350 100644 --- a/doc/development/packages.md +++ b/doc/development/packages.md @@ -191,7 +191,7 @@ support is done by overriding a specific function in the API helpers, like For this authentication mechanism, keep in mind that some clients can send an unauthenticated request first, wait for the 401 Unauthorized response with the [`WWW-Authenticate`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate) field, then send an updated (authenticated) request. This case is more involved as -GitLab needs to handle the 401 Unauthorized response. The [Nuget API](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/api/nuget_packages.rb) +GitLab needs to handle the 401 Unauthorized response. The [NuGet API](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/api/nuget_packages.rb) supports this case. #### Authorization @@ -245,8 +245,8 @@ in your local development environment. #### Rate Limits on GitLab.com Package manager clients can make rapid requests that exceed the -[GitLab.com standard API rate limits](../user/gitlab_com/index.md#gitlabcom-specific-rate-limits). -This results in a `429 Too Many Requests` error. +[GitLab.com standard API rate limits](../user/gitlab_com/index.md#gitlabcom-specific-rate-limits). +This results in a `429 Too Many Requests` error. We have opened a set of paths to allow higher rate limits. Unless it is not possible, new package managers should follow these conventions so they can take advantage of the diff --git a/doc/development/performance.md b/doc/development/performance.md index f28c35a72a1..3b4525dc8ee 100644 --- a/doc/development/performance.md +++ b/doc/development/performance.md @@ -50,7 +50,7 @@ GitLab provides built-in tools to help improve performance and availability: - [Service measurement](service_measurement.md) for measuring and logging service execution. GitLab team members can use [GitLab.com's performance monitoring systems](https://about.gitlab.com/handbook/engineering/monitoring/) located at -, this requires you to log in using your +[`dashboards.gitlab.net`](https://dashboards.gitlab.net), this requires you to log in using your `@gitlab.com` email address. Non-GitLab team-members are advised to set up their own Prometheus and Grafana stack. @@ -176,7 +176,7 @@ stackprof tmp/project_policy_spec.rb.dump --graphviz > project_policy_spec.dot dot -Tsvg project_policy_spec.dot > project_policy_spec.svg ``` -To load the profile in [kcachegrind](https://kcachegrind.github.io/): +To load the profile in [KCachegrind](https://kcachegrind.github.io/): ```shell stackprof tmp/project_policy_spec.rb.dump --callgrind > project_policy_spec.callgrind @@ -184,7 +184,7 @@ kcachegrind project_policy_spec.callgrind # Linux qcachegrind project_policy_spec.callgrind # Mac ``` -For flamegraphs, enable raw collection first. Note that raw +For flame graphs, enable raw collection first. Note that raw collection can generate a very large file, so increase the `INTERVAL`, or run on a smaller number of specs for smaller file size: @@ -192,7 +192,7 @@ run on a smaller number of specs for smaller file size: RAW=true bin/rspec-stackprof spec/policies/group_member_policy_spec.rb ``` -You can then generate, and view the resultant flamegraph. It might take a +You can then generate, and view the resultant flame graph. It might take a while to generate based on the output file size: ```shell @@ -251,7 +251,7 @@ In order to enable production profiling for Ruby processes, you can set the `STA The following configuration options can be configured: -- `STACKPROF_ENABLED`: Enables stackprof signal handler on SIGUSR2 signal. +- `STACKPROF_ENABLED`: Enables Stackprof signal handler on SIGUSR2 signal. Defaults to `false`. - `STACKPROF_MODE`: See [sampling modes](https://github.com/tmm1/stackprof#sampling). Defaults to `cpu`. @@ -264,7 +264,7 @@ The following configuration options can be configured: - `STACKPROF_TIMEOUT_S`: Profiling timeout in seconds. Profiling will automatically stop after this time has elapsed. Defaults to `30`. - `STACKPROF_RAW`: Whether to collect raw samples or only aggregates. Raw - samples are needed to generate flamegraphs, but they do have a higher memory + samples are needed to generate flame graphs, but they do have a higher memory and disk overhead. Defaults to `true`. Once enabled, profiling can be triggered by sending a `SIGUSR2` signal to the @@ -287,7 +287,7 @@ The Puma master process is not supported. Neither is Unicorn. Sending SIGUSR2 to either of those triggers restarts. In the case of Puma, take care to only send the signal to Puma workers. -This can be done via `pkill -USR2 puma:`. The `:` disambiguates between `puma +This can be done via `pkill -USR2 puma:`. The `:` distinguishes between `puma 4.3.3.gitlab.2 ...` (the master process) from `puma: cluster worker 0: ...` (the worker processes), selecting the latter. @@ -296,7 +296,7 @@ For Sidekiq, the signal can be sent to the `sidekiq-cluster` process via `pkill children. Alternatively, you can also select a specific pid of interest. Production profiles can be especially noisy. It can be helpful to visualize them -as a [flamegraph](https://github.com/brendangregg/FlameGraph). This can be done +as a [flame graph](https://github.com/brendangregg/FlameGraph). This can be done via: ```shell @@ -747,5 +747,4 @@ Assuming you are working with ActiveRecord models, you might also find these lin ### Examples -You may find some useful examples in this snippet: - +You may find some useful examples in [this snippet](https://gitlab.com/gitlab-org/gitlab-foss/snippets/33946). diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md index 1bcd502e4b4..be725a702e9 100644 --- a/doc/development/pipelines.md +++ b/doc/development/pipelines.md @@ -648,7 +648,7 @@ that are scoped to a single [configuration keyword](../ci/yaml/README.md#job-key |------------------|-------------| | `.default-retry` | Allows a job to [retry](../ci/yaml/README.md#retry) upon `unknown_failure`, `api_failure`, `runner_system_failure`, `job_execution_timeout`, or `stuck_or_timeout_failure`. | | `.default-before_script` | Allows a job to use a default `before_script` definition suitable for Ruby/Rails tasks that may need a database running (e.g. tests). | -| `.setup-test-env-cache` | Allows a job to use a default `cache` definition suitable for setuping test environment for subsequent Ruby/Rails tasks. | +| `.setup-test-env-cache` | Allows a job to use a default `cache` definition suitable for setting up test environment for subsequent Ruby/Rails tasks. | | `.rails-cache` | Allows a job to use a default `cache` definition suitable for Ruby/Rails tasks. | | `.static-analysis-cache` | Allows a job to use a default `cache` definition suitable for static analysis tasks. | | `.coverage-cache` | Allows a job to use a default `cache` definition suitable for coverage tasks. | diff --git a/doc/development/stage_group_dashboards.md b/doc/development/stage_group_dashboards.md index 8b717638552..e75237869ba 100644 --- a/doc/development/stage_group_dashboards.md +++ b/doc/development/stage_group_dashboards.md @@ -61,7 +61,7 @@ Although most of the metrics displayed in the panels are self-explanatory in the To inspect the raw data of the panel for further calculation, click on the Inspect button from the dropdown menu of a panel. Queries, raw data, and panel JSON structure are available. Read more at [Grafana panel inspection](https://grafana.com/docs/grafana/latest/panels/inspect-panel/). -All the dashboards are powered by [Grafana](https://grafana.com/), a frontend for displaying metrics. Grafana consumes the data returned from queries to backend Prometheus data source, then presents them under different visualizations. The stage group dashboards are built to serve the most common use cases with a limited set of filters, and pre-built queries. Grafana provides a way to explore and visualize the metrics data with [Grafana Explore](https://grafana.com/docs/grafana/latest/explore/). This would require some knowledge about [Prometheus Promql query language](https://prometheus.io/docs/prometheus/latest/querying/basics/). +All the dashboards are powered by [Grafana](https://grafana.com/), a frontend for displaying metrics. Grafana consumes the data returned from queries to backend Prometheus data source, then presents them under different visualizations. The stage group dashboards are built to serve the most common use cases with a limited set of filters, and pre-built queries. Grafana provides a way to explore and visualize the metrics data with [Grafana Explore](https://grafana.com/docs/grafana/latest/explore/). This would require some knowledge about [Prometheus PromQL query language](https://prometheus.io/docs/prometheus/latest/querying/basics/). ## How to debug with the dashboards @@ -83,7 +83,7 @@ All the dashboards are powered by [Grafana](https://grafana.com/), a frontend fo ## How to customize the dashboard -All Grafana dashboards at GitLab are generated from the [Jsonnet files](https://github.com/grafana/grafonnet-lib) stored in [the runbook project](https://gitlab.com/gitlab-com/runbooks/-/tree/master/dashboards). Particularly, the stage group dashboards definitions are stored in [/dashboards/stage-groups](https://gitlab.com/gitlab-com/runbooks/-/tree/master/dashboards/stage-groups) subfolder in the Runbook. By convention, each group has a corresponding jsonnet file. The dashboards are synced with GitLab [stage group data](https://gitlab.com/gitlab-com/www-gitlab-com/-/raw/master/data/stages.yml) every month. Expansion and customization are one of the key principles used when we designed this system. To customize your group's dashboard, you need to edit the corresponding file and follow the [Runbook workflow](https://gitlab.com/gitlab-com/runbooks/-/tree/master/dashboards#dashboard-source). The dashboard is updated after the MR is merged. Looking at an autogenerated file, for example, [`product_planning.dashboard.jsonnet`](https://gitlab.com/gitlab-com/runbooks/-/blob/master/dashboards/stage-groups/product_planning.dashboard.jsonnet): +All Grafana dashboards at GitLab are generated from the [Jsonnet files](https://github.com/grafana/grafonnet-lib) stored in [the runbook project](https://gitlab.com/gitlab-com/runbooks/-/tree/master/dashboards). Particularly, the stage group dashboards definitions are stored in [/dashboards/stage-groups](https://gitlab.com/gitlab-com/runbooks/-/tree/master/dashboards/stage-groups) subfolder in the Runbook. By convention, each group has a corresponding Jsonnet file. The dashboards are synced with GitLab [stage group data](https://gitlab.com/gitlab-com/www-gitlab-com/-/raw/master/data/stages.yml) every month. Expansion and customization are one of the key principles used when we designed this system. To customize your group's dashboard, you need to edit the corresponding file and follow the [Runbook workflow](https://gitlab.com/gitlab-com/runbooks/-/tree/master/dashboards#dashboard-source). The dashboard is updated after the MR is merged. Looking at an autogenerated file, for example, [`product_planning.dashboard.jsonnet`](https://gitlab.com/gitlab-com/runbooks/-/blob/master/dashboards/stage-groups/product_planning.dashboard.jsonnet): ```jsonnet // This file is autogenerated using scripts/update_stage_groups_dashboards.rb diff --git a/doc/development/utilities.md b/doc/development/utilities.md index f0f71842d69..d7baa6b23a5 100644 --- a/doc/development/utilities.md +++ b/doc/development/utilities.md @@ -117,9 +117,8 @@ regular Ruby module. Since we already have `Prependable` as a patch for `ActiveSupport::Concern` to enable `prepend`, it has consequences with how it would interact with -`override` and `class_methods`. We add a workaround directly into -`Prependable` to resolve the problem, by `extend`ing `ClassMethods` into the -defining module. +`override` and `class_methods`. As a workaround, `extend` `ClassMethods` +into the defining `Prependable` module. This allows us to use `override` to verify `class_methods` used in the context mentioned above. This workaround only applies when we run the diff --git a/doc/install/README.md b/doc/install/README.md index 39bea33de0b..092aca8a5b7 100644 --- a/doc/install/README.md +++ b/doc/install/README.md @@ -24,7 +24,7 @@ install GitLab: The cloud native Helm chart for installing GitLab and all of its components on Kubernetes. - [_Docker_](#installing-gitlab-with-docker): The Omnibus GitLab packages, - dockerized. + Dockerized. - [_Source_](#installing-gitlab-from-source): Install GitLab and all of its components from scratch. - [_Cloud provider_](#installing-gitlab-on-cloud-providers): Install directly diff --git a/doc/install/installation.md b/doc/install/installation.md index e42acde8780..86f8476a786 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -172,8 +172,12 @@ sudo apt-get install -y postfix Then select 'Internet Site' and press enter to confirm the hostname. + + ### Exiftool + + [GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse#dependencies) requires `exiftool` to remove EXIF data from uploaded images. @@ -187,7 +191,7 @@ The Ruby interpreter is required to run GitLab. See the [requirements page](requirements.md#ruby-versions) for the minimum Ruby requirements. -The use of Ruby version managers such as [RVM](https://rvm.io/), [rbenv](https://github.com/rbenv/rbenv) or [chruby](https://github.com/postmodern/chruby) with GitLab +The use of Ruby version managers such as [`RVM`](https://rvm.io/), [`rbenv`](https://github.com/rbenv/rbenv) or [`chruby`](https://github.com/postmodern/chruby) with GitLab in production, frequently leads to hard to diagnose problems. Version managers are not supported and we strongly advise everyone to follow the instructions below to use a system Ruby. @@ -241,7 +245,7 @@ requirements for these are: - `node` >= v10.14.2. (We recommend node 14.x as it is faster) - `yarn` >= v1.10.0. -In many distros, +In many distributions, the versions provided by the official package repositories are out of date, so we need to install through the following commands: @@ -935,7 +939,7 @@ production: url: redis://redis.example.tld:6379 ``` -If you want to connect the Redis server via socket, use the "unix:" URL scheme and the path to the Redis socket file in the `config/resque.yml` file. +If you want to connect the Redis server via socket, use the `unix:` URL scheme and the path to the Redis socket file in the `config/resque.yml` file. ```yaml # example diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md index 21fe87a71b1..3ee899958af 100644 --- a/doc/install/openshift_and_gitlab/index.md +++ b/doc/install/openshift_and_gitlab/index.md @@ -15,7 +15,7 @@ for details. ## Introduction -[OpenShift Origin](https://www.okd.io/) (**Note:** renamed to OKD in Aug 2018) is an open source container application +[OpenShift Origin](https://www.okd.io/) (**Note:** renamed to OKD in August 2018) is an open source container application platform created by [RedHat](https://www.redhat.com/en), based on [Kubernetes](https://kubernetes.io/) and [Docker](https://www.docker.com). That means you can host your own PaaS for free and almost with no hassle. @@ -44,7 +44,7 @@ test OpenShift easily: It is also important to mention that for the purposes of this tutorial, the latest Origin release is used: -- **oc** `v1.3.0` (must be [installed](https://github.com/openshift/origin/releases/tag/v1.3.0) locally on your computer) +- **`oc`** `v1.3.0` (must be [installed](https://github.com/openshift/origin/releases/tag/v1.3.0) locally on your computer) - **OpenShift** `v1.3.0` (is pre-installed in the [VM image](https://app.vagrantup.com/openshift/boxes/origin-all-in-one)) - **Kubernetes** `v1.3.0` (is pre-installed in the [VM image](https://app.vagrantup.com/openshift/boxes/origin-all-in-one)) @@ -59,8 +59,8 @@ on your computer. ## Getting familiar with OpenShift Origin -The environment we are about to use is based on CentOS 7 which comes with all -the tools needed pre-installed: Docker, Kubernetes, OpenShift, etcd. +The environment we are about to use is based on CentOS 7, which comes with all +the tools needed pre-installed, including Docker, Kubernetes, and OpenShift. ### Test OpenShift using Vagrant @@ -100,14 +100,14 @@ order to proceed. Let's login as admin with username/password `admin/admin`. This is what the landing page looks like: -![openshift web console](img/web-console.png) +![OpenShift web console](img/web-console.png) You can see that a number of [projects](https://docs.okd.io/3.11/dev_guide/projects.html) are already created for testing purposes. If you head over the `openshift-infra` project, a number of services with their respective pods are there to explore. -![openshift web console](img/openshift-infra-project.png) +![OpenShift web console](img/openshift-infra-project.png) We are not going to explore the whole interface, but if you want to learn about the key concepts of OpenShift, read the [core concepts reference](https://docs.okd.io/3.11/architecture/core_concepts/index.html) @@ -318,7 +318,7 @@ NOTE: The `gitlab.apps.10.2.2.2.nip.io` hostname that is used by default will resolve to the host with IP `10.2.2.2` which is the IP our VM uses. It is a trick to have distinct FQDNs pointing to services that are on our local network. -Read more on how this works in . +Read more on how this works at [nip.io](https://nip.io). Now that we configured this, let's see how to manage and scale GitLab. @@ -343,7 +343,7 @@ created the GitLab app? This is where you can see them in action. ![Running pods](img/running-pods.png) -You can see GitLab being reconfigured by taking look at the logs in realtime. +You can see GitLab being reconfigured by taking look at the logs in real time. Click on `gitlab-ce-2-j7ioe` (your ID will be different) and go to the **Logs** tab. @@ -464,7 +464,7 @@ OpenShift's website about [autoscaling](https://docs.okd.io/3.11/dev_guide/pod_a As stated in the [all-in-one VM](https://www.okd.io/minishift/) page: > By default, OpenShift will not allow a container to run as root or even a -non-random container assigned userid. Most Docker images in Docker Hub do not +non-random container assigned user ID. Most Docker images in Docker Hub do not follow this best practice and instead run as root. The all-in-one VM we are using has this security turned off so it will not diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 69983edc383..482109646eb 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -179,7 +179,7 @@ Omnibus GitLab defaults to the recommended Puma settings. Regardless of installa tune the Puma settings. If you're using Omnibus GitLab, see [Puma settings](https://docs.gitlab.com/omnibus/settings/puma.html) -for instructions on changing the Puma settings. If you're using the GitLab Helm chart, see the [Webservice chart](https://docs.gitlab.com/charts/charts/gitlab/webservice/index.html). +for instructions on changing the Puma settings. If you're using the GitLab Helm chart, see the [`webservice` chart](https://docs.gitlab.com/charts/charts/gitlab/webservice/index.html). ### Puma workers @@ -227,7 +227,7 @@ recommendation above) please see [the Unicorn settings in the Omnibus GitLab doc Redis stores all user sessions and the background task queue. The storage requirements for Redis are minimal, about 25kB per user. -Sidekiq processes the background jobs with a multithreaded process. +Sidekiq processes the background jobs with a multi-threaded process. This process starts with the entire Rails stack (200MB+) but it can grow over time due to memory leaks. On a very active server (10,000 billable users) the Sidekiq process can use 1GB+ of memory. diff --git a/doc/integration/vault.md b/doc/integration/vault.md index 3c49cd47509..d3102c2616a 100644 --- a/doc/integration/vault.md +++ b/doc/integration/vault.md @@ -44,7 +44,7 @@ The following assumes you already have Vault installed and running. Success! Enabled oidc auth method at: oidc/ ``` -1. **Write the OIDC config:** +1. **Write the OIDC configuration:** Next, Vault needs to be given the application ID and secret generated by GitLab. @@ -67,7 +67,7 @@ The following assumes you already have Vault installed and running. Success! Data written to: auth/oidc/config ``` -1. **Write the OIDC Role Config:** +1. **Write the OIDC Role Configuration:** Now that Vault has a GitLab application ID and secret, it needs to know the [**Redirect URIs**](https://www.vaultproject.io/docs/auth/jwt#redirect-uris) and scopes given to GitLab during the application creation process. The redirect URIs need to match where your Vault instance is running. The `oidc_scopes` field needs to include the `openid`. Similarly to the previous step, replace `your_application_id` with the generated application ID from GitLab: @@ -108,7 +108,7 @@ The following assumes you already have Vault installed and running. Here's a short explanation of what this command does: - 1. In the **Write the OIDC Role Config** (step 4), we created a role called + 1. In the **Write the OIDC Role Configuration** (step 4), we created a role called `demo`. We set `role=demo` so Vault knows which configuration we'd like to sign in with. 1. To set Vault to use the `OIDC` sign-in method, we set `-method=oidc`. diff --git a/doc/push_rules/push_rules.md b/doc/push_rules/push_rules.md index babbf5d04ce..7c652e2326e 100644 --- a/doc/push_rules/push_rules.md +++ b/doc/push_rules/push_rules.md @@ -92,7 +92,7 @@ The following options are available: | Check whether the commit author is a GitLab user | Restrict commits to existing GitLab users (checked against their emails). | | Reject unverified users **(PREMIUM)** | GitLab rejects any commit that was not committed by an authenticated user. | | Check whether commit is signed through GPG **(PREMIUM)** | Reject commit when it is not signed through GPG. Read [signing commits with GPG](../user/project/repository/gpg_signed_commits/index.md). | -| Prevent pushing secret files | GitLab rejects any files that are likely to contain secrets. See the [forbiden file names](#prevent-pushing-secrets-to-the-repository). | +| Prevent pushing secret files | GitLab rejects any files that are likely to contain secrets. See the [forbidden file names](#prevent-pushing-secrets-to-the-repository). | | Require expression in commit messages | Only commit messages that match this regular expression are allowed to be pushed. Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. | | Reject expression in commit messages | Only commit messages that do not match this regular expression are allowed to be pushed. Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. | | Restrict by branch name | Only branch names that match this regular expression are allowed to be pushed. Leave empty to allow any branch name. | diff --git a/doc/security/cicd_environment_variables.md b/doc/security/cicd_environment_variables.md index 4d60df8e531..914cf553991 100644 --- a/doc/security/cicd_environment_variables.md +++ b/doc/security/cicd_environment_variables.md @@ -8,6 +8,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w Environment variables are applied to environments via the runner and can be set from the project's **Settings > CI/CD** page. -The values are encrypted using [aes-256-cbc](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) and stored in the database. +The values are encrypted using [`aes-256-cbc`](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) and stored in the database. This data can only be decrypted with a valid [secrets file](../raketasks/backup_restore.md#when-the-secrets-file-is-lost). diff --git a/doc/security/crime_vulnerability.md b/doc/security/crime_vulnerability.md index c5f8afe36ad..9a43f5dfca8 100644 --- a/doc/security/crime_vulnerability.md +++ b/doc/security/crime_vulnerability.md @@ -58,7 +58,7 @@ vulnerability. ## References -- NGINX ["Module ngx_http_spdy_module"](http://nginx.org/en/docs/http/ngx_http_spdy_module.html) +- NGINX ["Module `ngx_http_spdy_module`"](http://nginx.org/en/docs/http/ngx_http_spdy_module.html) - Tenable Network Security, Inc. ["Transport Layer Security (TLS) Protocol CRIME Vulnerability"](https://www.tenable.com/plugins/index.php?view=single&id=62565) - Wikipedia contributors, ["CRIME"](https://en.wikipedia.org/wiki/CRIME) Wikipedia, The Free Encyclopedia diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index 4be8210f376..9a367d218f0 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -15,7 +15,7 @@ NOTE: Support for MySQL was removed in GitLab 12.1. This procedure should be performed **before** installing GitLab 12.1. -[pgloader](https://pgloader.io/) 3.4.1+ is required, confirm with `pgloader -V`. +[pgLoader](https://pgloader.io/) 3.4.1+ is required, confirm with `pgloader -V`. You can install it directly from your distribution, for example in Debian/Ubuntu: @@ -52,7 +52,7 @@ For other distributions, follow the instructions in PostgreSQL's and then install `pgloader`. If you are migrating to a Docker based installation, you must install -pgloader within the container as it is not included in the container image. +pgLoader within the container as it is not included in the container image. 1. Start a shell session in the context of the running container: @@ -60,7 +60,7 @@ pgloader within the container as it is not included in the container image. docker exec -it gitlab bash ``` -1. Install pgloader: +1. Install pgLoader: ```shell apt-get update diff --git a/lib/gitlab/ci/trace/checksum.rb b/lib/gitlab/ci/trace/checksum.rb index 7cdb6a6c03c..92bed817875 100644 --- a/lib/gitlab/ci/trace/checksum.rb +++ b/lib/gitlab/ci/trace/checksum.rb @@ -30,7 +30,11 @@ module Gitlab end def state_crc32 - strong_memoize(:state_crc32) { build.pending_state&.crc32 } + strong_memoize(:state_crc32) do + ::Gitlab::Database::Consistency.with_read_consistency do + build.pending_state&.crc32 + end + end end def chunks_crc32 @@ -59,8 +63,10 @@ module Gitlab # def trace_chunks strong_memoize(:trace_chunks) do - build.trace_chunks.persisted - .select(::Ci::BuildTraceChunk.metadata_attributes) + ::Ci::BuildTraceChunk.with_read_consistency(build) do + build.trace_chunks.persisted + .select(::Ci::BuildTraceChunk.metadata_attributes) + end end end diff --git a/lib/gitlab/ci/trace/chunked_io.rb b/lib/gitlab/ci/trace/chunked_io.rb index 6f3e4ccf48d..7c2e39b1e53 100644 --- a/lib/gitlab/ci/trace/chunked_io.rb +++ b/lib/gitlab/ci/trace/chunked_io.rb @@ -227,12 +227,20 @@ module Gitlab end # rubocop: enable CodeReuse/ActiveRecord - def build_chunk - @chunks_cache[chunk_index] = ::Ci::BuildTraceChunk.new(build: build, chunk_index: chunk_index) + def next_chunk + @chunks_cache[chunk_index] = begin + if ::Ci::BuildTraceChunk.consistent_reads_enabled?(build) + ::Ci::BuildTraceChunk + .safe_find_or_create_by(build: build, chunk_index: chunk_index) + else + ::Ci::BuildTraceChunk + .new(build: build, chunk_index: chunk_index) + end + end end def ensure_chunk - current_chunk || build_chunk + current_chunk || next_chunk || current_chunk end # rubocop: disable CodeReuse/ActiveRecord diff --git a/lib/gitlab/database/consistency.rb b/lib/gitlab/database/consistency.rb new file mode 100644 index 00000000000..b7d06a26ddb --- /dev/null +++ b/lib/gitlab/database/consistency.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Gitlab + module Database + ## + # This class is used to make it possible to ensure read consistency in + # GitLab EE without the need of overriding a lot of methods / classes / + # classs. + # + # This is a CE class that does nothing in CE, because database load + # balancing is EE-only feature, but you can still use it in CE. It will + # start ensuring read consistency once it is overridden in EE. + # + # Using this class in CE helps to avoid creeping discrepancy between CE / + # EE only to force usage of the primary database in EE. + # + class Consistency + ## + # In CE there is no database load balancing, so all reads are expected to + # be consistent by the ACID guarantees of a single PostgreSQL instance. + # + # This method is overridden in EE. + # + def self.with_read_consistency(&block) + yield + end + end + end +end + +::Gitlab::Database::Consistency.singleton_class.prepend_if_ee('EE::Gitlab::Database::Consistency') diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb index 0cf82acb16d..48f204e0b86 100644 --- a/lib/gitlab/setup_helper.rb +++ b/lib/gitlab/setup_helper.rb @@ -121,7 +121,7 @@ module Gitlab config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path } - config[:bin_dir] = File.expand_path(Gitlab.config.gitaly.client_path) + config[:bin_dir] = Gitlab.config.gitaly.client_path config[:gitlab] = { url: Gitlab.config.gitlab.url } config[:logging] = { dir: Rails.root.join('log').to_s } diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml index 9f77c327584..466c151a803 100644 --- a/lib/gitlab/usage_data_counters/known_events/common.yml +++ b/lib/gitlab/usage_data_counters/known_events/common.yml @@ -273,6 +273,11 @@ redis_slot: testing aggregation: weekly feature_flag: usage_data_i_testing_group_code_coverage_project_click_total +- name: i_testing_load_performance_widget_total + category: testing + redis_slot: testing + aggregation: weekly + feature_flag: usage_data_i_testing_load_performance_widget_total # Project Management group - name: g_project_management_issue_title_changed category: issues_edit diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8a8b65c90e7..a89ccb085e8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -13630,9 +13630,6 @@ msgstr "" msgid "Go full screen" msgstr "" -msgid "Go to %{strongStart}Issues%{strongEnd} > %{strongStart}Boards%{strongEnd} to access your personalized learning issue board." -msgstr "" - msgid "Go to Integrations" msgstr "" @@ -14524,9 +14521,6 @@ msgstr "" msgid "Helps reduce request volume for protected paths" msgstr "" -msgid "Here are all your projects in your group, including the one you just created. To start, let’s take a look at your personalized learning project which will help you learn about GitLab at your own pace." -msgstr "" - msgid "Here you will find recent merge request activity" msgstr "" diff --git a/spec/frontend/boards/components/board_list_header_deprecated_spec.js b/spec/frontend/boards/components/board_list_header_deprecated_spec.js index 6207724e6a9..15e767e9d91 100644 --- a/spec/frontend/boards/components/board_list_header_deprecated_spec.js +++ b/spec/frontend/boards/components/board_list_header_deprecated_spec.js @@ -74,7 +74,13 @@ describe('Board List Header Component', () => { describe('Add issue button', () => { const hasNoAddButton = [ListType.closed]; - const hasAddButton = [ListType.backlog, ListType.label, ListType.milestone, ListType.assignee]; + const hasAddButton = [ + ListType.backlog, + ListType.label, + ListType.milestone, + ListType.iteration, + ListType.assignee, + ]; it.each(hasNoAddButton)('does not render when List Type is `%s`', (listType) => { createComponent({ listType }); diff --git a/spec/frontend/boards/components/board_list_header_spec.js b/spec/frontend/boards/components/board_list_header_spec.js index 357d05ced02..6d8c9e51d3f 100644 --- a/spec/frontend/boards/components/board_list_header_spec.js +++ b/spec/frontend/boards/components/board_list_header_spec.js @@ -78,7 +78,13 @@ describe('Board List Header Component', () => { describe('Add issue button', () => { const hasNoAddButton = [ListType.closed]; - const hasAddButton = [ListType.backlog, ListType.label, ListType.milestone, ListType.assignee]; + const hasAddButton = [ + ListType.backlog, + ListType.label, + ListType.milestone, + ListType.iteration, + ListType.assignee, + ]; it.each(hasNoAddButton)('does not render when List Type is `%s`', (listType) => { createComponent({ listType }); @@ -167,7 +173,7 @@ describe('Board List Header Component', () => { describe('user can drag', () => { const cannotDragList = [ListType.backlog, ListType.closed]; - const canDragList = [ListType.label, ListType.milestone, ListType.assignee]; + const canDragList = [ListType.label, ListType.milestone, ListType.iteration, ListType.assignee]; it.each(cannotDragList)( 'does not have user-can-drag-class so user cannot drag list', diff --git a/spec/frontend/onboarding_issues/index_spec.js b/spec/frontend/onboarding_issues/index_spec.js deleted file mode 100644 index 13093901e16..00000000000 --- a/spec/frontend/onboarding_issues/index_spec.js +++ /dev/null @@ -1,137 +0,0 @@ -import $ from 'jquery'; -import setWindowLocation from 'helpers/set_window_location_helper'; -import { showLearnGitLabIssuesPopover } from '~/onboarding_issues'; -import { getCookie, setCookie, removeCookie } from '~/lib/utils/common_utils'; -import Tracking from '~/tracking'; - -describe('Onboarding Issues Popovers', () => { - const COOKIE_NAME = 'onboarding_issues_settings'; - const getCookieValue = () => JSON.parse(getCookie(COOKIE_NAME)); - - beforeEach(() => { - jest.spyOn($.fn, 'popover'); - }); - - afterEach(() => { - $.fn.popover.mockRestore(); - document.getElementsByTagName('html')[0].innerHTML = ''; - removeCookie(COOKIE_NAME); - }); - - const setupShowLearnGitLabIssuesPopoverTest = ({ - currentPath = 'group/learn-gitlab', - isIssuesBoardsLinkShown = true, - isCookieSet = true, - cookieValue = true, - } = {}) => { - setWindowLocation(`http://example.com/${currentPath}`); - - if (isIssuesBoardsLinkShown) { - const elem = document.createElement('a'); - elem.setAttribute('data-qa-selector', 'issue_boards_link'); - document.body.appendChild(elem); - } - - if (isCookieSet) { - setCookie(COOKIE_NAME, { previous: true, 'issues#index': cookieValue }); - } - - showLearnGitLabIssuesPopover(); - }; - - describe('showLearnGitLabIssuesPopover', () => { - describe('when on another project', () => { - beforeEach(() => { - setupShowLearnGitLabIssuesPopoverTest({ - currentPath: 'group/another-project', - }); - }); - - it('does not show a popover', () => { - expect($.fn.popover).not.toHaveBeenCalled(); - }); - }); - - describe('when the issues boards link is not shown', () => { - beforeEach(() => { - setupShowLearnGitLabIssuesPopoverTest({ - isIssuesBoardsLinkShown: false, - }); - }); - - it('does not show a popover', () => { - expect($.fn.popover).not.toHaveBeenCalled(); - }); - }); - - describe('when the cookie is not set', () => { - beforeEach(() => { - setupShowLearnGitLabIssuesPopoverTest({ - isCookieSet: false, - }); - }); - - it('does not show a popover', () => { - expect($.fn.popover).not.toHaveBeenCalled(); - }); - }); - - describe('when the cookie value is false', () => { - beforeEach(() => { - setupShowLearnGitLabIssuesPopoverTest({ - cookieValue: false, - }); - }); - - it('does not show a popover', () => { - expect($.fn.popover).not.toHaveBeenCalled(); - }); - }); - - describe('with all the right conditions', () => { - beforeEach(() => { - setupShowLearnGitLabIssuesPopoverTest(); - }); - - it('shows a popover', () => { - expect($.fn.popover).toHaveBeenCalled(); - }); - - it('does not change the cookie value', () => { - expect(getCookieValue()['issues#index']).toBe(true); - }); - - it('disables the previous popover', () => { - expect(getCookieValue().previous).toBe(false); - }); - - describe('when clicking the issues boards link', () => { - beforeEach(() => { - document.querySelector('a[data-qa-selector="issue_boards_link"]').click(); - }); - - it('deletes the cookie', () => { - expect(getCookie(COOKIE_NAME)).toBe(undefined); - }); - }); - - describe('when dismissing the popover', () => { - beforeEach(() => { - jest.spyOn(Tracking, 'event'); - document.querySelector('.learn-gitlab.popover .js-close-learn-gitlab').click(); - }); - - it('deletes the cookie', () => { - expect(getCookie(COOKIE_NAME)).toBe(undefined); - }); - - it('sends a tracking event', () => { - expect(Tracking.event).toHaveBeenCalledWith( - 'Growth::Conversion::Experiment::OnboardingIssues', - 'dismiss_popover', - ); - }); - }); - }); - }); -}); diff --git a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js index aae25a3aa6d..76c56bd2815 100644 --- a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js +++ b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js @@ -5,7 +5,7 @@ import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue'; import { mockCommitMessage, mockDefaultBranch } from '../../mock_data'; -describe('~/pipeline_editor/pipeline_editor_app.vue', () => { +describe('Pipeline Editor | Commit Form', () => { let wrapper; const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => { @@ -21,8 +21,8 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { }); }; - const findCommitTextarea = () => wrapper.find(GlFormTextarea); - const findBranchInput = () => wrapper.find(GlFormInput); + const findCommitTextarea = () => wrapper.findComponent(GlFormTextarea); + const findBranchInput = () => wrapper.findComponent(GlFormInput); const findNewMrCheckbox = () => wrapper.find('[data-testid="new-mr-checkbox"]'); const findSubmitBtn = () => wrapper.find('[type="submit"]'); const findCancelBtn = () => wrapper.find('[type="reset"]'); diff --git a/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js new file mode 100644 index 00000000000..a480af3ea5b --- /dev/null +++ b/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js @@ -0,0 +1,223 @@ +import { mount } from '@vue/test-utils'; +import { GlFormTextarea, GlFormInput, GlLoadingIcon } from '@gitlab/ui'; +import CommitSection from '~/pipeline_editor/components/commit/commit_section.vue'; +import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue'; +import { objectToQuery, redirectTo } from '~/lib/utils/url_utility'; +import commitCreate from '~/pipeline_editor/graphql/mutations/commit_ci_file.mutation.graphql'; + +import { + mockCiConfigPath, + mockCiYml, + mockCommitSha, + mockCommitNextSha, + mockCommitMessage, + mockDefaultBranch, + mockProjectFullPath, + mockNewMergeRequestPath, +} from '../../mock_data'; +import { COMMIT_SUCCESS } from '~/pipeline_editor/constants'; + +jest.mock('~/lib/utils/url_utility', () => ({ + redirectTo: jest.fn(), + refreshCurrentPage: jest.fn(), + objectToQuery: jest.requireActual('~/lib/utils/url_utility').objectToQuery, + mergeUrlParams: jest.requireActual('~/lib/utils/url_utility').mergeUrlParams, +})); + +const mockVariables = { + projectPath: mockProjectFullPath, + startBranch: mockDefaultBranch, + message: mockCommitMessage, + filePath: mockCiConfigPath, + content: mockCiYml, + lastCommitId: mockCommitSha, +}; + +const mockProvide = { + ciConfigPath: mockCiConfigPath, + defaultBranch: mockDefaultBranch, + projectFullPath: mockProjectFullPath, + newMergeRequestPath: mockNewMergeRequestPath, +}; + +describe('Pipeline Editor | Commit section', () => { + let wrapper; + let mockMutate; + + const defaultProps = { ciFileContent: mockCiYml }; + + const createComponent = ({ props = {}, options = {}, provide = {} } = {}) => { + mockMutate = jest.fn().mockResolvedValue({ + data: { + commitCreate: { + errors: [], + commit: { + sha: mockCommitNextSha, + }, + }, + }, + }); + + wrapper = mount(CommitSection, { + propsData: { ...defaultProps, ...props }, + provide: { ...mockProvide, ...provide }, + data() { + return { + commitSha: mockCommitSha, + }; + }, + mocks: { + $apollo: { + mutate: mockMutate, + }, + }, + attachTo: document.body, + ...options, + }); + }; + + const findCommitForm = () => wrapper.findComponent(CommitForm); + const findCommitBtnLoadingIcon = () => + wrapper.find('[type="submit"]').findComponent(GlLoadingIcon); + + const submitCommit = async ({ + message = mockCommitMessage, + branch = mockDefaultBranch, + openMergeRequest = false, + } = {}) => { + await findCommitForm().findComponent(GlFormTextarea).setValue(message); + await findCommitForm().findComponent(GlFormInput).setValue(branch); + if (openMergeRequest) { + await findCommitForm().find('[data-testid="new-mr-checkbox"]').setChecked(openMergeRequest); + } + await findCommitForm().find('[type="submit"]').trigger('click'); + // Simulate the write to local cache that occurs after a commit + await wrapper.setData({ commitSha: mockCommitNextSha }); + }; + + const cancelCommitForm = async () => { + const findCancelBtn = () => wrapper.find('[type="reset"]'); + await findCancelBtn().trigger('click'); + }; + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + mockMutate.mockReset(); + + wrapper.destroy(); + wrapper = null; + }); + + describe('when the user commits changes to the current branch', () => { + beforeEach(async () => { + await submitCommit(); + }); + + it('calls the mutation with the default branch', () => { + expect(mockMutate).toHaveBeenCalledTimes(1); + expect(mockMutate).toHaveBeenCalledWith({ + mutation: commitCreate, + update: expect.any(Function), + variables: { + ...mockVariables, + branch: mockDefaultBranch, + }, + }); + }); + + it('emits an event to communicate the commit was successful', () => { + expect(wrapper.emitted('commit')).toHaveLength(1); + expect(wrapper.emitted('commit')[0]).toEqual([{ type: COMMIT_SUCCESS }]); + }); + + it('shows no saving state', () => { + expect(findCommitBtnLoadingIcon().exists()).toBe(false); + }); + + it('a second commit submits the latest sha, keeping the form updated', async () => { + await submitCommit(); + + expect(mockMutate).toHaveBeenCalledTimes(2); + expect(mockMutate).toHaveBeenCalledWith({ + mutation: commitCreate, + update: expect.any(Function), + variables: { + ...mockVariables, + lastCommitId: mockCommitNextSha, + branch: mockDefaultBranch, + }, + }); + }); + }); + + describe('when the user commits changes to a new branch', () => { + const newBranch = 'new-branch'; + + beforeEach(async () => { + await submitCommit({ + branch: newBranch, + }); + }); + + it('calls the mutation with the new branch', () => { + expect(mockMutate).toHaveBeenCalledWith({ + mutation: commitCreate, + update: expect.any(Function), + variables: { + ...mockVariables, + branch: newBranch, + }, + }); + }); + }); + + describe('when the user commits changes to open a new merge request', () => { + const newBranch = 'new-branch'; + + beforeEach(async () => { + await submitCommit({ + branch: newBranch, + openMergeRequest: true, + }); + }); + + it('redirects to the merge request page with source and target branches', () => { + const branchesQuery = objectToQuery({ + 'merge_request[source_branch]': newBranch, + 'merge_request[target_branch]': mockDefaultBranch, + }); + + expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`); + }); + }); + + describe('when the commit is ocurring', () => { + it('shows a saving state', async () => { + mockMutate.mockImplementationOnce(() => { + expect(findCommitBtnLoadingIcon().exists()).toBe(true); + return Promise.resolve(); + }); + + await submitCommit({ + message: mockCommitMessage, + branch: mockDefaultBranch, + openMergeRequest: false, + }); + }); + }); + + describe('when the commit form is cancelled', () => { + beforeEach(async () => { + createComponent(); + }); + + it('emits an event so that it cab be reseted', async () => { + await cancelCommitForm(); + + expect(wrapper.emitted('resetContent')).toHaveLength(1); + }); + }); +}); diff --git a/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js b/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js new file mode 100644 index 00000000000..df15a6c8e7f --- /dev/null +++ b/spec/frontend/pipeline_editor/components/header/pipeline_editor_header_spec.js @@ -0,0 +1,34 @@ +import { shallowMount } from '@vue/test-utils'; +import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue'; +import ValidationSegment from '~/pipeline_editor/components/header/validation_segment.vue'; + +import { mockLintResponse } from '../../mock_data'; + +describe('Pipeline editor header', () => { + let wrapper; + + const createComponent = () => { + wrapper = shallowMount(PipelineEditorHeader, { + props: { + ciConfigData: mockLintResponse, + isCiConfigDataLoading: false, + }, + }); + }; + + const findValidationSegment = () => wrapper.findComponent(ValidationSegment); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('template', () => { + beforeEach(() => { + createComponent(); + }); + it('renders the validation segment', () => { + expect(findValidationSegment().exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/pipeline_editor/components/info/validation_segment_spec.js b/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js similarity index 95% rename from spec/frontend/pipeline_editor/components/info/validation_segment_spec.js rename to spec/frontend/pipeline_editor/components/header/validation_segment_spec.js index 8a991d82018..c5b88e4b904 100644 --- a/spec/frontend/pipeline_editor/components/info/validation_segment_spec.js +++ b/spec/frontend/pipeline_editor/components/header/validation_segment_spec.js @@ -3,7 +3,9 @@ import { shallowMount } from '@vue/test-utils'; import { GlIcon } from '@gitlab/ui'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { sprintf } from '~/locale'; -import ValidationSegment, { i18n } from '~/pipeline_editor/components/info/validation_segment.vue'; +import ValidationSegment, { + i18n, +} from '~/pipeline_editor/components/header/validation_segment.vue'; import { CI_CONFIG_STATUS_INVALID } from '~/pipeline_editor/constants'; import { mockYmlHelpPagePath, mergeUnwrappedCiConfig } from '../../mock_data'; @@ -29,6 +31,11 @@ describe('~/pipeline_editor/components/info/validation_segment.vue', () => { const findLearnMoreLink = () => wrapper.findByTestId('learnMoreLink'); const findValidationMsg = () => wrapper.findByTestId('validationMsg'); + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + it('shows the loading state', () => { createComponent({ loading: true }); diff --git a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js new file mode 100644 index 00000000000..275e2987f38 --- /dev/null +++ b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js @@ -0,0 +1,129 @@ +import { nextTick } from 'vue'; +import { shallowMount, mount } from '@vue/test-utils'; +import { GlLoadingIcon } from '@gitlab/ui'; +import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; +import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue'; +import CiLint from '~/pipeline_editor/components/lint/ci_lint.vue'; + +import { mockLintResponse, mockCiYml } from '../mock_data'; + +describe('Pipeline editor tabs component', () => { + let wrapper; + const MockTextEditor = { + template: '
', + }; + const mockProvide = { + glFeatures: { + ciConfigVisualizationTab: true, + }, + }; + + const createComponent = ({ props = {}, provide = {}, mountFn = shallowMount } = {}) => { + wrapper = mountFn(PipelineEditorTabs, { + propsData: { + ciConfigData: mockLintResponse, + ciFileContent: mockCiYml, + isCiConfigDataLoading: false, + ...props, + }, + provide: { ...mockProvide, ...provide }, + stubs: { + TextEditor: MockTextEditor, + }, + }); + }; + + const findEditorTab = () => wrapper.find('[data-testid="editor-tab"]'); + const findLintTab = () => wrapper.find('[data-testid="lint-tab"]'); + const findVisualizationTab = () => wrapper.find('[data-testid="visualization-tab"]'); + const findCiLint = () => wrapper.findComponent(CiLint); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findPipelineGraph = () => wrapper.findComponent(PipelineGraph); + const findTextEditor = () => wrapper.findComponent(MockTextEditor); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('tabs', () => { + describe('editor tab', () => { + it('displays editor only after the tab is mounted', async () => { + createComponent({ mountFn: mount }); + + expect(findTextEditor().exists()).toBe(false); + + await nextTick(); + + expect(findTextEditor().exists()).toBe(true); + expect(findEditorTab().exists()).toBe(true); + }); + }); + + describe('visualization tab', () => { + describe('with feature flag on', () => { + describe('while loading', () => { + beforeEach(() => { + createComponent({ props: { isCiConfigDataLoading: true } }); + }); + + it('displays a loading icon if the lint query is loading', () => { + expect(findLoadingIcon().exists()).toBe(true); + expect(findPipelineGraph().exists()).toBe(false); + }); + }); + describe('after loading', () => { + beforeEach(() => { + createComponent(); + }); + + it('display the tab and visualization', () => { + expect(findVisualizationTab().exists()).toBe(true); + expect(findPipelineGraph().exists()).toBe(true); + }); + }); + }); + + describe('with feature flag off', () => { + beforeEach(() => { + createComponent({ + provide: { + glFeatures: { ciConfigVisualizationTab: false }, + }, + }); + }); + + it('does not display the tab or component', () => { + expect(findVisualizationTab().exists()).toBe(false); + expect(findPipelineGraph().exists()).toBe(false); + }); + }); + }); + + describe('lint tab', () => { + describe('while loading', () => { + beforeEach(() => { + createComponent({ props: { isCiConfigDataLoading: true } }); + }); + + it('displays a loading icon if the lint query is loading', () => { + expect(findLoadingIcon().exists()).toBe(true); + }); + + it('does not display the lint component', () => { + expect(findCiLint().exists()).toBe(false); + }); + }); + describe('after loading', () => { + beforeEach(() => { + createComponent(); + }); + + it('display the tab and the lint component', () => { + expect(findLintTab().exists()).toBe(true); + expect(findCiLint().exists()).toBe(true); + }); + }); + }); + }); +}); diff --git a/spec/frontend/pipeline_editor/components/text_editor_spec.js b/spec/frontend/pipeline_editor/components/text_editor_spec.js index 5cd226a1345..86ee370bb72 100644 --- a/spec/frontend/pipeline_editor/components/text_editor_spec.js +++ b/spec/frontend/pipeline_editor/components/text_editor_spec.js @@ -1,7 +1,5 @@ import { shallowMount } from '@vue/test-utils'; -import { EDITOR_READY_EVENT } from '~/editor/constants'; -import TextEditor from '~/pipeline_editor/components/text_editor.vue'; import { mockCiConfigPath, mockCiYml, @@ -10,7 +8,10 @@ import { mockProjectNamespace, } from '../mock_data'; -describe('~/pipeline_editor/components/text_editor.vue', () => { +import { EDITOR_READY_EVENT } from '~/editor/constants'; +import TextEditor from '~/pipeline_editor/components/text_editor.vue'; + +describe('Pipeline Editor | Text editor component', () => { let wrapper; let editorReadyListener; @@ -36,14 +37,17 @@ describe('~/pipeline_editor/components/text_editor.vue', () => { provide: { projectPath: mockProjectPath, projectNamespace: mockProjectNamespace, - }, - propsData: { ciConfigPath: mockCiConfigPath, - commitSha: mockCommitSha, }, attrs: { value: mockCiYml, }, + // Simulate graphQL client query result + data() { + return { + commitSha: mockCommitSha, + }; + }, listeners: { [EDITOR_READY_EVENT]: editorReadyListener, }, @@ -54,41 +58,64 @@ describe('~/pipeline_editor/components/text_editor.vue', () => { }); }; - const findEditor = () => wrapper.find(MockEditorLite); + const findEditor = () => wrapper.findComponent(MockEditorLite); - beforeEach(() => { - editorReadyListener = jest.fn(); - mockUse = jest.fn(); - mockRegisterCiSchema = jest.fn(); + afterEach(() => { + wrapper.destroy(); + wrapper = null; - createComponent(); + mockUse.mockClear(); + mockRegisterCiSchema.mockClear(); }); - it('contains an editor', () => { - expect(findEditor().exists()).toBe(true); - }); + describe('template', () => { + beforeEach(() => { + editorReadyListener = jest.fn(); + mockUse = jest.fn(); + mockRegisterCiSchema = jest.fn(); - it('editor contains the value provided', () => { - expect(findEditor().props('value')).toBe(mockCiYml); - }); + createComponent(); + }); - it('editor is configured for the CI config path', () => { - expect(findEditor().props('fileName')).toBe(mockCiConfigPath); - }); + it('contains an editor', () => { + expect(findEditor().exists()).toBe(true); + }); - it('editor is configured with syntax highligting', async () => { - expect(mockUse).toHaveBeenCalledTimes(1); - expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1); - expect(mockRegisterCiSchema).toHaveBeenCalledWith({ - projectNamespace: mockProjectNamespace, - projectPath: mockProjectPath, - ref: mockCommitSha, + it('editor contains the value provided', () => { + expect(findEditor().props('value')).toBe(mockCiYml); + }); + + it('editor is configured for the CI config path', () => { + expect(findEditor().props('fileName')).toBe(mockCiConfigPath); + }); + + it('bubbles up events', () => { + findEditor().vm.$emit(EDITOR_READY_EVENT); + + expect(editorReadyListener).toHaveBeenCalled(); }); }); - it('bubbles up events', () => { - findEditor().vm.$emit(EDITOR_READY_EVENT); + describe('register CI schema', () => { + beforeEach(async () => { + createComponent(); - expect(editorReadyListener).toHaveBeenCalled(); + // Since the editor will have already mounted, the event will have fired. + // To ensure we properly test this, we clear the mock and re-remit the event. + mockRegisterCiSchema.mockClear(); + mockUse.mockClear(); + + findEditor().vm.$emit(EDITOR_READY_EVENT); + }); + + it('configures editor with syntax highlight', async () => { + expect(mockUse).toHaveBeenCalledTimes(1); + expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1); + expect(mockRegisterCiSchema).toHaveBeenCalledWith({ + projectNamespace: mockProjectNamespace, + projectPath: mockProjectPath, + ref: mockCommitSha, + }); + }); }); }); diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js index e9423cf3fba..d9e7c708396 100644 --- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js +++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js @@ -1,119 +1,71 @@ -import { nextTick } from 'vue'; -import { mount, shallowMount, createLocalVue } from '@vue/test-utils'; -import { GlAlert, GlButton, GlFormInput, GlFormTextarea, GlLoadingIcon, GlTabs } from '@gitlab/ui'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { GlAlert, GlButton, GlLoadingIcon, GlTabs } from '@gitlab/ui'; import VueApollo from 'vue-apollo'; import waitForPromises from 'helpers/wait_for_promises'; import createMockApollo from 'helpers/mock_apollo_helper'; +import TextEditor from '~/pipeline_editor/components/text_editor.vue'; import httpStatusCodes from '~/lib/utils/http_status'; -import { objectToQuery, redirectTo, refreshCurrentPage } from '~/lib/utils/url_utility'; - -import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue'; -import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.graphql'; -import EditorTab from '~/pipeline_editor/components/ui/editor_tab.vue'; -import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; -import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue'; -import TextEditor from '~/pipeline_editor/components/text_editor.vue'; import { mockCiConfigPath, mockCiConfigQueryResponse, mockCiYml, - mockCommitSha, - mockCommitNextSha, - mockCommitMessage, mockDefaultBranch, - mockProjectPath, mockProjectFullPath, - mockProjectNamespace, - mockNewMergeRequestPath, } from './mock_data'; +import { COMMIT_SUCCESS, COMMIT_FAILURE, LOAD_FAILURE_UNKNOWN } from '~/pipeline_editor/constants'; +import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue'; +import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.graphql'; +import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue'; +import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue'; + const localVue = createLocalVue(); localVue.use(VueApollo); -jest.mock('~/lib/utils/url_utility', () => ({ - redirectTo: jest.fn(), - refreshCurrentPage: jest.fn(), - objectToQuery: jest.requireActual('~/lib/utils/url_utility').objectToQuery, - mergeUrlParams: jest.requireActual('~/lib/utils/url_utility').mergeUrlParams, -})); - const MockEditorLite = { template: '
', }; const mockProvide = { + ciConfigPath: mockCiConfigPath, + defaultBranch: mockDefaultBranch, projectFullPath: mockProjectFullPath, - projectPath: mockProjectPath, - projectNamespace: mockProjectNamespace, - glFeatures: { - ciConfigVisualizationTab: true, - }, }; -describe('~/pipeline_editor/pipeline_editor_app.vue', () => { +describe('Pipeline editor app component', () => { let wrapper; let mockApollo; let mockBlobContentData; let mockCiConfigData; - let mockMutate; - const createComponent = ({ - props = {}, - blobLoading = false, - lintLoading = false, - options = {}, - mountFn = shallowMount, - provide = mockProvide, - } = {}) => { - mockMutate = jest.fn().mockResolvedValue({ - data: { - commitCreate: { - errors: [], - commit: { - sha: mockCommitNextSha, - }, - }, - }, - }); - - wrapper = mountFn(PipelineEditorApp, { - propsData: { - ciConfigPath: mockCiConfigPath, - commitSha: mockCommitSha, - defaultBranch: mockDefaultBranch, - newMergeRequestPath: mockNewMergeRequestPath, - ...props, - }, - provide, + const createComponent = ({ blobLoading = false, options = {} } = {}) => { + wrapper = shallowMount(PipelineEditorApp, { + provide: mockProvide, stubs: { GlTabs, GlButton, CommitForm, EditorLite: MockEditorLite, - TextEditor, }, mocks: { $apollo: { queries: { - content: { + initialCiFileContent: { loading: blobLoading, }, ciConfigData: { - loading: lintLoading, + loading: false, }, }, - mutate: mockMutate, }, }, - // attachTo is required for input/submit events - attachTo: mountFn === mount ? document.body : null, ...options, }); }; - const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => { + const createComponentWithApollo = ({ props = {} } = {}) => { const handlers = [[getCiConfigData, mockCiConfigData]]; const resolvers = { Query: { @@ -134,18 +86,13 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { apolloProvider: mockApollo, }; - createComponent({ props, options }, mountFn); + createComponent({ props, options }); }; - const findLoadingIcon = () => wrapper.find(GlLoadingIcon); - const findAlert = () => wrapper.find(GlAlert); - const findTabAt = (i) => wrapper.findAll(EditorTab).at(i); - const findVisualizationTab = () => wrapper.find('[data-testid="visualization-tab"]'); - const findTextEditor = () => wrapper.find(TextEditor); - const findEditorLite = () => wrapper.find(MockEditorLite); - const findCommitForm = () => wrapper.find(CommitForm); - const findPipelineGraph = () => wrapper.find(PipelineGraph); - const findCommitBtnLoadingIcon = () => wrapper.find('[type="submit"]').find(GlLoadingIcon); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findAlert = () => wrapper.findComponent(GlAlert); + const findEditorHome = () => wrapper.findComponent(PipelineEditorHome); + const findTextEditor = () => wrapper.findComponent(TextEditor); beforeEach(() => { mockBlobContentData = jest.fn(); @@ -155,9 +102,6 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { afterEach(() => { mockBlobContentData.mockReset(); mockCiConfigData.mockReset(); - refreshCurrentPage.mockReset(); - redirectTo.mockReset(); - mockMutate.mockReset(); wrapper.destroy(); wrapper = null; @@ -170,245 +114,6 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { expect(findTextEditor().exists()).toBe(false); }); - describe('tabs', () => { - describe('editor tab', () => { - it('displays editor only after the tab is mounted', async () => { - createComponent({ mountFn: mount }); - - expect(findTabAt(0).find(TextEditor).exists()).toBe(false); - - await nextTick(); - - expect(findTabAt(0).find(TextEditor).exists()).toBe(true); - }); - }); - - describe('visualization tab', () => { - describe('with feature flag on', () => { - beforeEach(() => { - createComponent(); - }); - - it('display the tab', () => { - expect(findVisualizationTab().exists()).toBe(true); - }); - - it('displays a loading icon if the lint query is loading', () => { - createComponent({ lintLoading: true }); - - expect(findLoadingIcon().exists()).toBe(true); - expect(findPipelineGraph().exists()).toBe(false); - }); - }); - - describe('with feature flag off', () => { - beforeEach(() => { - createComponent({ - provide: { - ...mockProvide, - glFeatures: { ciConfigVisualizationTab: false }, - }, - }); - }); - - it('does not display the tab', () => { - expect(findVisualizationTab().exists()).toBe(false); - }); - }); - }); - }); - - describe('when data is set', () => { - beforeEach(async () => { - createComponent({ mountFn: mount }); - - wrapper.setData({ - content: mockCiYml, - contentModel: mockCiYml, - }); - - await waitForPromises(); - }); - - it('displays content after the query loads', () => { - expect(findLoadingIcon().exists()).toBe(false); - - expect(findEditorLite().attributes('value')).toBe(mockCiYml); - expect(findEditorLite().attributes('file-name')).toBe(mockCiConfigPath); - }); - - it('configures text editor', () => { - expect(findTextEditor().props('commitSha')).toBe(mockCommitSha); - }); - - describe('commit form', () => { - const mockVariables = { - content: mockCiYml, - filePath: mockCiConfigPath, - lastCommitId: mockCommitSha, - message: mockCommitMessage, - projectPath: mockProjectFullPath, - startBranch: mockDefaultBranch, - }; - - const findInForm = (selector) => findCommitForm().find(selector); - - const submitCommit = async ({ - message = mockCommitMessage, - branch = mockDefaultBranch, - openMergeRequest = false, - } = {}) => { - await findInForm(GlFormTextarea).setValue(message); - await findInForm(GlFormInput).setValue(branch); - if (openMergeRequest) { - await findInForm('[data-testid="new-mr-checkbox"]').setChecked(openMergeRequest); - } - await findInForm('[type="submit"]').trigger('click'); - }; - - const cancelCommitForm = async () => { - const findCancelBtn = () => wrapper.find('[type="reset"]'); - await findCancelBtn().trigger('click'); - }; - - describe('when the user commits changes to the current branch', () => { - beforeEach(async () => { - await submitCommit(); - }); - - it('calls the mutation with the default branch', () => { - expect(mockMutate).toHaveBeenCalledWith({ - mutation: expect.any(Object), - variables: { - ...mockVariables, - branch: mockDefaultBranch, - }, - }); - }); - - it('displays an alert to indicate success', () => { - expect(findAlert().text()).toMatchInterpolatedText( - 'Your changes have been successfully committed.', - ); - }); - - it('shows no saving state', () => { - expect(findCommitBtnLoadingIcon().exists()).toBe(false); - }); - - it('a second commit submits the latest sha, keeping the form updated', async () => { - await submitCommit(); - - expect(mockMutate).toHaveBeenCalledTimes(2); - expect(mockMutate).toHaveBeenLastCalledWith({ - mutation: expect.any(Object), - variables: { - ...mockVariables, - lastCommitId: mockCommitNextSha, - branch: mockDefaultBranch, - }, - }); - }); - }); - - describe('when the user commits changes to a new branch', () => { - const newBranch = 'new-branch'; - - beforeEach(async () => { - await submitCommit({ - branch: newBranch, - }); - }); - - it('calls the mutation with the new branch', () => { - expect(mockMutate).toHaveBeenCalledWith({ - mutation: expect.any(Object), - variables: { - ...mockVariables, - branch: newBranch, - }, - }); - }); - }); - - describe('when the user commits changes to open a new merge request', () => { - const newBranch = 'new-branch'; - - beforeEach(async () => { - await submitCommit({ - branch: newBranch, - openMergeRequest: true, - }); - }); - - it('redirects to the merge request page with source and target branches', () => { - const branchesQuery = objectToQuery({ - 'merge_request[source_branch]': newBranch, - 'merge_request[target_branch]': mockDefaultBranch, - }); - - expect(redirectTo).toHaveBeenCalledWith(`${mockNewMergeRequestPath}?${branchesQuery}`); - }); - }); - - describe('when the commit is ocurring', () => { - it('shows a saving state', async () => { - await mockMutate.mockImplementationOnce(() => { - expect(findCommitBtnLoadingIcon().exists()).toBe(true); - return Promise.resolve(); - }); - - await submitCommit({ - message: mockCommitMessage, - branch: mockDefaultBranch, - openMergeRequest: false, - }); - }); - }); - - describe('when the commit fails', () => { - it('shows an error message', async () => { - mockMutate.mockRejectedValueOnce(new Error('commit failed')); - - await submitCommit(); - - await waitForPromises(); - - expect(findAlert().text()).toMatchInterpolatedText( - 'The GitLab CI configuration could not be updated. commit failed', - ); - }); - - it('shows an unkown error', async () => { - mockMutate.mockRejectedValueOnce(); - - await submitCommit(); - - await waitForPromises(); - - expect(findAlert().text()).toMatchInterpolatedText( - 'The GitLab CI configuration could not be updated.', - ); - }); - }); - - describe('when the commit form is cancelled', () => { - const otherContent = 'other content'; - - beforeEach(async () => { - findTextEditor().vm.$emit('input', otherContent); - await nextTick(); - }); - - it('content is restored after cancel is called', async () => { - await cancelCommitForm(); - - expect(findEditorLite().attributes('value')).toBe(mockCiYml); - }); - }); - }); - }); - describe('when queries are called', () => { beforeEach(() => { mockBlobContentData.mockResolvedValue(mockCiYml); @@ -422,14 +127,12 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { await waitForPromises(); }); - it('shows editor and commit form', () => { - expect(findEditorLite().exists()).toBe(true); - expect(findTextEditor().exists()).toBe(true); + it('shows pipeline editor home component', () => { + expect(findEditorHome().exists()).toBe(true); }); - it('no error is shown when data is set', async () => { + it('no error is shown when data is set', () => { expect(findAlert().exists()).toBe(false); - expect(findEditorLite().attributes('value')).toBe(mockCiYml); }); it('ci config query is called with correct variables', async () => { @@ -445,10 +148,10 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { }); describe('when no file exists', () => { - const expectedAlertMsg = + const noFileAlertMsg = 'There is no .gitlab-ci.yml file in this repository, please add one and visit the Pipeline Editor again.'; - it('shows a 404 error message and does not show editor or commit form', async () => { + it('shows a 404 error message and does not show editor home component', async () => { mockBlobContentData.mockRejectedValueOnce({ response: { status: httpStatusCodes.NOT_FOUND, @@ -458,12 +161,11 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { await waitForPromises(); - expect(findAlert().text()).toBe(expectedAlertMsg); - expect(findEditorLite().exists()).toBe(false); - expect(findTextEditor().exists()).toBe(false); + expect(findAlert().text()).toBe(noFileAlertMsg); + expect(findEditorHome().exists()).toBe(false); }); - it('shows a 400 error message and does not show editor or commit form', async () => { + it('shows a 400 error message and does not show editor home component', async () => { mockBlobContentData.mockRejectedValueOnce({ response: { status: httpStatusCodes.BAD_REQUEST, @@ -473,9 +175,8 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { await waitForPromises(); - expect(findAlert().text()).toBe(expectedAlertMsg); - expect(findEditorLite().exists()).toBe(false); - expect(findTextEditor().exists()).toBe(false); + expect(findAlert().text()).toBe(noFileAlertMsg); + expect(findEditorHome().exists()).toBe(false); }); it('shows a unkown error message', async () => { @@ -483,9 +184,60 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { createComponentWithApollo(); await waitForPromises(); - expect(findAlert().text()).toBe('The CI configuration was not loaded, please try again.'); - expect(findEditorLite().exists()).toBe(true); - expect(findTextEditor().exists()).toBe(true); + expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts[LOAD_FAILURE_UNKNOWN]); + expect(findEditorHome().exists()).toBe(true); + }); + }); + + describe('when the user commits', () => { + const updateFailureMessage = 'The GitLab CI configuration could not be updated.'; + + describe('and the commit mutation succeeds', () => { + beforeEach(() => { + createComponent(); + + findEditorHome().vm.$emit('commit', { type: COMMIT_SUCCESS }); + }); + + it('shows a confirmation message', () => { + expect(findAlert().text()).toBe(wrapper.vm.$options.successTexts[COMMIT_SUCCESS]); + }); + }); + describe('and the commit mutation fails', () => { + const commitFailedReasons = ['Commit failed']; + + beforeEach(() => { + createComponent(); + + findEditorHome().vm.$emit('showError', { + type: COMMIT_FAILURE, + reasons: commitFailedReasons, + }); + }); + + it('shows an error message', () => { + expect(findAlert().text()).toMatchInterpolatedText( + `${updateFailureMessage} ${commitFailedReasons[0]}`, + ); + }); + }); + describe('when an unknown error occurs', () => { + const unknownReasons = ['Commit failed']; + + beforeEach(() => { + createComponent(); + + findEditorHome().vm.$emit('showError', { + type: COMMIT_FAILURE, + reasons: unknownReasons, + }); + }); + + it('shows an error message', () => { + expect(findAlert().text()).toMatchInterpolatedText( + `${updateFailureMessage} ${unknownReasons[0]}`, + ); + }); }); }); }); diff --git a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js new file mode 100644 index 00000000000..6ef1b3cdd1c --- /dev/null +++ b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js @@ -0,0 +1,50 @@ +import { shallowMount } from '@vue/test-utils'; + +import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue'; +import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue'; +import CommitSection from '~/pipeline_editor/components/commit/commit_section.vue'; +import PipelineEditorHeader from '~/pipeline_editor/components/header/pipeline_editor_header.vue'; + +import { mockLintResponse, mockCiYml } from './mock_data'; + +describe('Pipeline editor home wrapper', () => { + let wrapper; + + const createComponent = ({ props = {} } = {}) => { + wrapper = shallowMount(PipelineEditorHome, { + propsData: { + ciConfigData: mockLintResponse, + ciFileContent: mockCiYml, + isCiConfigDataLoading: false, + ...props, + }, + }); + }; + + const findPipelineEditorHeader = () => wrapper.findComponent(PipelineEditorTabs); + const findPipelineEditorTabs = () => wrapper.findComponent(CommitSection); + const findCommitSection = () => wrapper.findComponent(PipelineEditorHeader); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('renders', () => { + beforeEach(() => { + createComponent(); + }); + + it('shows the pipeline editor header', () => { + expect(findPipelineEditorHeader().exists()).toBe(true); + }); + + it('shows the pipeline editor tabs', () => { + expect(findPipelineEditorTabs().exists()).toBe(true); + }); + + it('shows the commit section', () => { + expect(findCommitSection().exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js b/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js index ef8eb78d1a8..fb743c8b90c 100644 --- a/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js +++ b/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js @@ -9,7 +9,6 @@ const localVue = createLocalVue(); localVue.use(Vuex); describe('Grouped code quality reports app', () => { - const Component = localVue.extend(GroupedCodequalityReportsApp); let wrapper; let mockStore; @@ -22,7 +21,7 @@ describe('Grouped code quality reports app', () => { }; const mountComponent = (props = {}) => { - wrapper = mount(Component, { + wrapper = mount(GroupedCodequalityReportsApp, { store: mockStore, localVue, propsData: { diff --git a/spec/frontend/reports/components/grouped_test_reports_app_spec.js b/spec/frontend/reports/components/grouped_test_reports_app_spec.js index 492192988fb..88a82908e1e 100644 --- a/spec/frontend/reports/components/grouped_test_reports_app_spec.js +++ b/spec/frontend/reports/components/grouped_test_reports_app_spec.js @@ -18,12 +18,11 @@ localVue.use(Vuex); describe('Grouped test reports app', () => { const endpoint = 'endpoint.json'; const pipelinePath = '/path/to/pipeline'; - const Component = localVue.extend(GroupedTestReportsApp); let wrapper; let mockStore; const mountComponent = ({ props = { pipelinePath } } = {}) => { - wrapper = mount(Component, { + wrapper = mount(GroupedTestReportsApp, { store: mockStore, localVue, propsData: { diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb index d04d97e9e6b..4bc12aef755 100644 --- a/spec/helpers/diff_helper_spec.rb +++ b/spec/helpers/diff_helper_spec.rb @@ -358,4 +358,24 @@ RSpec.describe DiffHelper do expect(diff_file_path_text(diff_file, max: 10)).to eq("...open.rb") end end + + describe "#collapsed_diff_url" do + let(:params) do + { + controller: "projects/commit", + action: "show", + namespace_id: "foo", + project_id: "bar", + id: commit.sha + } + end + + subject { helper.collapsed_diff_url(diff_file) } + + it "returns a valid URL" do + allow(helper).to receive(:safe_params).and_return(params) + + expect(subject).to match(/foo\/bar\/-\/commit\/#{commit.sha}\/diff_for_path/) + end + end end diff --git a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb index a2903391c6f..f09e03b4d55 100644 --- a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb +++ b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do let(:chunked_io) { described_class.new(build) } before do - stub_feature_flags(ci_enable_live_trace: true) + stub_feature_flags(ci_enable_live_trace: true, gitlab_ci_trace_read_consistency: true) end describe "#initialize" do diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 75ed5939724..3d728b9335e 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do it_behaves_like 'having unique enum values' before do - stub_feature_flags(ci_enable_live_trace: true) + stub_feature_flags(ci_enable_live_trace: true, gitlab_ci_trace_read_consistency: true) stub_artifacts_object_storage end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 5edc6347716..cb25f5f9429 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -184,7 +184,7 @@ module TestEnv end def gitaly_dir - File.dirname(File.expand_path(gitaly_socket_path)) + File.dirname(gitaly_socket_path) end def start_gitaly(gitaly_dir)