Commit c0dffdff authored by Mike Greiling's avatar Mike Greiling

Merge branch 'master' into 43511-upgrade-to-babel-7

* master: (30 commits)
  Refactor quick actions docs into multiple tables
  CE Resolve "Refactor code quality similar to JUnit tests"
  Fix rename_login_root_namespaces post migration
  Merge branch 'master-i18n' into 'master'
  Hides Close MR button on merged MR
  Show the commit-sha for pre-release versions
  Resolve "Add "Link" shortcut/icon in markdown editor to make it easier to add references"
  Use tiller locally for Auto Devops
  Improve logging when username update fails due to registry tags
  [QA] Improve admin hashed-storage settings
  circumvent browser cache on browser back navigation
  Highlight current user in comments and system notes
  Resolve "Selecting an autofill suggestion for project name will not update the project slug"
  Update Workhorse to 7.0.0 for Gitaly's new auth scheme
  Update to Rouge 3.3.0 including frozen string literals for improved memory usage
  Don't check for the groups list before filtering
  Add missing changelog type [ci skip]
  Filter issues without an Assignee via the API
  Make single diff patch limit configurable
  Add yarn integrity hashes
  ...
parents a9df7d86 9ab59100
......@@ -444,10 +444,10 @@ setup-test-env:
- vendor/gitaly-ruby
danger-review:
<<: *pull-cache
image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger
stage: test
allow_failure: true
cache: {}
dependencies: []
before_script: []
only:
......@@ -461,6 +461,8 @@ danger-review:
- $CI_COMMIT_REF_NAME =~ /.*-stable(-ee)?-prepare-.*/
script:
- git version
- node --version
- yarn install --frozen-lockfile --cache-folder .yarn-cache
- danger --fail-on-errors=true
rspec-pg 0 30: *rspec-metadata-pg
......
......@@ -7,3 +7,5 @@ danger.import_dangerfile(path: 'danger/database')
danger.import_dangerfile(path: 'danger/documentation')
danger.import_dangerfile(path: 'danger/frozen_string')
danger.import_dangerfile(path: 'danger/commit_messages')
danger.import_dangerfile(path: 'danger/prettier')
danger.import_dangerfile(path: 'danger/eslint')
6.1.1
7.0.0
\ No newline at end of file
......@@ -729,7 +729,7 @@ GEM
retriable (3.1.2)
rinku (2.0.0)
rotp (2.1.2)
rouge (3.2.1)
rouge (3.3.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
......
......@@ -738,7 +738,7 @@ GEM
retriable (3.1.2)
rinku (2.0.0)
rotp (2.1.2)
rouge (3.2.1)
rouge (3.3.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
......
/**
* Highlights the current user in existing elements with a user ID data attribute.
*
* @param elements DOM elements that represent user mentions
*/
export default function highlightCurrentUser(elements) {
const currentUserId = gon && gon.current_user_id;
if (!currentUserId) {
return;
}
elements.forEach(element => {
if (parseInt(element.dataset.user, 10) === currentUserId) {
element.classList.add('current-user');
}
});
}
......@@ -2,6 +2,7 @@ import $ from 'jquery';
import syntaxHighlight from '~/syntax_highlight';
import renderMath from './render_math';
import renderMermaid from './render_mermaid';
import highlightCurrentUser from './highlight_current_user';
// Render GitLab flavoured Markdown
//
......@@ -11,6 +12,7 @@ $.fn.renderGFM = function renderGFM() {
syntaxHighlight(this.find('.js-syntax-highlight'));
renderMath(this.find('.js-render-math'));
renderMermaid(this.find('.js-render-mermaid'));
highlightCurrentUser(this.find('.gfm-project_member').get());
return this;
};
......
......@@ -25,6 +25,7 @@ import './components/board_sidebar';
import './components/new_list_dropdown';
import BoardAddIssuesModal from './components/modal/index.vue';
import '~/vue_shared/vue_resource_interceptor';
import { NavigationType } from '~/lib/utils/common_utils';
export default () => {
const $boardApp = document.getElementById('board-app');
......@@ -32,6 +33,16 @@ export default () => {
window.gl = window.gl || {};
// check for browser back and trigger a hard reload to circumvent browser caching.
window.addEventListener('pageshow', (event) => {
const isNavTypeBackForward = window.performance &&
window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD;
if (event.persisted || isNavTypeBackForward) {
window.location.reload();
}
});
if (gl.IssueBoardsApp) {
gl.IssueBoardsApp.$destroy(true);
}
......
......@@ -2,54 +2,114 @@ import _ from 'underscore';
export const placeholderImage =
'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
const SCROLL_THRESHOLD = 300;
const SCROLL_THRESHOLD = 500;
export default class LazyLoader {
constructor(options = {}) {
this.intersectionObserver = null;
this.lazyImages = [];
this.observerNode = options.observerNode || '#content-body';
const throttledScrollCheck = _.throttle(() => this.scrollCheck(), 300);
const debouncedElementsInView = _.debounce(() => this.checkElementsInView(), 300);
window.addEventListener('scroll', throttledScrollCheck);
window.addEventListener('resize', debouncedElementsInView);
const scrollContainer = options.scrollContainer || window;
scrollContainer.addEventListener('load', () => this.loadCheck());
scrollContainer.addEventListener('load', () => this.register());
}
static supportsIntersectionObserver() {
return 'IntersectionObserver' in window;
}
searchLazyImages() {
const that = this;
requestIdleCallback(
() => {
that.lazyImages = [].slice.call(document.querySelectorAll('.lazy'));
const lazyImages = [].slice.call(document.querySelectorAll('.lazy'));
if (that.lazyImages.length) {
that.checkElementsInView();
if (LazyLoader.supportsIntersectionObserver()) {
if (this.intersectionObserver) {
lazyImages.forEach(img => this.intersectionObserver.observe(img));
}
} else if (lazyImages.length) {
this.lazyImages = lazyImages;
this.checkElementsInView();
}
},
{ timeout: 500 },
);
}
startContentObserver() {
const contentNode = document.querySelector(this.observerNode) || document.querySelector('body');
if (contentNode) {
const observer = new MutationObserver(() => this.searchLazyImages());
this.mutationObserver = new MutationObserver(() => this.searchLazyImages());
observer.observe(contentNode, {
this.mutationObserver.observe(contentNode, {
childList: true,
subtree: true,
});
}
}
loadCheck() {
this.searchLazyImages();
stopContentObserver() {
if (this.mutationObserver) {
this.mutationObserver.takeRecords();
this.mutationObserver.disconnect();
this.mutationObserver = null;
}
}
unregister() {
this.stopContentObserver();
if (this.intersectionObserver) {
this.intersectionObserver.takeRecords();
this.intersectionObserver.disconnect();
this.intersectionObserver = null;
}
if (this.throttledScrollCheck) {
window.removeEventListener('scroll', this.throttledScrollCheck);
}
if (this.debouncedElementsInView) {
window.removeEventListener('resize', this.debouncedElementsInView);
}
}
register() {
if (LazyLoader.supportsIntersectionObserver()) {
this.startIntersectionObserver();
} else {
this.startLegacyObserver();
}
this.startContentObserver();
this.searchLazyImages();
}
startIntersectionObserver = () => {
this.throttledElementsInView = _.throttle(() => this.checkElementsInView(), 300);
this.intersectionObserver = new IntersectionObserver(this.onIntersection, {
rootMargin: `${SCROLL_THRESHOLD}px 0px`,
thresholds: 0.1,
});
};
onIntersection = entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.intersectionObserver.unobserve(entry.target);
this.lazyImages.push(entry.target);
}
});
this.throttledElementsInView();
};
startLegacyObserver() {
this.throttledScrollCheck = _.throttle(() => this.scrollCheck(), 300);
this.debouncedElementsInView = _.debounce(() => this.checkElementsInView(), 300);
window.addEventListener('scroll', this.throttledScrollCheck);
window.addEventListener('resize', this.debouncedElementsInView);
}
scrollCheck() {
requestAnimationFrame(() => this.checkElementsInView());
}
checkElementsInView() {
const scrollTop = window.pageYOffset;
const visHeight = scrollTop + window.innerHeight + SCROLL_THRESHOLD;
......@@ -61,18 +121,29 @@ export default class LazyLoader {
const imgTop = scrollTop + imgBoundRect.top;
const imgBound = imgTop + imgBoundRect.height;
if (scrollTop < imgBound && visHeight > imgTop) {
if (scrollTop <= imgBound && visHeight >= imgTop) {
requestAnimationFrame(() => {
LazyLoader.loadImage(selectedImage);
});
return false;
}
/*
If we are scrolling fast, the img we watched intersecting could have left the view port.
So we are going watch for new intersections.
*/
if (LazyLoader.supportsIntersectionObserver()) {
if (this.intersectionObserver) {
this.intersectionObserver.observe(selectedImage);
}
return false;
}
return true;
}
return false;
});
}
static loadImage(img) {
if (img.getAttribute('data-src')) {
let imgUrl = img.getAttribute('data-src');
......
......@@ -616,6 +616,17 @@ export const roundOffFloat = (number, precision = 0) => {
return Math.round(number * multiplier) / multiplier;
};
/**
* Represents navigation type constants of the Performance Navigation API.
* Detailed explanation see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigation.
*/
export const NavigationType = {
TYPE_NAVIGATE: 0,
TYPE_RELOAD: 1,
TYPE_BACK_FORWARD: 2,
TYPE_RESERVED: 255,
};
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
......
......@@ -31,11 +31,17 @@ function blockTagText(text, textArea, blockTag, selected) {
}
}
function moveCursor(textArea, tag, wrapped, removedLastNewLine) {
function moveCursor({ textArea, tag, wrapped, removedLastNewLine, select }) {
var pos;
if (!textArea.setSelectionRange) {
return;
}
if (select && select.length > 0) {
// calculate the part of the text to be selected
const startPosition = textArea.selectionStart - (tag.length - tag.indexOf(select));
const endPosition = startPosition + select.length;
return textArea.setSelectionRange(startPosition, endPosition);
}
if (textArea.selectionStart === textArea.selectionEnd) {
if (wrapped) {
pos = textArea.selectionStart - tag.length;
......@@ -51,7 +57,7 @@ function moveCursor(textArea, tag, wrapped, removedLastNewLine) {
}
}
export function insertMarkdownText(textArea, text, tag, blockTag, selected, wrap) {
export function insertMarkdownText({ textArea, text, tag, blockTag, selected, wrap, select }) {
var textToInsert, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine;
removedLastNewLine = false;
removedFirstNewLine = false;
......@@ -82,11 +88,16 @@ export function insertMarkdownText(textArea, text, tag, blockTag, selected, wrap
startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : '';
const textPlaceholder = '{text}';
if (selectedSplit.length > 1 && (!wrap || (blockTag != null && blockTag !== ''))) {
if (blockTag != null && blockTag !== '') {
textToInsert = blockTagText(text, textArea, blockTag, selected);
} else {
textToInsert = selectedSplit.map(function(val) {
if (tag.indexOf(textPlaceholder) > -1) {
return tag.replace(textPlaceholder, val);
}
if (val.indexOf(tag) === 0) {
return "" + (val.replace(tag, ''));
} else {
......@@ -94,6 +105,8 @@ export function insertMarkdownText(textArea, text, tag, blockTag, selected, wrap
}
}).join('\n');
}
} else if (tag.indexOf(textPlaceholder) > -1) {
textToInsert = tag.replace(textPlaceholder, selected);
} else {
textToInsert = "" + startChar + tag + selected + (wrap ? tag : ' ');
}
......@@ -107,17 +120,17 @@ export function insertMarkdownText(textArea, text, tag, blockTag, selected, wrap
}
insertText(textArea, textToInsert);
return moveCursor(textArea, tag, wrap, removedLastNewLine);
return moveCursor({ textArea, tag: tag.replace(textPlaceholder, selected), wrap, removedLastNewLine, select });
}
function updateText(textArea, tag, blockTag, wrap) {
function updateText({ textArea, tag, blockTag, wrap, select }) {
var $textArea, selected, text;
$textArea = $(textArea);
textArea = $textArea.get(0);
text = $textArea.val();
selected = selectedText(text, textArea);
$textArea.focus();
return insertMarkdownText(textArea, text, tag, blockTag, selected, wrap);
return insertMarkdownText({ textArea, text, tag, blockTag, selected, wrap, select });
}
function replaceRange(s, start, end, substitute) {
......@@ -127,7 +140,12 @@ function replaceRange(s, start, end, substitute) {
export function addMarkdownListeners(form) {
return $('.js-md', form).off('click').on('click', function() {
const $this = $(this);
return updateText($this.closest('.md-area').find('textarea'), $this.data('mdTag'), $this.data('mdBlock'), !$this.data('mdPrepend'));
return updateText({
textArea: $this.closest('.md-area').find('textarea'),
tag: $this.data('mdTag'),
blockTag: $this.data('mdBlock'),
wrap: !$this.data('mdPrepend'),
select: $this.data('mdSelect') });
});
}
......
......@@ -11,6 +11,7 @@ import commentForm from './comment_form.vue';
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
import skeletonLoadingContainer from '../../vue_shared/components/notes/skeleton_note.vue';
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
export default {
name: 'NotesApp',
......@@ -96,6 +97,9 @@ export default {
});
}
},
updated() {
this.$nextTick(() => highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member')));
},
methods: {
...mapActions({
fetchDiscussions: 'fetchDiscussions',
......
......@@ -147,7 +147,7 @@ const bindEvents = () => {
$projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl));
$projectName.keyup(() => {
$projectName.on('keyup change', () => {
onProjectNameChange($projectName, $projectPath);
hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
});
......
......@@ -139,7 +139,7 @@ export default {
<section class="media-section">
<div class="media">
<status-icon :status="statusIconName" />
<div class="media-body space-children d-flex flex-align-self-center">
<div class="media-body d-flex flex-align-self-center">
<span class="js-code-text code-text">
{{ headerText }}
......
......@@ -105,6 +105,12 @@
button-title="Insert code"
icon="code"
/>
<toolbar-button
tag="[{text}](url)"
tag-select="url"
button-title="Add a link"
icon="link"
/>
<toolbar-button
:prepend="true"
tag="* "
......
......@@ -27,6 +27,11 @@
required: false,
default: '',
},
tagSelect: {
type: String,
required: false,
default: '',
},
prepend: {
type: Boolean,
required: false,
......@@ -40,6 +45,7 @@
<button
v-tooltip
:data-md-tag="tag"
:data-md-select="tagSelect"
:data-md-block="tagBlock"
:data-md-prepend="prepend"
:title="buttonTitle"
......
......@@ -11,6 +11,10 @@
padding: 0 2px;
background-color: $blue-100;
border-radius: $border-radius-default;
&.current-user {
background-color: $orange-100;
}
}
.gfm-color_chip {
......
......@@ -14,6 +14,8 @@ class Projects::ArtifactsController < Projects::ApplicationController
before_action :entry, only: [:file]
def download
return render_404 unless artifacts_file
send_upload(artifacts_file, attachment: artifacts_file.filename)
end
......@@ -100,7 +102,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
def artifacts_file
@artifacts_file ||= build.artifacts_file
@artifacts_file ||= build.artifacts_file_for_type(params[:file_type] || :archive)
end
def entry
......
......@@ -236,16 +236,16 @@ class IssuableFinder
# rubocop: enable CodeReuse/ActiveRecord
def assignee_id?
params[:assignee_id].present? && params[:assignee_id] != NONE
params[:assignee_id].present? && params[:assignee_id].to_s != NONE
end
def assignee_username?
params[:assignee_username].present? && params[:assignee_username] != NONE
params[:assignee_username].present? && params[:assignee_username].to_s != NONE
end
def no_assignee?
# Assignee_id takes precedence over assignee_username
params[:assignee_id] == NONE || params[:assignee_username] == NONE
params[:assignee_id].to_s == NONE || params[:assignee_username].to_s == NONE
end
# rubocop: disable CodeReuse/ActiveRecord
......
......@@ -254,7 +254,8 @@ module ApplicationSettingsHelper
:user_default_internal_regex,
:user_oauth_applications,
:version_check_enabled,
:web_ide_clientside_preview_enabled
:web_ide_clientside_preview_enabled,
:diff_max_patch_bytes
]
end
......
......@@ -327,11 +327,15 @@ module IssuablesHelper
end
def issuable_button_visibility(issuable, closed)
return 'hidden' if issuable_button_hidden?(issuable, closed)
end
def issuable_button_hidden?(issuable, closed)
case issuable
when Issue
issue_button_visibility(issuable, closed)
issue_button_hidden?(issuable, closed)
when MergeRequest
merge_request_button_visibility(issuable, closed)
merge_request_button_hidden?(issuable, closed)
end
end
......
......@@ -64,7 +64,11 @@ module IssuesHelper
end
def issue_button_visibility(issue, closed)
return 'hidden' if issue.closed? == closed
return 'hidden' if issue_button_hidden?(issue, closed)
end
def issue_button_hidden?(issue, closed)
issue.closed? == closed || (!closed && issue.discussion_locked)
end
def confidential_icon(issue)
......
......@@ -80,7 +80,11 @@ module MergeRequestsHelper
end
def merge_request_button_visibility(merge_request, closed)
return 'hidden' if merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_without_fork?
return 'hidden' if merge_request_button_hidden?(merge_request, closed)
end
def merge_request_button_hidden?(merge_request, closed)
merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_without_fork?
end
def merge_request_version_path(project, merge_request, merge_request_diff, start_sha = nil)
......
......@@ -9,4 +9,17 @@ module VersionCheckHelper
image_url = VersionCheck.new.url
image_tag image_url, class: 'js-version-status-badge'
end
def link_to_version
if Gitlab.pre_release?
commit_link = link_to(Gitlab.revision, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', source_code_project, Gitlab.revision))
[Gitlab::VERSION, content_tag(:small, commit_link)].join(' ').html_safe
else
link_to Gitlab::VERSION, Gitlab::COM_URL + namespace_project_tag_path('gitlab-org', source_code_project, "v#{Gitlab::VERSION}")
end
end
def source_code_project
'gitlab-ce'
end
end
......@@ -182,6 +182,12 @@ class ApplicationSetting < ActiveRecord::Base
numericality: { less_than_or_equal_to: :gitaly_timeout_default },
if: :gitaly_timeout_default
validates :diff_max_patch_bytes,
presence: true,
numericality: { only_integer: true,
greater_than_or_equal_to: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
less_than_or_equal_to: Gitlab::Git::Diff::MAX_PATCH_BYTES_UPPER_BOUND }
validates :user_default_internal_regex, js_regex: true, allow_nil: true
SUPPORTED_KEY_TYPES.each do |type|
......@@ -293,7 +299,8 @@ class ApplicationSetting < ActiveRecord::Base
user_default_external: false,
user_default_internal_regex: nil,
user_show_add_ssh_key_message: true,
usage_stats_set_by_user_id: nil
usage_stats_set_by_user_id: nil,
diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES
}
end
......
......@@ -522,6 +522,13 @@ module Ci
self.job_artifacts.update_all(expire_at: nil)
end
def artifacts_file_for_type(type)
file = job_artifacts.find_by(file_type: Ci::JobArtifact.file_types[type])&.file
# TODO: to be removed once legacy artifacts is removed
file ||= legacy_artifacts_file if type == :archive
file
end
def coverage_regex
super || project.try(:build_coverage_regex)
end
......
......@@ -15,6 +15,7 @@ module Ci
metadata: nil,
trace: nil,
junit: 'junit.xml',
codequality: 'codequality.json',
sast: 'gl-sast-report.json',
dependency_scanning: 'gl-dependency-scanning-report.json',
container_scanning: 'gl-container-scanning-report.json',
......@@ -26,6 +27,7 @@ module Ci
metadata: :gzip,
trace: :raw,
junit: :gzip,
codequality: :gzip,
sast: :gzip,
dependency_scanning: :gzip,
container_scanning: :gzip,
......@@ -73,7 +75,8 @@ module Ci
sast: 5, ## EE-specific
dependency_scanning: 6, ## EE-specific
container_scanning: 7, ## EE-specific
dast: 8 ## EE-specific
dast: 8, ## EE-specific
codequality: 9 ## EE-specific
}
enum file_format: {
......
......@@ -15,6 +15,9 @@ module Clusters
state :scheduled, value: 1
state :installing, value: 2
state :installed, value: 3
state :updating, value: 4
state :updated, value: 5
state :update_errored, value: 6
event :make_scheduled do
transition [:installable, :errored] => :scheduled
......@@ -32,6 +35,18 @@ module Clusters
transition any => :errored
end
event :make_updating do
transition [:installed, :updated, :update_errored] => :updating
end
event :make_updated do
transition [:updating] => :updated
end
event :make_update_errored do
transition any => :update_errored
end
before_transition any => [:scheduled] do |app_status, _|
app_status.status_reason = nil
end
......@@ -40,6 +55,15 @@ module Clusters
status_reason = transition.args.first
app_status.status_reason = status_reason if status_reason
end
before_transition any => [:updating] do |app_status, _|
app_status.status_reason = nil
end
before_transition any => [:update_errored] do |app_status, transition|
status_reason = transition.args.first
app_status.status_reason = status_reason if status_reason
end
end
end
end
......
......@@ -5,8 +5,10 @@ module Storage
extend ActiveSupport::Concern
def move_dir
if any_project_has_container_registry_tags?
raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry')
proj_with_tags = first_project_with_container_registry_tags
if proj_with_tags
raise Gitlab::UpdatePathError.new("Namespace #{name} (#{id}) cannot be moved because at least one project (e.g. #{proj_with_tags.name} (#{proj_with_tags.id})) has tags in container registry")
end
parent_was = if parent_changed? && parent_id_was.present?
......
......@@ -135,6 +135,10 @@ class Namespace < ActiveRecord::Base
all_projects.any?(&:has_container_registry_tags?)
end
def first_project_with_container_registry_tags
all_projects.find(&:has_container_registry_tags?)
end
def send_update_instructions
projects.each do |project|
project.send_move_instructions("#{full_path_was}/#{project.path}")
......
......@@ -1365,6 +1365,18 @@ class Project < ActiveRecord::Base
end
end
# Filters `users` to return only authorized users of the project
def members_among(users)
if users.is_a?(ActiveRecord::Relation) && !users.loaded?
authorized_users.merge(users)
else
return [] if users.empty?
user_ids = authorized_users.where(users: { id: users.map(&:id) }).pluck(:id)
users.select { |user| user_ids.include?(user.id) }
end
end
def default_branch
@default_branch ||= repository.root_ref if repository.exists?
end
......
= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-merge-request-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
= f.label :diff_max_patch_bytes, 'Maximum diff patch size (Bytes)', class: 'label-light'
= f.number_field :diff_max_patch_bytes, class: 'form-control'
%span.form-text.text-muted
Diff files surpassing this limit will be presented as 'too large'
and won't be expandable.
= link_to icon('question-circle'),
help_page_path('user/admin_area/diff_limits',
anchor: 'maximum-diff-patch-size')
= f.submit _('Save changes'), class: 'btn btn-success'
......@@ -5,7 +5,7 @@
.sub-section
.form-group
.form-check
= f.check_box :hashed_storage_enabled, class: 'form-check-input'
= f.check_box :hashed_storage_enabled, class: 'form-check-input qa-hashed-storage-checkbox'
= f.label :hashed_storage_enabled, class: 'form-check-label' do
Use hashed storage paths for newly created and renamed projects
.form-text.text-muted
......@@ -48,4 +48,4 @@
.form-text.text-muted
= circuitbreaker_failure_reset_time_help_text
= f.submit 'Save changes', class: "btn btn-success"
= f.submit 'Save changes', class: "btn btn-success qa-save-changes-button"
......@@ -13,7 +13,7 @@
.settings-content
= render partial: 'repository_mirrors_form'
%section.settings.as-repository-storage.no-animate#js-repository-storage-settings{ class: ('expanded' if expanded_by_default?) }
%section.settings.qa-repository-storage-settings.as-repository-storage.no-animate#js-repository-storage-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Repository storage')
......
......@@ -24,6 +24,17 @@
.settings-content
= render 'account_and_limit'
%section.settings.as-diff-limits.no-animate#js-merge-request-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Diff limits')
%button.btn.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Diff content limits')
.settings-content
= render 'diff_limits'
%section.settings.as-signup.no-animate#js-signup-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
......@@ -46,7 +57,7 @@
.settings-content
= render 'signin'
%section.qa-terms-settings.settings.as-terms.no-animate#js-terms-settings{ class: ('expanded' if expanded_by_default?) }
%section.settings.as-terms.no-animate#js-terms-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Terms of Service and Privacy Policy')
......
......@@ -7,7 +7,7 @@
GitLab
Community Edition
- if user_signed_in?
%span= link_to Gitlab::VERSION, Gitlab::COM_URL + namespace_project_tag_path('gitlab-org', 'gitlab-ce', "v#{Gitlab::VERSION}")
%span= link_to_version
= version_status_badge
%hr
......
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar.qa-admin-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header
= link_to admin_root_path, title: _('Admin Overview') do
......@@ -197,10 +197,10 @@
= link_to admin_application_settings_path do
.nav-icon-container
= sprite_icon('settings')
%span.nav-item-name
%span.nav-item-name.qa-admin-settings-item
= _('Settings')
%ul.sidebar-sub-level-items
%ul.sidebar-sub-level-items.qa-admin-sidebar-submenu
= nav_link(controller: :application_settings, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_application_settings_path do
%strong.fly-out-top-item-name
......@@ -215,7 +215,7 @@
%span
= _('Integrations')
= nav_link(path: 'application_settings#repository') do
= link_to repository_admin_application_settings_path, title: _('Repository') do
= link_to repository_admin_application_settings_path, title: _('Repository'), class: 'qa-admin-settings-repository-item' do
%span
= _('Repository')
- if template_exists?('admin/application_settings/templates')
......
......@@ -18,14 +18,15 @@
Preview
%li.md-header-toolbar.active
= markdown_toolbar_button({ icon: "bold", data: { "md-tag" => "**" }, title: "Add bold text" })
= markdown_toolbar_button({ icon: "italic", data: { "md-tag" => "*" }, title: "Add italic text" })
= markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" })
= markdown_toolbar_button({ icon: "code", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" })
= markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" })
= markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
= markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
%button.toolbar-btn.toolbar-fullscreen-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: "Go full screen", data: { container: "body" } }
= markdown_toolbar_button({ icon: "bold", data: { "md-tag" => "**" }, title: s_("MarkdownToolbar|Add bold text") })
= markdown_toolbar_button({ icon: "italic", data: { "md-tag" => "*" }, title: s_("MarkdownToolbar|Add italic text") })
= markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: s_("MarkdownToolbar|Insert a quote") })
= markdown_toolbar_button({ icon: "code", data: { "md-tag" => "`", "md-block" => "```" }, title: s_("MarkdownToolbar|Insert code") })
= markdown_toolbar_button({ icon: "link", data: { "md-tag" => "[{text}](url)", "md-select" => "url" }, title: s_("MarkdownToolbar|Add a link") })
= markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a bullet list") })
= markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a numbered list") })
= markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a task list") })
%button.toolbar-btn.toolbar-fullscreen-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: s_("MarkdownToolbar|Go full screen"), data: { container: "body" } }
= sprite_icon("screen-full")
.md-write-holder
......
.group-empty-state.row.align-items-center.justify-content-center.qa-groups-empty-state
.group-empty-state.row.align-items-center.justify-content-center
.icon.text-center.order-md-2
= custom_icon("icon_empty_groups")
......
= form_tag request.path, method: :get, class: "group-filter-form js-group-filter-form", id: 'group-filter-form' do |f|
= search_field_tag :filter, params[:filter], placeholder: s_('GroupsTree|Search by name'), class: 'group-filter-form-field form-control js-groups-list-filter', spellcheck: false, id: 'group-filter-form-field', tabindex: "2"
= search_field_tag :filter, params[:filter], placeholder: s_('GroupsTree|Search by name'), class: 'group-filter-form-field form-control js-groups-list-filter qa-groups-filter', spellcheck: false, id: 'group-filter-form-field', tabindex: "2"
- is_current_user = issuable_author_is_current_user(issuable)
- display_issuable_type = issuable_display_type(issuable)
- button_method = issuable_close_reopen_button_method(issuable)
- are_close_and_open_buttons_hidden = issuable_button_hidden?(issuable, true) && issuable_button_hidden?(issuable, false)
- if can_update
- if is_current_user
- if is_current_user
- if can_update
= link_to "Close #{display_issuable_type}", close_issuable_path(issuable), method: button_method,
class: "d-none d-sm-none d-md-block btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)}", title: "Close #{display_issuable_type}"
- else
= render 'shared/issuable/close_reopen_report_toggle', issuable: issuable
- if can_reopen && is_current_user
- if can_reopen
= link_to "Reopen #{display_issuable_type}", reopen_issuable_path(issuable), method: button_method,
class: "d-none d-sm-none d-md-block btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}", title: "Reopen #{display_issuable_type}"
- else
- if can_update && !are_close_and_open_buttons_hidden
= render 'shared/issuable/close_reopen_report_toggle', issuable: issuable
- else
= link_to 'Report abuse', new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
class: 'd-none d-sm-none d-md-block btn btn-grouped btn-close-color', title: 'Report abuse'
---
title: Fix stale issue boards after browser back
merge_request: 22006
author: Johann Hubert Sonntagbauer
type: fixed
---
title: Improve lazy image loading performance by using IntersectionObserver where
available
merge_request: 21565
author:
type: performance
---
title: Filter issues without an Assignee via the API
merge_request: 22009
author: Eva Kadlecová
type: fixed
---
title: Add link button to markdown editor toolbar
merge_request: 18579
author: Jan Beckmann
type: added
---
title: Hides Close Merge request btn on merged Merge request
merge_request: 21840
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: Fix migration to avoid an exception during upgrade
merge_request: 22055
author:
type: fixed
---
title: Use local tiller for Auto DevOps
merge_request: 22036
author:
type: changed
---
title: Update project path on project name autofill
merge_request: 22016
author:
type: other
---
title: Show SHA for pre-release versions on the help page
merge_request: 22026
author:
type: changed
---
title: Make single diff patch limit configurable
merge_request: 21886
author:
type: added
---
title: Update to Rouge 3.3.0 including frozen string literals for improved memory
usage
merge_request:
author:
type: changed
---
title: Improve logging when username update fails due to registry tags
merge_request: 22038
author:
type: other
---
title: Highlight current user in comments
merge_request: 21406
author:
type: changed
# frozen_string_literal: true
def get_eslint_files(files)
files.select do |file|
file.end_with?('.js', '.vue') &&
File.read(file).include?('/* eslint-disable')
end
end
eslint_candidates = get_eslint_files(git.added_files + git.modified_files)
return if eslint_candidates.empty?
warn 'This merge request changed files with disabled eslint rules. Please consider fixing them.'
markdown(<<~MARKDOWN)
## Disabled eslint rules
The following files have disabled `eslint` rules. Please consider fixing them:
* #{eslint_candidates.map { |path| "`#{path}`" }.join("\n* ")}
Run the following command for more details
```
node_modules/.bin/eslint --report-unused-disable-directives --no-inline-config \\
#{eslint_candidates.map { |path| " '#{path}'" }.join(" \\\n")}
```
MARKDOWN
# frozen_string_literal: true
def get_prettier_files(files)
files.select do |file|
file.end_with?('.js', '.scss', '.vue')
end
end
prettier_candidates = get_prettier_files(git.added_files + git.modified_files)
return if prettier_candidates.empty?
unpretty = `node_modules/prettier/bin-prettier.js --list-different #{prettier_candidates.join(" ")}`
.split(/$/)
.map(&:strip)
.reject(&:empty?)
return if unpretty.empty?
warn 'This merge request changed frontend files without pretty printing them.'
markdown(<<~MARKDOWN)
## Pretty print Frontend files
The following files should have been pretty printed with `prettier`:
* #{unpretty.map { |path| "`#{path}`" }.join("\n* ")}
Please run
```
node_modules/.bin/prettier --write \\
#{unpretty.map { |path| " '#{path}'" }.join(" \\\n")}
```
Also consider auto-formatting [on-save].
[on-save]: https://docs.gitlab.com/ee/development/new_fe_guide/style/prettier.html
MARKDOWN
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddDiffMaxPatchBytesToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:application_settings,
:diff_max_patch_bytes,
:integer,
default: 100.kilobytes,
allow_null: false)
end
def down
remove_column(:application_settings, :diff_max_patch_bytes)
end
end
......@@ -5,6 +5,8 @@ class RenameLoginRootNamespaces < ActiveRecord::Migration
DOWNTIME = false
disable_ddl_transaction!
# We're taking over the /login namespace as part of a fix for the Jira integration
def up
disable_statement_timeout do
......
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180917172041) do
ActiveRecord::Schema.define(version: 20180924141949) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -171,6 +171,7 @@ ActiveRecord::Schema.define(version: 20180917172041) do
t.boolean "user_show_add_ssh_key_message", default: true, null: false
t.integer "usage_stats_set_by_user_id"
t.integer "receive_max_input_size"
t.integer "diff_max_patch_bytes", default: 102400, null: false
end
create_table "audit_events", force: :cascade do |t|
......
......@@ -47,6 +47,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Enforcing Terms of Service](../user/admin_area/settings/terms.md)
- [Third party offers](../user/admin_area/settings/third_party_offers.md)
- [Compliance](compliance.md): A collection of features from across the application that you may configure to help ensure that your GitLab instance and DevOps workflow meet compliance standards.
- [Diff limits](../user/admin_area/diff_limits.md): Configure the diff rendering size limits of branch comparison pages.
#### Customizing GitLab's appearance
......
......@@ -110,7 +110,8 @@ At this point the script would ask you to select the category of the change (map
4. New deprecation
5. Feature removal
6. Security fix
7. Other
7. Performance improvement
8. Other
```
The entry filename is based on the name of the current Git branch. If you run
......
......@@ -2,13 +2,10 @@
Currently we rely on different sources to present diffs, these include:
- Rugged gem
- Gitaly service
- Database (through `merge_request_diff_files`)
- Redis (cached highlighted diffs)
We're constantly moving Rugged calls to Gitaly and the progress can be followed through [Gitaly repo](https://gitlab.com/gitlab-org/gitaly).
## Architecture overview
### Merge request diffs
......@@ -19,8 +16,9 @@ we fetch the comparison information using `Gitlab::Git::Compare`, which fetches
The diffs fetching process _limits_ single file diff sizes and the overall size of the whole diff through a series of constant values. Raw diff files are
then persisted on `merge_request_diff_files` table.
Even though diffs higher than 10kb are collapsed (`Gitlab::Git::Diff::COLLAPSE_LIMIT`), we still keep them on Postgres. However, diff files over _safety limits_
(see the [Diff limits section](#diff-limits)) are _not_ persisted.
Even though diffs larger than 10% of the value of `ApplicationSettings#diff_max_patch_bytes` are collapsed,
we still keep them on Postgres. However, diff files larger than defined _safety limits_
(see the [Diff limits section](#diff-limits)) are _not_ persisted in the database.
In order to present diffs information on the Merge Request diffs page, we:
......@@ -102,23 +100,20 @@ Gitaly will only return the safe amount of data to be persisted on `merge_reques
Limits that act onto each diff file of a collection. Files number, lines number and files size are considered.
```ruby
Gitlab::Git::Diff::COLLAPSE_LIMIT = 10.kilobytes
```
#### Expandable patches (collapsed)
File diff will be collapsed (but be expandable) if it is larger than 10 kilobytes.
Diff patches are collapsed when surpassing 10% of the value set in `ApplicationSettings#diff_max_patch_bytes`.
That is, it's equivalent to 10kb if the maximum allowed value is 100kb.
The diff will still be persisted and expandable if the patch size doesn't
surpass `ApplicationSettings#diff_max_patch_bytes`.
*Note:* Although this nomenclature (Collapsing) is also used on Gitaly, this limit is only used on GitLab (hardcoded - not sent to Gitaly).
Gitaly will only return `Diff.Collapsed` (RPC) when surpassing collection limits.
```ruby
Gitlab::Git::Diff::SIZE_LIMIT = 100.kilobytes
```
File diff will not be rendered if it's larger than 100 kilobytes.
#### Not expandable patches (too large)
*Note:* This limit is currently hardcoded and applied on Gitaly and the RPC returns `Diff.TooLarge` when this limit is surpassed.
Although we're still also applying it on GitLab, we should remove the redundancy from GitLab once we're confident with the Gitaly integration.
The patch not be rendered if it's larger than `ApplicationSettings#diff_max_patch_bytes`.
Users will see a `This source diff could not be displayed because it is too large` message.
```ruby
Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
......
# Diff limits administration
NOTE: **Note:**
Merge requests and branch comparison views will be affected.
CAUTION: **Caution:**
These settings are currently under experimental state. They'll
increase the resource consumption of your instance and should
be edited mindfully.
1. Access **Admin area > Settings > General**
1. Expand **Diff limits**
### Maximum diff patch size
This is the content size each diff file (patch) is allowed to reach before
it's collapsed, without the possibility of being expanded. A link redirecting
to the blob view will be presented for the patches that surpass this limit.
Patches surpassing 10% of this content size will be automatically collapsed,
but expandable (a link to expand the diff will be presented).
# GitLab quick actions
Quick actions are textual shortcuts for common actions on issues, merge requests
or commits that are usually done by clicking buttons or dropdowns in GitLab's UI.
You can enter these commands while creating a new issue or merge request, and
in comments. Each command should be on a separate line in order to be properly
detected and executed. The commands are removed from the issue, merge request or
comment body before it is saved and will not be visible to anyone else.
Quick actions are textual shortcuts for common actions on issues, epics, merge requests,
and commits that are usually done by clicking buttons or dropdowns in GitLab's UI.
You can enter these commands while creating a new issue or merge request, or
in comments of issues, epics, merge requests, and commits. Each command should be
on a separate line in order to be properly detected and executed. Once executed,
the commands are removed from the text body and not visible to anyone else.
Below is a list of all of the available commands and descriptions about what they
do.
## Quick actions for issues and merge requests
The following quick actions are applicable to both issues and merge requests threads,
discussions, and descriptions:
| Command | Action | Issue | Merge request |
|:---------------------------|:------------------------------ |:------|:--------------|
| `/tableflip <Comment>` | Append the comment with `(╯°□°)╯︵ ┻━┻` | ✓ | ✓ |
| `/shrug <Comment>` | Append the comment with `¯\_(ツ)_/¯` | ✓ | ✓ |
| `/todo` | Add a todo | ✓ | ✓ |
| `/done` | Mark todo as done | ✓ | ✓ |
| `/subscribe` | Subscribe | ✓ | ✓ |
| `/unsubscribe` | Unsubscribe | ✓ | ✓ |
| `/close` | Close | ✓ | ✓ |
| `/reopen` | Reopen | ✓ | ✓ |
| `/title <New title>` | Change title | ✓ | ✓ |
| `/award :emoji:` | Toggle emoji award | ✓ | ✓ |
| `/assign @user` | Assign one user | ✓ | ✓ |
| `/assign @user1 @user2` | Assign multiple users **[STARTER]** | ✓ | |
| `/unassign` | Remove assignee(s) | ✓ | ✓ |
| `/reassign @user1 @user2` | Change assignee | ✓ | ✓ |
| `/milestone %milestone` | Set milestone | ✓ | ✓ |
| `/remove_milestone` | Remove milestone | ✓ | ✓ |
| `/label ~label1 ~label2` | Add label(s) | ✓ | ✓ |
| `/unlabel ~label1 ~label2` | Remove all or specific label(s)| ✓ | ✓ |
| `/relabel ~label1 ~label2` | Replace label | ✓ | ✓ |
| <code>/copy_metadata #issue &#124; !merge_request</code> | Copy labels and milestone from other issue or merge request | ✓ | ✓ |
| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate | ✓ | ✓ |
| `/remove_estimate` | Remove time estimate | ✓ | ✓ |
| <code>/spend &lt;time(1h 30m &#124; -1h 5m)&gt; &lt;date(YYYY-MM-DD)&gt;</code> | Add or subtract spent time; optionally, specify the date that time was spent on | ✓ | ✓ |
| `/remove_time_spent` | Remove time spent | ✓ | ✓ |
| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code>| Set due date | ✓ |
| `/remove_due_date` | Remove due date | ✓ | |
| `/weight 0,1,2, ...` | Set weight **[STARTER]** | ✓ | |
| `/clear_weight` | Clears weight **[STARTER]** | ✓ | |
| `/epic <group&epic &#124; Epic URL>` | Add to epic **[ULTIMATE]** | ✓ | |
| `/remove_epic` | Removes from epic **[ULTIMATE]** | ✓ | |
| `/confidential` | Make confidential | ✓ | |
| `/duplicate #issue` | Mark this issue as a duplicate of another issue | ✓ |
| `/move path/to/project` | Move this issue to another project | ✓ | |
| `/target_branch <Local branch Name>` | Set target branch | | ✓ |
| `/wip` | Toggle the Work In Progress status | | ✓ |
| `/merge` | Merge (when pipeline succeeds) | | ✓ |
## Quick actions for commit messages
The following quick actions are applicable for commit messages:
| Command | Action |
|:---------------------------|:-------------|
| `/close` | Close the issue or merge request |
| `/reopen` | Reopen the issue or merge request |
| `/merge` | Merge (when pipeline succeeds) |
| `/title <New title>` | Change title |
| `/assign @username` | Assign |
| `/unassign` | Remove assignee |
| `/milestone %milestone` | Set milestone |
| `/remove_milestone` | Remove milestone |
| `/label ~foo ~"bar baz"` | Add label(s) |
| `/unlabel ~foo ~"bar baz"` | Remove all or specific label(s) |
| `/relabel ~foo ~"bar baz"` | Replace all label(s) |
|:------------------------|:------------------------------------------|
| `/tag v1.2.3 <message>` | Tags this commit with an optional message |
## Quick actions for Epics **[ULTIMATE]**
The following quick actions are applicable for epics threads and description:
| Command | Action |
|:---------------------------|:----------------------------------------|
| `/tableflip <Comment>` | Append the comment with `(╯°□°)╯︵ ┻━┻` |
| `/shrug <Comment>` | Append the comment with `¯\_(ツ)_/¯` |
| `/todo` | Add a todo |
| `/done` | Mark todo as done |
| `/subscribe` | Subscribe |
| `/unsubscribe` | Unsubscribe |
| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code> | Set due date |
| `/remove_due_date` | Remove due date |
| `/wip` | Toggle the Work In Progress status |
| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate |
| `/remove_estimate` | Remove estimated time |
| <code>/spend &lt;time(1h 30m &#124; -1h 5m)&gt; &lt;date(YYYY-MM-DD)&gt;</code> | Add or subtract spent time; optionally, specify the date that time was spent on |
| `/remove_time_spent` | Remove time spent |
| `/target_branch <Branch Name>` | Set target branch for current merge request |
| `/award :emoji:` | Toggle award for :emoji: |
| `/board_move ~column` | Move issue to column on the board |
| `/duplicate #issue` | Closes this issue and marks it as a duplicate of another issue |
| `/move path/to/project` | Moves issue to another project |
| `/tag v1.2.3 <message>` | Tags a commit with a given tag name and optional message |
| `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` |
| `/shrug` | Append the comment with `¯\_(ツ)_/¯` |
| <code>/copy_metadata #issue &#124; !merge_request</code> | Copy labels and milestone from other issue or merge request |
| `/confidential` | Makes the issue confidential |
| `/lock` | Lock the discussion |
| `/unlock` | Unlock the discussion |
| `/close` | Close |
| `/reopen` | Reopen |
| `/title <New title>` | Change title |
| `/award :emoji:` | Toggle emoji award |
| `/label ~label1 ~label2` | Add label(s) |
| `/unlabel ~label1 ~label2` | Remove all or specific label(s) |
| `/relabel ~label1 ~label2` | Replace label |
\ No newline at end of file
......@@ -47,4 +47,8 @@ module Gitlab
def self.dev_env_or_com?
Rails.env.development? || org? || com?
end
def self.pre_release?
VERSION.include?('pre')
end
end
......@@ -11,7 +11,7 @@ module Gitlab
include Validatable
include Attributable
ALLOWED_KEYS = %i[junit sast dependency_scanning container_scanning dast].freeze
ALLOWED_KEYS = %i[junit codequality sast dependency_scanning container_scanning dast].freeze
attributes ALLOWED_KEYS
......@@ -21,6 +21,7 @@ module Gitlab
with_options allow_nil: true do
validates :junit, array_of_strings_or_string: true
validates :codequality, array_of_strings_or_string: true
validates :sast, array_of_strings_or_string: true
validates :dependency_scanning, array_of_strings_or_string: true
validates :container_scanning, array_of_strings_or_string: true
......
......@@ -49,7 +49,7 @@ variables:
POSTGRES_DB: $CI_ENVIRONMENT_SLUG
KUBERNETES_VERSION: 1.8.6
HELM_VERSION: 2.10.0
HELM_VERSION: 2.11.0
DOCKER_DRIVER: overlay2
......@@ -239,7 +239,7 @@ review:
- install_dependencies
- download_chart
- ensure_namespace
- install_tiller
- initialize_tiller
- create_secret
- deploy
- persist_environment_url
......@@ -265,6 +265,7 @@ stop_review:
GIT_STRATEGY: none
script:
- install_dependencies
- initialize_tiller
- delete
environment:
name: review/$CI_COMMIT_REF_NAME
......@@ -299,7 +300,7 @@ staging:
- install_dependencies
- download_chart
- ensure_namespace
- install_tiller
- initialize_tiller
- create_secret
- deploy
environment:
......@@ -323,7 +324,7 @@ canary:
- install_dependencies
- download_chart
- ensure_namespace
- install_tiller
- initialize_tiller
- create_secret
- deploy canary
environment:
......@@ -344,7 +345,7 @@ canary:
- install_dependencies
- download_chart
- ensure_namespace
- install_tiller
- initialize_tiller
- create_secret
- deploy
- delete canary
......@@ -392,7 +393,7 @@ production_manual:
- install_dependencies
- download_chart
- ensure_namespace
- install_tiller
- initialize_tiller
- create_secret
- deploy rollout $ROLLOUT_PERCENTAGE
- scale stable $((100-ROLLOUT_PERCENTAGE))
......@@ -651,7 +652,12 @@ rollout 100%:
curl "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx
mv linux-amd64/helm /usr/bin/
mv linux-amd64/tiller /usr/bin/
helm version --client
tiller -version
helm init --client-only
helm plugin install https://github.com/adamreese/helm-local
curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
chmod +x /usr/bin/kubectl
......@@ -758,10 +764,13 @@ rollout 100%:
echo ""
}
function install_tiller() {
function initialize_tiller() {
echo "Checking Tiller..."
helm init --upgrade
kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy"
helm local start
helm local status
export HELM_HOST=":44134"
if ! helm version --debug; then
echo "Failed to init Tiller."
return 1
......
......@@ -19,13 +19,17 @@ module Gitlab
alias_method :expanded?, :expanded
SERIALIZE_KEYS = %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large).freeze
# The default maximum content size to display a diff patch.
#
# If this value ever changes, make sure to create a migration to update
# current records, and default of `ApplicationSettings#diff_max_patch_bytes`.
DEFAULT_MAX_PATCH_BYTES = 100.kilobytes
# The maximum size of a diff to display.
SIZE_LIMIT = 100.kilobytes
# This is a limitation applied on the source (Gitaly), therefore we don't allow
# persisting limits over that.
MAX_PATCH_BYTES_UPPER_BOUND = 500.kilobytes
# The maximum size before a diff is collapsed.
COLLAPSE_LIMIT = 10.kilobytes
SERIALIZE_KEYS = %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large).freeze
class << self
def between(repo, head, base, options = {}, *paths)
......@@ -105,6 +109,26 @@ module Gitlab
def binary_message(old_path, new_path)
"Binary files #{old_path} and #{new_path} differ\n"
end
# Returns the limit of bytes a single diff file can reach before it
# appears as 'collapsed' for end-users.
# By convention, it's 10% of the persisted `diff_max_patch_bytes`.
#
# Example: If we have 100k for the `diff_max_patch_bytes`, it will be 10k by
# default.
#
# Patches surpassing this limit should still be persisted in the database.
def patch_safe_limit_bytes
patch_hard_limit_bytes / 10
end
# Returns the limit for a single diff file (patch).
#
# Patches surpassing this limit shouldn't be persisted in the database
# and will be presented as 'too large' for end-users.
def patch_hard_limit_bytes
Gitlab::CurrentSettings.diff_max_patch_bytes
end
end
def initialize(raw_diff, expanded: true)
......@@ -150,7 +174,7 @@ module Gitlab
def too_large?
if @too_large.nil?
@too_large = @diff.bytesize >= SIZE_LIMIT
@too_large = @diff.bytesize >= self.class.patch_hard_limit_bytes
else
@too_large
end
......@@ -168,7 +192,7 @@ module Gitlab
def collapsed?
return @collapsed if defined?(@collapsed)
@collapsed = !expanded && @diff.bytesize >= COLLAPSE_LIMIT
@collapsed = !expanded && @diff.bytesize >= self.class.patch_safe_limit_bytes
end
def collapse!
......@@ -219,30 +243,6 @@ module Gitlab
collapse!
end
end
# If the patch surpasses any of the diff limits it calls the appropiate
# prune method and returns true. Otherwise returns false.
def prune_large_patch(patch)
size = 0
patch.each_hunk do |hunk|
hunk.each_line do |line|
size += line.content.bytesize
if size >= SIZE_LIMIT
too_large!
return true # rubocop:disable Cop/AvoidReturnFromBlocks
end
end
end
if !expanded && size >= COLLAPSE_LIMIT
collapse!
return true
end
false
end
end
end
end
......@@ -19,7 +19,7 @@ module Gitlab
limits[:safe_max_files] = [limits[:max_files], DEFAULT_LIMITS[:max_files]].min
limits[:safe_max_lines] = [limits[:max_lines], DEFAULT_LIMITS[:max_lines]].min
limits[:safe_max_bytes] = limits[:safe_max_files] * 5.kilobytes # Average 5 KB per file
limits[:max_patch_bytes] = Gitlab::Git::Diff::SIZE_LIMIT
limits[:max_patch_bytes] = Gitlab::Git::Diff.patch_hard_limit_bytes
OpenStruct.new(limits)
end
......
......@@ -17,6 +17,12 @@ module Gitlab
kubeclient.create_pod(command.pod_resource)
end
def update(command)
namespace.ensure_exists!
update_config_map(command)
kubeclient.create_pod(command.pod_resource)
end
##
# Returns Pod phase
#
......@@ -36,6 +42,12 @@ module Gitlab
kubeclient.delete_pod(pod_name, namespace.name)
end
def get_config_map(config_map_name)
namespace.ensure_exists!
kubeclient.get_config_map(config_map_name, namespace.name)
end
private
attr_reader :kubeclient, :namespace
......@@ -46,6 +58,12 @@ module Gitlab
end
end
def update_config_map(command)
command.config_map_resource.tap do |config_map_resource|
kubeclient.update_config_map(config_map_resource)
end
end
def create_service_account(command)
command.service_account_resource.tap do |service_account_resource|
break unless service_account_resource
......
# frozen_string_literal: true
module Gitlab
module Kubernetes
module Helm
class UpgradeCommand
include BaseCommand
attr_reader :name, :chart, :version, :repository, :files
def initialize(name, chart:, files:, rbac:, version: nil, repository: nil)
@name = name
@chart = chart
@rbac = rbac
@version = version
@files = files
@repository = repository
end
def generate_script
super + [
init_command,
repository_command,
script_command
].compact.join("\n")
end
def rbac?
@rbac
end
def pod_name
"upgrade-#{name}"
end
private
def init_command
'helm init --client-only >/dev/null'
end
def repository_command
"helm repo add #{name} #{repository}" if repository
end
def script_command
upgrade_flags = "#{optional_version_flag}#{optional_tls_flags}" \
" --reset-values" \
" --install" \
" --namespace #{::Gitlab::Kubernetes::Helm::NAMESPACE}" \
" -f /data/helm/#{name}/config/values.yaml"
"helm upgrade #{name} #{chart}#{upgrade_flags} >/dev/null\n"
end
def optional_version_flag
" --version #{version}" if version
end
def optional_tls_flags
return unless files.key?(:'ca.pem')
" --tls" \
" --tls-ca-cert #{files_dir}/ca.pem" \
" --tls-cert #{files_dir}/cert.pem" \
" --tls-key #{files_dir}/key.pem"
end
end
end
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -2292,6 +2292,12 @@ msgstr ""
msgid "Details"
msgstr ""
msgid "Diff content limits"
msgstr ""
msgid "Diff limits"
msgstr ""
msgid "Diffs|No file name available"
msgstr ""
......@@ -3645,6 +3651,33 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
msgid "MarkdownToolbar|Add a bullet list"
msgstr ""
msgid "MarkdownToolbar|Add a link"
msgstr ""
msgid "MarkdownToolbar|Add a numbered list"
msgstr ""
msgid "MarkdownToolbar|Add a task list"
msgstr ""
msgid "MarkdownToolbar|Add bold text"
msgstr ""
msgid "MarkdownToolbar|Add italic text"
msgstr ""
msgid "MarkdownToolbar|Go full screen"
msgstr ""
msgid "MarkdownToolbar|Insert a quote"
msgstr ""
msgid "MarkdownToolbar|Insert code"
msgstr ""
msgid "Max access level"
msgstr ""
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment