Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
224d2fe167
commit
3fbfc0075a
|
@ -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 \
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -1 +1 @@
|
|||
2106629e3af3e8949b23f20825d6bfee62c10992
|
||||
d7eedd059daf9059990a95e53c76e567eac64899
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.6 KiB |
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
>
|
||||
|
|
|
@ -7,6 +7,9 @@ class PwaController < ApplicationController # rubocop:disable Gitlab/NamespacedC
|
|||
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def manifest
|
||||
end
|
||||
|
||||
def offline
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
= javascript_tag "", visual_review_toolbar_options
|
|
@ -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 }
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "GitLab",
|
||||
"short_name": "GitLab",
|
||||
"description": "<%= _("The complete DevOps platform. One application with endless possibilities. Organizations rely on GitLab’s 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"
|
||||
}]
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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`,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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**.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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 GitLab’s 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 |
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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}" \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue