Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-05-13 12:10:02 +00:00
parent 19325d74c2
commit 3748ae5cbb
27 changed files with 349 additions and 69 deletions

View File

@ -330,3 +330,6 @@ Dangerfile @gl-quality/eng-prod
[Workhorse]
/workhorse/ @jacobvosmaer-gitlab @nick.thomas @nolith @patrickbajao
[Application Security]
/lib/gitlab/content_security_policy/ @gitlab-com/gl-security/appsec

View File

@ -140,6 +140,7 @@ export default {
</div>
<div class="gl-p-2">
<gl-button
is-unsafe-link
:href="codeDownloadUrl"
:title="$options.i18n.downloadButton"
icon="download"

View File

@ -68,7 +68,7 @@ export default {
<local-storage-sync v-model="isExpanded" :storage-key="$options.localDrawerKey" as-json>
<aside
aria-live="polite"
class="gl-fixed gl-right-0 gl-bg-gray-10 gl-shadow-drawer gl-transition-medium gl-border-l-solid gl-border-1 gl-border-gray-100 gl-h-full gl-z-index-9999 gl-overflow-y-auto"
class="gl-fixed gl-right-0 gl-bg-gray-10 gl-shadow-drawer gl-transition-medium gl-border-l-solid gl-border-1 gl-border-gray-100 gl-h-full gl-z-index-3 gl-overflow-y-auto"
:style="rootStyle"
>
<gl-button

View File

@ -15,7 +15,7 @@ export default {
mixins: [timeagoMixin],
props: {
time: {
type: String,
type: [String, Number],
required: true,
},
tooltipPlacement: {

View File

@ -372,7 +372,7 @@ $system-note-svg-size: 16px;
top: $mr-tabs-height + $header-height;
.with-performance-bar & {
top: 126px;
top: 123px;
}
}

View File

@ -2539,7 +2539,7 @@ class Project < ApplicationRecord
def default_branch_or_main
return default_branch if default_branch
Feature.enabled?(:main_branch_over_master, self, default_enabled: :yaml) ? 'main' : 'master'
Gitlab::DefaultBranch.value(project: self)
end
def ci_config_path_or_default

View File

@ -20,7 +20,6 @@ class Snippet < ApplicationRecord
extend ::Gitlab::Utils::Override
MAX_FILE_COUNT = 10
MASTER_BRANCH = 'master'
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description
@ -316,19 +315,19 @@ class Snippet < ApplicationRecord
override :default_branch
def default_branch
super || MASTER_BRANCH
super || Gitlab::DefaultBranch.value(project: project)
end
def repository_storage
snippet_repository&.shard_name || Repository.pick_storage_shard
end
# Repositories are created by default with the `master` branch.
# Repositories are created with a default branch. This branch
# can be different from the default branch set in the platform.
# This method changes the `HEAD` file to point to the existing
# default branch in case it's not master.
# default branch in case it's different.
def change_head_to_default_branch
return unless repository.exists?
return if default_branch == MASTER_BRANCH
# All snippets must have at least 1 file. Therefore, if
# `HEAD` is empty is because it's pointing to the wrong
# default branch

View File

@ -0,0 +1,5 @@
---
title: Reschedule DropInvalidVulnerabilities and track jobs
merge_request: 61491
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Lowers the z-indexing of the pipeline drawer
merge_request: 61683
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Omit trailing slash when proxying pre-authorized routes with no suffix
merge_request: 61638
author:
type: fixed

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
class ScheduleDropInvalidVulnerabilities2 < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
MIGRATION = 'DropInvalidVulnerabilities'
DELAY_INTERVAL = 2.minutes.to_i
BATCH_SIZE = 10_000
disable_ddl_transaction!
def up
queue_background_migration_jobs_by_range_at_intervals(
define_batchable_model('vulnerabilities'),
MIGRATION,
DELAY_INTERVAL,
batch_size: BATCH_SIZE,
track_jobs: true
)
end
def down
# no-op
end
end

View File

@ -0,0 +1 @@
def18f68ad71a1581452d52d331d2fd99ec9a3eb9b8e2fd111277eda498169fa

View File

@ -21,6 +21,17 @@ class Gitlab::BackgroundMigration::DropInvalidVulnerabilities
.left_joins(:findings)
.where(vulnerability_occurrences: { vulnerability_id: nil })
.delete_all
mark_job_as_succeeded(start_id, end_id)
end
# rubocop: enable CodeReuse/ActiveRecord
private
def mark_job_as_succeeded(*arguments)
Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
'DropInvalidVulnerabilities',
arguments
)
end
end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
# Class is used while we're migrating from master to main
module Gitlab
module DefaultBranch
def self.value(project: nil)
Feature.enabled?(:main_branch_over_master, project, default_enabled: :yaml) ? 'main' : 'master'
end
end
end

View File

@ -5054,25 +5054,22 @@ msgstr ""
msgid "BillingPlan|Upgrade for free"
msgstr ""
msgid "Billings|As a user on a free or trial namespace, you'll need to verify your account with a credit card to run pipelines. This is required to help prevent cryptomining attacks on GitLab infrastructure. %{strongStart}GitLab will not charge or store your credit card, it will only be used for validation.%{strongEnd}"
msgid "Billings|To use free pipeline minutes, you'll need to validate your account with a credit card. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge or store your credit card, it will only be used for validation.%{strongEnd}"
msgstr ""
msgid "Billings|To discourage and reduce abuse GitLab will require some users to provide a valid credit card to use free pipeline minutes on GitLab.com. To use free pipeline minutes, you will need to validate your account with a credit card. %{strongStart}GitLab will not add permanent charges to your credit card as we will only use it for validation.%{strongEnd}"
msgid "Billings|User successfully validated"
msgstr ""
msgid "Billings|User Verification Required"
msgid "Billings|User validation required"
msgstr ""
msgid "Billings|User successfully verified"
msgid "Billings|Validate account"
msgstr ""
msgid "Billings|Verify User Account"
msgid "Billings|Validate user account"
msgstr ""
msgid "Billings|Verify account"
msgstr ""
msgid "Billings|Your user account has been successfully verified. You will now be able to run pipelines on any free or trial namespace."
msgid "Billings|Your user account has been successfully validated. You can now use free pipeline minutes."
msgstr ""
msgid "Billing|An email address is only visible for users with public emails."

View File

@ -53,7 +53,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.195.0",
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "29.21.0",
"@gitlab/ui": "29.23.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4",

View File

@ -94,7 +94,10 @@ describe('Pipelines', () => {
beforeAll(() => {
origWindowLocation = window.location;
delete window.location;
window.location = { search: '' };
window.location = {
search: '',
protocol: 'https:',
};
});
afterAll(() => {

View File

@ -1,28 +1,36 @@
import { shallowMount } from '@vue/test-utils';
import timezoneMock from 'timezone-mock';
import { formatDate, getTimeago } from '~/lib/utils/datetime_utility';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
describe('Time ago with tooltip component', () => {
let vm;
const buildVm = (propsData = {}, scopedSlots = {}) => {
vm = shallowMount(TimeAgoTooltip, {
propsData,
scopedSlots,
});
};
const timestamp = '2017-05-08T14:57:39.781Z';
const timeAgoTimestamp = getTimeago().format(timestamp);
const defaultProps = {
time: timestamp,
};
const buildVm = (props = {}, scopedSlots = {}) => {
vm = shallowMount(TimeAgoTooltip, {
propsData: {
...defaultProps,
...props,
},
scopedSlots,
});
};
afterEach(() => {
vm.destroy();
timezoneMock.unregister();
});
it('should render timeago with a bootstrap tooltip', () => {
buildVm({
time: timestamp,
});
buildVm();
expect(vm.attributes('title')).toEqual(formatDate(timestamp));
expect(vm.text()).toEqual(timeAgoTimestamp);
@ -30,7 +38,6 @@ describe('Time ago with tooltip component', () => {
it('should render provided html class', () => {
buildVm({
time: timestamp,
cssClass: 'foo',
});
@ -38,14 +45,58 @@ describe('Time ago with tooltip component', () => {
});
it('should render with the datetime attribute', () => {
buildVm({ time: timestamp });
buildVm();
expect(vm.attributes('datetime')).toEqual(timestamp);
});
it('should render provided scope content with the correct timeAgo string', () => {
buildVm({ time: timestamp }, { default: `<span>The time is {{ props.timeAgo }}</span>` });
buildVm(null, { default: `<span>The time is {{ props.timeAgo }}</span>` });
expect(vm.text()).toEqual(`The time is ${timeAgoTimestamp}`);
});
describe('number based timestamps', () => {
// Store a date object before we mock the TZ
const date = new Date();
describe('with default TZ', () => {
beforeEach(() => {
buildVm({ time: date.getTime() });
});
it('handled correctly', () => {
expect(vm.text()).toEqual(getTimeago().format(date.getTime()));
});
});
describe.each`
timezone | offset
${'US/Pacific'} | ${420}
${'US/Eastern'} | ${240}
${'Brazil/East'} | ${180}
${'UTC'} | ${-0}
${'Europe/London'} | ${-60}
`('with different client vs server TZ', ({ timezone, offset }) => {
let tzDate;
beforeEach(() => {
timezoneMock.register(timezone);
// Date object with mocked TZ
tzDate = new Date();
buildVm({ time: date.getTime() });
});
it('the date object should have correct timezones', () => {
expect(tzDate.getTimezoneOffset()).toBe(offset);
});
it('timeago should handled the date correctly', () => {
// getTime() should always handle the TZ, which allows for us to validate the date objects represent
// the same date and time regardless of the TZ.
expect(vm.text()).toEqual(getTimeago().format(date.getTime()));
expect(vm.text()).toEqual(getTimeago().format(tzDate.getTime()));
});
});
});
});

View File

@ -201,4 +201,25 @@ RSpec.describe Banzai::Filter::References::UserReferenceFilter do
expect(filter.send(:usernames)).to eq([user.username])
end
end
context 'checking N+1' do
let(:user2) { create(:user) }
let(:group) { create(:group) }
let(:reference2) { user2.to_reference }
let(:reference3) { group.to_reference }
it 'does not have N+1 per multiple user references', :use_sql_query_cache do
markdown = "#{reference}"
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
reference_filter(markdown)
end.count
markdown = "#{reference} @qwertyuiopzx @wertyuio @ertyu @rtyui #{reference2} #{reference3}"
expect do
reference_filter(markdown)
end.not_to exceed_all_query_limit(control_count)
end
end
end

View File

@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::DropInvalidVulnerabilities, schema: 20201110110454 do
let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
let_it_be(:users) { table(:users) }
let_it_be(:user) { create_user! }
@ -47,16 +48,34 @@ RSpec.describe Gitlab::BackgroundMigration::DropInvalidVulnerabilities, schema:
)
end
subject { described_class.new.perform(vulnerability_with_finding.id, vulnerability_without_finding.id) }
let(:succeeded_status) { 1 }
let(:pending_status) { 0 }
it 'drops Vulnerabilities without any Findings' do
expect(vulnerabilities.pluck(:id)).to eq([vulnerability_with_finding.id, vulnerability_without_finding.id])
expect { subject }.to change(vulnerabilities, :count).by(-1)
expect { subject.perform(vulnerability_with_finding.id, vulnerability_without_finding.id) }.to change(vulnerabilities, :count).by(-1)
expect(vulnerabilities.pluck(:id)).to eq([vulnerability_with_finding.id])
end
it 'marks jobs as done' do
background_migration_jobs.create!(
class_name: 'DropInvalidVulnerabilities',
arguments: [vulnerability_with_finding.id, vulnerability_with_finding.id]
)
background_migration_jobs.create!(
class_name: 'DropInvalidVulnerabilities',
arguments: [vulnerability_without_finding.id, vulnerability_without_finding.id]
)
subject.perform(vulnerability_with_finding.id, vulnerability_with_finding.id)
expect(background_migration_jobs.first.status).to eq(succeeded_status)
expect(background_migration_jobs.second.status).to eq(pending_status)
end
private
def create_vulnerability!(project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0)

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'spec_helper'
# We disabled main_branch_over_master feature for tests
# In order to have consistent branch usages
# When we migrate the branch name to main, we can enable it
RSpec.describe Gitlab::DefaultBranch do
context 'main_branch_over_master is enabled' do
before do
stub_feature_flags(main_branch_over_master: true)
end
it 'returns main' do
expect(described_class.value).to eq('main')
end
end
context 'main_branch_over_master is disabled' do
it 'returns master' do
expect(described_class.value).to eq('master')
end
end
end

View File

@ -0,0 +1,120 @@
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20210511142748_schedule_drop_invalid_vulnerabilities2.rb')
RSpec.describe ScheduleDropInvalidVulnerabilities2, :migration do
let_it_be(:background_migration_jobs) { table(:background_migration_jobs) }
let_it_be(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
let_it_be(:users) { table(:users) }
let_it_be(:user) { create_user! }
let_it_be(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) }
let_it_be(:scanners) { table(:vulnerability_scanners) }
let_it_be(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
let_it_be(:different_scanner) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
let_it_be(:vulnerabilities) { table(:vulnerabilities) }
let_it_be(:vulnerability_with_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
let_it_be(:vulnerability_without_finding) do
create_vulnerability!(
project_id: project.id,
author_id: user.id
)
end
let_it_be(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
let_it_be(:primary_identifier) do
vulnerability_identifiers.create!(
project_id: project.id,
external_type: 'uuid-v5',
external_id: 'uuid-v5',
fingerprint: '7e394d1b1eb461a7406d7b1e08f057a1cf11287a',
name: 'Identifier for UUIDv5')
end
let_it_be(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
let_it_be(:finding) do
create_finding!(
vulnerability_id: vulnerability_with_finding.id,
project_id: project.id,
scanner_id: scanner.id,
primary_identifier_id: primary_identifier.id
)
end
before do
stub_const("#{described_class}::BATCH_SIZE", 1)
end
around do |example|
freeze_time { Sidekiq::Testing.fake! { example.run } }
end
it 'schedules background migrations' do
migrate!
expect(background_migration_jobs.count).to eq(2)
expect(background_migration_jobs.first.arguments).to eq([vulnerability_with_finding.id, vulnerability_with_finding.id])
expect(background_migration_jobs.second.arguments).to eq([vulnerability_without_finding.id, vulnerability_without_finding.id])
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, vulnerability_with_finding.id, vulnerability_with_finding.id)
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(4.minutes, vulnerability_without_finding.id, vulnerability_without_finding.id)
end
private
def create_vulnerability!(project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0)
vulnerabilities.create!(
project_id: project_id,
author_id: author_id,
title: title,
severity: severity,
confidence: confidence,
report_type: report_type
)
end
# rubocop:disable Metrics/ParameterLists
def create_finding!(
vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
name: "test", severity: 7, confidence: 7, report_type: 0,
project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
vulnerabilities_findings.create!(
vulnerability_id: vulnerability_id,
project_id: project_id,
name: name,
severity: severity,
confidence: confidence,
report_type: report_type,
project_fingerprint: project_fingerprint,
scanner_id: scanner_id,
primary_identifier_id: primary_identifier_id,
location_fingerprint: location_fingerprint,
metadata_version: metadata_version,
raw_metadata: raw_metadata,
uuid: uuid
)
end
# rubocop:enable Metrics/ParameterLists
def create_user!(name: "Example User", email: "user@example.com", user_type: nil)
users.create!(
name: name,
email: email,
username: name,
projects_limit: 0,
user_type: user_type,
confirmed_at: Time.current
)
end
end

View File

@ -6923,11 +6923,6 @@ RSpec.describe Project, factory_default: :keep do
describe '#default_branch_or_main' do
let(:project) { create(:project, :repository) }
before do
# Stubbing it as true since the FF disabled for tests globally
stub_feature_flags(main_branch_over_master: true)
end
it 'returns default branch' do
expect(project.default_branch_or_main).to eq(project.default_branch)
end
@ -6935,18 +6930,8 @@ RSpec.describe Project, factory_default: :keep do
context 'when default branch is nil' do
let(:project) { create(:project, :empty_repo) }
it 'returns main' do
expect(project.default_branch_or_main).to eq('main')
end
context 'main_branch_over_master is disabled' do
before do
stub_feature_flags(main_branch_over_master: false)
end
it 'returns master' do
expect(project.default_branch_or_main).to eq('master')
end
it 'returns Gitlab::DefaultBranch.value' do
expect(project.default_branch_or_main).to eq(Gitlab::DefaultBranch.value)
end
end
end

View File

@ -826,18 +826,6 @@ RSpec.describe Snippet do
allow(Gitlab::CurrentSettings).to receive(:default_branch_name).and_return(default_branch)
end
context 'when default branch in settings is "master"' do
let(:default_branch) { 'master' }
it 'does nothing' do
expect(File.read(head_path).squish).to eq 'ref: refs/heads/master'
expect(snippet.repository.raw_repository).not_to receive(:write_ref)
subject
end
end
context 'when default branch in settings is different from "master"' do
let(:default_branch) { 'main' }

View File

@ -168,7 +168,10 @@ func singleJoiningSlash(a, b string) string {
// joinURLPath is taken from reverseproxy.go:joinURLPath
func joinURLPath(a *url.URL, b string) (path string, rawpath string) {
if a.RawPath == "" && b == "" {
// Avoid adding a trailing slash if the suffix is empty
if b == "" {
return a.Path, a.RawPath
} else if a.RawPath == "" {
return singleJoiningSlash(a.Path, b), ""
}

View File

@ -536,7 +536,11 @@ func TestApiContentTypeBlock(t *testing.T) {
func TestAPIFalsePositivesAreProxied(t *testing.T) {
goodResponse := []byte(`<html></html>`)
ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(secret.RequestHeader) != "" && r.Method != "GET" {
url := r.URL.String()
if url[len(url)-1] == '/' {
w.WriteHeader(500)
w.Write([]byte("PreAuthorize request included a trailing slash"))
} else if r.Header.Get(secret.RequestHeader) != "" && r.Method != "GET" {
w.WriteHeader(500)
w.Write([]byte("non-GET request went through PreAuthorize handler"))
} else {

View File

@ -907,10 +907,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@29.21.0":
version "29.21.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.21.0.tgz#f91169d693bc92109deadc412cf6ed76a8ae07ad"
integrity sha512-fAqhQjLXsl6JRM56NewFqtYowzjvne7IsM6F+aD4C/9OF9u81bXfa5do1o5usl030Hz9e8+PTwkXWwLTe0Nz8w==
"@gitlab/ui@29.23.0":
version "29.23.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.23.0.tgz#cf0bf2ed85c679a618f0f78d7f5d4e4346663b55"
integrity sha512-ELMHentJ9v5WXlAAuSr5s5kYu8KW710N4qCnzdt09g+McSsvXUhOx2ltM0d4rZe+TZwgwAUndwCUDP+dHqDtrA==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"