Merge remote-tracking branch 'origin/master' into 39549-label-list-page-redesign-with-draggable-labels

This commit is contained in:
Luke Bennett 2018-06-06 08:50:13 +01:00
commit 0af7333ef9
No known key found for this signature in database
GPG key ID: A738E9C68D3BF31A
175 changed files with 1837 additions and 677 deletions

View file

@ -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

View file

@ -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

View file

@ -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':

View file

@ -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>

View file

@ -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

View file

@ -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;

View file

@ -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">

View file

@ -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>

View file

@ -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 {

View file

@ -448,6 +448,10 @@ img.emoji {
.break-word {
word-wrap: break-word;
&.all-words {
word-break: break-word;
}
}
/** COMMON CLASSES **/

View file

@ -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;
}
}

View file

@ -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 {

View file

@ -102,10 +102,6 @@
.form-text.text-muted {
margin-top: 0;
}
.label-light {
margin-bottom: 0;
}
}
.settings-list-icon {

View file

@ -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

View file

@ -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,

View file

@ -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]

View file

@ -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,

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -36,4 +36,8 @@ module WithUploads
upload.destroy
end
end
def retrieve_upload(_identifier, paths)
uploads.find_by(path: paths)
end
end

View file

@ -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

View file

@ -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

View file

@ -1,2 +1,3 @@
class PersonalSnippet < Snippet
include WithUploads
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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|

View file

@ -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

View file

@ -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

View file

@ -1,5 +1,7 @@
module ApplicationSettings
class UpdateService < ApplicationSettings::BaseService
attr_reader :params, :application_setting
def execute
update_terms(@params.delete(:terms))

View file

@ -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)

View file

@ -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

View file

@ -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?

View file

@ -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

View file

@ -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

View file

@ -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|

View file

@ -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

View file

@ -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 &#8984; shift p
%kbd &#8984; 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 &#8984; p
%kbd &#8984; 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

View file

@ -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'

View file

@ -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'

View file

@ -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'}

View file

@ -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'

View file

@ -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' }

View file

@ -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

View file

@ -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'

View file

@ -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')

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,5 @@
---
title: Add variables to POST api/v4/projects/:id/pipeline
merge_request: 19124
author: Jacopo Beschi @jacopo-beschi
type: added

View file

@ -0,0 +1,5 @@
---
title: Rephrasing Merge Request's 'allow edits from maintainer' functionality
merge_request: 19061
author:
type: deprecated

View file

@ -0,0 +1,5 @@
---
title: Fix repository archive generation when hashed storage is enabled
merge_request: 19441
author:
type: fixed

View 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

View file

@ -0,0 +1,5 @@
---
title: Fix an N+1 when loading user avatars
merge_request:
author:
type: performance

View file

@ -0,0 +1,5 @@
---
title: Update runner cached informations without performing validations
merge_request:
author:
type: performance

View 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

View file

@ -0,0 +1,5 @@
---
title: Add migration to disable the usage of DSA keys
merge_request: 19299
author:
type: other

View file

@ -0,0 +1,5 @@
---
title: Eliminate N+1 queries with authors and push_data_payload in Events API
merge_request:
author:
type: performance

View file

@ -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

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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"

View file

@ -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)

View file

@ -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

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View file

@ -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:

View file

@ -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).

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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
View file

@ -0,0 +1,3 @@
module Backup
Error = Class.new(StandardError)
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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?

View file

@ -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