Commit bfbf7931 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 12af3e15befd
...@@ -17,7 +17,7 @@ export default { ...@@ -17,7 +17,7 @@ export default {
diffFilesLength: { diffFilesLength: {
type: Number, type: Number,
required: false, required: false,
default: 0, default: null,
}, },
}, },
computed: { computed: {
......
...@@ -2,7 +2,15 @@ ...@@ -2,7 +2,15 @@
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { GlButton, GlFormInput, GlLink, GlLoadingIcon, GlBadge } from '@gitlab/ui'; import {
GlButton,
GlFormInput,
GlLink,
GlLoadingIcon,
GlBadge,
GlAlert,
GlSprintf,
} from '@gitlab/ui';
import { __, sprintf, n__ } from '~/locale'; import { __, sprintf, n__ } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -26,6 +34,8 @@ export default { ...@@ -26,6 +34,8 @@ export default {
Icon, Icon,
Stacktrace, Stacktrace,
GlBadge, GlBadge,
GlAlert,
GlSprintf,
}, },
directives: { directives: {
TrackEvent: TrackEventDirective, TrackEvent: TrackEventDirective,
...@@ -85,6 +95,8 @@ export default { ...@@ -85,6 +95,8 @@ export default {
return { return {
GQLerror: null, GQLerror: null,
issueCreationInProgress: false, issueCreationInProgress: false,
isAlertVisible: false,
closedIssueId: null,
}; };
}, },
computed: { computed: {
...@@ -184,7 +196,14 @@ export default { ...@@ -184,7 +196,14 @@ export default {
onResolveStatusUpdate() { onResolveStatusUpdate() {
const status = const status =
this.errorStatus === errorStatus.RESOLVED ? errorStatus.UNRESOLVED : errorStatus.RESOLVED; this.errorStatus === errorStatus.RESOLVED ? errorStatus.UNRESOLVED : errorStatus.RESOLVED;
this.updateResolveStatus({ endpoint: this.issueUpdatePath, status });
// eslint-disable-next-line promise/catch-or-return
this.updateResolveStatus({ endpoint: this.issueUpdatePath, status }).then(res => {
this.closedIssueId = res.closed_issue_iid;
if (this.closedIssueId) {
this.isAlertVisible = true;
}
});
}, },
formatDate(date) { formatDate(date) {
return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`; return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
...@@ -199,6 +218,18 @@ export default { ...@@ -199,6 +218,18 @@ export default {
<gl-loading-icon :size="3" /> <gl-loading-icon :size="3" />
</div> </div>
<div v-else-if="showDetails" class="error-details"> <div v-else-if="showDetails" class="error-details">
<gl-alert v-if="isAlertVisible" @dismiss="isAlertVisible = false">
<gl-sprintf
:message="
__('The associated issue #%{issueId} has been closed as the error is now resolved.')
"
>
<template #issueId>
<span>{{ closedIssueId }}</span>
</template>
</gl-sprintf>
</gl-alert>
<div class="top-area align-items-center justify-content-between py-3"> <div class="top-area align-items-center justify-content-between py-3">
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span> <span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
<div class="d-inline-flex"> <div class="d-inline-flex">
......
...@@ -11,9 +11,11 @@ export const setStatus = ({ commit }, status) => { ...@@ -11,9 +11,11 @@ export const setStatus = ({ commit }, status) => {
export const updateStatus = ({ commit }, { endpoint, redirectUrl, status }) => export const updateStatus = ({ commit }, { endpoint, redirectUrl, status }) =>
service service
.updateErrorStatus(endpoint, status) .updateErrorStatus(endpoint, status)
.then(() => { .then(resp => {
if (redirectUrl) visitUrl(redirectUrl);
commit(types.SET_ERROR_STATUS, status); commit(types.SET_ERROR_STATUS, status);
if (redirectUrl) visitUrl(redirectUrl);
return resp.data.result;
}) })
.catch(() => createFlash(__('Failed to update issue status'))); .catch(() => createFlash(__('Failed to update issue status')));
......
...@@ -27,7 +27,7 @@ export default { ...@@ -27,7 +27,7 @@ export default {
<template> <template>
<div class="d-flex"> <div class="d-flex">
<gl-sprintf message="by %{user}"> <gl-sprintf :message="__('by %{user}')">
<template #user> <template #user>
<user-avatar-link <user-avatar-link
class="prepend-left-4" class="prepend-left-4"
......
...@@ -146,9 +146,8 @@ export default { ...@@ -146,9 +146,8 @@ export default {
v-if="commit.description" v-if="commit.description"
:class="{ 'd-block': showDescription }" :class="{ 'd-block': showDescription }"
class="commit-row-description append-bottom-8" class="commit-row-description append-bottom-8"
>{{ commit.description }}</pre
> >
{{ commit.description }}
</pre>
</div> </div>
<div class="commit-actions flex-row"> <div class="commit-actions flex-row">
<div v-if="commit.signatureHtml" v-html="commit.signatureHtml"></div> <div v-if="commit.signatureHtml" v-html="commit.signatureHtml"></div>
......
...@@ -165,7 +165,7 @@ export default { ...@@ -165,7 +165,7 @@ export default {
<gl-icon :name="visibilityLevelIcon" :size="14" /> <gl-icon :name="visibilityLevelIcon" :size="14" />
</div> </div>
<div class="creator"> <div class="creator">
<gl-sprintf message="Authored %{timeago} by %{author}"> <gl-sprintf :message="__('Authored %{timeago} by %{author}')">
<template #timeago> <template #timeago>
<time-ago-tooltip <time-ago-tooltip
:time="snippet.createdAt" :time="snippet.createdAt"
...@@ -218,7 +218,7 @@ export default { ...@@ -218,7 +218,7 @@ export default {
errorMessage errorMessage
}}</gl-alert> }}</gl-alert>
<gl-sprintf message="Are you sure you want to delete %{name}?"> <gl-sprintf :message="__('Are you sure you want to delete %{name}?')">
<template #name <template #name
><strong>{{ snippet.title }}</strong></template ><strong>{{ snippet.title }}</strong></template
> >
......
...@@ -25,7 +25,7 @@ export default { ...@@ -25,7 +25,7 @@ export default {
</div> </div>
<small v-if="snippet.updatedAt !== snippet.createdAt" class="edited-text"> <small v-if="snippet.updatedAt !== snippet.createdAt" class="edited-text">
<gl-sprintf message="Edited %{timeago}"> <gl-sprintf :message="__('Edited %{timeago}')">
<template #timeago> <template #timeago>
<time-ago-tooltip :time="snippet.updatedAt" tooltip-placement="bottom" /> <time-ago-tooltip :time="snippet.updatedAt" tooltip-placement="bottom" />
</template> </template>
......
...@@ -65,7 +65,10 @@ class Blob < SimpleDelegator ...@@ -65,7 +65,10 @@ class Blob < SimpleDelegator
BlobViewer::YarnLock BlobViewer::YarnLock
].freeze ].freeze
attr_reader :project attr_reader :container
delegate :repository, to: :container, allow_nil: true
delegate :project, to: :repository, allow_nil: true
# Wrap a Gitlab::Git::Blob object, or return nil when given nil # Wrap a Gitlab::Git::Blob object, or return nil when given nil
# #
...@@ -77,22 +80,22 @@ class Blob < SimpleDelegator ...@@ -77,22 +80,22 @@ class Blob < SimpleDelegator
# #
# blob = Blob.decorate(nil) # blob = Blob.decorate(nil)
# puts "truthy" if blob # No output # puts "truthy" if blob # No output
def self.decorate(blob, project = nil) def self.decorate(blob, container = nil)
return if blob.nil? return if blob.nil?
new(blob, project) new(blob, container)
end end
def self.lazy(project, commit_id, path, blob_size_limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) def self.lazy(container, commit_id, path, blob_size_limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
BatchLoader.for([commit_id, path]).batch(key: project.repository) do |items, loader, args| BatchLoader.for([commit_id, path]).batch(key: container.repository) do |items, loader, args|
args[:key].blobs_at(items, blob_size_limit: blob_size_limit).each do |blob| args[:key].blobs_at(items, blob_size_limit: blob_size_limit).each do |blob|
loader.call([blob.commit_id, blob.path], blob) if blob loader.call([blob.commit_id, blob.path], blob) if blob
end end
end end
end end
def initialize(blob, project = nil) def initialize(blob, container = nil)
@project = project @container = container
super(blob) super(blob)
end end
...@@ -116,7 +119,7 @@ def data ...@@ -116,7 +119,7 @@ def data
def load_all_data! def load_all_data!
# Endpoint needed: https://gitlab.com/gitlab-org/gitaly/issues/756 # Endpoint needed: https://gitlab.com/gitlab-org/gitaly/issues/756
Gitlab::GitalyClient.allow_n_plus_1_calls do Gitlab::GitalyClient.allow_n_plus_1_calls do
super(project.repository) if project super(repository) if container
end end
end end
......
...@@ -21,11 +21,14 @@ class Commit ...@@ -21,11 +21,14 @@ class Commit
participant :committer participant :committer
participant :notes_with_associations participant :notes_with_associations
attr_accessor :project, :author attr_accessor :author
attr_accessor :redacted_description_html attr_accessor :redacted_description_html
attr_accessor :redacted_title_html attr_accessor :redacted_title_html
attr_accessor :redacted_full_title_html attr_accessor :redacted_full_title_html
attr_reader :gpg_commit attr_reader :gpg_commit, :container
delegate :repository, to: :container
delegate :project, to: :repository, allow_nil: true
DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines]
...@@ -44,12 +47,12 @@ class Commit ...@@ -44,12 +47,12 @@ class Commit
cache_markdown_field :description, pipeline: :commit_description cache_markdown_field :description, pipeline: :commit_description
class << self class << self
def decorate(commits, project) def decorate(commits, container)
commits.map do |commit| commits.map do |commit|
if commit.is_a?(Commit) if commit.is_a?(Commit)
commit commit
else else
self.new(commit, project) self.new(commit, container)
end end
end end
end end
...@@ -85,24 +88,24 @@ def max_diff_options ...@@ -85,24 +88,24 @@ def max_diff_options
} }
end end
def from_hash(hash, project) def from_hash(hash, container)
raw_commit = Gitlab::Git::Commit.new(project.repository.raw, hash) raw_commit = Gitlab::Git::Commit.new(container.repository.raw, hash)
new(raw_commit, project) new(raw_commit, container)
end end
def valid_hash?(key) def valid_hash?(key)
!!(EXACT_COMMIT_SHA_PATTERN =~ key) !!(EXACT_COMMIT_SHA_PATTERN =~ key)
end end
def lazy(project, oid) def lazy(container, oid)
BatchLoader.for({ project: project, oid: oid }).batch(replace_methods: false) do |items, loader| BatchLoader.for({ container: container, oid: oid }).batch(replace_methods: false) do |items, loader|
items_by_project = items.group_by { |i| i[:project] } items_by_container = items.group_by { |i| i[:container] }
items_by_project.each do |project, commit_ids| items_by_container.each do |container, commit_ids|
oids = commit_ids.map { |i| i[:oid] } oids = commit_ids.map { |i| i[:oid] }
project.repository.commits_by(oids: oids).each do |commit| container.repository.commits_by(oids: oids).each do |commit|
loader.call({ project: commit.project, oid: commit.id }, commit) if commit loader.call({ container: commit.container, oid: commit.id }, commit) if commit
end end
end end
end end
...@@ -115,12 +118,12 @@ def parent_class ...@@ -115,12 +118,12 @@ def parent_class
attr_accessor :raw attr_accessor :raw
def initialize(raw_commit, project) def initialize(raw_commit, container)
raise "Nil as raw commit passed" unless raw_commit raise "Nil as raw commit passed" unless raw_commit
@raw = raw_commit @raw = raw_commit
@project = project @container = container
@gpg_commit = Gitlab::Gpg::Commit.new(self) if project @gpg_commit = Gitlab::Gpg::Commit.new(self) if container
end end
delegate \ delegate \
...@@ -141,7 +144,7 @@ def id ...@@ -141,7 +144,7 @@ def id
end end
def project_id def project_id
project.id project&.id
end end
def ==(other) def ==(other)
...@@ -269,17 +272,17 @@ def committer(confirmed: true) ...@@ -269,17 +272,17 @@ def committer(confirmed: true)
end end
def parents def parents
@parents ||= parent_ids.map { |oid| Commit.lazy(project, oid) } @parents ||= parent_ids.map { |oid| Commit.lazy(container, oid) }
end end
def parent def parent
strong_memoize(:parent) do strong_memoize(:parent) do
project.commit_by(oid: self.parent_id) if self.parent_id container.commit_by(oid: self.parent_id) if self.parent_id
end end
end end
def notes def notes
project.notes.for_commit_id(self.id) container.notes.for_commit_id(self.id)
end end
def user_mentions def user_mentions
...@@ -295,7 +298,7 @@ def notes_with_associations ...@@ -295,7 +298,7 @@ def notes_with_associations
end end
def merge_requests def merge_requests
@merge_requests ||= project.merge_requests.by_commit_sha(sha) @merge_requests ||= project&.merge_requests&.by_commit_sha(sha)
end end
def method_missing(method, *args, &block) def method_missing(method, *args, &block)
...@@ -330,7 +333,7 @@ def revert_branch_name ...@@ -330,7 +333,7 @@ def revert_branch_name
end end
def cherry_pick_branch_name def cherry_pick_branch_name
project.repository.next_branch("cherry-pick-#{short_id}", mild: true) repository.next_branch("cherry-pick-#{short_id}", mild: true)
end end
def cherry_pick_description(user) def cherry_pick_description(user)
...@@ -418,7 +421,7 @@ def uri_type(path) ...@@ -418,7 +421,7 @@ def uri_type(path)
return unless entry return unless entry
if entry[:type] == :blob if entry[:type] == :blob
blob = ::Blob.decorate(Gitlab::Git::Blob.new(name: entry[:name]), @project) blob = ::Blob.decorate(Gitlab::Git::Blob.new(name: entry[:name]), container)
blob.image? || blob.video? || blob.audio? ? :raw : :blob blob.image? || blob.video? || blob.audio? ? :raw : :blob
else else
entry[:type] entry[:type]
...@@ -484,7 +487,7 @@ def expire_note_etag_cache_for_related_mrs ...@@ -484,7 +487,7 @@ def expire_note_etag_cache_for_related_mrs
end end
def commit_reference(from, referable_commit_id, full: false) def commit_reference(from, referable_commit_id, full: false)
base = project.to_reference_base(from, full: full) base = project&.to_reference_base(from, full: full)
if base.present? if base.present?
"#{base}#{self.class.reference_prefix}#{referable_commit_id}" "#{base}#{self.class.reference_prefix}#{referable_commit_id}"
...@@ -510,6 +513,6 @@ def repo_changes ...@@ -510,6 +513,6 @@ def repo_changes
end end
def merged_merge_request_no_cache(user) def merged_merge_request_no_cache(user)
MergeRequestsFinder.new(user, project_id: project.id).find_by(merge_commit_sha: id) if merge_commit? MergeRequestsFinder.new(user, project_id: project_id).find_by(merge_commit_sha: id) if merge_commit?
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
# A collection of Commit instances for a specific project and Git reference. # A collection of Commit instances for a specific container and Git reference.
class CommitCollection class CommitCollection
include Enumerable include Enumerable
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
attr_reader :project, :ref, :commits attr_reader :container, :ref, :commits
# project - The project the commits belong to. delegate :repository, to: :container, allow_nil: true
delegate :project, to: :repository, allow_nil: true
# container - The object the commits belong to.
# commits - The Commit instances to store. # commits - The Commit instances to store.
# ref - The name of the ref (e.g. "master"). # ref - The name of the ref (e.g. "master").
def initialize(project, commits, ref = nil) def initialize(container, commits, ref = nil)
@project = project @container = container
@commits = commits @commits = commits
@ref = ref @ref = ref
end end
...@@ -39,6 +42,8 @@ def without_merge_commits ...@@ -39,6 +42,8 @@ def without_merge_commits
# Setting the pipeline for each commit ahead of time removes the need for running # Setting the pipeline for each commit ahead of time removes the need for running
# a query for every commit we're displaying. # a query for every commit we're displaying.
def with_latest_pipeline(ref = nil) def with_latest_pipeline(ref = nil)
return self unless project
pipelines = project.ci_pipelines.latest_pipeline_per_commit(map(&:id), ref) pipelines = project.ci_pipelines.latest_pipeline_per_commit(map(&:id), ref)
each do |commit| each do |commit|
...@@ -59,16 +64,16 @@ def fully_enriched? ...@@ -59,16 +64,16 @@ def fully_enriched?
# Batch load any commits that are not backed by full gitaly data, and # Batch load any commits that are not backed by full gitaly data, and
# replace them in the collection. # replace them in the collection.
def enrich! def enrich!
# A project is needed in order to fetch data from gitaly. Projects # A container is needed in order to fetch data from gitaly. Containers
# can be absent from commits in certain rare situations (like when # can be absent from commits in certain rare situations (like when
# viewing a MR of a deleted fork). In these cases, assume that the # viewing a MR of a deleted fork). In these cases, assume that the
# enriched data is not needed. # enriched data is not needed.
return self if project.blank? || fully_enriched? return self if container.blank? || fully_enriched?
# Batch load full Commits from the repository # Batch load full Commits from the repository
# and map to a Hash of id => Commit # and map to a Hash of id => Commit
replacements = Hash[unenriched.map do |c| replacements = Hash[unenriched.map do |c|
[c.id, Commit.lazy(project, c.id)] [c.id, Commit.lazy(container, c.id)]
end.compact] end.compact]
# Replace the commits, keeping the same order # Replace the commits, keeping the same order
......
# frozen_string_literal: true
module HasRepository
extend ActiveSupport::Concern
include Gitlab::ShellAdapter
include AfterCommitQueue
include Gitlab::Utils::StrongMemoize
delegate :base_dir, :disk_path, to: :storage
def valid_repo?
repository.exists?
rescue
errors.add(:path, _('Invalid repository path'))
false
end
def repo_exists?
strong_memoize(:repo_exists) do
repository.exists?
rescue
false
end
end
def repository_exists?
!!repository.exists?
end
def root_ref?(branch)
repository.root_ref == branch
end