Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-03-20 12:10:03 +00:00
parent 196ada0844
commit 43b4b3e2d2
30 changed files with 260 additions and 76 deletions

View File

@ -1,6 +0,0 @@
# For DEVELOPMENT only. Production uses Runit in
# https://gitlab.com/gitlab-org/omnibus-gitlab or the init scripts in
# lib/support/init.d, which call scripts in bin/ .
#
web: RAILS_ENV=development bin/web start_foreground
worker: RAILS_ENV=development bin/background_jobs start_foreground

View File

@ -24,10 +24,12 @@ export default class DropdownOperator extends FilteredSearchDropdown {
if (selected.tagName === 'LI') {
if (selected.hasAttribute('data-value')) {
const name = FilteredSearchVisualTokens.getLastTokenPartial();
const operator = selected.dataset.value;
FilteredSearchVisualTokens.removeLastTokenPartial();
FilteredSearchDropdownManager.addWordToInput({
tokenName: this.filter,
tokenName: name,
tokenOperator: operator,
clicked: false,
});
@ -38,8 +40,6 @@ export default class DropdownOperator extends FilteredSearchDropdown {
}
renderContent(forceShowList = false) {
this.filter = FilteredSearchVisualTokens.getLastTokenPartial();
const dropdownData = [
{
tag: 'equal',

View File

@ -35,7 +35,7 @@ export default {
</script>
<template>
<ul class="content-list group-list-tree">
<ul class="groups-list group-list-tree">
<group-item
v-for="(group, index) in groups"
:key="index"

View File

@ -8,7 +8,7 @@ export const ACTIVE_TAB_ARCHIVED = 'archived';
export const GROUPS_LIST_HOLDER_CLASS = '.js-groups-list-holder';
export const GROUPS_FILTER_FORM_CLASS = '.js-group-filter-form';
export const CONTENT_LIST_CLASS = '.content-list';
export const CONTENT_LIST_CLASS = '.groups-list';
export const COMMON_STR = {
FAILURE: __('An error occurred. Please try again.'),

View File

@ -10,9 +10,30 @@ export default () => {
const $broadcastMessageType = $('.js-broadcast-message-type');
const $broadcastBannerMessagePreview = $('.js-broadcast-banner-message-preview');
const $broadcastMessage = $('.js-broadcast-message-message');
const previewPath = $broadcastMessage.data('previewPath');
const $jsBroadcastMessagePreview = $('.js-broadcast-message-preview');
const reloadPreview = function reloadPreview() {
const previewPath = $broadcastMessage.data('previewPath');
const message = $broadcastMessage.val();
const type = $broadcastMessageType.val();
if (message === '') {
$jsBroadcastMessagePreview.text(__('Your message here'));
} else {
axios
.post(previewPath, {
broadcast_message: {
message,
broadcast_type: type,
},
})
.then(({ data }) => {
$jsBroadcastMessagePreview.html(data.message);
})
.catch(() => flash(__('An error occurred while rendering preview broadcast message')));
}
};
$broadcastMessageColor.on('input', function onMessageColorInput() {
const previewColor = $(this).val();
$broadcastBannerMessagePreview.css('background-color', previewColor);
@ -32,26 +53,14 @@ export default () => {
$broadcastMessageDismissableFormGroup.toggleClass('hidden');
$broadcastBannerMessagePreview.toggleClass('hidden');
$broadcastNotificationMessagePreview.toggleClass('hidden');
reloadPreview();
});
$broadcastMessage.on(
'input',
debounce(function onMessageInput() {
const message = $(this).val();
if (message === '') {
$jsBroadcastMessagePreview.text(__('Your message here'));
} else {
axios
.post(previewPath, {
broadcast_message: {
message,
},
})
.then(({ data }) => {
$jsBroadcastMessagePreview.html(data.message);
})
.catch(() => flash(__('An error occurred while rendering preview broadcast message')));
}
debounce(() => {
reloadPreview();
}, 250),
);

View File

@ -27,9 +27,9 @@ export function fetchLogsTree(client, path, offset, resolver = null) {
fetchpromise = axios
.get(
`${gon.relative_url_root}/${projectPath}/-/refs/${escape(ref)}/logs_tree/${encodeURIComponent(
path.replace(/^\//, ''),
)}`,
`${gon.relative_url_root}/${projectPath}/-/refs/${encodeURIComponent(
ref,
)}/logs_tree/${encodeURIComponent(path.replace(/^\//, ''))}`,
{
params: { format: 'json', offset },
},

View File

@ -12,7 +12,7 @@ export default function createRouter(base, baseRef) {
base: joinPaths(gon.relative_url_root || '', base),
routes: [
{
path: `(/-)?/tree/${escape(baseRef)}/:path*`,
path: `(/-)?/tree/(${encodeURIComponent(baseRef)}|${baseRef})/:path*`,
name: 'treePath',
component: TreePage,
props: route => ({

View File

@ -8,6 +8,32 @@
}
}
.groups-list {
@include basic-list;
display: flex;
flex-direction: column;
margin: 0;
.group-row-contents .controls > .btn:last-child {
margin: 0;
}
li {
.title {
font-weight: 600;
}
a {
color: $gray-900;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
.group-root-path {
max-width: 40vw;
overflow: hidden;
@ -15,11 +41,6 @@
word-wrap: nowrap;
}
.content-list .group-name {
font-weight: $gl-font-weight-bold;
color: $pages-group-name-color;
}
.group-row {
@include basic-list-stats;
@ -322,10 +343,6 @@ table.pipeline-project-metrics tr td {
}
}
.content-list li:last-child {
padding-bottom: 0;
}
.group-list-tree {
margin-bottom: 0;
margin-left: 30px;

View File

@ -4,6 +4,11 @@ module SnippetsActions
extend ActiveSupport::Concern
def edit
# We need to load some info from the existing blob
snippet.content = blob.data
snippet.file_name = blob.path
render 'edit'
end
def raw
@ -25,6 +30,18 @@ module SnippetsActions
private
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def blob
return unless snippet
@blob ||= if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def convert_line_endings(content)
params[:line_ending] == 'raw' ? content : content.gsub(/\r\n/, "\n")
end

View File

@ -121,16 +121,6 @@ class Projects::SnippetsController < Projects::ApplicationController
alias_method :awardable, :snippet
alias_method :spammable, :snippet
def blob
return unless snippet
@blob ||= if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
end
def spammable_path
project_snippet_path(@project, @snippet)
end

View File

@ -126,16 +126,6 @@ class SnippetsController < ApplicationController
alias_method :awardable, :snippet
alias_method :spammable, :snippet
def blob
return unless snippet
@blob ||= if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
end
def spammable_path
snippet_path(@snippet)
end

View File

@ -2,7 +2,7 @@
.empty-state.hidden
= render "shared/groups/empty_state"
%ul.content-list{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
%section{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
.js-groups-list-holder
.loading-container.text-center.prepend-top-20
.spinner.spinner-md

View File

@ -0,0 +1,5 @@
---
title: Add a DB column to track external issue and epic ids when importing from external sources
merge_request: 27522
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Fix filtered search tokenization
merge_request: 27648
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Show the proper information in snippet edit form
merge_request: 27479
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Fixes the repository Vue router not working with Chinese characters
merge_request: 27494
author:
type: fixed

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class AddExternalKeyToIssuesTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :issues, :external_key, :string, limit: 255
end
end
def down
with_lock_retries do
remove_column :issues, :external_key
end
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddIndexOnExternalKeyToIssuesTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index(:issues, [:project_id, :external_key], unique: true, where: 'external_key IS NOT NULL')
end
def down
remove_concurrent_index(:issues, [:project_id, :external_key])
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class AddExternalKeyToEpicsTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :epics, :external_key, :string, limit: 255
end
end
def down
with_lock_retries do
remove_column :epics, :external_key
end
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddIndexOnExternalKeyToEpicsTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index(:epics, [:group_id, :external_key], unique: true, where: 'external_key IS NOT NULL')
end
def down
remove_concurrent_index(:epics, [:group_id, :external_key])
end
end

View File

@ -1606,6 +1606,7 @@ ActiveRecord::Schema.define(version: 2020_03_19_203901) do
t.integer "start_date_sourcing_epic_id"
t.integer "due_date_sourcing_epic_id"
t.integer "health_status", limit: 2
t.string "external_key", limit: 255
t.index "group_id, ((iid)::character varying) varchar_pattern_ops", name: "index_epics_on_group_id_and_iid_varchar_pattern"
t.index ["assignee_id"], name: "index_epics_on_assignee_id"
t.index ["author_id"], name: "index_epics_on_author_id"
@ -1613,6 +1614,7 @@ ActiveRecord::Schema.define(version: 2020_03_19_203901) do
t.index ["due_date_sourcing_epic_id"], name: "index_epics_on_due_date_sourcing_epic_id", where: "(due_date_sourcing_epic_id IS NOT NULL)"
t.index ["due_date_sourcing_milestone_id"], name: "index_epics_on_due_date_sourcing_milestone_id"
t.index ["end_date"], name: "index_epics_on_end_date"
t.index ["group_id", "external_key"], name: "index_epics_on_group_id_and_external_key", unique: true, where: "(external_key IS NOT NULL)"
t.index ["group_id"], name: "index_epics_on_group_id"
t.index ["iid"], name: "index_epics_on_iid"
t.index ["lock_version"], name: "index_epics_on_lock_version", where: "(lock_version IS NULL)"
@ -2222,6 +2224,7 @@ ActiveRecord::Schema.define(version: 2020_03_19_203901) do
t.integer "duplicated_to_id"
t.integer "promoted_to_epic_id"
t.integer "health_status", limit: 2
t.string "external_key", limit: 255
t.index ["author_id", "id", "created_at"], name: "index_issues_on_author_id_and_id_and_created_at"
t.index ["author_id"], name: "index_issues_on_author_id"
t.index ["closed_by_id"], name: "index_issues_on_closed_by_id"
@ -2233,6 +2236,7 @@ ActiveRecord::Schema.define(version: 2020_03_19_203901) do
t.index ["moved_to_id"], name: "index_issues_on_moved_to_id", where: "(moved_to_id IS NOT NULL)"
t.index ["project_id", "created_at", "id", "state_id"], name: "idx_issues_on_project_id_and_created_at_and_id_and_state_id"
t.index ["project_id", "due_date", "id", "state_id"], name: "idx_issues_on_project_id_and_due_date_and_id_and_state_id", where: "(due_date IS NOT NULL)"
t.index ["project_id", "external_key"], name: "index_issues_on_project_id_and_external_key", unique: true, where: "(external_key IS NOT NULL)"
t.index ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true
t.index ["project_id", "relative_position", "state_id", "id"], name: "idx_issues_on_project_id_and_rel_position_and_state_id_and_id", order: { id: :desc }
t.index ["project_id", "updated_at", "id", "state_id"], name: "idx_issues_on_project_id_and_updated_at_and_id_and_state_id"

View File

@ -268,6 +268,11 @@ See [Analyzer settings](#analyzer-settings) for the complete list of available o
SAST can be [configured](#customizing-the-sast-settings) using environment variables.
#### Custom Certificate Authority
To trust a custom Certificate Authority, set the `ADDITIONAL_CA_CERT_BUNDLE` variable to the bundle
of CA certs that you want to trust within the SAST environment.
#### Docker images
The following are Docker image-related variables.

View File

@ -144,7 +144,7 @@ module Gitlab
%r{\A(ee/)?spec/(?!javascripts|frontend)[^/]+} => :backend,
%r{\A(ee/)?vendor/(?!assets)[^/]+} => :backend,
%r{\A(ee/)?vendor/(languages\.yml|licenses\.csv)\z} => :backend,
%r{\A(Gemfile|Gemfile.lock|Procfile|Rakefile)\z} => :backend,
%r{\A(Gemfile|Gemfile.lock|Rakefile)\z} => :backend,
%r{\A[A-Z_]+_VERSION\z} => :backend,
%r{\A\.rubocop(_todo)?\.yml\z} => :backend,

View File

@ -77,7 +77,7 @@ describe 'Dashboard Groups page', :js do
expect(page).to have_content(group.name)
expect(page).to have_content(nested_group.parent.name)
expect(page).not_to have_content(another_group.name)
expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
expect(page.all('.js-groups-list-holder .groups-list li').length).to eq 2
end
end

View File

@ -47,26 +47,26 @@ describe 'Explore Groups page', :js do
expect(page).to have_content(group.full_name)
expect(page).to have_content(public_group.full_name)
expect(page).not_to have_content(private_group.full_name)
expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
expect(page.all('.js-groups-list-holder .groups-list li').length).to eq 2
end
it 'shows non-archived projects count' do
# Initially project is not archived
expect(find('.js-groups-list-holder .content-list li:first-child .stats .number-projects')).to have_text("1")
expect(find('.js-groups-list-holder .groups-list li:first-child .stats .number-projects')).to have_text("1")
# Archive project
::Projects::UpdateService.new(empty_project, user, archived: true).execute
visit explore_groups_path
# Check project count
expect(find('.js-groups-list-holder .content-list li:first-child .stats .number-projects')).to have_text("0")
expect(find('.js-groups-list-holder .groups-list li:first-child .stats .number-projects')).to have_text("0")
# Unarchive project
::Projects::UpdateService.new(empty_project, user, archived: false).execute
visit explore_groups_path
# Check project count
expect(find('.js-groups-list-holder .content-list li:first-child .stats .number-projects')).to have_text("1")
expect(find('.js-groups-list-holder .groups-list li:first-child .stats .number-projects')).to have_text("1")
end
describe 'landing component' do

View File

@ -162,4 +162,17 @@ describe 'Visual tokens', :js do
])
end
end
it 'does retain hint token when mix of typing and clicks are performed' do
input_filtered_search('label:', extra_space: false, submit: false)
expect(page).to have_css('#js-dropdown-operator', visible: true)
find('#js-dropdown-operator li[data-value="="]').click
token = page.all('.tokens-container .js-visual-token')[0]
expect(token.find('.name').text).to eq('Label')
expect(token.find('.operator').text).to eq('=')
end
end

View File

@ -2,13 +2,17 @@
require 'spec_helper'
describe 'Projects > Snippets > User updates a snippet' do
describe 'Projects > Snippets > User updates a snippet', :js do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
let_it_be(:snippet, reload: true) { create(:project_snippet, :repository, project: project, author: user) }
let(:version_snippet_enabled) { true }
before do
stub_feature_flags(snippets_vue: false)
stub_feature_flags(version_snippets: version_snippet_enabled)
project.add_maintainer(user)
sign_in(user)
@ -17,6 +21,29 @@ describe 'Projects > Snippets > User updates a snippet' do
page.within('.detail-page-header') do
first(:link, 'Edit').click
end
wait_for_all_requests
end
it 'displays the snippet blob path and content' do
blob = snippet.blobs.first
aggregate_failures do
expect(page.find_field('project_snippet_file_name').value).to eq blob.path
expect(page.find('.file-content')).to have_content(blob.data.strip)
expect(page.find('.snippet-file-content', visible: false).value).to eq blob.data
end
end
context 'when feature flag :version_snippets is disabled' do
let(:version_snippet_enabled) { false }
it 'displays the snippet file_name and content' do
aggregate_failures do
expect(page.find_field('project_snippet_file_name').value).to eq snippet.file_name
expect(page.find('.file-content')).to have_content(snippet.content)
expect(page.find('.snippet-file-content', visible: false).value).to eq snippet.content
end
end
end
it 'updates a snippet' do

View File

@ -5,18 +5,43 @@ require 'spec_helper'
describe 'User edits snippet', :js do
include DropzoneHelper
let(:file_name) { 'test.rb' }
let(:content) { 'puts "test"' }
let_it_be(:file_name) { 'test.rb' }
let_it_be(:content) { 'puts "test"' }
let_it_be(:user) { create(:user) }
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, author: user) }
let_it_be(:snippet, reload: true) { create(:personal_snippet, :repository, :public, file_name: file_name, content: content, author: user) }
let(:version_snippet_enabled) { true }
before do
stub_feature_flags(snippets_vue: false)
stub_feature_flags(version_snippets: version_snippet_enabled)
sign_in(user)
visit edit_snippet_path(snippet)
wait_for_requests
wait_for_all_requests
end
it 'displays the snippet blob path and content' do
blob = snippet.blobs.first
aggregate_failures do
expect(page.find_field('personal_snippet_file_name').value).to eq blob.path
expect(page.find('.file-content')).to have_content(blob.data.strip)
expect(page.find('.snippet-file-content', visible: false).value).to eq blob.data
end
end
context 'when feature flag :version_snippets is disabled' do
let(:version_snippet_enabled) { false }
it 'displays the snippet file_name and content' do
aggregate_failures do
expect(page.find_field('personal_snippet_file_name').value).to eq file_name
expect(page.find('.file-content')).to have_content(content)
expect(page.find('.snippet-file-content', visible: false).value).to eq content
end
end
end
it 'updates the snippet' do

View File

@ -206,7 +206,6 @@ describe Gitlab::Danger::Helper do
'Gemfile' | :backend
'Gemfile.lock' | :backend
'Procfile' | :backend
'Rakefile' | :backend
'FOO_VERSION' | :backend

View File

@ -31,6 +31,7 @@ Issue:
- last_edited_by_id
- discussion_locked
- health_status
- external_key
Event:
- id
- target_type
@ -843,6 +844,7 @@ Epic:
- start_date_sourcing_epic_id
- due_date_sourcing_epic_id
- health_status
- external_key
EpicIssue:
- id
- relative_position