Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-10-08 12:08:31 +00:00
parent 33882b0ed1
commit 2f5c5b1081
126 changed files with 1478 additions and 958 deletions

View file

@ -3,6 +3,8 @@
stage: notify
dependencies: []
cache: {}
variables:
MERGE_REQUEST_URL: ${CI_MERGE_REQUEST_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}
before_script:
- apk update && apk add git curl bash
@ -16,8 +18,19 @@ notify-update-gitaly:
variables:
NOTIFY_CHANNEL: g_create_gitaly
GITALY_UPDATE_BRANCH: release-tools/update-gitaly
MERGE_REQUEST_URL: ${CI_MERGE_REQUEST_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}
script:
- echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL}"
- echo "CI_PIPELINE_URL is ${CI_PIPELINE_URL}"
- scripts/slack ${NOTIFY_CHANNEL} "☠️ \`${GITALY_UPDATE_BRANCH}\` failed! ☠️ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing
- scripts/slack ${NOTIFY_CHANNEL} "☠️ \`${GITALY_UPDATE_BRANCH}\` failed! ☠️ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing "GitLab QA Bot"
notify-security-pipeline:
extends:
- .notify-slack
- .delivery:rules:security-pipeline-merge-result-failure
variables:
NOTIFY_CHANNEL: f_upcoming_release
script:
- echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL}"
- echo "CI_PIPELINE_URL is ${CI_PIPELINE_URL}"
# <!subteam^S0127FU8PDE> mentions the `@release-managers` group
- scripts/slack ${NOTIFY_CHANNEL} "<!subteam^S0127FU8PDE> ☠️ Pipeline for merged result failed! ☠️ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing "GitLab Release Tools Bot"

View file

@ -73,6 +73,12 @@
.if-rspec-fail-fast-skipped: &if-rspec-fail-fast-skipped
if: '$CI_MERGE_REQUEST_TITLE =~ /SKIP RSPEC FAIL-FAST/'
# For Security merge requests, the gitlab-release-tools-bot triggers a new
# pipeline for the "Pipelines for merged results" feature. If the pipeline
# fails, we notify release managers.
.if-security-pipeline-merge-result: &if-security-pipeline-merge-result
if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH && $CI_PROJECT_NAMESPACE == "gitlab-org/security" && $GITLAB_USER_LOGIN == "gitlab-release-tools-bot"'
####################
# Changes patterns #
####################
@ -285,6 +291,14 @@
when: manual
allow_failure: true
##################
# Delivery rules #
##################
.delivery:rules:security-pipeline-merge-result-failure:
rules:
- <<: *if-security-pipeline-merge-result
when: on_failure
######################
# Dev fixtures rules #
######################

View file

@ -4,7 +4,6 @@
* Used in the environments table and the environment detail view.
*/
import $ from 'jquery';
import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
@ -42,7 +41,7 @@ export default {
},
methods: {
onClick() {
$(this.$el).tooltip('dispose');
this.$root.$emit('bv::hide::tooltip', this.$options.deleteEnvironmentTooltipId);
eventHub.$emit('requestDeleteEnvironment', this.environment);
},
onDeleteEnvironment(environment) {
@ -51,11 +50,12 @@ export default {
}
},
},
deleteEnvironmentTooltipId: 'delete-environment-button-tooltip',
};
</script>
<template>
<loading-button
v-gl-tooltip
v-gl-tooltip="{ id: $options.deleteEnvironmentTooltipId }"
:loading="isLoading"
:title="title"
:aria-label="title"

View file

@ -1,98 +0,0 @@
const entryTypeIcons = {
tree: 'folder',
commit: 'archive',
};
const fileTypeIcons = [
{ extensions: ['pdf'], name: 'file-pdf-o' },
{
extensions: [
'jpg',
'jpeg',
'jif',
'jfif',
'jp2',
'jpx',
'j2k',
'j2c',
'png',
'gif',
'tif',
'tiff',
'svg',
'ico',
'bmp',
],
name: 'file-image-o',
},
{
extensions: ['zip', 'zipx', 'tar', 'gz', 'bz', 'bzip', 'xz', 'rar', '7z'],
name: 'file-archive-o',
},
{ extensions: ['mp3', 'wma', 'ogg', 'oga', 'wav', 'flac', 'aac'], name: 'file-audio-o' },
{
extensions: [
'mp4',
'm4p',
'm4v',
'mpg',
'mp2',
'mpeg',
'mpe',
'mpv',
'm2v',
'avi',
'mkv',
'flv',
'ogv',
'mov',
'3gp',
'3g2',
],
name: 'file-video-o',
},
{ extensions: ['doc', 'dot', 'docx', 'docm', 'dotx', 'dotm', 'docb'], name: 'file-word-o' },
{
extensions: [
'xls',
'xlt',
'xlm',
'xlsx',
'xlsm',
'xltx',
'xltm',
'xlsb',
'xla',
'xlam',
'xll',
'xlw',
],
name: 'file-excel-o',
},
{
extensions: [
'ppt',
'pot',
'pps',
'pptx',
'pptm',
'potx',
'potm',
'ppam',
'ppsx',
'ppsm',
'sldx',
'sldm',
],
name: 'file-powerpoint-o',
},
];
export const getIconName = (type, path) => {
if (entryTypeIcons[type]) return entryTypeIcons[type];
const extension = path.split('.').pop();
const file = fileTypeIcons.find(t => t.extensions.some(ext => ext === extension));
return file ? file.name : 'file-text-o';
};

View file

@ -1,37 +0,0 @@
import { __ } from '~/locale';
import { parseUrlPathname, parseUrl } from '../lib/utils/common_utils';
function swapActiveState(activateBtn, deactivateBtn) {
activateBtn.classList.add('is-active');
deactivateBtn.classList.remove('is-active');
}
export default () => {
const shareBtn = document.querySelector('.js-share-btn');
if (shareBtn) {
const embedBtn = document.querySelector('.js-embed-btn');
const snippetUrlArea = document.querySelector('.js-snippet-url-area');
const embedAction = document.querySelector('.js-embed-action');
const dataUrl = snippetUrlArea.getAttribute('data-url');
snippetUrlArea.addEventListener('click', () => snippetUrlArea.select());
shareBtn.addEventListener('click', () => {
swapActiveState(shareBtn, embedBtn);
snippetUrlArea.value = dataUrl;
embedAction.innerText = __('Share');
});
embedBtn.addEventListener('click', () => {
const parser = parseUrl(dataUrl);
const url = `${parser.origin + parseUrlPathname(dataUrl)}`;
const params = parser.search;
const scriptTag = `<script src="${url}.js${params}"></script>`;
swapActiveState(embedBtn, shareBtn);
snippetUrlArea.value = scriptTag;
embedAction.innerText = __('Embed');
});
}
};

View file

@ -1,48 +1,13 @@
if (!gon.features.snippetsVue) {
const LineHighlighterModule = import('~/line_highlighter');
const BlobViewerModule = import('~/blob/viewer');
const ZenModeModule = import('~/zen_mode');
const SnippetEmbedModule = import('~/snippet/snippet_embed');
const initNotesModule = import('~/init_notes');
const loadAwardsHandlerModule = import('~/awards_handler');
import initNotes from '~/init_notes';
import loadAwardsHandler from '~/awards_handler';
import { SnippetShowInit } from '~/snippets';
import ZenMode from '~/zen_mode';
Promise.all([
LineHighlighterModule,
BlobViewerModule,
ZenModeModule,
SnippetEmbedModule,
initNotesModule,
loadAwardsHandlerModule,
])
.then(
([
{ default: LineHighlighter },
{ default: BlobViewer },
{ default: ZenMode },
{ default: SnippetEmbed },
{ default: initNotes },
{ default: loadAwardsHandler },
]) => {
new LineHighlighter(); // eslint-disable-line no-new
new BlobViewer(); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new
SnippetEmbed();
initNotes();
loadAwardsHandler();
},
)
.catch(() => {});
} else {
import('~/snippets')
.then(({ SnippetShowInit }) => {
document.addEventListener('DOMContentLoaded', () => {
SnippetShowInit();
})
.then(() => {
return Promise.all([import('~/init_notes'), import('~/awards_handler')]);
})
.then(([{ default: initNotes }, { default: loadAwardsHandler }]) => {
initNotes();
loadAwardsHandler();
})
.catch(() => {});
}
// eslint-disable-next-line no-new
new ZenMode();
});

View file

@ -60,7 +60,7 @@ export default {
class="gl-dropdown-text-py-0 gl-dropdown-text-block"
data-testid="input"
>
<gl-form-input-group :value="value" readonly select-on-click>
<gl-form-input-group :value="value" readonly select-on-click :aria-label="name">
<template #append>
<gl-button
v-gl-tooltip.hover

View file

@ -0,0 +1,121 @@
import { deprecatedCreateFlash as createFlash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import Api from '~/api';
import * as types from './mutation_types';
export const setEndpoints = ({ commit }, params) => {
const { milestonesEndpoint, labelsEndpoint, groupEndpoint, projectEndpoint } = params;
commit(types.SET_MILESTONES_ENDPOINT, milestonesEndpoint);
commit(types.SET_LABELS_ENDPOINT, labelsEndpoint);
commit(types.SET_GROUP_ENDPOINT, groupEndpoint);
commit(types.SET_PROJECT_ENDPOINT, projectEndpoint);
};
export function fetchBranches({ commit, state }, search = '') {
const { projectEndpoint } = state;
commit(types.REQUEST_BRANCHES);
return Api.branches(projectEndpoint, search)
.then(response => {
commit(types.RECEIVE_BRANCHES_SUCCESS, response.data);
return response;
})
.catch(({ response }) => {
const { status } = response;
commit(types.RECEIVE_BRANCHES_ERROR, status);
createFlash(__('Failed to load branches. Please try again.'));
});
}
export const fetchMilestones = ({ commit, state }, search_title = '') => {
commit(types.REQUEST_MILESTONES);
const { milestonesEndpoint } = state;
return axios
.get(milestonesEndpoint, { params: { search_title } })
.then(response => {
commit(types.RECEIVE_MILESTONES_SUCCESS, response.data);
return response;
})
.catch(({ response }) => {
const { status } = response;
commit(types.RECEIVE_MILESTONES_ERROR, status);
createFlash(__('Failed to load milestones. Please try again.'));
});
};
export const fetchLabels = ({ commit, state }, search = '') => {
commit(types.REQUEST_LABELS);
return axios
.get(state.labelsEndpoint, { params: { search } })
.then(response => {
commit(types.RECEIVE_LABELS_SUCCESS, response.data);
return response;
})
.catch(({ response }) => {
const { status } = response;
commit(types.RECEIVE_LABELS_ERROR, status);
createFlash(__('Failed to load labels. Please try again.'));
});
};
function fetchUser(options = {}) {
const { commit, projectEndpoint, groupEndpoint, query, action, errorMessage } = options;
commit(`REQUEST_${action}`);
let fetchUserPromise;
if (projectEndpoint) {
fetchUserPromise = Api.projectUsers(projectEndpoint, query).then(data => ({ data }));
} else {
fetchUserPromise = Api.groupMembers(groupEndpoint, { query });
}
return fetchUserPromise
.then(response => {
commit(`RECEIVE_${action}_SUCCESS`, response.data);
return response;
})
.catch(({ response }) => {
const { status } = response;
commit(`RECEIVE_${action}_ERROR`, status);
createFlash(errorMessage);
});
}
export const fetchAuthors = ({ commit, state }, query = '') => {
const { projectEndpoint, groupEndpoint } = state;
return fetchUser({
commit,
query,
projectEndpoint,
groupEndpoint,
action: 'AUTHORS',
errorMessage: __('Failed to load authors. Please try again.'),
});
};
export const fetchAssignees = ({ commit, state }, query = '') => {
const { projectEndpoint, groupEndpoint } = state;
return fetchUser({
commit,
query,
projectEndpoint,
groupEndpoint,
action: 'ASSIGNEES',
errorMessage: __('Failed to load assignees. Please try again.'),
});
};
export const setFilters = ({ commit, dispatch }, filters) => {
commit(types.SET_SELECTED_FILTERS, filters);
return dispatch('setFilters', filters, { root: true });
};
export const initialize = ({ commit }, initialFilters) => {
commit(types.SET_SELECTED_FILTERS, initialFilters);
};

View file

@ -0,0 +1,10 @@
import state from './state';
import * as actions from './actions';
import mutations from './mutations';
export default {
namespaced: true,
actions,
mutations,
state: state(),
};

View file

@ -0,0 +1,26 @@
export const SET_MILESTONES_ENDPOINT = 'SET_MILESTONES_ENDPOINT';
export const SET_LABELS_ENDPOINT = 'SET_LABELS_ENDPOINT';
export const SET_GROUP_ENDPOINT = 'SET_GROUP_ENDPOINT';
export const SET_PROJECT_ENDPOINT = 'SET_PROJECT_ENDPOINT';
export const REQUEST_BRANCHES = 'REQUEST_BRANCHES';
export const RECEIVE_BRANCHES_SUCCESS = 'RECEIVE_BRANCHES_SUCCESS';
export const RECEIVE_BRANCHES_ERROR = 'RECEIVE_BRANCHES_ERROR';
export const REQUEST_MILESTONES = 'REQUEST_MILESTONES';
export const RECEIVE_MILESTONES_SUCCESS = 'RECEIVE_MILESTONES_SUCCESS';
export const RECEIVE_MILESTONES_ERROR = 'RECEIVE_MILESTONES_ERROR';
export const REQUEST_LABELS = 'REQUEST_LABELS';
export const RECEIVE_LABELS_SUCCESS = 'RECEIVE_LABELS_SUCCESS';
export const RECEIVE_LABELS_ERROR = 'RECEIVE_LABELS_ERROR';
export const REQUEST_AUTHORS = 'REQUEST_AUTHORS';
export const RECEIVE_AUTHORS_SUCCESS = 'RECEIVE_AUTHORS_SUCCESS';
export const RECEIVE_AUTHORS_ERROR = 'RECEIVE_AUTHORS_ERROR';
export const REQUEST_ASSIGNEES = 'REQUEST_ASSIGNEES';
export const RECEIVE_ASSIGNEES_SUCCESS = 'RECEIVE_ASSIGNEES_SUCCESS';
export const RECEIVE_ASSIGNEES_ERROR = 'RECEIVE_ASSIGNEES_ERROR';
export const SET_SELECTED_FILTERS = 'SET_SELECTED_FILTERS';

View file

@ -0,0 +1,109 @@
import * as types from './mutation_types';
export default {
[types.SET_SELECTED_FILTERS](state, params) {
const {
selectedSourceBranch = null,
selectedSourceBranchList = [],
selectedTargetBranch = null,
selectedTargetBranchList = [],
selectedAuthor = null,
selectedAuthorList = [],
selectedMilestone = null,
selectedMilestoneList = [],
selectedAssignee = null,
selectedAssigneeList = [],
selectedLabel = null,
selectedLabelList = [],
} = params;
state.branches.source.selected = selectedSourceBranch;
state.branches.source.selectedList = selectedSourceBranchList;
state.branches.target.selected = selectedTargetBranch;
state.branches.target.selectedList = selectedTargetBranchList;
state.authors.selected = selectedAuthor;
state.authors.selectedList = selectedAuthorList;
state.assignees.selected = selectedAssignee;
state.assignees.selectedList = selectedAssigneeList;
state.milestones.selected = selectedMilestone;
state.milestones.selectedList = selectedMilestoneList;
state.labels.selected = selectedLabel;
state.labels.selectedList = selectedLabelList;
},
[types.SET_MILESTONES_ENDPOINT](state, milestonesEndpoint) {
state.milestonesEndpoint = milestonesEndpoint;
},
[types.SET_LABELS_ENDPOINT](state, labelsEndpoint) {
state.labelsEndpoint = labelsEndpoint;
},
[types.SET_GROUP_ENDPOINT](state, groupEndpoint) {
state.groupEndpoint = groupEndpoint;
},
[types.SET_PROJECT_ENDPOINT](state, projectEndpoint) {
state.projectEndpoint = projectEndpoint;
},
[types.REQUEST_MILESTONES](state) {
state.milestones.isLoading = true;
},
[types.RECEIVE_MILESTONES_SUCCESS](state, data) {
state.milestones.isLoading = false;
state.milestones.data = data;
state.milestones.errorCode = null;
},
[types.RECEIVE_MILESTONES_ERROR](state, errorCode) {
state.milestones.isLoading = false;
state.milestones.errorCode = errorCode;
state.milestones.data = [];
},
[types.REQUEST_LABELS](state) {
state.labels.isLoading = true;
},
[types.RECEIVE_LABELS_SUCCESS](state, data) {
state.labels.isLoading = false;
state.labels.data = data;
state.labels.errorCode = null;
},
[types.RECEIVE_LABELS_ERROR](state, errorCode) {
state.labels.isLoading = false;
state.labels.errorCode = errorCode;
state.labels.data = [];
},
[types.REQUEST_AUTHORS](state) {
state.authors.isLoading = true;
},
[types.RECEIVE_AUTHORS_SUCCESS](state, data) {
state.authors.isLoading = false;
state.authors.data = data;
state.authors.errorCode = null;
},
[types.RECEIVE_AUTHORS_ERROR](state, errorCode) {
state.authors.isLoading = false;
state.authors.errorCode = errorCode;
state.authors.data = [];
},
[types.REQUEST_ASSIGNEES](state) {
state.assignees.isLoading = true;
},
[types.RECEIVE_ASSIGNEES_SUCCESS](state, data) {
state.assignees.isLoading = false;
state.assignees.data = data;
state.assignees.errorCode = null;
},
[types.RECEIVE_ASSIGNEES_ERROR](state, errorCode) {
state.assignees.isLoading = false;
state.assignees.errorCode = errorCode;
state.assignees.data = [];
},
[types.REQUEST_BRANCHES](state) {
state.branches.isLoading = true;
},
[types.RECEIVE_BRANCHES_SUCCESS](state, data) {
state.branches.isLoading = false;
state.branches.data = data;
state.branches.errorCode = null;
},
[types.RECEIVE_BRANCHES_ERROR](state, errorCode) {
state.branches.isLoading = false;
state.branches.errorCode = errorCode;
state.branches.data = [];
},
};

View file

@ -0,0 +1,47 @@
export default () => ({
milestonesEndpoint: '',
labelsEndpoint: '',
groupEndpoint: '',
projectEndpoint: '',
branches: {
isLoading: false,
errorCode: null,
data: [],
source: {
selected: null,
selectedList: [],
},
target: {
selected: null,
selectedList: [],
},
},
milestones: {
isLoading: false,
errorCode: null,
data: [],
selected: null,
selectedList: [],
},
labels: {
isLoading: false,
errorCode: null,
data: [],
selected: null,
selectedList: [],
},
authors: {
isLoading: false,
errorCode: null,
data: [],
selected: null,
selectedList: [],
},
assignees: {
isLoading: false,
errorCode: null,
data: [],
selected: null,
selectedList: [],
},
});

View file

@ -18,14 +18,6 @@
}
}
.markdown-snippet-copy {
position: fixed;
top: -10px;
left: -10px;
max-height: 0;
max-width: 0;
}
.snippet-file-content {
border-radius: 3px;
@ -45,21 +37,6 @@
min-height: $header-height;
}
.snippet-actions {
@include media-breakpoint-up(sm) {
float: right;
}
}
.snippet-scope-menu .btn-success {
margin-top: 15px;
}
.embed-snippet {
padding-right: 0;
padding-top: $gl-padding;
.embed-toggle-list li button {
padding: 8px 40px;
}
}

View file

@ -34,7 +34,7 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
private
def autocomplete_service
@autocomplete_service ||= ::Projects::AutocompleteService.new(@project, current_user)
@autocomplete_service ||= ::Projects::AutocompleteService.new(@project, current_user, params)
end
def target

View file

@ -9,6 +9,7 @@ module Projects
respond_to :json, only: [:reset_alerting_token, :reset_pagerduty_token]
helper_method :error_tracking_setting
helper_method :tracing_setting
def update
result = ::Projects::Operations::UpdateService.new(project, current_user, update_params).execute
@ -17,15 +18,6 @@ module Projects
render_update_response(result)
end
# overridden in EE
def track_events(result)
if result[:status] == :success
::Gitlab::Tracking::IncidentManagement.track_from_params(
update_params[:incident_management_setting_attributes]
)
end
end
def reset_alerting_token
result = ::Projects::Operations::UpdateService
.new(project, current_user, alerting_params)
@ -55,6 +47,24 @@ module Projects
private
def track_events(result)
if result[:status] == :success
::Gitlab::Tracking::IncidentManagement.track_from_params(
update_params[:incident_management_setting_attributes]
)
track_tracing_external_url
end
end
def track_tracing_external_url
external_url_previous_change = project&.tracing_setting&.external_url_previous_change
return unless external_url_previous_change
return unless external_url_previous_change[0].blank? && external_url_previous_change[1].present?
::Gitlab::Tracking.event('project:operations:tracing', 'external_url_populated')
end
def alerting_params
{ alerting_setting_attributes: { regenerate_token: true } }
end
@ -106,6 +116,10 @@ module Projects
project.build_error_tracking_setting
end
def tracing_setting
@tracing_setting ||= project.tracing_setting || project.build_tracing_setting
end
def update_params
params.require(:project).permit(permitted_project_params)
end
@ -124,7 +138,8 @@ module Projects
project: [:slug, :name, :organization_slug, :organization_name]
],
grafana_integration_attributes: [:token, :grafana_url, :enabled]
grafana_integration_attributes: [:token, :grafana_url, :enabled],
tracing_setting_attributes: [:external_url]
}
if Feature.enabled?(:settings_operations_prometheus_service, project)

View file

@ -159,6 +159,7 @@ module NotesHelper
members: autocomplete,
issues: autocomplete,
mergeRequests: autocomplete,
vulnerabilities: autocomplete,
epics: autocomplete,
milestones: autocomplete,
labels: autocomplete

View file

@ -32,31 +32,6 @@ module SnippetsHelper
end
end
# Get an array of line numbers surrounding a matching
# line, bounded by min/max.
#
# @returns Array of line numbers
def bounded_line_numbers(line, min, max, surrounding_lines)
lower = line - surrounding_lines > min ? line - surrounding_lines : min
upper = line + surrounding_lines < max ? line + surrounding_lines : max
(lower..upper).to_a
end
def snippet_embed_tag(snippet)
content_tag(:script, nil, src: gitlab_snippet_url(snippet, format: :js))
end
def snippet_embed_input(snippet)
content_tag(:input,
nil,
type: :text,
readonly: true,
class: 'js-snippet-url-area snippet-embed-input form-control',
data: { url: gitlab_snippet_url(snippet) },
value: snippet_embed_tag(snippet),
autocomplete: 'off')
end
def snippet_badge(snippet)
return unless attrs = snippet_badge_attributes(snippet)

View file

@ -23,8 +23,6 @@ module VisibilityLevelHelper
project_visibility_level_description(level)
when Group
group_visibility_level_description(level)
when Snippet
snippet_visibility_level_description(level, form_model)
end
end
@ -50,21 +48,6 @@ module VisibilityLevelHelper
end
end
def snippet_visibility_level_description(level, snippet = nil)
case level
when Gitlab::VisibilityLevel::PRIVATE
if snippet.is_a? ProjectSnippet
_("The snippet is visible only to project members.")
else
_("The snippet is visible only to me.")
end
when Gitlab::VisibilityLevel::INTERNAL
_("The snippet is visible to any logged in user.")
when Gitlab::VisibilityLevel::PUBLIC
_("The snippet can be accessed without any authentication.")
end
end
# Note: these messages closely mirror the form validation strings found in the project
# model and any changes or additons to these may also need to be made there.
def disallowed_project_visibility_level_description(level, project)

View file

@ -22,7 +22,7 @@ module Mentionable
def self.default_pattern
strong_memoize(:default_pattern) do
issue_pattern = Issue.reference_pattern
link_patterns = Regexp.union([Issue, Commit, MergeRequest, Epic].map(&:link_reference_pattern).compact)
link_patterns = Regexp.union([Issue, Commit, MergeRequest, Epic, Vulnerability].map(&:link_reference_pattern).compact)
reference_pattern(link_patterns, issue_pattern)
end
end

View file

@ -5,6 +5,10 @@
class Vulnerability < ApplicationRecord
include IgnorableColumns
def self.link_reference_pattern
nil
end
def self.reference_prefix
'+'
end

View file

@ -63,7 +63,7 @@ module Issues
end
def queue_copy_designs
return unless copy_designs_enabled? && original_entity.designs.present?
return unless original_entity.designs.present?
response = DesignManagement::CopyDesignCollection::QueueService.new(
current_user,
@ -74,11 +74,6 @@ module Issues
log_error(response.message) if response.error?
end
def copy_designs_enabled?
Feature.enabled?(:design_management_copy_designs, old_project) &&
Feature.enabled?(:design_management_copy_designs, target_project)
end
def mark_as_moved
original_entity.update(moved_to: new_entity)
end

View file

@ -1,36 +0,0 @@
- return unless current_user
.d-none.d-sm-block
- if can?(current_user, :update_snippet, @snippet)
= link_to edit_project_snippet_path(@project, @snippet), class: "btn btn-grouped" do
= _('Edit')
- if can?(current_user, :admin_snippet, @snippet)
= link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: _("Are you sure?") }, class: "btn btn-grouped btn-inverted btn-remove", title: _('Delete Snippet') do
= _('Delete')
- if can?(current_user, :create_snippet, @project)
= link_to new_project_snippet_path(@project), class: 'btn btn-grouped btn-inverted btn-success', title: _("New snippet") do
= _('New snippet')
- if @snippet.submittable_as_spam_by?(current_user)
= link_to _('Submit as spam'), mark_as_spam_project_snippet_path(@project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: _('Submit as spam')
- if can?(current_user, :create_snippet, @project) || can?(current_user, :update_snippet, @snippet)
.d-block.d-sm-none.dropdown
%button.btn.btn-default.btn-block.gl-mb-0.gl-mt-2{ data: { toggle: "dropdown" } }
= _('Options')
= icon('caret-down')
.dropdown-menu.dropdown-menu-full-width
%ul
- if can?(current_user, :create_snippet, @project)
%li
= link_to new_project_snippet_path(@project), title: _("New snippet") do
= _('New snippet')
- if can?(current_user, :admin_snippet, @snippet)
%li
= link_to project_snippet_path(@project, @snippet), method: :delete, data: { confirm: _("Are you sure?") }, title: _('Delete Snippet') do
= _('Delete')
- if can?(current_user, :update_snippet, @snippet)
%li
= link_to edit_project_snippet_path(@project, @snippet) do
= _('Edit')
- if @snippet.submittable_as_spam_by?(current_user)
%li
= link_to _('Submit as spam'), mark_as_spam_project_snippet_path(@project, @snippet), method: :post

View file

@ -3,13 +3,7 @@
- breadcrumb_title @snippet.to_reference
- page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
- if Feature.enabled?(:snippets_vue, default_enabled: true)
#js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id} }
- else
= render 'shared/snippets/header'
.project-snippets
= render 'shared/snippets/blob', blob: @blob
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true

View file

@ -1,13 +0,0 @@
%article.file-holder.snippet-file-content
.js-file-title.file-title-flex-parent
= render 'projects/blob/header_content', blob: blob
.file-actions.d-none.d-sm-block
= render 'projects/blob/viewer_switcher', blob: blob
.btn-group{ role: "group" }<
= copy_blob_source_button(blob)
= open_raw_blob_button(blob)
= download_raw_snippet_button(@snippet)
= render 'projects/blob/content', blob: blob

View file

@ -1,45 +0,0 @@
.detail-page-header
.detail-page-header-body
.snippet-box.has-tooltip.inline.gl-mr-2{ title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: "body" } }
%span.sr-only
= visibility_level_label(@snippet.visibility_level)
= visibility_level_icon(@snippet.visibility_level)
%span.creator
= s_('Snippets|Authored %{time_ago} by %{author}').html_safe % { time_ago: time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago'), author: link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title", avatar_class: "d-none d-sm-inline") + user_status(@snippet.author) }
.detail-page-header-actions
- if @snippet.project_id?
= render "projects/snippets/actions"
- else
= render "snippets/actions"
.snippet-header.limited-header-width
%h2.snippet-title.gl-mt-0.mb-3
= markdown_field(@snippet, :title)
- if @snippet.description.present?
.description
.md
= markdown_field(@snippet, :description)
- if @snippet.updated_at != @snippet.created_at
= edited_time_ago_with_tooltip(@snippet, placement: 'bottom', exclude_author: true)
- if @snippet.embeddable?
.embed-snippet
.input-group
.input-group-prepend
%button.btn.gl-button.btn-svg.embed-toggle.input-group-text{ 'data-toggle': 'dropdown', type: 'button' }
%span.js-embed-action= _("Embed")
= sprite_icon('angle-down', size: 12, css_class: 'caret-down')
%ul.dropdown-menu.dropdown-menu-selectable.embed-toggle-list
%li
%button.js-embed-btn.btn.gl-button.btn-default-tertiary.is-active{ type: 'button' }
%strong.embed-toggle-list-item= _("Embed")
%li
%button.js-share-btn.btn.gl-button.btn-default-tertiary{ type: 'button' }
%strong.embed-toggle-list-item= _("Share")
= snippet_embed_input(@snippet)
.input-group-append
= clipboard_button(title: _('Copy'), class: 'js-clipboard-btn snippet-clipboard-btn btn btn-default', target: '.js-snippet-url-area')
.clearfix

View file

@ -1,35 +0,0 @@
- return unless current_user
.d-none.d-sm-block
- if can?(current_user, :update_snippet, @snippet)
= link_to edit_snippet_path(@snippet), class: "btn btn-grouped" do
= _("Edit")
- if can?(current_user, :admin_snippet, @snippet)
= link_to gitlab_snippet_path(@snippet), method: :delete, data: { confirm: _("Are you sure?") }, class: "btn btn-grouped btn-inverted btn-remove", title: _('Delete Snippet') do
= _("Delete")
- if can?(current_user, :create_snippet)
= link_to new_snippet_path, class: "btn btn-grouped btn-success btn-inverted", title: _("New snippet") do
= _("New snippet")
- if @snippet.submittable_as_spam_by?(current_user)
= link_to _('Submit as spam'), mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: _('Submit as spam')
.d-block.d-sm-none.dropdown
%button.btn.btn-default.btn-block.gl-mb-0.gl-mt-2{ data: { toggle: "dropdown" } }
= _("Options")
= icon('caret-down')
.dropdown-menu.dropdown-menu-full-width
%ul
- if can?(current_user, :create_snippet)
%li
= link_to new_snippet_path, title: _("New snippet") do
= _("New snippet")
- if can?(current_user, :admin_snippet, @snippet)
%li
= link_to gitlab_snippet_path(@snippet), method: :delete, data: { confirm: _("Are you sure?") }, title: _('Delete Snippet') do
= _("Delete")
- if can?(current_user, :update_snippet, @snippet)
%li
= link_to edit_snippet_path(@snippet) do
= _("Edit")
- if @snippet.submittable_as_spam_by?(current_user)
%li
= link_to _('Submit as spam'), mark_as_spam_snippet_path(@snippet), method: :post

View file

@ -4,13 +4,7 @@
- breadcrumb_title @snippet.to_reference
- page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
- if Feature.enabled?(:snippets_vue, default_enabled: true)
#js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id} }
- else
= render 'shared/snippets/header'
.personal-snippets
= render 'shared/snippets/blob', blob: @blob
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true

View file

@ -0,0 +1,5 @@
---
title: Designs are moved with an Issue that is moved
merge_request: 44524
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Fix full screen comment button on snippets
merge_request: 44083
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Remove jquery tooltip API call from delete environment button
merge_request: 44191
author:
type: other

View file

@ -0,0 +1,5 @@
---
title: Respect DNT when tracking experiments
merge_request: 44420
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Rate limit documentation for non-configurable limits
merge_request: 44003
author:
type: other

View file

@ -1,7 +0,0 @@
---
name: design_management_copy_designs
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41714
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/247062
group: group::knowledge
type: development
default_enabled: false

View file

@ -1,7 +0,0 @@
---
name: snippets_vue
introduced_by_url:
rollout_issue_url:
group: group::editor
type: development
default_enabled: true

View file

@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41304
rollout_issue_url:
group: group::continuous integration
type: ops
default_enabled: false
default_enabled: true

View file

@ -184,5 +184,4 @@ The following are additional validation tests we performed.
[Test Gitaly Cluster on a Geo Deployment](https://gitlab.com/gitlab-org/gitlab/-/issues/223210):
- Description: Tested a Geo deployment with Gitaly clusters configured on both the primary and secondary Geo sites. Triggered automatic Gitaly cluster failover on the primary Geo site, and ran end-to-end Geo tests. Then triggered Gitaly cluster failover on the secondary Geo site, and re-ran the end-to-end Geo tests.
- Outcome: Successful end-to-end tests before and after Gitaly cluster failover on the primary site, and before and after Gitaly cluster failover on the secondary site.

View file

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View file

@ -13,7 +13,7 @@ described, it is possible to adapt these instructions to your needs.
## Architecture overview
![Geo multi-node diagram](../../high_availability/img/geo-ha-diagram.png)
![Geo multi-node diagram](img/geo-ha-diagram.png)
_[diagram source - GitLab employees only](https://docs.google.com/drawings/d/1z0VlizKiLNXVVVaERFwgsIOuEgjcUqDTWPdQYsE7Z4c/edit)_

View file

@ -1,7 +0,0 @@
---
redirect_to: ../reference_architectures/index.md
---
# Reference Architectures
This document was moved to [another location](../reference_architectures/index.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: 'database.md'
---
This document was moved to [another location](../postgresql/index.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../consul.md
---
This document was moved to [another location](../consul.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../postgresql/index.md'
---
This document was moved to [another location](../postgresql/index.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../gitaly/index.md
---
This document was moved to [another location](../gitaly/index.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../reference_architectures/index.md
---
This document was moved to [another location](../reference_architectures/index.md).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View file

@ -1,5 +0,0 @@
---
redirect_to: ../load_balancer.md
---
This document was moved to [another location](../load_balancer.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../monitoring/prometheus/index.md
---
This document was moved to [another location](../monitoring/prometheus/index.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../nfs.md
---
This document was moved to [another location](../nfs.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../nfs.md
---
This document was moved to [another location](../nfs.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../object_storage.md'
---
This document was moved to [another location](../object_storage.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../postgresql/pgbouncer.md
---
This document was moved to [another location](../postgresql/pgbouncer.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../redis/index.md
---
This document was moved to [another location](../redis/index.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../redis/replication_and_failover_external.md
---
This document was moved to [another location](../redis/replication_and_failover_external.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: ../sidekiq.md
---
This document was moved to [another location](../sidekiq.md).

View file

@ -1582,8 +1582,8 @@ On each node perform the following:
```
1. Save the `/etc/gitlab/gitlab-secrets.json` file from one of the two
application nodes and install it on the other application node and the
[Gitaly node](#configure-gitaly) and
application nodes and install it on the other application node, the
[Gitaly node](#configure-gitaly) and the [Sidekiq node](#configure-sidekiq) and
[reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
1. Verify the GitLab services are running:

View file

@ -1581,8 +1581,8 @@ On each node perform the following:
```
1. Save the `/etc/gitlab/gitlab-secrets.json` file from one of the two
application nodes and install it on the other application node and the
[Gitaly node](#configure-gitaly) and
application nodes and install it on the other application node, the
[Gitaly node](#configure-gitaly) and the [Sidekiq node](#configure-sidekiq) and
[reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
1. Verify the GitLab services are running:

View file

@ -444,6 +444,8 @@ Recommendations:
metric's name and week of the year, `2020-33-{metric_name}`.
- Use a [feature flag](../../operations/feature_flags.md) to have a control over the impact when
adding new metrics.
- Feature flags should be [default on](../documentation/feature_flags.md#criteria)
before final release to ensure we receive data from self-managed instances.
##### Known events in usage data payload

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/bamboo.md'
---
This document was moved to [another location](../user/project/integrations/bamboo.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/bugzilla.md'
---
This document was moved to [another location](../user/project/integrations/bugzilla.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/emails_on_push.md'
---
This document was moved to [another location](../user/project/integrations/emails_on_push.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/hipchat.md'
---
This document was moved to [another location](../user/project/integrations/hipchat.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/irker.md'
---
This document was moved to [another location](../user/project/integrations/irker.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/jira.md'
---
This document was moved to [another location](../user/project/integrations/jira.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/kubernetes.md'
---
This document was moved to [another location](../user/project/integrations/kubernetes.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/mattermost.md'
---
This document was moved to [another location](../user/project/integrations/mattermost.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/mattermost_slash_commands.md'
---
This document was moved to [another location](../user/project/integrations/mattermost_slash_commands.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/overview.md'
---
This document was moved to [another location](../user/project/integrations/overview.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/redmine.md'
---
This document was moved to [another location](../user/project/integrations/redmine.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/services_templates.md'
---
This document was moved to [another location](../user/project/integrations/services_templates.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/slack.md'
---
This document was moved to [another location](../user/project/integrations/slack.md).

View file

@ -1,5 +0,0 @@
---
redirect_to: '../user/project/integrations/slack_slash_commands.md'
---
This document was moved to [another location](../user/project/integrations/slack_slash_commands.md).

View file

@ -28,6 +28,25 @@ similarly mitigated by a rate limit.
- [Protected paths](../user/admin_area/settings/protected_paths.md).
- [Import/Export rate limits](../user/admin_area/settings/import_export_rate_limits.md).
## Non-configurable limits
### Repository archives
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25750) in GitLab 12.9.
There is a rate limit for [downloading repository archives](../api/repositories.md#get-file-archive),
which applies to the project and to the user initiating the download either through the UI or the API.
The **rate limit** is 5 requests per minute per user.
### Webhook Testing
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/commit/35bc85c3ca093fee58d60dacdc9ed1fd9a15adec) in GitLab 13.4.
There is a rate limit for [testing webhooks](../user/project/integrations/webhooks.md#testing-webhooks), which prevents abuse of the webhook functionality.
The **rate limit** is 5 requests per minute per user.
## Rack Attack initializer
This method of rate limiting is cumbersome, but has some advantages. It allows

View file

@ -6,26 +6,27 @@ type: reference
Your GitLab instance can perform HTTP POST requests on the following events:
- `group_create`
- `group_destroy`
- `group_rename`
- `key_create`
- `key_destroy`
- `project_create`
- `project_destroy`
- `project_rename`
- `project_transfer`
- `project_update`
- `repository_update`
- `user_add_to_group`
- `user_add_to_team`
- `user_remove_from_team`
- `user_update_for_team`
- `user_create`
- `user_destroy`
- `user_failed_login`
- `user_rename`
- `key_create`
- `key_destroy`
- `group_create`
- `group_destroy`
- `group_rename`
- `user_add_to_group`
- `user_remove_from_group`
- `user_remove_from_team`
- `user_rename`
- `user_update_for_group`
- `user_update_for_team`
The triggers for most of these are self-explanatory, but `project_update` and `project_rename` deserve some clarification: `project_update` is fired any time an attribute of a project is changed (name, description, tags, etc.) *unless* the `path` attribute is also changed. In that case, a `project_rename` is triggered instead (so that, for instance, if all you care about is the repository URL, you can just listen for `project_rename`).

View file

@ -608,6 +608,11 @@ dropped and users get
To help avoid abuse, project and group imports, exports, and export downloads are rate limited. See [Project import/export rate limits](../../user/project/settings/import_export.md#rate-limits) and [Group import/export rate limits](../../user/group/settings/import_export.md#rate-limits) for details.
### Non-configurable limits
See [non-configurable limits](../../security/rate_limits.md#non-configurable-limits) for information on
rate limits that are not configurable, and therefore also used on GitLab.com.
## GitLab.com Logging
We use [Fluentd](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#fluentd) to parse our logs. Fluentd sends our logs to

View file

@ -72,9 +72,7 @@ to add an issue to an epic, reorder issues, move issues between epics, or promot
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/199184) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.10.
> - The health status of a closed issue [will be hidden](https://gitlab.com/gitlab-org/gitlab/-/issues/220867) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.3 or later.
You can report on and quickly respond to the health of individual issues and epics by setting a
red, amber, or green [health status on an issue](../../project/issues/index.md#health-status),
which will appear on your Epic tree.
Report or respond to the health of issues and epics by setting a red, amber, or green [health status](../../project/issues/index.md#health-status), which then appears on your Epic tree.
### Disable Issue health status in Epic tree

View file

@ -427,6 +427,7 @@ GFM recognizes the following:
| merge request | `!123` | `namespace/project!123` | `project!123` |
| snippet | `$123` | `namespace/project$123` | `project$123` |
| epic **(ULTIMATE)** | `&123` | `group1/subgroup&123` | |
| vulnerability **(ULTIMATE)** | `+123` | `namespace/project+123` | `project+123` |
| label by ID | `~123` | `namespace/project~123` | `project~123` |
| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` |
| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` |

View file

@ -226,13 +226,13 @@ To delete the currently active issue board:
An issue board can be associated with a GitLab [Milestone](milestones/index.md#milestones),
[Labels](labels.md), Assignee and Weight
which will automatically filter the Board issues according to these fields.
which automatically filter the board issues accordingly.
This allows you to create unique boards according to your team's need.
![Create scoped board](img/issue_board_creation.png)
You can define the scope of your board when creating it or by clicking the "Edit board" button.
Once a milestone, assignee or weight is assigned to an issue board, you will no longer be able to
Once a milestone, assignee or weight is assigned to an issue board, you can no longer
filter through these in the search bar. In order to do that, you need to remove the desired scope
(for example, milestone, assignee, or weight) from the issue board.
@ -270,24 +270,22 @@ especially in combination with [assignee lists](#assignee-lists).
### Group issue boards **(PREMIUM)**
> [Introduced](https://about.gitlab.com/releases/2017/09/22/gitlab-10-0-released/#group-issue-boards) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.0.
> - One group issue board per group introduced in GitLab 10.6.
> - Multiple group issue boards [introduced](https://about.gitlab.com/releases/2017/09/22/gitlab-10-0-released/#group-issue-boards) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.0.
Accessible at the group navigation level, a group issue board offers the same features as a project-level board,
but it can display issues from all projects in that
Accessible at the group navigation level, a group issue board offers the same features as a project-level board.
It can display issues from all projects in that
group and its descendant subgroups. Similarly, you can only filter by group labels for these
boards. When updating milestones and labels for an issue through the sidebar update mechanism, again only
group-level objects are available.
NOTE: **Note:**
Multiple group issue boards were originally [introduced](https://about.gitlab.com/releases/2017/09/22/gitlab-10-0-released/#group-issue-boards) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.0, and one group issue board per group was made available in GitLab Core 10.6.
![Group issue board](img/group_issue_board.png)
### Assignee lists **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5784) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.0.
Like in a regular list that shows all issues with a chosen label, you can add
As in a regular list showing all issues with a chosen label, you can add
an assignee list that shows all issues assigned to a user.
You can have a board with both label lists and assignee lists. To add an
assignee list:
@ -313,7 +311,7 @@ milestone, giving you more freedom and visibility on the issue board. To add a m
1. Select the **Milestone** tab.
1. Search and click the milestone.
Similar to the assignee lists, you're now able to [drag issues](#drag-issues-between-lists)
Like the assignee lists, you're able to [drag issues](#drag-issues-between-lists)
to and from a milestone list to manipulate the milestone of the dragged issues.
As in other list types, click the trash icon to remove a list.
@ -329,7 +327,7 @@ You cannot set a WIP limit on the default lists (**Open** and **Closed**).
Examples:
- You have a list with four issues, and a limit of five, the header will show **4/5**.
- When you have a list with four issues and a limit of five, the header shows **4/5**.
If you exceed the limit, the current number of issues is shown in red.
- You have a list with five issues with a limit of five. When you move another issue to that list,
the list's header displays **6/5**, with the six shown in red.
@ -387,7 +385,7 @@ Create a new list by clicking the **Add list** button in the upper right corner
![creating a new list in an issue board](img/issue_board_add_list.png)
Then, choose the label or user to create the list from. The new list will be inserted
Then, choose the label or user to create the list from. The new list is inserted
at the end of the lists, before **Done**. Moving and reordering lists is as
easy as dragging them around.
@ -398,15 +396,15 @@ You can now choose it to create a list.
### Delete a list
To delete a list from the issue board, use the small trash icon present
in the list's heading. A confirmation dialog will appear for you to confirm.
in the list's heading. A confirmation dialog appears for you to confirm.
Deleting a list doesn't have any effect in issues and labels, it's just the
list view that is removed. You can always add it back later if you need.
Deleting a list doesn't have any effect on issues and labels, as it's just the
list view that's removed. You can always restore it later if you need.
### Add issues to a list
You can add issues to a list by clicking the **Add issues** button
present in the upper right corner of the issue board. This will open up a modal
present in the upper right corner of the issue board. This opens up a modal
window where you can see all the issues that do not belong to any list.
Select one or more issues by clicking the cards and then click **Add issues**
@ -426,7 +424,7 @@ respective label is removed.
### Filter issues
You should be able to use the filters on top of your issue board to show only
the results you want. This is similar to the filtering used in the issue tracker
the results you want. It's similar to the filtering used in the issue tracker
since the metadata from the issues and labels are re-used in the issue board.
You can filter by author, assignee, milestone, and label.
@ -460,12 +458,12 @@ For example, you can create a list based on the label of **Frontend** and one fo
worked on by the designers.
Then, once they're done, all they have to do is
drag it to the next list, **Backend**, where a backend developer can
drag it to the next list, **Backend**. Then, a backend developer can
eventually pick it up. Once theyre done, they move it to **Done**, to close the
issue.
This process can be seen clearly when visiting an issue since with every move
to another list the label changes and a system note is recorded.
This process can be seen clearly when visiting an issue. With every move
to another list, the label changes and a system note is recorded.
![issue board system notes](img/issue_board_system_notes.png)

View file

@ -56,8 +56,7 @@ Support for [PDF](https://gitlab.com/gitlab-org/gitlab/issues/32811) is planned
- From GitLab 13.1, Design filenames are limited to 255 characters.
- Design Management data
[isn't deleted when a project is destroyed](https://gitlab.com/gitlab-org/gitlab/-/issues/13429) yet.
- Design Management data [won't be moved](https://gitlab.com/gitlab-org/gitlab/-/issues/13426)
when an issue is moved, nor [deleted](https://gitlab.com/gitlab-org/gitlab/-/issues/13427)
- Design Management data [won't be deleted](https://gitlab.com/gitlab-org/gitlab/-/issues/13427)
when an issue is deleted.
- From GitLab 12.7, Design Management data [can be replicated](../../../administration/geo/replication/datatypes.md#limitations-on-replicationverification)
by Geo but [not verified](https://gitlab.com/gitlab-org/gitlab/-/issues/32467).

View file

@ -119,7 +119,7 @@ module Banzai
# Yields the link's URL and inner HTML whenever the node is a valid <a> tag.
def yield_valid_link(node)
link = CGI.unescape(node.attr('href').to_s)
link = unescape_link(node.attr('href').to_s)
inner_html = node.inner_html
return unless link.force_encoding('UTF-8').valid_encoding?
@ -127,6 +127,10 @@ module Banzai
yield link, inner_html
end
def unescape_link(href)
CGI.unescape(href)
end
def replace_text_when_pattern_matches(node, index, pattern)
return unless node.text =~ pattern

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
module Banzai
module Filter
# The actual filter is implemented in the EE mixin
class VulnerabilityReferenceFilter < IssuableReferenceFilter
self.reference_type = :vulnerability
def self.object_class
Vulnerability
end
private
def project
context[:project]
end
end
end
end
Banzai::Filter::VulnerabilityReferenceFilter.prepend_if_ee('EE::Banzai::Filter::VulnerabilityReferenceFilter')

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
module Banzai
module ReferenceParser
# The actual parser is implemented in the EE mixin
class VulnerabilityParser < IssuableParser
self.reference_type = :vulnerability
def records_for_nodes(_nodes)
{}
end
end
end
end
Banzai::ReferenceParser::VulnerabilityParser.prepend_if_ee('::EE::Banzai::ReferenceParser::VulnerabilityParser')

View file

@ -56,7 +56,7 @@ module Gitlab
def self.accept_trace?(project)
::Feature.enabled?(:ci_enable_live_trace, project) &&
::Feature.enabled?(:ci_accept_trace, project, type: :ops, default_enabled: false)
::Feature.enabled?(:ci_accept_trace, project, type: :ops, default_enabled: true)
end
def self.log_invalid_trace_chunks?(project)

View file

@ -114,18 +114,23 @@ module Gitlab
end
def track_experiment_event(experiment_key, action, value = nil)
return if dnt_enabled?
track_experiment_event_for(experiment_key, action, value) do |tracking_data|
::Gitlab::Tracking.event(tracking_data.delete(:category), tracking_data.delete(:action), **tracking_data)
end
end
def frontend_experimentation_tracking_data(experiment_key, action, value = nil)
return if dnt_enabled?
track_experiment_event_for(experiment_key, action, value) do |tracking_data|
gon.push(tracking_data: tracking_data)
end
end
def record_experiment_user(experiment_key)
return if dnt_enabled?
return unless Experimentation.enabled?(experiment_key) && current_user
::Experiment.add_user(experiment_key, tracking_group(experiment_key), current_user)

View file

@ -43,7 +43,6 @@ module Gitlab
# Initialize gon.features with any flags that should be
# made globally available to the frontend
push_frontend_feature_flag(:snippets_vue, default_enabled: true)
push_frontend_feature_flag(:monaco_blobs, default_enabled: true)
push_frontend_feature_flag(:monaco_ci, default_enabled: true)
push_frontend_feature_flag(:webperf_experiment, default_enabled: false)

View file

@ -4,7 +4,7 @@ module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor < Banzai::ReferenceExtractor
REFERABLES = %i(user issue label milestone mentioned_user mentioned_group mentioned_project
merge_request snippet commit commit_range directly_addressed_user epic iteration).freeze
merge_request snippet commit commit_range directly_addressed_user epic iteration vulnerability).freeze
attr_accessor :project, :current_user, :author
# This counter is increased by a number of references filtered out by
# banzai reference exctractor. Note that this counter is stateful and
@ -38,7 +38,7 @@ module Gitlab
end
REFERABLES.each do |type|
define_method("#{type}s") do
define_method(type.to_s.pluralize) do
@references[type] ||= references(type)
end
end

View file

@ -8427,9 +8427,6 @@ msgstr ""
msgid "Delete Comment"
msgstr ""
msgid "Delete Snippet"
msgstr ""
msgid "Delete Value Stream"
msgstr ""
@ -24059,9 +24056,6 @@ msgstr ""
msgid "Snippets|Add another file %{num}/%{total}"
msgstr ""
msgid "Snippets|Authored %{time_ago} by %{author}"
msgstr ""
msgid "Snippets|Delete file"
msgstr ""
@ -24878,9 +24872,6 @@ msgstr ""
msgid "Submit a review"
msgstr ""
msgid "Submit as spam"
msgstr ""
msgid "Submit changes"
msgstr ""

View file

@ -41,7 +41,7 @@ fi
# Do not use 'README.md', instead use 'index.md'
# Number of 'README.md's as of 2020-05-28
NUMBER_READMES=41
NUMBER_READMES=40
FIND_READMES=$(find doc/ -name "README.md" | wc -l)
echo '=> Checking for new README.md files...'
echo

View file

@ -1,6 +1,5 @@
#!/bin/bash
# This is copied from:
# https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/bin/slack
# This is based on https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/bin/slack
#
# Sends Slack notification MSG to CI_SLACK_WEBHOOK_URL (which needs to be set).
# ICON_EMOJI needs to be set to an icon emoji name (without the `:` around it).
@ -8,10 +7,11 @@
CHANNEL=$1
MSG=$2
ICON_EMOJI=$3
USERNAME=$4
if [ -z "$CHANNEL" ] || [ -z "$CI_SLACK_WEBHOOK_URL" ] || [ -z "$MSG" ] || [ -z "$ICON_EMOJI" ]; then
echo "Missing argument(s) - Use: $0 channel message icon_emoji"
if [ -z "$CHANNEL" ] || [ -z "$CI_SLACK_WEBHOOK_URL" ] || [ -z "$MSG" ] || [ -z "$ICON_EMOJI" ] || [ -z "$USERNAME" ]; then
echo "Missing argument(s) - Use: $0 channel message icon_emoji username"
echo "and set CI_SLACK_WEBHOOK_URL environment variable."
else
curl -X POST --data-urlencode 'payload={"channel": "#'"$CHANNEL"'", "username": "GitLab QA Bot", "text": "'"$MSG"'", "icon_emoji": "'":$ICON_EMOJI:"'"}' "$CI_SLACK_WEBHOOK_URL"
curl -X POST --data-urlencode 'payload={"channel": "#'"$CHANNEL"'", "username": "'"$USERNAME"'", "text": "'"$MSG"'", "icon_emoji": "'":$ICON_EMOJI:"'"}' "$CI_SLACK_WEBHOOK_URL"
fi

View file

@ -6,9 +6,12 @@ RSpec.describe Projects::Settings::OperationsController do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project) }
before_all do
project.add_maintainer(user)
end
before do
sign_in(user)
project.add_maintainer(user)
end
shared_examples 'PATCHable' do
@ -163,10 +166,6 @@ RSpec.describe Projects::Settings::OperationsController do
context 'updating each incident management setting' do
let(:new_incident_management_settings) { {} }
before do
project.add_maintainer(user)
end
shared_examples 'a gitlab tracking event' do |params, event_key|
it "creates a gitlab tracking event #{event_key}" do
new_incident_management_settings = params
@ -194,10 +193,6 @@ RSpec.describe Projects::Settings::OperationsController do
end
describe 'POST #reset_pagerduty_token' do
before do
project.add_maintainer(user)
end
context 'with existing incident management setting has active PagerDuty webhook' do
let!(:incident_management_setting) do
create(:project_incident_management_setting, project: project, pagerduty_active: true)
@ -392,10 +387,6 @@ RSpec.describe Projects::Settings::OperationsController do
end
describe 'POST #reset_alerting_token' do
before do
project.add_maintainer(user)
end
context 'with existing alerting setting' do
let!(:alerting_setting) do
create(:project_alerting_setting, project: project)
@ -478,6 +469,104 @@ RSpec.describe Projects::Settings::OperationsController do
end
end
context 'tracing integration' do
describe 'GET #show' do
context 'with existing setting' do
let_it_be(:setting) do
create(:project_tracing_setting, project: project)
end
it 'loads existing setting' do
get :show, params: project_params(project)
expect(controller.helpers.tracing_setting).to eq(setting)
end
end
context 'without an existing setting' do
it 'builds a new setting' do
get :show, params: project_params(project)
expect(controller.helpers.tracing_setting).to be_new_record
end
end
end
describe 'PATCH #update' do
let_it_be(:external_url) { 'https://gitlab.com' }
let(:params) do
{
tracing_setting_attributes: {
external_url: external_url
}
}
end
it_behaves_like 'PATCHable'
describe 'gitlab tracking', :snowplow, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/259282#note_425243784' do
shared_examples 'event tracking' do
it 'tracks an event' do
expect_snowplow_event(
category: 'project:operations:tracing',
action: 'external_url_populated'
)
end
end
shared_examples 'no event tracking' do
it 'does not track an event' do
expect_no_snowplow_event
end
end
before do
make_request
end
subject(:make_request) do
patch :update, params: project_params(project, params), format: :json
end
context 'without existing setting' do
context 'when creating a new setting' do
it_behaves_like 'event tracking'
end
context 'with invalid external_url' do
let_it_be(:external_url) { nil }
it_behaves_like 'no event tracking'
end
end
context 'with existing setting' do
let_it_be(:existing_setting) do
create(:project_tracing_setting,
project: project,
external_url: external_url)
end
context 'when changing external_url' do
let_it_be(:external_url) { 'https://example.com' }
it_behaves_like 'no event tracking'
end
context 'with unchanged external_url' do
it_behaves_like 'no event tracking'
end
context 'with invalid external_url' do
let_it_be(:external_url) { nil }
it_behaves_like 'no event tracking'
end
end
end
end
end
private
def project_params(project, params = {})

View file

@ -8,7 +8,6 @@ RSpec.describe 'Thread Comments Snippet', :js do
let_it_be(:snippet) { create(:project_snippet, :private, :repository, project: project, author: user) }
before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user)
sign_in(user)

View file

@ -13,8 +13,6 @@ RSpec.describe 'Projects > Snippets > Project snippet', :js do
let_it_be(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
before do
stub_feature_flags(snippets_vue: false)
sign_in(user)
end
@ -28,12 +26,8 @@ RSpec.describe 'Projects > Snippets > Project snippet', :js do
end
end
it_behaves_like 'showing user status' do
let(:file_path) { 'files/ruby/popen.rb' }
let(:user_with_status) { snippet.author }
subject { visit project_snippet_path(project, snippet) }
end
# it_behaves_like 'showing user status' do
# This will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/262394
it_behaves_like 'does not show New Snippet button' do
let(:file_path) { 'files/ruby/popen.rb' }

View file

@ -8,7 +8,6 @@ RSpec.describe 'Projects > Snippets > User comments on a snippet', :js do
let_it_be(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user)
sign_in(user)

View file

@ -2,13 +2,12 @@
require 'spec_helper'
RSpec.describe 'Projects > Snippets > User deletes a snippet' do
RSpec.describe 'Projects > Snippets > User deletes a snippet', :js do
let(:project) { create(:project) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
let!(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
let(:user) { create(:user) }
before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user)
sign_in(user)
@ -16,7 +15,11 @@ RSpec.describe 'Projects > Snippets > User deletes a snippet' do
end
it 'deletes a snippet' do
first(:link, 'Delete').click
expect(page).to have_content(snippet.title)
click_button('Delete')
click_button('Delete snippet')
wait_for_requests
expect(page).not_to have_content(snippet.title)
end

View file

@ -7,7 +7,6 @@ RSpec.describe 'Reportable note on snippets', :js do
let_it_be(:project) { create(:project) }
before do
stub_feature_flags(snippets_vue: false)
project.add_maintainer(user)
sign_in(user)
end

View file

@ -3,11 +3,8 @@
require 'spec_helper'
RSpec.describe 'Internal Snippets', :js do
let(:internal_snippet) { create(:personal_snippet, :internal) }
before do
stub_feature_flags(snippets_vue: false)
end
let(:internal_snippet) { create(:personal_snippet, :internal, :repository) }
let(:content) { internal_snippet.blobs.first.data.strip! }
describe 'normal user' do
before do
@ -17,13 +14,13 @@ RSpec.describe 'Internal Snippets', :js do
it 'sees internal snippets' do
visit snippet_path(internal_snippet)
expect(page).to have_content(internal_snippet.content)
expect(page).to have_content(content)
end
it 'sees raw internal snippets' do
visit raw_snippet_path(internal_snippet)
expect(page).to have_content(internal_snippet.content)
expect(page).to have_content(content)
end
end
end

View file

@ -18,7 +18,6 @@ RSpec.describe 'Comments on personal snippets', :js do
end
before do
stub_feature_flags(snippets_vue: false)
sign_in user
visit snippet_path(snippet)

View file

@ -4,19 +4,18 @@ require 'spec_helper'
RSpec.describe 'Private Snippets', :js do
let(:user) { create(:user) }
let(:private_snippet) { create(:personal_snippet, :repository, :private, author: user) }
let(:content) { private_snippet.blobs.first.data.strip! }
before do
stub_feature_flags(snippets_vue: false)
sign_in(user)
end
it 'Private Snippet renders for creator' do
private_snippet = create(:personal_snippet, :private, author: user)
visit snippet_path(private_snippet)
wait_for_requests
expect(page).to have_content(private_snippet.content)
expect(page).to have_content(content)
expect(page).not_to have_css('.js-embed-btn')
expect(page).not_to have_css('.js-share-btn')
end

View file

@ -3,27 +3,24 @@
require 'spec_helper'
RSpec.describe 'Public Snippets', :js do
before do
stub_feature_flags(snippets_vue: false)
end
let(:public_snippet) { create(:personal_snippet, :public, :repository) }
let(:content) { public_snippet.blobs.first.data.strip! }
it 'Unauthenticated user should see public snippets' do
public_snippet = create(:personal_snippet, :public)
url = Gitlab::UrlBuilder.build(public_snippet)
visit snippet_path(public_snippet)
wait_for_requests
expect(page).to have_content(public_snippet.content)
expect(page).to have_css('.js-embed-btn', visible: false)
expect(page).to have_css('.js-share-btn', visible: false)
expect(page.find('.js-snippet-url-area')).to be_readonly
expect(page).to have_content(content)
click_button('Embed')
expect(page).to have_field('Embed', readonly: true, with: "<script src=\"#{url}.js\"></script>")
expect(page).to have_field('Share', readonly: true, with: url)
end
it 'Unauthenticated user should see raw public snippets' do
public_snippet = create(:personal_snippet, :public)
visit raw_snippet_path(public_snippet)
expect(page).to have_content(public_snippet.content)
expect(page).to have_content(content)
end
end

Some files were not shown because too many files have changed in this diff Show more