Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-03-25 00:08:11 +00:00
parent c4db541c1b
commit 23bc19cb73
30 changed files with 397 additions and 257 deletions

View File

@ -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"

View File

@ -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

View File

@ -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?,

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
---
title: Filter out Releases with missing tags
merge_request: 27716
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Add missing track_exception() call to Ci::CreateJobArtifactsService
merge_request: 27954
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Ensure freshness of settings with snippet creation
merge_request: 27897
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Show object access warning when disabling repo LFS
merge_request: 26696
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Add namespace_storage_size_limit to application settings
merge_request: 27786
author:
type: added

View File

@ -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 '.'
create_developers!
create_issues!
# Stage 1
Timecop.travel 5.days.from_now
add_milestones_and_list_labels(issues)
print '.'
seed_issue_stage!
seed_plan_stage!
seed_code_stage!
seed_test_stage!
seed_review_stage!
seed_staging_stage!
# 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 '.'
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]
}
def seed_issue_stage!
issues.each do |issue|
time = within_end_time(issue.created_at + rand(ISSUE_STAGE_MAX_DURATION_IN_HOURS).hours)
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
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)}"
issue.project.repository.add_branch(@user, branch_name, 'master')
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)
::Git::BranchPushService.new(
issue.project,
@user,
change: {
oldrev: issue.project.repository.commit("master").sha,
newrev: commit_sha,
ref: 'refs/heads/master'
}
).execute
branch_name
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
def create_merge_requests_closing_issues(issues, branches)
issues.zip(branches).map do |issue, branch|
Timecop.travel 12.hours.from_now
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)
)
opts = {
title: 'Value Stream Analytics merge_request',
description: "Fixes #{issue.to_reference}",
source_branch: branch,
target_branch: 'master'
}
@merge_requests << merge_request
MergeRequests::CreateService.new(issue.project, @user, opts).execute
MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request)
end
end
def run_builds(merge_requests)
def seed_test_stage!
merge_requests.each do |merge_request|
Timecop.travel 12.hours.from_now
pipeline = FactoryBot.create(:ci_pipeline, :success, project: project)
build = FactoryBot.create(:ci_build, pipeline: pipeline, project: project, user: developers.sample)
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)
pipeline.builds.each(&:enqueue) # make sure all pipelines in pending state
pipeline.builds.each(&:run!)
pipeline.update_legacy_status
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 merge_merge_requests(merge_requests)
def seed_review_stage!
merge_requests.each do |merge_request|
Timecop.travel 12.hours.from_now
MergeRequests::MergeService.new(merge_request.project, @user).execute(merge_request)
merge_request.metrics.update!(merged_at: within_end_time(merge_request.created_at + REVIEW_STAGE_MAX_DURATION_IN_HOURS.hours))
end
end
def deploy_to_production(merge_requests)
def seed_staging_stage!
merge_requests.each do |merge_request|
next unless merge_request.head_pipeline
Timecop.travel 12.hours.from_now
job = merge_request.head_pipeline.builds.where.not(environment: nil).last
job.success!
job.pipeline.update_legacy_status
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
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 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"
)
project.group.add_developer(user)
project.add_developer(user)
@developers << user
end
end
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
def admin
@admin ||= User.admins.first
end
def suffix
@suffix ||= Time.now.to_i
end
def start_time
@start_time ||= 25.days.ago
end
def end_time
@end_time ||= Time.now
end
def within_end_time(time)
[time, end_time].min
end
end
Gitlab::Seeder.quiet do
flag = 'SEED_CYCLE_ANALYTICS'
project_id = ENV['CYCLE_ANALYTICS_SEED_PROJECT_ID']
project = Project.find(project_id) if project_id
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.seeder_base_on_env(project)
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)
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

View File

@ -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

View File

@ -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');

View File

@ -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).

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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)
```
````

View File

@ -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)

View File

@ -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

View File

@ -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 ""

View File

@ -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

View File

@ -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', () => {

View File

@ -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

View File

@ -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

View File

@ -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