......@@ -147,7 +147,7 @@ function deferredInitialisation() {
const canaryBadge = document.querySelector('.js-canary-badge');
const canaryLink = document.querySelector('.js-canary-link');
if (canaryBadge) {
canaryBadge.classList.remove('hidden');
canaryBadge.classList.add('hidden');
}
if (canaryLink) {
canaryLink.classList.add('hidden');
......
......
......@@ -47,6 +47,7 @@
display: flex;
align-items: flex-start;
width: 100%;
padding-bottom: $gl-padding;
@include media-breakpoint-down(xs) {
display: block;
......
......
......@@ -337,8 +337,8 @@ class Project < ApplicationRecord
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_personal_projects_limit, on: :create
validate :check_repository_path_availability, on: :update, if: ->(project) { project.renamed? }
validate :visibility_level_allowed_by_group, if: -> { changes.has_key?(:visibility_level) }
validate :visibility_level_allowed_as_fork, if: -> { changes.has_key?(:visibility_level) }
validate :visibility_level_allowed_by_group, if: :should_validate_visibility_level?
validate :visibility_level_allowed_as_fork, if: :should_validate_visibility_level?
validate :check_wiki_path_conflict
validate :validate_pages_https_only, if: -> { changes.has_key?(:pages_https_only) }
validates :repository_storage,
......@@ -892,6 +892,10 @@ class Project < ApplicationRecord
self.errors.add(:limit_reached, error % { limit: limit })
end
def should_validate_visibility_level?
new_record? || changes.has_key?(:visibility_level)
end
def visibility_level_allowed_by_group
return if visibility_level_allowed_by_group?
......
......
......@@ -488,6 +488,10 @@ class ProjectPolicy < BasePolicy
def team_access_level
return -1 if @user.nil?
lookup_access_level!
end
def lookup_access_level!
# NOTE: max_member_access has its own cache
project.team.max_member_access(@user.id)
end
......
......
......@@ -9,7 +9,7 @@ module Projects
end
def execute
Projects::HousekeepingService.new(@project, :gc).execute do
Projects::HousekeepingService.new(@project).execute do
repository.delete_all_refs_except(RESERVED_REF_PREFIXES)
end
rescue Projects::HousekeepingService::LeaseTaken => e
......
......
......@@ -37,7 +37,17 @@ module Projects
raise DownloadLinksError, response.message unless response.success?
parse_response_links(response['objects'])
# Since the LFS Batch API may return a Content-Ttpe of
# application/vnd.git-lfs+json
# (https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md#requests),
# HTTParty does not know this is actually JSON.
data = JSON.parse(response.body)
raise DownloadLinksError, "LFS Batch API did return any objects" unless data.is_a?(Hash) && data.key?('objects')
parse_response_links(data['objects'])
rescue JSON::ParserError
raise DownloadLinksError, "LFS Batch API response is not JSON"
end
def parse_response_links(objects_response)
......
......
......@@ -10,6 +10,7 @@
.hide.alert.alert-danger.js-ci-variable-error-box
%ul.ci-variable-list
= render 'ci/variables/variable_header'
- @variables.each.each do |variable|
= render 'ci/variables/variable_row', form_field: 'variables', variable: variable
= render 'ci/variables/variable_row', form_field: 'variables'
......
......
- only_key_value = local_assigns.fetch(:only_key_value, false)
%li.ci-variable-row.m-0.d-none.d-sm-block
.d-flex.w-100.align-items-center.pb-2
.bold.table-section.section-15.append-right-10
= s_('CiVariables|Type')
.bold.table-section.section-15.append-right-10
= s_('CiVariables|Key')
.bold.table-section.section-15.append-right-10
= s_('CiVariables|Value')
- unless only_key_value
.bold.table-section.section-20
= s_('CiVariables|State')
.bold.table-section.section-20
= s_('CiVariables|Masked')
= render_if_exists 'ci/variables/environment_scope_header'
......@@ -20,18 +20,18 @@
- masked_input_name = "#{form_field}[variables_attributes][][masked]"
%li.js-row.ci-variable-row{ data: { is_persisted: "#{!id.nil?}" } }
.ci-variable-row-body
.ci-variable-row-body.border-bottom
%input.js-ci-variable-input-id{ type: "hidden", name: id_input_name, value: id }
%input.js-ci-variable-input-destroy{ type: "hidden", name: destroy_input_name }
%select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control{ name: variable_type_input_name }
%select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control.table-section.section-15{ name: variable_type_input_name }
= options_for_select(ci_variable_type_options, variable_type)
%input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control{ type: "text",
%input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control.table-section.section-15{ type: "text",
name: key_input_name,
value: key,
placeholder: s_('CiVariables|Input variable key') }
.ci-variable-body-item.gl-show-field-errors
.ci-variable-body-item.gl-show-field-errors.table-section.section-15.border-top-0.p-0
.form-control.js-secret-value-placeholder.qa-ci-variable-input-value{ class: ('hide' unless id) }
= '*' * 20
= '*' * 17
%textarea.js-ci-variable-input-value.js-secret-value.qa-ci-variable-input-value.form-control{ class: ('hide' if id),
rows: 1,
name: value_input_name,
......@@ -41,7 +41,7 @@
= s_("CiVariables|Cannot use Masked Variable with current value")
= link_to icon('question-circle'), help_page_path('ci/variables/README', anchor: 'masked-variables'), target: '_blank', rel: 'noopener noreferrer'
- unless only_key_value
.ci-variable-body-item.ci-variable-protected-item
.ci-variable-body-item.ci-variable-protected-item.table-section.section-20.mr-0.border-top-0
.append-right-default
= s_("CiVariable|Protected")
%button{ type: 'button',
......@@ -55,7 +55,7 @@
%span.toggle-icon
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
.ci-variable-body-item.ci-variable-masked-item
.ci-variable-body-item.ci-variable-masked-item.table-section.section-20.mr-0.border-top-0
.append-right-default
= s_("CiVariable|Masked")
%button{ type: 'button',
......@@ -70,5 +70,5 @@
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
= render_if_exists 'ci/variables/environment_scope', form_field: form_field, variable: variable
%button.js-row-remove-button.ci-variable-row-remove-button{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
= icon('minus-circle')
%button.js-row-remove-button.ci-variable-row-remove-button.table-section.section-5.border-top-0{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
= icon('minus-circle')
......@@ -10,7 +10,7 @@
.branch-info
.branch-title
= sprite_icon('fork', size: 12)
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name prepend-left-8' do
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name prepend-left-8 qa-branch-name' do
= branch.name
- if branch.name == @repository.root_ref
%span.badge.badge-primary.prepend-left-5 default
......
......
......@@ -53,7 +53,7 @@
= time_ago_with_tooltip(member.created_at)
- if show_roles
- current_resource = @project || @group
.controls.member-controls.row
.controls.member-controls
- if show_controls && member.source == current_resource
- if member.can_resend_invite?
......
......
---
title: Fixes next badge being always visible
merge_request:
author:
type: fixed
---
title: Remove non-semantic use of `.row` in member listing controls
merge_request: 28204
author:
type: fixed
---
title: Fix project visibility level validation
merge_request: 28305
author: Peter Marko
type: fixed
---
title: Add EE fixtures to SeedFu list
merge_request: 28241
author:
type: other
---
title: Properly handle LFS Batch API response in project import
merge_request: 28223
author:
type: fixed
# frozen_string_literal: true
if Gitlab.ee?
SeedFu.fixture_paths += %W[ee/db/fixtures ee/db/fixtures/#{Rails.env}]
end
......@@ -157,6 +157,42 @@ For wikis:
sudo -u git -H bundle exec rake geo:verification:wiki:reset RAILS_ENV=production
```
## Reconcile differences with checksum mismatches
If the **primary** and **secondary** nodes have a checksum verification mismatch, the cause may not be apparent. To find the cause of a checksum mismatch:
1. Navigate to the **Admin Area > Projects** dashboard on the **primary** node, find the
project that you want to check the checksum differences and click on the
**Edit** button:
![Projects dashboard](img/checksum-differences-admin-projects.png)
1. On the project admin page get the **Gitaly storage name**, and **Gitaly relative path**:
![Project admin page](img/checksum-differences-admin-project-page.png)
1. Navigate to the project's repository directory on both **primary** and **secondary** nodes. For an installation from source, the path is usually `/home/git/repositories`. For Omnibus installs, the path is usually `/var/opt/gitlab/git-data/repositories`. Note that if `git_data_dirs` is customized, check the directory layout on your server to be sure.
```sh
cd /var/opt/gitlab/git-data/repositories
```
1. Run the following command on the **primary** node, redirecting the output to a file:
```sh
git show-ref --head | grep -E "HEAD|(refs/(heads|tags|keep-around|merge-requests|environments|notes)/)" > primary-node-refs
```
1. Run the following command on the **secondary** node, redirecting the output to a file:
```sh
git show-ref --head | grep -E "HEAD|(refs/(heads|tags|keep-around|merge-requests|environments|notes)/)" > secondary-node-refs
```
1. Copy the files from the previous steps on the same system, and do a diff between the contents:
```sh
diff primary-node-refs secondary-node-refs
```
## Current limitations
Until [issue #5064][ee-5064] is completed, background verification doesn't cover
......
......
doc/administration/geo/disaster_recovery/img/checksum-differences-admin-project-page.png

197 KiB

doc/administration/geo/disaster_recovery/img/checksum-differences-admin-projects.png

84 KiB