提交 a1d73247 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 97cdd36a
......@@ -282,6 +282,14 @@ Cop/ActiveRecordAssociationReload:
Gitlab/AvoidFeatureGet:
Enabled: true
RSpec/TimecopFreeze:
Enabled: false
AutoCorrect: true
Include:
- 'spec/**/*.rb'
- 'ee/spec/**/*.rb'
- 'qa/spec/**/*.rb'
Naming/PredicateName:
Enabled: true
Exclude:
......
<script>
import { GlLink, GlModalDirective, GlSprintf } from '@gitlab/ui';
import { GlLink, GlModalDirective, GlSprintf, GlButton } from '@gitlab/ui';
import { s__, __, sprintf } from '~/locale';
import eventHub from '../event_hub';
import identicon from '../../vue_shared/components/identicon.vue';
import loadingButton from '../../vue_shared/components/loading_button.vue';
import UninstallApplicationButton from './uninstall_application_button.vue';
import UninstallApplicationConfirmationModal from './uninstall_application_confirmation_modal.vue';
import UpdateApplicationConfirmationModal from './update_application_confirmation_modal.vue';
......@@ -12,7 +11,7 @@ import { APPLICATION_STATUS, ELASTIC_STACK } from '../constants';
export default {
components: {
loadingButton,
GlButton,
identicon,
GlLink,
GlSprintf,
......@@ -390,16 +389,18 @@ export default {
</div>
<template v-if="updateAvailable || updateFailed || isUpdating">
<template v-if="updatingNeedsConfirmation">
<loading-button
<gl-button
v-gl-modal-directive="updateModalId"
class="btn btn-primary js-cluster-application-update-button mt-2"
class="js-cluster-application-update-button mt-2"
variant="info"
category="primary"
:loading="isUpdating"
:disabled="isUpdating"
:label="updateButtonLabel"
data-qa-selector="update_button_with_confirmation"
:data-qa-application="id"
/>
>
{{ updateButtonLabel }}
</gl-button>
<update-application-confirmation-modal
:application="id"
:application-title="title"
......@@ -407,16 +408,19 @@ export default {
/>
</template>
<loading-button
<gl-button
v-else
class="btn btn-primary js-cluster-application-update-button mt-2"
class="js-cluster-application-update-button mt-2"
variant="info"
category="primary"
:loading="isUpdating"
:disabled="isUpdating"
:label="updateButtonLabel"
data-qa-selector="update_button"
:data-qa-application="id"
@click="updateConfirmed"
/>
>
{{ updateButtonLabel }}
</gl-button>
</template>
</div>
</div>
......@@ -431,16 +435,18 @@ export default {
}}</a>
</div>
<div class="btn-group table-action-buttons">
<loading-button
<gl-button
v-if="displayInstallButton"
:loading="installButtonLoading"
:disabled="disabled || installButtonDisabled"
:label="installButtonLabel"
class="js-cluster-application-install-button"
variant="default"
data-qa-selector="install_button"
:data-qa-application="id"
@click="installClicked"
/>
>
{{ installButtonLabel }}
</gl-button>
<uninstall-application-button
v-if="displayUninstallButton"
v-gl-modal-directive="uninstallModalId"
......
<script>
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { GlButton } from '@gitlab/ui';
import { APPLICATION_STATUS } from '~/clusters/constants';
import { __ } from '~/locale';
......@@ -7,7 +7,7 @@ const { UPDATING, UNINSTALLING } = APPLICATION_STATUS;
export default {
components: {
LoadingButton,
GlButton,
},
props: {
status: {
......@@ -30,5 +30,7 @@ export default {
</script>
<template>
<loading-button :label="label" :disabled="disabled" :loading="loading" />
<gl-button :disabled="disabled" variant="default" :loading="loading">
{{ label }}
</gl-button>
</template>
export const KUBERNETES_VERSIONS = [{ name: '1.14', value: '1.14' }];
export const KUBERNETES_VERSIONS = [
{ name: '1.14', value: '1.14' },
{ name: '1.15', value: '1.15' },
{ name: '1.16', value: '1.16', default: true },
{ name: '1.17', value: '1.17' },
];
......@@ -56,6 +56,7 @@ export const createCluster = ({ dispatch, state }) => {
environment_scope: state.environmentScope,
managed: state.gitlabManagedCluster,
provider_aws_attributes: {
kubernetes_version: state.kubernetesVersion,
region: state.selectedRegion,
vpc_id: state.selectedVpc,
subnet_ids: state.selectedSubnet,
......
import { KUBERNETES_VERSIONS } from '../constants';
const [{ value: kubernetesVersion }] = KUBERNETES_VERSIONS;
const kubernetesVersion = KUBERNETES_VERSIONS.find(version => version.default).value;
export default () => ({
createRolePath: null,
......
/* eslint-disable consistent-return */
import $ from 'jquery';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { isObject } from '~/lib/utils/type_utility';
const BLUR_KEYCODES = [27, 40];
const HAS_VALUE_CLASS = 'has-value';
export class GitLabDropdownFilter {
constructor(input, options) {
let ref;
let timeout;
this.input = input;
this.options = options;
// eslint-disable-next-line no-cond-assign
this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true;
const $inputContainer = this.input.parent();
const $clearButton = $inputContainer.find('.js-dropdown-input-clear');
$clearButton.on('click', e => {
// Clear click
e.preventDefault();
e.stopPropagation();
return this.input
.val('')
.trigger('input')
.focus();
});
// Key events
timeout = '';
this.input
.on('keydown', e => {
const keyCode = e.which;
if (keyCode === 13 && !options.elIsInput) {
e.preventDefault();
}
})
.on('input', () => {
if (this.input.val() !== '' && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.addClass(HAS_VALUE_CLASS);
} else if (this.input.val() === '' && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.removeClass(HAS_VALUE_CLASS);
}
// Only filter asynchronously only if option remote is set
if (this.options.remote) {
clearTimeout(timeout);
// eslint-disable-next-line no-return-assign
return (timeout = setTimeout(() => {
$inputContainer.parent().addClass('is-loading');
return this.options.query(this.input.val(), data => {
$inputContainer.parent().removeClass('is-loading');
return this.options.callback(data);
});
}, 250));
}
return this.filter(this.input.val());
});
}
static shouldBlur(keyCode) {
return BLUR_KEYCODES.indexOf(keyCode) !== -1;
}
filter(searchText) {
let group;
let results;
let tmp;
if (this.options.onFilter) {
this.options.onFilter(searchText);
}
const data = this.options.data();
if (data != null && !this.options.filterByText) {
results = data;
if (searchText !== '') {
// When data is an array of objects therefore [object Array] e.g.
// [
// { prop: 'foo' },
// { prop: 'baz' }
// ]
if (Array.isArray(data)) {
results = fuzzaldrinPlus.filter(data, searchText, {
key: this.options.keys,
});
}
// If data is grouped therefore an [object Object]. e.g.
// {
// groupName1: [
// { prop: 'foo' },
// { prop: 'baz' }
// ],
// groupName2: [
// { prop: 'abc' },
// { prop: 'def' }
// ]
// }
else if (isObject(data)) {
results = {};
Object.keys(data).forEach(key => {
group = data[key];
tmp = fuzzaldrinPlus.filter(group, searchText, {
key: this.options.keys,
});
if (tmp.length) {
results[key] = tmp.map(item => item);
}
});
}
}
return this.options.callback(results);
}
const elements = this.options.elements();
if (searchText) {
// eslint-disable-next-line func-names
elements.each(function() {
const $el = $(this);
const matches = fuzzaldrinPlus.match($el.text().trim(), searchText);
if (!$el.is('.dropdown-header')) {
if (matches.length) {
return $el.show().removeClass('option-hidden');
}
return $el.hide().addClass('option-hidden');
}
});
} else {
elements.show().removeClass('option-hidden');
}
elements
.parent()
.find('.dropdown-menu-empty-item')
.toggleClass('hidden', elements.is(':visible'));
}
}
export class GitLabDropdownInput {
constructor(input, options) {
this.input = input;
this.options = options;
this.fieldName = this.options.fieldName || 'field-name';
const $inputContainer = this.input.parent();
const $clearButton = $inputContainer.find('.js-dropdown-input-clear');
$clearButton.on('click', e => {
// Clear click
e.preventDefault();
e.stopPropagation();
return this.input
.val('')
.trigger('input')
.focus();
});
this.input
.on('keydown', e => {
const keyCode = e.which;
if (keyCode === 13 && !options.elIsInput) {
e.preventDefault();
}
})
.on('input', e => {
let val = e.currentTarget.value || this.options.inputFieldName;
val = val
.split(' ')
.join('-') // replaces space with dash
.replace(/[^a-zA-Z0-9 -]/g, '')
.toLowerCase() // replace non alphanumeric
.replace(/(-)\1+/g, '-'); // replace repeated dashes
this.cb(this.options.fieldName, val, {}, true);
this.input
.closest('.dropdown')
.find('.dropdown-toggle-text')
.text(val);
});
}
onInput(cb) {
this.cb = cb;
}
}
/* eslint-disable consistent-return */
import axios from '../lib/utils/axios_utils';
export class GitLabDropdownRemote {
constructor(dataEndpoint, options) {
this.dataEndpoint = dataEndpoint;
this.options = options;
}
execute() {
if (typeof this.dataEndpoint === 'string') {
return this.fetchData();
} else if (typeof this.dataEndpoint === 'function') {
if (this.options.beforeSend) {
this.options.beforeSend();
}
return this.dataEndpoint('', data => {
// Fetch the data by calling the data function
if (this.options.success) {
this.options.success(data);
}
if (this.options.beforeSend) {
return this.options.beforeSend();
}
});
}
}
fetchData() {
if (this.options.beforeSend) {
this.options.beforeSend();
}
// Fetch the data through ajax if the data is a string
return axios.get(this.dataEndpoint).then(({ data }) => {
if (this.options.success) {
return this.options.success(data);
}
});
}
}
......@@ -238,6 +238,7 @@ class Clusters::ClustersController < Clusters::BaseController
:environment_scope,
:managed,
provider_aws_attributes: [
:kubernetes_version,
:key_name,
:role_arn,
:region,
......
......@@ -21,7 +21,7 @@ class Projects::PagesController < Projects::ApplicationController
format.html do
redirect_to project_pages_path(@project),
status: :found,
notice: 'Pages were removed'
notice: 'Pages were scheduled for removal'
end
end
end
......
......@@ -110,7 +110,9 @@ class MergeRequestsFinder < IssuableFinder
.or(table[:title].matches('WIP %'))
.or(table[:title].matches('[WIP]%'))
return items unless Feature.enabled?(:merge_request_draft_filter)
# Let's keep this FF around until https://gitlab.com/gitlab-org/gitlab/-/issues/232999
# is implemented
return items unless Feature.enabled?(:merge_request_draft_filter, default_enabled: true)
items
.or(table[:title].matches('Draft - %'))
......
......@@ -37,7 +37,7 @@ module Clusters
greater_than: 0
}
validates :key_name, :region, :instance_type, :security_group_id, length: { in: 1..255 }
validates :kubernetes_version, :key_name, :region, :instance_type, :security_group_id, length: { in: 1..255 }
validates :subnet_ids, presence: true
def nullify_credentials
......
......@@ -63,6 +63,7 @@ module Clusters
[
parameter('ClusterName', provider.cluster.name),
parameter('ClusterRole', provider.role_arn),
parameter('KubernetesVersion', provider.kubernetes_version),
parameter('ClusterControlPlaneSecurityGroup', provider.security_group_id),
parameter('VpcId', provider.vpc_id),
parameter('Subnets', provider.subnet_ids.join(',')),
......
......@@ -3,8 +3,11 @@
module Pages
class DeleteService < BaseService
def execute
project.remove_pages
project.pages_domains.destroy_all # rubocop: disable Cop/DestroyAll
if Feature.enabled?(:async_pages_removal, project)
PagesRemoveWorker.perform_async(project.id)
else
PagesRemoveWorker.new.perform(project.id)
end
end
end
end
......@@ -1358,7 +1358,7 @@
:tags: []
- :name: flush_counter_increments
:feature_category: :not_owned
:has_external_dependencies:
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
......@@ -1450,7 +1450,7 @@
:urgency: :high
:resource_boundary: :unknown
:weight: 5
:idempotent:
:idempotent:
:tags: []
- :name: merge_request_mergeability_check
:feature_category: :source_code_management
......@@ -1532,6 +1532,14 @@
:weight: 1
:idempotent:
:tags: []
- :name: pages_remove
:feature_category: :pages
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
- :name: pages_update_configuration
:feature_category: :pages
:has_external_dependencies:
......
# frozen_string_literal: true
class PagesRemoveWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
sidekiq_options retry: 3
feature_category :pages
loggable_arguments 0
def perform(project_id)
project = Project.find_by_id(project_id)
return unless project
project.remove_pages
project.pages_domains.delete_all
end
end
---
title: Update EKS Kubernetes versions
merge_request: 38644
author:
type: fixed
---
title: Re-name Analytics Workspace as instance-level analytics
merge_request: 40436
author:
type: changed
---
title: Include draft merge request into filter response
merge_request: 40376
author:
type: changed
---
name: async_pages_removal
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38313
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/239318
group: team::Scalability
type: development
default_enabled: false
\ No newline at end of file
......@@ -178,6 +178,8 @@
- 1
- - pages_domain_verification
- 1
- - pages_remove
- 1
- - pages_update_configuration
- 1
- - personal_access_tokens
......
# frozen_string_literal: true
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddKubernetesVersionToClusterProvidersAws < ActiveRecord::Migration[6.0]
# Uncomment the following include if you require helper functions:
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def up
unless column_exists?(:cluster_providers_aws, :kubernetes_version)
add_column :cluster_providers_aws, :kubernetes_version, :text, null: false, default: '1.14'
end
add_text_limit :cluster_providers_aws, :kubernetes_version, 30
end
def down
if column_exists?(:cluster_providers_aws, :kubernetes_version)
remove_column :cluster_providers_aws, :kubernetes_version
end
end
end
94b494b5f8e351cf453699debf03aa28f8a9136c829cb7410c90590b5106cdd5
\ No newline at end of file
......@@ -10593,7 +10593,9 @@ CREATE TABLE public.cluster_providers_aws (
encrypted_secret_access_key_iv character varying(255),
encrypted_secret_access_key text,
session_token text,
status_reason text
status_reason text,
kubernetes_version text DEFAULT '1.14'::text NOT NULL,
CONSTRAINT check_f1f42cd85e CHECK ((char_length(kubernetes_version) <= 30))
);
CREATE SEQUENCE public.cluster_providers_aws_id_seq
......
......@@ -6,15 +6,15 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Analytics
## Analytics workspace
## Instance-level analytics
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12077) in GitLab 12.2.
The Analytics workspace will make it possible to aggregate analytics across
Instance-level analytics make it possible to aggregate analytics across
GitLab, so that users can view information across multiple projects and groups
in one place.
To access the Analytics workspace, click on **More > Analytics** in the top navigation bar.
[Learn more about instance-level analytics](../admin_area/analytics/index.md).
## Group-level analytics
......
......@@ -5,7 +5,7 @@ return if Rails.env.production?
namespace :gitlab do
namespace :sidekiq do
def write_yaml(path, banner, object)
File.write(path, banner + YAML.dump(object))
File.write(path, banner + YAML.dump(object).gsub(/ *$/m, ''))
end
namespace :all_queues_yml do
......
......@@ -2834,9 +2834,6 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
msgid "An error occurred while saving the approval settings"
msgstr ""
msgid "An error occurred while saving the template. Please check if the template exists."
msgstr ""
......@@ -21124,6 +21121,9 @@ msgstr ""
msgid "Rook"
msgstr ""
msgid "Rule name is already taken."
msgstr ""
msgid "Rules that define what git pushes are accepted for a project in this group. All newly created projects in this group will use these settings."
msgstr ""
......@@ -24893,10 +24893,10 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
msgid "There was an error while fetching the chart data."
msgid "There was an error while fetching the chart data. Please refresh the page to try again."
msgstr ""
msgid "There was an error while fetching the table data."
msgid "There was an error while fetching the table data. Please refresh the page to try again."
msgstr ""
msgid "There was an error while fetching value stream analytics %{requestTypeName} data."
......
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# This cop checks for `Timecop.freeze` usage in specs.
#
# @example
#
# # bad
# Timecop.freeze(Time.current) { example.run }
#
# # good
# freeze_time(Time.current) { example.run }
#
class TimecopFreeze < RuboCop::Cop::Cop
include MatchRange
MESSAGE = 'Do not use `Timecop.freeze`, use `freeze_time` instead. ' \
'See https://gitlab.com/gitlab-org/gitlab/-/issues/214432 for more info.'
def_node_matcher :timecop_freeze?, <<~PATTERN
(send (const nil? :Timecop) :freeze ?_)
PATTERN
def on_send(node)
return unless timecop_freeze?(node)
add_offense(node, location: :expression, message: MESSAGE)
end
def autocorrect(node)
-> (corrector) do
each_match_range(node.source_range, /^(Timecop\.freeze)/) do |match_range|
corrector.replace(match_range, 'freeze_time')
end
end
end
end
end
end
end
......@@ -4,6 +4,7 @@ FactoryBot.define do
factory :cluster_provider_aws, class: 'Clusters::Providers::Aws' do
association :cluster, platform_type: :kubernetes, provider_type: :aws
kubernetes_version { '1.16' }
role_arn { 'arn:aws:iam::123456789012:role/role-name' }
vpc_id { 'vpc-00000000000000000' }
subnet_ids { %w(subnet-00000000000000000 subnet-11111111111111111) }
......
......@@ -380,14 +380,14 @@ RSpec.shared_examples 'pages settings editing' do
expect(project).to be_pages_deployed
end
it 'removes the pages' do
it 'removes the pages', :sidekiq_inline do
visit project_pages_path(project)
expect(page).to have_link('Remove pages')
accept_confirm { click_link 'Remove pages' }
expect(page).to have_content('Pages were removed')
expect(page).to have_content('Pages were scheduled for removal')
expect(project.reload.pages_deployed?).to be_falsey
end
end
......
......@@ -159,7 +159,7 @@ RSpec.describe 'Project' do
describe 'remove forked relationship', :js do
let(:user) { create(:user) }
let(:project) { fork_project(create(:project, :public), user, namespace_id: user.namespace) }
let(:project) { fork_project(create(:project, :public), user, namespace: user.namespace) }
before do
sign_in user
......
......@@ -48,7 +48,7 @@ describe('Application Row', () => {
describe('Install button', () => {
const button = () => wrapper.find('.js-cluster-application-install-button');
const checkButtonState = (label, loading, disabled) => {
expect(button().props('label')).toEqual(label);
expect(button().text()).toEqual(label);
expect(button().props('loading')).toEqual(loading);
expect(button().props('disabled')).toEqual(disabled);
};
......@@ -56,7 +56,7 @@ describe('Application Row', () => {
it('has indeterminate state on page load', () => {
mountComponent({ status: null });
expect(button().props('label')).toBeUndefined();
expect(button().text()).toBe('');
});
it('has install button', () => {
......@@ -225,7 +225,7 @@ describe('Application Row', () => {
mountComponent({ updateAvailable: true });
expect(button().exists()).toBe(true);
expect(button().props('label')).toContain('Update');
expect(button().text()).toContain('Update');
});
it('has enabled "Retry update" when update process fails', () => {
......@@ -235,14 +235,14 @@ describe('Application Row', () => {
});
expect(button().exists()).toBe(true);
expect(button().props('label')).toContain('Retry update');
expect(button().text()).toContain('Retry update');
});
it('has disabled "Updating" when APPLICATION_STATUS.UPDATING', () => {
mountComponent({ status: APPLICATION_STATUS.UPDATING });
expect(button().exists()).toBe(true);
expect(button().props('label')).toContain('Updating');
expect(button().text()).toContain('Updating');
});
it('clicking update button emits event', () => {
......
import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import UninstallApplicationButton from '~/clusters/components/uninstall_application_button.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { APPLICATION_STATUS } from '~/clusters/constants';
const { INSTALLED, UPDATING, UNINSTALLING } = APPLICATION_STATUS;
......@@ -19,14 +19,21 @@ describe('UninstallApplicationButton', () => {
});
describe.each`
status | loading | disabled | label
status | loading | disabled | text
${INSTALLED} | ${false} | ${false} | ${'Uninstall'}
${UPDATING} | ${false} | ${true} | ${'Uninstall'}
${UNINSTALLING} | ${true} | ${true} | ${'Uninstalling'}
`('when app status is $status', ({ loading, disabled, status, label }) => {
it(`renders a loading=${loading}, disabled=${disabled} button with label="${label}"`, () => {
`('when app status is $status', ({ loading, disabled, status, text }) => {
beforeAll(() => {
createComponent({ status });
expect(wrapper.find(LoadingButton).props()).toMatchObject({ loading, disabled, label });
});
it(`renders a button with loading=${loading} and disabled=${disabled}`, () => {
expect(wrapper.find(GlButton).props()).toMatchObject({ loading, disabled });
});
it(`renders a button with text="${text}"`, () => {
expect(wrapper.find(GlButton).text()).toBe(text);
});
});
});
......@@ -147,6 +147,7 @@ describe('EksClusterConfigurationForm', () => {
initialState: {
clusterName: 'cluster name',
environmentScope: '*',
kubernetesVersion: '1.16',
selectedRegion: 'region',
selectedRole: 'role',
selectedKeyPair: 'key pair',
......
......@@ -47,7 +47,7 @@ describe('EKS Cluster Store Actions', () => {
beforeEach(() => {
clusterName = 'my cluster';
environmentScope = 'production';
kubernetesVersion = '11.1';
kubernetesVersion = '1.16';
region = 'regions-1';
vpc = 'vpc-1';
subnet = 'subnet-1';
......@@ -180,6 +180,7 @@ describe('EKS Cluster Store Actions', () => {
environment_scope: environmentScope,
managed: gitlabManagedCluster,
provider_aws_attributes: {
kubernetes_version: kubernetesVersion,
region,
vpc_id: vpc,
subnet_ids: subnet,
......
......@@ -19,7 +19,7 @@ RSpec.describe Backup::Repository do
end
end
describe '#dump' do
describe '#dump', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/241251' do
before do
allow(Gitlab.config.repositories.storages).to receive(:keys).and_return(storage_keys)
end
......
......@@ -39,7 +39,9 @@ RSpec.describe API::Pages do
expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project).and_return true
expect(PagesWorker).to receive(:perform_in).with(5.minutes, :remove, project.namespace.full_path, anything)
delete api("/projects/#{project.id}/pages", admin )
Sidekiq::Testing.inline! do
delete api("/projects/#{project.id}/pages", admin )
end
expect(project.reload.pages_metadatum.deployed?).to be(false)
end
......
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/rspec/timecop_freeze'
RSpec.describe RuboCop::Cop::RSpec::TimecopFreeze, type: :rubocop do
include CopHelper
subject(:cop) { described_class.new }
context 'when calling Timecop.freeze' do
let(:source) do
<<~SRC
Timecop.freeze(Time.current) { example.run }
SRC
end
let(:corrected_source) do
<<~SRC
freeze_time(Time.current) { example.run }
SRC
end
it 'registers an offence' do
inspect_source(source)
expect(cop.offenses.size).to eq(1)
end
it 'can autocorrect the source' do
expect(autocorrect_source(source)).to eq(corrected_source)
end
end
context 'when calling a different method on Timecop' do
let(:source) do
<<~SRC
Timecop.travel(Time.current)
SRC
end
it 'does not register an offence' do
inspect_source(source)
expect(cop.offenses).to be_empty
end
end
end
......@@ -22,6 +22,7 @@ RSpec.describe Clusters::Aws::ProvisionService do
[
{ parameter_key: 'ClusterName', parameter_value: provider.cluster.name },
{ parameter_key: 'ClusterRole', parameter_value: provider.role_arn },
{ parameter_key: 'KubernetesVersion', parameter_value: provider.kubernetes_version },
{ parameter_key: 'ClusterControlPlaneSecurityGroup', parameter_value: provider.security_group_id },
{ parameter_key: 'VpcId', parameter_value: provider.vpc_id },
{ parameter_key: 'Subnets', parameter_value: provider.subnet_ids.join(',') },
......
......@@ -348,7 +348,7 @@ RSpec.describe Git::BranchHooksService do
context 'when the project is forked', :sidekiq_might_not_need_inline do
let(:upstream_project) { project }
let(:forked_project) { fork_project(upstream_project, user, repository: true) }
let(:forked_project) { fork_project(upstream_project, user, repository: true, using_service: true) }
let!(:forked_service) do
described_class.new(forked_project, user, change: { oldrev: oldrev, newrev: newrev, ref: ref })
......
......@@ -88,6 +88,10 @@ RSpec.describe MergeRequests::BuildService do
let(:source_project) { fork_project(project, user) }
let(:merge_request) { described_class.new(project, user, mr_params).execute }
before do
project.add_reporter(user)
end
it 'assigns force_remove_source_branch' do
expect(merge_request.force_remove_source_branch?).to be_truthy
end
......
......@@ -3,25 +3,45 @@
require 'spec_helper'
RSpec.describe Pages::DeleteService do
let_it_be(:project) { create(:project, path: "my.project")}
let_it_be(:admin) { create(:admin) }
let_it_be(:domain) { create(:pages_domain, project: project) }
let_it_be(:service) { described_class.new(project, admin)}
shared_examples 'remove pages' do
let_it_be(:project) { create(:project, path: "my.project")}
let_it_be(:admin) { create(:admin) }
let_it_be(:domain) { create(:pages_domain, project: project) }
let_it_be(:service) { described_class.new(project, admin)}
it 'deletes published pages' do
expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project).and_return true
expect(PagesWorker).to receive(:perform_in).with(5.minutes, :remove, project.namespace.full_path, anything)
it 'deletes published pages' do
expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project).and_return true
expect(PagesWorker).to receive(:perform_in).with(5.minutes, :remove, project.namespace.full_path, anything)
service.execute
Sidekiq::Testing.inline! { service.execute }
expect(project.reload.pages_metadatum.deployed?).to be(false)
expect(project.reload.pages_metadatum.deployed?).to be(false)
end
it 'deletes all domains' do
expect(project.pages_domains.count).to be 1
Sidekiq::Testing.inline! { service.execute }
expect(project.reload.pages_domains.count).to be 0
end
end
it 'deletes all domains' do
expect(project.pages_domains.count).to be 1
context 'with feature flag disabled' do
before do
stub_feature_flags(async_pages_removal: false)
expect(PagesRemoveWorker).not_to receive(:perform_async)
end
it_behaves_like 'remove pages'
end
service.execute
context 'with feature flag enabled' do
before do
expect(PagesRemoveWorker).to receive(:perform_async).and_call_original
end
expect(project.reload.pages_domains.count).to be 0
it_behaves_like 'remove pages'
end
end
......@@ -9,7 +9,7 @@ RSpec.describe Projects::ForkService do
it 'flushes the forks count cache of the source project', :clean_gitlab_redis_cache do
expect(from_project.forks_count).to be_zero
fork_project(from_project, to_user)
fork_project(from_project, to_user, using_service: true)
BatchLoader::Executor.clear_current
expect(from_project.forks_count).to eq(1)
......@@ -40,7 +40,7 @@ RSpec.describe Projects::ForkService do
@guest = create(:user)
@from_project.add_user(@guest, :guest)
end
subject { fork_project(@from_project, @guest) }
subject { fork_project(@from_project, @guest, using_service: true) }
it { is_expected.not_to be_persisted }
it { expect(subject.errors[:forked_from_project_id]).to eq(['is forbidden']) }
......@@ -56,7 +56,7 @@ RSpec.describe Projects::ForkService do
end
describe "successfully creates project in the user namespace" do
let(:to_project) { fork_project(@from_project, @to_user, namespace: @to_user.namespace) }
let(:to_project) { fork_project(@from_project, @to_user, namespace: @to_user.namespace, using_service: true) }
it { expect(to_project).to be_persisted }
it { expect(to_project.errors).to be_empty }
......@@ -92,21 +92,21 @@ RSpec.describe Projects::ForkService do
end
it 'imports the repository of the forked project', :sidekiq_might_not_need_inline do
to_project = fork_project(@from_project, @to_user, repository: true)
to_project = fork_project(@from_project, @to_user, repository: true, using_service: true)
expect(to_project.empty_repo?).to be_falsy
end
end
context 'creating a fork of a fork' do
let(:from_forked_project) { fork_project(@from_project, @to_user) }
let(:from_forked_project) { fork_project(@from_project, @to_user, using_service: true) }
let(:other_namespace) do
group = create(:group)
group.add_owner(@to_user)
group
end
let(:to_project) { fork_project(from_forked_project, @to_user, namespace: other_namespace) }
let(:to_project) { fork_project(from_forked_project, @to_user, namespace: other_namespace, using_service: true) }
it 'sets the root of the network to the root project' do
expect(to_project.fork_network.root_project).to eq(@from_project)
......@@ -126,7 +126,7 @@ RSpec.describe Projects::ForkService do
context 'project already exists' do
it "fails due to validation, not transaction failure" do
@existing_project = create(:project, :repository, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace)
@to_project = fork_project(@from_project, @to_user, namespace: @to_namespace)
@to_project = fork_project(@from_project, @to_user, namespace: @to_namespace, using_service: true)
expect(@existing_project).to be_persisted
expect(@to_project).not_to be_persisted
......@@ -137,7 +137,7 @@ RSpec.describe Projects::ForkService do
context 'repository in legacy storage already exists' do
let(:fake_repo_path) { File.join(TestEnv.repos_path, @to_user.namespace.full_path, "#{@from_project.path}.git") }
let(:params) { { namespace: @to_user.namespace } }
let(:params) { { namespace: @to_user.namespace, using_service: true } }
before do
stub_application_setting(hashed_storage_enabled: false)
......@@ -169,13 +169,13 @@ RSpec.describe Projects::ForkService do
context 'GitLab CI is enabled' do
it "forks and enables CI for fork" do
@from_project.enable_ci
@to_project = fork_project(@from_project, @to_user)
@to_project = fork_project(@from_project, @to_user, using_service: true)
expect(@to_project.builds_enabled?).to be_truthy
end
end
context "CI/CD settings" do
let(:to_project) { fork_project(@from_project, @to_user) }
let(:to_project) { fork_project(@from_project, @to_user, using_service: true) }
context "when origin has git depth specified" do
before do
......@@ -206,7 +206,7 @@ RSpec.describe Projects::ForkService do
end
it "creates fork with lowest level" do
forked_project = fork_project(@from_project, @to_user)
forked_project = fork_project(@from_project, @to_user, using_service: true)
expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
......@@ -218,7 +218,7 @@ RSpec.describe Projects::ForkService do
end
it "creates fork with private visibility levels" do
forked_project = fork_project(@from_project, @to_user)
forked_project = fork_project(@from_project, @to_user, using_service: true)
expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
......@@ -232,7 +232,7 @@ RSpec.describe Projects::ForkService do
end
it 'fails' do
to_project = fork_project(@from_project, @to_user, namespace: @to_user.namespace)
to_project = fork_project(@from_project, @to_user, namespace: @to_user.namespace, using_service: true)
expect(to_project.errors[:forked_from_project_id]).to eq(['is forbidden'])
end
......@@ -253,7 +253,7 @@ RSpec.describe Projects::ForkService do
@group.add_user(@developer, GroupMember::DEVELOPER)
@project.add_user(@developer, :developer)
@project.add_user(@group_owner, :developer)
@opts = { namespace: @group }
@opts = { namespace: @group, using_service: true }
end
context 'fork project for group' do
......@@ -299,7 +299,7 @@ RSpec.describe Projects::ForkService do
group_owner = create(:user)
private_group.add_owner(group_owner)
forked_project = fork_project(public_project, group_owner, namespace: private_group)
forked_project = fork_project(public_project, group_owner, namespace: private_group, using_service: true)
expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
......@@ -310,7 +310,7 @@ RSpec.describe Projects::ForkService do
context 'when a project is already forked' do
it 'creates a new poolresository after the project is moved to a new shard' do
project = create(:project, :public, :repository)
fork_before_move = fork_project(project)
fork_before_move = fork_project(project, nil, using_service: true)
# Stub everything required to move a project to a Gitaly shard that does not exist
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
......@@ -329,7 +329,7 @@ RSpec.describe Projects::ForkService do
destination_storage_name: 'test_second_storage'
)
Projects::UpdateRepositoryStorageService.new(storage_move).execute
fork_after_move = fork_project(project.reload)
fork_after_move = fork_project(project.reload, nil, using_service: true)
pool_repository_before_move = PoolRepository.joins(:shard)
.find_by(source_project: project, shards: { name: 'default' })
pool_repository_after_move = PoolRepository.joins(:shard)
......@@ -350,7 +350,7 @@ RSpec.describe Projects::ForkService do
context 'when no pool exists' do
it 'creates a new object pool' do
forked_project = fork_project(fork_from_project, forker)
forked_project = fork_project(fork_from_project, forker, using_service: true)
expect(forked_project.pool_repository).to eq(fork_from_project.pool_repository)
end
......@@ -360,7 +360,7 @@ RSpec.describe Projects::ForkService do
let!(:pool_repository) { create(:pool_repository, source_project: fork_from_project) }
it 'joins the object pool' do
forked_project = fork_project(fork_from_project, forker)
forked_project = fork_project(fork_from_project, forker, using_service: true)
expect(forked_project.pool_repository).to eq(fork_from_project.pool_repository)
end
......
......@@ -17,14 +17,26 @@ module ProjectForksHelper
project.add_developer(user)
end
unless params[:namespace] || params[:namespace_id]
unless params[:namespace]
params[:namespace] = create(:group)
params[:namespace].add_owner(user)
end
namespace = params[:namespace]
create_repository = params.delete(:repository)
unless params[:target_project] || params[:using_service]
target_level = [project.visibility_level, namespace.visibility_level].min
visibility_level = Gitlab::VisibilityLevel.closest_allowed_level(target_level)
params[:target_project] =
create(:project,
(:repository if create_repository),
visibility_level: visibility_level, creator: user, namespace: namespace)
end
service = Projects::ForkService.new(project, user, params)
create_repository = params.delete(:repository)
# Avoid creating a repository
unless create_repository
allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe PagesRemoveWorker do
let_it_be(:project) { create(:project, path: "my.project")}
let_it_be(:domain) { create(:pages_domain, project: project) }
subject { described_class.new.perform(project.id) }
it 'deletes published pages' do
expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project).and_return true
expect(PagesWorker).to receive(:perform_in).with(5.minutes, :remove, project.namespace.full_path, anything)
subject
expect(project.reload.pages_metadatum.deployed?).to be(false)
end
it 'deletes all domains' do
expect(project.pages_domains.count).to be 1
subject
expect(project.reload.pages_domains.count).to be 0
end
end
......@@ -7,11 +7,12 @@ Parameters:
KubernetesVersion:
Description: The Kubernetes version to install
Type: String
Default: 1.14
Default: 1.16
AllowedValues:
- 1.12
- 1.13
- 1.14
- 1.15
- 1.16
- 1.17
KeyName:
Description: The EC2 Key Pair to allow SSH access to the node instances
......
......@@ -848,10 +848,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.161.0.tgz#661e8d19862dfba0e4c558e2eb6d64b402c1453e"
integrity sha512-qsbboEICn08ZoEoAX/TuYygsFaXlzsCY+CfmdOzqvJbOdfHhVXmrJBxd2hP2qqjTZm2PkbRRmn+03+ce1jvatQ==
"@gitlab/ui@20.8.0":
version "20.8.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-20.8.0.tgz#00ecc698d2ed003f87fd59ea408f999c804d7dda"
integrity sha512-BHXDFtY1Zw6rIkUopAJQMoDpWlAKyDZiHR13U6Tv+Ot0G6p6tW6PVVHb12m+fycNxBDKdYisdLJwH8MKNjWRrA==
"@gitlab/ui@20.9.1":
version "20.9.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-20.9.1.tgz#1e5960102a0c41925a531d6b8d00072b4717da54"
integrity sha512-6C03BakNqharsDVChnIYzMbp0vj4syfoo4YDxJ1Ktfev8wq4gy5HrcoZt5dgNNXEfmwbyFnJlXaKBNDlLpbmVA==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册