Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-19 09:09:08 +00:00
parent 224d2fe167
commit 3fbfc0075a
51 changed files with 748 additions and 279 deletions

View File

@ -50,6 +50,8 @@ include:
--tag ~orchestrated \
--tag ~transient \
--tag ~skip_signup_disabled \
--tag ~requires_git_protocol_v2 \
--tag ~requires_praefect \
--force-color \
--order random \
--format documentation \

View File

@ -1001,7 +1001,6 @@ Layout/LineLength:
- 'db/migrate/20220310101118_update_holder_name_limit.rb'
- 'db/migrate/20220314184209_add_group_fk_to_protected_environment_approval_rules.rb'
- 'db/migrate/20220314204009_add_approval_rule_fk_to_deployment_approvals.rb'
- 'db/optional_migrations/composite_primary_keys.rb'
- 'db/post_migrate/20210328214434_remove_temporary_index_from_vulnerabilities_table.rb'
- 'db/post_migrate/20210401131948_move_container_registry_enabled_to_project_features2.rb'
- 'db/post_migrate/20210402005225_add_source_and_level_index_on_notification_settings.rb'

View File

@ -229,7 +229,6 @@ Style/PercentLiteralDelimiters:
- 'db/migrate/20210621044000_rename_services_indexes_to_integrations.rb'
- 'db/migrate/20210709085759_index_batched_migration_jobs_by_max_value.rb'
- 'db/migrate/20210928155022_improve_index_for_error_tracking.rb'
- 'db/optional_migrations/composite_primary_keys.rb'
- 'db/post_migrate/20210329102724_add_new_trail_plans.rb'
- 'db/post_migrate/20210420121149_backfill_conversion_of_ci_job_artifacts.rb'
- 'db/post_migrate/20210426094549_backfill_ci_builds_for_bigint_conversion.rb'

View File

@ -1 +1 @@
2106629e3af3e8949b23f20825d6bfee62c10992
d7eedd059daf9059990a95e53c76e567eac64899

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,5 +1,5 @@
<script>
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { GlSkeletonLoader } from '@gitlab/ui';
import { mapState, mapGetters } from 'vuex';
import { SIDEBAR_INIT_WIDTH, leftSidebarViews } from '../constants';
import ActivityBar from './activity_bar.vue';
@ -10,7 +10,7 @@ import ResizablePanel from './resizable_panel.vue';
export default {
components: {
GlSkeletonLoading,
GlSkeletonLoader,
ResizablePanel,
ActivityBar,
IdeTree,
@ -38,7 +38,7 @@ export default {
<template v-if="loading">
<div class="multi-file-commit-panel-inner" data-testid="ide-side-bar-inner">
<div v-for="n in 3" :key="n" class="multi-file-loading-container">
<gl-skeleton-loading />
<gl-skeleton-loader />
</div>
</div>
</template>

View File

@ -132,6 +132,7 @@ export default {
<div
v-gl-tooltip="{ title: tag.name }"
data-testid="name"
data-qa-selector="tag_name_content"
class="gl-text-overflow-ellipsis gl-overflow-hidden gl-white-space-nowrap"
:class="mobileClasses"
>

View File

@ -7,6 +7,9 @@ class PwaController < ApplicationController # rubocop:disable Gitlab/NamespacedC
skip_before_action :authenticate_user!
def manifest
end
def offline
end
end

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
module Tooling
module VisualReviewHelper
# Since we only use the visual review toolbar for the gitlab project,
# we can hardcode the project ID and project path for now.
#
# If we need to extend the review apps to other applications in the future,
# we should create REVIEW_APPS_PROJECT_ID and REVIEW_APPS_PROJECT_PATH
# environment variables (mapped to CI_PROJECT_ID and CI_PROJECT_PATH respectively),
# as well as setting `data-require-auth` according to the project visibility.
GITLAB_INSTANCE_URL = 'https://gitlab.com'
GITLAB_ORG_GITLAB_PROJECT_ID = '278964'
GITLAB_ORG_GITLAB_PROJECT_PATH = 'gitlab-org/gitlab'
def visual_review_toolbar_options
{ 'data-merge-request-id': "#{ENV['REVIEW_APPS_MERGE_REQUEST_IID']}",
'data-mr-url': "#{GITLAB_INSTANCE_URL}",
'data-project-id': "#{GITLAB_ORG_GITLAB_PROJECT_ID}",
'data-project-path': "#{GITLAB_ORG_GITLAB_PROJECT_PATH}",
'data-require-auth': false,
'id': 'review-app-toolbar-script',
'src': 'https://gitlab.com/assets/webpack/visual_review_toolbar.js' }
end
end
end

View File

@ -28,7 +28,6 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
commits_anchor_data,
branches_anchor_data,
tags_anchor_data,
files_anchor_data,
storage_anchor_data,
releases_anchor_data
].compact.select(&:is_link)
@ -161,26 +160,16 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
can_current_user_push_to_branch?(default_branch)
end
def files_anchor_data
AnchorData.new(true,
statistic_icon('doc-code') +
_('%{strong_start}%{human_size}%{strong_end} Files').html_safe % {
human_size: storage_counter(statistics.total_repository_size),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_tree_path(project))
end
def storage_anchor_data
can_show_quota = can?(current_user, :admin_project, project) && !empty_repo?
AnchorData.new(true,
statistic_icon('disk') +
_('%{strong_start}%{human_size}%{strong_end} Storage').html_safe % {
_('%{strong_start}%{human_size}%{strong_end} Project Storage').html_safe % {
human_size: storage_counter(statistics.storage_size),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
empty_repo? ? nil : project_tree_path(project))
can_show_quota ? project_usage_quotas_path(project) : nil)
end
def releases_anchor_data

View File

@ -68,6 +68,7 @@
%meta{ name: "description", content: page_description }
%link{ rel: 'manifest', href: manifest_path(format: :json) }
%meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
%meta{ name: 'theme-color', content: user_theme_primary_color }

View File

@ -0,0 +1 @@
= javascript_tag "", visual_review_toolbar_options

View File

@ -8,6 +8,7 @@
%body{ class: body_classes, data: body_data }
= render "layouts/init_auto_complete" if @gfm_form
= render "layouts/init_client_detection_flags"
= render "layouts/visual_review" if ENV['REVIEW_APPS_ENABLED']
= render 'peek/bar'
= header_message
= render partial: "layouts/header/default", locals: { project: @project, group: @group }

View File

@ -0,0 +1,27 @@
{
"name": "GitLab",
"short_name": "GitLab",
"description": "<%= _("The complete DevOps platform. One application with endless possibilities. Organizations rely on GitLabs source code management, CI/CD, security, and more to deliver software rapidly.") %>",
"start_url": "<%= explore_projects_path %>",
"scope": "<%= root_path %>",
"display": "browser",
"orientation": "any",
"background_color": "#fff",
"theme_color": "<%= user_theme_primary_color %>",
"icons": [{
"src": "<%= Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, '/-/pwa-icons/logo-192.png') %>",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "<%= Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, '/-/pwa-icons/logo-512.png') %>",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "<%= Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, '/-/pwa-icons/maskable-logo.png') %>",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}]
}

View File

@ -9,6 +9,6 @@
.note-form-actions.clearfix
.settings-message.note-edit-warning.js-finish-edit-warning
= _("Finish editing this message first!")
= submit_tag _('Save comment'), class: 'gl-button btn btn-success js-comment-save-button', data: { qa_selector: 'save_comment_button' }
= submit_tag _('Save comment'), class: 'gl-button btn btn-confirm js-comment-save-button', data: { qa_selector: 'save_comment_button' }
%button.btn.gl-button.btn-cancel.note-edit-cancel{ type: 'button' }
= _("Cancel")

View File

@ -629,6 +629,9 @@ Settings.cron_jobs['projects_schedule_refresh_build_artifacts_size_statistics_wo
Settings.cron_jobs['inactive_projects_deletion_cron_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['inactive_projects_deletion_cron_worker']['cron'] ||= '0 1 * * *'
Settings.cron_jobs['inactive_projects_deletion_cron_worker']['job_class'] = 'Projects::InactiveProjectsDeletionCronWorker'
Settings.cron_jobs['loose_foreign_keys_cleanup_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['loose_foreign_keys_cleanup_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['loose_foreign_keys_cleanup_worker']['job_class'] = 'LooseForeignKeys::CleanupWorker'
Gitlab.ee do
Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker'] ||= Settingslogic.new({})
@ -760,9 +763,6 @@ Gitlab.ee do
Settings.cron_jobs['app_sec_dast_profile_schedule_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['app_sec_dast_profile_schedule_worker']['cron'] ||= '7-59/15 * * * *'
Settings.cron_jobs['app_sec_dast_profile_schedule_worker']['job_class'] = 'AppSec::Dast::ProfileScheduleWorker'
Settings.cron_jobs['loose_foreign_keys_cleanup_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['loose_foreign_keys_cleanup_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['loose_foreign_keys_cleanup_worker']['job_class'] = 'LooseForeignKeys::CleanupWorker'
Settings.cron_jobs['ci_namespace_mirrors_consistency_check_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['ci_namespace_mirrors_consistency_check_worker']['cron'] ||= '*/4 * * * *'
Settings.cron_jobs['ci_namespace_mirrors_consistency_check_worker']['job_class'] = 'Database::CiNamespaceMirrorsConsistencyCheckWorker'

View File

@ -117,6 +117,7 @@ Rails.application.routes.draw do
get '/whats_new' => 'whats_new#index'
get 'offline' => "pwa#offline"
get 'manifest' => "pwa#manifest", constraints: lambda { |req| req.format == :json }
# '/-/health' implemented by BasicHealthCheck middleware
get 'liveness' => 'health#liveness'

View File

@ -1,63 +0,0 @@
# frozen_string_literal: true
# This migration adds a primary key constraint to tables
# that only have a composite unique key.
#
# This is not strictly relevant to Rails (v4 does not
# support composite primary keys). However this becomes
# useful for e.g. PostgreSQL's logical replication (pglogical)
# which requires all tables to have a primary key constraint.
#
# In that sense, the migration is optional and not strictly needed.
class CompositePrimaryKeysMigration < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
Index = Struct.new(:table, :name, :columns)
TABLES = [
Index.new(:issue_assignees, 'index_issue_assignees_on_issue_id_and_user_id', %i(issue_id user_id)),
Index.new(:user_interacted_projects, 'index_user_interacted_projects_on_project_id_and_user_id', %i(project_id user_id)),
Index.new(:merge_request_diff_files, 'index_merge_request_diff_files_on_mr_diff_id_and_order', %i(merge_request_diff_id relative_order)),
Index.new(:merge_request_diff_commits, 'index_merge_request_diff_commits_on_mr_diff_id_and_order', %i(merge_request_diff_id relative_order)),
Index.new(:project_authorizations, 'index_project_authorizations_on_user_id_project_id_access_level', %i(user_id project_id access_level)),
Index.new(:push_event_payloads, 'index_push_event_payloads_on_event_id', %i(event_id)),
Index.new(:schema_migrations, 'unique_schema_migrations', %(version))
].freeze
disable_ddl_transaction!
def up
disable_statement_timeout do
TABLES.each do |index|
add_primary_key(index)
end
end
end
def down
disable_statement_timeout do
TABLES.each do |index|
remove_primary_key(index)
end
end
end
private
def add_primary_key(index)
execute "ALTER TABLE #{index.table} ADD PRIMARY KEY USING INDEX #{index.name}"
end
def remove_primary_key(index)
temp_index_name = "#{index.name[0..58]}_old"
rename_index index.table, index.name, temp_index_name if index_exists_by_name?(index.table, index.name)
# re-create unique key index
add_concurrent_index index.table, index.columns, unique: true, name: index.name
# This also drops the `temp_index_name` as this is owned by the constraint
execute "ALTER TABLE #{index.table} DROP CONSTRAINT IF EXISTS #{temp_index_name}"
end
end

View File

@ -127,10 +127,10 @@ POST /features/:name
| `value` | integer/string | yes | `true` or `false` to enable/disable, or an integer for percentage of time |
| `key` | string | no | `percentage_of_actors` or `percentage_of_time` (default) |
| `feature_group` | string | no | A Feature group name |
| `user` | string | no | A GitLab username |
| `group` | string | no | A GitLab group's path, for example `gitlab-org` |
| `namespace` | string | no | A GitLab group or user namespace's path, for example `gitlab-org` or username path. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353117) in GitLab 15.0. |
| `project` | string | no | A projects path, for example `gitlab-org/gitlab-foss` |
| `user` | string | no | A GitLab username or comma-separated multiple usernames |
| `group` | string | no | A GitLab group's path, for example `gitlab-org`, or comma-separated multiple group paths |
| `namespace` | string | no | A GitLab group or user namespace's path, for example `john-doe`, or comma-separated multiple namespace paths. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353117) in GitLab 15.0. |
| `project` | string | no | A projects path, for example `gitlab-org/gitlab-foss`, or comma-separated multiple project paths |
| `force` | boolean | no | Skip feature flag validation checks, such as a YAML definition |
You can enable or disable a feature for a `feature_group`, a `user`,

View File

@ -74,6 +74,8 @@ If you set a quota for a subgroup, it is not used.
## View CI/CD minutes used by a group
> Displaying shared runners duration per project [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355666) in GitLab 15.0.
You can view the number of CI/CD minutes being used by a group.
Prerequisite:

View File

@ -226,33 +226,47 @@ A fork *does* copy the CI/CD settings of the cloned repository.
### Create a specific runner
You can create a specific runner for your self-managed GitLab instance or for GitLab.com.
You must have the Owner role for the project.
Prerequisite:
- You must have at least the Maintainer role for the project.
To create a specific runner:
1. [Install runner](https://docs.gitlab.com/runner/install/).
1. Go to the project's **Settings > CI/CD** and expand the **Runners** section.
1. Note the URL and token.
1. [Install GitLab Runner](https://docs.gitlab.com/runner/install/).
1. On the top bar, select **Menu > Projects** and find the project where you want to use the runner.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. In the **Specific runners** section, note the URL and token.
1. [Register the runner](https://docs.gitlab.com/runner/register/).
### Enable a specific runner for a specific project
The runner is now enabled for the project.
A specific runner is available in the project it was created for. An administrator can
enable a specific runner to apply to additional projects.
### Enable a specific runner for a different project
- You must have the Owner role for the
project.
After a specific runner is created, you can enable it for other projects.
Prerequisites:
You must have at least the Maintainer role for:
- The project where the runner is already enabled.
- The project where you want to enable the runner.
- The specific runner must not be [locked](#prevent-a-specific-runner-from-being-enabled-for-other-projects).
To enable or disable a specific runner for a project:
To enable a specific runner for a project:
1. Go to the project's **Settings > CI/CD** and expand the **Runners** section.
1. Click **Enable for this project** or **Disable for this project**.
1. On the top bar, select **Menu > Projects** and find the project where you want to enable the runner.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Expand **Runners**.
1. By the runner you want, select **Enable for this project**.
You can edit a specific runner from any of the projects it's enabled for.
The modifications, which include unlocking, editing tags and the description,
The modifications, which include unlocking and editing tags and the description,
affect all projects that use the runner.
An administrator can [enable the runner for multiple projects](../../user/admin_area/settings/continuous_integration.md#enable-a-specific-runner-for-multiple-projects).
### Prevent a specific runner from being enabled for other projects
You can configure a specific runner so it is "locked" and cannot be enabled for other projects.
@ -261,8 +275,9 @@ but can also be changed later.
To lock or unlock a specific runner:
1. Go to the project's **Settings > CI/CD** and expand the **Runners** section.
1. Go to the project's **Settings > CI/CD**.
1. Expand the **Runners** section.
1. Find the specific runner you want to lock or unlock. Make sure it's enabled. You cannot lock shared or group runners.
1. Click the pencil button.
1. Select **Edit** (**{pencil}**).
1. Check the **Lock to current projects** option.
1. Click **Save changes**.
1. Select **Save changes**.

View File

@ -39,6 +39,23 @@ You can set all new projects to have the instance's shared runners available by
Any time a new project is created, the shared runners are available.
## Shared runners CI/CD minutes
As an administrator you can set either a global or namespace-specific
limit on the number of [CI/CD minutes](../../../ci/pipelines/cicd_minutes.md) you can use.
## Enable a specific runner for multiple projects
To enable a specific runner for one or more projects:
1. On the top bar, select **Menu > Admin**.
1. From the left sidebar, select **Overview > Runners**.
1. Select the runner you want to edit.
1. In the top right, select **Edit** (**{pencil}**).
1. Under **Restrict projects for this runner**, search for a project.
1. To the left of the project, select **Enable**.
1. Repeat this process for each additional project.
## Add a message for shared runners
To display details about the instance's shared runners in all projects'
@ -143,10 +160,6 @@ A new pipeline must run before the latest artifacts can expire and be deleted.
NOTE:
All application settings have a [customizable cache expiry interval](../../../administration/application_settings_cache.md) which can delay the settings affect.
## Shared runners CI/CD minutes
As an administrator you can set either a global or namespace-specific limit on the number of [CI/CD minutes](../../../ci/pipelines/cicd_minutes.md) you can use.
## Archive jobs
Archiving jobs is useful for reducing the CI/CD footprint on the system by removing some

View File

@ -22,16 +22,15 @@ module API
use :pagination
optional :name, type: String, desc: 'Returns the environment with this name'
optional :search, type: String, desc: 'Returns list of environments matching the search criteria'
optional :states, type: String, values: Environment.valid_states.map(&:to_s), desc: 'List all environments that match a specific state'
mutually_exclusive :name, :search, message: 'cannot be used together'
end
get ':id/environments' do
authorize! :read_environment, user_project
environments = ::Environments::EnvironmentsFinder.new(user_project, current_user, params).execute
environments = ::Environments::EnvironmentsFinder.new(user_project, current_user, declared_params(include_missing: false)).execute
present paginate(environments), with: Entities::Environment, current_user: current_user
rescue ::Environments::EnvironmentsFinder::InvalidStatesError => exception
bad_request!(exception.message)
end
desc 'Creates a new environment' do

View File

@ -68,10 +68,13 @@ module API
requires :value, type: String, desc: '`true` or `false` to enable/disable, a float for percentage of time'
optional :key, type: String, desc: '`percentage_of_actors` or the default `percentage_of_time`'
optional :feature_group, type: String, desc: 'A Feature group name'
optional :user, type: String, desc: 'A GitLab username'
optional :group, type: String, desc: "A GitLab group's path, such as 'gitlab-org'"
optional :namespace, type: String, desc: "A GitLab group or user namespace path, such as 'gitlab-org'"
optional :project, type: String, desc: 'A projects path, like gitlab-org/gitlab-ce'
optional :user, type: String, desc: 'A GitLab username or comma-separated multiple usernames'
optional :group, type: String,
desc: "A GitLab group's path, such as 'gitlab-org', or comma-separated multiple group paths"
optional :namespace, type: String,
desc: "A GitLab group or user namespace path, such as 'john-doe', or comma-separated multiple namespace paths"
optional :project, type: String,
desc: "A projects path, such as `gitlab-org/gitlab-ce`, or comma-separated multiple project paths"
optional :force, type: Boolean, desc: 'Skip feature flag validation checks, ie. YAML definition'
mutually_exclusive :key, :feature_group
@ -110,6 +113,8 @@ module API
present Feature.get(params[:name]), # rubocop:disable Gitlab/AvoidFeatureGet
with: Entities::Feature, current_user: current_user
rescue Feature::Target::UnknowTargetError => e
bad_request!(e.message)
end
desc 'Remove the gate value for the given feature'

View File

@ -41,29 +41,8 @@ module Backup
end
def create
if incremental?
unpack(ENV.fetch('PREVIOUS_BACKUP', ENV['BACKUP']))
read_backup_information
verify_backup_version
update_backup_information
end
build_backup_information
definitions.keys.each do |task_name|
run_create_task(task_name)
end
write_backup_information
if skipped?('tar')
upload
else
pack
upload
cleanup
remove_old
end
unpack(ENV.fetch('PREVIOUS_BACKUP', ENV['BACKUP'])) if incremental?
run_all_create_tasks
puts_time "Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data \n" \
"and are not included in this backup. You will need these files to restore a backup.\n" \
@ -95,22 +74,8 @@ module Backup
end
def restore
cleanup_required = unpack(ENV['BACKUP'])
read_backup_information
verify_backup_version
definitions.keys.each do |task_name|
run_restore_task(task_name) if !skipped?(task_name) && enabled_task?(task_name)
end
Rake::Task['gitlab:shell:setup'].invoke
Rake::Task['cache:clear'].invoke
if cleanup_required
cleanup
end
remove_tmp
unpack(ENV['BACKUP'])
run_all_restore_tasks
puts_time "Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data \n" \
"and are not included in this backup. You will need to restore these files manually.".color(:red)
@ -232,6 +197,48 @@ module Backup
Files.new(progress, app_files_dir, excludes: excludes)
end
def run_all_create_tasks
if incremental?
read_backup_information
verify_backup_version
update_backup_information
end
build_backup_information
definitions.keys.each do |task_name|
run_create_task(task_name)
end
write_backup_information
unless skipped?('tar')
pack
upload
remove_old
end
ensure
cleanup unless skipped?('tar')
remove_tmp
end
def run_all_restore_tasks
read_backup_information
verify_backup_version
definitions.keys.each do |task_name|
run_restore_task(task_name) if !skipped?(task_name) && enabled_task?(task_name)
end
Rake::Task['gitlab:shell:setup'].invoke
Rake::Task['cache:clear'].invoke
ensure
cleanup unless skipped?('tar')
remove_tmp
end
def incremental?
@incremental
end
@ -299,7 +306,7 @@ module Backup
def upload
connection_settings = Gitlab.config.backup.upload.connection
if connection_settings.blank? || skipped?('remote')
if connection_settings.blank? || skipped?('remote') || skipped?('tar')
puts_time "Uploading backup archive to remote storage #{remote_directory} ... ".color(:blue) + "[SKIPPED]".color(:cyan)
return
end
@ -405,8 +412,7 @@ module Backup
def unpack(source_backup_id)
if source_backup_id.blank? && non_tarred_backup?
puts_time "Non tarred backup found in #{backup_path}, using that"
return false
return
end
Dir.chdir(backup_path) do

View File

@ -19,7 +19,12 @@ module Banzai
def find_object(project, id)
return unless project.is_a?(Project) && project.valid_repo?
_, record = reference_cache.records_per_parent[project].detect { |k, _v| Gitlab::Git.shas_eql?(k, id) }
# Optimization: try exact commit hash match first
record = reference_cache.records_per_parent[project].fetch(id, nil)
unless record
_, record = reference_cache.records_per_parent[project].detect { |k, _v| Gitlab::Git.shas_eql?(k, id) }
end
record
end

View File

@ -281,6 +281,8 @@ class Feature
end
class Target
UnknowTargetError = Class.new(StandardError)
attr_reader :params
def initialize(params)
@ -292,7 +294,7 @@ class Feature
end
def targets
[feature_group, user, project, group, namespace].compact
[feature_group, users, projects, groups, namespaces].flatten.compact
end
private
@ -305,29 +307,37 @@ class Feature
end
# rubocop: enable CodeReuse/ActiveRecord
def user
def users
return unless params.key?(:user)
UserFinder.new(params[:user]).find_by_username!
params[:user].split(',').map do |arg|
UserFinder.new(arg).find_by_username || (raise UnknowTargetError, "#{arg} is not found!")
end
end
def project
def projects
return unless params.key?(:project)
Project.find_by_full_path(params[:project])
params[:project].split(',').map do |arg|
Project.find_by_full_path(arg) || (raise UnknowTargetError, "#{arg} is not found!")
end
end
def group
def groups
return unless params.key?(:group)
Group.find_by_full_path(params[:group])
params[:group].split(',').map do |arg|
Group.find_by_full_path(arg) || (raise UnknowTargetError, "#{arg} is not found!")
end
end
def namespace
def namespaces
return unless params.key?(:namespace)
# We are interested in Group or UserNamespace
Namespace.without_project_namespaces.find_by_full_path(params[:namespace])
params[:namespace].split(',').map do |arg|
# We are interested in Group or UserNamespace
Namespace.without_project_namespaces.find_by_full_path(arg) || (raise UnknowTargetError, "#{arg} is not found!")
end
end
end
end

View File

@ -44,6 +44,7 @@ module Gitlab
allow_sentry(directives) if Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn
allow_framed_gitlab_paths(directives)
allow_customersdot(directives) if ENV['CUSTOMER_PORTAL_URL'].present?
allow_review_apps(directives) if ENV['REVIEW_APPS_ENABLED']
# The follow section contains workarounds to patch Safari's lack of support for CSP Level 3
# See https://gitlab.com/gitlab-org/gitlab/-/issues/343579
@ -154,6 +155,11 @@ module Gitlab
append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, path))
end
end
def self.allow_review_apps(directives)
# Allow-listed to allow POSTs to https://gitlab.com/api/v4/projects/278964/merge_requests/:merge_request_iid/visual_review_discussions
append_to_directive(directives, 'connect_src', 'https://gitlab.com/api/v4/projects/278964/merge_requests/')
end
end
end
end

View File

@ -87,7 +87,12 @@ module Gitlab
length = [sha1.length, sha2.length].min
return false if length < Gitlab::Git::Commit::MIN_SHA_LENGTH
sha1[0, length] == sha2[0, length]
# Optimization: prevent unnecessary substring creation
if sha1.length == sha2.length
sha1 == sha2
else
sha1[0, length] == sha2[0, length]
end
end
end
end

View File

@ -0,0 +1,85 @@
# frozen_string_literal: true
namespace :gitlab do
namespace :db do
TRIGGER_FUNCTION_NAME = 'gitlab_schema_prevent_write'
desc "GitLab | DB | Install prevent write triggers on all databases"
task lock_writes: [:environment, 'gitlab:db:validate_config'] do
Gitlab::Database::EachDatabase.each_database_connection do |connection, database_name|
create_write_trigger_function(connection)
schemas_for_connection = Gitlab::Database.gitlab_schemas_for_connection(connection)
Gitlab::Database::GitlabSchema.tables_to_schema.each do |table_name, schema_name|
connection.transaction do
if schemas_for_connection.include?(schema_name.to_sym)
drop_write_trigger(database_name, connection, table_name)
else
create_write_trigger(database_name, connection, table_name)
end
end
end
end
end
desc "GitLab | DB | Remove all triggers that prevents writes from all databases"
task unlock_writes: :environment do
Gitlab::Database::EachDatabase.each_database_connection do |connection, database_name|
Gitlab::Database::GitlabSchema.tables_to_schema.each do |table_name, schema_name|
drop_write_trigger(database_name, connection, table_name)
end
drop_write_trigger_function(connection)
end
end
def create_write_trigger_function(connection)
sql = <<-SQL
CREATE OR REPLACE FUNCTION #{TRIGGER_FUNCTION_NAME}()
RETURNS TRIGGER AS
$$
BEGIN
RAISE EXCEPTION 'Table: "%" is write protected within this Gitlab database.', TG_TABLE_NAME
USING ERRCODE = 'modifying_sql_data_not_permitted',
HINT = 'Make sure you are using the right database connection';
END
$$ LANGUAGE PLPGSQL
SQL
connection.execute(sql)
end
def drop_write_trigger_function(connection)
sql = <<-SQL
DROP FUNCTION IF EXISTS #{TRIGGER_FUNCTION_NAME}()
SQL
connection.execute(sql)
end
def create_write_trigger(database_name, connection, table_name)
puts "#{database_name}: '#{table_name}'... Lock Writes".color(:red)
sql = <<-SQL
DROP TRIGGER IF EXISTS #{write_trigger_name(table_name)} ON #{table_name};
CREATE TRIGGER #{write_trigger_name(table_name)}
BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE
ON #{table_name}
FOR EACH STATEMENT EXECUTE FUNCTION #{TRIGGER_FUNCTION_NAME}();
SQL
connection.execute(sql)
end
def drop_write_trigger(database_name, connection, table_name)
puts "#{database_name}: '#{table_name}'... Allow Writes".color(:green)
sql = <<-SQL
DROP TRIGGER IF EXISTS #{write_trigger_name(table_name)} ON #{table_name}
SQL
connection.execute(sql)
end
def write_trigger_name(table_name)
"gitlab_schema_write_trigger_for_#{table_name}"
end
end
end

View File

@ -1,17 +0,0 @@
# frozen_string_literal: true
namespace :gitlab do
namespace :db do
desc 'GitLab | DB | Adds primary keys to tables that only have composite unique keys'
task composite_primary_keys_add: :environment do
require Rails.root.join('db/optional_migrations/composite_primary_keys')
CompositePrimaryKeysMigration.new.up
end
desc 'GitLab | DB | Removes previously added composite primary keys'
task composite_primary_keys_drop: :environment do
require Rails.root.join('db/optional_migrations/composite_primary_keys')
CompositePrimaryKeysMigration.new.down
end
end
end

View File

@ -1008,10 +1008,7 @@ msgid_plural "%{strong_start}%{count} members%{strong_end} must approve to merge
msgstr[0] ""
msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Files"
msgstr ""
msgid "%{strong_start}%{human_size}%{strong_end} Storage"
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
msgid "%{strong_start}%{release_count}%{strong_end} Release"
@ -37702,6 +37699,9 @@ msgstr ""
msgid "The comparison view may be inaccurate due to merge conflicts."
msgstr ""
msgid "The complete DevOps platform. One application with endless possibilities. Organizations rely on GitLabs source code management, CI/CD, security, and more to deliver software rapidly."
msgstr ""
msgid "The compliance report shows the merge request violations merged in protected environments."
msgstr ""

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -11,10 +11,8 @@ module QA
view 'app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row.vue' do
element :more_actions_menu
end
view 'app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row.vue' do
element :tag_delete_button
element :tag_name_content
end
def has_registry_repository?(name)
@ -26,11 +24,11 @@ module QA
end
def has_tag?(tag_name)
has_button?(tag_name)
has_element?(:tag_name_content, text: tag_name)
end
def has_no_tag?(tag_name)
has_no_button?(tag_name)
has_no_element?(:tag_name_content, text: tag_name)
end
def click_delete

View File

@ -7,14 +7,15 @@ module QA
include Members
include Visibility
attr_accessor :repository_storage, # requires admin access
:initialize_with_readme,
attr_accessor :initialize_with_readme,
:auto_devops_enabled,
:github_personal_access_token,
:github_repository_path,
:gitlab_repository_path,
:personal_namespace
attr_reader :repository_storage
attributes :id,
:name,
:path,
@ -70,6 +71,15 @@ module QA
@name = @add_name_uuid ? "#{raw_name}-#{SecureRandom.hex(8)}" : raw_name
end
# Sets the project's repository storage
# This feature requires admin access so be sure to fabricate the project as an admin user, and add the metadata
# `:requires_admin` to the test it's used in.
def repository_storage=(name)
raise ArgumentError, "Please provide a valid repository storage name" if name.to_s.empty?
@repository_storage = name
end
def fabricate!
return if @import

View File

@ -297,12 +297,21 @@ function deploy() {
create_application_secret
cat > review_apps.values.yml <<EOF
gitlab:
webservice:
extraEnv:
REVIEW_APPS_ENABLED: "true"
REVIEW_APPS_MERGE_REQUEST_IID: "${CI_MERGE_REQUEST_IID}"
EOF
HELM_CMD=$(cat << EOF
helm upgrade \
--namespace="${namespace}" \
--create-namespace \
--install \
--wait \
-f review_apps.values.yml \
--timeout "${HELM_INSTALL_TIMEOUT:-20m}" \
--set ci.branch="${CI_COMMIT_REF_NAME}" \
--set ci.commit.sha="${CI_COMMIT_SHORT_SHA}" \

View File

@ -124,7 +124,7 @@ RSpec.describe 'Comments on personal snippets', :js do
page.within('.current-note-edit-form') do
fill_in 'note[note]', with: 'new content'
find('.btn-success').click
find('.btn-confirm').click
end
page.within("#notes-list li#note_#{snippet_notes[0].id}") do

View File

@ -1,4 +1,4 @@
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { GlSkeletonLoader } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
@ -47,7 +47,7 @@ describe('IdeSidebar', () => {
await nextTick();
expect(wrapper.findAll(GlSkeletonLoading)).toHaveLength(3);
expect(wrapper.findAll(GlSkeletonLoader)).toHaveLength(3);
});
describe('deferred rendering components', () => {

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Tooling::VisualReviewHelper do
describe '#visual_review_toolbar_options' do
subject(:result) { helper.visual_review_toolbar_options }
before do
stub_env('REVIEW_APPS_MERGE_REQUEST_IID', '123')
end
it 'returns the correct params' do
expect(result).to eq(
'data-merge-request-id': '123',
'data-mr-url': 'https://gitlab.com',
'data-project-id': '278964',
'data-project-path': 'gitlab-org/gitlab',
'data-require-auth': false,
'id': 'review-app-toolbar-script',
'src': 'https://gitlab.com/assets/webpack/visual_review_toolbar.js'
)
end
end
end

View File

@ -15,6 +15,7 @@ RSpec.describe Backup::Manager do
# is trying to display a diff and `File.exist?` is stubbed. Adding a
# default stub fixes this.
allow(File).to receive(:exist?).and_call_original
allow(FileUtils).to receive(:rm_rf).and_call_original
allow(progress).to receive(:puts)
allow(progress).to receive(:print)
@ -171,12 +172,14 @@ RSpec.describe Backup::Manager do
allow(task2).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'task2.tar.gz'), full_backup_id)
end
it 'executes tar' do
it 'creates a backup tar' do
travel_to(backup_time) do
subject.create # rubocop:disable Rails/SaveBang
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
end
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'tmp'))
end
context 'when BACKUP is set' do
@ -203,6 +206,8 @@ RSpec.describe Backup::Manager do
end.to raise_error(Backup::Error, 'Backup failed')
expect(Gitlab::BackupLogger).to have_received(:info).with(message: "Creating archive #{pack_tar_file} failed")
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'tmp'))
end
end
@ -597,6 +602,7 @@ RSpec.describe Backup::Manager do
skipped: 'tar',
tar_version: be_a(String)
)
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'tmp'))
end
end
@ -697,6 +703,8 @@ RSpec.describe Backup::Manager do
expect(Kernel).to have_received(:system).with(*unpack_tar_cmdline)
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'tmp'))
end
context 'untar fails' do
@ -724,6 +732,8 @@ RSpec.describe Backup::Manager do
end.to raise_error(Backup::Error, 'Backup failed')
expect(Gitlab::BackupLogger).to have_received(:info).with(message: "Creating archive #{pack_tar_file} failed")
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'tmp'))
end
end
@ -786,6 +796,8 @@ RSpec.describe Backup::Manager do
expect(Kernel).to have_received(:system).with(*unpack_tar_cmdline)
expect(Kernel).to have_received(:system).with(*pack_tar_cmdline)
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'tmp'))
end
context 'untar fails' do
@ -817,6 +829,8 @@ RSpec.describe Backup::Manager do
end.to raise_error(Backup::Error, 'Backup failed')
expect(Gitlab::BackupLogger).to have_received(:info).with(message: "Creating archive #{pack_tar_file} failed")
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'tmp'))
end
end
@ -1001,6 +1015,8 @@ RSpec.describe Backup::Manager do
subject.restore
expect(Kernel).to have_received(:system).with(*tar_cmdline)
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'backup_information.yml'))
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'tmp'))
end
context 'tar fails' do
@ -1031,22 +1047,6 @@ RSpec.describe Backup::Manager do
.with(a_string_matching('GitLab version mismatch'))
end
end
describe 'tmp files' do
let(:path) { File.join(Gitlab.config.backup.path, 'tmp') }
before do
allow(FileUtils).to receive(:rm_rf).and_call_original
end
it 'removes backups/tmp dir' do
expect(FileUtils).to receive(:rm_rf).with(path).and_call_original
subject.restore
expect(Gitlab::BackupLogger).to have_received(:info).with(message: 'Deleting backups/tmp ... ')
end
end
end
context 'when there is a non-tarred backup in the directory' do
@ -1066,6 +1066,7 @@ RSpec.describe Backup::Manager do
expect(progress).to have_received(:puts)
.with(a_string_matching('Non tarred backup found '))
expect(FileUtils).to have_received(:rm_rf).with(File.join(Gitlab.config.backup.path, 'tmp'))
end
context 'on version mismatch' do
@ -1082,22 +1083,6 @@ RSpec.describe Backup::Manager do
.with(a_string_matching('GitLab version mismatch'))
end
end
describe 'tmp files' do
let(:path) { File.join(Gitlab.config.backup.path, 'tmp') }
before do
allow(FileUtils).to receive(:rm_rf).and_call_original
end
it 'removes backups/tmp dir' do
expect(FileUtils).to receive(:rm_rf).with(path).and_call_original
subject.restore
expect(Gitlab::BackupLogger).to have_received(:info).with(message: 'Deleting backups/tmp ... ')
end
end
end
end
end

View File

@ -178,6 +178,16 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
expect(directives['connect_src']).not_to include(snowplow_micro_url)
end
end
context 'when REVIEW_APPS_ENABLED is set' do
before do
stub_env('REVIEW_APPS_ENABLED', 'true')
end
it 'adds gitlab-org/gitlab merge requests API endpoint to CSP' do
expect(directives['connect_src']).to include('https://gitlab.com/api/v4/projects/278964/merge_requests/')
end
end
end
end
end

View File

@ -211,16 +211,6 @@ RSpec.describe ProjectPresenter do
context 'statistics anchors (empty repo)' do
let_it_be(:project) { create(:project, :empty_repo) }
describe '#files_anchor_data' do
it 'returns files data' do
expect(presenter.files_anchor_data).to have_attributes(
is_link: true,
label: a_string_including('0 Bytes'),
link: nil
)
end
end
describe '#storage_anchor_data' do
it 'returns storage data' do
expect(presenter.storage_anchor_data).to have_attributes(
@ -275,22 +265,22 @@ RSpec.describe ProjectPresenter do
let(:presenter) { described_class.new(project, current_user: user) }
describe '#files_anchor_data' do
it 'returns files data' do
expect(presenter.files_anchor_data).to have_attributes(
is_link: true,
label: a_string_including('0 Bytes'),
link: presenter.project_tree_path(project)
)
end
end
describe '#storage_anchor_data' do
it 'returns storage data' do
it 'returns storage data without usage quotas link for non-admin users' do
expect(presenter.storage_anchor_data).to have_attributes(
is_link: true,
label: a_string_including('0 Bytes'),
link: presenter.project_tree_path(project)
link: nil
)
end
it 'returns storage data with usage quotas link for admin users' do
project.add_owner(user)
expect(presenter.storage_anchor_data).to have_attributes(
is_link: true,
label: a_string_including('0 Bytes'),
link: presenter.project_usage_quotas_path(project)
)
end
end

View File

@ -113,7 +113,7 @@ RSpec.describe API::Environments do
end
context 'when filtering' do
let_it_be(:environment2) { create(:environment, project: project) }
let_it_be(:stopped_environment) { create(:environment, :stopped, project: project) }
it 'returns environment by name' do
get api("/projects/#{project.id}/environments?name=#{environment.name}", user)
@ -152,11 +152,32 @@ RSpec.describe API::Environments do
expect(json_response.size).to eq(0)
end
it 'returns a 400 status code with invalid states' do
it 'returns environment by valid state' do
get api("/projects/#{project.id}/environments?states=available", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['name']).to eq(environment.name)
end
it 'returns all environments when state is not specified' do
get api("/projects/#{project.id}/environments", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(2)
expect(json_response.first['name']).to eq(environment.name)
expect(json_response.last['name']).to eq(stopped_environment.name)
end
it 'returns a 400 when filtering by invalid state' do
get api("/projects/#{project.id}/environments?states=test", user)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to include('Requested states are invalid')
expect(json_response['error']).to eq('states does not have a valid value')
end
end
end

View File

@ -168,19 +168,15 @@ RSpec.describe API::Features, stub_feature_flags: false do
end
end
shared_examples 'does not enable the flag' do |actor_type, actor_path|
shared_examples 'does not enable the flag' do |actor_type|
let(:actor_path) { raise NotImplementedError }
let(:expected_inexistent_path) { actor_path }
it 'returns the current state of the flag without changes' do
post api("/features/#{feature_name}", admin), params: { value: 'true', actor_type => actor_path }
expect(response).to have_gitlab_http_status(:created)
expect(json_response).to match(
"name" => feature_name,
"state" => "off",
"gates" => [
{ "key" => "boolean", "value" => false }
],
'definition' => known_feature_flag_definition_hash
)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq("400 Bad request - #{expected_inexistent_path} is not found!")
end
end
@ -201,6 +197,19 @@ RSpec.describe API::Features, stub_feature_flags: false do
end
end
shared_examples 'creates an enabled feature for the specified entries' do
it do
post api("/features/#{feature_name}", admin), params: { value: 'true', **gate_params }
expect(response).to have_gitlab_http_status(:created)
expect(json_response['name']).to eq(feature_name)
expect(json_response['gates']).to contain_exactly(
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => array_including(expected_gate_params) }
)
end
end
context 'when enabling for a project by path' do
context 'when the project exists' do
it_behaves_like 'enables the flag for the actor', :project do
@ -209,7 +218,9 @@ RSpec.describe API::Features, stub_feature_flags: false do
end
context 'when the project does not exist' do
it_behaves_like 'does not enable the flag', :project, 'mep/to/the/mep/mep'
it_behaves_like 'does not enable the flag', :project do
let(:actor_path) { 'mep/to/the/mep/mep' }
end
end
end
@ -221,7 +232,9 @@ RSpec.describe API::Features, stub_feature_flags: false do
end
context 'when the group does not exist' do
it_behaves_like 'does not enable the flag', :group, 'not/a/group'
it_behaves_like 'does not enable the flag', :group do
let(:actor_path) { 'not/a/group' }
end
end
end
@ -239,7 +252,9 @@ RSpec.describe API::Features, stub_feature_flags: false do
end
context 'when the user namespace does not exist' do
it_behaves_like 'does not enable the flag', :namespace, 'not/a/group'
it_behaves_like 'does not enable the flag', :namespace do
let(:actor_path) { 'not/a/group' }
end
end
context 'when a project namespace exists' do
@ -251,6 +266,98 @@ RSpec.describe API::Features, stub_feature_flags: false do
end
end
context 'with multiple users' do
let_it_be(:users) { create_list(:user, 3) }
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { user: users.map(&:username).join(',') } }
let(:expected_gate_params) { users.map(&:flipper_id) }
end
context 'when empty value exists between comma' do
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { user: "#{users.first.username},,,," } }
let(:expected_gate_params) { users.first.flipper_id }
end
end
context 'when one of the users does not exist' do
it_behaves_like 'does not enable the flag', :user do
let(:actor_path) { "#{users.first.username},inexistent-entry" }
let(:expected_inexistent_path) { "inexistent-entry" }
end
end
end
context 'with multiple projects' do
let_it_be(:projects) { create_list(:project, 3) }
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { project: projects.map(&:full_path).join(',') } }
let(:expected_gate_params) { projects.map(&:flipper_id) }
end
context 'when empty value exists between comma' do
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { project: "#{projects.first.full_path},,,," } }
let(:expected_gate_params) { projects.first.flipper_id }
end
end
context 'when one of the projects does not exist' do
it_behaves_like 'does not enable the flag', :project do
let(:actor_path) { "#{projects.first.full_path},inexistent-entry" }
let(:expected_inexistent_path) { "inexistent-entry" }
end
end
end
context 'with multiple groups' do
let_it_be(:groups) { create_list(:group, 3) }
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { group: groups.map(&:full_path).join(',') } }
let(:expected_gate_params) { groups.map(&:flipper_id) }
end
context 'when empty value exists between comma' do
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { group: "#{groups.first.full_path},,,," } }
let(:expected_gate_params) { groups.first.flipper_id }
end
end
context 'when one of the groups does not exist' do
it_behaves_like 'does not enable the flag', :group do
let(:actor_path) { "#{groups.first.full_path},inexistent-entry" }
let(:expected_inexistent_path) { "inexistent-entry" }
end
end
end
context 'with multiple namespaces' do
let_it_be(:namespaces) { create_list(:namespace, 3) }
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { namespace: namespaces.map(&:full_path).join(',') } }
let(:expected_gate_params) { namespaces.map(&:flipper_id) }
end
context 'when empty value exists between comma' do
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { namespace: "#{namespaces.first.full_path},,,," } }
let(:expected_gate_params) { namespaces.first.flipper_id }
end
end
context 'when one of the namespaces does not exist' do
it_behaves_like 'does not enable the flag', :namespace do
let(:actor_path) { "#{namespaces.first.full_path},inexistent-entry" }
let(:expected_inexistent_path) { "inexistent-entry" }
end
end
end
it 'creates a feature with the given percentage of time if passed an integer' do
post api("/features/#{feature_name}", admin), params: { value: '50' }

View File

@ -3,6 +3,15 @@
require 'spec_helper'
RSpec.describe PwaController do
describe 'GET #manifest' do
it 'responds with json' do
get manifest_path(format: :json)
expect(response.body).to include('The complete DevOps platform.')
expect(response).to have_gitlab_http_status(:success)
end
end
describe 'GET #offline' do
it 'responds with static HTML page' do
get offline_path

View File

@ -239,6 +239,7 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
end
let(:gitlab_schema) { "gitlab_#{tracking_database}" }
let!(:migration) do
create(
:batched_background_migration,
@ -249,10 +250,12 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
batch_size: batch_size,
sub_batch_size: sub_batch_size,
job_class_name: 'ExampleDataMigration',
job_arguments: [1]
job_arguments: [1],
gitlab_schema: gitlab_schema
)
end
let(:base_model) { Gitlab::Database.database_base_models[tracking_database] }
let(:table_name) { 'example_data' }
let(:batch_size) { 5 }
let(:sub_batch_size) { 2 }
@ -289,7 +292,7 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
WHERE some_column = #{migration_records - 5};
SQL
stub_feature_flags(execute_batched_migrations_on_schedule: true)
stub_feature_flags(feature_flag => true)
stub_const('Gitlab::BackgroundMigration::ExampleDataMigration', migration_class)
end

View File

@ -0,0 +1,152 @@
# frozen_string_literal: true
require 'rake_helper'
RSpec.describe 'gitlab:db:lock_writes', :silence_stdout, :reestablished_active_record_base do
before :all do
Rake.application.rake_require 'active_record/railties/databases'
Rake.application.rake_require 'tasks/seed_fu'
Rake.application.rake_require 'tasks/gitlab/db/validate_config'
Rake.application.rake_require 'tasks/gitlab/db/lock_writes'
# empty task as env is already loaded
Rake::Task.define_task :environment
end
let!(:project) { create(:project) }
let!(:ci_build) { create(:ci_build) }
let(:main_connection) { ApplicationRecord.connection }
let(:ci_connection) { Ci::ApplicationRecord.connection }
context 'single database' do
before do
skip_if_multiple_databases_are_setup
end
context 'when locking writes' do
it 'does not add any triggers to the main schema tables' do
expect do
run_rake_task('gitlab:db:lock_writes')
end.to change {
number_of_triggers(main_connection)
}.by(0)
end
it 'will be still able to modify tables that belong to the main two schemas' do
run_rake_task('gitlab:db:lock_writes')
expect do
Project.last.touch
Ci::Build.last.touch
end.not_to raise_error
end
end
end
context 'multiple databases' do
before do
skip_if_multiple_databases_not_setup
end
context 'when locking writes' do
it 'adds 3 triggers to the ci schema tables on the main database' do
expect do
run_rake_task('gitlab:db:lock_writes')
end.to change {
number_of_triggers_on(main_connection, Ci::Build.table_name)
}.by(3) # Triggers to block INSERT / UPDATE / DELETE
# Triggers on TRUNCATE are not added to the information_schema.triggers
# See https://www.postgresql.org/message-id/16934.1568989957%40sss.pgh.pa.us
end
it 'adds 3 triggers to the main schema tables on the ci database' do
expect do
run_rake_task('gitlab:db:lock_writes')
end.to change {
number_of_triggers_on(ci_connection, Project.table_name)
}.by(3) # Triggers to block INSERT / UPDATE / DELETE
# Triggers on TRUNCATE are not added to the information_schema.triggers
# See https://www.postgresql.org/message-id/16934.1568989957%40sss.pgh.pa.us
end
it 'still allows writes on the tables with the correct connections' do
Project.update_all(updated_at: Time.now)
Ci::Build.update_all(updated_at: Time.now)
end
it 'still allows writing to gitlab_shared schema on any connection' do
connections = [main_connection, ci_connection]
connections.each do |connection|
Gitlab::Database::SharedModel.using_connection(connection) do
LooseForeignKeys::DeletedRecord.create!(
fully_qualified_table_name: "public.projects",
primary_key_value: 1,
cleanup_attempts: 0
)
end
end
end
it 'prevents writes on the main tables on the ci database' do
run_rake_task('gitlab:db:lock_writes')
expect do
ci_connection.execute("delete from projects")
end.to raise_error(ActiveRecord::StatementInvalid, /Table: "projects" is write protected/)
end
it 'prevents writes on the ci tables on the main database' do
run_rake_task('gitlab:db:lock_writes')
expect do
main_connection.execute("delete from ci_builds")
end.to raise_error(ActiveRecord::StatementInvalid, /Table: "ci_builds" is write protected/)
end
it 'prevents truncating a ci table on the main database' do
run_rake_task('gitlab:db:lock_writes')
expect do
main_connection.execute("truncate ci_build_needs")
end.to raise_error(ActiveRecord::StatementInvalid, /Table: "ci_build_needs" is write protected/)
end
end
context 'when unlocking writes' do
before do
run_rake_task('gitlab:db:lock_writes')
end
it 'removes the write protection triggers from the gitlab_main tables on the ci database' do
expect do
run_rake_task('gitlab:db:unlock_writes')
end.to change {
number_of_triggers_on(ci_connection, Project.table_name)
}.by(-3) # Triggers to block INSERT / UPDATE / DELETE
# Triggers on TRUNCATE are not added to the information_schema.triggers
# See https://www.postgresql.org/message-id/16934.1568989957%40sss.pgh.pa.us
expect do
ci_connection.execute("delete from projects")
end.not_to raise_error
end
it 'removes the write protection triggers from the gitlab_ci tables on the main database' do
expect do
run_rake_task('gitlab:db:unlock_writes')
end.to change {
number_of_triggers_on(main_connection, Ci::Build.table_name)
}.by(-3)
expect do
main_connection.execute("delete from ci_builds")
end.not_to raise_error
end
end
end
def number_of_triggers(connection)
connection.select_value("SELECT count(*) FROM information_schema.triggers")
end
def number_of_triggers_on(connection, table_name)
connection
.select_value("SELECT count(*) FROM information_schema.triggers WHERE event_object_table=$1", nil, [table_name])
end
end

View File

@ -14,6 +14,35 @@ RSpec.describe 'layouts/application' do
allow(view).to receive(:current_user_mode).and_return(Gitlab::Auth::CurrentUserMode.new(user))
end
describe "visual review toolbar" do
context "ENV['REVIEW_APPS_ENABLED'] is set to true" do
before do
stub_env(
'REVIEW_APPS_ENABLED' => true,
'REVIEW_APPS_MERGE_REQUEST_IID' => '123'
)
end
it 'renders the visual review toolbar' do
render
expect(rendered).to include('review-app-toolbar-script')
end
end
context "ENV['REVIEW_APPS_ENABLED'] is set to false" do
before do
stub_env('REVIEW_APPS_ENABLED', false)
end
it 'does not render the visual review toolbar' do
render
expect(rendered).not_to include('review-app-toolbar-script')
end
end
end
context 'body data elements for pageview context' do
let(:body_data) do
{

View File

@ -2,6 +2,6 @@
require 'spec_helper'
RSpec.describe Database::BatchedBackgroundMigration::CiDatabaseWorker, :clean_gitlab_redis_shared_state, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/362821' do
RSpec.describe Database::BatchedBackgroundMigration::CiDatabaseWorker, :clean_gitlab_redis_shared_state do
it_behaves_like 'it runs batched background migration jobs', 'ci', feature_flag: :execute_batched_migrations_on_schedule_ci_database
end