From 5c8c561ac63d7b8f372316b4409500474220fcda Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 24 Apr 2020 12:10:16 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/ci/rules.gitlab-ci.yml | 11 +- Gemfile | 2 +- Gemfile.lock | 5 +- app/assets/javascripts/diffs/store/actions.js | 23 ++ .../javascripts/users_select/constants.js | 18 ++ .../index.js} | 29 +-- app/assets/javascripts/users_select/utils.js | 27 +++ .../stores/get_state_key.js | 2 +- app/models/namespace.rb | 5 - app/models/project_statistics.rb | 3 + .../groups/import_export/import_service.rb | 29 ++- .../projects/import_export/export_service.rb | 18 +- .../208715-bump-tslint-search-depth.yml | 5 + .../bvl-remove-sidekiq-request-store-env.yml | 5 + .../ph-207121-shaMismatchCanMerge.yml | 5 + config/initializers/sidekiq.rb | 4 +- doc/.vale/gitlab/spelling-exceptions.txt | 1 - doc/administration/raketasks/check.md | 10 +- doc/administration/raketasks/geo.md | 4 +- doc/administration/raketasks/github_import.md | 2 +- doc/administration/raketasks/ldap.md | 6 +- doc/administration/raketasks/maintenance.md | 39 ++-- .../raketasks/project_import_export.md | 6 +- doc/administration/raketasks/storage.md | 52 ++--- doc/gitlab-basics/command-line-commands.md | 2 +- doc/raketasks/import.md | 90 ++++---- doc/raketasks/list_repos.md | 19 +- doc/user/group/epics/index.md | 2 +- .../ci/templates/Security/SAST.gitlab-ci.yml | 1 + lib/gitlab/ci/yaml_processor.rb | 2 +- .../import_export/group/group_restorer.rb | 71 +++++++ .../import_export/group/tree_restorer.rb | 140 ++++++++++++ lib/gitlab/import_export/project/base_task.rb | 17 +- .../import_export/project/export_task.rb | 4 +- lib/gitlab/sidekiq_middleware.rb | 4 +- lib/gitlab/utils/measuring.rb | 23 +- lib/gitlab/with_request_store.rb | 14 +- spec/factories/identities.rb | 2 +- spec/fixtures/group_export.tar.gz | Bin 3546 -> 2921 bytes .../group_export_invalid_subrelations.tar.gz | Bin 3602 -> 2868 bytes spec/fixtures/legacy_group_export.tar.gz | Bin 0 -> 3546 bytes ...y_group_export_invalid_subrelations.tar.gz | Bin 0 -> 3602 bytes spec/fixtures/legacy_symlink_export.tar.gz | Bin 0 -> 435 bytes .../child_with_no_parent/tree.tar.gz | Bin 0 -> 885 bytes .../group_exports/complex/tree.tar.gz | Bin 0 -> 9208 bytes .../group_exports/no_children/tree.tar.gz | Bin 0 -> 4416 bytes .../visibility_levels/internal/tree.tar.gz | Bin 0 -> 881 bytes .../visibility_levels/private/tree.tar.gz | Bin 0 -> 876 bytes .../visibility_levels/public/tree.tar.gz | Bin 0 -> 871 bytes spec/frontend/diffs/store/actions_spec.js | 59 ++++++ spec/frontend/users_select/utils_spec.js | 33 +++ .../stores/get_state_key_spec.js | 24 +++ spec/lib/gitlab/ci/yaml_processor_spec.rb | 4 +- .../import_export/group/tree_restorer_spec.rb | 200 ++++++++++++++++++ spec/lib/gitlab/sidekiq_middleware_spec.rb | 8 +- spec/lib/gitlab/utils/measuring_spec.rb | 2 +- spec/lib/gitlab/with_request_store_spec.rb | 30 +++ .../import_export/import_service_spec.rb | 106 +++++++++- .../import_export/export_service_spec.rb | 51 +++++ spec/spec_helper.rb | 13 +- spec/support/sidekiq_middleware.rb | 13 ++ 61 files changed, 1014 insertions(+), 231 deletions(-) create mode 100644 app/assets/javascripts/users_select/constants.js rename app/assets/javascripts/{users_select.js => users_select/index.js} (96%) create mode 100644 app/assets/javascripts/users_select/utils.js create mode 100644 changelogs/unreleased/208715-bump-tslint-search-depth.yml create mode 100644 changelogs/unreleased/bvl-remove-sidekiq-request-store-env.yml create mode 100644 changelogs/unreleased/ph-207121-shaMismatchCanMerge.yml create mode 100644 lib/gitlab/import_export/group/group_restorer.rb create mode 100644 lib/gitlab/import_export/group/tree_restorer.rb create mode 100644 spec/fixtures/legacy_group_export.tar.gz create mode 100644 spec/fixtures/legacy_group_export_invalid_subrelations.tar.gz create mode 100644 spec/fixtures/legacy_symlink_export.tar.gz create mode 100644 spec/fixtures/lib/gitlab/import_export/group_exports/child_with_no_parent/tree.tar.gz create mode 100644 spec/fixtures/lib/gitlab/import_export/group_exports/complex/tree.tar.gz create mode 100644 spec/fixtures/lib/gitlab/import_export/group_exports/no_children/tree.tar.gz create mode 100644 spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/tree.tar.gz create mode 100644 spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/tree.tar.gz create mode 100644 spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/tree.tar.gz create mode 100644 spec/frontend/users_select/utils_spec.js create mode 100644 spec/lib/gitlab/import_export/group/tree_restorer_spec.rb create mode 100644 spec/lib/gitlab/with_request_store_spec.rb diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index ff7ca52244e..87ca984416f 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -78,6 +78,7 @@ - "{,ee/}rubocop/**/*" - "{,ee/}spec/**/*" - "doc/README.md" # Some RSpec test rely on this file + - "doc/administration/raketasks/maintenance.md" # Some RSpec test rely on this file .code-patterns: &code-patterns - "{package.json,yarn.lock}" @@ -121,6 +122,7 @@ - "{,ee/}rubocop/**/*" - "{,ee/}spec/**/*" - "doc/README.md" # Some RSpec test rely on this file + - "doc/administration/raketasks/maintenance.md" # Some RSpec test rely on this file .code-qa-patterns: &code-qa-patterns - "{package.json,yarn.lock}" @@ -163,6 +165,7 @@ - "{,ee/}rubocop/**/*" - "{,ee/}spec/**/*" - "doc/README.md" # Some RSpec test rely on this file + - "doc/administration/raketasks/maintenance.md" # Some RSpec test rely on this file # QA changes - ".dockerignore" - "qa/**/*" @@ -500,14 +503,6 @@ - <<: *if-dot-com-gitlab-org-schedule when: on_success -.review:rules:review-gcp-cleanup: - rules: - - <<: *if-dot-com-gitlab-org-merge-request - changes: *code-qa-patterns - when: manual - - <<: *if-dot-com-gitlab-org-schedule - when: on_success - .review:rules:danger: rules: - if: '$DANGER_GITLAB_API_TOKEN && $CI_MERGE_REQUEST_IID' diff --git a/Gemfile b/Gemfile index 13303c2f7a2..031252846e0 100644 --- a/Gemfile +++ b/Gemfile @@ -287,7 +287,7 @@ gem 'addressable', '~> 2.7' gem 'font-awesome-rails', '~> 4.7' gem 'gemojione', '~> 3.3' gem 'gon', '~> 6.2' -gem 'request_store', '~> 1.3' +gem 'request_store', '~> 1.5' gem 'base32', '~> 0.3.0' gem "gitlab-license", "~> 1.0" diff --git a/Gemfile.lock b/Gemfile.lock index 68e87fc3304..cba3e863e24 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -883,7 +883,8 @@ GEM declarative (< 0.1.0) declarative-option (< 0.2.0) uber (< 0.2.0) - request_store (1.3.1) + request_store (1.5.0) + rack (>= 1.4) responders (3.0.0) actionpack (>= 5.0) railties (>= 5.0) @@ -1350,7 +1351,7 @@ DEPENDENCIES redis (~> 4.0) redis-namespace (~> 1.6.0) redis-rails (~> 5.0.2) - request_store (~> 1.3) + request_store (~> 1.5) responders (~> 3.0) retriable (~> 3.1.2) rouge (~> 3.18.0) diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 93c242e32ac..4e44cb5e6f4 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -642,5 +642,28 @@ export const setSuggestPopoverDismissed = ({ commit, state }) => createFlash(s__('MergeRequest|Error dismissing suggestion popover. Please try again.')); }); +export function changeCurrentCommit({ dispatch, commit, state }, { commitId }) { + /* eslint-disable @gitlab/require-i18n-strings */ + if (!commitId) { + return Promise.reject(new Error('`commitId` is a required argument')); + } else if (!state.commit) { + return Promise.reject(new Error('`state` must already contain a valid `commit`')); + } + /* eslint-enable @gitlab/require-i18n-strings */ + + // this is less than ideal, see: https://gitlab.com/gitlab-org/gitlab/-/issues/215421 + const commitRE = new RegExp(state.commit.id, 'g'); + + commit(types.SET_DIFF_FILES, []); + commit(types.SET_BASE_CONFIG, { + ...state, + endpoint: state.endpoint.replace(commitRE, commitId), + endpointBatch: state.endpointBatch.replace(commitRE, commitId), + endpointMetadata: state.endpointMetadata.replace(commitRE, commitId), + }); + + return dispatch('fetchDiffFilesMeta'); +} + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/users_select/constants.js b/app/assets/javascripts/users_select/constants.js new file mode 100644 index 00000000000..64df1e1748c --- /dev/null +++ b/app/assets/javascripts/users_select/constants.js @@ -0,0 +1,18 @@ +export const AJAX_USERS_SELECT_OPTIONS_MAP = { + projectId: 'projectId', + groupId: 'groupId', + showCurrentUser: 'currentUser', + authorId: 'authorId', + skipUsers: 'skipUsers', +}; + +export const AJAX_USERS_SELECT_PARAMS_MAP = { + project_id: 'projectId', + group_id: 'groupId', + skip_ldap: 'skipLdap', + todo_filter: 'todoFilter', + todo_state_filter: 'todoStateFilter', + current_user: 'showCurrentUser', + author_id: 'authorId', + skip_users: 'skipUsers', +}; diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select/index.js similarity index 96% rename from app/assets/javascripts/users_select.js rename to app/assets/javascripts/users_select/index.js index ebbe8549656..577c21fed5d 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select/index.js @@ -4,10 +4,15 @@ import $ from 'jquery'; import { escape, template, uniqBy } from 'lodash'; -import axios from './lib/utils/axios_utils'; -import { s__, __, sprintf } from './locale'; -import ModalStore from './boards/stores/modal_store'; -import { parseBoolean } from './lib/utils/common_utils'; +import axios from '../lib/utils/axios_utils'; +import { s__, __, sprintf } from '../locale'; +import ModalStore from '../boards/stores/modal_store'; +import { parseBoolean } from '../lib/utils/common_utils'; +import { + AJAX_USERS_SELECT_OPTIONS_MAP, + AJAX_USERS_SELECT_PARAMS_MAP, +} from 'ee_else_ce/users_select/constants'; +import { getAjaxUsersSelectOptions, getAjaxUsersSelectParams } from './utils'; // TODO: remove eventHub hack after code splitting refactor window.emitSidebarEvent = window.emitSidebarEvent || $.noop; @@ -558,13 +563,8 @@ function UsersSelect(currentUser, els, options = {}) { import(/* webpackChunkName: 'select2' */ 'select2/select2') .then(() => { $('.ajax-users-select').each((i, select) => { - const options = {}; + const options = getAjaxUsersSelectOptions($(select), AJAX_USERS_SELECT_OPTIONS_MAP); options.skipLdap = $(select).hasClass('skip_ldap'); - options.projectId = $(select).data('projectId'); - options.groupId = $(select).data('groupId'); - options.showCurrentUser = $(select).data('currentUser'); - options.authorId = $(select).data('authorId'); - options.skipUsers = $(select).data('skipUsers'); const showNullUser = $(select).data('nullUser'); const showAnyUser = $(select).data('anyUser'); const showEmailUser = $(select).data('emailUser'); @@ -705,14 +705,7 @@ UsersSelect.prototype.users = function(query, options, callback) { const params = { search: query, active: true, - project_id: options.projectId || null, - group_id: options.groupId || null, - skip_ldap: options.skipLdap || null, - todo_filter: options.todoFilter || null, - todo_state_filter: options.todoStateFilter || null, - current_user: options.showCurrentUser || null, - author_id: options.authorId || null, - skip_users: options.skipUsers || null, + ...getAjaxUsersSelectParams(options, AJAX_USERS_SELECT_PARAMS_MAP), }; if (options.issuableType === 'merge_request') { diff --git a/app/assets/javascripts/users_select/utils.js b/app/assets/javascripts/users_select/utils.js new file mode 100644 index 00000000000..b46fd15fb77 --- /dev/null +++ b/app/assets/javascripts/users_select/utils.js @@ -0,0 +1,27 @@ +/** + * Get options from data attributes on passed `$select`. + * @param {jQuery} $select + * @param {Object} optionsMap e.g. { optionKeyName: 'dataAttributeName' } + */ +export const getAjaxUsersSelectOptions = ($select, optionsMap) => { + return Object.keys(optionsMap).reduce((accumulator, optionKey) => { + const dataKey = optionsMap[optionKey]; + accumulator[optionKey] = $select.data(dataKey); + + return accumulator; + }, {}); +}; + +/** + * Get query parameters used for users request from passed `options` parameter + * @param {Object} options e.g. { currentUserId: 1, fooBar: 'baz' } + * @param {Object} paramsMap e.g. { user_id: 'currentUserId', foo_bar: 'fooBar' } + */ +export const getAjaxUsersSelectParams = (options, paramsMap) => { + return Object.keys(paramsMap).reduce((accumulator, paramKey) => { + const optionKey = paramsMap[paramKey]; + accumulator[paramKey] = options[optionKey] || null; + + return accumulator; + }, {}); +}; diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js index a298331c1fc..a2ee0bc3ca1 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js @@ -21,7 +21,7 @@ export default function deviseState(data) { return stateKey.unresolvedDiscussions; } else if (this.isPipelineBlocked) { return stateKey.pipelineBlocked; - } else if (this.isSHAMismatch) { + } else if (this.canMerge && this.isSHAMismatch) { return stateKey.shaMismatch; } else if (this.autoMergeEnabled) { return this.mergeError ? stateKey.autoMergeFailed : stateKey.autoMergeEnabled; diff --git a/app/models/namespace.rb b/app/models/namespace.rb index d8a305b4cf3..dfffe49fbbf 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -135,11 +135,6 @@ class Namespace < ApplicationRecord name = host.delete_suffix(gitlab_host) Namespace.where(parent_id: nil).by_path(name) end - - # overridden in ee - def reset_ci_minutes!(namespace_id) - false - end end def default_branch_protection diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb index b71ed75dde6..6f04a36392d 100644 --- a/app/models/project_statistics.rb +++ b/app/models/project_statistics.rb @@ -21,6 +21,9 @@ class ProjectStatistics < ApplicationRecord scope :for_project_ids, ->(project_ids) { where(project_id: project_ids) } + scope :for_namespaces, -> (namespaces) { where(namespace: namespaces) } + scope :with_any_ci_minutes_used, -> { where.not(shared_runners_seconds: 0) } + def total_repository_size repository_size + lfs_objects_size end diff --git a/app/services/groups/import_export/import_service.rb b/app/services/groups/import_export/import_service.rb index f62b9d3c8a6..25ffc4d9fae 100644 --- a/app/services/groups/import_export/import_service.rb +++ b/app/services/groups/import_export/import_service.rb @@ -27,18 +27,29 @@ module Groups private def import_file - @import_file ||= Gitlab::ImportExport::FileImporter.import(importable: @group, - archive_file: nil, - shared: @shared) + @import_file ||= Gitlab::ImportExport::FileImporter.import( + importable: @group, + archive_file: nil, + shared: @shared + ) end def restorer - @restorer ||= Gitlab::ImportExport::Group::LegacyTreeRestorer.new( - user: @current_user, - shared: @shared, - group: @group, - group_hash: nil - ) + @restorer ||= + if ::Feature.enabled?(:group_import_export_ndjson, @group&.parent) + Gitlab::ImportExport::Group::TreeRestorer.new( + user: @current_user, + shared: @shared, + group: @group + ) + else + Gitlab::ImportExport::Group::LegacyTreeRestorer.new( + user: @current_user, + shared: @shared, + group: @group, + group_hash: nil + ) + end end def remove_import_file diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb index 8893bf18e1f..3cb03aa16ad 100644 --- a/app/services/projects/import_export/export_service.rb +++ b/app/services/projects/import_export/export_service.rb @@ -10,8 +10,13 @@ module Projects @shared = project.import_export_shared - save_all! - execute_after_export_action(after_export_strategy) + measurement_enabled = !!options[:measurement_enabled] + measurement_logger = options[:measurement_logger] + + ::Gitlab::Utils::Measuring.execute_with(measurement_enabled, measurement_logger, base_log_data) do + save_all! + execute_after_export_action(after_export_strategy) + end ensure cleanup end @@ -20,6 +25,15 @@ module Projects attr_accessor :shared + def base_log_data + { + class: self.class.name, + current_user: current_user.name, + project_full_path: project.full_path, + file_path: shared.export_path + } + end + def execute_after_export_action(after_export_strategy) return unless after_export_strategy diff --git a/changelogs/unreleased/208715-bump-tslint-search-depth.yml b/changelogs/unreleased/208715-bump-tslint-search-depth.yml new file mode 100644 index 00000000000..2fdb511ed5d --- /dev/null +++ b/changelogs/unreleased/208715-bump-tslint-search-depth.yml @@ -0,0 +1,5 @@ +--- +title: Bump max search depth from 2 to 4 when looking for files SAST analyzers can handle +merge_request: 29732 +author: +type: fixed diff --git a/changelogs/unreleased/bvl-remove-sidekiq-request-store-env.yml b/changelogs/unreleased/bvl-remove-sidekiq-request-store-env.yml new file mode 100644 index 00000000000..a0f6cffe63d --- /dev/null +++ b/changelogs/unreleased/bvl-remove-sidekiq-request-store-env.yml @@ -0,0 +1,5 @@ +--- +title: Remove the SIDEKIQ_REQUEST_STORE configuration +merge_request: 29955 +author: +type: other diff --git a/changelogs/unreleased/ph-207121-shaMismatchCanMerge.yml b/changelogs/unreleased/ph-207121-shaMismatchCanMerge.yml new file mode 100644 index 00000000000..2d96f14642d --- /dev/null +++ b/changelogs/unreleased/ph-207121-shaMismatchCanMerge.yml @@ -0,0 +1,5 @@ +--- +title: Fixed enabled merge button incorrectly showing to users who can't merge +merge_request: +author: +type: fixed diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index fa4fc2d2c7b..cc45ee3c706 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -33,7 +33,6 @@ enable_json_logs = Gitlab.config.sidekiq.log_format == 'json' enable_sidekiq_memory_killer = ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'].to_i.nonzero? use_sidekiq_daemon_memory_killer = ENV["SIDEKIQ_DAEMON_MEMORY_KILLER"].to_i.nonzero? use_sidekiq_legacy_memory_killer = !use_sidekiq_daemon_memory_killer -use_request_store = ENV.fetch('SIDEKIQ_REQUEST_STORE', 1).to_i.nonzero? Sidekiq.configure_server do |config| if enable_json_logs @@ -50,8 +49,7 @@ Sidekiq.configure_server do |config| config.server_middleware(&Gitlab::SidekiqMiddleware.server_configurator({ metrics: Settings.monitoring.sidekiq_exporter, arguments_logger: ENV['SIDEKIQ_LOG_ARGUMENTS'] && !enable_json_logs, - memory_killer: enable_sidekiq_memory_killer && use_sidekiq_legacy_memory_killer, - request_store: use_request_store + memory_killer: enable_sidekiq_memory_killer && use_sidekiq_legacy_memory_killer })) config.client_middleware(&Gitlab::SidekiqMiddleware.client_configurator) diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt index cd951c29e23..3f0d61ed791 100644 --- a/doc/.vale/gitlab/spelling-exceptions.txt +++ b/doc/.vale/gitlab/spelling-exceptions.txt @@ -209,7 +209,6 @@ Piwik PgBouncer plaintext PostgreSQL -precompile preconfigure preconfigured preconfigures diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md index fd869548fb4..f55c6e8e86b 100644 --- a/doc/administration/raketasks/check.md +++ b/doc/administration/raketasks/check.md @@ -1,8 +1,6 @@ -# Integrity check Rake task +# Integrity Check Rake Task -GitLab provides Rake tasks to check the integrity of various components. - -## Repository integrity +## Repository Integrity Even though Git is very resilient and tries to prevent data integrity issues, there are times when things go wrong. The following Rake tasks intend to @@ -45,7 +43,7 @@ sudo gitlab-rake gitlab:git:fsck sudo -u git -H bundle exec rake gitlab:git:fsck RAILS_ENV=production ``` -## Uploaded files integrity +## Uploaded Files Integrity Various types of files can be uploaded to a GitLab installation by users. These integrity checks can detect missing files. Additionally, for locally @@ -129,7 +127,7 @@ Checking integrity of Uploads Done! ``` -## LDAP check +## LDAP Check The LDAP check Rake task will test the bind DN and password credentials (if configured) and will list a sample of LDAP users. This task is also diff --git a/doc/administration/raketasks/geo.md b/doc/administration/raketasks/geo.md index 71e4f922348..707ed1dbee0 100644 --- a/doc/administration/raketasks/geo.md +++ b/doc/administration/raketasks/geo.md @@ -1,11 +1,9 @@ # Geo Rake Tasks **(PREMIUM ONLY)** -The following Rake tasks are for [Geo installations](../geo/replication/index.md). - ## Git housekeeping There are few tasks you can run to schedule a Git housekeeping to start at the -next repository sync in a **secondary** node: +next repository sync in a **Secondary node**: ### Incremental Repack diff --git a/doc/administration/raketasks/github_import.md b/doc/administration/raketasks/github_import.md index 2c2046de564..4c6521f38ec 100644 --- a/doc/administration/raketasks/github_import.md +++ b/doc/administration/raketasks/github_import.md @@ -9,7 +9,7 @@ which will become the owner of the project. You can resume an import with the same command. Bear in mind that the syntax is very specific. Remove any spaces within the argument block and -before/after the brackets. Also, some shells (for example, `zsh`) can interpret the open/close brackets +before/after the brackets. Also, Some shells (e.g., zsh) can interpret the open/close brackets (`[]`) separately. You may need to either escape the brackets or use double quotes. ## Importing multiple projects diff --git a/doc/administration/raketasks/ldap.md b/doc/administration/raketasks/ldap.md index ba08a41d92b..361ab28f6d4 100644 --- a/doc/administration/raketasks/ldap.md +++ b/doc/administration/raketasks/ldap.md @@ -1,6 +1,4 @@ -# LDAP Rake tasks - -The following are LDAP-related Rake tasks. +# LDAP Rake Tasks ## Check @@ -28,7 +26,7 @@ limit by passing a number to the check task: rake gitlab:ldap:check[50] ``` -## Run a group sync +## Run a Group Sync > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14735) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.2. diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md index 56d5d239d15..c79a1aa6ba1 100644 --- a/doc/administration/raketasks/maintenance.md +++ b/doc/administration/raketasks/maintenance.md @@ -1,11 +1,8 @@ -# Maintenance Rake tasks +# Maintenance Rake Tasks -GitLab provides Rake tasks for general maintenance. +## Gather information about GitLab and the system it runs on -## Gather GitLab and system information - -This command gathers information about your GitLab installation and the system it runs on. -These may be useful when asking for help or reporting issues. +This command gathers information about your GitLab installation and the System it runs on. These may be useful when asking for help or reporting issues. **Omnibus Installation** @@ -53,23 +50,20 @@ Git: /usr/bin/git ## Check GitLab configuration -The `gitlab:check` Rake task runs the following Rake tasks: +Runs the following Rake tasks: - `gitlab:gitlab_shell:check` - `gitlab:gitaly:check` - `gitlab:sidekiq:check` - `gitlab:app:check` -It will check that each component was set up according to the installation guide and suggest fixes -for issues found. This command must be run from your application server and will not work correctly on -component servers like [Gitaly](../gitaly/index.md#running-gitaly-on-its-own-server). +It will check that each component was set up according to the installation guide and suggest fixes for issues found. +This command must be run from your app server and will not work correctly on component servers like [Gitaly](../gitaly/index.md#running-gitaly-on-its-own-server). -You may also have a look at our troubleshooting guides for: +You may also have a look at our Troubleshooting Guides: -- [GitLab](../index.md#troubleshooting) -- [Omnibus GitLab](https://docs.gitlab.com/omnibus/README.html#troubleshooting) - -To run `gitlab:check`, run: +- [Troubleshooting Guide (GitLab)](../index.md#troubleshooting) +- [Troubleshooting Guide (Omnibus GitLab)](https://docs.gitlab.com/omnibus/README.html#troubleshooting) **Omnibus Installation** @@ -83,8 +77,7 @@ sudo gitlab-rake gitlab:check bundle exec rake gitlab:check RAILS_ENV=production ``` -NOTE: **Note:** -Use `SANITIZE=true` for `gitlab:check` if you want to omit project names from the output. +NOTE: Use `SANITIZE=true` for `gitlab:check` if you want to omit project names from the output. Example output: @@ -133,7 +126,7 @@ Checking GitLab ... Finished ## Rebuild authorized_keys file -In some case it is necessary to rebuild the `authorized_keys` file. To do this, run: +In some case it is necessary to rebuild the `authorized_keys` file. **Omnibus Installation** @@ -148,8 +141,6 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:shell:setup RAILS_ENV=production ``` -Example output: - ```plaintext This will rebuild an authorized_keys file. You will lose any data stored in authorized_keys file. @@ -158,8 +149,8 @@ Do you want to continue (yes/no)? yes ## Clear Redis cache -If for some reason the dashboard displays the wrong information, you might want to -clear Redis' cache. To do this, run: +If for some reason the dashboard shows wrong information you might want to +clear Redis' cache. **Omnibus Installation** @@ -179,7 +170,7 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production Sometimes during version upgrades you might end up with some wrong CSS or missing some icons. In that case, try to precompile the assets again. -This only applies to source installations and does NOT apply to +Note that this only applies to source installations and does NOT apply to Omnibus packages. **Source Installation** @@ -202,8 +193,6 @@ GitLab provides a Rake task that lets you track deployments in GitLab Performance Monitoring. This Rake task simply stores the current GitLab version in the GitLab Performance Monitoring database. -To run `gitlab:track_deployment`: - **Omnibus Installation** ```shell diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md index 2ab8b13e29e..6d874d596e1 100644 --- a/doc/administration/raketasks/project_import_export.md +++ b/doc/administration/raketasks/project_import_export.md @@ -3,13 +3,11 @@ > - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/3050) in GitLab 8.9. > - From GitLab 11.3, import/export can use object storage automatically. -GitLab provides Rake tasks relating to project import and export. For more information, see: +See also: - [Project import/export documentation](../../user/project/settings/import_export.md). - [Project import/export API](../../api/project_import_export.md). -## Import/export tasks - The GitLab import/export version can be checked by using the following command: ```shell @@ -30,6 +28,8 @@ sudo gitlab-rake gitlab:import_export:data bundle exec rake gitlab:import_export:data RAILS_ENV=production ``` +## Important notes + Note the following: - Importing is only possible if the version of the import and export GitLab instances are diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md index 5d55b6cb9fe..e5ed9cd0dc1 100644 --- a/doc/administration/raketasks/storage.md +++ b/doc/administration/raketasks/storage.md @@ -1,4 +1,4 @@ -# Repository storage Rake tasks +# Repository Storage Rake Tasks This is a collection of Rake tasks you can use to help you list and migrate existing projects and attachments associated with it from Legacy storage to @@ -6,7 +6,7 @@ the new Hashed storage type. You can read more about the storage types [here](../repository_storage_types.md). -## Migrate existing projects to hashed storage +## Migrate existing projects to Hashed storage Before migrating your existing projects, you should [enable hashed storage](../repository_storage_types.md#how-to-migrate-to-hashed-storage) for the new projects as well. @@ -34,9 +34,9 @@ export ID_FROM=20 export ID_TO=50 ``` -You can monitor the progress in the **{admin}** **Admin Area > Monitoring > Background Jobs** page. -There is a specific queue you can watch to see how long it will take to finish: -`hashed_storage:hashed_storage_project_migrate`. +You can monitor the progress in the **Admin Area > Monitoring > Background Jobs** page. +There is a specific Queue you can watch to see how long it will take to finish: +`hashed_storage:hashed_storage_project_migrate` After it reaches zero, you can confirm every project has been migrated by running the commands bellow. If you find it necessary, you can run this migration script again to schedule missing projects. @@ -44,18 +44,16 @@ If you find it necessary, you can run this migration script again to schedule mi Any error or warning will be logged in Sidekiq's log file. NOTE: **Note:** -If [Geo](../geo/replication/index.md) is enabled, each project that is successfully migrated -generates an event to replicate the changes on any **secondary** nodes. +If Geo is enabled, each project that is successfully migrated generates an event to replicate the changes on any **secondary** nodes. You only need the `gitlab:storage:migrate_to_hashed` Rake task to migrate your repositories, but we have additional commands below that helps you inspect projects and attachments in both legacy and hashed storage. -## Rollback from hashed storage to legacy storage +## Rollback from Hashed storage to Legacy storage If you need to rollback the storage migration for any reason, you can follow the steps described here. -NOTE: **Note:** -Hashed storage will be required in future version of GitLab. +NOTE: **Note:** Hashed Storage will be required in future version of GitLab. To prevent new projects from being created in the Hashed storage, you need to undo the [enable hashed storage](../repository_storage_types.md#how-to-migrate-to-hashed-storage) changes. @@ -83,7 +81,7 @@ export ID_FROM=20 export ID_TO=50 ``` -You can monitor the progress in the **{admin}** **Admin Area > Monitoring > Background Jobs** page. +You can monitor the progress in the **Admin Area > Monitoring > Background Jobs** page. On the **Queues** tab, you can watch the `hashed_storage:hashed_storage_project_rollback` queue to see how long the process will take to finish. After it reaches zero, you can confirm every project has been rolled back by running the commands bellow. @@ -91,13 +89,9 @@ If some projects weren't rolled back, you can run this rollback script again to Any error or warning will be logged in Sidekiq's log file. -## List projects +## List projects on Legacy storage -The following are Rake tasks for listing projects. - -### List projects on legacy storage - -To have a simple summary of projects using legacy storage: +To have a simple summary of projects using **Legacy** storage: **Omnibus Installation** @@ -111,7 +105,7 @@ sudo gitlab-rake gitlab:storage:legacy_projects sudo -u git -H bundle exec rake gitlab:storage:legacy_projects RAILS_ENV=production ``` -To list projects using legacy storage: +To list projects using **Legacy** storage: **Omnibus Installation** @@ -126,9 +120,9 @@ sudo -u git -H bundle exec rake gitlab:storage:list_legacy_projects RAILS_ENV=pr ``` -### List projects on hashed storage +## List projects on Hashed storage -To have a simple summary of projects using hashed storage: +To have a simple summary of projects using **Hashed** storage: **Omnibus Installation** @@ -142,7 +136,7 @@ sudo gitlab-rake gitlab:storage:hashed_projects sudo -u git -H bundle exec rake gitlab:storage:hashed_projects RAILS_ENV=production ``` -To list projects using hashed storage: +To list projects using **Hashed** storage: **Omnibus Installation** @@ -156,13 +150,9 @@ sudo gitlab-rake gitlab:storage:list_hashed_projects sudo -u git -H bundle exec rake gitlab:storage:list_hashed_projects RAILS_ENV=production ``` -## List attachments +## List attachments on Legacy storage -The following are Rake tasks for listing attachments. - -### List attachments on legacy storage - -To have a simple summary of project attachments using legacy storage: +To have a simple summary of project attachments using **Legacy** storage: **Omnibus Installation** @@ -176,7 +166,7 @@ sudo gitlab-rake gitlab:storage:legacy_attachments sudo -u git -H bundle exec rake gitlab:storage:legacy_attachments RAILS_ENV=production ``` -To list project attachments using legacy storage: +To list project attachments using **Legacy** storage: **Omnibus Installation** @@ -190,9 +180,9 @@ sudo gitlab-rake gitlab:storage:list_legacy_attachments sudo -u git -H bundle exec rake gitlab:storage:list_legacy_attachments RAILS_ENV=production ``` -### List attachments on hashed storage +## List attachments on Hashed storage -To have a simple summary of project attachments using hashed storage: +To have a simple summary of project attachments using **Hashed** storage: **Omnibus Installation** @@ -206,7 +196,7 @@ sudo gitlab-rake gitlab:storage:hashed_attachments sudo -u git -H bundle exec rake gitlab:storage:hashed_attachments RAILS_ENV=production ``` -To list project attachments using hashed storage: +To list project attachments using **Hashed** storage: **Omnibus Installation** diff --git a/doc/gitlab-basics/command-line-commands.md b/doc/gitlab-basics/command-line-commands.md index fed91046c32..b9426ec0849 100644 --- a/doc/gitlab-basics/command-line-commands.md +++ b/doc/gitlab-basics/command-line-commands.md @@ -2,7 +2,7 @@ type: howto, reference --- -# Command Line basic commands +# Edit files through the command line When [working with Git from the command line](start-using-git.md), you will need to use more than just the Git commands. There are several basic commands that you should diff --git a/doc/raketasks/import.md b/doc/raketasks/import.md index b6847dfbdb1..cda742b6077 100644 --- a/doc/raketasks/import.md +++ b/doc/raketasks/import.md @@ -1,63 +1,61 @@ -# Import bare repositories +# Import bare repositories into your GitLab instance -Rake tasks are available to import bare repositories into a GitLab instance. +## Notes -Note that: +- The owner of the project will be the first admin +- The groups will be created as needed, including subgroups +- The owner of the group will be the first admin +- Existing projects will be skipped +- Projects in hashed storage may be skipped (see [Importing bare repositories from hashed storage](#importing-bare-repositories-from-hashed-storage)) +- The existing Git repos will be moved from disk (removed from the original path) -- The owner of the project will be the first administrator. -- The groups will be created as needed, including subgroups. -- The owner of the group will be the first administrator. -- Existing projects will be skipped. -- Projects in hashed storage may be skipped. For more information, see - [Importing bare repositories from hashed storage](#importing-bare-repositories-from-hashed-storage). -- The existing Git repositories will be moved from disk (removed from the original path). +## How to use -To import bare repositories into a GitLab instance: +### Create a new folder to import your Git repositories from -1. Create a new folder to import your Git repositories from. The new folder needs to have Git user - ownership and read/write/execute access for Git user and its group: +The new folder needs to have Git user ownership and read/write/execute access for Git user and its group: - ```shell - sudo -u git mkdir -p /var/opt/gitlab/git-data/repository-import-/new_group - ``` +```shell +sudo -u git mkdir -p /var/opt/gitlab/git-data/repository-import-/new_group +``` -1. Copy your bare repositories inside this newly created folder. Note: +### Copy your bare repositories inside this newly created folder - - Any `.git` repositories found on any of the subfolders will be imported as projects. - - Groups will be created as needed, these could be nested folders. +- Any `.git` repositories found on any of the subfolders will be imported as projects +- Groups will be created as needed, these could be nested folders. Example: - For example, if we copy the repositories to `/var/opt/gitlab/git-data/repository-import-`, - and repository `A` needs to be under the groups `G1` and `G2`, it must be created under those folders: - `/var/opt/gitlab/git-data/repository-import-/G1/G2/A.git`. +If we copy the repos to `/var/opt/gitlab/git-data/repository-import-`, and repo A needs to be under the groups G1 and G2, it will +have to be created under those folders: `/var/opt/gitlab/git-data/repository-import-/G1/G2/A.git`. - ```shell - sudo cp -r /old/git/foo.git /var/opt/gitlab/git-data/repository-import-/new_group/ +```shell +sudo cp -r /old/git/foo.git /var/opt/gitlab/git-data/repository-import-/new_group/ - # Do this once when you are done copying git repositories - sudo chown -R git:git /var/opt/gitlab/git-data/repository-import- - ``` +# Do this once when you are done copying git repositories +sudo chown -R git:git /var/opt/gitlab/git-data/repository-import- +``` - `foo.git` needs to be owned by the `git` user and `git` users group. +`foo.git` needs to be owned by the `git` user and `git` users group. - If you are using an installation from source, replace `/var/opt/gitlab/` with `/home/git`. +If you are using an installation from source, replace `/var/opt/gitlab/` with `/home/git`. -1. Run the following command depending on your type of installation: +### Run the command below depending on your type of installation - - Omnibus Installation +#### Omnibus Installation - ```shell - sudo gitlab-rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-'] - ``` +```shell +sudo gitlab-rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-'] +``` - - Installation from source. Before running this command you need to change to the directory where - your GitLab installation is located: +#### Installation from source - ```shell - cd /home/git/gitlab - sudo -u git -H bundle exec rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-'] RAILS_ENV=production - ``` +Before running this command you need to change the directory to where your GitLab installation is located: -## Example output +```shell +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:import:repos['/var/opt/gitlab/git-data/repository-import-'] RAILS_ENV=production +``` + +#### Example output ```plaintext Processing /var/opt/gitlab/git-data/repository-import-1/a/b/c/blah.git @@ -75,6 +73,8 @@ Processing /var/opt/gitlab/git-data/repository-import-1/group/xyz.git ## Importing bare repositories from hashed storage +### Background + Projects in legacy storage have a directory structure that mirrors their full project path in GitLab, including their namespace structure. This information is leveraged by the bare repository importer to import projects into their proper @@ -86,17 +86,17 @@ improved performance and data integrity. See [Repository Storage Types](../administration/repository_storage_types.md) for more details. -The repositories that are importable depends on the version of GitLab. +### Which repositories are importable? -### GitLab 10.3 or earlier +#### GitLab 10.3 or earlier Importing bare repositories from hashed storage is unsupported. -### GitLab 10.4 and later +#### GitLab 10.4 and later To support importing bare repositories from hashed storage, GitLab 10.4 and later stores the full project path with each repository, in a special section of -the Git repository's configuration file. This section is formatted as follows: +the Git repository's config file. This section is formatted as follows: ```ini [gitlab] diff --git a/doc/raketasks/list_repos.md b/doc/raketasks/list_repos.md index 6437ff583ce..10e6cb04bfa 100644 --- a/doc/raketasks/list_repos.md +++ b/doc/raketasks/list_repos.md @@ -1,8 +1,7 @@ # Listing repository directories -You can print a list of all Git repositories on disk managed by GitLab. - -To print a list, run the following command: +You can print a list of all Git repositories on disk managed by +GitLab with the following command: ```shell # Omnibus @@ -13,13 +12,10 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:list_repos RAILS_ENV=production ``` -NOTE: **Note:** -The results use the default ordering of the GitLab Rails application. - -## Limit search results - -To list only projects with recent activity, pass a date with the `SINCE` environment variable. The -time you specify is parsed by the Rails [TimeZone#parse function](https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-i-parse). +If you only want to list projects with recent activity you can pass +a date with the 'SINCE' environment variable. The time you specify +is parsed by the Rails [TimeZone#parse +function](https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-i-parse). ```shell # Omnibus @@ -29,3 +25,6 @@ sudo gitlab-rake gitlab:list_repos SINCE='Sep 1 2015' cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:list_repos RAILS_ENV=production SINCE='Sep 1 2015' ``` + +Note that the projects listed are NOT sorted by activity; they use +the default ordering of the GitLab Rails application. diff --git a/doc/user/group/epics/index.md b/doc/user/group/epics/index.md index d0962dbc65f..773bd463d97 100644 --- a/doc/user/group/epics/index.md +++ b/doc/user/group/epics/index.md @@ -236,7 +236,7 @@ To apply labels across multiple epics: NOTE: **Note:** To delete an epic, you need to be an [Owner](../../permissions.md#group-members-permissions) of a group/subgroup. -When inside a single epic view, click the **Delete** button to delete the epic. +When editing the description of an epic, click the **Delete** button to delete the epic. A modal will pop-up to confirm your action. Deleting an epic releases all existing issues from their associated epic in the diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml index dfbed5094af..7207922def8 100644 --- a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml @@ -24,6 +24,7 @@ sast: - $GITLAB_FEATURES =~ /\bsast\b/ image: docker:stable variables: + SEARCH_MAX_DEPTH: 4 DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "" services: diff --git a/lib/gitlab/ci/yaml_processor.rb b/lib/gitlab/ci/yaml_processor.rb index 933504ea82f..5816ac3bc54 100644 --- a/lib/gitlab/ci/yaml_processor.rb +++ b/lib/gitlab/ci/yaml_processor.rb @@ -157,7 +157,7 @@ module Gitlab return unless job[:stage] unless job[:stage].is_a?(String) && job[:stage].in?(@stages) - raise ValidationError, "#{name} job: stage parameter should be #{@stages.join(", ")}" + raise ValidationError, "#{name} job: chosen stage does not exist; available stages are #{@stages.join(", ")}" end end diff --git a/lib/gitlab/import_export/group/group_restorer.rb b/lib/gitlab/import_export/group/group_restorer.rb new file mode 100644 index 00000000000..b338950fb71 --- /dev/null +++ b/lib/gitlab/import_export/group/group_restorer.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module Gitlab + module ImportExport + module Group + class GroupRestorer + def initialize( + user:, + shared:, + group:, + attributes:, + importable_path:, + relation_reader:, + reader: + ) + @user = user + @shared = shared + @group = group + @group_attributes = attributes + @importable_path = importable_path + @relation_reader = relation_reader + @reader = reader + end + + def restore + # consume_relation returns a list of [relation, index] + @group_members = @relation_reader + .consume_relation(@importable_path, 'members') + .map(&:first) + + return unless members_mapper.map + + restorer.restore + end + + private + + def restorer + @relation_tree_restorer ||= RelationTreeRestorer.new( + user: @user, + shared: @shared, + relation_reader: @relation_reader, + members_mapper: members_mapper, + object_builder: object_builder, + relation_factory: relation_factory, + reader: @reader, + importable: @group, + importable_attributes: @group_attributes, + importable_path: @importable_path + ) + end + + def members_mapper + @members_mapper ||= Gitlab::ImportExport::MembersMapper.new( + exported_members: @group_members, + user: @user, + importable: @group + ) + end + + def relation_factory + Gitlab::ImportExport::Group::RelationFactory + end + + def object_builder + Gitlab::ImportExport::Group::ObjectBuilder + end + end + end + end +end diff --git a/lib/gitlab/import_export/group/tree_restorer.rb b/lib/gitlab/import_export/group/tree_restorer.rb new file mode 100644 index 00000000000..d0c0999f291 --- /dev/null +++ b/lib/gitlab/import_export/group/tree_restorer.rb @@ -0,0 +1,140 @@ +# frozen_string_literal: true + +module Gitlab + module ImportExport + module Group + class TreeRestorer + include Gitlab::Utils::StrongMemoize + + attr_reader :user, :shared + + def initialize(user:, shared:, group:) + @user = user + @shared = shared + @top_level_group = group + @groups_mapping = {} + end + + def restore + group_ids = relation_reader.consume_relation('groups', '_all').map { |value, _idx| Integer(value) } + root_group_id = group_ids.delete_at(0) + + process_root(root_group_id) + + group_ids.each do |group_id| + process_child(group_id) + end + + true + rescue => e + shared.error(e) + false + end + + class GroupAttributes + attr_reader :attributes, :group_id, :id, :path + + def initialize(group_id, relation_reader) + @group_id = group_id + + @path = "groups/#{group_id}" + @attributes = relation_reader.consume_attributes(@path) + @id = @attributes.delete('id') + + unless @id == @group_id + raise ArgumentError, "Invalid group_id for #{group_id}" + end + end + + def delete_attribute(name) + attributes.delete(name) + end + + def delete_attributes(*names) + names.map(&method(:delete_attribute)) + end + end + private_constant :GroupAttributes + + private + + def process_root(group_id) + group_attributes = GroupAttributes.new(group_id, relation_reader) + + # name and path are not imported on the root group to avoid conflict + # with existing groups name and/or path. + group_attributes.delete_attributes('name', 'path') + + restore_group(@top_level_group, group_attributes) + end + + def process_child(group_id) + group_attributes = GroupAttributes.new(group_id, relation_reader) + + group = create_group(group_attributes) + + restore_group(group, group_attributes) + end + + def create_group(group_attributes) + parent_id = group_attributes.delete_attribute('parent_id') + name = group_attributes.delete_attribute('name') + path = group_attributes.delete_attribute('path') + + parent_group = @groups_mapping.fetch(parent_id) { raise(ArgumentError, 'Parent group not found') } + + ::Groups::CreateService.new( + user, + name: name, + path: path, + parent_id: parent_group.id, + visibility_level: sub_group_visibility_level(group_attributes.attributes, parent_group) + ).execute + end + + def restore_group(group, group_attributes) + @groups_mapping[group_attributes.id] = group + + Group::GroupRestorer.new( + user: user, + shared: shared, + group: group, + attributes: group_attributes.attributes, + importable_path: group_attributes.path, + relation_reader: relation_reader, + reader: reader + ).restore + end + + def relation_reader + strong_memoize(:relation_reader) do + ImportExport::JSON::NdjsonReader.new( + File.join(shared.export_path, 'tree') + ) + end + end + + def sub_group_visibility_level(group_hash, parent_group) + original_visibility_level = group_hash['visibility_level'] || Gitlab::VisibilityLevel::PRIVATE + + if parent_group && parent_group.visibility_level < original_visibility_level + Gitlab::VisibilityLevel.closest_allowed_level(parent_group.visibility_level) + else + original_visibility_level + end + end + + def reader + strong_memoize(:reader) do + Gitlab::ImportExport::Reader.new( + shared: @shared, + config: Gitlab::ImportExport::Config.new( + config: Gitlab::ImportExport.group_config_file + ).to_h + ) + end + end + end + end + end +end diff --git a/lib/gitlab/import_export/project/base_task.rb b/lib/gitlab/import_export/project/base_task.rb index 5b0ca37f9fe..5c75eca2bed 100644 --- a/lib/gitlab/import_export/project/base_task.rb +++ b/lib/gitlab/import_export/project/base_task.rb @@ -12,16 +12,18 @@ module Gitlab @namespace = Namespace.find_by_full_path(opts.fetch(:namespace_path)) @current_user = User.find_by_username(opts.fetch(:username)) @measurement_enabled = opts.fetch(:measurement_enabled) - @measurement = Gitlab::Utils::Measuring.new(logger: logger) if @measurement_enabled @logger = logger end private - attr_reader :measurement, :project, :namespace, :current_user, :file_path, :project_path, :logger + attr_reader :project, :namespace, :current_user, :file_path, :project_path, :logger, :measurement_enabled - def measurement_enabled? - @measurement_enabled + def measurement_options + { + measurement_enabled: measurement_enabled, + measurement_logger: logger + } end def success(message) @@ -30,13 +32,6 @@ module Gitlab true end - def measurement_options - { - measurement_enabled: measurement_enabled?, - measurement_logger: logger - } - end - def error(message) logger.error(message) diff --git a/lib/gitlab/import_export/project/export_task.rb b/lib/gitlab/import_export/project/export_task.rb index ec287380c48..22efd0cb8a3 100644 --- a/lib/gitlab/import_export/project/export_task.rb +++ b/lib/gitlab/import_export/project/export_task.rb @@ -16,7 +16,7 @@ module Gitlab with_export do ::Projects::ImportExport::ExportService.new(project, current_user) - .execute(Gitlab::ImportExport::AfterExportStrategies::MoveFileStrategy.new(archive_path: file_path)) + .execute(Gitlab::ImportExport::AfterExportStrategies::MoveFileStrategy.new(archive_path: file_path), measurement_options) end success('Done!') @@ -33,7 +33,7 @@ module Gitlab def with_export with_request_store do ::Gitlab::GitalyClient.allow_n_plus_1_calls do - measurement_enabled? ? measurement.with_measuring { yield } : yield + yield end end end diff --git a/lib/gitlab/sidekiq_middleware.rb b/lib/gitlab/sidekiq_middleware.rb index 1b155570f18..083053c3d44 100644 --- a/lib/gitlab/sidekiq_middleware.rb +++ b/lib/gitlab/sidekiq_middleware.rb @@ -7,13 +7,13 @@ module Gitlab # The result of this method should be passed to # Sidekiq's `config.server_middleware` method # eg: `config.server_middleware(&Gitlab::SidekiqMiddleware.server_configurator)` - def self.server_configurator(metrics: true, arguments_logger: true, memory_killer: true, request_store: true) + def self.server_configurator(metrics: true, arguments_logger: true, memory_killer: true) lambda do |chain| chain.add ::Gitlab::SidekiqMiddleware::Monitor chain.add ::Gitlab::SidekiqMiddleware::ServerMetrics if metrics chain.add ::Gitlab::SidekiqMiddleware::ArgumentsLogger if arguments_logger chain.add ::Gitlab::SidekiqMiddleware::MemoryKiller if memory_killer - chain.add ::Gitlab::SidekiqMiddleware::RequestStoreMiddleware if request_store + chain.add ::Gitlab::SidekiqMiddleware::RequestStoreMiddleware chain.add ::Gitlab::SidekiqMiddleware::BatchLoader chain.add ::Labkit::Middleware::Sidekiq::Server chain.add ::Gitlab::SidekiqMiddleware::InstrumentationLogger diff --git a/lib/gitlab/utils/measuring.rb b/lib/gitlab/utils/measuring.rb index 381eaa96170..3ee1a544095 100644 --- a/lib/gitlab/utils/measuring.rb +++ b/lib/gitlab/utils/measuring.rb @@ -23,10 +23,11 @@ module Gitlab end def with_measuring - result = with_gc_stats do + result = nil + with_gc_stats do with_count_queries do with_measure_time do - yield + result = yield end end end @@ -56,31 +57,27 @@ module Gitlab ActiveSupport::Notifications.subscribed(counter_f, "sql.active_record", &block) end - def log_info(details) - details = base_log_data.merge(details) - details = details.to_yaml if ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT) - logger.info(details) - end - def with_gc_stats GC.start # perform a full mark-and-sweep stats_before = GC.stat - result = yield + yield stats_after = GC.stat @gc_stats = stats_after.map do |key, after_value| before_value = stats_before[key] [key, before: before_value, after: after_value, diff: after_value - before_value] end.to_h - result end def with_measure_time - result = nil @time_to_finish = Benchmark.realtime do - result = yield + yield end + end - result + def log_info(details) + details = base_log_data.merge(details) + details = details.to_yaml if ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT) + logger.info(details) end def duration_in_numbers(duration_in_seconds) diff --git a/lib/gitlab/with_request_store.rb b/lib/gitlab/with_request_store.rb index d6c05e1e256..d13cd9a72f7 100644 --- a/lib/gitlab/with_request_store.rb +++ b/lib/gitlab/with_request_store.rb @@ -2,12 +2,24 @@ module Gitlab module WithRequestStore - def with_request_store + def with_request_store(&block) + # Skip enabling the request store if it was already active. Whatever + # instantiated the request store first is responsible for clearing it + return yield if RequestStore.active? + + enabling_request_store(&block) + end + + private + + def enabling_request_store RequestStore.begin! yield ensure RequestStore.end! RequestStore.clear! end + + extend self end end diff --git a/spec/factories/identities.rb b/spec/factories/identities.rb index a2615ce30c3..fda4bfa589b 100644 --- a/spec/factories/identities.rb +++ b/spec/factories/identities.rb @@ -3,6 +3,6 @@ FactoryBot.define do factory :identity do provider { 'ldapmain' } - extern_uid { 'my-ldap-id' } + sequence(:extern_uid) { |n| "my-ldap-id-#{n}" } end end diff --git a/spec/fixtures/group_export.tar.gz b/spec/fixtures/group_export.tar.gz index 5f5fd989f752e49bbba88fc295f34b2ab823d571..c8f3869ce519c09137567a67a3e3e157783236da 100644 GIT binary patch literal 2921 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 T*pBVk?zjC1wN^9q07d`+fjzGg literal 3546 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 U75qjTY2>)%e?sAhMgV9405dfCMgRZ+ diff --git a/spec/fixtures/group_export_invalid_subrelations.tar.gz b/spec/fixtures/group_export_invalid_subrelations.tar.gz index 6844d1662602f223f2d29b894e58fc2c0813569d..e895e8ad9a216a5b63620c4c63bd21bacdb62784 100644 GIT binary patch literal 2868 zcmV-43(NE$iwFRp!IfSB1MOQ|bK5o+?sI&Z+U`E=c83Fz zM-pNZpb1d1o5_FQa{yi-*^X7kv7K3ACXqxQ0_TGBoev;vYJk6f^JQBYhO^0p+0Uu_ z45P$-wvCPBX)=kEa2ifnh(D&|Z`kAu>t$P`4X;s^bFQ^e3stW`-z0}(%jGBfY3=Ag zzmjGs{;OIwmHuMW;Lpc@5{2P0{twao_@nQ?$Q(Ez{*!TZjQ>Nlo%jduw37Ed2Kr-~ zB)7(YJV|Cd@t-E+=w<0&S|MU2N7)f5j=xp?(Fa=+**Y#?2GOD;)KG zn~(n~FN#3s_g(_-z<+GX4*t*L@$`uQ57E%y-#)eu$M$$_m;bkg_+DY)j{Hxq<$n~$ zli4xTSjHh%Qq|PRAV?sJ`k&^iBEI+vX``-?VIZAqG}KmXjAA!c z<`@|RRb$9)VIM^p;%8y>EJ}VGPjU3KI0=#{`Zx7unbh5KU4M%!k8KFK5y)3;ncul) z)H%qVYh-fO4K^?>q?B9NkiH`M(mU*~r(9}-BogUwe}UXV1h?}@tIlW%HIf(}EL0B9 z*Z6}=8DzY5V9UdK&dbrqkKeX|N$x2yd@`F3>bwUXQmPrMtR@nPXPJv=<|>2$B~v~pidcut4#CI2g~9Jp$CreI)_%aE<5a_~&rRadi$DfN}@S zlrj{L$L1i%_IPcV{}+4##r@L%JNf@Cn(XrbC>$U8|3fr~8Pk)I5vCwe_**TcxFon! z1?`NE{t{+U5)wdu_Hx}mVh?Ovo6c^`VTX|_L|Ynuq>KNcxohoAem+~w^6*gW_l$k~P_d6~>#d*o);H2@8_wtPKHhWM#}7X3{{;Wr<^NEL?4IfW9sGYB&*Es8|A$F<Ej*De#b_DAg7dpi7a?FN?KbOW{a=0AnPq`A$Kb|XaQ zTfhD8hRqQ_SGRQ;uCzele%z3I!`P|abrN!wmZAV{5I8)&^9J^i@_Ji}OSt~@)>Thh z=vyAxy^$44@A|a8HW%sgiIt|9*ot(j! z>k8N}ATog}2+hDSuI;&=8niuYWq(R^bIo$922e7|OD2kIXE!O#7Xf?Q2+nve zDxovM1RX`P_cRim;CK1Ee1mbssfhjIQl0vqRfptZ|{xuv{!+R)# zUkZ-Z03VP_GT^Kb3p6EQzgjztGGwixvu%aQP}B~@%|!!Nb-bG?Nk)@Z0sE)vO|{oE z17t1sAetH&(F43`wNs@O+8Xkdi<3nhpR0|s~u0`7u zXQH%+?zR;S*bA!!<5|{d7jRapqQE)8@k_q#@ePI(eQszD9Rach$dRf*LVk-&9swld zPl4q4c94vpE=Y#+I~UWwTnCabgf>EE7`Fl0Y}WPp1qf*_lbOh`L*v_qL(K^w$l>(B z09YPEmE9!|ibC_whW3aIb^}rUeAF$61{}DnC<$*gyLwrX4ezC9Vpv72sgRq+h@_{jLGJ#eG z?6no4dBa@==zyP29(UGbG0Cm!8Drzy@Aqnk7GHs@xidzd1HNKtBI?NIi{q*#4K?_lJQtr2x`$lQn&-} z)`@xxugMh{J36`Sx~1uZ9+0-0^fR{$06wAdD_Y;jwg%f`xfkqfa6G?Z6&A zVPK!#xohIf_1iZsd@rzjN;yJ{9td#O4_G^pqJg>INP#i+1W`>yT+l%K|E^n%hrn(s zk3$Bm8{bi^A$=VCCelZU;BW)D&2NW^)PI}P2&fieum=JMI{H9$#bSTS9htZEFCQSa z#XW*0`{x!Gy1qjp$BqX;CW*X-@jlIa^l(UMIcvb)^tGy`cO^Q|F=S4w3N+r87E3h= z=Pa)227O)G(osQyBjl*}+S=;S4@(fjuGR2$mT{m18SYVX5*b6ERHI%}Y4QI)gsu}c zZ-=);^)0PFocRN$Stuod;!tsF{0plkOg_AqqkNR>Q6jJt6qB$~Tm*jR~=s_Unb7O7J=t7LiF0q%6OKqnbCA3}II%x6PFfD5x0d0wihXrk49hX9& za**|2Q20MgRc!mY`Sw literal 3602 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 Y-Ru6XL`NNU^qA5A0h8nH<{9 diff --git a/spec/fixtures/legacy_group_export.tar.gz b/spec/fixtures/legacy_group_export.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..5f5fd989f752e49bbba88fc295f34b2ab823d571 GIT binary patch literal 3546 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 U75qjTY2>)%e?sAhMgV9405dfCMgRZ+ literal 0 HcmV?d00001 diff --git a/spec/fixtures/legacy_group_export_invalid_subrelations.tar.gz b/spec/fixtures/legacy_group_export_invalid_subrelations.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..6844d1662602f223f2d29b894e58fc2c0813569d GIT binary patch literal 3602 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 Y-Ru6XL`NNU^qA5A0h8nH<{9 literal 0 HcmV?d00001 diff --git a/spec/fixtures/legacy_symlink_export.tar.gz b/spec/fixtures/legacy_symlink_export.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..f295f69c56c3ad2fd717f719bbfb78009a2a79a3 GIT binary patch literal 435 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 Oum1)883YOdDggkzTSdkI literal 0 HcmV?d00001 diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/no_children/tree.tar.gz b/spec/fixtures/lib/gitlab/import_export/group_exports/no_children/tree.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..33f500ebfa35be90ad60ad7ddf957e4ff89f84ce GIT binary patch literal 4416 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 Gf&c(Cle#wm literal 0 HcmV?d00001 diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/tree.tar.gz b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/tree.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..f6d3cbe9625f7e9740bc66658ea601a42f5ed2dc GIT binary patch literal 881 zcmV-%1CIP3iwFQW0exNo1MOPLa-%R1%{gD;;_P_X1R1_#PIFPoAZ#T95iL$Usr>si zi!o-hR@pd{=#^c>QqyYnQ`}F&T2lJs;ah&|7bJ$L59 z2NYh+(23(}V3Z~jLb)YMF|ibKMb&e5V3y~v`03`+U;h*6n*Ws)rcx(c1|Q%*2t5S+ zBQK6D|5ISdzkeMrZj>mGJqF|>3d0xq_u{X{$aDDS{69J@a}r>F z?v$jUPU2K%$yw^mor>u7@Uyrtsl>H;X*i!dsiZ{H3=<7jKG1mO`d6O+%ZrmBNL+V; zV)PsCGgbC3yNs$-vPv^if*~}J4FPVPX>xTQ1b z@{wU_N}x^Qf|W*7g*hu&??%!}D5iznV#WT@ap#?3kWzgwa86RN!o=t`48l^2dk!%;)bRgH>(Gn^mKq5$q0vOWeV5MTl<`G2!R&h2J*AJ zd$0=OA6OK9$Ieo9i(qdEu?w8hji~-f9evP};P)h~;UZ;yXDKwxyL1fc8JQVHR`k%g zn|5oj%(4x4oD~m(D+uEXXG|T$T#yG`fmhWqH*6&mh5eKfK~&3TO8K^;7s*D9-aR|{SR&Zp8~u39~`;m$xi(ped>!w*fJ=QB}W$$n(4oH zZF4bhn$Q_Ax#-M*Kw7U>dt`RC#wbd^U7Z)c@23b&+BWH0Ka9I}gGmr2Q5rPw!Ni9K zrB@SlX1iKgZHNL>?uph+&ZSyV{gNDrmE|jXHtX269|Wf1f1#wU_1U(;(fo(L9|sZe zkAgIC{EvY?|KV*syVFvfdJgbM6374KA6O5!_kSD&2>6H3$<@R^wIMIzi}U~NvBJxM z`Kc#JMZL_c?VPjRn|d`d%l>!yD5%1XxUig0yD}XkQD<-U=8go{$of<`JshN>#jWzp4hl3B6flJM!#3ji=3KMIVFbT^^$I`~R zJ&A=PImI*$kF1RvwDmW!P228A@N+_~; zn`L+M+Ri(3hh7Q5xQZ~Yal!O~%@ujVHE7i>bNybTU>aa!Fw*cQ#Zb}j>r4LvIb0Hl z@jp3B4*381KSBX=`QI2go_|4CmBkz zD4u-}<5#19CwDtb1OEci|MLd_O+<0g-$j2H{T*Jx;Q7z{ssBW-{xb#!`QQ8DKXm+$ zfkFN6{qP?<{>Q+e{(qtKU$j5}g@JqiKO(m0zvQInzmHM<{k-QtSNwIyKX?3dcpLrr zj|t~9QJg#lcq9Hd8NlHGpK0Wt|BeHQ6xVR>hZllZ;(iPq92^|J2>t+^oT>8wGyniQ CPsnos literal 0 HcmV?d00001 diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/tree.tar.gz b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/tree.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..96b07717977b6ec4d8f1904b886e0ef992bbb8fc GIT binary patch literal 871 zcmV-t1DO0DiwFRT0DWEn1MOMcQrj>P<(aR@_&KEVo%%ca)EA9n%S0ecMwdX!@bBH& zj^nr#=uF~J(3#1YNbB`#kMP-5jH2}0#c6>MO5=FeT$A>Sf~dWAnE7E6roJEfaWq5l zB|#Un_{4EFu-Xs>q1+LznOsVBL-k8`U{;o|`03WsU;h)BE&n%4+FGA%8@!wU03kmL zf&bV~eaHV481f%nM~ho6#j&3O`ADMZulxh+LC-%55%8bD3*cggjvdHL_~QIOd93g< zV1DiiQc*ASYP;kt_vT(r%xd^uJ_xGt)?8T5=U%QTF|@$MfK>oAUZLR14}SP*7KRx@ zi#Uyc!hN<{Ot9bj==ebwWYDtw|u^Mxk-+5U{xTbSY zuZW`ffteL<_VeNFnrXIVoS6;gbWOosfaac}_m*mdNuE=!F%@LVDJ;+^3-|DX)L3ss zj)8n*HADqVE;in9iYg`5E+zY6PQY!u@@C;L&THE|x3v6i)9qh_*>VtVJ57;Sj6(>t0ymJK z-Q9y-2>-yc>L+%Vy4wVMLx^4Cg05x#N9yRKQ3SsyVGS2K>nDXYtnAV;q-SJiHMyaO z#@(`8du^9(xZ|w&5L`tV*SKK%Am)lZ;u^f_hPmM=QScaGV>GknO^UIj=gV`yfgH|> zL;X)qQUm^b{U67E;_AOCaL9jrrur`#)PG@9|D~?}n*_W1FYfCrRSr{$82hpqA-`ZU- zQXjeVpJQr&Cn!$*o%lQPcX$n>-+w;N`A_7|f2P2w{`)xmhmQX#FgpMHIQ++s|0ys! z|37>G=Lf#)|0cw)|4a7$pJLE=(Ln>)4}7xI-_P3sy2D@B|GECp;cX1{9}~_OqBwd9 x@P__xD}cTJC)?kD(-5Uj|EB;b#U=Fp@QLA*xR-&0gM-5t!EdfKS#tn1004Eq&5r;8 literal 0 HcmV?d00001 diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js index ceccce6312f..2678c51b6f1 100644 --- a/spec/frontend/diffs/store/actions_spec.js +++ b/spec/frontend/diffs/store/actions_spec.js @@ -43,6 +43,7 @@ import { setFileCollapsed, setExpandedDiffLines, setSuggestPopoverDismissed, + changeCurrentCommit, } from '~/diffs/store/actions'; import eventHub from '~/notes/event_hub'; import * as types from '~/diffs/store/mutation_types'; @@ -1347,4 +1348,62 @@ describe('DiffsStoreActions', () => { ); }); }); + + describe('changeCurrentCommit', () => { + it('commits the new commit information and re-requests the diff metadata for the commit', () => { + return testAction( + changeCurrentCommit, + { commitId: 'NEW' }, + { + commit: { + id: 'OLD', + }, + endpoint: 'URL/OLD', + endpointBatch: 'URL/OLD', + endpointMetadata: 'URL/OLD', + }, + [ + { type: types.SET_DIFF_FILES, payload: [] }, + { + type: types.SET_BASE_CONFIG, + payload: { + commit: { + id: 'OLD', // Not a typo: the action fired next will overwrite all of the `commit` in state + }, + endpoint: 'URL/NEW', + endpointBatch: 'URL/NEW', + endpointMetadata: 'URL/NEW', + }, + }, + ], + [{ type: 'fetchDiffFilesMeta' }], + ); + }); + + it.each` + commitId | commit | msg + ${undefined} | ${{ id: 'OLD' }} | ${'`commitId` is a required argument'} + ${'NEW'} | ${null} | ${'`state` must already contain a valid `commit`'} + ${undefined} | ${null} | ${'`commitId` is a required argument'} + `( + 'returns a rejected promise with the error message $msg given `{ "commitId": $commitId, "state.commit": $commit }`', + ({ commitId, commit, msg }) => { + const err = new Error(msg); + const actionReturn = testAction( + changeCurrentCommit, + { commitId }, + { + endpoint: 'URL/OLD', + endpointBatch: 'URL/OLD', + endpointMetadata: 'URL/OLD', + commit, + }, + [], + [], + ); + + return expect(actionReturn).rejects.toStrictEqual(err); + }, + ); + }); }); diff --git a/spec/frontend/users_select/utils_spec.js b/spec/frontend/users_select/utils_spec.js new file mode 100644 index 00000000000..a09935d8a04 --- /dev/null +++ b/spec/frontend/users_select/utils_spec.js @@ -0,0 +1,33 @@ +import $ from 'jquery'; +import { getAjaxUsersSelectOptions, getAjaxUsersSelectParams } from '~/users_select/utils'; + +const options = { + fooBar: 'baz', + activeUserId: 1, +}; + +describe('getAjaxUsersSelectOptions', () => { + it('returns options built from select data attributes', () => { + const $select = $('