Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
03c5d7f2c1
commit
d35de87f96
|
@ -354,7 +354,11 @@ export default {
|
|||
v-if="!diffFile.submodule && addMergeRequestButtons"
|
||||
class="file-actions d-flex align-items-center gl-ml-auto gl-align-self-start"
|
||||
>
|
||||
<diff-stats :added-lines="diffFile.added_lines" :removed-lines="diffFile.removed_lines" />
|
||||
<diff-stats
|
||||
:diff-file="diffFile"
|
||||
:added-lines="diffFile.added_lines"
|
||||
:removed-lines="diffFile.removed_lines"
|
||||
/>
|
||||
<gl-form-checkbox
|
||||
v-if="isReviewable && showLocalFileReviews"
|
||||
v-gl-tooltip.hover
|
||||
|
|
|
@ -2,10 +2,16 @@
|
|||
import { GlIcon } from '@gitlab/ui';
|
||||
import { isNumber } from 'lodash';
|
||||
import { n__ } from '~/locale';
|
||||
import { isNotDiffable, stats } from '../utils/diff_file';
|
||||
|
||||
export default {
|
||||
components: { GlIcon },
|
||||
props: {
|
||||
diffFile: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => null,
|
||||
},
|
||||
addedLines: {
|
||||
type: Number,
|
||||
required: true,
|
||||
|
@ -33,6 +39,12 @@ export default {
|
|||
hasDiffFiles() {
|
||||
return isNumber(this.diffFilesLength) && this.diffFilesLength >= 0;
|
||||
},
|
||||
notDiffable() {
|
||||
return isNotDiffable(this.diffFile);
|
||||
},
|
||||
fileStats() {
|
||||
return stats(this.diffFile);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -45,23 +57,28 @@ export default {
|
|||
'd-none d-sm-inline-flex': !isCompareVersionsHeader,
|
||||
}"
|
||||
>
|
||||
<div v-if="hasDiffFiles" class="diff-stats-group">
|
||||
<gl-icon name="doc-code" class="diff-stats-icon text-secondary" />
|
||||
<span class="text-secondary bold">{{ diffFilesCountText }} {{ filesText }}</span>
|
||||
<div v-if="notDiffable" :class="fileStats.classes">
|
||||
{{ fileStats.text }}
|
||||
</div>
|
||||
<div
|
||||
class="diff-stats-group cgreen d-flex align-items-center"
|
||||
:class="{ bold: isCompareVersionsHeader }"
|
||||
>
|
||||
<span>+</span>
|
||||
<span class="js-file-addition-line">{{ addedLines }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="diff-stats-group cred d-flex align-items-center"
|
||||
:class="{ bold: isCompareVersionsHeader }"
|
||||
>
|
||||
<span>-</span>
|
||||
<span class="js-file-deletion-line">{{ removedLines }}</span>
|
||||
<div v-else class="diff-stats-contents">
|
||||
<div v-if="hasDiffFiles" class="diff-stats-group">
|
||||
<gl-icon name="doc-code" class="diff-stats-icon text-secondary" />
|
||||
<span class="text-secondary bold">{{ diffFilesCountText }} {{ filesText }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="diff-stats-group cgreen d-flex align-items-center"
|
||||
:class="{ bold: isCompareVersionsHeader }"
|
||||
>
|
||||
<span>+</span>
|
||||
<span class="js-file-addition-line">{{ addedLines }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="diff-stats-group cred d-flex align-items-center"
|
||||
:class="{ bold: isCompareVersionsHeader }"
|
||||
>
|
||||
<span>-</span>
|
||||
<span class="js-file-deletion-line">{{ removedLines }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { diffViewerModes as viewerModes } from '~/ide/constants';
|
||||
import { changeInPercent, numberToHumanSize } from '~/lib/utils/number_utils';
|
||||
import { truncateSha } from '~/lib/utils/text_utility';
|
||||
import { uuids } from '~/lib/utils/uuids';
|
||||
|
||||
|
@ -46,6 +48,8 @@ function identifier(file) {
|
|||
})[0];
|
||||
}
|
||||
|
||||
export const isNotDiffable = (file) => file?.viewer?.name === viewerModes.not_diffable;
|
||||
|
||||
export function prepareRawDiffFile({ file, allFiles, meta = false }) {
|
||||
const additionalProperties = {
|
||||
brokenSymlink: fileSymlinkInformation(file, allFiles),
|
||||
|
@ -84,3 +88,35 @@ export function isCollapsed(file) {
|
|||
export function getShortShaFromFile(file) {
|
||||
return file.content_sha ? truncateSha(String(file.content_sha)) : null;
|
||||
}
|
||||
|
||||
export function stats(file) {
|
||||
let valid = false;
|
||||
let classes = '';
|
||||
let sign = '';
|
||||
let text = '';
|
||||
let percent = 0;
|
||||
let diff = 0;
|
||||
|
||||
if (file) {
|
||||
percent = changeInPercent(file.old_size, file.new_size);
|
||||
diff = file.new_size - file.old_size;
|
||||
sign = diff >= 0 ? '+' : '';
|
||||
text = `${sign}${numberToHumanSize(diff)} (${sign}${percent}%)`;
|
||||
valid = true;
|
||||
|
||||
if (diff > 0) {
|
||||
classes = 'cgreen';
|
||||
} else if (diff < 0) {
|
||||
classes = 'cred';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
changed: diff,
|
||||
text,
|
||||
percent,
|
||||
classes,
|
||||
sign,
|
||||
valid,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -32,9 +32,9 @@ export default {
|
|||
:href="helpPath"
|
||||
:title="__('About this feature')"
|
||||
target="_blank"
|
||||
class="d-flex-center pl-1"
|
||||
class="d-flex-center"
|
||||
>
|
||||
<gl-icon name="question" />
|
||||
<gl-icon name="question-o" class="gl-ml-3" />
|
||||
</gl-link>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -53,6 +53,6 @@ export default {
|
|||
</span>
|
||||
|
||||
<gl-link v-else target="_blank" :href="helpPath" :aria-label="$options.i18n.securityReportsHelp">
|
||||
<gl-icon name="question" />
|
||||
<gl-icon name="question-o" />
|
||||
</gl-link>
|
||||
</template>
|
||||
|
|
|
@ -191,6 +191,7 @@ export default {
|
|||
<security-summary :message="groupedSummaryText" />
|
||||
|
||||
<help-icon
|
||||
class="gl-ml-3"
|
||||
:help-path="securityReportsDocsPath"
|
||||
:discover-project-security-path="discoverProjectSecurityPath"
|
||||
/>
|
||||
|
@ -219,6 +220,7 @@ export default {
|
|||
{{ $options.i18n.scansHaveRun }}
|
||||
|
||||
<help-icon
|
||||
class="gl-ml-3"
|
||||
:help-path="securityReportsDocsPath"
|
||||
:discover-project-security-path="discoverProjectSecurityPath"
|
||||
/>
|
||||
|
|
|
@ -646,6 +646,10 @@ table.code {
|
|||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
|
||||
.diff-stats-contents {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.diff-stats-group {
|
||||
padding: 0 0.25rem;
|
||||
}
|
||||
|
|
|
@ -183,7 +183,12 @@ class GroupsController < Groups::ApplicationController
|
|||
|
||||
def download_export
|
||||
if @group.export_file_exists?
|
||||
send_upload(@group.export_file, attachment: @group.export_file.filename)
|
||||
if @group.export_archive_exists?
|
||||
send_upload(@group.export_file, attachment: @group.export_file.filename)
|
||||
else
|
||||
redirect_to edit_group_path(@group),
|
||||
alert: _('The file containing the export is not available yet; it may still be transferring. Please try again later.')
|
||||
end
|
||||
else
|
||||
redirect_to edit_group_path(@group),
|
||||
alert: _('Group export link has expired. Please generate a new export from your group settings.')
|
||||
|
|
|
@ -226,7 +226,14 @@ class ProjectsController < Projects::ApplicationController
|
|||
|
||||
def download_export
|
||||
if @project.export_file_exists?
|
||||
send_upload(@project.export_file, attachment: @project.export_file.filename)
|
||||
if @project.export_archive_exists?
|
||||
send_upload(@project.export_file, attachment: @project.export_file.filename)
|
||||
else
|
||||
redirect_to(
|
||||
edit_project_path(@project, anchor: 'js-export-project'),
|
||||
alert: _("The file containing the export is not available yet; it may still be transferring. Please try again later.")
|
||||
)
|
||||
end
|
||||
else
|
||||
redirect_to(
|
||||
edit_project_path(@project, anchor: 'js-export-project'),
|
||||
|
|
|
@ -338,6 +338,8 @@ module ApplicationSettingsHelper
|
|||
:version_check_enabled,
|
||||
:web_ide_clientside_preview_enabled,
|
||||
:diff_max_patch_bytes,
|
||||
:diff_max_files,
|
||||
:diff_max_lines,
|
||||
:commit_email_hostname,
|
||||
:protected_ci_variables,
|
||||
:local_markdown_version,
|
||||
|
|
|
@ -273,6 +273,18 @@ class ApplicationSetting < ApplicationRecord
|
|||
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 :diff_max_files,
|
||||
presence: true,
|
||||
numericality: { only_integer: true,
|
||||
greater_than_or_equal_to: Commit::DEFAULT_MAX_DIFF_FILES_SETTING,
|
||||
less_than_or_equal_to: Commit::MAX_DIFF_FILES_SETTING_UPPER_BOUND }
|
||||
|
||||
validates :diff_max_lines,
|
||||
presence: true,
|
||||
numericality: { only_integer: true,
|
||||
greater_than_or_equal_to: Commit::DEFAULT_MAX_DIFF_LINES_SETTING,
|
||||
less_than_or_equal_to: Commit::MAX_DIFF_LINES_SETTING_UPPER_BOUND }
|
||||
|
||||
validates :user_default_internal_regex, js_regex: true, allow_nil: true
|
||||
|
||||
validates :personal_access_token_prefix,
|
||||
|
|
|
@ -60,6 +60,8 @@ module ApplicationSettingImplementation
|
|||
default_projects_limit: Settings.gitlab['default_projects_limit'],
|
||||
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
||||
diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
|
||||
diff_max_files: Commit::DEFAULT_MAX_DIFF_FILES_SETTING,
|
||||
diff_max_lines: Commit::DEFAULT_MAX_DIFF_LINES_SETTING,
|
||||
disable_feed_token: false,
|
||||
disabled_oauth_sign_in_sources: [],
|
||||
dns_rebinding_protection_enabled: true,
|
||||
|
|
|
@ -33,6 +33,12 @@ class Commit
|
|||
# Used by GFM to match and present link extensions on node texts and hrefs.
|
||||
LINK_EXTENSION_PATTERN = /(patch)/.freeze
|
||||
|
||||
DEFAULT_MAX_DIFF_LINES_SETTING = 50_000
|
||||
DEFAULT_MAX_DIFF_FILES_SETTING = 1_000
|
||||
MAX_DIFF_LINES_SETTING_UPPER_BOUND = 100_000
|
||||
MAX_DIFF_FILES_SETTING_UPPER_BOUND = 3_000
|
||||
DIFF_SAFE_LIMIT_FACTOR = 10
|
||||
|
||||
cache_markdown_field :title, pipeline: :single_line
|
||||
cache_markdown_field :full_title, pipeline: :single_line, limit: 1.kilobyte
|
||||
cache_markdown_field :description, pipeline: :commit_description, limit: 1.megabyte
|
||||
|
@ -78,20 +84,24 @@ class Commit
|
|||
end
|
||||
|
||||
def diff_safe_lines(project: nil)
|
||||
Gitlab::Git::DiffCollection.default_limits(project: project)[:max_lines]
|
||||
diff_safe_max_lines(project: project)
|
||||
end
|
||||
|
||||
def diff_hard_limit_files(project: nil)
|
||||
def diff_max_files(project: nil)
|
||||
if Feature.enabled?(:increased_diff_limits, project)
|
||||
3000
|
||||
elsif Feature.enabled?(:configurable_diff_limits, project)
|
||||
Gitlab::CurrentSettings.diff_max_files
|
||||
else
|
||||
1000
|
||||
end
|
||||
end
|
||||
|
||||
def diff_hard_limit_lines(project: nil)
|
||||
def diff_max_lines(project: nil)
|
||||
if Feature.enabled?(:increased_diff_limits, project)
|
||||
100000
|
||||
elsif Feature.enabled?(:configurable_diff_limits, project)
|
||||
Gitlab::CurrentSettings.diff_max_lines
|
||||
else
|
||||
50000
|
||||
end
|
||||
|
@ -99,11 +109,19 @@ class Commit
|
|||
|
||||
def max_diff_options(project: nil)
|
||||
{
|
||||
max_files: diff_hard_limit_files(project: project),
|
||||
max_lines: diff_hard_limit_lines(project: project)
|
||||
max_files: diff_max_files(project: project),
|
||||
max_lines: diff_max_lines(project: project)
|
||||
}
|
||||
end
|
||||
|
||||
def diff_safe_max_files(project: nil)
|
||||
diff_max_files(project: project) / DIFF_SAFE_LIMIT_FACTOR
|
||||
end
|
||||
|
||||
def diff_safe_max_lines(project: nil)
|
||||
diff_max_lines(project: project) / DIFF_SAFE_LIMIT_FACTOR
|
||||
end
|
||||
|
||||
def from_hash(hash, container)
|
||||
raw_commit = Gitlab::Git::Commit.new(container.repository.raw, hash)
|
||||
new(raw_commit, container)
|
||||
|
|
|
@ -647,13 +647,17 @@ class Group < Namespace
|
|||
end
|
||||
|
||||
def export_file_exists?
|
||||
export_file&.file
|
||||
import_export_upload&.export_file_exists?
|
||||
end
|
||||
|
||||
def export_file
|
||||
import_export_upload&.export_file
|
||||
end
|
||||
|
||||
def export_archive_exists?
|
||||
import_export_upload&.export_archive_exists?
|
||||
end
|
||||
|
||||
def adjourned_deletion?
|
||||
false
|
||||
end
|
||||
|
|
|
@ -11,10 +11,42 @@ class ImportExportUpload < ApplicationRecord
|
|||
mount_uploader :import_file, ImportExportUploader
|
||||
mount_uploader :export_file, ImportExportUploader
|
||||
|
||||
# This causes CarrierWave v1 and v3 (but not v2) to upload the file to
|
||||
# object storage *after* the database entry has been committed to the
|
||||
# database. This avoids idling in a transaction.
|
||||
if Gitlab::Utils.to_boolean(ENV.fetch('ENABLE_STORE_EXPORT_FILE_AFTER_COMMIT', true))
|
||||
skip_callback :save, :after, :store_export_file!
|
||||
set_callback :commit, :after, :store_export_file!
|
||||
end
|
||||
|
||||
scope :updated_before, ->(date) { where('updated_at < ?', date) }
|
||||
scope :with_export_file, -> { where.not(export_file: nil) }
|
||||
|
||||
def retrieve_upload(_identifier, paths)
|
||||
Upload.find_by(model: self, path: paths)
|
||||
end
|
||||
|
||||
def export_file_exists?
|
||||
!!carrierwave_export_file
|
||||
end
|
||||
|
||||
# This checks if the export archive is actually stored on disk. It
|
||||
# requires a HEAD request if object storage is used.
|
||||
def export_archive_exists?
|
||||
!!carrierwave_export_file&.exists?
|
||||
# Handle any HTTP unexpected error
|
||||
# https://github.com/excon/excon/blob/bbb5bd791d0bb2251593b80e3bce98dbec6e8f24/lib/excon/error.rb#L129-L169
|
||||
rescue Excon::Error => e
|
||||
# The HEAD request will fail with a 403 Forbidden if the file does not
|
||||
# exist, and the user does not have permission to list the object
|
||||
# storage bucket.
|
||||
Gitlab::ErrorTracking.track_exception(e)
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def carrierwave_export_file
|
||||
export_file&.file
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1987,7 +1987,11 @@ class Project < ApplicationRecord
|
|||
end
|
||||
|
||||
def export_file_exists?
|
||||
export_file&.file
|
||||
import_export_upload&.export_file_exists?
|
||||
end
|
||||
|
||||
def export_archive_exists?
|
||||
import_export_upload&.export_archive_exists?
|
||||
end
|
||||
|
||||
def export_file
|
||||
|
|
|
@ -3,11 +3,30 @@
|
|||
|
||||
%fieldset
|
||||
.form-group
|
||||
= f.label :diff_max_patch_bytes, _('Maximum diff patch size in bytes'), class: 'label-light'
|
||||
= f.label :diff_max_patch_bytes, _('Maximum diff patch size (Bytes)'), class: 'label-light'
|
||||
= f.number_field :diff_max_patch_bytes, class: 'form-control gl-form-input'
|
||||
%span.form-text.text-muted
|
||||
= _("Collapse diffs larger than this size, and show a 'too large' message instead.")
|
||||
= _("Diff files surpassing this limit will be presented as 'too large' and won't be expandable.")
|
||||
|
||||
= link_to sprite_icon('question-o'),
|
||||
help_page_path('user/admin_area/diff_limits')
|
||||
help_page_path('user/admin_area/diff_limits',
|
||||
anchor: 'diff-limits-administration')
|
||||
|
||||
= f.label :diff_max_files, _('Maximum files in a diff'), class: 'label-light'
|
||||
= f.number_field :diff_max_files, class: 'form-control gl-form-input'
|
||||
%span.form-text.text-muted
|
||||
= _("Diff files surpassing this limit will be presented as 'too large' and won't be expandable.")
|
||||
|
||||
= link_to sprite_icon('question-o'),
|
||||
help_page_path('user/admin_area/diff_limits',
|
||||
anchor: 'diff-limits-administration')
|
||||
|
||||
= f.label :diff_max_lines, _('Maximum lines in a diff'), class: 'label-light'
|
||||
= f.number_field :diff_max_lines, class: 'form-control gl-form-input'
|
||||
%span.form-text.text-muted
|
||||
= _("Diff files surpassing this limit will be presented as 'too large' and won't be expandable.")
|
||||
|
||||
= link_to sprite_icon('question-o'),
|
||||
help_page_path('user/admin_area/diff_limits',
|
||||
anchor: 'diff-limits-administration')
|
||||
= f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
- settings_link_start = "<a href=\"#{integrations_admin_application_settings_path}\">".html_safe
|
||||
|
||||
.gl-alert.gl-alert-danger.gl-mt-5{ role: 'alert' }
|
||||
= sprite_icon('error', css_class: 'gl-alert-icon gl-alert-icon-no-title')
|
||||
%h4.gl-alert-title= s_('AdminSettings|Service templates are deprecated and will be removed in GitLab 14.0.')
|
||||
.gl-alert-body
|
||||
= html_escape_once(s_("AdminSettings|You can't add new templates. To migrate or remove a Service template, create a new integration at %{settings_link_start}Settings > Integrations%{link_end}. Learn more about %{doc_link_start}Project integration management%{link_end}.")).html_safe % { settings_link_start: settings_link_start, doc_link_start: doc_link_start, link_end: '</a>'.html_safe }
|
||||
.gl-alert-container
|
||||
= sprite_icon('error', css_class: 'gl-alert-icon gl-alert-icon-no-title')
|
||||
.gl-alert-content
|
||||
%h4.gl-alert-title= s_('AdminSettings|Service templates are deprecated and will be removed in GitLab 14.0.')
|
||||
.gl-alert-body
|
||||
= html_escape_once(s_("AdminSettings|You can't add new templates. To migrate or remove a Service template, create a new integration at %{settings_link_start}Settings > Integrations%{link_end}. Learn more about %{doc_link_start}Project integration management%{link_end}.")).html_safe % { settings_link_start: settings_link_start, doc_link_start: doc_link_start, link_end: '</a>'.html_safe }
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
- experiment(:new_repo, user: current_user) do |e|
|
||||
- e.use do
|
||||
= nav_link(path: 'projects/new#blank_project', html_options: { class: 'gl-border-0 gl-border-t-1 gl-border-solid gl-border-gray-100' }) do
|
||||
= link_to new_project_path(anchor: 'blank_project'), data: { track_label: "projects_dropdown_blank_project", track_event: "click_link", track_experiment: "new_repo" } do
|
||||
= link_to new_project_path(anchor: 'blank_project'), data: { track_label: "projects_dropdown_blank_project", track_event: "click_link", track_experiment: "new_repo", qa_selector: "create_project_link" } do
|
||||
= _('Create blank project')
|
||||
= nav_link(path: 'projects/new#import_project') do
|
||||
= link_to new_project_path(anchor: 'import_project'), data: { track_label: "projects_dropdown_import_project", track_event: "click_link", track_experiment: "new_repo" } do
|
||||
= link_to new_project_path(anchor: 'import_project'), data: { track_label: "projects_dropdown_import_project", track_event: "click_link", track_experiment: "new_repo", qa_selector: "import_project_link" } do
|
||||
= _('Import project')
|
||||
- e.try do
|
||||
= nav_link(path: 'projects/new#blank_project', html_options: { class: 'gl-border-0 gl-border-t-1 gl-border-solid gl-border-gray-100' }) do
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: configurable_diff_limits
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56722
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332194
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: false
|
|
@ -2,15 +2,21 @@
|
|||
|
||||
require 'digest/md5'
|
||||
|
||||
MESSAGE = <<MARKDOWN
|
||||
REVIEW_ROULETTE_SECTION = <<MARKDOWN
|
||||
## Reviewer roulette
|
||||
|
||||
Changes that require review have been detected! A merge request is normally
|
||||
reviewed by both a reviewer and a maintainer in its primary category (e.g.
|
||||
~frontend or ~backend), and by a maintainer in all other categories.
|
||||
MARKDOWN
|
||||
|
||||
CATEGORY_TABLE_HEADER = <<MARKDOWN
|
||||
CATEGORY_TABLE = <<MARKDOWN
|
||||
|
||||
Changes that require review have been detected!
|
||||
|
||||
Please refer to the table below for assigning reviewers and maintainers suggested by Danger in the specified category:
|
||||
|
||||
| Category | Reviewer | Maintainer |
|
||||
| -------- | -------- | ---------- |
|
||||
MARKDOWN
|
||||
|
||||
POST_TABLE_MESSAGE = <<MARKDOWN
|
||||
|
||||
To spread load more evenly across eligible reviewers, Danger has picked a candidate for each
|
||||
review slot, based on their timezone. Feel free to
|
||||
|
@ -26,12 +32,15 @@ Please consider assigning a reviewer or maintainer who is a
|
|||
|
||||
Once you've decided who will review this merge request, assign them as a reviewer!
|
||||
Danger does not automatically notify them for you.
|
||||
MARKDOWN
|
||||
|
||||
| Category | Reviewer | Maintainer |
|
||||
| -------- | -------- | ---------- |
|
||||
NO_SUGGESTIONS = <<MARKDOWN
|
||||
|
||||
There are no reviewer and maintainer suggestions for the changes in this MR.
|
||||
MARKDOWN
|
||||
|
||||
UNKNOWN_FILES_MESSAGE = <<MARKDOWN
|
||||
### Uncategorised files
|
||||
|
||||
These files couldn't be categorised, so Danger was unable to suggest a reviewer.
|
||||
Please consider creating a merge request to
|
||||
|
@ -99,8 +108,14 @@ if changes.any?
|
|||
markdown_row_for_spins(spin.category, [spin])
|
||||
end
|
||||
|
||||
markdown(MESSAGE)
|
||||
markdown(CATEGORY_TABLE_HEADER + rows.join("\n")) unless rows.empty?
|
||||
markdown(REVIEW_ROULETTE_SECTION)
|
||||
|
||||
if rows.empty?
|
||||
markdown(NO_SUGGESTIONS)
|
||||
else
|
||||
markdown(CATEGORY_TABLE + rows.join("\n"))
|
||||
markdown(POST_TABLE_MESSAGE)
|
||||
end
|
||||
|
||||
unknown = changes.fetch(:unknown, [])
|
||||
markdown(UNKNOWN_FILES_MESSAGE + helper.markdown_list(unknown)) unless unknown.empty?
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDiffMaxLinesToApplicationSettings < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column(:application_settings,
|
||||
:diff_max_lines,
|
||||
:integer,
|
||||
default: 50000,
|
||||
null: false)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDiffMaxFilesToApplicationSettings < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column(:application_settings,
|
||||
:diff_max_files,
|
||||
:integer,
|
||||
default: 1000,
|
||||
null: false)
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
aaf5936c945451fa98df7c21ab34c9aa7190dcf301f536c259e5b1fe54407f36
|
|
@ -0,0 +1 @@
|
|||
ac4522ee51d4a4cda317b680c16be3d9ef3e1619bba80c26aefe8d5dc70f013c
|
|
@ -9517,6 +9517,8 @@ CREATE TABLE application_settings (
|
|||
elasticsearch_username text,
|
||||
encrypted_elasticsearch_password bytea,
|
||||
encrypted_elasticsearch_password_iv bytea,
|
||||
diff_max_lines integer DEFAULT 50000 NOT NULL,
|
||||
diff_max_files integer DEFAULT 1000 NOT NULL,
|
||||
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
|
||||
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
|
||||
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
|
||||
|
|
|
@ -251,7 +251,9 @@ listed in the descriptions of the relevant settings.
|
|||
| `default_projects_limit` | integer | no | Project limit per user. Default is `100000`. |
|
||||
| `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
|
||||
| `deletion_adjourned_period` | integer | no | **(PREMIUM SELF)** The number of days to wait before deleting a project or group that is marked for deletion. Value must be between 0 and 90.
|
||||
| `diff_max_patch_bytes` | integer | no | Maximum diff patch size (Bytes). |
|
||||
| `diff_max_patch_bytes` | integer | no | Maximum [diff patch size](../user/admin_area/diff_limits.md), in bytes. |
|
||||
| `diff_max_files` | integer | no | Maximum [files in a diff](../user/admin_area/diff_limits.md). |
|
||||
| `diff_max_lines` | integer | no | Maximum [lines in a diff](../user/admin_area/diff_limits.md). |
|
||||
| `disable_feed_token` | boolean | no | Disable display of RSS/Atom and calendar feed tokens ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/231493) in GitLab 13.7) |
|
||||
| `disabled_oauth_sign_in_sources` | array of strings | no | Disabled OAuth sign-in sources. |
|
||||
| `dns_rebinding_protection_enabled` | boolean | no | Enforce DNS rebinding attack protection. |
|
||||
|
|
|
@ -12,17 +12,25 @@ You can set a maximum size for display of diff files (patches).
|
|||
For details about diff files, [view changes between files](../project/merge_requests/changes.md).
|
||||
Read more about the [built-in limits for merge requests and diffs](../../administration/instance_limits.md#merge-requests).
|
||||
|
||||
## Maximum diff patch size
|
||||
## Configure diff limits
|
||||
|
||||
Diff files which exceed this value are presented as 'too large' and cannot
|
||||
be expandable. Instead of an expandable view, a link to the blob view is
|
||||
shown.
|
||||
WARNING:
|
||||
These settings are experimental. An increased maximum increases resource
|
||||
consumption of your instance. Keep this in mind when adjusting the maximum.
|
||||
|
||||
Patches greater than 10% of this size are automatically collapsed, and a
|
||||
link to expand the diff is presented.
|
||||
This affects merge requests and branch comparison views.
|
||||
To speed the loading time of merge request views and branch comparison views
|
||||
on your instance, you can configure three instance-level maximum values for diffs:
|
||||
|
||||
To set the maximum diff patch size:
|
||||
- **Maximum diff patch size**: The total size, in bytes, of the entire diff.
|
||||
- **Maximum diff files**: The total number of files changed in a diff.
|
||||
- **Maximum diff files**: The total number of files changed in a diff. The default value is 1000.
|
||||
- **Maximum diff lines**: The total number of lines changed in a diff. The default value is 50,000.
|
||||
|
||||
When a diff reaches 10% of any of these values, the files are shown in a
|
||||
collapsed view, with a link to expand the diff. Diffs that exceed any of the
|
||||
set values are presented as **Too large** are cannot be expanded in the UI.
|
||||
|
||||
To configure these values:
|
||||
|
||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
||||
1. In the left sidebar, select **Settings > General**.
|
||||
|
@ -30,10 +38,6 @@ To set the maximum diff patch size:
|
|||
1. Enter a value for **Maximum diff patch size**, measured in bytes.
|
||||
1. Select **Save changes**.
|
||||
|
||||
WARNING:
|
||||
This setting is experimental. An increased maximum increases resource
|
||||
consumption of your instance. Keep this in mind when adjusting the maximum.
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
|
||||
|
|
|
@ -170,6 +170,8 @@ the site during a scan could lead to inaccurate results.
|
|||
|
||||
#### Include the DAST template
|
||||
|
||||
> This template was [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62597) to DAST_VERSION: 2 in GitLab 14.0.
|
||||
|
||||
If you want to manually add DAST to your application, the DAST job is defined
|
||||
in a CI/CD template file. Updates to the template are provided with GitLab
|
||||
upgrades, allowing you to benefit from any improvements and additions.
|
||||
|
|
|
@ -6,17 +6,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Epic Boards **(PREMIUM)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2864) in GitLab 13.10.
|
||||
> - It's [deployed behind a feature flag](../../feature_flags.md), disabled by default.
|
||||
> - It's disabled on GitLab.com.
|
||||
> - It's not recommended for production use.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](../../../administration/feature_flags.md).
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
The GitLab Epic Board is a software project management tool used to plan,
|
||||
organize, and visualize a workflow for a feature or product release.
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5067) in GitLab 13.10.
|
||||
> - [Deployed behind a feature flag](../../feature_flags.md), disabled by default.
|
||||
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/290039) in GitLab 14.0.
|
||||
> - Enabled on GitLab.com.
|
||||
> - Recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](../../../administration/feature_flags.md).
|
||||
|
||||
Epic boards build on the existing [epic tracking functionality](index.md) and
|
||||
[labels](../../project/labels.md). Your epics appear as cards in vertical lists, organized by their assigned
|
||||
|
@ -24,45 +19,156 @@ labels.
|
|||
|
||||
To view an epic board, in a group, select **Epics > Boards**.
|
||||
|
||||
![GitLab epic board - Premium](img/epic_board_v13_10.png)
|
||||
![GitLab epic board - Premium](img/epic_board_v14_0.png)
|
||||
|
||||
## Create an epic board
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- A minimum of [Reporter](../../permissions.md#group-members-permissions) access to a group in GitLab.
|
||||
|
||||
To create a new epic board:
|
||||
|
||||
1. Select the dropdown with the current board name in the upper left corner of the Epic Boards page.
|
||||
1. Go to your group and select **Epics > Boards**.
|
||||
1. In the upper left corner, select the dropdown with the current board name.
|
||||
1. Select **Create new board**.
|
||||
1. Enter the new board's name and select **Create**.
|
||||
1. Enter the new board's title.
|
||||
1. Optional. To hide the Open or Closed lists, clear the **Show the Open list** and
|
||||
**Show the Closed list** checkboxes.
|
||||
1. Optional. Set board scope:
|
||||
1. Next to **Scope**, select **Expand**.
|
||||
1. Next to **Labels**, select **Edit** and select the labels to use as board scope.
|
||||
1. Select **Create board**.
|
||||
|
||||
## Limitations of epic boards
|
||||
Now you can [add some lists](#create-a-new-list).
|
||||
To change these options later, [edit the board](#edit-the-scope-of-an-epic-board).
|
||||
|
||||
As of GitLab 13.10, these limitations apply:
|
||||
## Delete an epic board
|
||||
|
||||
- Epic Boards need to be enabled by an administrator.
|
||||
- Epic Boards can be created but not deleted.
|
||||
- Lists can be added to the board but not deleted.
|
||||
- There is no sidebar on the board. To edit an epic, go to the epic's page.
|
||||
- There is no drag and drop support yet. To move an epic between lists, edit epic labels on the epic's page.
|
||||
- Epics cannot be re-ordered within the list.
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5079) in GitLab 14.0.
|
||||
|
||||
To learn more about the future iterations of this feature, visit
|
||||
[epic 5067](https://gitlab.com/groups/gitlab-org/-/epics/5067).
|
||||
Prerequisites:
|
||||
|
||||
## Enable or disable Epic Boards
|
||||
- A minimum of [Reporter](../../permissions.md#group-members-permissions) access to a group in GitLab.
|
||||
- A minimum of two boards present in a group.
|
||||
|
||||
Epic Boards are under development and not ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
To delete the active epic board:
|
||||
|
||||
1. Select the dropdown with the current board name in the upper left corner of the Epic Boards page.
|
||||
1. Select **Delete board**.
|
||||
1. Select **Delete**.
|
||||
|
||||
## Actions you can take on an epic board
|
||||
|
||||
- [Create a new list](#create-a-new-list).
|
||||
- [Remove an existing list](#remove-a-list).
|
||||
- [Filter epics](#filter-epics).
|
||||
- Create workflows, like when using [issue boards](../../project/issue_board.md#create-workflows).
|
||||
- [Move epics and lists](#move-epics-and-lists).
|
||||
- Change epic labels (by dragging an epic between lists).
|
||||
- Close an epic (by dragging it to the **Closed** list).
|
||||
- [Edit the scope of a board](#edit-the-scope-of-an-epic-board).
|
||||
|
||||
### Create a new list
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- A minimum of [Reporter](../../permissions.md#group-members-permissions) access to a group in GitLab.
|
||||
|
||||
To create a new list:
|
||||
|
||||
1. Go to your group and select **Epics > Boards**.
|
||||
1. In the upper-right corner, select **Create list**.
|
||||
1. In the **New list** column expand the **Select a label** dropdown and select the label to use as
|
||||
list scope.
|
||||
1. Select **Add to board**.
|
||||
|
||||
### Remove a list
|
||||
|
||||
Removing a list doesn't have any effect on epics and labels, as it's just the
|
||||
list view that's removed. You can always create it again later if you need.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- A minimum of [Reporter](../../permissions.md#group-members-permissions) access to a group in GitLab.
|
||||
|
||||
To remove a list from an epic board:
|
||||
|
||||
1. On the top of the list you want to remove, select the **List settings** icon (**{settings}**).
|
||||
The list settings sidebar opens on the right.
|
||||
1. Select **Remove list**. A confirmation dialog appears.
|
||||
1. Select **OK**.
|
||||
|
||||
### Filter epics
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5079) in GitLab 14.0.
|
||||
|
||||
Use the filters on top of your epic board to show only
|
||||
the results you want. It's similar to the filtering used in the epic list,
|
||||
as the metadata from the epics and labels is re-used in the epic board.
|
||||
|
||||
You can filter by the following:
|
||||
|
||||
- Author
|
||||
- Label
|
||||
|
||||
### Move epics and lists
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5079) in GitLab 14.0.
|
||||
|
||||
You can move epics and lists by dragging them.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- A minimum of [Reporter](../../permissions.md#group-members-permissions) access to a group in GitLab.
|
||||
|
||||
To move an epic, select the epic card and drag it to another position in its current list or
|
||||
into another list. Learn about possible effects in [Dragging epics between lists](#dragging-epics-between-lists).
|
||||
|
||||
To move a list, select its top bar, and drag it horizontally.
|
||||
You can't move the **Open** and **Closed** lists, but you can hide them when editing an epic board.
|
||||
|
||||
#### Dragging epics between lists
|
||||
|
||||
When you drag epics between lists, the result is different depending on the source list
|
||||
and the target list.
|
||||
|
||||
| | To Open | To Closed | To label B list |
|
||||
| --------------------- | -------------- | ---------- | ------------------------------ |
|
||||
| **From Open** | - | Close epic | Add label B |
|
||||
| **From Closed** | Reopen epic | - | Reopen epic and add label B |
|
||||
| **From label A list** | Remove label A | Close epic | Remove label A and add label B |
|
||||
|
||||
### Edit the scope of an epic board
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- A minimum of [Reporter](../../permissions.md#group-members-permissions) access to a group in GitLab.
|
||||
|
||||
To edit the scope of an epic board:
|
||||
|
||||
1. In the upper-right corner, select **Edit board**.
|
||||
1. Optional:
|
||||
- Edit the board's title.
|
||||
- Show or hide the Open and Closed columns.
|
||||
- Select other labels as the board's scope.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Enable or disable epic boards
|
||||
|
||||
Epic boards are under development but ready for production use.
|
||||
It is deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:epic_boards)
|
||||
```
|
||||
can disable it.
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:epic_boards)
|
||||
```
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:epic_boards)
|
||||
```
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 41 KiB |
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -263,8 +263,9 @@ The following table lists group permissions available for each role:
|
|||
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| View group wiki pages **(PREMIUM)** | ✓ (6) | ✓ | ✓ | ✓ | ✓ |
|
||||
| View Insights charts **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| View group epic **(PREMIUM)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Create/edit group epic **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
|
||||
| View group epic **(PREMIUM)** | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Create/edit group epic **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Create/edit/delete epic boards **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Manage group labels | | ✓ | ✓ | ✓ | ✓ |
|
||||
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
|
||||
| Pull [packages](packages/index.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||
|
@ -288,8 +289,8 @@ The following table lists group permissions available for each role:
|
|||
| Create/Delete group deploy tokens | | | | | ✓ |
|
||||
| Manage group members | | | | | ✓ |
|
||||
| Delete group | | | | | ✓ |
|
||||
| Delete group epic **(PREMIUM)** | | | | | ✓ |
|
||||
| Edit SAML SSO Billing **(PREMIUM SAAS)** | ✓ | ✓ | ✓ | ✓ | ✓ (4) |
|
||||
| Delete group epic **(PREMIUM)** | | | | | ✓ |
|
||||
| Edit SAML SSO Billing **(PREMIUM SAAS)** | ✓ | ✓ | ✓ | ✓ | ✓ (4) |
|
||||
| View group Audit Events | | | ✓ (7) | ✓ (7) | ✓ |
|
||||
| Disable notification emails | | | | | ✓ |
|
||||
| View Contribution analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
|
|
|
@ -34,11 +34,11 @@ boards in the same project.
|
|||
Different issue board features are available in different [GitLab tiers](https://about.gitlab.com/pricing/),
|
||||
as shown in the following table:
|
||||
|
||||
| Tier | Number of project issue boards | Number of [group issue boards](#group-issue-boards) | [Configurable issue boards](#configurable-issue-boards) | [Assignee lists](#assignee-lists) |
|
||||
|------------------|--------------------------------|------------------------------|---------------------------|----------------|
|
||||
| Free | Multiple | 1 | No | No |
|
||||
| Premium | Multiple | Multiple | Yes | Yes |
|
||||
| Ultimate | Multiple | Multiple | Yes | Yes |
|
||||
| Tier | Number of project issue boards | Number of [group issue boards](#group-issue-boards) | [Configurable issue boards](#configurable-issue-boards) | [Assignee lists](#assignee-lists) |
|
||||
| -------- | ------------------------------ | --------------------------------------------------- | ------------------------------------------------------- | --------------------------------- |
|
||||
| Free | Multiple | 1 | No | No |
|
||||
| Premium | Multiple | Multiple | Yes | Yes |
|
||||
| Ultimate | Multiple | Multiple | Yes | Yes |
|
||||
|
||||
To learn more, visit [GitLab Enterprise features for issue boards](#gitlab-enterprise-features-for-issue-boards) below.
|
||||
|
||||
|
@ -312,7 +312,7 @@ assignee list:
|
|||
1. Search and select the user you want to add as an assignee.
|
||||
|
||||
Now that the assignee list is added, you can assign or unassign issues to that user
|
||||
by [dragging issues](#drag-issues-between-lists) to and from an assignee list.
|
||||
by [moving issues](#move-issues-and-lists) to and from an assignee list.
|
||||
To remove an assignee list, just as with a label list, click the trash icon.
|
||||
|
||||
![Assignee lists](img/issue_board_assignee_lists_v13_6.png)
|
||||
|
@ -328,7 +328,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.
|
||||
|
||||
Like the assignee lists, you're able to [drag issues](#drag-issues-between-lists)
|
||||
Like the assignee lists, you're able to [drag issues](#move-issues-and-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.
|
||||
|
||||
|
@ -355,7 +355,7 @@ iteration. To add an iteration list:
|
|||
1. In the dropdown, select an iteration.
|
||||
1. Select **Add to board**.
|
||||
|
||||
Like the milestone lists, you're able to [drag issues](#drag-issues-between-lists)
|
||||
Like the milestone lists, you're able to [drag issues](#move-issues-and-lists)
|
||||
to and from a iteration list to manipulate the iteration of the dragged issues.
|
||||
|
||||
![Iteration lists](img/issue_board_iteration_lists_v13_10.png)
|
||||
|
@ -380,7 +380,20 @@ To group issues by epic in an issue board:
|
|||
|
||||
![Epics Swimlanes](img/epics_swimlanes_v13.6.png)
|
||||
|
||||
You can also [drag issues](#drag-issues-between-lists) to change their position and epic assignment:
|
||||
To edit an issue without leaving this view, select the issue card (not its title), and a sidebar
|
||||
appears on the right. There you can see and edit the issue's:
|
||||
|
||||
- Title
|
||||
- Assignees
|
||||
- Epic **PREMIUM**
|
||||
- Milestone
|
||||
- Time tracking value (view only)
|
||||
- Due date
|
||||
- Labels
|
||||
- Weight
|
||||
- Notifications setting
|
||||
|
||||
You can also [drag issues](#move-issues-and-lists) to change their position and epic assignment:
|
||||
|
||||
- To reorder an issue, drag it to the new position within a list.
|
||||
- To assign an issue to another epic, drag it to the epic's horizontal lane.
|
||||
|
@ -435,11 +448,11 @@ This feature is only supported when using the [GraphQL-based boards](#graphql-ba
|
|||
- [Remove an issue from a list](#remove-an-issue-from-a-list).
|
||||
- [Filter issues](#filter-issues) that appear across your issue board.
|
||||
- [Create workflows](#create-workflows).
|
||||
- [Drag issues between lists](#drag-issues-between-lists).
|
||||
- [Move issues and lists](#move-issues-and-lists).
|
||||
- [Multi-select issue cards](#multi-select-issue-cards).
|
||||
- Drag and reorder the lists.
|
||||
- Change issue labels (by dragging an issue between lists).
|
||||
- Close an issue (by dragging it to the **Done** list).
|
||||
- Close an issue (by dragging it to the **Closed** list).
|
||||
|
||||
If you're not able to do some of the things above, make sure you have the right
|
||||
[permissions](#permissions).
|
||||
|
@ -483,12 +496,12 @@ You can now choose it to create a list.
|
|||
### Remove a list
|
||||
|
||||
Removing 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.
|
||||
list view that's removed. You can always create it again later if you need.
|
||||
|
||||
To remove a list from an issue board:
|
||||
|
||||
1. Select the **List settings** icon (**{settings}**) on the top of the list you want to remove. The
|
||||
list settings sidebar opens on the right.
|
||||
1. On the top of the list you want to remove, select the **List settings** icon (**{settings}**).
|
||||
The list settings sidebar opens on the right.
|
||||
1. Select **Remove list**. A confirmation dialog appears.
|
||||
1. Select **OK**.
|
||||
|
||||
|
@ -582,16 +595,33 @@ to another list, the label changes and a system note is recorded.
|
|||
|
||||
![issue board system notes](img/issue_board_system_notes_v13_6.png)
|
||||
|
||||
### Drag issues between lists
|
||||
### Move issues and lists
|
||||
|
||||
When dragging issues between lists, different behavior occurs depending on the source list and the target list.
|
||||
You can move issues and lists by dragging them.
|
||||
|
||||
| | To Open | To Closed | To label `B` list | To assignee `Bob` list |
|
||||
| ------------------------------ | ------------------ | ------------ | ---------------------------- | ------------------------------------- |
|
||||
| **From Open** | - | Issue closed | `B` added | `Bob` assigned |
|
||||
| **From Closed** | Issue reopened | - | Issue reopened<br/>`B` added | Issue reopened<br/>`Bob` assigned |
|
||||
| **From label `A` list** | `A` removed | Issue closed | `A` removed<br/>`B` added | `Bob` assigned |
|
||||
| **From assignee `Alice` list** | `Alice` unassigned | Issue closed | `B` added | `Alice` unassigned<br/>`Bob` assigned |
|
||||
Prerequisites:
|
||||
|
||||
- A minimum of [Reporter](../permissions.md#project-members-permissions) access to a project in GitLab.
|
||||
|
||||
To move an issue, select the issue card and drag it to another position in its current list or
|
||||
into a different list. Learn about possible effects in [Dragging issues between lists](#dragging-issues-between-lists).
|
||||
|
||||
To move a list, select its top bar, and drag it horizontally.
|
||||
You can't move the **Open** and **Closed** lists, but you can hide them when editing an issue board.
|
||||
|
||||
#### Dragging issues between lists
|
||||
|
||||
To move an issue to another list, select the issue card and drag it onto that list.
|
||||
|
||||
When you drag issues between lists, the result is different depending on the source list
|
||||
and the target list.
|
||||
|
||||
| | To Open | To Closed | To label B list | To assignee Bob list |
|
||||
| ---------------------------- | -------------- | ----------- | ------------------------------ | ----------------------------- |
|
||||
| **From Open** | - | Close issue | Add label B | Assign Bob |
|
||||
| **From Closed** | Reopen issue | - | Reopen issue and add label B | Reopen issue and assign Bob |
|
||||
| **From label A list** | Remove label A | Close issue | Remove label A and add label B | Assign Bob |
|
||||
| **From assignee Alice list** | Unassign Alice | Close issue | Add label B | Unassign Alice and assign Bob |
|
||||
|
||||
### Multi-select issue cards
|
||||
|
||||
|
|
|
@ -23,7 +23,11 @@ module API
|
|||
check_rate_limit! :group_download_export, [current_user, user_group]
|
||||
|
||||
if user_group.export_file_exists?
|
||||
present_carrierwave_file!(user_group.export_file)
|
||||
if user_group.export_archive_exists?
|
||||
present_carrierwave_file!(user_group.export_file)
|
||||
else
|
||||
render_api_error!('The group export file is not available yet', 404)
|
||||
end
|
||||
else
|
||||
render_api_error!('404 Not found or has expired', 404)
|
||||
end
|
||||
|
|
|
@ -30,7 +30,11 @@ module API
|
|||
check_rate_limit! :project_download_export, [current_user, user_project]
|
||||
|
||||
if user_project.export_file_exists?
|
||||
present_carrierwave_file!(user_project.export_file)
|
||||
if user_project.export_archive_exists?
|
||||
present_carrierwave_file!(user_project.export_file)
|
||||
else
|
||||
render_api_error!('The project export file is not available yet', 404)
|
||||
end
|
||||
else
|
||||
render_api_error!('404 Not found or has expired', 404)
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ stages:
|
|||
- dast
|
||||
|
||||
variables:
|
||||
DAST_VERSION: 1
|
||||
DAST_VERSION: 2
|
||||
# Setting this variable will affect all Security templates
|
||||
# (SAST, Dependency Scanning, ...)
|
||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# List of available variables: https://docs.gitlab.com/ee/user/application_security/dast/#available-variables
|
||||
|
||||
variables:
|
||||
DAST_VERSION: 1
|
||||
DAST_VERSION: 2
|
||||
# Setting this variable will affect all Security templates
|
||||
# (SAST, Dependency Scanning, ...)
|
||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
||||
|
|
|
@ -12,11 +12,7 @@ module Gitlab
|
|||
delegate :max_files, :max_lines, :max_bytes, :safe_max_files, :safe_max_lines, :safe_max_bytes, to: :limits
|
||||
|
||||
def self.default_limits(project: nil)
|
||||
if Feature.enabled?(:increased_diff_limits, project)
|
||||
{ max_files: 300, max_lines: 10000 }
|
||||
else
|
||||
{ max_files: 100, max_lines: 5000 }
|
||||
end
|
||||
{ max_files: ::Commit.diff_safe_max_files(project: project), max_lines: ::Commit.diff_safe_max_lines(project: project) }
|
||||
end
|
||||
|
||||
def self.limits(options = {})
|
||||
|
|
|
@ -7969,9 +7969,6 @@ msgstr ""
|
|||
msgid "Collapse approvers"
|
||||
msgstr ""
|
||||
|
||||
msgid "Collapse diffs larger than this size, and show a 'too large' message instead."
|
||||
msgstr ""
|
||||
|
||||
msgid "Collapse issues"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11417,6 +11414,9 @@ msgstr ""
|
|||
msgid "Didn't receive unlock instructions?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Diff files surpassing this limit will be presented as 'too large' and won't be expandable."
|
||||
msgstr ""
|
||||
|
||||
msgid "Diff limits"
|
||||
msgstr ""
|
||||
|
||||
|
@ -20252,7 +20252,7 @@ msgstr ""
|
|||
msgid "Maximum delay (Minutes)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum diff patch size in bytes"
|
||||
msgid "Maximum diff patch size (Bytes)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum duration of a session."
|
||||
|
@ -20276,6 +20276,9 @@ msgstr ""
|
|||
msgid "Maximum file size is 2MB. Please select a smaller file."
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum files in a diff"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum import size (MB)"
|
||||
msgstr ""
|
||||
|
||||
|
@ -20288,6 +20291,9 @@ msgstr ""
|
|||
msgid "Maximum lifetime allowable for Personal Access Tokens is active, your expire date must be set before %{maximum_allowable_date}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum lines in a diff"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximum npm package file size in bytes"
|
||||
msgstr ""
|
||||
|
||||
|
@ -32425,6 +32431,9 @@ msgstr ""
|
|||
msgid "The errors we encountered were:"
|
||||
msgstr ""
|
||||
|
||||
msgid "The file containing the export is not available yet; it may still be transferring. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
msgid "The file has been successfully created."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -55,13 +55,17 @@ module QA
|
|||
end
|
||||
|
||||
def go_to_projects
|
||||
go_to_menu_dropdown_option(:projects_dropdown)
|
||||
|
||||
within_element(:menu_subview_container) do
|
||||
within_projects_menu do
|
||||
click_element(:menu_item_link, title: 'Your projects')
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_create_project
|
||||
within_projects_menu do
|
||||
click_element(:menu_item_link, title: 'Create new project')
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_menu_dropdown_option(option_name)
|
||||
within_top_menu do
|
||||
click_element(:navbar_dropdown, title: 'Menu')
|
||||
|
@ -84,11 +88,11 @@ module QA
|
|||
def go_to_admin_area
|
||||
click_admin_area
|
||||
|
||||
if has_text?('Enter Admin Mode', wait: 1.0)
|
||||
Admin::NewSession.perform do |new_session|
|
||||
new_session.set_password(Runtime::User.admin_password)
|
||||
new_session.click_enter_admin_mode
|
||||
end
|
||||
return unless has_text?('Enter Admin Mode', wait: 1.0)
|
||||
|
||||
Admin::NewSession.perform do |new_session|
|
||||
new_session.set_password(Runtime::User.admin_password)
|
||||
new_session.click_enter_admin_mode
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -166,19 +170,15 @@ module QA
|
|||
|
||||
private
|
||||
|
||||
def within_top_menu
|
||||
within_element(:navbar) do
|
||||
yield
|
||||
end
|
||||
def within_top_menu(&block)
|
||||
within_element(:navbar, &block)
|
||||
end
|
||||
|
||||
def within_user_menu
|
||||
def within_user_menu(&block)
|
||||
within_top_menu do
|
||||
click_element :user_avatar
|
||||
|
||||
within_element(:user_menu) do
|
||||
yield
|
||||
end
|
||||
within_element(:user_menu, &block)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -188,6 +188,12 @@ module QA
|
|||
within_element(:menu_subview_container, &block)
|
||||
end
|
||||
|
||||
def within_projects_menu(&block)
|
||||
go_to_menu_dropdown_option(:projects_dropdown)
|
||||
|
||||
within_element(:menu_subview_container, &block)
|
||||
end
|
||||
|
||||
def click_admin_area
|
||||
go_to_menu_dropdown_option(:admin_area_link)
|
||||
end
|
||||
|
|
|
@ -32,24 +32,20 @@ module QA
|
|||
end
|
||||
|
||||
def import!(full_path, name)
|
||||
unless already_imported(full_path)
|
||||
choose_test_namespace(full_path)
|
||||
set_path(full_path, name)
|
||||
import_project(full_path)
|
||||
wait_for_success
|
||||
end
|
||||
return if already_imported(full_path)
|
||||
|
||||
go_to_project(name)
|
||||
choose_test_namespace(full_path)
|
||||
set_path(full_path, name)
|
||||
import_project(full_path)
|
||||
wait_for_success
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def within_repo_path(full_path)
|
||||
def within_repo_path(full_path, &block)
|
||||
project_import_row = find_element(:project_import_row, text: full_path)
|
||||
|
||||
within(project_import_row) do
|
||||
yield
|
||||
end
|
||||
within(project_import_row, &block)
|
||||
end
|
||||
|
||||
def choose_test_namespace(full_path)
|
||||
|
@ -75,8 +71,13 @@ module QA
|
|||
def wait_for_success
|
||||
# TODO: set reload:false and remove skip_finished_loading_check_on_refresh when
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/292861 is fixed
|
||||
wait_until(max_duration: 60, sleep_interval: 5.0, reload: true, skip_finished_loading_check_on_refresh: true) do
|
||||
page.has_no_content?('Importing 1 repository', wait: 3.0)
|
||||
wait_until(
|
||||
max_duration: 60,
|
||||
sleep_interval: 5.0,
|
||||
reload: true,
|
||||
skip_finished_loading_check_on_refresh: true
|
||||
) do
|
||||
page.has_no_content?('Importing 1 repository')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ module QA
|
|||
include Visibility
|
||||
|
||||
attr_accessor :repository_storage # requires admin access
|
||||
attr_writer :initialize_with_readme
|
||||
attr_writer :auto_devops_enabled
|
||||
attr_writer :github_personal_access_token
|
||||
attr_writer :github_repository_path
|
||||
attr_writer :initialize_with_readme,
|
||||
:auto_devops_enabled,
|
||||
:github_personal_access_token,
|
||||
:github_repository_path
|
||||
|
||||
attribute :id
|
||||
attribute :name
|
||||
|
@ -40,15 +40,11 @@ module QA
|
|||
end
|
||||
|
||||
attribute :repository_ssh_location do
|
||||
Page::Project::Show.perform do |show|
|
||||
show.repository_clone_ssh_location
|
||||
end
|
||||
Page::Project::Show.perform(&:repository_clone_ssh_location)
|
||||
end
|
||||
|
||||
attribute :repository_http_location do
|
||||
Page::Project::Show.perform do |show|
|
||||
show.repository_clone_http_location
|
||||
end
|
||||
Page::Project::Show.perform(&:repository_clone_http_location)
|
||||
end
|
||||
|
||||
def initialize
|
||||
|
@ -104,7 +100,7 @@ module QA
|
|||
def has_file?(file_path)
|
||||
response = repository_tree
|
||||
|
||||
raise ResourceNotFoundError, "#{response[:message]}" if response.is_a?(Hash) && response.has_key?(:message)
|
||||
raise ResourceNotFoundError, (response[:message]).to_s if response.is_a?(Hash) && response.has_key?(:message)
|
||||
|
||||
response.any? { |file| file[:path] == file_path }
|
||||
end
|
||||
|
@ -115,14 +111,14 @@ module QA
|
|||
|
||||
def has_branches?(branches)
|
||||
branches.all? do |branch|
|
||||
response = get(Runtime::API::Request.new(api_client, "#{api_repository_branches_path}/#{branch}").url)
|
||||
response = get(request_url("#{api_repository_branches_path}/#{branch}"))
|
||||
response.code == HTTP_STATUS_OK
|
||||
end
|
||||
end
|
||||
|
||||
def has_tags?(tags)
|
||||
tags.all? do |tag|
|
||||
response = get(Runtime::API::Request.new(api_client, "#{api_repository_tags_path}/#{tag}").url)
|
||||
response = get(request_url("#{api_repository_tags_path}/#{tag}"))
|
||||
response.code == HTTP_STATUS_OK
|
||||
end
|
||||
end
|
||||
|
@ -183,6 +179,10 @@ module QA
|
|||
"#{api_get_path}/pipeline_schedules"
|
||||
end
|
||||
|
||||
def api_issues_path
|
||||
"#{api_get_path}/issues"
|
||||
end
|
||||
|
||||
def api_put_path
|
||||
"/projects/#{id}"
|
||||
end
|
||||
|
@ -217,19 +217,28 @@ module QA
|
|||
|
||||
def change_repository_storage(new_storage)
|
||||
put_body = { repository_storage: new_storage }
|
||||
response = put Runtime::API::Request.new(api_client, api_put_path).url, put_body
|
||||
response = put(request_url(api_put_path), put_body)
|
||||
|
||||
unless response.code == HTTP_STATUS_OK
|
||||
raise ResourceUpdateFailedError, "Could not change repository storage to #{new_storage}. Request returned (#{response.code}): `#{response}`."
|
||||
raise(
|
||||
ResourceUpdateFailedError,
|
||||
"Could not change repository storage to #{new_storage}. Request returned (#{response.code}): `#{response}`."
|
||||
)
|
||||
end
|
||||
|
||||
wait_until(sleep_interval: 1) { Runtime::API::RepositoryStorageMoves.has_status?(self, 'finished', new_storage) }
|
||||
wait_until(sleep_interval: 1) do
|
||||
Runtime::API::RepositoryStorageMoves.has_status?(self, 'finished', new_storage)
|
||||
end
|
||||
rescue Support::Repeater::RepeaterConditionExceededError
|
||||
raise Runtime::API::RepositoryStorageMoves::RepositoryStorageMovesError, 'Timed out while waiting for the repository storage move to finish'
|
||||
raise(
|
||||
Runtime::API::RepositoryStorageMoves::RepositoryStorageMovesError,
|
||||
'Timed out while waiting for the repository storage move to finish'
|
||||
)
|
||||
end
|
||||
|
||||
def commits
|
||||
parse_body(get(Runtime::API::Request.new(api_client, api_commits_path).url))
|
||||
response = get(request_url(api_commits_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def default_branch
|
||||
|
@ -237,7 +246,7 @@ module QA
|
|||
end
|
||||
|
||||
def import_status
|
||||
response = get Runtime::API::Request.new(api_client, "/projects/#{id}/import").url
|
||||
response = get(request_url("/projects/#{id}/import"))
|
||||
|
||||
unless response.code == HTTP_STATUS_OK
|
||||
raise ResourceQueryError, "Could not get import status. Request returned (#{response.code}): `#{response}`."
|
||||
|
@ -251,7 +260,8 @@ module QA
|
|||
end
|
||||
|
||||
def merge_requests
|
||||
parse_body(get(Runtime::API::Request.new(api_client, api_merge_requests_path).url))
|
||||
response = get(request_url(api_merge_requests_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def merge_request_with_title(title)
|
||||
|
@ -260,42 +270,52 @@ module QA
|
|||
|
||||
def runners(tag_list: nil)
|
||||
response = if tag_list
|
||||
get Runtime::API::Request.new(api_client, "#{api_runners_path}?tag_list=#{tag_list.compact.join(',')}", per_page: '100').url
|
||||
get(request_url("#{api_runners_path}?tag_list=#{tag_list.compact.join(',')}", per_page: '100'))
|
||||
else
|
||||
get Runtime::API::Request.new(api_client, "#{api_runners_path}", per_page: '100').url
|
||||
get(request_url(api_runners_path, per_page: '100'))
|
||||
end
|
||||
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def registry_repositories
|
||||
response = get Runtime::API::Request.new(api_client, "#{api_registry_repositories_path}").url
|
||||
response = get(request_url(api_registry_repositories_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def packages
|
||||
response = get Runtime::API::Request.new(api_client, "#{api_packages_path}").url
|
||||
response = get(request_url(api_packages_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def repository_branches
|
||||
parse_body(get(Runtime::API::Request.new(api_client, api_repository_branches_path).url))
|
||||
response = get(request_url(api_repository_branches_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def repository_tags
|
||||
parse_body(get(Runtime::API::Request.new(api_client, api_repository_tags_path).url))
|
||||
response = get(request_url(api_repository_tags_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def repository_tree
|
||||
parse_body(get(Runtime::API::Request.new(api_client, api_repository_tree_path).url))
|
||||
response = get(request_url(api_repository_tree_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def pipelines
|
||||
parse_body(get(Runtime::API::Request.new(api_client, api_pipelines_path).url))
|
||||
response = get(request_url(api_pipelines_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def pipeline_schedules
|
||||
parse_body(get(Runtime::API::Request.new(api_client, api_pipeline_schedules_path).url))
|
||||
response = get(request_url(api_pipeline_schedules_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
def issues
|
||||
response = get(request_url(api_issues_path))
|
||||
parse_body(response)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -307,6 +327,14 @@ module QA
|
|||
Git::Location.new(api_resource[:http_url_to_repo])
|
||||
api_resource
|
||||
end
|
||||
|
||||
# Get api request url
|
||||
#
|
||||
# @param [String] path
|
||||
# @return [String]
|
||||
def request_url(path, **opts)
|
||||
Runtime::API::Request.new(api_client, path, **opts).url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,23 +7,19 @@ module QA
|
|||
class ProjectImportedFromGithub < Resource::Project
|
||||
def fabricate!
|
||||
self.import = true
|
||||
super
|
||||
|
||||
group.visit!
|
||||
Page::Main::Menu.perform(&:go_to_create_project)
|
||||
|
||||
Page::Group::Show.perform(&:go_to_new_project)
|
||||
go_to_import_page
|
||||
Page::Project::New.perform(&:click_github_link)
|
||||
Page::Project::New.perform do |project_page|
|
||||
project_page.click_import_project
|
||||
project_page.click_github_link
|
||||
end
|
||||
|
||||
Page::Project::Import::Github.perform do |import_page|
|
||||
import_page.add_personal_access_token(@github_personal_access_token)
|
||||
import_page.import!(@github_repository_path, @name)
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_import_page
|
||||
Page::Project::New.perform(&:click_import_project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,25 +3,26 @@
|
|||
module QA
|
||||
RSpec.describe 'Manage', :github, :requires_admin do
|
||||
describe 'Project import' do
|
||||
let!(:api_client) { Runtime::API::Client.as_admin }
|
||||
let!(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } }
|
||||
let!(:user) do
|
||||
Resource::User.fabricate_via_api! do |resource|
|
||||
resource.api_client = Runtime::API::Client.as_admin
|
||||
resource.api_client = api_client
|
||||
resource.hard_delete_on_api_removal = true
|
||||
end
|
||||
end
|
||||
|
||||
let(:group) { Resource::Group.fabricate_via_api! }
|
||||
|
||||
let(:imported_project) do
|
||||
Resource::ProjectImportedFromGithub.fabricate_via_browser_ui! do |project|
|
||||
project.name = 'imported-project'
|
||||
project.group = group
|
||||
project.github_personal_access_token = Runtime::Env.github_access_token
|
||||
project.github_repository_path = 'gitlab-qa-github/test-project'
|
||||
project.api_client = api_client
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
Runtime::Feature.enable(:invite_members_group_modal, group: group)
|
||||
group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
|
||||
end
|
||||
|
||||
|
@ -32,90 +33,49 @@ module QA
|
|||
it 'imports a GitHub repo', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1762' do
|
||||
Flow::Login.sign_in(as: user)
|
||||
|
||||
imported_project # import the project
|
||||
imported_project.reload! # import the project and reload all fields
|
||||
|
||||
Page::Main::Menu.perform(&:go_to_projects)
|
||||
Page::Dashboard::Projects.perform do |dashboard|
|
||||
dashboard.go_to_project(imported_project.name)
|
||||
aggregate_failures do
|
||||
verify_repository_import
|
||||
verify_issues_import
|
||||
verify_merge_requests_import
|
||||
end
|
||||
|
||||
Page::Project::Show.perform(&:wait_for_import)
|
||||
|
||||
verify_repository_import
|
||||
verify_issues_import
|
||||
verify_merge_requests_import
|
||||
verify_labels_import
|
||||
verify_milestones_import
|
||||
verify_wiki_import
|
||||
end
|
||||
|
||||
def verify_repository_import
|
||||
Page::Project::Show.perform do |project|
|
||||
expect(project).to have_content('This test project is used for automated GitHub import by GitLab QA.')
|
||||
expect(project).to have_content(imported_project.name)
|
||||
end
|
||||
expect(imported_project.api_response).to include(
|
||||
description: 'A new repo for test',
|
||||
import_status: 'finished',
|
||||
import_error: nil
|
||||
)
|
||||
end
|
||||
|
||||
def verify_issues_import
|
||||
QA::Support::Retrier.retry_on_exception do
|
||||
Page::Project::Menu.perform(&:click_issues)
|
||||
issues = imported_project.issues
|
||||
|
||||
Page::Project::Issue::Show.perform do |issue_page|
|
||||
expect(issue_page).to have_content('This is a sample issue')
|
||||
|
||||
click_link 'This is a sample issue'
|
||||
|
||||
expect(issue_page).to have_content('This is a sample first comment')
|
||||
|
||||
# Comments
|
||||
comment_text = 'This is a comment from @sliaquat'
|
||||
|
||||
expect(issue_page).to have_comment(comment_text)
|
||||
expect(issue_page).to have_label('custom new label')
|
||||
expect(issue_page).to have_label('help wanted')
|
||||
expect(issue_page).to have_label('good first issue')
|
||||
end
|
||||
end
|
||||
expect(issues.length).to eq(1)
|
||||
expect(issues.first).to include(
|
||||
title: 'This is a sample issue',
|
||||
description: "*Created by: gitlab-qa-github*\n\nThis is a sample first comment",
|
||||
labels: ['custom new label', 'good first issue', 'help wanted'],
|
||||
user_notes_count: 1
|
||||
)
|
||||
end
|
||||
|
||||
def verify_merge_requests_import
|
||||
Page::Project::Menu.perform(&:click_merge_requests)
|
||||
merge_requests = imported_project.merge_requests
|
||||
|
||||
Page::MergeRequest::Show.perform do |merge_request|
|
||||
expect(merge_request).to have_content('Improve readme')
|
||||
|
||||
click_link 'Improve readme'
|
||||
|
||||
expect(merge_request).to have_content('This improves the README file a bit.')
|
||||
|
||||
# Comments
|
||||
expect(merge_request).to have_content('[PR comment by @sliaquat] Nice work!')
|
||||
|
||||
# Diff comments
|
||||
expect(merge_request).to have_content('[Single diff comment] Good riddance')
|
||||
expect(merge_request).to have_content('[Single diff comment] Nice addition')
|
||||
|
||||
expect(merge_request).to have_label('bug')
|
||||
expect(merge_request).to have_label('documentation')
|
||||
end
|
||||
end
|
||||
|
||||
def verify_labels_import
|
||||
# TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/19228
|
||||
# to build upon it.
|
||||
end
|
||||
|
||||
def verify_milestones_import
|
||||
# TODO: Waiting on https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/18727
|
||||
# to build upon it.
|
||||
end
|
||||
|
||||
def verify_wiki_import
|
||||
Page::Project::Menu.perform(&:click_wiki)
|
||||
|
||||
Page::Project::Wiki::Show.perform do |wiki|
|
||||
expect(wiki).to have_content('Welcome to the test-project wiki!')
|
||||
end
|
||||
expect(merge_requests.length).to eq(1)
|
||||
expect(merge_requests.first).to include(
|
||||
title: 'Improve readme',
|
||||
state: 'opened',
|
||||
target_branch: 'main',
|
||||
source_branch: 'improve-readme',
|
||||
labels: %w[bug documentation],
|
||||
description: <<~DSC.strip
|
||||
*Created by: gitlab-qa-github*\n\nThis improves the README file a bit.\r\n\r\nTODO:\r\n\r\n \r\n\r\n- [ ] Do foo\r\n- [ ] Make bar\r\n - [ ] Think about baz
|
||||
DSC
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1065,14 +1065,13 @@ RSpec.describe GroupsController, factory_default: :keep do
|
|||
|
||||
describe 'GET #download_export' do
|
||||
let(:admin) { create(:admin) }
|
||||
let(:export_file) { fixture_file_upload('spec/fixtures/group_export.tar.gz') }
|
||||
|
||||
before do
|
||||
enable_admin_mode!(admin)
|
||||
end
|
||||
|
||||
context 'when there is a file available to download' do
|
||||
let(:export_file) { fixture_file_upload('spec/fixtures/group_export.tar.gz') }
|
||||
|
||||
before do
|
||||
sign_in(admin)
|
||||
create(:import_export_upload, group: group, export_file: export_file)
|
||||
|
@ -1085,6 +1084,22 @@ RSpec.describe GroupsController, factory_default: :keep do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the file is no longer present on disk' do
|
||||
before do
|
||||
sign_in(admin)
|
||||
|
||||
create(:import_export_upload, group: group, export_file: export_file)
|
||||
group.export_file.file.delete
|
||||
end
|
||||
|
||||
it 'returns not found' do
|
||||
get :download_export, params: { id: group.to_param }
|
||||
|
||||
expect(flash[:alert]).to include('file containing the export is not available yet')
|
||||
expect(response).to redirect_to(edit_group_path(group))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is no file available to download' do
|
||||
before do
|
||||
sign_in(admin)
|
||||
|
|
|
@ -7,7 +7,7 @@ RSpec.describe ProjectsController do
|
|||
include ProjectForksHelper
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let_it_be(:project, reload: true) { create(:project, service_desk_enabled: false) }
|
||||
let_it_be(:project, reload: true) { create(:project, :with_export, service_desk_enabled: false) }
|
||||
let_it_be(:public_project) { create(:project, :public) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
|
@ -1349,7 +1349,7 @@ RSpec.describe ProjectsController do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#download_export' do
|
||||
describe '#download_export', :clean_gitlab_redis_cache do
|
||||
let(:action) { :download_export }
|
||||
|
||||
context 'object storage enabled' do
|
||||
|
@ -1361,6 +1361,17 @@ RSpec.describe ProjectsController do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when project export file is absent' do
|
||||
it 'alerts the user and returns 302' do
|
||||
project.export_file.file.delete
|
||||
|
||||
get action, params: { namespace_id: project.namespace, id: project }
|
||||
|
||||
expect(flash[:alert]).to include('file containing the export is not available yet')
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project export is disabled' do
|
||||
before do
|
||||
stub_application_setting(project_export_enabled?: false)
|
||||
|
|
|
@ -58,6 +58,13 @@ FactoryBot.define do
|
|||
shared_runners_enabled { false }
|
||||
end
|
||||
|
||||
trait :with_export do
|
||||
after(:create) do |group, _evaluator|
|
||||
export_file = fixture_file_upload('spec/fixtures/group_export.tar.gz')
|
||||
create(:import_export_upload, group: group, export_file: export_file)
|
||||
end
|
||||
end
|
||||
|
||||
trait :allow_descendants_override_disabled_shared_runners do
|
||||
allow_descendants_override_disabled_shared_runners { true }
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { GlIcon } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import DiffStats from '~/diffs/components/diff_stats.vue';
|
||||
import mockDiffFile from '../mock_data/diff_file';
|
||||
|
||||
const TEST_ADDED_LINES = 100;
|
||||
const TEST_REMOVED_LINES = 200;
|
||||
|
@ -38,9 +39,37 @@ describe('diff_stats', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('bytes changes', () => {
|
||||
let file;
|
||||
const getBytesContainer = () => wrapper.find('.diff-stats > div:first-child');
|
||||
|
||||
beforeEach(() => {
|
||||
file = {
|
||||
...mockDiffFile,
|
||||
viewer: {
|
||||
...mockDiffFile.viewer,
|
||||
name: 'not_diffable',
|
||||
},
|
||||
};
|
||||
|
||||
createComponent({ diffFile: file });
|
||||
});
|
||||
|
||||
it("renders the bytes changes instead of line changes when the file isn't diffable", () => {
|
||||
const content = getBytesContainer();
|
||||
|
||||
expect(content.classes('cgreen')).toBe(true);
|
||||
expect(content.text()).toBe('+1.00 KiB (+100%)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('line changes', () => {
|
||||
const findFileLine = (name) => wrapper.find(name);
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('shows the amount of lines added', () => {
|
||||
expect(findFileLine('.js-file-addition-line').text()).toBe(TEST_ADDED_LINES.toString());
|
||||
});
|
||||
|
|
|
@ -19,6 +19,8 @@ export default {
|
|||
renamed_file: false,
|
||||
old_path: 'CHANGELOG',
|
||||
new_path: 'CHANGELOG',
|
||||
old_size: 1024,
|
||||
new_size: 2048,
|
||||
mode_changed: false,
|
||||
a_mode: '100644',
|
||||
b_mode: '100644',
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import { prepareRawDiffFile, getShortShaFromFile } from '~/diffs/utils/diff_file';
|
||||
import {
|
||||
prepareRawDiffFile,
|
||||
getShortShaFromFile,
|
||||
stats,
|
||||
isNotDiffable,
|
||||
} from '~/diffs/utils/diff_file';
|
||||
import { diffViewerModes } from '~/ide/constants';
|
||||
import mockDiffFile from '../mock_data/diff_file';
|
||||
|
||||
function getDiffFiles() {
|
||||
const loadFull = 'namespace/project/-/merge_requests/12345/diff_for_path?file_identifier=abc';
|
||||
|
@ -154,4 +161,73 @@ describe('diff_file utilities', () => {
|
|||
expect(getShortShaFromFile({ content_sha: cs })).toBe(response);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stats', () => {
|
||||
const noFile = [
|
||||
"returns empty stats when the file isn't provided",
|
||||
undefined,
|
||||
{
|
||||
text: '',
|
||||
percent: 0,
|
||||
changed: 0,
|
||||
classes: '',
|
||||
sign: '',
|
||||
valid: false,
|
||||
},
|
||||
];
|
||||
const validFile = [
|
||||
'computes the correct stats from a file',
|
||||
mockDiffFile,
|
||||
{
|
||||
changed: 1024,
|
||||
percent: 100,
|
||||
classes: 'cgreen',
|
||||
sign: '+',
|
||||
text: '+1.00 KiB (+100%)',
|
||||
valid: true,
|
||||
},
|
||||
];
|
||||
const negativeChange = [
|
||||
'computed the correct states from a file with a negative size change',
|
||||
{
|
||||
...mockDiffFile,
|
||||
new_size: 0,
|
||||
old_size: 1024,
|
||||
},
|
||||
{
|
||||
changed: -1024,
|
||||
percent: -100,
|
||||
classes: 'cred',
|
||||
sign: '',
|
||||
text: '-1.00 KiB (-100%)',
|
||||
valid: true,
|
||||
},
|
||||
];
|
||||
|
||||
it.each([noFile, validFile, negativeChange])('%s', (_, file, output) => {
|
||||
expect(stats(file)).toEqual(output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNotDiffable', () => {
|
||||
it.each`
|
||||
bool | vw
|
||||
${true} | ${diffViewerModes.not_diffable}
|
||||
${false} | ${diffViewerModes.text}
|
||||
${false} | ${diffViewerModes.image}
|
||||
`('returns $bool when the viewer is $vw', ({ bool, vw }) => {
|
||||
expect(isNotDiffable({ viewer: { name: vw } })).toBe(bool);
|
||||
});
|
||||
|
||||
it.each`
|
||||
file
|
||||
${undefined}
|
||||
${null}
|
||||
${{}}
|
||||
${{ viewer: undefined }}
|
||||
${{ viewer: null }}
|
||||
`('reports `false` when the file is `$file`', ({ file }) => {
|
||||
expect(isNotDiffable(file)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -990,6 +990,34 @@ RSpec.describe ApplicationSetting do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#diff_max_files' do
|
||||
context 'validations' do
|
||||
it { is_expected.to validate_presence_of(:diff_max_files) }
|
||||
|
||||
specify do
|
||||
is_expected
|
||||
.to validate_numericality_of(:diff_max_files)
|
||||
.only_integer
|
||||
.is_greater_than_or_equal_to(Commit::DEFAULT_MAX_DIFF_FILES_SETTING)
|
||||
.is_less_than_or_equal_to(Commit::MAX_DIFF_FILES_SETTING_UPPER_BOUND)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#diff_max_lines' do
|
||||
context 'validations' do
|
||||
it { is_expected.to validate_presence_of(:diff_max_lines) }
|
||||
|
||||
specify do
|
||||
is_expected
|
||||
.to validate_numericality_of(:diff_max_lines)
|
||||
.only_integer
|
||||
.is_greater_than_or_equal_to(Commit::DEFAULT_MAX_DIFF_LINES_SETTING)
|
||||
.is_less_than_or_equal_to(Commit::MAX_DIFF_LINES_SETTING_UPPER_BOUND)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#sourcegraph_url_is_com?' do
|
||||
|
|
|
@ -672,6 +672,92 @@ eos
|
|||
it_behaves_like '#uri_type'
|
||||
end
|
||||
|
||||
describe '.diff_max_files' do
|
||||
subject(:diff_max_files) { described_class.diff_max_files }
|
||||
|
||||
let(:increased_diff_limits) { false }
|
||||
let(:configurable_diff_limits) { false }
|
||||
|
||||
before do
|
||||
stub_feature_flags(increased_diff_limits: increased_diff_limits, configurable_diff_limits: configurable_diff_limits)
|
||||
end
|
||||
|
||||
context 'when increased_diff_limits is enabled' do
|
||||
let(:increased_diff_limits) { true }
|
||||
|
||||
it 'returns 3000' do
|
||||
expect(diff_max_files).to eq(3000)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when configurable_diff_limits is enabled' do
|
||||
let(:configurable_diff_limits) { true }
|
||||
|
||||
it 'returns the current settings' do
|
||||
Gitlab::CurrentSettings.update!(diff_max_files: 1234)
|
||||
expect(diff_max_files).to eq(1234)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when neither feature flag is enabled' do
|
||||
it 'returns 1000' do
|
||||
expect(diff_max_files).to eq(1000)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.diff_max_lines' do
|
||||
subject(:diff_max_lines) { described_class.diff_max_lines }
|
||||
|
||||
let(:increased_diff_limits) { false }
|
||||
let(:configurable_diff_limits) { false }
|
||||
|
||||
before do
|
||||
stub_feature_flags(increased_diff_limits: increased_diff_limits, configurable_diff_limits: configurable_diff_limits)
|
||||
end
|
||||
|
||||
context 'when increased_diff_limits is enabled' do
|
||||
let(:increased_diff_limits) { true }
|
||||
|
||||
it 'returns 100000' do
|
||||
expect(diff_max_lines).to eq(100000)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when configurable_diff_limits is enabled' do
|
||||
let(:configurable_diff_limits) { true }
|
||||
|
||||
it 'returns the current settings' do
|
||||
Gitlab::CurrentSettings.update!(diff_max_lines: 65321)
|
||||
expect(diff_max_lines).to eq(65321)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when neither feature flag is enabled' do
|
||||
it 'returns 50000' do
|
||||
expect(diff_max_lines).to eq(50000)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.diff_safe_max_files' do
|
||||
subject(:diff_safe_max_files) { described_class.diff_safe_max_files }
|
||||
|
||||
it 'returns the commit diff max divided by the limit factor of 10' do
|
||||
expect(::Commit).to receive(:diff_max_files).and_return(10)
|
||||
expect(diff_safe_max_files).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.diff_safe_max_lines' do
|
||||
subject(:diff_safe_max_lines) { described_class.diff_safe_max_lines }
|
||||
|
||||
it 'returns the commit diff max divided by the limit factor of 10' do
|
||||
expect(::Commit).to receive(:diff_max_lines).and_return(100)
|
||||
expect(diff_safe_max_lines).to eq(10)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.from_hash' do
|
||||
subject { described_class.from_hash(commit.to_hash, container) }
|
||||
|
||||
|
|
|
@ -2613,4 +2613,16 @@ RSpec.describe Group do
|
|||
expect(group.activity_path).to eq(expected_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with export' do
|
||||
let(:group) { create(:group, :with_export) }
|
||||
|
||||
it '#export_file_exists returns true' do
|
||||
expect(group.export_file_exists?).to be true
|
||||
end
|
||||
|
||||
it '#export_archive_exists? returns true' do
|
||||
expect(group.export_archive_exists?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ImportExportUpload do
|
||||
subject { described_class.new(project: create(:project)) }
|
||||
let(:project) { create(:project) }
|
||||
|
||||
subject { described_class.new(project: project) }
|
||||
|
||||
shared_examples 'stores the Import/Export file' do |method|
|
||||
it 'stores the import file' do
|
||||
|
@ -43,4 +45,80 @@ RSpec.describe ImportExportUpload do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ActiveRecord callbacks' do
|
||||
let(:after_save_callbacks) { described_class._save_callbacks.select { |cb| cb.kind == :after } }
|
||||
let(:after_commit_callbacks) { described_class._commit_callbacks.select { |cb| cb.kind == :after } }
|
||||
|
||||
def find_callback(callbacks, key)
|
||||
callbacks.find { |cb| cb.instance_variable_get(:@key) == key }
|
||||
end
|
||||
|
||||
it 'export file is stored in after_commit callback' do
|
||||
expect(find_callback(after_commit_callbacks, :store_export_file!)).to be_present
|
||||
expect(find_callback(after_save_callbacks, :store_export_file!)).to be_nil
|
||||
end
|
||||
|
||||
it 'import file is stored in after_save callback' do
|
||||
expect(find_callback(after_save_callbacks, :store_import_file!)).to be_present
|
||||
expect(find_callback(after_commit_callbacks, :store_import_file!)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe 'export file' do
|
||||
it '#export_file_exists? returns false' do
|
||||
expect(subject.export_file_exists?).to be false
|
||||
end
|
||||
|
||||
it '#export_archive_exists? returns false' do
|
||||
expect(subject.export_archive_exists?).to be false
|
||||
end
|
||||
|
||||
context 'with export' do
|
||||
let(:project_with_export) { create(:project, :with_export) }
|
||||
|
||||
subject { described_class.with_export_file.find_by(project: project_with_export) }
|
||||
|
||||
it '#export_file_exists? returns true' do
|
||||
expect(subject.export_file_exists?).to be true
|
||||
end
|
||||
|
||||
it '#export_archive_exists? returns false' do
|
||||
expect(subject.export_archive_exists?).to be true
|
||||
end
|
||||
|
||||
context 'when object file does not exist' do
|
||||
before do
|
||||
subject.export_file.file.delete
|
||||
end
|
||||
|
||||
it '#export_file_exists? returns true' do
|
||||
expect(subject.export_file_exists?).to be true
|
||||
end
|
||||
|
||||
it '#export_archive_exists? returns false' do
|
||||
expect(subject.export_archive_exists?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when checking object existence raises a error' do
|
||||
let(:exception) { Excon::Error::Forbidden.new('not allowed') }
|
||||
|
||||
before do
|
||||
file = double
|
||||
allow(file).to receive(:exists?).and_raise(exception)
|
||||
allow(subject).to receive(:carrierwave_export_file).and_return(file)
|
||||
end
|
||||
|
||||
it '#export_file_exists? returns true' do
|
||||
expect(subject.export_file_exists?).to be true
|
||||
end
|
||||
|
||||
it '#export_archive_exists? returns false' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(exception)
|
||||
expect(subject.export_archive_exists?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4376,6 +4376,18 @@ RSpec.describe Project, factory_default: :keep do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with export' do
|
||||
let(:project) { create(:project, :with_export) }
|
||||
|
||||
it '#export_file_exists? returns true' do
|
||||
expect(project.export_file_exists?).to be true
|
||||
end
|
||||
|
||||
it '#export_archive_exists? returns false' do
|
||||
expect(project.export_archive_exists?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#forks_count' do
|
||||
it 'returns the number of forks' do
|
||||
project = build(:project)
|
||||
|
@ -6638,7 +6650,7 @@ RSpec.describe Project, factory_default: :keep do
|
|||
context 'when project export is completed' do
|
||||
before do
|
||||
finish_job(project_export_job)
|
||||
allow(project).to receive(:export_file).and_return(double(ImportExportUploader, file: 'exists.zip'))
|
||||
allow(project).to receive(:export_file_exists?).and_return(true)
|
||||
end
|
||||
|
||||
it { expect(project.export_status).to eq :finished }
|
||||
|
@ -6649,7 +6661,7 @@ RSpec.describe Project, factory_default: :keep do
|
|||
|
||||
before do
|
||||
finish_job(project_export_job)
|
||||
allow(project).to receive(:export_file).and_return(double(ImportExportUploader, file: 'exists.zip'))
|
||||
allow(project).to receive(:export_file_exists?).and_return(true)
|
||||
end
|
||||
|
||||
it { expect(project.export_status).to eq :regeneration_in_progress }
|
||||
|
|
|
@ -64,6 +64,23 @@ RSpec.describe API::GroupExport do
|
|||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when object is not present' do
|
||||
let(:other_group) { create(:group, :with_export) }
|
||||
let(:other_download_path) { "/groups/#{other_group.id}/export/download" }
|
||||
|
||||
before do
|
||||
other_group.add_owner(user)
|
||||
other_group.export_file.file.delete
|
||||
end
|
||||
|
||||
it 'returns 404' do
|
||||
get api(other_download_path, user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(json_response['message']).to eq('The group export file is not available yet')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when export file does not exist' do
|
||||
|
|
|
@ -1353,7 +1353,7 @@ RSpec.describe API::MergeRequests do
|
|||
|
||||
context 'when a merge request has more than the changes limit' do
|
||||
it "returns a string indicating that more changes were made" do
|
||||
allow(Commit).to receive(:diff_hard_limit_files).and_return(5)
|
||||
allow(Commit).to receive(:diff_max_files).and_return(5)
|
||||
|
||||
merge_request_overflow = create(:merge_request, :simple,
|
||||
author: user,
|
||||
|
|
|
@ -196,6 +196,19 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when export object is not present' do
|
||||
before do
|
||||
project_after_export.export_file.file.delete
|
||||
end
|
||||
|
||||
it 'returns 404' do
|
||||
get api(download_path_export_action, user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(json_response['message']).to eq('The project export file is not available yet')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when upload complete' do
|
||||
before do
|
||||
project_after_export.remove_exports
|
||||
|
|
|
@ -113,6 +113,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
|
|||
terms: 'Hello world!',
|
||||
performance_bar_allowed_group_path: group.full_path,
|
||||
diff_max_patch_bytes: 300_000,
|
||||
diff_max_files: 2000,
|
||||
diff_max_lines: 50000,
|
||||
default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE,
|
||||
local_markdown_version: 3,
|
||||
allow_local_requests_from_web_hooks_and_services: true,
|
||||
|
@ -159,6 +161,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
|
|||
expect(json_response['terms']).to eq('Hello world!')
|
||||
expect(json_response['performance_bar_allowed_group_id']).to eq(group.id)
|
||||
expect(json_response['diff_max_patch_bytes']).to eq(300_000)
|
||||
expect(json_response['diff_max_files']).to eq(2000)
|
||||
expect(json_response['diff_max_lines']).to eq(50000)
|
||||
expect(json_response['default_branch_protection']).to eq(Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
|
||||
expect(json_response['local_markdown_version']).to eq(3)
|
||||
expect(json_response['allow_local_requests_from_web_hooks_and_services']).to eq(true)
|
||||
|
|
Loading…
Reference in New Issue