Commit 23bc19cb authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent c4db541c
<script> <script>
import { GlSprintf, GlLink } from '@gitlab/ui';
import settingsMixin from 'ee_else_ce/pages/projects/shared/permissions/mixins/settings_pannel_mixin'; import settingsMixin from 'ee_else_ce/pages/projects/shared/permissions/mixins/settings_pannel_mixin';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import projectFeatureSetting from './project_feature_setting.vue'; import projectFeatureSetting from './project_feature_setting.vue';
...@@ -19,6 +21,8 @@ export default { ...@@ -19,6 +21,8 @@ export default {
projectFeatureSetting, projectFeatureSetting,
projectFeatureToggle, projectFeatureToggle,
projectSettingRow, projectSettingRow,
GlSprintf,
GlLink,
}, },
mixins: [settingsMixin], mixins: [settingsMixin],
...@@ -67,6 +71,16 @@ export default { ...@@ -67,6 +71,16 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
lfsObjectsExist: {
type: Boolean,
required: false,
default: false,
},
lfsObjectsRemovalHelpPath: {
type: String,
required: false,
default: '',
},
registryHelpPath: { registryHelpPath: {
type: String, type: String,
required: false, required: false,
...@@ -377,6 +391,23 @@ export default { ...@@ -377,6 +391,23 @@ export default {
:disabled-input="!repositoryEnabled" :disabled-input="!repositoryEnabled"
name="project[lfs_enabled]" 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>
<project-setting-row <project-setting-row
v-if="packagesAvailable" v-if="packagesAvailable"
......
...@@ -9,7 +9,8 @@ class ReleasesFinder ...@@ -9,7 +9,8 @@ class ReleasesFinder
def execute(preload: true) def execute(preload: true)
return Release.none unless Ability.allowed?(@current_user, :read_release, @project) 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 = releases.preloaded if preload
releases.sorted releases.sorted
end end
......
...@@ -603,6 +603,8 @@ module ProjectsHelper ...@@ -603,6 +603,8 @@ module ProjectsHelper
registryHelpPath: help_page_path('user/packages/container_registry/index'), registryHelpPath: help_page_path('user/packages/container_registry/index'),
lfsAvailable: Gitlab.config.lfs.enabled, lfsAvailable: Gitlab.config.lfs.enabled,
lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'), 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, pagesAvailable: Gitlab.config.pages.enabled,
pagesAccessControlEnabled: Gitlab.config.pages.access_control, pagesAccessControlEnabled: Gitlab.config.pages.access_control,
pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?, pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?,
......
...@@ -15,6 +15,15 @@ module HasRepository ...@@ -15,6 +15,15 @@ module HasRepository
delegate :base_dir, :disk_path, to: :storage 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? def valid_repo?
repository.exists? repository.exists?
rescue rescue
......
...@@ -67,10 +67,7 @@ class Project < ApplicationRecord ...@@ -67,10 +67,7 @@ class Project < ApplicationRecord
default_value_for :resolve_outdated_diff_discussions, false default_value_for :resolve_outdated_diff_discussions, false
default_value_for :container_registry_enabled, gitlab_config_features.container_registry default_value_for :container_registry_enabled, gitlab_config_features.container_registry
default_value_for(:repository_storage) do default_value_for(:repository_storage) do
# We need to ensure application settings are fresh when we pick pick_repository_storage
# a repository storage to use.
Gitlab::CurrentSettings.expire_current_application_settings
Gitlab::CurrentSettings.pick_repository_storage
end end
default_value_for(:shared_runners_enabled) { Gitlab::CurrentSettings.shared_runners_enabled } default_value_for(:shared_runners_enabled) { Gitlab::CurrentSettings.shared_runners_enabled }
......
...@@ -288,8 +288,7 @@ class Snippet < ApplicationRecord ...@@ -288,8 +288,7 @@ class Snippet < ApplicationRecord
end end
def repository_storage def repository_storage
snippet_repository&.shard_name || snippet_repository&.shard_name || self.class.pick_repository_storage
Gitlab::CurrentSettings.pick_repository_storage
end end
def create_repository def create_repository
......
...@@ -77,6 +77,7 @@ module Ci ...@@ -77,6 +77,7 @@ module Ci
track_exception(error, job, params) track_exception(error, job, params)
error(error.message, :service_unavailable) error(error.message, :service_unavailable)
rescue => error rescue => error
track_exception(error, job, params)
error(error.message, :bad_request) error(error.message, :bad_request)
end end
......
---
title: Filter out Releases with missing tags
merge_request: 27716
author:
type: fixed
---
title: Add missing track_exception() call to Ci::CreateJobArtifactsService
merge_request: 27954
author:
type: other
---
title: Ensure freshness of settings with snippet creation
merge_request: 27897
author:
type: changed
---
title: Show object access warning when disabling repo LFS
merge_request: 26696
author:
type: other
---
title: Add namespace_storage_size_limit to application settings
merge_request: 27786
author:
type: added
# frozen_string_literal: true
require './spec/support/sidekiq_middleware' require './spec/support/sidekiq_middleware'
require './spec/support/helpers/test_env' 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 class Gitlab::Seeder::CycleAnalytics
def initialize(project, perf: false) attr_reader :project, :issues, :merge_requests, :developers
@project = project
@user = User.admins.first FLAG = 'SEED_CYCLE_ANALYTICS'
@issue_count = perf ? 1000 : ENV.fetch('CYCLE_ANALYTICS_ISSUE_COUNT', 5).to_i 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 end
def seed_metrics! def initialize(project: nil, perf: false)
@issue_count.times do |index| @project = project || create_new_vsm_project
# Issue @issue_count = perf ? 1000 : ENV.fetch('CYCLE_ANALYTICS_ISSUE_COUNT', 5).to_i
Timecop.travel 5.days.from_now @issues = []
title = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" @merge_requests = []
issue = Issue.create(project: @project, title: title, author: @user) @developers = []
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
end end
def seed! def seed!
Sidekiq::Worker.skipping_transaction_check do create_developers!
Sidekiq::Testing.inline! do create_issues!
issues = create_issues
puts '.' seed_issue_stage!
seed_plan_stage!
# Stage 1 seed_code_stage!
Timecop.travel 5.days.from_now seed_test_stage!
add_milestones_and_list_labels(issues) seed_review_stage!
print '.' seed_staging_stage!
# Stage 2 puts "Successfully seeded '#{project.full_path}' for Value Stream Management!"
Timecop.travel 5.days.from_now puts "URL: #{Rails.application.routes.url_helpers.project_url(project)}"
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 '.'
end end
private private
def create_issues def seed_issue_stage!
Array.new(@issue_count) do issues.each do |issue|
issue_params = { time = within_end_time(issue.created_at + rand(ISSUE_STAGE_MAX_DURATION_IN_HOURS).hours)
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
if index.even? if issue.id.even?
issue.update(milestone: @project.milestones.sample) issue.metrics.update!(first_associated_with_milestone_at: time)
else else
label_name = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" issue.metrics.update!(first_added_to_board_at: time)
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])
end end
issue
end end
end end
def mention_in_commits(issues) def seed_plan_stage!
issues.map do |issue| issues.each do |issue|
Timecop.travel 12.hours.from_now 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) @merge_requests << merge_request
issue.project.repository.commit(commit_sha)
::Git::BranchPushService.new( MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request)
issue.project, end
@user, end
change: {
oldrev: issue.project.repository.commit("master").sha,
newrev: commit_sha,
ref: 'refs/heads/master'
}
).execute
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
end end
def create_merge_requests_closing_issues(issues, branches) def seed_review_stage!
issues.zip(branches).map do |issue, branch| merge_requests.each do |merge_request|
Timecop.travel 12.hours.from_now merge_request.metrics.update!(merged_at: within_end_time(merge_request.created_at + REVIEW_STAGE_MAX_DURATION_IN_HOURS.hours))
end
end
opts = { def seed_staging_stage!
title: 'Value Stream Analytics merge_request', merge_requests.each do |merge_request|
description: "Fixes #{issue.to_reference}", merge_request.metrics.update!(first_deployed_to_production_at: within_end_time(merge_request.metrics.merged_at + DEPLOYMENT_MAX_DURATION_IN_HOURS.hours))
source_branch: branch, end
target_branch: 'master' 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
end end
def run_builds(merge_requests) def create_developers!
merge_requests.each do |merge_request| 5.times do |i|
Timecop.travel 12.hours.from_now 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, project.group.add_developer(user)
@user, project.add_developer(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 @developers << user
pipeline.builds.each(&:run!)
pipeline.update_legacy_status
end end
end end
def merge_merge_requests(merge_requests) def create_new_vsm_project
merge_requests.each do |merge_request| project = FactoryBot.create(
Timecop.travel 12.hours.from_now :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) def admin
end @admin ||= User.admins.first
end end
def deploy_to_production(merge_requests) def suffix
merge_requests.each do |merge_request| @suffix ||= Time.now.to_i
next unless merge_request.head_pipeline 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! def within_end_time(time)
job.pipeline.update_legacy_status [time, end_time].min
end
end end
end end
Gitlab::Seeder.quiet do 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| seeder = Gitlab::Seeder::CycleAnalytics.seeder_base_on_env(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 if seeder
# 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)
seeder.seed! seeder.seed!
elsif ENV['CYCLE_ANALYTICS_POPULATE_METRICS_DIRECTLY']
seeder = Gitlab::Seeder::CycleAnalytics.new(Project.order(:id).first, perf: true)
seeder.seed_metrics!
else else
puts "Skipped. Use the `#{flag}` environment variable to enable." puts "Skipped. Use the `#{Gitlab::Seeder::CycleAnalytics::FLAG}` environment variable to enable."
end end
end end
# 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
...@@ -395,7 +395,8 @@ CREATE TABLE public.application_settings ( ...@@ -395,7 +395,8 @@ CREATE TABLE public.application_settings (
prevent_merge_requests_committers_approval boolean DEFAULT false NOT NULL, prevent_merge_requests_committers_approval boolean DEFAULT false NOT NULL,
email_restrictions_enabled boolean DEFAULT false NOT NULL, email_restrictions_enabled boolean DEFAULT false NOT NULL,
email_restrictions text, 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 CREATE SEQUENCE public.application_settings_id_seq
...@@ -12750,5 +12751,6 @@ INSERT INTO "schema_migrations" (version) VALUES ...@@ -12750,5 +12751,6 @@ INSERT INTO "schema_migrations" (version) VALUES
('20200319123041'), ('20200319123041'),
('20200319203901'), ('20200319203901'),
('20200323075043'), ('20200323075043'),
('20200323122201'); ('20200323122201'),
('20200324115359');
...@@ -314,6 +314,19 @@ It is important to note that selective synchronization: ...@@ -314,6 +314,19 @@ It is important to note that selective synchronization:
Selective synchronization restrictions are implemented on the **secondary** nodes, Selective synchronization restrictions are implemented on the **secondary** nodes,
not the **primary** node. 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 ## Upgrading Geo
See the [updating the Geo nodes document](updating_the_geo_nodes.md). See the [updating the Geo nodes document](updating_the_geo_nodes.md).
......
...@@ -244,6 +244,7 @@ CAUTION: **Caution:** ...@@ -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. 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`. - 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 **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. - 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. - Real-time updates of issues/merge requests (for example, via long polling) doesn't work on the **secondary** node.
......
...@@ -93,6 +93,13 @@ git lfs fetch origin master ...@@ -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). 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 ## File Locking
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/35856) in GitLab 10.5. > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/35856) in GitLab 10.5.
......
...@@ -94,3 +94,5 @@ Note the following: ...@@ -94,3 +94,5 @@ Note the following:
to allow project imports into the desired group structure. to allow project imports into the desired group structure.
- Imported groups are given a `private` visibility level, unless imported into a parent group. - 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. - 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.
...@@ -14,7 +14,7 @@ see fit. ...@@ -14,7 +14,7 @@ see fit.
## Issue triaging ## 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. 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) We also organize [issue bash events](https://gitlab.com/gitlab-org/gitlab-foss/issues/17815)
once every quarter. once every quarter.
......
...@@ -45,13 +45,6 @@ A database **reviewer**'s role is to: ...@@ -45,13 +45,6 @@ A database **reviewer**'s role is to:
reassign MR to the database **maintainer** suggested by Reviewer reassign MR to the database **maintainer** suggested by Reviewer
Roulette. 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: A database **maintainer**'s role is to:
- Perform the final database review on the MR. - Perform the final database review on the MR.
......
...@@ -972,6 +972,7 @@ The following are examples of source Markdown for menu items with their publishe ...@@ -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 **{book}** **Wiki**
1. Go to **{snippet}** **Snippets** 1. Go to **{snippet}** **Snippets**
1. Go to **{users}** **Members** 1. Go to **{users}** **Members**
1. Select the **More actions** **{ellipsis_v}** icon > **Hide stage**
``` ```
1. Go to **{home}** **Project overview > Details** 1. Go to **{home}** **Project overview > Details**
...@@ -986,6 +987,7 @@ The following are examples of source Markdown for menu items with their publishe ...@@ -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 **{book}** **Wiki**
1. Go to **{snippet}** **Snippets** 1. Go to **{snippet}** **Snippets**
1. Go to **{users}** **Members** 1. Go to **{users}** **Members**
1. Select the **More actions** **{ellipsis_v}** icon > **Hide stage**
## Alert boxes ## Alert boxes
...@@ -1364,17 +1366,18 @@ NOTE: **Note:** ...@@ -1364,17 +1366,18 @@ NOTE: **Note:**
The [Product Manager for the relevant group](https://about.gitlab.com/handbook/product/categories/#devops-stages) 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. 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 ````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 comes with the `:feature_flag` feature flag disabled by default. In some cases,
this feature is incompatible with old configuration. To turn off the feature while configuration is this feature is incompatible with an old configuration. To turn on the feature,
migrated, ask a GitLab administrator with Rails console access to run the following command: ask a GitLab administrator with Rails console access to run the following command:
```ruby ```ruby
Feature.disable(:feature_flag) Feature.enable(:feature_flag)
``` ```
```` ````
......
...@@ -85,6 +85,7 @@ The following relate to Git Large File Storage: ...@@ -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/) - [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) - [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 user documentation](../../administration/lfs/manage_large_binaries_with_git_lfs.md)
- [GitLab Git LFS admin documentation](../../administration/lfs/lfs_administration.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) - [git-annex to Git-LFS migration guide](../../administration/lfs/migrate_from_git_annex_to_git_lfs.md)
......
...@@ -32,8 +32,8 @@ module Gitlab ...@@ -32,8 +32,8 @@ module Gitlab
MAX_ALLOWED_LOOPS = 10_000 MAX_ALLOWED_LOOPS = 10_000
SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep
# Each query should take <<500ms https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22705 # Each query should take <<500ms https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22705
DEFAULT_DISTINCT_BATCH_SIZE = 100_000 DEFAULT_DISTINCT_BATCH_SIZE = 10_000
DEFAULT_BATCH_SIZE = 10_000 DEFAULT_BATCH_SIZE = 100_000
def initialize(relation, column: nil) def initialize(relation, column: nil)
@relation = relation @relation = relation
...@@ -51,7 +51,7 @@ module Gitlab ...@@ -51,7 +51,7 @@ module Gitlab
raise "The mode #{mode.inspect} is not supported" unless [:itself, :distinct].include?(mode) raise "The mode #{mode.inspect} is not supported" unless [:itself, :distinct].include?(mode)
# non-distinct have better performance # 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 start = @relation.minimum(@column) || 0
finish = @relation.maximum(@column) || 0 finish = @relation.maximum(@column) || 0
......
...@@ -15552,6 +15552,9 @@ msgstr "" ...@@ -15552,6 +15552,9 @@ msgstr ""
msgid "ProjectSettings|Issues" msgid "ProjectSettings|Issues"
msgstr "" 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." msgid "ProjectSettings|Learn more about badges."
msgstr "" msgstr ""
......
...@@ -52,6 +52,18 @@ describe ReleasesFinder do ...@@ -52,6 +52,18 @@ describe ReleasesFinder do
subject subject
end end
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 end
end end
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 settingsPanel from '~/pages/projects/shared/permissions/components/settings_panel.vue';
import { import {
...@@ -32,6 +32,8 @@ const defaultProps = { ...@@ -32,6 +32,8 @@ const defaultProps = {
registryHelpPath: '/help/user/packages/container_registry/index', registryHelpPath: '/help/user/packages/container_registry/index',
lfsAvailable: true, lfsAvailable: true,
lfsHelpPath: '/help/workflow/lfs/manage_large_binaries_with_git_lfs', 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, pagesAvailable: true,
pagesAccessControlEnabled: false, pagesAccessControlEnabled: false,
pagesAccessControlForced: false, pagesAccessControlForced: false,
...@@ -43,21 +45,25 @@ const defaultProps = { ...@@ -43,21 +45,25 @@ const defaultProps = {
describe('Settings Panel', () => { describe('Settings Panel', () => {
let wrapper; let wrapper;
const mountComponent = customProps => { const mountComponent = (
const propsData = { ...defaultProps, ...customProps }; { currentSettings = {}, ...customProps } = {},
return shallowMount(settingsPanel, { propsData }); mountFn = shallowMount,
) => {
const propsData = {
...defaultProps,
...customProps,
currentSettings: { ...defaultProps.currentSettings, ...currentSettings },
};
return mountFn(settingsPanel, { propsData });
}; };
const overrideCurrentSettings = (currentSettingsProps, extraProps = {}) => { const overrideCurrentSettings = (currentSettingsProps, extraProps = {}) => {
return mountComponent({ return mountComponent({ ...extraProps, currentSettings: currentSettingsProps });
...extraProps,
currentSettings: {
...defaultProps.currentSettings,
...currentSettingsProps,
},
});
}; };
const findLFSSettingsMessage = () => wrapper.find({ ref: 'git-lfs-settings' }).find('p');
beforeEach(() => { beforeEach(() => {
wrapper = mountComponent(); wrapper = mountComponent();
}); });
...@@ -333,6 +339,40 @@ describe('Settings Panel', () => { ...@@ -333,6 +339,40 @@ describe('Settings Panel', () => {
expect(wrapper.find('[name="project[lfs_enabled]"]').props().disabledInput).toEqual(true); 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', () => { describe('Packages', () => {
......
...@@ -1391,35 +1391,14 @@ describe Project do ...@@ -1391,35 +1391,14 @@ describe Project do
context 'repository storage by default' do context 'repository storage by default' do
let(:project) { build(:project) } 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 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') expect(project.repository_storage).to eq('picked')
end 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 end
context 'shared runners by default' do context 'shared runners by default' do
......
...@@ -655,10 +655,18 @@ describe Snippet do ...@@ -655,10 +655,18 @@ describe Snippet do
describe '#repository_storage' do describe '#repository_storage' do
let(:snippet) { create(:snippet) } let(:snippet) { create(:snippet) }
it 'returns default repository storage' do subject { snippet.repository_storage }
expect(Gitlab::CurrentSettings).to receive(:pick_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 end
context 'when snippet_project is already created' do context 'when snippet_project is already created' do
...@@ -669,9 +677,7 @@ describe Snippet do ...@@ -669,9 +677,7 @@ describe Snippet do
end end
it 'returns repository_storage from snippet_project' do it 'returns repository_storage from snippet_project' do
expect(Gitlab::CurrentSettings).not_to receive(:pick_repository_storage) expect(subject).to eq 'foo'
expect(snippet.repository_storage).to eq 'foo'
end end
end end
end end
......
...@@ -168,4 +168,37 @@ RSpec.shared_examples 'model with repository' do ...@@ -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(:base_dir) }
it { is_expected.to respond_to(:disk_path) } it { is_expected.to respond_to(:disk_path) }
end 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 end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment