Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-11-18 06:09:29 +00:00
parent cfaf98a3b2
commit 0b81231d2d
20 changed files with 148 additions and 171 deletions

View file

@ -1,5 +1,5 @@
<script>
import { GlButton, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { GlDropdown, GlDropdownItem, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
import { formatTime } from '~/lib/utils/datetime_utility';
import eventHub from '../event_hub';
@ -9,7 +9,8 @@ export default {
GlTooltip: GlTooltipDirective,
},
components: {
GlButton,
GlDropdown,
GlDropdownItem,
GlIcon,
GlLoadingIcon,
},
@ -35,7 +36,7 @@ export default {
if (action.scheduledAt) {
const confirmationMessage = sprintf(
s__(
"DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes.",
'DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes.',
),
{ jobName: action.name },
);
@ -67,40 +68,32 @@ export default {
};
</script>
<template>
<div class="btn-group" role="group">
<gl-button
v-gl-tooltip
:title="title"
:aria-label="title"
:disabled="isLoading"
class="dropdown dropdown-new js-environment-actions-dropdown"
data-container="body"
data-toggle="dropdown"
data-testid="environment-actions-button"
<gl-dropdown
v-gl-tooltip
:title="title"
:aria-label="title"
:disabled="isLoading"
right
data-container="body"
data-testid="environment-actions-button"
>
<template #button-content>
<gl-icon name="play" />
<gl-icon name="chevron-down" />
<gl-loading-icon v-if="isLoading" />
</template>
<gl-dropdown-item
v-for="(action, i) in actions"
:key="i"
:disabled="isActionDisabled(action)"
data-testid="manual-action-link"
@click="onClickAction(action)"
>
<span>
<gl-icon name="play" />
<gl-icon name="chevron-down" />
<gl-loading-icon v-if="isLoading" />
<span class="gl-flex-fill-1">{{ action.name }}</span>
<span v-if="action.scheduledAt" class="gl-text-gray-500 float-right">
<gl-icon name="clock" />
{{ remainingTime(action) }}
</span>
</gl-button>
<ul class="dropdown-menu dropdown-menu-right">
<li v-for="(action, i) in actions" :key="i" class="gl-display-flex">
<gl-button
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
variant="link"
class="js-manual-action-link gl-flex-fill-1"
@click="onClickAction(action)"
>
<span class="gl-flex-fill-1">{{ action.name }}</span>
<span v-if="action.scheduledAt" class="text-secondary float-right">
<gl-icon name="clock" />
{{ remainingTime(action) }}
</span>
</gl-button>
</li>
</ul>
</div>
</gl-dropdown-item>
</gl-dropdown>
</template>

View file

@ -32,7 +32,7 @@ export default {
if (action.scheduled_at) {
const confirmationMessage = sprintf(
s__(
"DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes.",
'DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes.',
),
{ jobName: action.name },
);

View file

@ -206,7 +206,7 @@ module Ci
override :dependency_variables
def dependency_variables
return [] unless ::Feature.enabled?(:ci_bridge_dependency_variables, project)
return [] unless ::Feature.enabled?(:ci_bridge_dependency_variables, project, default_enabled: true)
super
end

View file

@ -0,0 +1,5 @@
---
title: Implement passing dotenv variables to bridge jobs
merge_request: 47905
author:
type: fixed

View file

@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46530
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273734
type: development
group: group::pipeline authoring
default_enabled: false
default_enabled: true

View file

@ -1,8 +0,0 @@
---
name: codequality_mr_diff
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47938
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284140
milestone: '13.7'
type: development
group: group::testing
default_enabled: false

View file

@ -529,12 +529,18 @@ To disable Gitaly on a GitLab server:
## Enable TLS support
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22602) in GitLab 11.8.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22602) in GitLab 11.8.
> - [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/3160) in GitLab 13.6, outgoing TLS connections to GitLab provide client certificates if configured.
Gitaly supports TLS encryption. To communicate with a Gitaly instance that listens for secure
connections, you must use `tls://` URL scheme in the `gitaly_address` of the corresponding
storage entry in the GitLab configuration.
Gitaly provides the same server certificates as client certificates in TLS
connections to GitLab. This can be used as part of a mutual TLS authentication strategy
when combined with reverse proxies (for example, NGINX) that validate client certificate
to grant access to GitLab.
You must supply your own certificates as this isn't provided automatically. The certificate
corresponding to each Gitaly server must be installed on that Gitaly server.

View file

@ -63,8 +63,6 @@ job finishes but the DAST job fails, the security dashboard doesn't show SAST re
the analyzer outputs an
[exit code](../../../development/integrations/secure.md#exit-code).
You can filter the vulnerabilities list by selecting from the **Severity** and **Scanner** dropdowns.
## Project Security Dashboard
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235558) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.6.
@ -107,11 +105,6 @@ You can filter the vulnerabilities by one or more of the following:
| Severity | Critical, High, Medium, Low, Info, Unknown |
| Scanner | [Available Scanners](../index.md#security-scanning-tools) |
You can filter the vulnerabilities list by selecting from the **Status**, **Severity**, and
**Scanner** dropdowns. In the **Scanner** dropdown, select individual scanners or scanner groups to
toggle those scanners. The **Scanner** dropdown includes both GitLab scanners, and in GitLab 13.6
and later, custom scanners.
You can also dismiss vulnerabilities in the table:
1. Select the checkbox for each vulnerability you want to dismiss.
@ -267,11 +260,6 @@ You can filter which vulnerabilities the vulnerability report displays by:
| Scanner | [Available Scanners](../index.md#security-scanning-tools) |
| Project | Projects configured in the Security Center settings |
You can filter the vulnerabilities list by selecting from the **Status**, **Severity**, and
**Scanner**, and **Project** dropdowns. In the **Scanner** dropdown, select individual scanners or
scanner groups to toggle those scanners. The **Scanner** dropdown includes both GitLab scanners, and
in GitLab 13.6 and later, custom scanners.
Clicking any vulnerability in the table takes you to its
[Vulnerability Details](../vulnerabilities) page to see more information on that vulnerability.
To create an issue associated with the vulnerability, click the **Create Issue** button.

View file

@ -7710,9 +7710,6 @@ msgstr ""
msgid "Could not restore the group"
msgstr ""
msgid "Could not retrieve custom scanners for scanner filter. Please try again later."
msgstr ""
msgid "Could not revoke impersonation token %{token_name}."
msgstr ""
@ -8749,7 +8746,7 @@ msgstr ""
msgid "Delayed Project Deletion (%{adjourned_deletion})"
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."

View file

@ -15,16 +15,6 @@ RSpec.describe DashboardController do
describe 'GET issues' do
it_behaves_like 'issuables list meta-data', :issue, :issues
it_behaves_like 'issuables requiring filter', :issues
it 'lists only incidents and issues' do
issue = create(:incident, project: project, author: user)
incident = create(:incident, project: project, author: user)
create(:quality_test_case, project: project, author: user)
get :issues, params: { author_id: user.id }
expect(assigns(:issues)).to match_array([issue, incident])
end
end
describe 'GET merge requests' do

View file

@ -400,15 +400,6 @@ RSpec.describe GroupsController, factory_default: :keep do
sign_in(user)
end
it 'lists only incidents and issues' do
incident = create(:incident, project: project)
create(:quality_test_case, project: project)
get :issues, params: { id: group.to_param }
expect(assigns(:issues)).to match_array([issue_1, issue_2, incident])
end
context 'sorting by votes' do
it 'sorts most popular issues' do
get :issues, params: { id: group.to_param, sort: 'upvotes_desc' }

View file

@ -52,9 +52,5 @@ FactoryBot.define do
factory :incident do
issue_type { :incident }
end
factory :quality_test_case do
issue_type { :test_case }
end
end
end

View file

@ -12,8 +12,16 @@ RSpec.describe 'Environments page', :js do
sign_in(user)
end
def actions_button_selector
'[data-testid="environment-actions-button"]'
end
def action_link_selector
'[data-testid="manual-action-link"]'
end
def stop_button_selector
%q{button[title="Stop environment"]}
'button[title="Stop environment"]'
end
describe 'page tabs' do
@ -187,18 +195,17 @@ RSpec.describe 'Environments page', :js do
end
it 'shows a play button' do
find('.js-environment-actions-dropdown').click
find(actions_button_selector).click
expect(page).to have_content(action.name)
end
it 'allows to play a manual action', :js do
expect(action).to be_manual
find('.js-environment-actions-dropdown').click
find(actions_button_selector).click
expect(page).to have_content(action.name)
expect { find('.js-manual-action-link').click }
expect { find(action_link_selector).click }
.not_to change { Ci::Pipeline.count }
end
@ -301,11 +308,11 @@ RSpec.describe 'Environments page', :js do
end
it 'has a dropdown for actionable jobs' do
expect(page).to have_selector('.dropdown-new.btn.btn-default [data-testid="play-icon"]')
expect(page).to have_selector("#{actions_button_selector} [data-testid=\"play-icon\"]")
end
it "has link to the delayed job's action" do
find('.js-environment-actions-dropdown').click
find(actions_button_selector).click
expect(page).to have_button('delayed job')
expect(page).to have_content(/\d{2}:\d{2}:\d{2}/)
@ -320,7 +327,7 @@ RSpec.describe 'Environments page', :js do
end
it "shows 00:00:00 as the remaining time" do
find('.js-environment-actions-dropdown').click
find(actions_button_selector).click
expect(page).to have_content("00:00:00")
end
@ -328,8 +335,8 @@ RSpec.describe 'Environments page', :js do
context 'when user played a delayed job immediately' do
before do
find('.js-environment-actions-dropdown').click
page.accept_confirm { click_button('delayed job') }
find(actions_button_selector).click
accept_confirm { find(action_link_selector).click }
wait_for_requests
end

View file

@ -1,51 +1,69 @@
import { shallowMount } from '@vue/test-utils';
import { shallowMount, mount } from '@vue/test-utils';
import { TEST_HOST } from 'helpers/test_constants';
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { GlDropdown, GlDropdownItem, GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import eventHub from '~/environments/event_hub';
import EnvironmentActions from '~/environments/components/environment_actions.vue';
const scheduledJobAction = {
name: 'scheduled action',
playPath: `${TEST_HOST}/scheduled/job/action`,
playable: true,
scheduledAt: '2063-04-05T00:42:00Z',
};
const expiredJobAction = {
name: 'expired action',
playPath: `${TEST_HOST}/expired/job/action`,
playable: true,
scheduledAt: '2018-10-05T08:23:00Z',
};
describe('EnvironmentActions Component', () => {
let vm;
let wrapper;
const findEnvironmentActionsButton = () => vm.find('[data-testid="environment-actions-button"]');
const findEnvironmentActionsButton = () =>
wrapper.find('[data-testid="environment-actions-button"]');
beforeEach(() => {
vm = shallowMount(EnvironmentActions, {
propsData: { actions: [] },
function createComponent(props, { mountFn = shallowMount } = {}) {
wrapper = mountFn(EnvironmentActions, {
propsData: { actions: [], ...props },
directives: {
GlTooltip: createMockDirective(),
},
});
});
}
function createComponentWithScheduledJobs(opts = {}) {
return createComponent({ actions: [scheduledJobAction, expiredJobAction] }, opts);
}
const findDropdownItem = action => {
const buttons = wrapper.findAll(GlDropdownItem);
return buttons.filter(button => button.text().startsWith(action.name)).at(0);
};
afterEach(() => {
vm.destroy();
wrapper.destroy();
wrapper = null;
});
it('should render a dropdown button with 2 icons', () => {
expect(vm.find('.dropdown-new').findAll(GlIcon).length).toBe(2);
createComponent({}, { mountFn: mount });
expect(wrapper.find(GlDropdown).findAll(GlIcon).length).toBe(2);
});
it('should render a dropdown button with aria-label description', () => {
expect(vm.find('.dropdown-new').attributes('aria-label')).toEqual('Deploy to...');
createComponent();
expect(wrapper.find(GlDropdown).attributes('aria-label')).toBe('Deploy to...');
});
it('should render a tooltip', () => {
createComponent();
const tooltip = getBinding(findEnvironmentActionsButton().element, 'gl-tooltip');
expect(tooltip).toBeDefined();
});
describe('is loading', () => {
beforeEach(() => {
vm.setData({ isLoading: true });
});
it('should render a dropdown button with a loading icon', () => {
expect(vm.findAll(GlLoadingIcon).length).toBe(1);
});
});
describe('manual actions', () => {
const actions = [
{
@ -64,68 +82,71 @@ describe('EnvironmentActions Component', () => {
];
beforeEach(() => {
vm.setProps({ actions });
createComponent({ actions });
});
it('should render a dropdown with the provided list of actions', () => {
expect(vm.findAll('.dropdown-menu li').length).toEqual(actions.length);
expect(wrapper.findAll(GlDropdownItem)).toHaveLength(actions.length);
});
it("should render a disabled action when it's not playable", () => {
expect(vm.find('.dropdown-menu li:last-child gl-button-stub').props('disabled')).toBe(true);
const dropdownItems = wrapper.findAll(GlDropdownItem);
const lastDropdownItem = dropdownItems.at(dropdownItems.length - 1);
expect(lastDropdownItem.attributes('disabled')).toBe('true');
});
});
describe('scheduled jobs', () => {
const scheduledJobAction = {
name: 'scheduled action',
playPath: `${TEST_HOST}/scheduled/job/action`,
playable: true,
scheduledAt: '2063-04-05T00:42:00Z',
};
const expiredJobAction = {
name: 'expired action',
playPath: `${TEST_HOST}/expired/job/action`,
playable: true,
scheduledAt: '2018-10-05T08:23:00Z',
};
const findDropdownItem = action => {
const buttons = vm.findAll('.dropdown-menu li gl-button-stub');
return buttons.filter(button => button.text().startsWith(action.name)).at(0);
let emitSpy;
const clickAndConfirm = async ({ confirm = true } = {}) => {
jest.spyOn(window, 'confirm').mockImplementation(() => confirm);
findDropdownItem(scheduledJobAction).vm.$emit('click');
await wrapper.vm.$nextTick();
};
beforeEach(() => {
emitSpy = jest.fn();
eventHub.$on('postAction', emitSpy);
jest.spyOn(Date, 'now').mockImplementation(() => new Date('2063-04-04T00:42:00Z').getTime());
vm.setProps({ actions: [scheduledJobAction, expiredJobAction] });
});
it('emits postAction event after confirming', () => {
const emitSpy = jest.fn();
eventHub.$on('postAction', emitSpy);
jest.spyOn(window, 'confirm').mockImplementation(() => true);
describe('when postAction event is confirmed', () => {
beforeEach(async () => {
createComponentWithScheduledJobs({ mountFn: mount });
clickAndConfirm();
});
findDropdownItem(scheduledJobAction).vm.$emit('click');
it('emits postAction event', () => {
expect(window.confirm).toHaveBeenCalled();
expect(emitSpy).toHaveBeenCalledWith({ endpoint: scheduledJobAction.playPath });
});
expect(window.confirm).toHaveBeenCalled();
expect(emitSpy).toHaveBeenCalledWith({ endpoint: scheduledJobAction.playPath });
it('should render a dropdown button with a loading icon', () => {
expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true);
});
});
it('does not emit postAction event if confirmation is cancelled', () => {
const emitSpy = jest.fn();
eventHub.$on('postAction', emitSpy);
jest.spyOn(window, 'confirm').mockImplementation(() => false);
describe('when postAction event is denied', () => {
beforeEach(() => {
createComponentWithScheduledJobs({ mountFn: mount });
clickAndConfirm({ confirm: false });
});
findDropdownItem(scheduledJobAction).vm.$emit('click');
expect(window.confirm).toHaveBeenCalled();
expect(emitSpy).not.toHaveBeenCalled();
it('does not emit postAction event if confirmation is cancelled', () => {
expect(window.confirm).toHaveBeenCalled();
expect(emitSpy).not.toHaveBeenCalled();
});
});
it('displays the remaining time in the dropdown', () => {
createComponentWithScheduledJobs();
expect(findDropdownItem(scheduledJobAction).text()).toContain('24:00:00');
});
it('displays 00:00:00 for expired jobs in the dropdown', () => {
createComponentWithScheduledJobs();
expect(findDropdownItem(expiredJobAction).text()).toContain('00:00:00');
});
});

View file

@ -46,7 +46,7 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow do
it 'sends event to tracker' do
allow(tracker).to receive(:track_self_describing_event).and_call_original
subject.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', foo: 'bar')
subject.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', { foo: 'bar' })
expect(tracker).to have_received(:track_self_describing_event) do |event, context, timestamp|
expect(event.to_json[:schema]).to eq('iglu:com.gitlab/foo/jsonschema/1-0-0')
@ -71,7 +71,7 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow do
it 'does not send event to tracker' do
expect_any_instance_of(SnowplowTracker::Tracker).not_to receive(:track_self_describing_event)
subject.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', foo: 'bar')
subject.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', { foo: 'bar' })
end
end
end

View file

@ -51,7 +51,7 @@ RSpec.describe Gitlab::Tracking do
.to receive(:self_describing_event)
.with('iglu:com.gitlab/foo/jsonschema/1-0-0', { foo: 'bar' }, context: nil)
described_class.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', foo: 'bar')
described_class.self_describing_event('iglu:com.gitlab/foo/jsonschema/1-0-0', { foo: 'bar' })
end
end
end

View file

@ -141,7 +141,6 @@ RSpec.describe Issue do
describe '.with_issue_type' do
let_it_be(:issue) { create(:issue, project: reusable_project) }
let_it_be(:incident) { create(:incident, project: reusable_project) }
let_it_be(:test_case) { create(:quality_test_case, project: reusable_project) }
it 'gives issues with the given issue type' do
expect(described_class.with_issue_type('issue'))
@ -149,8 +148,8 @@ RSpec.describe Issue do
end
it 'gives issues with the given issue type' do
expect(described_class.with_issue_type(%w(issue incident test_case)))
.to contain_exactly(issue, incident, test_case)
expect(described_class.with_issue_type(%w(issue incident)))
.to contain_exactly(issue, incident)
end
end

View file

@ -10,14 +10,6 @@ RSpec.describe Projects::OpenIssuesCountService, :use_clean_rails_memory_store_c
it_behaves_like 'a counter caching service'
describe '#count' do
it 'does not count test cases' do
create(:issue, :opened, project: project)
create(:incident, :opened, project: project)
create(:quality_test_case, :opened, project: project)
expect(described_class.new(project).count).to eq(2)
end
context 'when user is nil' do
it 'does not include confidential issues in the issue count' do
create(:issue, :opened, project: project)

View file

@ -37,7 +37,7 @@ module MultipartHelpers
# *without* the "#{key}." prefix
result.deep_transform_keys! { |k| k.remove("#{key}.") }
{
"#{key}.gitlab-workhorse-upload" => jwt_token('upload' => result)
"#{key}.gitlab-workhorse-upload" => jwt_token(data: { 'upload' => result })
}
end

View file

@ -85,15 +85,15 @@ module WorkhorseHelpers
return {} if upload_params.empty?
{ "#{key}.gitlab-workhorse-upload" => jwt_token('upload' => upload_params) }
{ "#{key}.gitlab-workhorse-upload" => jwt_token(data: { 'upload' => upload_params }) }
end
def jwt_token(data = {}, issuer: 'gitlab-workhorse', secret: Gitlab::Workhorse.secret, algorithm: 'HS256')
def jwt_token(data: {}, issuer: 'gitlab-workhorse', secret: Gitlab::Workhorse.secret, algorithm: 'HS256')
JWT.encode({ 'iss' => issuer }.merge(data), secret, algorithm)
end
def workhorse_rewritten_fields_header(fields)
{ Gitlab::Middleware::Multipart::RACK_ENV_KEY => jwt_token('rewritten_fields' => fields) }
{ Gitlab::Middleware::Multipart::RACK_ENV_KEY => jwt_token(data: { 'rewritten_fields' => fields }) }
end
def workhorse_disk_accelerated_file_params(key, file)