Merge remote-tracking branch 'origin/master' into 39549-label-list-page-redesign-with-draggable-labels
This commit is contained in:
commit
0af7333ef9
175 changed files with 1837 additions and 677 deletions
|
@ -349,7 +349,7 @@ on those issues. Please select someone with relevant experience from the
|
|||
[GitLab team][team]. If there is nobody mentioned with that expertise look in
|
||||
the commit history for the affected files to find someone.
|
||||
|
||||
[described in our handbook]: https://about.gitlab.com/handbook/engineering/issues/issue-triage-policies/
|
||||
[described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
|
||||
[issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
|
||||
|
||||
### Feature proposals
|
||||
|
|
|
@ -168,6 +168,7 @@ the stable branch are:
|
|||
|
||||
* Fixes for [regressions](#regressions)
|
||||
* Fixes for security issues
|
||||
* Fixes or improvements to automated QA scenarios
|
||||
* New or updated translations (as long as they do not touch application code)
|
||||
|
||||
During the feature freeze all merge requests that are meant to go into the
|
||||
|
|
|
@ -58,7 +58,7 @@ class ImporterStatus {
|
|||
job.find('.import-target').html(`<a href="${data.full_path}">${data.full_path}</a>`);
|
||||
$('table.import-jobs tbody').prepend(job);
|
||||
|
||||
job.addClass('active');
|
||||
job.addClass('table-active');
|
||||
const connectingVerb = this.ciCdOnly ? __('connecting') : __('importing');
|
||||
job.find('.import-actions').html(sprintf(
|
||||
_.escape(__('%{loadingIcon} Started')), {
|
||||
|
@ -81,7 +81,7 @@ class ImporterStatus {
|
|||
|
||||
switch (job.import_status) {
|
||||
case 'finished':
|
||||
jobItem.removeClass('active').addClass('success');
|
||||
jobItem.removeClass('table-active').addClass('table-success');
|
||||
statusField.html(`<span><i class="fa fa-check"></i> ${__('Done')}</span>`);
|
||||
break;
|
||||
case 'scheduled':
|
||||
|
|
|
@ -56,6 +56,7 @@ export default {
|
|||
<gl-modal
|
||||
:id="`modal-peek-${metric}-details`"
|
||||
:header-title-text="header"
|
||||
modal-size="lg"
|
||||
class="performance-bar-modal"
|
||||
>
|
||||
<table
|
||||
|
@ -70,7 +71,7 @@ export default {
|
|||
<td
|
||||
v-for="key in keys"
|
||||
:key="key"
|
||||
class="break-word"
|
||||
class="break-word all-words"
|
||||
>
|
||||
{{ item[key] }}
|
||||
</td>
|
||||
|
|
|
@ -265,10 +265,10 @@ export default {
|
|||
/>
|
||||
|
||||
<section
|
||||
v-if="mr.maintainerEditAllowed"
|
||||
v-if="mr.allowCollaboration"
|
||||
class="mr-info-list mr-links"
|
||||
>
|
||||
{{ s__("mrWidget|Allows edits from maintainers") }}
|
||||
{{ s__("mrWidget|Allows commits from members who can merge to the target branch") }}
|
||||
</section>
|
||||
|
||||
<mr-widget-related-links
|
||||
|
|
|
@ -83,7 +83,7 @@ export default class MergeRequestStore {
|
|||
this.canBeMerged = data.can_be_merged || false;
|
||||
this.isMergeAllowed = data.mergeable || false;
|
||||
this.mergeOngoing = data.merge_ongoing;
|
||||
this.maintainerEditAllowed = data.allow_maintainer_to_push;
|
||||
this.allowCollaboration = data.allow_collaboration;
|
||||
|
||||
// Cherry-pick and Revert actions related
|
||||
this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
<script>
|
||||
const buttonVariants = ['danger', 'primary', 'success', 'warning'];
|
||||
const sizeVariants = ['sm', 'md', 'lg'];
|
||||
|
||||
export default {
|
||||
name: 'GlModal',
|
||||
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
modalSize: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'md',
|
||||
validator: value => sizeVariants.includes(value),
|
||||
},
|
||||
headerTitleText: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -27,7 +33,11 @@ export default {
|
|||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
modalSizeClass() {
|
||||
return this.modalSize === 'md' ? '' : `modal-${this.modalSize}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
emitCancel(event) {
|
||||
this.$emit('cancel', event);
|
||||
|
@ -48,6 +58,7 @@ export default {
|
|||
>
|
||||
<div
|
||||
class="modal-dialog"
|
||||
:class="modalSizeClass"
|
||||
role="document"
|
||||
>
|
||||
<div class="modal-content">
|
||||
|
|
|
@ -124,15 +124,18 @@
|
|||
break;
|
||||
}
|
||||
},
|
||||
hideOnSmallScreen(item) {
|
||||
return !item.first && !item.last && !item.next && !item.prev && !item.active;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
v-if="showPagination"
|
||||
class="gl-pagination"
|
||||
class="gl-pagination prepend-top-default"
|
||||
>
|
||||
<ul class="pagination clearfix">
|
||||
<ul class="pagination justify-content-center">
|
||||
<li
|
||||
v-for="(item, index) in getItems"
|
||||
:key="index"
|
||||
|
@ -142,12 +145,17 @@
|
|||
'js-next-button': item.next,
|
||||
'js-last-button': item.last,
|
||||
'js-first-button': item.first,
|
||||
'd-none d-md-block': hideOnSmallScreen(item),
|
||||
separator: item.separator,
|
||||
active: item.active,
|
||||
disabled: item.disabled
|
||||
disabled: item.disabled || item.separator
|
||||
}"
|
||||
class="page-item"
|
||||
>
|
||||
<a @click.prevent="changePage(item.title, item.disabled)">
|
||||
<a
|
||||
@click.prevent="changePage(item.title, item.disabled)"
|
||||
class="page-link"
|
||||
>
|
||||
{{ item.title }}
|
||||
</a>
|
||||
</li>
|
||||
|
|
65
app/assets/stylesheets/bootstrap_migration.scss
vendored
65
app/assets/stylesheets/bootstrap_migration.scss
vendored
|
@ -24,16 +24,54 @@ html {
|
|||
font-size: 14px;
|
||||
}
|
||||
|
||||
legend {
|
||||
border-bottom: 1px solid $border-color;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
button,
|
||||
html [type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
[type="submit"],
|
||||
[role="button"] {
|
||||
// Override bootstrap reboot
|
||||
-webkit-appearance: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: $gl-text-color;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
h1,
|
||||
.h1,
|
||||
h2,
|
||||
.h2,
|
||||
h3,
|
||||
.h3 {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h4,
|
||||
.h4,
|
||||
h5,
|
||||
.h5,
|
||||
h6,
|
||||
.h6 {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h5,
|
||||
.h5 {
|
||||
font-size: $gl-font-size;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
|
@ -59,6 +97,10 @@ a {
|
|||
}
|
||||
}
|
||||
|
||||
kbd {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 2px 4px;
|
||||
color: $red-600;
|
||||
|
@ -108,6 +150,16 @@ table {
|
|||
color: $gl-text-color-secondary !important;
|
||||
}
|
||||
|
||||
.bg-success,
|
||||
.bg-primary,
|
||||
.bg-info,
|
||||
.bg-danger,
|
||||
.bg-warning {
|
||||
.card-header {
|
||||
color: $white-light;
|
||||
}
|
||||
}
|
||||
|
||||
// Polyfill deprecated selectors
|
||||
|
||||
.hidden {
|
||||
|
@ -182,8 +234,13 @@ table {
|
|||
}
|
||||
|
||||
.nav-tabs {
|
||||
// Override bootstrap's default border
|
||||
border-bottom: 0;
|
||||
|
||||
.nav-link {
|
||||
border: 0;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
|
|
|
@ -448,6 +448,10 @@ img.emoji {
|
|||
|
||||
.break-word {
|
||||
word-wrap: break-word;
|
||||
|
||||
&.all-words {
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
/** COMMON CLASSES **/
|
||||
|
|
|
@ -1,91 +1,6 @@
|
|||
.gl-pagination {
|
||||
text-align: center;
|
||||
border-top: 1px solid $border-color;
|
||||
margin: 0;
|
||||
margin-top: 0;
|
||||
|
||||
.pagination {
|
||||
padding: 0;
|
||||
margin: 20px 0;
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.separator,
|
||||
.separator:hover {
|
||||
a {
|
||||
cursor: default;
|
||||
background-color: $gray-light;
|
||||
padding: $gl-vert-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.gap,
|
||||
.gap:hover {
|
||||
background-color: $gray-light;
|
||||
padding: $gl-vert-padding;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.card > .gl-pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra-small screen pagination.
|
||||
*/
|
||||
@media (max-width: 320px) {
|
||||
.gl-pagination {
|
||||
.first,
|
||||
.last {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page-item {
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Small screen pagination
|
||||
*/
|
||||
@include media-breakpoint-down(xs) {
|
||||
.gl-pagination {
|
||||
.pagination li a {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
.page-item {
|
||||
display: none;
|
||||
|
||||
&.active {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Medium screen pagination
|
||||
*/
|
||||
@media (min-width: map-get($grid-breakpoints, xs)) and (max-width: map-get($grid-breakpoints, sm)) {
|
||||
.gl-pagination {
|
||||
.page-item {
|
||||
display: none;
|
||||
|
||||
&.active,
|
||||
&.sibling {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -485,6 +485,15 @@
|
|||
.sidebar-collapsed-user {
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.author_link {
|
||||
padding-left: 0;
|
||||
|
||||
.avatar {
|
||||
position: static;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-header-btn {
|
||||
|
|
|
@ -102,10 +102,6 @@
|
|||
.form-text.text-muted {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.label-light {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-list-icon {
|
||||
|
|
|
@ -130,12 +130,17 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def access_denied!(message = nil)
|
||||
# If we display a custom access denied message to the user, we don't want to
|
||||
# hide existence of the resource, rather tell them they cannot access it using
|
||||
# the provided message
|
||||
status = message.present? ? :forbidden : :not_found
|
||||
|
||||
respond_to do |format|
|
||||
format.any { head :not_found }
|
||||
format.any { head status }
|
||||
format.html do
|
||||
render "errors/access_denied",
|
||||
layout: "errors",
|
||||
status: 404,
|
||||
status: status,
|
||||
locals: { message: message }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,8 +3,12 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
include MembersPresentation
|
||||
include SortingHelper
|
||||
|
||||
def self.admin_not_required_endpoints
|
||||
%i[index leave request_access]
|
||||
end
|
||||
|
||||
# Authorize
|
||||
before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access]
|
||||
before_action :authorize_admin_group_member!, except: admin_not_required_endpoints
|
||||
|
||||
skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
|
||||
:approve_access_request, :leave, :resend_invite,
|
||||
|
|
|
@ -76,12 +76,15 @@ class Groups::MilestonesController < Groups::ApplicationController
|
|||
|
||||
def milestones
|
||||
milestones = MilestonesFinder.new(search_params).execute
|
||||
legacy_milestones = GroupMilestone.build_collection(group, group_projects, params)
|
||||
|
||||
@sort = params[:sort] || 'due_date_asc'
|
||||
MilestoneArray.sort(milestones + legacy_milestones, @sort)
|
||||
end
|
||||
|
||||
def legacy_milestones
|
||||
GroupMilestone.build_collection(group, group_projects, params)
|
||||
end
|
||||
|
||||
def milestone
|
||||
@milestone =
|
||||
if params[:title]
|
||||
|
|
|
@ -15,7 +15,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
|
|||
|
||||
def merge_request_params_attributes
|
||||
[
|
||||
:allow_maintainer_to_push,
|
||||
:allow_collaboration,
|
||||
:assignee_id,
|
||||
:description,
|
||||
:force_remove_source_branch,
|
||||
|
|
|
@ -23,8 +23,6 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
@finished_count = limited_pipelines_count(project, 'finished')
|
||||
@pipelines_count = limited_pipelines_count(project)
|
||||
|
||||
Gitlab::Ci::Pipeline::Preloader.preload(@pipelines)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
|
@ -34,7 +32,7 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
pipelines: PipelineSerializer
|
||||
.new(project: @project, current_user: @current_user)
|
||||
.with_pagination(request, response)
|
||||
.represent(@pipelines, disable_coverage: true),
|
||||
.represent(@pipelines, disable_coverage: true, preload: true),
|
||||
count: {
|
||||
all: @pipelines_count,
|
||||
running: @running_count,
|
||||
|
|
|
@ -13,6 +13,10 @@ module Users
|
|||
|
||||
def index
|
||||
@redirect = redirect_path
|
||||
|
||||
if @term.accepted_by_user?(current_user)
|
||||
flash.now[:notice] = "You have already accepted the Terms of Service as #{current_user.to_reference}"
|
||||
end
|
||||
end
|
||||
|
||||
def accept
|
||||
|
|
|
@ -126,8 +126,8 @@ module MergeRequestsHelper
|
|||
link_to(url[merge_request.project, merge_request], data: data_attrs, &block)
|
||||
end
|
||||
|
||||
def allow_maintainer_push_unavailable_reason(merge_request)
|
||||
return if merge_request.can_allow_maintainer_to_push?(current_user)
|
||||
def allow_collaboration_unavailable_reason(merge_request)
|
||||
return if merge_request.can_allow_collaboration?(current_user)
|
||||
|
||||
minimum_visibility = [merge_request.target_project.visibility_level,
|
||||
merge_request.source_project.visibility_level].min
|
||||
|
|
|
@ -389,11 +389,11 @@ module ProjectsHelper
|
|||
def project_status_css_class(status)
|
||||
case status
|
||||
when "started"
|
||||
"active"
|
||||
"table-active"
|
||||
when "failed"
|
||||
"danger"
|
||||
"table-danger"
|
||||
when "finished"
|
||||
"success"
|
||||
"table-success"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -412,7 +412,10 @@ module ProjectsHelper
|
|||
exports_path = File.join(Settings.shared['path'], 'tmp/project_exports')
|
||||
filtered_message = message.strip.gsub(exports_path, "[REPO EXPORT PATH]")
|
||||
|
||||
disk_path = Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path
|
||||
disk_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
|
||||
Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path
|
||||
end
|
||||
|
||||
filtered_message.gsub(disk_path.chomp('/'), "[REPOS PATH]")
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ module WorkhorseHelper
|
|||
headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob))
|
||||
headers['Content-Disposition'] = 'inline'
|
||||
headers['Content-Type'] = safe_content_type(blob)
|
||||
head :ok # 'render nothing: true' messes up the Content-Type
|
||||
render plain: ""
|
||||
end
|
||||
|
||||
# Send a Git diff through Workhorse
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
class ApplicationSetting
|
||||
class Term < ActiveRecord::Base
|
||||
include CacheMarkdownField
|
||||
has_many :term_agreements
|
||||
|
||||
validates :terms, presence: true
|
||||
|
||||
|
@ -9,5 +10,10 @@ class ApplicationSetting
|
|||
def self.latest
|
||||
order(:id).last
|
||||
end
|
||||
|
||||
def accepted_by_user?(user)
|
||||
user.accepted_term_id == id ||
|
||||
term_agreements.accepted.where(user: user).exists?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,6 +31,14 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def self.fabricate(stage)
|
||||
stage.statuses.ordered.latest
|
||||
.sort_by(&:sortable_name).group_by(&:group_name)
|
||||
.map do |group_name, grouped_statuses|
|
||||
self.new(stage, name: group_name, jobs: grouped_statuses)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def commit_statuses
|
||||
|
|
|
@ -16,11 +16,7 @@ module Ci
|
|||
end
|
||||
|
||||
def groups
|
||||
@groups ||= statuses.ordered.latest
|
||||
.sort_by(&:sortable_name).group_by(&:group_name)
|
||||
.map do |group_name, grouped_statuses|
|
||||
Ci::Group.new(self, name: group_name, jobs: grouped_statuses)
|
||||
end
|
||||
@groups ||= Ci::Group.fabricate(self)
|
||||
end
|
||||
|
||||
def to_param
|
||||
|
|
|
@ -18,7 +18,7 @@ module Ci
|
|||
s&.project&.pipelines&.maximum(:iid) || s&.project&.pipelines&.count
|
||||
end
|
||||
|
||||
has_many :stages
|
||||
has_many :stages, -> { order(position: :asc) }, inverse_of: :pipeline
|
||||
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
|
||||
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
|
||||
|
@ -254,6 +254,20 @@ module Ci
|
|||
stage unless stage.statuses_count.zero?
|
||||
end
|
||||
|
||||
##
|
||||
# TODO We do not completely switch to persisted stages because of
|
||||
# race conditions with setting statuses gitlab-ce#23257.
|
||||
#
|
||||
def ordered_stages
|
||||
return legacy_stages unless complete?
|
||||
|
||||
if Feature.enabled?('ci_pipeline_persisted_stages')
|
||||
stages
|
||||
else
|
||||
legacy_stages
|
||||
end
|
||||
end
|
||||
|
||||
def legacy_stages
|
||||
# TODO, this needs refactoring, see gitlab-ce#26481.
|
||||
|
||||
|
@ -416,7 +430,7 @@ module Ci
|
|||
|
||||
def number_of_warnings
|
||||
BatchLoader.for(id).batch(default_value: 0) do |pipeline_ids, loader|
|
||||
Build.where(commit_id: pipeline_ids)
|
||||
::Ci::Build.where(commit_id: pipeline_ids)
|
||||
.latest
|
||||
.failed_but_allowed
|
||||
.group(:commit_id)
|
||||
|
@ -508,7 +522,8 @@ module Ci
|
|||
|
||||
def update_status
|
||||
retry_optimistic_lock(self) do
|
||||
case latest_builds_status
|
||||
case latest_builds_status.to_s
|
||||
when 'created' then nil
|
||||
when 'pending' then enqueue
|
||||
when 'running' then run
|
||||
when 'success' then succeed
|
||||
|
@ -516,6 +531,9 @@ module Ci
|
|||
when 'canceled' then cancel
|
||||
when 'skipped' then skip
|
||||
when 'manual' then block
|
||||
else
|
||||
raise HasStatus::UnknownStatusError,
|
||||
"Unknown status `#{latest_builds_status}`"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -219,10 +219,8 @@ module Ci
|
|||
|
||||
cache_attributes(values)
|
||||
|
||||
if persist_cached_data?
|
||||
self.assign_attributes(values)
|
||||
self.save if self.changed?
|
||||
end
|
||||
# We save data without validation, it will always change due to `contacted_at`
|
||||
self.update_columns(values) if persist_cached_data?
|
||||
end
|
||||
|
||||
def pick_build!(build)
|
||||
|
|
|
@ -68,16 +68,44 @@ module Ci
|
|||
def update_status
|
||||
retry_optimistic_lock(self) do
|
||||
case statuses.latest.status
|
||||
when 'created' then nil
|
||||
when 'pending' then enqueue
|
||||
when 'running' then run
|
||||
when 'success' then succeed
|
||||
when 'failed' then drop
|
||||
when 'canceled' then cancel
|
||||
when 'manual' then block
|
||||
when 'skipped' then skip
|
||||
else skip
|
||||
when 'skipped', nil then skip
|
||||
else
|
||||
raise HasStatus::UnknownStatusError,
|
||||
"Unknown status `#{statuses.latest.status}`"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups ||= Ci::Group.fabricate(self)
|
||||
end
|
||||
|
||||
def has_warnings?
|
||||
number_of_warnings.positive?
|
||||
end
|
||||
|
||||
def number_of_warnings
|
||||
BatchLoader.for(id).batch(default_value: 0) do |stage_ids, loader|
|
||||
::Ci::Build.where(stage_id: stage_ids)
|
||||
.latest
|
||||
.failed_but_allowed
|
||||
.group(:stage_id)
|
||||
.count
|
||||
.each { |id, amount| loader.call(id, amount) }
|
||||
end
|
||||
end
|
||||
|
||||
def detailed_status(current_user)
|
||||
Gitlab::Ci::Status::Stage::Factory
|
||||
.new(self, current_user)
|
||||
.fabricate!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,11 +4,14 @@ module Avatarable
|
|||
included do
|
||||
prepend ShadowMethods
|
||||
include ObjectStorage::BackgroundMove
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
|
||||
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
|
||||
|
||||
mount_uploader :avatar, AvatarUploader
|
||||
|
||||
after_initialize :add_avatar_to_batch
|
||||
end
|
||||
|
||||
module ShadowMethods
|
||||
|
@ -18,6 +21,17 @@ module Avatarable
|
|||
|
||||
avatar_path(only_path: args.fetch(:only_path, true)) || super
|
||||
end
|
||||
|
||||
def retrieve_upload(identifier, paths)
|
||||
upload = retrieve_upload_from_batch(identifier)
|
||||
|
||||
# This fallback is needed when deleting an upload, because we may have
|
||||
# already been removed from the DB. We have to check an explicit `#nil?`
|
||||
# because it's a BatchLoader instance.
|
||||
upload = super if upload.nil?
|
||||
|
||||
upload
|
||||
end
|
||||
end
|
||||
|
||||
def avatar_type
|
||||
|
@ -52,4 +66,37 @@ module Avatarable
|
|||
|
||||
url_base + avatar.local_url
|
||||
end
|
||||
|
||||
# Path that is persisted in the tracking Upload model. Used to fetch the
|
||||
# upload from the model.
|
||||
def upload_paths(identifier)
|
||||
avatar_mounter.blank_uploader.store_dirs.map { |store, path| File.join(path, identifier) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def retrieve_upload_from_batch(identifier)
|
||||
BatchLoader.for(identifier: identifier, model: self).batch(key: self.class) do |upload_params, loader, args|
|
||||
model_class = args[:key]
|
||||
paths = upload_params.flat_map do |params|
|
||||
params[:model].upload_paths(params[:identifier])
|
||||
end
|
||||
|
||||
Upload.where(uploader: AvatarUploader, path: paths).find_each do |upload|
|
||||
model = model_class.instantiate('id' => upload.model_id)
|
||||
|
||||
loader.call({ model: model, identifier: File.basename(upload.path) }, upload)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_avatar_to_batch
|
||||
return unless avatar_mounter
|
||||
|
||||
avatar_mounter.read_identifiers.each { |identifier| retrieve_upload_from_batch(identifier) }
|
||||
end
|
||||
|
||||
def avatar_mounter
|
||||
strong_memoize(:avatar_mounter) { _mounter(:avatar) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,8 @@ module HasStatus
|
|||
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
|
||||
failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze
|
||||
|
||||
UnknownStatusError = Class.new(StandardError)
|
||||
|
||||
class_methods do
|
||||
def status_sql
|
||||
scope_relevant = respond_to?(:exclude_ignored) ? exclude_ignored : all
|
||||
|
|
|
@ -36,4 +36,8 @@ module WithUploads
|
|||
upload.destroy
|
||||
end
|
||||
end
|
||||
|
||||
def retrieve_upload(_identifier, paths)
|
||||
uploads.find_by(path: paths)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1125,21 +1125,21 @@ class MergeRequest < ActiveRecord::Base
|
|||
project.merge_requests.merged.where(author_id: author_id).empty?
|
||||
end
|
||||
|
||||
def allow_maintainer_to_push
|
||||
maintainer_push_possible? && super
|
||||
def allow_collaboration
|
||||
collaborative_push_possible? && super
|
||||
end
|
||||
|
||||
alias_method :allow_maintainer_to_push?, :allow_maintainer_to_push
|
||||
alias_method :allow_collaboration?, :allow_collaboration
|
||||
|
||||
def maintainer_push_possible?
|
||||
def collaborative_push_possible?
|
||||
source_project.present? && for_fork? &&
|
||||
target_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE &&
|
||||
source_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE &&
|
||||
!ProtectedBranch.protected?(source_project, source_branch)
|
||||
end
|
||||
|
||||
def can_allow_maintainer_to_push?(user)
|
||||
maintainer_push_possible? &&
|
||||
def can_allow_collaboration?(user)
|
||||
collaborative_push_possible? &&
|
||||
Ability.allowed?(user, :push_code, source_project)
|
||||
end
|
||||
|
||||
|
|
|
@ -435,6 +435,10 @@ class Note < ActiveRecord::Base
|
|||
super.merge(noteable: noteable)
|
||||
end
|
||||
|
||||
def retrieve_upload(_identifier, paths)
|
||||
Upload.find_by(model: self, path: paths)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def keep_around_commit
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
class PersonalSnippet < Snippet
|
||||
include WithUploads
|
||||
end
|
||||
|
|
|
@ -228,6 +228,7 @@ class Project < ActiveRecord::Base
|
|||
|
||||
has_many :commit_statuses
|
||||
has_many :pipelines, class_name: 'Ci::Pipeline', inverse_of: :project
|
||||
has_many :stages, class_name: 'Ci::Stage', inverse_of: :project
|
||||
|
||||
# Ci::Build objects store data on the file system such as artifact files and
|
||||
# build traces. Currently there's no efficient way of removing this data in
|
||||
|
@ -1425,8 +1426,14 @@ class Project < ActiveRecord::Base
|
|||
Ci::Runner.from("(#{union.to_sql}) ci_runners")
|
||||
end
|
||||
|
||||
def active_runners
|
||||
strong_memoize(:active_runners) do
|
||||
all_runners.active
|
||||
end
|
||||
end
|
||||
|
||||
def any_runners?(&block)
|
||||
all_runners.active.any?(&block)
|
||||
active_runners.any?(&block)
|
||||
end
|
||||
|
||||
def valid_runners_token?(token)
|
||||
|
@ -1968,18 +1975,18 @@ class Project < ActiveRecord::Base
|
|||
.limit(1)
|
||||
.select(1)
|
||||
source_of_merge_requests.opened
|
||||
.where(allow_maintainer_to_push: true)
|
||||
.where(allow_collaboration: true)
|
||||
.where('EXISTS (?)', developer_access_exists)
|
||||
end
|
||||
|
||||
def branch_allows_maintainer_push?(user, branch_name)
|
||||
def branch_allows_collaboration?(user, branch_name)
|
||||
return false unless user
|
||||
|
||||
cache_key = "user:#{user.id}:#{branch_name}:branch_allows_push"
|
||||
|
||||
memoized_results = strong_memoize(:branch_allows_maintainer_push) do
|
||||
memoized_results = strong_memoize(:branch_allows_collaboration) do
|
||||
Hash.new do |result, cache_key|
|
||||
result[cache_key] = fetch_branch_allows_maintainer_push?(user, branch_name)
|
||||
result[cache_key] = fetch_branch_allows_collaboration?(user, branch_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -2121,18 +2128,18 @@ class Project < ActiveRecord::Base
|
|||
raise ex
|
||||
end
|
||||
|
||||
def fetch_branch_allows_maintainer_push?(user, branch_name)
|
||||
def fetch_branch_allows_collaboration?(user, branch_name)
|
||||
check_access = -> do
|
||||
next false if empty_repo?
|
||||
|
||||
merge_request = source_of_merge_requests.opened
|
||||
.where(allow_maintainer_to_push: true)
|
||||
.where(allow_collaboration: true)
|
||||
.find_by(source_branch: branch_name)
|
||||
merge_request&.can_be_merged_by?(user)
|
||||
end
|
||||
|
||||
if RequestStore.active?
|
||||
RequestStore.fetch("project-#{id}:branch-#{branch_name}:user-#{user.id}:branch_allows_maintainer_push") do
|
||||
RequestStore.fetch("project-#{id}:branch-#{branch_name}:user-#{user.id}:branch_allows_collaboration") do
|
||||
check_access.call
|
||||
end
|
||||
else
|
||||
|
|
|
@ -270,6 +270,16 @@ class Repository
|
|||
end
|
||||
end
|
||||
|
||||
def archive_metadata(ref, storage_path, format = "tar.gz", append_sha:)
|
||||
raw_repository.archive_metadata(
|
||||
ref,
|
||||
storage_path,
|
||||
project.path,
|
||||
format,
|
||||
append_sha: append_sha
|
||||
)
|
||||
end
|
||||
|
||||
def expire_tags_cache
|
||||
expire_method_caches(%i(tag_names tag_count))
|
||||
@tags = nil
|
||||
|
|
|
@ -2,5 +2,7 @@ class TermAgreement < ActiveRecord::Base
|
|||
belongs_to :term, class_name: 'ApplicationSetting::Term'
|
||||
belongs_to :user
|
||||
|
||||
scope :accepted, -> { where(accepted: true) }
|
||||
|
||||
validates :user, :term, presence: true
|
||||
end
|
||||
|
|
|
@ -14,8 +14,8 @@ module Ci
|
|||
@subject.triggered_by?(@user)
|
||||
end
|
||||
|
||||
condition(:branch_allows_maintainer_push) do
|
||||
@subject.project.branch_allows_maintainer_push?(@user, @subject.ref)
|
||||
condition(:branch_allows_collaboration) do
|
||||
@subject.project.branch_allows_collaboration?(@user, @subject.ref)
|
||||
end
|
||||
|
||||
rule { protected_ref }.policy do
|
||||
|
@ -25,7 +25,7 @@ module Ci
|
|||
|
||||
rule { can?(:admin_build) | (can?(:update_build) & owner_of_job) }.enable :erase_build
|
||||
|
||||
rule { can?(:public_access) & branch_allows_maintainer_push }.policy do
|
||||
rule { can?(:public_access) & branch_allows_collaboration }.policy do
|
||||
enable :update_build
|
||||
enable :update_commit_status
|
||||
end
|
||||
|
|
|
@ -4,13 +4,13 @@ module Ci
|
|||
|
||||
condition(:protected_ref) { ref_protected?(@user, @subject.project, @subject.tag?, @subject.ref) }
|
||||
|
||||
condition(:branch_allows_maintainer_push) do
|
||||
@subject.project.branch_allows_maintainer_push?(@user, @subject.ref)
|
||||
condition(:branch_allows_collaboration) do
|
||||
@subject.project.branch_allows_collaboration?(@user, @subject.ref)
|
||||
end
|
||||
|
||||
rule { protected_ref }.prevent :update_pipeline
|
||||
|
||||
rule { can?(:public_access) & branch_allows_maintainer_push }.policy do
|
||||
rule { can?(:public_access) & branch_allows_collaboration }.policy do
|
||||
enable :update_pipeline
|
||||
end
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ class MergeRequestWidgetEntity < IssuableEntity
|
|||
expose :squash
|
||||
expose :target_branch
|
||||
expose :target_project_id
|
||||
expose :allow_maintainer_to_push
|
||||
expose :allow_collaboration
|
||||
|
||||
expose :should_be_rebased?, as: :should_be_rebased
|
||||
expose :ff_only_enabled do |merge_request|
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class PipelineDetailsEntity < PipelineEntity
|
||||
expose :details do
|
||||
expose :legacy_stages, as: :stages, using: StageEntity
|
||||
expose :ordered_stages, as: :stages, using: StageEntity
|
||||
expose :artifacts, using: BuildArtifactEntity
|
||||
expose :manual_actions, using: BuildActionEntity
|
||||
end
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
class PipelineSerializer < BaseSerializer
|
||||
include WithPagination
|
||||
|
||||
InvalidResourceError = Class.new(StandardError)
|
||||
|
||||
entity PipelineDetailsEntity
|
||||
|
||||
def represent(resource, opts = {})
|
||||
if resource.is_a?(ActiveRecord::Relation)
|
||||
|
||||
resource = resource.preload([
|
||||
:stages,
|
||||
:retryable_builds,
|
||||
:cancelable_statuses,
|
||||
:trigger_requests,
|
||||
|
@ -20,10 +17,14 @@ class PipelineSerializer < BaseSerializer
|
|||
end
|
||||
|
||||
if paginated?
|
||||
super(@paginator.paginate(resource), opts)
|
||||
else
|
||||
super(resource, opts)
|
||||
resource = paginator.paginate(resource)
|
||||
end
|
||||
|
||||
if opts.delete(:preload)
|
||||
resource = Gitlab::Ci::Pipeline::Preloader.preload!(resource)
|
||||
end
|
||||
|
||||
super(resource, opts)
|
||||
end
|
||||
|
||||
def represent_status(resource)
|
||||
|
@ -36,7 +37,7 @@ class PipelineSerializer < BaseSerializer
|
|||
def represent_stages(resource)
|
||||
return {} unless resource.present?
|
||||
|
||||
data = represent(resource, { only: [{ details: [:stages] }] })
|
||||
data = represent(resource, { only: [{ details: [:stages] }], preload: true })
|
||||
data.dig(:details, :stages) || []
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module ApplicationSettings
|
||||
class UpdateService < ApplicationSettings::BaseService
|
||||
attr_reader :params, :application_setting
|
||||
|
||||
def execute
|
||||
update_terms(@params.delete(:terms))
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@ module Applications
|
|||
class CreateService
|
||||
def initialize(current_user, params)
|
||||
@current_user = current_user
|
||||
@params = params
|
||||
@ip_address = @params.delete(:ip_address)
|
||||
@params = params.except(:ip_address)
|
||||
end
|
||||
|
||||
def execute(request = nil)
|
||||
|
|
|
@ -38,8 +38,8 @@ module MergeRequests
|
|||
def filter_params(merge_request)
|
||||
super
|
||||
|
||||
unless merge_request.can_allow_maintainer_to_push?(current_user)
|
||||
params.delete(:allow_maintainer_to_push)
|
||||
unless merge_request.can_allow_collaboration?(current_user)
|
||||
params.delete(:allow_collaboration)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
module TestHooks
|
||||
class ProjectService < TestHooks::BaseService
|
||||
private
|
||||
attr_writer :project
|
||||
|
||||
def project
|
||||
@project ||= hook.project
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def push_events_data
|
||||
throw(:validation_error, 'Ensure the project has at least one commit.') if project.empty_repo?
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ module ObjectStorage
|
|||
|
||||
unless current_upload_satisfies?(paths, model)
|
||||
# the upload we already have isn't right, find the correct one
|
||||
self.upload = uploads.find_by(model: model, path: paths)
|
||||
self.upload = model&.retrieve_upload(identifier, paths)
|
||||
end
|
||||
|
||||
super
|
||||
|
@ -46,7 +46,7 @@ module ObjectStorage
|
|||
end
|
||||
|
||||
def upload=(upload)
|
||||
return unless upload
|
||||
return if upload.nil?
|
||||
|
||||
self.object_store = upload.store
|
||||
super
|
||||
|
|
|
@ -116,8 +116,8 @@
|
|||
.card-body
|
||||
= form_for @project, url: transfer_admin_project_path(@project), method: :put do |f|
|
||||
.form-group.row
|
||||
= f.label :new_namespace_id, "Namespace", class: 'col-form-label col-sm-2'
|
||||
.col-sm-10
|
||||
= f.label :new_namespace_id, "Namespace", class: 'col-form-label col-sm-3'
|
||||
.col-sm-9
|
||||
.dropdown
|
||||
= dropdown_toggle('Search for Namespace', { toggle: 'dropdown', field_name: 'new_namespace_id' }, { toggle_class: 'js-namespace-select large' })
|
||||
.dropdown-menu.dropdown-select
|
||||
|
@ -127,7 +127,7 @@
|
|||
= dropdown_loading
|
||||
|
||||
.form-group.row
|
||||
.offset-sm-2.col-sm-10
|
||||
.offset-sm-3.col-sm-9
|
||||
= f.submit 'Transfer', class: 'btn btn-primary'
|
||||
|
||||
.card.repository-check
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
%th Projects
|
||||
%th Jobs
|
||||
%th Tags
|
||||
%th= link_to 'Last contact', admin_runners_path(params.slice(:search).merge(sort: 'contacted_asc'))
|
||||
%th= link_to 'Last contact', admin_runners_path(safe_params.slice(:search).merge(sort: 'contacted_asc'))
|
||||
%th
|
||||
|
||||
- @runners.each do |runner|
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.nav-block
|
||||
.nav-block.activities
|
||||
.controls
|
||||
= link_to group_path(@group, rss_url_options), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do
|
||||
%i.fa.fa-rss
|
||||
|
|
|
@ -18,71 +18,71 @@
|
|||
%th Global Shortcuts
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key s
|
||||
%kbd s
|
||||
%td Focus Search
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key f
|
||||
%kbd f
|
||||
%td Focus Filter
|
||||
- if performance_bar_enabled?
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key p b
|
||||
%kbd p b
|
||||
%td Show/hide the Performance Bar
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key ?
|
||||
%kbd ?
|
||||
%td Show/hide this dialog
|
||||
%tr
|
||||
%td.shortcut
|
||||
- if browser.platform.mac?
|
||||
.key ⌘ shift p
|
||||
%kbd ⌘ shift p
|
||||
- else
|
||||
.key ctrl shift p
|
||||
%kbd ctrl shift p
|
||||
%td Toggle Markdown preview
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
%i.fa.fa-arrow-up
|
||||
%td Edit last comment (when focused on an empty textarea)
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift t
|
||||
%kbd shift t
|
||||
%td
|
||||
Go to todos
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift a
|
||||
%kbd shift a
|
||||
%td
|
||||
Go to the activity feed
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift p
|
||||
%kbd shift p
|
||||
%td
|
||||
Go to projects
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift i
|
||||
%kbd shift i
|
||||
%td
|
||||
Go to issues
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift m
|
||||
%kbd shift m
|
||||
%td
|
||||
Go to merge requests
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift g
|
||||
%kbd shift g
|
||||
%td
|
||||
Go to groups
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift l
|
||||
%kbd shift l
|
||||
%td
|
||||
Go to milestones
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key shift s
|
||||
%kbd shift s
|
||||
%td
|
||||
Go to snippets
|
||||
%tbody
|
||||
|
@ -91,21 +91,21 @@
|
|||
%th Finding Project File
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
%i.fa.fa-arrow-up
|
||||
%td Move selection up
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
%i.fa.fa-arrow-down
|
||||
%td Move selection down
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key enter
|
||||
%kbd enter
|
||||
%td Open Selection
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key esc
|
||||
%kbd esc
|
||||
%td Go back
|
||||
.col-lg-4
|
||||
%table.shortcut-mappings
|
||||
|
@ -115,95 +115,95 @@
|
|||
%th Project
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key p
|
||||
%kbd g
|
||||
%kbd p
|
||||
%td
|
||||
Go to the project's overview page
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key v
|
||||
%kbd g
|
||||
%kbd v
|
||||
%td
|
||||
Go to the project's activity feed
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key f
|
||||
%kbd g
|
||||
%kbd f
|
||||
%td
|
||||
Go to files
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key c
|
||||
%kbd g
|
||||
%kbd c
|
||||
%td
|
||||
Go to commits
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key j
|
||||
%kbd g
|
||||
%kbd j
|
||||
%td
|
||||
Go to jobs
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key n
|
||||
%kbd g
|
||||
%kbd n
|
||||
%td
|
||||
Go to network graph
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key d
|
||||
%kbd g
|
||||
%kbd d
|
||||
%td
|
||||
Go to repository charts
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key i
|
||||
%kbd g
|
||||
%kbd i
|
||||
%td
|
||||
Go to issues
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key b
|
||||
%kbd g
|
||||
%kbd b
|
||||
%td
|
||||
Go to issue boards
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key m
|
||||
%kbd g
|
||||
%kbd m
|
||||
%td
|
||||
Go to merge requests
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key e
|
||||
%kbd g
|
||||
%kbd e
|
||||
%td
|
||||
Go to environments
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key k
|
||||
%kbd g
|
||||
%kbd k
|
||||
%td
|
||||
Go to kubernetes
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key s
|
||||
%kbd g
|
||||
%kbd s
|
||||
%td
|
||||
Go to snippets
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key g
|
||||
.key w
|
||||
%kbd g
|
||||
%kbd w
|
||||
%td
|
||||
Go to wiki
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key t
|
||||
%kbd t
|
||||
%td Go to finding file
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key i
|
||||
%kbd i
|
||||
%td New issue
|
||||
|
||||
%tbody
|
||||
|
@ -212,17 +212,17 @@
|
|||
%th Project Files browsing
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
%i.fa.fa-arrow-up
|
||||
%td Move selection up
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
%i.fa.fa-arrow-down
|
||||
%td Move selection down
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key enter
|
||||
%kbd enter
|
||||
%td Open Selection
|
||||
%tbody
|
||||
%tr
|
||||
|
@ -230,7 +230,7 @@
|
|||
%th Project File
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key y
|
||||
%kbd y
|
||||
%td Go to file permalink
|
||||
%tbody
|
||||
%tr
|
||||
|
@ -239,115 +239,115 @@
|
|||
%tr
|
||||
%td.shortcut
|
||||
- if browser.platform.mac?
|
||||
.key ⌘ p
|
||||
%kbd ⌘ p
|
||||
- else
|
||||
.key ctrl p
|
||||
%kbd ctrl p
|
||||
%td Go to file
|
||||
.col-lg-4
|
||||
%table.shortcut-mappings
|
||||
%tbody.hidden-shortcut.network{ style: 'display:none' }
|
||||
%tbody.hidden-shortcut{ style: 'display:none' }
|
||||
%tr
|
||||
%th
|
||||
%th Network Graph
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
%i.fa.fa-arrow-left
|
||||
\/
|
||||
.key h
|
||||
%kbd h
|
||||
%td Scroll left
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
%i.fa.fa-arrow-right
|
||||
\/
|
||||
.key l
|
||||
%kbd l
|
||||
%td Scroll right
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
%i.fa.fa-arrow-up
|
||||
\/
|
||||
.key k
|
||||
%kbd k
|
||||
%td Scroll up
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
%i.fa.fa-arrow-down
|
||||
\/
|
||||
.key j
|
||||
%kbd j
|
||||
%td Scroll down
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
shift
|
||||
%i.fa.fa-arrow-up
|
||||
\/
|
||||
.key
|
||||
%kbd
|
||||
shift k
|
||||
%td Scroll to top
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%kbd
|
||||
shift
|
||||
%i.fa.fa-arrow-down
|
||||
\/
|
||||
.key
|
||||
%kbd
|
||||
shift j
|
||||
%td Scroll to bottom
|
||||
%tbody.hidden-shortcut.issues{ style: 'display:none' }
|
||||
%tbody.hidden-shortcut{ style: 'display:none' }
|
||||
%tr
|
||||
%th
|
||||
%th Issues
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key a
|
||||
%kbd a
|
||||
%td Change assignee
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key m
|
||||
%kbd m
|
||||
%td Change milestone
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key r
|
||||
%kbd r
|
||||
%td Reply (quoting selected text)
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key e
|
||||
%kbd e
|
||||
%td Edit issue
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key l
|
||||
%kbd l
|
||||
%td Change Label
|
||||
%tbody.hidden-shortcut.merge_requests{ style: 'display:none' }
|
||||
%tbody.hidden-shortcut{ style: 'display:none' }
|
||||
%tr
|
||||
%th
|
||||
%th Merge Requests
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key a
|
||||
%kbd a
|
||||
%td Change assignee
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key m
|
||||
%kbd m
|
||||
%td Change milestone
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key r
|
||||
%kbd r
|
||||
%td Reply (quoting selected text)
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key e
|
||||
%kbd e
|
||||
%td Edit merge request
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key l
|
||||
%kbd l
|
||||
%td Change Label
|
||||
%tbody.hidden-shortcut.wiki{ style: 'display:none' }
|
||||
%tbody.hidden-shortcut{ style: 'display:none' }
|
||||
%tr
|
||||
%th
|
||||
%th Wiki pages
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key e
|
||||
%kbd e
|
||||
%td Edit wiki page
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%li.first.page-item
|
||||
%li.page-item.js-first-button
|
||||
= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote, class: 'page-link'
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%li.page-item.disabled
|
||||
%li.page-item.disabled.d-none.d-md-block
|
||||
= link_to raw(t 'views.pagination.truncate'), '#', class: 'page-link'
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%li.last.page-item
|
||||
%li.page-item.js-last-button
|
||||
= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {remote: remote, class: 'page-link'}
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
|
||||
- page_url = current_page.last? ? '#' : url
|
||||
|
||||
%li.page-item{ class: ('disabled' if current_page.last?) }
|
||||
%li.page-item.js-next-button{ class: ('disabled' if current_page.last?) }
|
||||
= link_to raw(t 'views.pagination.next'), page_url, rel: 'next', remote: remote, class: 'page-link'
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
-# total_pages: total number of pages
|
||||
-# per_page: number of items to fetch per page
|
||||
-# remote: data-remote
|
||||
%li.page-item.js-pagination-page{ class: [active_when(page.current?), ('sibling' if page.next? || page.prev?)] }
|
||||
%li.page-item.js-pagination-page{ class: [active_when(page.current?), ('sibling' if page.next? || page.prev?), ('d-none d-md-block' if !page.current?) ] }
|
||||
= link_to page, url, { remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil, class: 'page-link' }
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
-# remote: data-remote
|
||||
-# paginator: the paginator that renders the pagination tags inside
|
||||
= paginator.render do
|
||||
.gl-pagination
|
||||
.gl-pagination.prepend-top-default
|
||||
%ul.pagination.justify-content-center
|
||||
- unless current_page.first?
|
||||
= first_page_tag unless total_pages < 5 # As kaminari will always show the first 5 pages
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
|
||||
- page_url = current_page.first? ? '#' : url
|
||||
|
||||
%li.page-item{ class: ('disabled' if current_page.first?) }
|
||||
%li.page-item.js-previous-button{ class: ('disabled' if current_page.first?) }
|
||||
= link_to raw(t 'views.pagination.previous'), page_url, rel: 'prev', remote: remote, class: 'page-link'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.gl-pagination
|
||||
%ul.pagination.clearfix
|
||||
.gl-pagination.prepend-top-default
|
||||
%ul.pagination.justify-content-center
|
||||
- if previous_path
|
||||
%li.page-item.prev
|
||||
= link_to(t('views.pagination.previous'), previous_path, rel: 'prev', class: 'page-link')
|
||||
|
|
|
@ -29,16 +29,16 @@
|
|||
|
||||
.col-lg-9.js-toggle-container
|
||||
%ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' }
|
||||
%li{ class: active_when(active_tab == 'blank'), role: 'presentation' }
|
||||
%a{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' }
|
||||
%li.nav-item{ role: 'presentation' }
|
||||
%a.nav-link.active{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' }
|
||||
%span.d-none.d-sm-block Blank project
|
||||
%span.d-block.d-sm-none Blank
|
||||
%li{ class: active_when(active_tab == 'template'), role: 'presentation' }
|
||||
%a{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' }
|
||||
%li.nav-item{ role: 'presentation' }
|
||||
%a.nav-link{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' }
|
||||
%span.d-none.d-sm-block Create from template
|
||||
%span.d-block.d-sm-none Template
|
||||
%li{ class: active_when(active_tab == 'import'), role: 'presentation' }
|
||||
%a{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' }
|
||||
%li.nav-item{ role: 'presentation' }
|
||||
%a.nav-link{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' }
|
||||
%span.d-none.d-sm-block Import project
|
||||
%span.d-block.d-sm-none Import
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
.errors-holder
|
||||
.card-body
|
||||
%p
|
||||
Removing the pages will prevent from exposing them to outside world.
|
||||
Removing pages will prevent them from being exposed to the outside world.
|
||||
.form-actions
|
||||
= link_to 'Remove pages', project_pages_path(@project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove"
|
||||
- else
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
= _('Contribution')
|
||||
.col-sm-10
|
||||
.form-check
|
||||
= form.check_box :allow_maintainer_to_push, disabled: !issuable.can_allow_maintainer_to_push?(current_user), class: 'form-check-input'
|
||||
= form.label :allow_maintainer_to_push, class: 'form-check-label' do
|
||||
= _('Allow edits from maintainers.')
|
||||
= link_to 'About this feature', help_page_path('user/project/merge_requests/maintainer_access')
|
||||
= form.check_box :allow_collaboration, disabled: !issuable.can_allow_collaboration?(current_user), class: 'form-check-input'
|
||||
= form.label :allow_collaboration, class: 'form-check-label' do
|
||||
= _('Allow commits from members who can merge to the target branch.')
|
||||
= link_to 'About this feature', help_page_path('user/project/merge_requests/allow_collaboration')
|
||||
.form-text.text-muted
|
||||
= allow_maintainer_push_unavailable_reason(issuable)
|
||||
= allow_collaboration_unavailable_reason(issuable)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- unless can?(current_user, :push_code, @project)
|
||||
.inline.prepend-left-10
|
||||
- if @project.branch_allows_maintainer_push?(current_user, selected_branch)
|
||||
- if @project.branch_allows_collaboration?(current_user, selected_branch)
|
||||
= commit_in_single_accessible_branch
|
||||
- else
|
||||
= commit_in_fork_help
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
.float-right
|
||||
= button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8' do
|
||||
= _('Accept terms')
|
||||
- else
|
||||
.pull-right
|
||||
= link_to root_path, class: 'btn btn-success prepend-left-8' do
|
||||
= _('Continue')
|
||||
- if can?(current_user, :decline_terms, @term)
|
||||
.float-right
|
||||
= button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add variables to POST api/v4/projects/:id/pipeline
|
||||
merge_request: 19124
|
||||
author: Jacopo Beschi @jacopo-beschi
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Rephrasing Merge Request's 'allow edits from maintainer' functionality
|
||||
merge_request: 19061
|
||||
author:
|
||||
type: deprecated
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix repository archive generation when hashed storage is enabled
|
||||
merge_request: 19441
|
||||
author:
|
||||
type: fixed
|
6
changelogs/unreleased/46585-gdpr-terms-acceptance.yml
Normal file
6
changelogs/unreleased/46585-gdpr-terms-acceptance.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Add flash notice if user has already accepted terms and allow users to continue
|
||||
to root path
|
||||
merge_request: 19156
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/fix-avatars-n-plus-one.yml
Normal file
5
changelogs/unreleased/fix-avatars-n-plus-one.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix an N+1 when loading user avatars
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update runner cached informations without performing validations
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
6
changelogs/unreleased/rails5-fix-47368.yml
Normal file
6
changelogs/unreleased/rails5-fix-47368.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: 'Rails 5 fix unknown keywords: changes, key_id, project, gl_repository, action,
|
||||
secret_token, protocol'
|
||||
merge_request: 19466
|
||||
author: Jasper Maes
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add migration to disable the usage of DSA keys
|
||||
merge_request: 19299
|
||||
author:
|
||||
type: other
|
5
changelogs/unreleased/sh-fix-events-nplus-one.yml
Normal file
5
changelogs/unreleased/sh-fix-events-nplus-one.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Eliminate N+1 queries with authors and push_data_payload in Events API
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,15 @@
|
|||
class RenameMergeRequestsAllowMaintainerToPush < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
rename_column_concurrently :merge_requests, :allow_maintainer_to_push, :allow_collaboration
|
||||
end
|
||||
|
||||
def down
|
||||
cleanup_concurrent_column_rename :merge_requests, :allow_collaboration, :allow_maintainer_to_push
|
||||
end
|
||||
end
|
15
db/migrate/20180530135500_add_index_to_stages_position.rb
Normal file
15
db/migrate/20180530135500_add_index_to_stages_position.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
class AddIndexToStagesPosition < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :ci_stages, [:pipeline_id, :position]
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index :ci_stages, [:pipeline_id, :position]
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
class ChangeDefaultValueForDsaKeyRestriction < ActiveRecord::Migration
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
change_column :application_settings, :dsa_key_restriction, :integer, null: false,
|
||||
default: -1
|
||||
|
||||
execute("UPDATE application_settings SET dsa_key_restriction = -1")
|
||||
end
|
||||
|
||||
def down
|
||||
change_column :application_settings, :dsa_key_restriction, :integer, null: false,
|
||||
default: 0
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
class CleanupMergeRequestsAllowMaintainerToPushRename < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
cleanup_concurrent_column_rename :merge_requests, :allow_maintainer_to_push, :allow_collaboration
|
||||
end
|
||||
|
||||
def down
|
||||
rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
|
||||
end
|
||||
end
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20180529152628) do
|
||||
ActiveRecord::Schema.define(version: 20180531220618) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -110,7 +110,7 @@ ActiveRecord::Schema.define(version: 20180529152628) do
|
|||
t.text "shared_runners_text_html"
|
||||
t.text "after_sign_up_text_html"
|
||||
t.integer "rsa_key_restriction", default: 0, null: false
|
||||
t.integer "dsa_key_restriction", default: 0, null: false
|
||||
t.integer "dsa_key_restriction", default: -1, null: false
|
||||
t.integer "ecdsa_key_restriction", default: 0, null: false
|
||||
t.integer "ed25519_key_restriction", default: 0, null: false
|
||||
t.boolean "housekeeping_enabled", default: true, null: false
|
||||
|
@ -520,6 +520,7 @@ ActiveRecord::Schema.define(version: 20180529152628) do
|
|||
end
|
||||
|
||||
add_index "ci_stages", ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", unique: true, using: :btree
|
||||
add_index "ci_stages", ["pipeline_id", "position"], name: "index_ci_stages_on_pipeline_id_and_position", using: :btree
|
||||
add_index "ci_stages", ["pipeline_id"], name: "index_ci_stages_on_pipeline_id", using: :btree
|
||||
add_index "ci_stages", ["project_id"], name: "index_ci_stages_on_project_id", using: :btree
|
||||
|
||||
|
@ -1229,7 +1230,7 @@ ActiveRecord::Schema.define(version: 20180529152628) do
|
|||
t.boolean "discussion_locked"
|
||||
t.integer "latest_merge_request_diff_id"
|
||||
t.string "rebase_commit_sha"
|
||||
t.boolean "allow_maintainer_to_push"
|
||||
t.boolean "allow_collaboration"
|
||||
t.boolean "squash", default: false, null: false
|
||||
end
|
||||
|
||||
|
|
|
@ -651,7 +651,8 @@ POST /projects/:id/merge_requests
|
|||
| `labels` | string | no | Labels for MR as a comma-separated list |
|
||||
| `milestone_id` | integer | no | The global ID of a milestone |
|
||||
| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging |
|
||||
| `allow_maintainer_to_push` | boolean | no | Whether or not a maintainer of the target project can push to the source branch |
|
||||
| `allow_collaboration` | boolean | no | Allow commits from members who can merge to the target branch |
|
||||
| `allow_maintainer_to_push` | boolean | no | Deprecated, see allow_collaboration |
|
||||
| `squash` | boolean | no | Squash commits into a single commit when merging |
|
||||
|
||||
```json
|
||||
|
@ -709,6 +710,7 @@ POST /projects/:id/merge_requests
|
|||
"squash": false,
|
||||
"web_url": "http://example.com/example/example/merge_requests/1",
|
||||
"discussion_locked": false,
|
||||
"allow_collaboration": false,
|
||||
"allow_maintainer_to_push": false,
|
||||
"time_stats": {
|
||||
"time_estimate": 0,
|
||||
|
@ -741,7 +743,8 @@ PUT /projects/:id/merge_requests/:merge_request_iid
|
|||
| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging |
|
||||
| `squash` | boolean | no | Squash commits into a single commit when merging |
|
||||
| `discussion_locked` | boolean | no | Flag indicating if the merge request's discussion is locked. If the discussion is locked only project members can add, edit or resolve comments. |
|
||||
| `allow_maintainer_to_push` | boolean | no | Whether or not a maintainer of the target project can push to the source branch |
|
||||
| `allow_collaboration` | boolean | no | Allow commits from members who can merge to the target branch |
|
||||
| `allow_maintainer_to_push` | boolean | no | Deprecated, see allow_collaboration |
|
||||
|
||||
Must include at least one non-required attribute from above.
|
||||
|
||||
|
@ -799,6 +802,7 @@ Must include at least one non-required attribute from above.
|
|||
"squash": false,
|
||||
"web_url": "http://example.com/example/example/merge_requests/1",
|
||||
"discussion_locked": false,
|
||||
"allow_collaboration": false,
|
||||
"allow_maintainer_to_push": false,
|
||||
"time_stats": {
|
||||
"time_estimate": 0,
|
||||
|
|
|
@ -102,6 +102,7 @@ POST /projects/:id/pipeline
|
|||
|------------|---------|----------|---------------------|
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `ref` | string | yes | Reference to commit |
|
||||
| `variables` | array | no | An array containing the variables available in the pipeline, matching the structure [{ 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }] |
|
||||
|
||||
```
|
||||
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipeline?ref=master"
|
||||
|
|
|
@ -16,7 +16,7 @@ are very appreciative of the work done by translators and proofreaders!
|
|||
- Dutch
|
||||
- Esperanto
|
||||
- French
|
||||
- Rémy Coutable - [GitLab](https://gitlab.com/rymai), [Crowdin](https://crowdin.com/profile/rymai)
|
||||
- Davy Defaud - [GitLab](https://gitlab.com/DevDef), [Crowdin](https://crowdin.com/profile/DevDef)
|
||||
- German
|
||||
- Indonesian
|
||||
- Ahmad Naufal Mukhtar - [GitLab](https://gitlab.com/anaufalm), [Crowdin](https://crowdin.com/profile/anaufalm)
|
||||
|
|
|
@ -143,6 +143,24 @@ docker login registry.example.com -u <your_username> -p <your_access_token>
|
|||
for errors (e.g. `/var/log/gitlab/gitlab-rails/production.log`). You may be able to find clues
|
||||
there.
|
||||
|
||||
#### Enable the registry debug server
|
||||
|
||||
The optional debug server can be enabled by setting the registry debug address
|
||||
in your `gitlab.rb` configuration.
|
||||
|
||||
```ruby
|
||||
registry['debug_addr'] = "localhost:5001"
|
||||
```
|
||||
|
||||
After adding the setting, [reconfigure] GitLab to apply the change.
|
||||
|
||||
Use curl to request debug output from the debug server:
|
||||
|
||||
```bash
|
||||
curl localhost:5001/debug/health
|
||||
curl localhost:5001/debug/vars
|
||||
```
|
||||
|
||||
### Advanced Troubleshooting
|
||||
|
||||
>**NOTE:** The following section is only recommended for experts.
|
||||
|
@ -275,3 +293,4 @@ Once the right permissions were set, the error will go away.
|
|||
[docker-docs]: https://docs.docker.com/engine/userguide/intro/
|
||||
[pat]: ../profile/personal_access_tokens.md
|
||||
[pdt]: ../project/deploy_tokens/index.md
|
||||
[reconfigure]: ../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
|
20
doc/user/project/merge_requests/allow_collaboration.md
Normal file
20
doc/user/project/merge_requests/allow_collaboration.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Allow collaboration on merge requests across forks
|
||||
|
||||
> [Introduced][ce-17395] in GitLab 10.6.
|
||||
|
||||
This feature is available for merge requests across forked projects that are
|
||||
publicly accessible. It makes it easier for members of projects to
|
||||
collaborate on merge requests across forks.
|
||||
|
||||
When enabled for a merge request, members with merge access to the target
|
||||
branch of the project will be granted write permissions to the source branch
|
||||
of the merge request.
|
||||
|
||||
The feature can only be enabled by users who already have push access to the
|
||||
source project, and only lasts while the merge request is open.
|
||||
|
||||
Enable this functionality while creating or editing a merge request:
|
||||
|
||||
![Enable collaboration](./img/allow_collaboration.png)
|
||||
|
||||
[ce-17395]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17395
|
BIN
doc/user/project/merge_requests/img/allow_collaboration.png
Normal file
BIN
doc/user/project/merge_requests/img/allow_collaboration.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
Before Width: | Height: | Size: 48 KiB |
|
@ -28,7 +28,7 @@ With GitLab merge requests, you can:
|
|||
- Enable [fast-forward merge requests](#fast-forward-merge-requests)
|
||||
- Enable [semi-linear history merge requests](#semi-linear-history-merge-requests) as another security layer to guarantee the pipeline is passing in the target branch
|
||||
- [Create new merge requests by email](#create-new-merge-requests-by-email)
|
||||
- Allow maintainers of the target project to push directly to the fork by [allowing edits from maintainers](maintainer_access.md)
|
||||
- [Allow collaboration](allow_collaboration.md) so members of the target project can push directly to the fork
|
||||
- [Squash and merge](squash_and_merge.md) for a cleaner commit history
|
||||
|
||||
With **[GitLab Enterprise Edition][ee]**, you can also:
|
||||
|
|
|
@ -1,20 +1 @@
|
|||
# Allow maintainer pushes for merge requests across forks
|
||||
|
||||
> [Introduced][ce-17395] in GitLab 10.6.
|
||||
|
||||
This feature is available for merge requests across forked projects that are
|
||||
publicly accessible. It makes it easier for maintainers of projects to
|
||||
collaborate on merge requests across forks.
|
||||
|
||||
When enabled for a merge request, members with merge access to the target
|
||||
branch of the project will be granted write permissions to the source branch
|
||||
of the merge request.
|
||||
|
||||
The feature can only be enabled by users who already have push access to the
|
||||
source project, and only lasts while the merge request is open.
|
||||
|
||||
Enable this functionality while creating a merge request:
|
||||
|
||||
![Enable maintainer edits](./img/allow_maintainer_push.png)
|
||||
|
||||
[ce-17395]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17395
|
||||
This document was moved to [another location](allow_collaboration.md).
|
||||
|
|
|
@ -42,7 +42,7 @@ to secure them.
|
|||
|
||||
Your files live in a project [repository](../repository/index.md) on GitLab.
|
||||
[GitLab CI](../../../ci/README.md) picks up those files and makes them available at, typically,
|
||||
`http://<username>.gitlab.io/<projectname>`. Please read through the docs on
|
||||
`https://<username>.gitlab.io/<projectname>`. Please read through the docs on
|
||||
[GitLab Pages domains](getting_started_part_one.md#gitlab-pages-domain) for more info.
|
||||
|
||||
## Explore GitLab Pages
|
||||
|
|
|
@ -559,7 +559,9 @@ module API
|
|||
expose :discussion_locked
|
||||
expose :should_remove_source_branch?, as: :should_remove_source_branch
|
||||
expose :force_remove_source_branch?, as: :force_remove_source_branch
|
||||
expose :allow_maintainer_to_push, if: -> (merge_request, _) { merge_request.for_fork? }
|
||||
expose :allow_collaboration, if: -> (merge_request, _) { merge_request.for_fork? }
|
||||
# Deprecated
|
||||
expose :allow_collaboration, as: :allow_maintainer_to_push, if: -> (merge_request, _) { merge_request.for_fork? }
|
||||
|
||||
expose :web_url do |merge_request, options|
|
||||
Gitlab::UrlBuilder.build(merge_request)
|
||||
|
|
|
@ -17,6 +17,7 @@ module API
|
|||
|
||||
def present_events(events)
|
||||
events = events.reorder(created_at: params[:sort])
|
||||
.with_associations
|
||||
|
||||
present paginate(events), with: Entities::Event
|
||||
end
|
||||
|
|
|
@ -162,7 +162,8 @@ module API
|
|||
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request'
|
||||
optional :labels, type: String, desc: 'Comma-separated list of label names'
|
||||
optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging'
|
||||
optional :allow_maintainer_to_push, type: Boolean, desc: 'Whether a maintainer of the target project can push to the source project'
|
||||
optional :allow_collaboration, type: Boolean, desc: 'Allow commits from members who can merge to the target branch'
|
||||
optional :allow_maintainer_to_push, type: Boolean, as: :allow_collaboration, desc: '[deprecated] See allow_collaboration'
|
||||
optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
|
||||
|
||||
use :optional_params_ee
|
||||
|
|
|
@ -41,15 +41,20 @@ module API
|
|||
end
|
||||
params do
|
||||
requires :ref, type: String, desc: 'Reference'
|
||||
optional :variables, Array, desc: 'Array of variables available in the pipeline'
|
||||
end
|
||||
post ':id/pipeline' do
|
||||
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42124')
|
||||
|
||||
authorize! :create_pipeline, user_project
|
||||
|
||||
pipeline_params = declared_params(include_missing: false)
|
||||
.merge(variables_attributes: params[:variables])
|
||||
.except(:variables)
|
||||
|
||||
new_pipeline = Ci::CreatePipelineService.new(user_project,
|
||||
current_user,
|
||||
declared_params(include_missing: false))
|
||||
pipeline_params)
|
||||
.execute(:api, ignore_skip_ci: true, save_on_errors: false)
|
||||
|
||||
if new_pipeline.persisted?
|
||||
|
|
3
lib/backup.rb
Normal file
3
lib/backup.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
module Backup
|
||||
Error = Class.new(StandardError)
|
||||
end
|
|
@ -44,7 +44,7 @@ module Backup
|
|||
end
|
||||
|
||||
report_success(success)
|
||||
abort 'Backup failed' unless success
|
||||
raise Backup::Error, 'Backup failed' unless success
|
||||
end
|
||||
|
||||
def restore
|
||||
|
@ -72,7 +72,7 @@ module Backup
|
|||
end
|
||||
|
||||
report_success(success)
|
||||
abort 'Restore failed' unless success
|
||||
abort Backup::Error, 'Restore failed' unless success
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -26,7 +26,7 @@ module Backup
|
|||
|
||||
unless status.zero?
|
||||
puts output
|
||||
abort 'Backup failed'
|
||||
raise Backup::Error, 'Backup failed'
|
||||
end
|
||||
|
||||
run_pipeline!([%W(tar --exclude=lost+found -C #{@backup_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600])
|
||||
|
@ -39,7 +39,11 @@ module Backup
|
|||
def restore
|
||||
backup_existing_files_dir
|
||||
|
||||
run_pipeline!([%w(gzip -cd), %W(tar --unlink-first --recursive-unlink -C #{app_files_dir} -xf -)], in: backup_tarball)
|
||||
run_pipeline!([%w(gzip -cd), %W(#{tar} --unlink-first --recursive-unlink -C #{app_files_dir} -xf -)], in: backup_tarball)
|
||||
end
|
||||
|
||||
def tar
|
||||
system(*%w[gtar --version], out: '/dev/null') ? 'gtar' : 'tar'
|
||||
end
|
||||
|
||||
def backup_existing_files_dir
|
||||
|
@ -61,7 +65,7 @@ module Backup
|
|||
|
||||
def run_pipeline!(cmd_list, options = {})
|
||||
status_list = Open3.pipeline(*cmd_list, options)
|
||||
abort 'Backup failed' unless status_list.compact.all?(&:success?)
|
||||
raise Backup::Error, 'Backup failed' unless status_list.compact.all?(&:success?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,7 +27,7 @@ module Backup
|
|||
progress.puts "done".color(:green)
|
||||
else
|
||||
puts "creating archive #{tar_file} failed".color(:red)
|
||||
abort 'Backup failed'
|
||||
raise Backup::Error, 'Backup failed'
|
||||
end
|
||||
|
||||
upload
|
||||
|
@ -52,7 +52,7 @@ module Backup
|
|||
progress.puts "done".color(:green)
|
||||
else
|
||||
puts "uploading backup to #{remote_directory} failed".color(:red)
|
||||
abort 'Backup failed'
|
||||
raise Backup::Error, 'Backup failed'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -66,7 +66,7 @@ module Backup
|
|||
progress.puts "done".color(:green)
|
||||
else
|
||||
puts "deleting tmp directory '#{dir}' failed".color(:red)
|
||||
abort 'Backup failed'
|
||||
raise Backup::Error, 'Backup failed'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,10 @@ module Backup
|
|||
|
||||
Project.find_each(batch_size: 1000) do |project|
|
||||
progress.print " * #{display_repo_path(project)} ... "
|
||||
path_to_project_repo = path_to_repo(project)
|
||||
|
||||
path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
|
||||
path_to_repo(project)
|
||||
end
|
||||
path_to_project_bundle = path_to_bundle(project)
|
||||
|
||||
# Create namespace dir or hashed path if missing
|
||||
|
@ -51,7 +54,9 @@ module Backup
|
|||
end
|
||||
|
||||
wiki = ProjectWiki.new(project)
|
||||
path_to_wiki_repo = path_to_repo(wiki)
|
||||
path_to_wiki_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
|
||||
path_to_repo(wiki)
|
||||
end
|
||||
path_to_wiki_bundle = path_to_bundle(wiki)
|
||||
|
||||
if File.exist?(path_to_wiki_repo)
|
||||
|
@ -111,7 +116,9 @@ module Backup
|
|||
# TODO: Need to find a way to do this for gitaly
|
||||
# Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195
|
||||
in_path(path_to_tars(project)) do |dir|
|
||||
path_to_project_repo = path_to_repo(project)
|
||||
path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
|
||||
path_to_repo(project)
|
||||
end
|
||||
cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir})
|
||||
|
||||
output, status = Gitlab::Popen.popen(cmd)
|
||||
|
|
|
@ -5,23 +5,47 @@ module Gitlab
|
|||
module Pipeline
|
||||
# Class for preloading data associated with pipelines such as commit
|
||||
# authors.
|
||||
module Preloader
|
||||
def self.preload(pipelines)
|
||||
# This ensures that all the pipeline commits are eager loaded before we
|
||||
# start using them.
|
||||
class Preloader
|
||||
def self.preload!(pipelines)
|
||||
##
|
||||
# This preloads all commits at once, because `Ci::Pipeline#commit` is
|
||||
# using a lazy batch loading, what results in only one batched Gitaly
|
||||
# call.
|
||||
#
|
||||
pipelines.each(&:commit)
|
||||
|
||||
pipelines.each do |pipeline|
|
||||
# This preloads the author of every commit. We're using "lazy_author"
|
||||
# here since "author" immediately loads the data on the first call.
|
||||
pipeline.commit.try(:lazy_author)
|
||||
|
||||
# This preloads the number of warnings for every pipeline, ensuring
|
||||
# that Ci::Pipeline#has_warnings? doesn't execute any additional
|
||||
# queries.
|
||||
pipeline.number_of_warnings
|
||||
self.new(pipeline).tap do |preloader|
|
||||
preloader.preload_commit_authors
|
||||
preloader.preload_pipeline_warnings
|
||||
preloader.preload_stages_warnings
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(pipeline)
|
||||
@pipeline = pipeline
|
||||
end
|
||||
|
||||
def preload_commit_authors
|
||||
# This also preloads the author of every commit. We're using "lazy_author"
|
||||
# here since "author" immediately loads the data on the first call.
|
||||
@pipeline.commit.try(:lazy_author)
|
||||
end
|
||||
|
||||
def preload_pipeline_warnings
|
||||
# This preloads the number of warnings for every pipeline, ensuring
|
||||
# that Ci::Pipeline#has_warnings? doesn't execute any additional
|
||||
# queries.
|
||||
@pipeline.number_of_warnings
|
||||
end
|
||||
|
||||
def preload_stages_warnings
|
||||
# This preloads the number of warnings for every stage, ensuring
|
||||
# that Ci::Stage#has_warnings? doesn't execute any additional
|
||||
# queries.
|
||||
@pipeline.stages.each { |stage| stage.number_of_warnings }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
def details_path
|
||||
project_pipeline_path(subject.project, subject.pipeline, anchor: subject.name)
|
||||
project_pipeline_path(subject.pipeline.project,
|
||||
subject.pipeline,
|
||||
anchor: subject.name)
|
||||
end
|
||||
|
||||
def has_action?
|
||||
|
|
|
@ -109,7 +109,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def ==(other)
|
||||
path == other.path
|
||||
[storage, relative_path] == [other.storage, other.relative_path]
|
||||
end
|
||||
|
||||
def path
|
||||
|
@ -395,12 +395,12 @@ module Gitlab
|
|||
nil
|
||||
end
|
||||
|
||||
def archive_metadata(ref, storage_path, format = "tar.gz", append_sha:)
|
||||
def archive_metadata(ref, storage_path, project_path, format = "tar.gz", append_sha:)
|
||||
ref ||= root_ref
|
||||
commit = Gitlab::Git::Commit.find(self, ref)
|
||||
return {} if commit.nil?
|
||||
|
||||
prefix = archive_prefix(ref, commit.id, append_sha: append_sha)
|
||||
prefix = archive_prefix(ref, commit.id, project_path, append_sha: append_sha)
|
||||
|
||||
{
|
||||
'ArchivePrefix' => prefix,
|
||||
|
@ -412,16 +412,12 @@ module Gitlab
|
|||
|
||||
# This is both the filename of the archive (missing the extension) and the
|
||||
# name of the top-level member of the archive under which all files go
|
||||
#
|
||||
# FIXME: The generated prefix is incorrect for projects with hashed
|
||||
# storage enabled
|
||||
def archive_prefix(ref, sha, append_sha:)
|
||||
def archive_prefix(ref, sha, project_path, append_sha:)
|
||||
append_sha = (ref != sha) if append_sha.nil?
|
||||
|
||||
project_name = self.name.chomp('.git')
|
||||
formatted_ref = ref.tr('/', '-')
|
||||
|
||||
prefix_segments = [project_name, formatted_ref]
|
||||
prefix_segments = [project_path, formatted_ref]
|
||||
prefix_segments << sha if append_sha
|
||||
|
||||
prefix_segments.join('-')
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue