diff options
30 files changed, 397 insertions, 257 deletions
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue index faaa65b1a16..a3743ded601 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue @@ -1,4 +1,6 @@ <script> +import { GlSprintf, GlLink } from '@gitlab/ui'; + import settingsMixin from 'ee_else_ce/pages/projects/shared/permissions/mixins/settings_pannel_mixin'; import { s__ } from '~/locale'; import projectFeatureSetting from './project_feature_setting.vue'; @@ -19,6 +21,8 @@ export default { projectFeatureSetting, projectFeatureToggle, projectSettingRow, + GlSprintf, + GlLink, }, mixins: [settingsMixin], @@ -67,6 +71,16 @@ export default { required: false, default: '', }, + lfsObjectsExist: { + type: Boolean, + required: false, + default: false, + }, + lfsObjectsRemovalHelpPath: { + type: String, + required: false, + default: '', + }, registryHelpPath: { type: String, required: false, @@ -377,6 +391,23 @@ export default { :disabled-input="!repositoryEnabled" name="project[lfs_enabled]" /> + <p v-if="!lfsEnabled && lfsObjectsExist"> + <gl-sprintf + :message=" + s__( + 'ProjectSettings|LFS objects from this repository are still available to forks. %{linkStart}How do I remove them?%{linkEnd}', + ) + " + > + <template #link="{ content }"> + <span class="d-block"> + <gl-link :href="lfsObjectsRemovalHelpPath" target="_blank"> + {{ content }} + </gl-link> + </span> + </template> + </gl-sprintf> + </p> </project-setting-row> <project-setting-row v-if="packagesAvailable" diff --git a/app/finders/releases_finder.rb b/app/finders/releases_finder.rb index 72bf968c8ec..e58a90922a5 100644 --- a/app/finders/releases_finder.rb +++ b/app/finders/releases_finder.rb @@ -9,7 +9,8 @@ class ReleasesFinder def execute(preload: true) return Release.none unless Ability.allowed?(@current_user, :read_release, @project) - releases = @project.releases + # See https://gitlab.com/gitlab-org/gitlab/-/issues/211988 + releases = @project.releases.where.not(tag: nil) # rubocop:disable CodeReuse/ActiveRecord releases = releases.preloaded if preload releases.sorted end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index cf9f3b9e924..015f8783db5 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -603,6 +603,8 @@ module ProjectsHelper registryHelpPath: help_page_path('user/packages/container_registry/index'), lfsAvailable: Gitlab.config.lfs.enabled, lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'), + lfsObjectsExist: project.lfs_objects.exists?, + lfsObjectsRemovalHelpPath: help_page_path('administration/lfs/manage_large_binaries_with_git_lfs', anchor: 'removing-objects-from-lfs'), pagesAvailable: Gitlab.config.pages.enabled, pagesAccessControlEnabled: Gitlab.config.pages.access_control, pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?, diff --git a/app/models/concerns/has_repository.rb b/app/models/concerns/has_repository.rb index cc792eab2e0..d50e088944e 100644 --- a/app/models/concerns/has_repository.rb +++ b/app/models/concerns/has_repository.rb @@ -15,6 +15,15 @@ module HasRepository delegate :base_dir, :disk_path, to: :storage + class_methods do + def pick_repository_storage + # We need to ensure application settings are fresh when we pick + # a repository storage to use. + Gitlab::CurrentSettings.expire_current_application_settings + Gitlab::CurrentSettings.pick_repository_storage + end + end + def valid_repo? repository.exists? rescue diff --git a/app/models/project.rb b/app/models/project.rb index b9d8fd1e4d8..9d055cbd6c6 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -67,10 +67,7 @@ class Project < ApplicationRecord default_value_for :resolve_outdated_diff_discussions, false default_value_for :container_registry_enabled, gitlab_config_features.container_registry default_value_for(:repository_storage) do - # We need to ensure application settings are fresh when we pick - # a repository storage to use. - Gitlab::CurrentSettings.expire_current_application_settings - Gitlab::CurrentSettings.pick_repository_storage + pick_repository_storage end default_value_for(:shared_runners_enabled) { Gitlab::CurrentSettings.shared_runners_enabled } diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 90221f4e463..b6127baca90 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -288,8 +288,7 @@ class Snippet < ApplicationRecord end def repository_storage - snippet_repository&.shard_name || - Gitlab::CurrentSettings.pick_repository_storage + snippet_repository&.shard_name || self.class.pick_repository_storage end def create_repository diff --git a/app/services/ci/create_job_artifacts_service.rb b/app/services/ci/create_job_artifacts_service.rb index d207c215618..5d7d552dc5a 100644 --- a/app/services/ci/create_job_artifacts_service.rb +++ b/app/services/ci/create_job_artifacts_service.rb @@ -77,6 +77,7 @@ module Ci track_exception(error, job, params) error(error.message, :service_unavailable) rescue => error + track_exception(error, job, params) error(error.message, :bad_request) end diff --git a/changelogs/unreleased/211394-releases-page-in-not-loading-on-pagination.yml b/changelogs/unreleased/211394-releases-page-in-not-loading-on-pagination.yml new file mode 100644 index 00000000000..f4455db8620 --- /dev/null +++ b/changelogs/unreleased/211394-releases-page-in-not-loading-on-pagination.yml @@ -0,0 +1,5 @@ +--- +title: Filter out Releases with missing tags +merge_request: 27716 +author: +type: fixed diff --git a/changelogs/unreleased/add-missing-track-exception-call.yml b/changelogs/unreleased/add-missing-track-exception-call.yml new file mode 100644 index 00000000000..ad08fbda765 --- /dev/null +++ b/changelogs/unreleased/add-missing-track-exception-call.yml @@ -0,0 +1,5 @@ +--- +title: Add missing track_exception() call to Ci::CreateJobArtifactsService +merge_request: 27954 +author: +type: other diff --git a/changelogs/unreleased/fj-ensure-freshness-snippet-creation.yml b/changelogs/unreleased/fj-ensure-freshness-snippet-creation.yml new file mode 100644 index 00000000000..9d6219cf177 --- /dev/null +++ b/changelogs/unreleased/fj-ensure-freshness-snippet-creation.yml @@ -0,0 +1,5 @@ +--- +title: Ensure freshness of settings with snippet creation +merge_request: 27897 +author: +type: changed diff --git a/changelogs/unreleased/improve-lfs-toggle-message.yml b/changelogs/unreleased/improve-lfs-toggle-message.yml new file mode 100644 index 00000000000..2db2152c676 --- /dev/null +++ b/changelogs/unreleased/improve-lfs-toggle-message.yml @@ -0,0 +1,5 @@ +--- +title: Show object access warning when disabling repo LFS +merge_request: 26696 +author: +type: other diff --git a/changelogs/unreleased/nicolasdular-add-max-namepsace-storage-limit-column.yml b/changelogs/unreleased/nicolasdular-add-max-namepsace-storage-limit-column.yml new file mode 100644 index 00000000000..fe47473426e --- /dev/null +++ b/changelogs/unreleased/nicolasdular-add-max-namepsace-storage-limit-column.yml @@ -0,0 +1,5 @@ +--- +title: Add namespace_storage_size_limit to application settings +merge_request: 27786 +author: +type: added diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb index 2bf3c918006..1bdaabad704 100644 --- a/db/fixtures/development/17_cycle_analytics.rb +++ b/db/fixtures/development/17_cycle_analytics.rb @@ -1,240 +1,204 @@ +# frozen_string_literal: true + require './spec/support/sidekiq_middleware' require './spec/support/helpers/test_env' +# Usage: +# +# Simple invocation always creates a new project: +# +# FILTER=cycle_analytics SEED_CYCLE_ANALYTICS=1 bundle exec rake db:seed_fu +# +# Create more issues/MRs: +# +# CYCLE_ANALYTICS_ISSUE_COUNT=100 FILTER=cycle_analytics SEED_CYCLE_ANALYTICS=1 bundle exec rake db:seed_fu +# +# Run for an existing project +# +# CYCLE_ANALYTICS_SEED_PROJECT_ID=10 FILTER=cycle_analytics SEED_CYCLE_ANALYTICS=1 bundle exec rake db:seed_fu + class Gitlab::Seeder::CycleAnalytics - def initialize(project, perf: false) - @project = project - @user = User.admins.first - @issue_count = perf ? 1000 : ENV.fetch('CYCLE_ANALYTICS_ISSUE_COUNT', 5).to_i + attr_reader :project, :issues, :merge_requests, :developers + + FLAG = 'SEED_CYCLE_ANALYTICS' + PERF_TEST = 'CYCLE_ANALYTICS_PERF_TEST' + + ISSUE_STAGE_MAX_DURATION_IN_HOURS = 72 + PLAN_STAGE_MAX_DURATION_IN_HOURS = 48 + CODE_STAGE_MAX_DURATION_IN_HOURS = 72 + TEST_STAGE_MAX_DURATION_IN_HOURS = 5 + REVIEW_STAGE_MAX_DURATION_IN_HOURS = 72 + DEPLOYMENT_MAX_DURATION_IN_HOURS = 48 + + def self.seeder_base_on_env(project) + if ENV[FLAG] + self.new(project: project) + elsif ENV[PERF_TEST] + self.new(project: project, perf: true) + end end - def seed_metrics! - @issue_count.times do |index| - # Issue - Timecop.travel 5.days.from_now - title = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" - issue = Issue.create(project: @project, title: title, author: @user) - issue_metrics = issue.metrics - - # Milestones / Labels - Timecop.travel 5.days.from_now - - if index.even? - issue_metrics.first_associated_with_milestone_at = rand(6..12).hours.from_now - else - issue_metrics.first_added_to_board_at = rand(6..12).hours.from_now - end - - # Commit - Timecop.travel 5.days.from_now - issue_metrics.first_mentioned_in_commit_at = rand(6..12).hours.from_now - - # MR - Timecop.travel 5.days.from_now - branch_name = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" - @project.repository.add_branch(@user, branch_name, 'master') - merge_request = MergeRequest.create(target_project: @project, source_project: @project, source_branch: branch_name, target_branch: 'master', title: branch_name, author: @user) - merge_request_metrics = merge_request.metrics - - # MR closing issues - Timecop.travel 5.days.from_now - MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request) - - # Merge - Timecop.travel 5.days.from_now - merge_request_metrics.merged_at = rand(6..12).hours.from_now - - # Start build - Timecop.travel 5.days.from_now - merge_request_metrics.latest_build_started_at = rand(6..12).hours.from_now - - # Finish build - Timecop.travel 5.days.from_now - merge_request_metrics.latest_build_finished_at = rand(6..12).hours.from_now - - # Deploy to production - Timecop.travel 5.days.from_now - merge_request_metrics.first_deployed_to_production_at = rand(6..12).hours.from_now - - issue_metrics.save! - merge_request_metrics.save! - - print '.' - end + def initialize(project: nil, perf: false) + @project = project || create_new_vsm_project + @issue_count = perf ? 1000 : ENV.fetch('CYCLE_ANALYTICS_ISSUE_COUNT', 5).to_i + @issues = [] + @merge_requests = [] + @developers = [] end def seed! - Sidekiq::Worker.skipping_transaction_check do - Sidekiq::Testing.inline! do - issues = create_issues - puts '.' - - # Stage 1 - Timecop.travel 5.days.from_now - add_milestones_and_list_labels(issues) - print '.' - - # Stage 2 - Timecop.travel 5.days.from_now - branches = mention_in_commits(issues) - print '.' - - # Stage 3 - Timecop.travel 5.days.from_now - merge_requests = create_merge_requests_closing_issues(issues, branches) - print '.' - - # Stage 4 - Timecop.travel 5.days.from_now - run_builds(merge_requests) - print '.' - - # Stage 5 - Timecop.travel 5.days.from_now - merge_merge_requests(merge_requests) - print '.' - - # Stage 6 / 7 - Timecop.travel 5.days.from_now - deploy_to_production(merge_requests) - print '.' - end - end - - print '.' + create_developers! + create_issues! + + seed_issue_stage! + seed_plan_stage! + seed_code_stage! + seed_test_stage! + seed_review_stage! + seed_staging_stage! + + puts "Successfully seeded '#{project.full_path}' for Value Stream Management!" + puts "URL: #{Rails.application.routes.url_helpers.project_url(project)}" end private - def create_issues - Array.new(@issue_count) do - issue_params = { - title: "Value Stream Analytics: #{FFaker::Lorem.sentence(6)}", - description: FFaker::Lorem.sentence, - state: 'opened', - assignees: [@project.team.users.sample] - } - - Issues::CreateService.new(@project, @project.team.users.sample, issue_params).execute - end - end - - def add_milestones_and_list_labels(issues) - issues.shuffle.map.with_index do |issue, index| - Timecop.travel 12.hours.from_now + def seed_issue_stage! + issues.each do |issue| + time = within_end_time(issue.created_at + rand(ISSUE_STAGE_MAX_DURATION_IN_HOURS).hours) - if index.even? - issue.update(milestone: @project.milestones.sample) + if issue.id.even? + issue.metrics.update!(first_associated_with_milestone_at: time) else - label_name = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" - list_label = FactoryBot.create(:label, title: label_name, project: issue.project) - FactoryBot.create(:list, board: FactoryBot.create(:board, project: issue.project), label: list_label) - issue.update(labels: [list_label]) + issue.metrics.update!(first_added_to_board_at: time) end - - issue end end - def mention_in_commits(issues) - issues.map do |issue| - Timecop.travel 12.hours.from_now + def seed_plan_stage! + issues.each do |issue| + plan_stage_start = issue.metrics.first_associated_with_milestone_at || issue.metrics.first_added_to_board_at - branch_name = filename = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" + first_mentioned_in_commit_at = within_end_time(plan_stage_start + rand(PLAN_STAGE_MAX_DURATION_IN_HOURS).hours) + issue.metrics.update!(first_mentioned_in_commit_at: first_mentioned_in_commit_at) + end + end - issue.project.repository.add_branch(@user, branch_name, 'master') + def seed_code_stage! + issues.each do |issue| + merge_request = FactoryBot.create( + :merge_request, + target_project: project, + source_project: project, + source_branch: "#{issue.iid}-feature-branch", + target_branch: 'master', + author: developers.sample, + created_at: within_end_time(issue.metrics.first_mentioned_in_commit_at + rand(CODE_STAGE_MAX_DURATION_IN_HOURS).hours) + ) - commit_sha = issue.project.repository.create_file(@user, filename, "content", message: "Commit for #{issue.to_reference}", branch_name: branch_name) - issue.project.repository.commit(commit_sha) + @merge_requests << merge_request - ::Git::BranchPushService.new( - issue.project, - @user, - change: { - oldrev: issue.project.repository.commit("master").sha, - newrev: commit_sha, - ref: 'refs/heads/master' - } - ).execute + MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request) + end + end - branch_name + def seed_test_stage! + merge_requests.each do |merge_request| + pipeline = FactoryBot.create(:ci_pipeline, :success, project: project) + build = FactoryBot.create(:ci_build, pipeline: pipeline, project: project, user: developers.sample) + + merge_request.metrics.update!( + latest_build_started_at: merge_request.created_at, + latest_build_finished_at: within_end_time(merge_request.created_at + TEST_STAGE_MAX_DURATION_IN_HOURS.hours), + pipeline_id: build.commit_id + ) end end - def create_merge_requests_closing_issues(issues, branches) - issues.zip(branches).map do |issue, branch| - Timecop.travel 12.hours.from_now + def seed_review_stage! + merge_requests.each do |merge_request| + merge_request.metrics.update!(merged_at: within_end_time(merge_request.created_at + REVIEW_STAGE_MAX_DURATION_IN_HOURS.hours)) + end + end - opts = { - title: 'Value Stream Analytics merge_request', - description: "Fixes #{issue.to_reference}", - source_branch: branch, - target_branch: 'master' - } + def seed_staging_stage! + merge_requests.each do |merge_request| + merge_request.metrics.update!(first_deployed_to_production_at: within_end_time(merge_request.metrics.merged_at + DEPLOYMENT_MAX_DURATION_IN_HOURS.hours)) + end + end - MergeRequests::CreateService.new(issue.project, @user, opts).execute + def create_issues! + @issue_count.times do + Timecop.travel start_time + rand(5).days do + title = "#{FFaker::Product.brand}-#{suffix}" + @issues << Issue.create!(project: project, title: title, author: developers.sample) + end end end - def run_builds(merge_requests) - merge_requests.each do |merge_request| - Timecop.travel 12.hours.from_now + def create_developers! + 5.times do |i| + user = FactoryBot.create( + :user, + name: "VSM User#{i}", + username: "vsm-user-#{i}-#{suffix}", + email: "vsm-user-#{i}@#{suffix}.com" + ) - service = Ci::CreatePipelineService.new(merge_request.project, - @user, - ref: "refs/heads/#{merge_request.source_branch}") - pipeline = service.execute(:push, ignore_skip_ci: true, save_on_errors: false) + project.group.add_developer(user) + project.add_developer(user) - pipeline.builds.each(&:enqueue) # make sure all pipelines in pending state - pipeline.builds.each(&:run!) - pipeline.update_legacy_status + @developers << user end end - def merge_merge_requests(merge_requests) - merge_requests.each do |merge_request| - Timecop.travel 12.hours.from_now + def create_new_vsm_project + project = FactoryBot.create( + :project, + name: "Value Stream Management Project #{suffix}", + path: "vsmp-#{suffix}", + creator: admin, + namespace: FactoryBot.create( + :group, + name: "Value Stream Management Group (#{suffix})", + path: "vsmg-#{suffix}" + ) + ) + + project.create_repository + project + end - MergeRequests::MergeService.new(merge_request.project, @user).execute(merge_request) - end + def admin + @admin ||= User.admins.first end - def deploy_to_production(merge_requests) - merge_requests.each do |merge_request| - next unless merge_request.head_pipeline + def suffix + @suffix ||= Time.now.to_i + end - Timecop.travel 12.hours.from_now + def start_time + @start_time ||= 25.days.ago + end - job = merge_request.head_pipeline.builds.where.not(environment: nil).last + def end_time + @end_time ||= Time.now + end - job.success! - job.pipeline.update_legacy_status - end + def within_end_time(time) + [time, end_time].min end end Gitlab::Seeder.quiet do - flag = 'SEED_CYCLE_ANALYTICS' - - if ENV[flag] - Project.not_mass_generated.find_each do |project| - # This seed naively assumes that every project has a repository, and every - # repository has a `master` branch, which may be the case for a pristine - # GDK seed, but is almost never true for a GDK that's actually had - # development performed on it. - next unless project.repository_exists? - next unless project.repository.commit('master') - - seeder = Gitlab::Seeder::CycleAnalytics.new(project) - seeder.seed! - end - elsif ENV['CYCLE_ANALYTICS_PERF_TEST'] - seeder = Gitlab::Seeder::CycleAnalytics.new(Project.order(:id).first, perf: true) + project_id = ENV['CYCLE_ANALYTICS_SEED_PROJECT_ID'] + project = Project.find(project_id) if project_id + + seeder = Gitlab::Seeder::CycleAnalytics.seeder_base_on_env(project) + + if seeder seeder.seed! - elsif ENV['CYCLE_ANALYTICS_POPULATE_METRICS_DIRECTLY'] - seeder = Gitlab::Seeder::CycleAnalytics.new(Project.order(:id).first, perf: true) - seeder.seed_metrics! else - puts "Skipped. Use the `#{flag}` environment variable to enable." + puts "Skipped. Use the `#{Gitlab::Seeder::CycleAnalytics::FLAG}` environment variable to enable." end end diff --git a/db/migrate/20200324115359_add_namespace_storage_size_limit_to_application_settings.rb b/db/migrate/20200324115359_add_namespace_storage_size_limit_to_application_settings.rb new file mode 100644 index 00000000000..e5fe98a4d19 --- /dev/null +++ b/db/migrate/20200324115359_add_namespace_storage_size_limit_to_application_settings.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddNamespaceStorageSizeLimitToApplicationSettings < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + disable_ddl_transaction! + + def up + add_column_with_default :application_settings, :namespace_storage_size_limit, :bigint, default: 0 + end + + def down + remove_column :application_settings, :namespace_storage_size_limit + end +end diff --git a/db/structure.sql b/db/structure.sql index af6fa7555bb..9ad3711dd32 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -395,7 +395,8 @@ CREATE TABLE public.application_settings ( prevent_merge_requests_committers_approval boolean DEFAULT false NOT NULL, email_restrictions_enabled boolean DEFAULT false NOT NULL, email_restrictions text, - npm_package_requests_forwarding boolean DEFAULT true NOT NULL + npm_package_requests_forwarding boolean DEFAULT true NOT NULL, + namespace_storage_size_limit bigint DEFAULT 0 NOT NULL ); CREATE SEQUENCE public.application_settings_id_seq @@ -12750,5 +12751,6 @@ INSERT INTO "schema_migrations" (version) VALUES ('20200319123041'), ('20200319203901'), ('20200323075043'), -('20200323122201'); +('20200323122201'), +('20200324115359'); diff --git a/doc/administration/geo/replication/configuration.md b/doc/administration/geo/replication/configuration.md index 1434eeb61af..ed3af59b7f0 100644 --- a/doc/administration/geo/replication/configuration.md +++ b/doc/administration/geo/replication/configuration.md @@ -314,6 +314,19 @@ It is important to note that selective synchronization: Selective synchronization restrictions are implemented on the **secondary** nodes, not the **primary** node. +### Git operations on unreplicated respositories + +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2562) in GitLab 12.10. + +Git clone, pull, and push operations over HTTP(S) are supported for repositories that +exist on the **primary** node but not on **secondary** nodes. This situation can occur +when: + +- Selective synchronization does not include the project attached to the repository. +- The repository is actively being replicated but has not completed yet. + +SSH [support is planned](https://gitlab.com/groups/gitlab-org/-/epics/2562). + ## Upgrading Geo See the [updating the Geo nodes document](updating_the_geo_nodes.md). diff --git a/doc/administration/geo/replication/index.md b/doc/administration/geo/replication/index.md index 4f598162a63..74f9f8c05f4 100644 --- a/doc/administration/geo/replication/index.md +++ b/doc/administration/geo/replication/index.md @@ -244,6 +244,7 @@ CAUTION: **Caution:** This list of limitations only reflects the latest version of GitLab. If you are using an older version, extra limitations may be in place. - Pushing directly to a **secondary** node redirects (for HTTP) or proxies (for SSH) the request to the **primary** node instead of [handling it directly](https://gitlab.com/gitlab-org/gitlab/issues/1381), except when using Git over HTTP with credentials embedded within the URI. For example, `https://user:password@secondary.tld`. +- Cloning, pulling, or pushing repositories that exist on the **primary** node but not on the **secondary** nodes where [selective synchronization](configuration.md#selective-synchronization) does not include the project is not supported over SSH [but support is planned](https://gitlab.com/groups/gitlab-org/-/epics/2562). HTTP(S) is supported. - The **primary** node has to be online for OAuth login to happen. Existing sessions and Git are not affected. - The installation takes multiple manual steps that together can take about an hour depending on circumstances. We are working on improving this experience. See [Omnibus GitLab issue #2978](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2978) for details. - Real-time updates of issues/merge requests (for example, via long polling) doesn't work on the **secondary** node. diff --git a/doc/administration/lfs/manage_large_binaries_with_git_lfs.md b/doc/administration/lfs/manage_large_binaries_with_git_lfs.md index 61b8f28293f..27cf27fddc9 100644 --- a/doc/administration/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/administration/lfs/manage_large_binaries_with_git_lfs.md @@ -93,6 +93,13 @@ git lfs fetch origin master Read the documentation on how to [migrate an existing Git repo with Git LFS](../../topics/git/migrate_to_git_lfs/index.md). +### Removing objects from LFS + +To remove objects from LFS: + +1. Use [BFG-Cleaner](../../user/project/repository/reducing_the_repo_size_using_git.md#using-the-bfg-repo-cleaner) or [filter-branch](../../user/project/repository/reducing_the_repo_size_using_git.md#using-git-filter-branch) to remove the objects from the repository. +1. Delete the relevant LFS lines for the objects you have removed from your `.gitattributes` file and commit those changes. + ## File Locking > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/35856) in GitLab 10.5. diff --git a/doc/api/group_import_export.md b/doc/api/group_import_export.md index 039f81a18d0..50e8bd9dcf2 100644 --- a/doc/api/group_import_export.md +++ b/doc/api/group_import_export.md @@ -94,3 +94,5 @@ Note the following: to allow project imports into the desired group structure. - Imported groups are given a `private` visibility level, unless imported into a parent group. - If imported into a parent group, subgroups will inherit a similar level of visibility, unless otherwise restricted. +- To preserve the member list and their respective permissions on imported groups, + review the users in these groups. Make sure these users exist before importing the desired groups. diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md index 94cf1c223dd..31d99813061 100644 --- a/doc/development/contributing/issue_workflow.md +++ b/doc/development/contributing/issue_workflow.md @@ -14,7 +14,7 @@ see fit. ## Issue triaging -Our issue triage policies are [described in our handbook](https://about.gitlab.com/handbook/engineering/issue-triage/). +Our issue triage policies are [described in our handbook](https://about.gitlab.com/handbook/engineering/quality/issue-triage/). You are very welcome to help the GitLab team triage issues. We also organize [issue bash events](https://gitlab.com/gitlab-org/gitlab-foss/issues/17815) once every quarter. diff --git a/doc/development/database_review.md b/doc/development/database_review.md index 9c428c62434..0fdf255e266 100644 --- a/doc/development/database_review.md +++ b/doc/development/database_review.md @@ -45,13 +45,6 @@ A database **reviewer**'s role is to: reassign MR to the database **maintainer** suggested by Reviewer Roulette. -#### When there are no database maintainers available - -Currently we have a [critical shortage of database maintainers](https://gitlab.com/gitlab-org/gitlab/issues/29717). Until we are able to increase the number of database maintainers to support the volume of reviews, we have implemented this temporary solution. If the database **reviewer** cannot find an available database **maintainer** then: - -1. Assign the MR for a second review by a **database trainee maintainer** for further review. -1. Once satisfied with the review process and if the database **maintainer** is still not available, skip the database maintainer approval step and assign the merge request to a backend maintainer for final review and approval. - A database **maintainer**'s role is to: - Perform the final database review on the MR. diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index 88f8b9b57d2..16a175ec115 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -972,6 +972,7 @@ The following are examples of source Markdown for menu items with their publishe 1. Go to **{book}** **Wiki** 1. Go to **{snippet}** **Snippets** 1. Go to **{users}** **Members** +1. Select the **More actions** **{ellipsis_v}** icon > **Hide stage** ``` 1. Go to **{home}** **Project overview > Details** @@ -986,6 +987,7 @@ The following are examples of source Markdown for menu items with their publishe 1. Go to **{book}** **Wiki** 1. Go to **{snippet}** **Snippets** 1. Go to **{users}** **Members** +1. Select the **More actions** **{ellipsis_v}** icon > **Hide stage** ## Alert boxes @@ -1364,17 +1366,18 @@ NOTE: **Note:** The [Product Manager for the relevant group](https://about.gitlab.com/handbook/product/categories/#devops-stages) must review and approve the addition or removal of any mentions of using feature flags before the doc change is merged. -The following is sample text for adding feature flag documentation for a feature: +The following is sample text for adding feature flag documentation for a feature that is +off by default: ````md -### Disabling the feature +### Enabling the feature -This feature comes with the `:feature_flag` feature flag enabled by default. However, in some cases -this feature is incompatible with old configuration. To turn off the feature while configuration is -migrated, ask a GitLab administrator with Rails console access to run the following command: +This feature comes with the `:feature_flag` feature flag disabled by default. In some cases, +this feature is incompatible with an old configuration. To turn on the feature, +ask a GitLab administrator with Rails console access to run the following command: ```ruby -Feature.disable(:feature_flag) +Feature.enable(:feature_flag) ``` ```` diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md index c7abfb1d974..4b76f5bdc58 100644 --- a/doc/topics/git/index.md +++ b/doc/topics/git/index.md @@ -85,6 +85,7 @@ The following relate to Git Large File Storage: - [Getting Started with Git LFS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/) - [Migrate an existing Git repo with Git LFS](migrate_to_git_lfs/index.md) +- [Removing objects from LFS](../../administration/lfs/manage_large_binaries_with_git_lfs.md#removing-objects-from-lfs) - [GitLab Git LFS user documentation](../../administration/lfs/manage_large_binaries_with_git_lfs.md) - [GitLab Git LFS admin documentation](../../administration/lfs/lfs_administration.md) - [git-annex to Git-LFS migration guide](../../administration/lfs/migrate_from_git_annex_to_git_lfs.md) diff --git a/lib/gitlab/database/batch_count.rb b/lib/gitlab/database/batch_count.rb index 728e0d423af..8972b77abef 100644 --- a/lib/gitlab/database/batch_count.rb +++ b/lib/gitlab/database/batch_count.rb @@ -32,8 +32,8 @@ module Gitlab MAX_ALLOWED_LOOPS = 10_000 SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep # Each query should take <<500ms https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22705 - DEFAULT_DISTINCT_BATCH_SIZE = 100_000 - DEFAULT_BATCH_SIZE = 10_000 + DEFAULT_DISTINCT_BATCH_SIZE = 10_000 + DEFAULT_BATCH_SIZE = 100_000 def initialize(relation, column: nil) @relation = relation @@ -51,7 +51,7 @@ module Gitlab raise "The mode #{mode.inspect} is not supported" unless [:itself, :distinct].include?(mode) # non-distinct have better performance - batch_size ||= mode == :distinct ? DEFAULT_BATCH_SIZE : DEFAULT_DISTINCT_BATCH_SIZE + batch_size ||= mode == :distinct ? DEFAULT_DISTINCT_BATCH_SIZE : DEFAULT_BATCH_SIZE start = @relation.minimum(@column) || 0 finish = @relation.maximum(@column) || 0 diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 48874a6c489..07aee723e82 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -15552,6 +15552,9 @@ msgstr "" msgid "ProjectSettings|Issues" msgstr "" +msgid "ProjectSettings|LFS objects from this repository are still available to forks. %{linkStart}How do I remove them?%{linkEnd}" +msgstr "" + msgid "ProjectSettings|Learn more about badges." msgstr "" diff --git a/spec/finders/releases_finder_spec.rb b/spec/finders/releases_finder_spec.rb index b9c67361f45..3da5ee47b6b 100644 --- a/spec/finders/releases_finder_spec.rb +++ b/spec/finders/releases_finder_spec.rb @@ -52,6 +52,18 @@ describe ReleasesFinder do subject end end + + # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27716 + context 'when tag is nil' do + before do + v1_0_0.update_column(:tag, nil) + end + + it 'ignores rows with a nil tag' do + expect(subject.size).to eq(1) + expect(subject).to eq([v1_1_0]) + end + end end end end diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js index c304dfd2048..76948f3ff4c 100644 --- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js +++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js @@ -1,4 +1,4 @@ -import { shallowMount } from '@vue/test-utils'; +import { shallowMount, mount } from '@vue/test-utils'; import settingsPanel from '~/pages/projects/shared/permissions/components/settings_panel.vue'; import { @@ -32,6 +32,8 @@ const defaultProps = { registryHelpPath: '/help/user/packages/container_registry/index', lfsAvailable: true, lfsHelpPath: '/help/workflow/lfs/manage_large_binaries_with_git_lfs', + lfsObjectsExist: false, + lfsObjectsRemovalHelpPath: `/help/administration/lfs/manage_large_binaries_with_git_lfs#removing-objects-from-lfs`, pagesAvailable: true, pagesAccessControlEnabled: false, pagesAccessControlForced: false, @@ -43,21 +45,25 @@ const defaultProps = { describe('Settings Panel', () => { let wrapper; - const mountComponent = customProps => { - const propsData = { ...defaultProps, ...customProps }; - return shallowMount(settingsPanel, { propsData }); + const mountComponent = ( + { currentSettings = {}, ...customProps } = {}, + mountFn = shallowMount, + ) => { + const propsData = { + ...defaultProps, + ...customProps, + currentSettings: { ...defaultProps.currentSettings, ...currentSettings }, + }; + + return mountFn(settingsPanel, { propsData }); }; const overrideCurrentSettings = (currentSettingsProps, extraProps = {}) => { - return mountComponent({ - ...extraProps, - currentSettings: { - ...defaultProps.currentSettings, - ...currentSettingsProps, - }, - }); + return mountComponent({ ...extraProps, currentSettings: currentSettingsProps }); }; + const findLFSSettingsMessage = () => wrapper.find({ ref: 'git-lfs-settings' }).find('p'); + beforeEach(() => { wrapper = mountComponent(); }); @@ -333,6 +339,40 @@ describe('Settings Panel', () => { expect(wrapper.find('[name="project[lfs_enabled]"]').props().disabledInput).toEqual(true); }); + + describe.each` + lfsObjectsExist | lfsEnabled | isShown + ${true} | ${true} | ${false} + ${true} | ${false} | ${true} + ${false} | ${true} | ${false} + ${false} | ${false} | ${false} + `( + 'with (lfsObjectsExist = $lfsObjectsExist, lfsEnabled = $lfsEnabled)', + ({ lfsObjectsExist, lfsEnabled, isShown }) => { + beforeEach(() => { + wrapper = mountComponent({ lfsObjectsExist, currentSettings: { lfsEnabled } }, mount); + }); + + if (isShown) { + it('shows warning message', () => { + const message = findLFSSettingsMessage(); + const link = message.find('a'); + + expect(message.text()).toContain( + 'LFS objects from this repository are still available to forks', + ); + expect(link.text()).toEqual('How do I remove them?'); + expect(link.attributes('href')).toEqual( + '/help/administration/lfs/manage_large_binaries_with_git_lfs#removing-objects-from-lfs', + ); + }); + } else { + it('does not show warning message', () => { + expect(findLFSSettingsMessage().exists()).toEqual(false); + }); + } + }, + ); }); describe('Packages', () => { diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f0423937710..2586289a699 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1391,35 +1391,14 @@ describe Project do context 'repository storage by default' do let(:project) { build(:project) } - before do - storages = { - 'default' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories'), - 'picked' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories') - } - allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) - end - it 'picks storage from ApplicationSetting' do - expect_any_instance_of(ApplicationSetting).to receive(:pick_repository_storage).and_return('picked') + expect_next_instance_of(ApplicationSetting) do |instance| + expect(instance).to receive(:pick_repository_storage).and_return('picked') + end + expect(described_class).to receive(:pick_repository_storage).and_call_original expect(project.repository_storage).to eq('picked') end - - it 'picks from the latest available storage', :request_store do - stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') - Gitlab::CurrentSettings.current_application_settings - - settings = ApplicationSetting.last - settings.repository_storages = %w(picked) - settings.save! - - expect(Gitlab::CurrentSettings.repository_storages).to eq(%w(default)) - - project - - expect(project.repository.storage).to eq('picked') - expect(Gitlab::CurrentSettings.repository_storages).to eq(%w(picked)) - end end context 'shared runners by default' do diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 533c10363ca..0e19dfc147b 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -655,10 +655,18 @@ describe Snippet do describe '#repository_storage' do let(:snippet) { create(:snippet) } - it 'returns default repository storage' do - expect(Gitlab::CurrentSettings).to receive(:pick_repository_storage) + subject { snippet.repository_storage } - snippet.repository_storage + before do + expect_next_instance_of(ApplicationSetting) do |instance| + expect(instance).to receive(:pick_repository_storage).and_return('picked') + end + end + + it 'returns repository storage from ApplicationSetting' do + expect(described_class).to receive(:pick_repository_storage).and_call_original + + expect(subject).to eq 'picked' end context 'when snippet_project is already created' do @@ -669,9 +677,7 @@ describe Snippet do end it 'returns repository_storage from snippet_project' do - expect(Gitlab::CurrentSettings).not_to receive(:pick_repository_storage) - - expect(snippet.repository_storage).to eq 'foo' + expect(subject).to eq 'foo' end end end diff --git a/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb b/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb index d5606e65981..5a5d7c8f038 100644 --- a/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb @@ -168,4 +168,37 @@ RSpec.shared_examples 'model with repository' do it { is_expected.to respond_to(:base_dir) } it { is_expected.to respond_to(:disk_path) } end + + describe '.pick_repository_storage' do + subject { described_class.pick_repository_storage } + + before do + storages = { + 'default' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories'), + 'picked' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories') + } + allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) + end + + it 'picks storage from ApplicationSetting' do + expect_next_instance_of(ApplicationSetting) do |instance| + expect(instance).to receive(:pick_repository_storage).and_return('picked') + end + + expect(subject).to eq('picked') + end + + it 'picks from the latest available storage', :request_store do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + Gitlab::CurrentSettings.current_application_settings + + settings = ApplicationSetting.last + settings.repository_storages = %w(picked) + settings.save! + + expect(Gitlab::CurrentSettings.repository_storages).to eq(%w(default)) + expect(subject).to eq('picked') + expect(Gitlab::CurrentSettings.repository_storages).to eq(%w(picked)) + end + end end |