Add latest changes from gitlab-org/gitlab@14-4-stable-ee
This commit is contained in:
parent
305ea394ef
commit
6edb7e9bb1
|
|
@ -430,7 +430,7 @@
|
|||
height: $input-height;
|
||||
}
|
||||
|
||||
.issue-boards-content {
|
||||
.issue-boards-content:not(.breadcrumbs) {
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class LegacyDiffNote < Note
|
|||
|
||||
validates :line_code, presence: true, line_code: true
|
||||
|
||||
before_create :set_diff
|
||||
before_create :set_diff, unless: :skip_setting_st_diff?
|
||||
|
||||
def discussion_class(*)
|
||||
LegacyDiffDiscussion
|
||||
|
|
@ -90,6 +90,10 @@ class LegacyDiffNote < Note
|
|||
self.st_diff = diff.to_hash if diff
|
||||
end
|
||||
|
||||
def skip_setting_st_diff?
|
||||
st_diff.present? && importing?
|
||||
end
|
||||
|
||||
def diff_for_line_code
|
||||
attributes = {
|
||||
noteable_type: noteable_type,
|
||||
|
|
|
|||
|
|
@ -27,12 +27,17 @@ class ProtectedBranch < ApplicationRecord
|
|||
# Check if branch name is marked as protected in the system
|
||||
def self.protected?(project, ref_name)
|
||||
return true if project.empty_repo? && project.default_branch_protected?
|
||||
return false if ref_name.blank?
|
||||
|
||||
Rails.cache.fetch("protected_ref-#{ref_name}-#{project.cache_key}") do
|
||||
Rails.cache.fetch(protected_ref_cache_key(project, ref_name)) do
|
||||
self.matching(ref_name, protected_refs: protected_refs(project)).present?
|
||||
end
|
||||
end
|
||||
|
||||
def self.protected_ref_cache_key(project, ref_name)
|
||||
"protected_ref-#{project.cache_key}-#{Digest::SHA1.hexdigest(ref_name)}"
|
||||
end
|
||||
|
||||
def self.allow_force_push?(project, ref_name)
|
||||
project.protected_branches.allowing_force_push.matching(ref_name).any?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
- @no_breadcrumb_container = true
|
||||
- @no_container = true
|
||||
- @content_wrapper_class = "#{@content_wrapper_class} gl-relative"
|
||||
- @content_class = "js-focus-mode-board"
|
||||
- @content_class = "issue-boards-content js-focus-mode-board"
|
||||
- is_epic_board = board.to_type == "EpicBoard"
|
||||
- if is_epic_board
|
||||
- breadcrumb_title _("Epic Boards")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
- title: Scheduled DAST scans
|
||||
body: |
|
||||
GitLab’s Dynamic Application Security Testing (DAST) now supports scheduled on-demand scans. Previously, on-demand DAST scans could only be manually triggered, which limited the usability to scans that you wanted to run immediately. With this new scheduler, you can set a DAST scan to run either once at a specific time in the future, or on a recurring basis. If adding DAST to your pipelines is not an option for your organization, or if the security or compliance regulations for your area require a scan to be scheduled, this feature provides an easy way to create a scheduled scan to meet your needs. The scan can be associated with the default branch, which allows for the results to show on the Secure Dashboard and Vulnerability list. Combined with the scan and site profiles, the scheduled on-demand scans give you quick and easy access to DAST results for your application or API, on a schedule that works for your development and security teams.
|
||||
stage: Secure
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
packages: [Ultimate]
|
||||
url: 'https://docs.gitlab.com/ee/user/application_security/dast/index.html#schedule-an-on-demand-scan'
|
||||
image_url: https://about.gitlab.com/images/14_4/dast_on_demand_schedule.png
|
||||
published_at: 2021-10-22
|
||||
release: 14.4
|
||||
- title: Remote Repositories for GitLab in Visual Studio Code
|
||||
body: |
|
||||
When working in your editor you may need to refer to another project or upstream library for additional information. When you don't have that project already cloned locally, you're forced to either leave your editor and browse the project on GitLab, or locate and then clone the project so you can browse it in your editor. Both of those tasks break your current context, introduce delays, and can take you to a less familiar interface for working with code.
|
||||
|
||||
[GitLab Workflow](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow) version `3.33.0` provides an option to open a remote repository. Open the command palette and use the `GitLab: Open Remote Repository` command to find and then open a project.
|
||||
|
||||
Opening a remote repository allows you to browse a read-only version of a project in your familiar VS Code environment. You can then quickly find the information you're looking for, compare an implementation, or copy a snippet you need.
|
||||
stage: Create
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
packages: [Free, Premium, Ultimate]
|
||||
url: 'https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/blob/main/README.md#browse-a-repository-without-cloning'
|
||||
image_url: https://img.youtube.com/vi/p4GTVx_Nd2s/hqdefault.jpg
|
||||
published_at: 2021-10-22
|
||||
release: 14.4
|
||||
- title: The GitLab Operator is Generally Available
|
||||
body: |
|
||||
GitLab 14.4 is proud to announce the general availability of [the GitLab Operator](https://about.gitlab.com/blog/2021/10/12/open-shift-ga/), with the ability to run production instances of GitLab on Kubernetes platforms, including Red Hat OpenShift. The GitLab Operator also automates day 2 operations such as upgrading components, application reconfiguration, and autoscaling. Check out the [GitLab Operator installation documentation](https://docs.gitlab.com/charts/installation/operator.html) for additional information.
|
||||
stage: Enablement
|
||||
self-managed: true
|
||||
gitlab-com: false
|
||||
packages: [Free, Premium, Ultimate]
|
||||
url: 'https://docs.gitlab.com/charts/installation/operator.html'
|
||||
image_url: https://img.youtube.com/vi/sEBnuhzYD2I/hqdefault.jpg
|
||||
published_at: 2021-10-22
|
||||
release: 14.4
|
||||
- title: DevOps Adoption trend graph
|
||||
body: |
|
||||
In GitLab 14.4, we added a new graph to group-level DevOps Adoption for trend over time. This graph shows you how groups adopt DevOps features over time and can give insights into how quickly groups are adopting additional DevOps processes. This is broken down by Dev, Sec, and Ops functionality.
|
||||
stage: Manage
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
packages: [Ultimate]
|
||||
url: 'https://docs.gitlab.com/ee/user/group/devops_adoption/#adoption-over-time'
|
||||
image_url: https://about.gitlab.com/images/14_4/devops_adop_table.png
|
||||
published_at: 2021-10-22
|
||||
release: 14.4
|
||||
- title: Integrated error tracking inside GitLab without a Sentry instance
|
||||
body: |
|
||||
Prior to GitLab 14.4, you could integrate with Sentry Error Tracking by supplying an endpoint for a Sentry backend (either self-deployed or in their cloud service). With Gitlab 14.4, you now have access to a Sentry-compatible backend built into your GitLab instance. This allows you to quickly instrument your apps so your errors show up directly in GitLab without the need for a separate Sentry instance.
|
||||
stage: Manage
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
packages: [Free, Premium, Ultimate]
|
||||
url: 'https://docs.gitlab.com/ee/operations/error_tracking.html#integrated-error-tracking'
|
||||
image_url: https://about.gitlab.com/images/14_4/monitor-integrated-error-tracking.png
|
||||
published_at: 2021-10-22
|
||||
release: 14.4
|
||||
|
||||
|
|
@ -6,7 +6,9 @@ class AddTemporaryIndexToIssueMetrics < Gitlab::Database::Migration[1.0]
|
|||
INDEX_NAME = 'index_issue_metrics_first_mentioned_in_commit'
|
||||
|
||||
def up
|
||||
add_concurrent_index :issue_metrics, :issue_id, where: 'EXTRACT(YEAR FROM first_mentioned_in_commit_at) > 2019', name: INDEX_NAME
|
||||
condition = Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt::TmpIssueMetrics
|
||||
.first_mentioned_in_commit_at_condition
|
||||
add_concurrent_index :issue_metrics, :issue_id, where: condition, name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ class ScheduleFixFirstMentionedInCommitAtJob < Gitlab::Database::Migration[1.0]
|
|||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
scope = define_batchable_model('issue_metrics')
|
||||
.where('EXTRACT(YEAR FROM first_mentioned_in_commit_at) > 2019')
|
||||
scope = Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt::TmpIssueMetrics
|
||||
.from_2020
|
||||
|
||||
queue_background_migration_jobs_by_range_at_intervals(
|
||||
scope,
|
||||
|
|
|
|||
|
|
@ -1581,11 +1581,29 @@ all state associated with a given repository including:
|
|||
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml remove-repository -virtual-storage <virtual-storage> -repository <repository>
|
||||
```
|
||||
|
||||
- `-virtual-storage` is the virtual storage the repository is located in.
|
||||
- `-repository` is the repository's relative path in the storage.
|
||||
- `-virtual-storage` is the virtual storage the repository is located in. Virtual storages are configured in `/etc/gitlab/gitlab.rb` under `praefect['virtual_storages]` and looks like the following:
|
||||
|
||||
Sometimes parts of the repository continue to exist after running `remove-repository`. This can be caused
|
||||
because of:
|
||||
```ruby
|
||||
praefect['virtual_storages'] = {
|
||||
'default' => {
|
||||
...
|
||||
},
|
||||
'storage-1' => {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the virtual storage to specify is `default` or `storage-1`.
|
||||
|
||||
- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_types.md#hashed-storage).
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
@hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git
|
||||
```
|
||||
|
||||
Parts of the repository can continue to exist after running `remove-repository`. This can be because of:
|
||||
|
||||
- A deletion error.
|
||||
- An in-flight RPC call targeting the repository.
|
||||
|
|
@ -1609,8 +1627,53 @@ The command outputs:
|
|||
Each entry is a complete JSON string with a newline at the end (configurable using the
|
||||
`-delimiter` flag). For example:
|
||||
|
||||
```shell
|
||||
```plaintext
|
||||
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml list-untracked-repositories
|
||||
{"virtual_storage":"default","storage":"gitaly-1","relative_path":"@hashed/ab/cd/abcd123456789012345678901234567890123456789012345678901234567890.git"}
|
||||
{"virtual_storage":"default","storage":"gitaly-1","relative_path":"@hashed/ab/cd/abcd123456789012345678901234567890123456789012345678901234567891.git"}
|
||||
```
|
||||
|
||||
### Manually track repositories
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5658) in GitLab 14.4.
|
||||
|
||||
The `track-repository` Praefect sub-command adds repositories on disk to the Praefect database to be tracked.
|
||||
|
||||
```shell
|
||||
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml track-repository -virtual-storage <virtual-storage> -repository <repository>
|
||||
```
|
||||
|
||||
- `-virtual-storage` is the virtual storage the repository is located in. Virtual storages are configured in `/etc/gitlab/gitlab.rb` under `praefect['virtual_storages]` and looks like the following:
|
||||
|
||||
```ruby
|
||||
praefect['virtual_storages'] = {
|
||||
'default' => {
|
||||
...
|
||||
},
|
||||
'storage-1' => {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the virtual storage to specify is `default` or `storage-1`.
|
||||
|
||||
- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_types.md#hashed-storage).
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
@hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git
|
||||
```
|
||||
|
||||
- `-authoritative-storage` is the storage we want Praefect to treat as the primary. Required if
|
||||
[per-repository replication](#configure-replication-factor) is set as the replication strategy.
|
||||
|
||||
The command outputs:
|
||||
|
||||
- Results to `STDOUT` and the command's logs.
|
||||
- Errors to `STDERR`.
|
||||
|
||||
This command fails if:
|
||||
|
||||
- The repository is already being tracked by the Praefect database.
|
||||
- The repository does not exist on disk.
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ If the highest number stable branch is unclear, check the [GitLab blog](https://
|
|||
| Software | Minimum version | Notes |
|
||||
| -------- | --------------- | ----- |
|
||||
| [Ruby](#2-ruby) | `2.7` | From GitLab 13.6, Ruby 2.7 is required. Ruby 3.0 is not supported yet (see [the relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/5149) for the current status). You must use the standard MRI implementation of Ruby. We love [JRuby](https://www.jruby.org/) and [Rubinius](https://github.com/rubinius/rubinius#the-rubinius-language-platform), but GitLab needs several Gems that have native extensions. |
|
||||
| [Go](#3-go) | `1.15` | |
|
||||
| [Go](#3-go) | `1.16` | |
|
||||
| [Git](#git) | `2.33.x` | From GitLab 14.4, Git 2.33.x and later is required. It's highly recommended that you use the [Git version provided by Gitaly](#git). |
|
||||
| [Node.js](#4-node) | `12.22.1` | GitLab uses [webpack](https://webpack.js.org/) to compile frontend assets. Node.js 14.x is recommended, as it's faster. You can check which version you're running with `node -v`. You need to update it to a newer version if needed. |
|
||||
|
||||
|
|
@ -251,11 +251,11 @@ page](https://golang.org/dl).
|
|||
# Remove former Go installation folder
|
||||
sudo rm -rf /usr/local/go
|
||||
|
||||
curl --remote-name --progress-bar "https://dl.google.com/go/go1.15.12.linux-amd64.tar.gz"
|
||||
echo 'bbdb935699e0b24d90e2451346da76121b2412d30930eabcd80907c230d098b7 go1.15.12.linux-amd64.tar.gz' | shasum -a256 -c - && \
|
||||
sudo tar -C /usr/local -xzf go1.15.12.linux-amd64.tar.gz
|
||||
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
|
||||
rm go1.15.12.linux-amd64.tar.gz
|
||||
curl --remote-name --progress-bar "https://golang.org/dl/go1.16.10.linux-amd64.tar.gz"
|
||||
echo '414cd18ce1d193769b9e97d2401ad718755ab47816e13b2a1cde203d263b55cf go1.16.10.linux-amd64.tar.gz' | shasum -a256 -c - && \
|
||||
sudo tar -C /usr/local -xzf go1.16.10.linux-amd64.tar.gz
|
||||
sudo ln -sf /usr/local/go/bin/{go,gofmt} /usr/local/bin/
|
||||
rm go1.16.10.linux-amd64.tar.gz
|
||||
```
|
||||
|
||||
## 4. Node
|
||||
|
|
|
|||
|
|
@ -107,12 +107,11 @@ Download and install Go (for Linux, 64-bit):
|
|||
# Remove former Go installation folder
|
||||
sudo rm -rf /usr/local/go
|
||||
|
||||
curl --remote-name --progress-bar "https://dl.google.com/go/go1.15.12.linux-amd64.tar.gz"
|
||||
echo 'bbdb935699e0b24d90e2451346da76121b2412d30930eabcd80907c230d098b7 go1.15.12.linux-amd64.tar.gz' | shasum -a256 -c - && \
|
||||
sudo tar -C /usr/local -xzf go1.15.12.linux-amd64.tar.gz
|
||||
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
|
||||
rm go1.15.12.linux-amd64.tar.gz
|
||||
|
||||
curl --remote-name --progress-bar "https://golang.org/dl/go1.16.10.linux-amd64.tar.gz"
|
||||
echo '414cd18ce1d193769b9e97d2401ad718755ab47816e13b2a1cde203d263b55cf go1.16.10.linux-amd64.tar.gz' | shasum -a256 -c - && \
|
||||
sudo tar -C /usr/local -xzf go1.16.10.linux-amd64.tar.gz
|
||||
sudo ln -sf /usr/local/go/bin/{go,gofmt} /usr/local/bin/
|
||||
rm go1.16.10.linux-amd64.tar.gz
|
||||
```
|
||||
|
||||
### 6. Update Git
|
||||
|
|
|
|||
|
|
@ -14,7 +14,15 @@ module Gitlab
|
|||
self.table_name = 'issue_metrics'
|
||||
|
||||
def self.from_2020
|
||||
where('EXTRACT(YEAR FROM first_mentioned_in_commit_at) > 2019')
|
||||
where(first_mentioned_in_commit_at_condition)
|
||||
end
|
||||
|
||||
def self.first_mentioned_in_commit_at_condition
|
||||
if columns_hash['first_mentioned_in_commit_at'].sql_type == 'timestamp without time zone'
|
||||
'EXTRACT(YEAR FROM first_mentioned_in_commit_at) > 2019'
|
||||
else
|
||||
"EXTRACT(YEAR FROM first_mentioned_in_commit_at at time zone 'UTC') > 2019"
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable Style/Documentation
|
||||
|
|
|
|||
|
|
@ -52,7 +52,10 @@ module Gitlab
|
|||
connection = host.connection
|
||||
return yield connection
|
||||
rescue StandardError => error
|
||||
if serialization_failure?(error)
|
||||
if primary_only?
|
||||
# If we only have primary configured, retrying is pointless
|
||||
raise error
|
||||
elsif serialization_failure?(error)
|
||||
# This error can occur when a query conflicts. See
|
||||
# https://www.postgresql.org/docs/current/static/hot-standby.html#HOT-STANDBY-CONFLICT
|
||||
# for more information.
|
||||
|
|
|
|||
|
|
@ -28,15 +28,21 @@ module Gitlab
|
|||
#
|
||||
# The worker classes aren't constants here, because that would force
|
||||
# Application Settings to be loaded earlier causing failures loading
|
||||
# the environmant in rake tasks
|
||||
# the environment in rake tasks
|
||||
EXEMPT_WORKER_NAMES = ["BackgroundMigrationWorker", "Database::BatchedBackgroundMigrationWorker"].to_set
|
||||
JOB_STATUS_KEY = 'size_limiter'
|
||||
|
||||
class << self
|
||||
def validate!(worker_class, job)
|
||||
return if EXEMPT_WORKER_NAMES.include?(worker_class.to_s)
|
||||
return if validated?(job)
|
||||
|
||||
new(worker_class, job).validate!
|
||||
end
|
||||
|
||||
def validated?(job)
|
||||
job.has_key?(JOB_STATUS_KEY)
|
||||
end
|
||||
end
|
||||
|
||||
DEFAULT_SIZE_LIMIT = 0
|
||||
|
|
@ -64,6 +70,8 @@ module Gitlab
|
|||
end
|
||||
|
||||
def validate!
|
||||
@job[JOB_STATUS_KEY] = 'validated'
|
||||
|
||||
job_args = compress_if_necessary(::Sidekiq.dump_json(@job['args']))
|
||||
|
||||
return if @size_limit == 0
|
||||
|
|
@ -72,8 +80,10 @@ module Gitlab
|
|||
|
||||
exception = exceed_limit_error(job_args)
|
||||
if compress_mode?
|
||||
@job.delete(JOB_STATUS_KEY)
|
||||
raise exception
|
||||
else
|
||||
@job[JOB_STATUS_KEY] = 'tracked'
|
||||
track(exception)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ module QA
|
|||
# with the attribute `data-qa-selector` since such element is not unique when the
|
||||
# `is-focused` class is not set, and it was not possible to find a better solution.
|
||||
def focused_board
|
||||
find('.js-focus-mode-board.is-focused')
|
||||
find('.issue-boards-content.js-focus-mode-board.is-focused')
|
||||
end
|
||||
|
||||
def boards_dropdown
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require Rails.root.join('db', 'post_migrate', '20211004110500_add_temporary_index_to_issue_metrics.rb')
|
||||
|
||||
RSpec.describe Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt, :migration, schema: 20211004110500 do
|
||||
let(:namespaces) { table(:namespaces) }
|
||||
|
|
@ -99,42 +100,67 @@ RSpec.describe Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt, :migrat
|
|||
.perform(issue_metrics.minimum(:issue_id), issue_metrics.maximum(:issue_id))
|
||||
end
|
||||
|
||||
it "marks successful slices as completed" do
|
||||
min_issue_id = issue_metrics.minimum(:issue_id)
|
||||
max_issue_id = issue_metrics.maximum(:issue_id)
|
||||
shared_examples 'fixes first_mentioned_in_commit_at' do
|
||||
it "marks successful slices as completed" do
|
||||
min_issue_id = issue_metrics.minimum(:issue_id)
|
||||
max_issue_id = issue_metrics.maximum(:issue_id)
|
||||
|
||||
expect(subject).to receive(:mark_job_as_succeeded).with(min_issue_id, max_issue_id)
|
||||
expect(subject).to receive(:mark_job_as_succeeded).with(min_issue_id, max_issue_id)
|
||||
|
||||
subject.perform(min_issue_id, max_issue_id)
|
||||
end
|
||||
subject.perform(min_issue_id, max_issue_id)
|
||||
end
|
||||
|
||||
context 'when the persisted first_mentioned_in_commit_at is later than the first commit authored_date' do
|
||||
it 'updates the issue_metrics record' do
|
||||
record1 = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: Time.current)
|
||||
record2 = issue_metrics.create!(issue_id: issue2.id, first_mentioned_in_commit_at: Time.current)
|
||||
context 'when the persisted first_mentioned_in_commit_at is later than the first commit authored_date' do
|
||||
it 'updates the issue_metrics record' do
|
||||
record1 = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: Time.current)
|
||||
record2 = issue_metrics.create!(issue_id: issue2.id, first_mentioned_in_commit_at: Time.current)
|
||||
|
||||
run_migration
|
||||
record1.reload
|
||||
record2.reload
|
||||
run_migration
|
||||
record1.reload
|
||||
record2.reload
|
||||
|
||||
expect(record1.first_mentioned_in_commit_at).to be_within(2.seconds).of(commit2.authored_date)
|
||||
expect(record2.first_mentioned_in_commit_at).to be_within(2.seconds).of(commit3.authored_date)
|
||||
expect(record1.first_mentioned_in_commit_at).to be_within(2.seconds).of(commit2.authored_date)
|
||||
expect(record2.first_mentioned_in_commit_at).to be_within(2.seconds).of(commit3.authored_date)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the persisted first_mentioned_in_commit_at is earlier than the first commit authored_date' do
|
||||
it 'does not update the issue_metrics record' do
|
||||
record = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: 20.days.ago)
|
||||
|
||||
expect { run_migration }.not_to change { record.reload.first_mentioned_in_commit_at }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the first_mentioned_in_commit_at is null' do
|
||||
it 'does nothing' do
|
||||
record = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: nil)
|
||||
|
||||
expect { run_migration }.not_to change { record.reload.first_mentioned_in_commit_at }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the persisted first_mentioned_in_commit_at is earlier than the first commit authored_date' do
|
||||
it 'does not update the issue_metrics record' do
|
||||
record = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: 20.days.ago)
|
||||
|
||||
expect { run_migration }.not_to change { record.reload.first_mentioned_in_commit_at }
|
||||
end
|
||||
describe 'running the migration when first_mentioned_in_commit_at is timestamp without time zone' do
|
||||
it_behaves_like 'fixes first_mentioned_in_commit_at'
|
||||
end
|
||||
|
||||
context 'when the first_mentioned_in_commit_at is null' do
|
||||
it 'does nothing' do
|
||||
record = issue_metrics.create!(issue_id: issue1.id, first_mentioned_in_commit_at: nil)
|
||||
describe 'running the migration when first_mentioned_in_commit_at is timestamp with time zone' do
|
||||
around do |example|
|
||||
AddTemporaryIndexToIssueMetrics.new.down
|
||||
|
||||
expect { run_migration }.not_to change { record.reload.first_mentioned_in_commit_at }
|
||||
ActiveRecord::Base.connection.execute "ALTER TABLE issue_metrics ALTER first_mentioned_in_commit_at type timestamp with time zone"
|
||||
Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt::TmpIssueMetrics.reset_column_information
|
||||
AddTemporaryIndexToIssueMetrics.new.up
|
||||
|
||||
example.run
|
||||
|
||||
AddTemporaryIndexToIssueMetrics.new.down
|
||||
ActiveRecord::Base.connection.execute "ALTER TABLE issue_metrics ALTER first_mentioned_in_commit_at type timestamp without time zone"
|
||||
Gitlab::BackgroundMigration::FixFirstMentionedInCommitAt::TmpIssueMetrics.reset_column_information
|
||||
AddTemporaryIndexToIssueMetrics.new.up
|
||||
end
|
||||
|
||||
it_behaves_like 'fixes first_mentioned_in_commit_at'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -140,6 +140,24 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
|
|||
lb.read { raise conflict_error }
|
||||
end
|
||||
|
||||
context 'only primary is configured' do
|
||||
let(:lb) do
|
||||
config = Gitlab::Database::LoadBalancing::Configuration.new(ActiveRecord::Base)
|
||||
allow(config).to receive(:load_balancing_enabled?).and_return(false)
|
||||
|
||||
described_class.new(config)
|
||||
end
|
||||
|
||||
it 'does not retry a query on connection error if only the primary is configured' do
|
||||
host = double(:host, query_cache_enabled: true)
|
||||
|
||||
allow(lb).to receive(:host).and_return(host)
|
||||
allow(host).to receive(:connection).and_raise(PG::UnableToSend)
|
||||
|
||||
expect { lb.read }.to raise_error(PG::UnableToSend)
|
||||
end
|
||||
end
|
||||
|
||||
it 'uses the primary if no secondaries are available' do
|
||||
allow(lb).to receive(:connection_error?).and_return(true)
|
||||
|
||||
|
|
|
|||
|
|
@ -187,37 +187,51 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai
|
|||
|
||||
context 'when size limit is 0' do
|
||||
let(:size_limit) { 0 }
|
||||
let(:job) { job_payload(a: 'a' * 300) }
|
||||
|
||||
it 'does not track jobs' do
|
||||
expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
|
||||
|
||||
validate.call(TestSizeLimiterWorker, job_payload(a: 'a' * 300))
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
end
|
||||
|
||||
it 'does not raise exception' do
|
||||
expect do
|
||||
validate.call(TestSizeLimiterWorker, job_payload(a: 'a' * 300))
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it 'marks the job as validated' do
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
|
||||
expect(job['size_limiter']).to eq('validated')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job size is bigger than size limit' do
|
||||
let(:size_limit) { 50 }
|
||||
let(:job) { job_payload(a: 'a' * 300) }
|
||||
|
||||
it 'tracks job' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
|
||||
be_a(Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError)
|
||||
)
|
||||
|
||||
validate.call(TestSizeLimiterWorker, job_payload(a: 'a' * 100))
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
end
|
||||
|
||||
it 'does not raise an exception' do
|
||||
expect do
|
||||
validate.call(TestSizeLimiterWorker, job_payload(a: 'a' * 300))
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it 'marks the job as tracked' do
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
|
||||
expect(job['size_limiter']).to eq('tracked')
|
||||
end
|
||||
|
||||
context 'when the worker has big_payload attribute' do
|
||||
before do
|
||||
worker_class.big_payload!
|
||||
|
|
@ -238,20 +252,33 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai
|
|||
validate.call('TestSizeLimiterWorker', job_payload(a: 'a' * 300))
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it 'marks the job as validated' do
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
|
||||
expect(job['size_limiter']).to eq('validated')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job size is less than size limit' do
|
||||
let(:size_limit) { 50 }
|
||||
let(:job) { job_payload(a: 'a') }
|
||||
|
||||
it 'does not track job' do
|
||||
expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
|
||||
|
||||
validate.call(TestSizeLimiterWorker, job_payload(a: 'a'))
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
end
|
||||
|
||||
it 'does not raise an exception' do
|
||||
expect { validate.call(TestSizeLimiterWorker, job_payload(a: 'a')) }.not_to raise_error
|
||||
expect { validate.call(TestSizeLimiterWorker, job) }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'marks the job as validated' do
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
|
||||
expect(job['size_limiter']).to eq('validated')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -266,7 +293,13 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai
|
|||
|
||||
it 'does not raise an exception' do
|
||||
expect(::Gitlab::SidekiqMiddleware::SizeLimiter::Compressor).not_to receive(:compress)
|
||||
expect { validate.call(TestSizeLimiterWorker, job_payload(a: 'a')) }.not_to raise_error
|
||||
expect { validate.call(TestSizeLimiterWorker, job) }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'marks the job as validated' do
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
|
||||
expect(job['size_limiter']).to eq('validated')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -283,6 +316,12 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai
|
|||
validate.call(TestSizeLimiterWorker, job)
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it 'marks the job as validated' do
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
|
||||
expect(job['size_limiter']).to eq('validated')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job size is bigger than compression threshold and size limit is 0' do
|
||||
|
|
@ -299,6 +338,12 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai
|
|||
validate.call(TestSizeLimiterWorker, job)
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it 'marks the job as validated' do
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
|
||||
expect(job['size_limiter']).to eq('validated')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the job was already compressed' do
|
||||
|
|
@ -326,6 +371,8 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai
|
|||
expect do
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
end.to raise_error(Gitlab::SidekiqMiddleware::SizeLimiter::ExceedLimitError)
|
||||
|
||||
expect(job['size_limiter']).to eq(nil)
|
||||
end
|
||||
|
||||
it 'does not raise an exception when the worker allows big payloads' do
|
||||
|
|
@ -338,6 +385,8 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai
|
|||
expect do
|
||||
validate.call(TestSizeLimiterWorker, job)
|
||||
end.not_to raise_error
|
||||
|
||||
expect(job['size_limiter']).to eq('validated')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -363,6 +412,29 @@ RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Validator, :aggregate_fai
|
|||
validate.call(class_name.constantize, job_payload)
|
||||
end
|
||||
end
|
||||
|
||||
it "skips jobs that are already validated" do
|
||||
expect(described_class).to receive(:new).once.and_call_original
|
||||
|
||||
job = job_payload
|
||||
|
||||
described_class.validate!(TestSizeLimiterWorker, job)
|
||||
described_class.validate!(TestSizeLimiterWorker, job)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.validated?' do
|
||||
let(:job) { job_payload }
|
||||
|
||||
it 'returns true when the job is already validated' do
|
||||
described_class.validate!(TestSizeLimiterWorker, job)
|
||||
|
||||
expect(described_class.validated?(job)).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns false when job is not yet validated' do
|
||||
expect(described_class.validated?(job)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#validate!' do
|
||||
|
|
|
|||
|
|
@ -8,4 +8,46 @@ RSpec.describe LegacyDiffNote do
|
|||
|
||||
it { is_expected.to eq('note') }
|
||||
end
|
||||
|
||||
describe 'callbacks' do
|
||||
describe '#set_diff' do
|
||||
let(:note) do
|
||||
build(:legacy_diff_note_on_merge_request, st_diff: '_st_diff_').tap do |record|
|
||||
record.instance_variable_set(:@diff, {})
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not importing' do
|
||||
it 'updates st_diff' do
|
||||
note.save!(validate: false)
|
||||
|
||||
expect(note.st_diff).to eq({})
|
||||
end
|
||||
end
|
||||
|
||||
context 'when importing' do
|
||||
before do
|
||||
note.importing = true
|
||||
end
|
||||
|
||||
it 'does not update st_diff' do
|
||||
note.save!(validate: false)
|
||||
|
||||
expect(note.st_diff).to eq('_st_diff_')
|
||||
end
|
||||
|
||||
context 'when st_diff is blank' do
|
||||
before do
|
||||
note.st_diff = nil
|
||||
end
|
||||
|
||||
it 'updates st_diff' do
|
||||
note.save!(validate: false)
|
||||
|
||||
expect(note.st_diff).to eq({})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -163,27 +163,32 @@ RSpec.describe ProtectedBranch do
|
|||
expect(described_class.protected?(project, 'staging/some-branch')).to eq(false)
|
||||
end
|
||||
|
||||
it 'returns false when branch name is nil' do
|
||||
expect(described_class.protected?(project, nil)).to eq(false)
|
||||
end
|
||||
|
||||
context 'with caching', :use_clean_rails_memory_store_caching do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:protected_branch) { create(:protected_branch, project: project, name: "jawn") }
|
||||
let_it_be(:protected_branch) { create(:protected_branch, project: project, name: "“jawn”") }
|
||||
|
||||
before do
|
||||
allow(described_class).to receive(:matching).once.and_call_original
|
||||
allow(described_class).to receive(:matching).with(protected_branch.name, protected_refs: anything).once.and_call_original
|
||||
|
||||
# the original call works and warms the cache
|
||||
described_class.protected?(project, 'jawn')
|
||||
described_class.protected?(project, protected_branch.name)
|
||||
end
|
||||
|
||||
it 'correctly invalidates a cache' do
|
||||
expect(described_class).to receive(:matching).once.and_call_original
|
||||
expect(described_class).to receive(:matching).with(protected_branch.name, protected_refs: anything).once.and_call_original
|
||||
|
||||
create(:protected_branch, project: project, name: "bar")
|
||||
# the cache is invalidated because the project has been "updated"
|
||||
expect(described_class.protected?(project, 'jawn')).to eq(true)
|
||||
expect(described_class.protected?(project, protected_branch.name)).to eq(true)
|
||||
end
|
||||
|
||||
it 'correctly uses the cached version' do
|
||||
expect(described_class).not_to receive(:matching)
|
||||
expect(described_class.protected?(project, 'jawn')).to eq(true)
|
||||
expect(described_class.protected?(project, protected_branch.name)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue