Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
88a0824944
commit
6b833f1e03
|
|
@ -17,7 +17,7 @@ export default {
|
|||
diffFilesLength: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,15 @@
|
|||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import dateFormat from 'dateformat';
|
||||
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 LoadingButton from '~/vue_shared/components/loading_button.vue';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
|
|
@ -26,6 +34,8 @@ export default {
|
|||
Icon,
|
||||
Stacktrace,
|
||||
GlBadge,
|
||||
GlAlert,
|
||||
GlSprintf,
|
||||
},
|
||||
directives: {
|
||||
TrackEvent: TrackEventDirective,
|
||||
|
|
@ -85,6 +95,8 @@ export default {
|
|||
return {
|
||||
GQLerror: null,
|
||||
issueCreationInProgress: false,
|
||||
isAlertVisible: false,
|
||||
closedIssueId: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -184,7 +196,14 @@ export default {
|
|||
onResolveStatusUpdate() {
|
||||
const status =
|
||||
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) {
|
||||
return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
|
||||
|
|
@ -199,6 +218,18 @@ export default {
|
|||
<gl-loading-icon :size="3" />
|
||||
</div>
|
||||
<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">
|
||||
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
|
||||
<div class="d-inline-flex">
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ export const setStatus = ({ commit }, status) => {
|
|||
export const updateStatus = ({ commit }, { endpoint, redirectUrl, status }) =>
|
||||
service
|
||||
.updateErrorStatus(endpoint, status)
|
||||
.then(() => {
|
||||
if (redirectUrl) visitUrl(redirectUrl);
|
||||
.then(resp => {
|
||||
commit(types.SET_ERROR_STATUS, status);
|
||||
if (redirectUrl) visitUrl(redirectUrl);
|
||||
|
||||
return resp.data.result;
|
||||
})
|
||||
.catch(() => createFlash(__('Failed to update issue status')));
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="d-flex">
|
||||
<gl-sprintf message="by %{user}">
|
||||
<gl-sprintf :message="__('by %{user}')">
|
||||
<template #user>
|
||||
<user-avatar-link
|
||||
class="prepend-left-4"
|
||||
|
|
|
|||
|
|
@ -146,9 +146,8 @@ export default {
|
|||
v-if="commit.description"
|
||||
:class="{ 'd-block': showDescription }"
|
||||
class="commit-row-description append-bottom-8"
|
||||
>{{ commit.description }}</pre
|
||||
>
|
||||
{{ commit.description }}
|
||||
</pre>
|
||||
</div>
|
||||
<div class="commit-actions flex-row">
|
||||
<div v-if="commit.signatureHtml" v-html="commit.signatureHtml"></div>
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ export default {
|
|||
<gl-icon :name="visibilityLevelIcon" :size="14" />
|
||||
</div>
|
||||
<div class="creator">
|
||||
<gl-sprintf message="Authored %{timeago} by %{author}">
|
||||
<gl-sprintf :message="__('Authored %{timeago} by %{author}')">
|
||||
<template #timeago>
|
||||
<time-ago-tooltip
|
||||
:time="snippet.createdAt"
|
||||
|
|
@ -218,7 +218,7 @@ export default {
|
|||
errorMessage
|
||||
}}</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
|
||||
><strong>{{ snippet.title }}</strong></template
|
||||
>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export default {
|
|||
</div>
|
||||
|
||||
<small v-if="snippet.updatedAt !== snippet.createdAt" class="edited-text">
|
||||
<gl-sprintf message="Edited %{timeago}">
|
||||
<gl-sprintf :message="__('Edited %{timeago}')">
|
||||
<template #timeago>
|
||||
<time-ago-tooltip :time="snippet.updatedAt" tooltip-placement="bottom" />
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -65,7 +65,10 @@ class Blob < SimpleDelegator
|
|||
BlobViewer::YarnLock
|
||||
].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
|
||||
#
|
||||
|
|
@ -77,22 +80,22 @@ class Blob < SimpleDelegator
|
|||
#
|
||||
# blob = Blob.decorate(nil)
|
||||
# puts "truthy" if blob # No output
|
||||
def self.decorate(blob, project = nil)
|
||||
def self.decorate(blob, container = nil)
|
||||
return if blob.nil?
|
||||
|
||||
new(blob, project)
|
||||
new(blob, container)
|
||||
end
|
||||
|
||||
def self.lazy(project, 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|
|
||||
def self.lazy(container, commit_id, path, blob_size_limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
|
||||
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|
|
||||
loader.call([blob.commit_id, blob.path], blob) if blob
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(blob, project = nil)
|
||||
@project = project
|
||||
def initialize(blob, container = nil)
|
||||
@container = container
|
||||
|
||||
super(blob)
|
||||
end
|
||||
|
|
@ -116,7 +119,7 @@ class Blob < SimpleDelegator
|
|||
def load_all_data!
|
||||
# Endpoint needed: https://gitlab.com/gitlab-org/gitaly/issues/756
|
||||
Gitlab::GitalyClient.allow_n_plus_1_calls do
|
||||
super(project.repository) if project
|
||||
super(repository) if container
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -21,11 +21,14 @@ class Commit
|
|||
participant :committer
|
||||
participant :notes_with_associations
|
||||
|
||||
attr_accessor :project, :author
|
||||
attr_accessor :author
|
||||
attr_accessor :redacted_description_html
|
||||
attr_accessor :redacted_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]
|
||||
|
||||
|
|
@ -44,12 +47,12 @@ class Commit
|
|||
cache_markdown_field :description, pipeline: :commit_description
|
||||
|
||||
class << self
|
||||
def decorate(commits, project)
|
||||
def decorate(commits, container)
|
||||
commits.map do |commit|
|
||||
if commit.is_a?(Commit)
|
||||
commit
|
||||
else
|
||||
self.new(commit, project)
|
||||
self.new(commit, container)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -85,24 +88,24 @@ class Commit
|
|||
}
|
||||
end
|
||||
|
||||
def from_hash(hash, project)
|
||||
raw_commit = Gitlab::Git::Commit.new(project.repository.raw, hash)
|
||||
new(raw_commit, project)
|
||||
def from_hash(hash, container)
|
||||
raw_commit = Gitlab::Git::Commit.new(container.repository.raw, hash)
|
||||
new(raw_commit, container)
|
||||
end
|
||||
|
||||
def valid_hash?(key)
|
||||
!!(EXACT_COMMIT_SHA_PATTERN =~ key)
|
||||
end
|
||||
|
||||
def lazy(project, oid)
|
||||
BatchLoader.for({ project: project, oid: oid }).batch(replace_methods: false) do |items, loader|
|
||||
items_by_project = items.group_by { |i| i[:project] }
|
||||
def lazy(container, oid)
|
||||
BatchLoader.for({ container: container, oid: oid }).batch(replace_methods: false) do |items, loader|
|
||||
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] }
|
||||
|
||||
project.repository.commits_by(oids: oids).each do |commit|
|
||||
loader.call({ project: commit.project, oid: commit.id }, commit) if commit
|
||||
container.repository.commits_by(oids: oids).each do |commit|
|
||||
loader.call({ container: commit.container, oid: commit.id }, commit) if commit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -115,12 +118,12 @@ class Commit
|
|||
|
||||
attr_accessor :raw
|
||||
|
||||
def initialize(raw_commit, project)
|
||||
def initialize(raw_commit, container)
|
||||
raise "Nil as raw commit passed" unless raw_commit
|
||||
|
||||
@raw = raw_commit
|
||||
@project = project
|
||||
@gpg_commit = Gitlab::Gpg::Commit.new(self) if project
|
||||
@container = container
|
||||
@gpg_commit = Gitlab::Gpg::Commit.new(self) if container
|
||||
end
|
||||
|
||||
delegate \
|
||||
|
|
@ -141,7 +144,7 @@ class Commit
|
|||
end
|
||||
|
||||
def project_id
|
||||
project.id
|
||||
project&.id
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
|
|
@ -269,17 +272,17 @@ class Commit
|
|||
end
|
||||
|
||||
def parents
|
||||
@parents ||= parent_ids.map { |oid| Commit.lazy(project, oid) }
|
||||
@parents ||= parent_ids.map { |oid| Commit.lazy(container, oid) }
|
||||
end
|
||||
|
||||
def parent
|
||||
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
|
||||
|
||||
def notes
|
||||
project.notes.for_commit_id(self.id)
|
||||
container.notes.for_commit_id(self.id)
|
||||
end
|
||||
|
||||
def user_mentions
|
||||
|
|
@ -295,7 +298,7 @@ class Commit
|
|||
end
|
||||
|
||||
def merge_requests
|
||||
@merge_requests ||= project.merge_requests.by_commit_sha(sha)
|
||||
@merge_requests ||= project&.merge_requests&.by_commit_sha(sha)
|
||||
end
|
||||
|
||||
def method_missing(method, *args, &block)
|
||||
|
|
@ -330,7 +333,7 @@ class Commit
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
def cherry_pick_description(user)
|
||||
|
|
@ -418,7 +421,7 @@ class Commit
|
|||
return unless entry
|
||||
|
||||
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
|
||||
else
|
||||
entry[:type]
|
||||
|
|
@ -484,7 +487,7 @@ class Commit
|
|||
end
|
||||
|
||||
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?
|
||||
"#{base}#{self.class.reference_prefix}#{referable_commit_id}"
|
||||
|
|
@ -510,6 +513,6 @@ class Commit
|
|||
end
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
# 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
|
||||
include Enumerable
|
||||
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.
|
||||
# ref - The name of the ref (e.g. "master").
|
||||
def initialize(project, commits, ref = nil)
|
||||
@project = project
|
||||
def initialize(container, commits, ref = nil)
|
||||
@container = container
|
||||
@commits = commits
|
||||
@ref = ref
|
||||
end
|
||||
|
|
@ -39,6 +42,8 @@ class CommitCollection
|
|||
# Setting the pipeline for each commit ahead of time removes the need for running
|
||||
# a query for every commit we're displaying.
|
||||
def with_latest_pipeline(ref = nil)
|
||||
return self unless project
|
||||
|
||||
pipelines = project.ci_pipelines.latest_pipeline_per_commit(map(&:id), ref)
|
||||
|
||||
each do |commit|
|
||||
|
|
@ -59,16 +64,16 @@ class CommitCollection
|
|||
# Batch load any commits that are not backed by full gitaly data, and
|
||||
# replace them in the collection.
|
||||
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
|
||||
# viewing a MR of a deleted fork). In these cases, assume that the
|
||||
# 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
|
||||
# and map to a Hash of id => Commit
|
||||
replacements = Hash[unenriched.map do |c|
|
||||
[c.id, Commit.lazy(project, c.id)]
|
||||
[c.id, Commit.lazy(container, c.id)]
|
||||
end.compact]
|
||||
|
||||
# Replace the commits, keeping the same order
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
# 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
|
||||
|
||||
def commit(ref = 'HEAD')
|
||||
repository.commit(ref)
|
||||
end
|
||||
|
||||
def commit_by(oid:)
|
||||
repository.commit_by(oid: oid)
|
||||
end
|
||||
|
||||
def commits_by(oids:)
|
||||
repository.commits_by(oids: oids)
|
||||
end
|
||||
|
||||
def repository
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def storage
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def full_path
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def empty_repo?
|
||||
repository.empty?
|
||||
end
|
||||
|
||||
def default_branch
|
||||
@default_branch ||= repository.root_ref
|
||||
end
|
||||
|
||||
def reload_default_branch
|
||||
@default_branch = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
default_branch
|
||||
end
|
||||
|
||||
def url_to_repo
|
||||
gitlab_shell.url_to_repo(full_path)
|
||||
end
|
||||
|
||||
def ssh_url_to_repo
|
||||
url_to_repo
|
||||
end
|
||||
|
||||
def http_url_to_repo
|
||||
custom_root = Gitlab::CurrentSettings.custom_http_clone_url_root
|
||||
|
||||
url = if custom_root.present?
|
||||
Gitlab::Utils.append_path(
|
||||
custom_root,
|
||||
web_url(only_path: true)
|
||||
)
|
||||
else
|
||||
web_url
|
||||
end
|
||||
|
||||
"#{url}.git"
|
||||
end
|
||||
|
||||
def web_url(only_path: nil)
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
|
@ -19,6 +19,7 @@ class Project < ApplicationRecord
|
|||
include ProjectFeaturesCompatibility
|
||||
include SelectForProjectAuthorization
|
||||
include Presentable
|
||||
include HasRepository
|
||||
include Routable
|
||||
include GroupDescendant
|
||||
include Gitlab::SQL::Pattern
|
||||
|
|
@ -326,7 +327,6 @@ class Project < ApplicationRecord
|
|||
to: :project_feature, allow_nil: true
|
||||
delegate :scheduled?, :started?, :in_progress?, :failed?, :finished?,
|
||||
prefix: :import, to: :import_state, allow_nil: true
|
||||
delegate :base_dir, :disk_path, to: :storage
|
||||
delegate :no_import?, to: :import_state, allow_nil: true
|
||||
delegate :name, to: :owner, allow_nil: true, prefix: true
|
||||
delegate :members, to: :team, prefix: true
|
||||
|
|
@ -767,10 +767,6 @@ class Project < ApplicationRecord
|
|||
Feature.enabled?(:context_commits, default_enabled: true)
|
||||
end
|
||||
|
||||
def empty_repo?
|
||||
repository.empty?
|
||||
end
|
||||
|
||||
def team
|
||||
@team ||= ProjectTeam.new(self)
|
||||
end
|
||||
|
|
@ -798,18 +794,6 @@ class Project < ApplicationRecord
|
|||
has_root_container_repository_tags?
|
||||
end
|
||||
|
||||
def commit(ref = 'HEAD')
|
||||
repository.commit(ref)
|
||||
end
|
||||
|
||||
def commit_by(oid:)
|
||||
repository.commit_by(oid: oid)
|
||||
end
|
||||
|
||||
def commits_by(oids:)
|
||||
repository.commits_by(oids: oids)
|
||||
end
|
||||
|
||||
# ref can't be HEAD, can only be branch/tag name
|
||||
def latest_successful_build_for_ref(job_name, ref = default_branch)
|
||||
return unless ref
|
||||
|
|
@ -1357,48 +1341,6 @@ class Project < ApplicationRecord
|
|||
services.public_send(hooks_scope).any? # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def valid_repo?
|
||||
repository.exists?
|
||||
rescue
|
||||
errors.add(:path, _('Invalid repository path'))
|
||||
false
|
||||
end
|
||||
|
||||
def url_to_repo
|
||||
gitlab_shell.url_to_repo(full_path)
|
||||
end
|
||||
|
||||
def repo_exists?
|
||||
strong_memoize(:repo_exists) do
|
||||
repository.exists?
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def root_ref?(branch)
|
||||
repository.root_ref == branch
|
||||
end
|
||||
|
||||
def ssh_url_to_repo
|
||||
url_to_repo
|
||||
end
|
||||
|
||||
def http_url_to_repo
|
||||
custom_root = Gitlab::CurrentSettings.custom_http_clone_url_root
|
||||
|
||||
project_url = if custom_root.present?
|
||||
Gitlab::Utils.append_path(
|
||||
custom_root,
|
||||
web_url(only_path: true)
|
||||
)
|
||||
else
|
||||
web_url
|
||||
end
|
||||
|
||||
"#{project_url}.git"
|
||||
end
|
||||
|
||||
# Is overridden in EE
|
||||
def lfs_http_url_to_repo(_)
|
||||
http_url_to_repo
|
||||
|
|
@ -1538,15 +1480,6 @@ class Project < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def default_branch
|
||||
@default_branch ||= repository.root_ref
|
||||
end
|
||||
|
||||
def reload_default_branch
|
||||
@default_branch = nil
|
||||
default_branch
|
||||
end
|
||||
|
||||
def visibility_level_field
|
||||
:visibility_level
|
||||
end
|
||||
|
|
@ -1583,10 +1516,6 @@ class Project < ApplicationRecord
|
|||
create_repository(force: true) unless repository_exists?
|
||||
end
|
||||
|
||||
def repository_exists?
|
||||
!!repository.exists?
|
||||
end
|
||||
|
||||
def wiki_repository_exists?
|
||||
wiki.repository_exists?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class Repository
|
|||
|
||||
include Gitlab::RepositoryCacheAdapter
|
||||
|
||||
attr_accessor :full_path, :disk_path, :project, :repo_type
|
||||
attr_accessor :full_path, :disk_path, :container, :repo_type
|
||||
|
||||
delegate :ref_name_for_sha, to: :raw_repository
|
||||
delegate :bundle_to_disk, to: :raw_repository
|
||||
|
|
@ -67,10 +67,10 @@ class Repository
|
|||
|
||||
MERGED_BRANCH_NAMES_CACHE_DURATION = 10.minutes
|
||||
|
||||
def initialize(full_path, project, disk_path: nil, repo_type: Gitlab::GlRepository::PROJECT)
|
||||
def initialize(full_path, container, disk_path: nil, repo_type: Gitlab::GlRepository::PROJECT)
|
||||
@full_path = full_path
|
||||
@disk_path = disk_path || full_path
|
||||
@project = project
|
||||
@container = container
|
||||
@commit_cache = {}
|
||||
@repo_type = repo_type
|
||||
end
|
||||
|
|
@ -97,7 +97,7 @@ class Repository
|
|||
def path_to_repo
|
||||
@path_to_repo ||=
|
||||
begin
|
||||
storage = Gitlab.config.repositories.storages[project.repository_storage]
|
||||
storage = Gitlab.config.repositories.storages[container.repository_storage]
|
||||
|
||||
File.expand_path(
|
||||
File.join(storage.legacy_disk_path, disk_path + '.git')
|
||||
|
|
@ -130,7 +130,7 @@ class Repository
|
|||
commits = Gitlab::Git::Commit.batch_by_oid(raw_repository, oids)
|
||||
|
||||
if commits.present?
|
||||
Commit.decorate(commits, project)
|
||||
Commit.decorate(commits, container)
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
|
@ -161,14 +161,14 @@ class Repository
|
|||
}
|
||||
|
||||
commits = Gitlab::Git::Commit.where(options)
|
||||
commits = Commit.decorate(commits, project) if commits.present?
|
||||
commits = Commit.decorate(commits, container) if commits.present?
|
||||
|
||||
CommitCollection.new(project, commits, ref)
|
||||
CommitCollection.new(container, commits, ref)
|
||||
end
|
||||
|
||||
def commits_between(from, to)
|
||||
commits = Gitlab::Git::Commit.between(raw_repository, from, to)
|
||||
commits = Commit.decorate(commits, project) if commits.present?
|
||||
commits = Commit.decorate(commits, container) if commits.present?
|
||||
commits
|
||||
end
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ class Repository
|
|||
def new_commits(newrev)
|
||||
commits = raw.new_commits(newrev)
|
||||
|
||||
::Commit.decorate(commits, project)
|
||||
::Commit.decorate(commits, container)
|
||||
end
|
||||
|
||||
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/384
|
||||
|
|
@ -188,7 +188,7 @@ class Repository
|
|||
commits = raw_repository.find_commits_by_message(query, ref, path, limit, offset).map do |c|
|
||||
commit(c)
|
||||
end
|
||||
CommitCollection.new(project, commits, ref)
|
||||
CommitCollection.new(container, commits, ref)
|
||||
end
|
||||
|
||||
def find_branch(name)
|
||||
|
|
@ -281,7 +281,7 @@ class Repository
|
|||
raw_repository.archive_metadata(
|
||||
ref,
|
||||
storage_path,
|
||||
project.path,
|
||||
project&.path,
|
||||
format,
|
||||
append_sha: append_sha,
|
||||
path: path
|
||||
|
|
@ -499,7 +499,7 @@ class Repository
|
|||
end
|
||||
|
||||
def blob_at(sha, path)
|
||||
blob = Blob.decorate(raw_repository.blob_at(sha, path), project)
|
||||
blob = Blob.decorate(raw_repository.blob_at(sha, path), container)
|
||||
|
||||
# Don't attempt to return a special result if there is no blob at all
|
||||
return unless blob
|
||||
|
|
@ -522,7 +522,7 @@ class Repository
|
|||
return [] unless exists?
|
||||
|
||||
raw_repository.batch_blobs(items, blob_size_limit: blob_size_limit).map do |blob|
|
||||
Blob.decorate(blob, project)
|
||||
Blob.decorate(blob, container)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -701,13 +701,13 @@ class Repository
|
|||
commits = raw_repository.list_last_commits_for_tree(sha, path, offset: offset, limit: limit)
|
||||
|
||||
commits.each do |path, commit|
|
||||
commits[path] = ::Commit.new(commit, project)
|
||||
commits[path] = ::Commit.new(commit, container)
|
||||
end
|
||||
end
|
||||
|
||||
def last_commit_for_path(sha, path)
|
||||
commit = raw_repository.last_commit_for_path(sha, path)
|
||||
::Commit.new(commit, project) if commit
|
||||
::Commit.new(commit, container) if commit
|
||||
end
|
||||
|
||||
def last_commit_id_for_path(sha, path)
|
||||
|
|
@ -986,6 +986,7 @@ class Repository
|
|||
# rubocop:disable Gitlab/RailsLogger
|
||||
def async_remove_remote(remote_name)
|
||||
return unless remote_name
|
||||
return unless project
|
||||
|
||||
job_id = RepositoryRemoveRemoteWorker.perform_async(project.id, remote_name)
|
||||
|
||||
|
|
@ -1157,6 +1158,10 @@ class Repository
|
|||
Gitlab::Git::Blob.batch_metadata(raw, references).map { |raw_blob| Blob.decorate(raw_blob) }
|
||||
end
|
||||
|
||||
def project
|
||||
container
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# TODO Genericize finder, later split this on finders by Ref or Oid
|
||||
|
|
@ -1203,10 +1208,10 @@ class Repository
|
|||
end
|
||||
|
||||
def initialize_raw_repository
|
||||
Gitlab::Git::Repository.new(project.repository_storage,
|
||||
Gitlab::Git::Repository.new(container.repository_storage,
|
||||
disk_path + '.git',
|
||||
repo_type.identifier_for_container(project),
|
||||
project.full_path)
|
||||
repo_type.identifier_for_container(container),
|
||||
container.full_path)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1527,6 +1527,13 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def read_only_attribute?(attribute)
|
||||
if Feature.enabled?(:ldap_readonly_attributes, default_enabled: true)
|
||||
enabled = Gitlab::Auth::LDAP::Config.enabled?
|
||||
read_only = attribute.to_sym.in?(UserSyncedAttributesMetadata::SYNCABLE_ATTRIBUTES)
|
||||
|
||||
return true if enabled && read_only
|
||||
end
|
||||
|
||||
user_synced_attributes_metadata&.read_only?(attribute)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module MergeRequests
|
|||
|
||||
diffs.each_batch(of: BATCH_SIZE) do |relation, index|
|
||||
ids = relation.pluck_primary_key.map { |id| [id] }
|
||||
DeleteDiffFilesWorker.bulk_perform_in(index * 5.minutes, ids)
|
||||
DeleteDiffFilesWorker.bulk_perform_in(index * 5.minutes, ids) # rubocop:disable Scalability/BulkPerformWithContext
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module MergeRequests
|
|||
def self.enqueue!
|
||||
ids = MergeRequestDiff.ids_for_external_storage_migration(limit: MAX_JOBS)
|
||||
|
||||
MigrateExternalDiffsWorker.bulk_perform_async(ids.map { |id| [id] })
|
||||
MigrateExternalDiffsWorker.bulk_perform_async(ids.map { |id| [id] }) # rubocop:disable Scalability/BulkPerformWithContext
|
||||
end
|
||||
|
||||
def initialize(merge_request_diff)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class UserProjectAccessChangedService
|
|||
if blocking
|
||||
AuthorizedProjectsWorker.bulk_perform_and_wait(bulk_args)
|
||||
else
|
||||
AuthorizedProjectsWorker.bulk_perform_async(bulk_args)
|
||||
AuthorizedProjectsWorker.bulk_perform_async(bulk_args) # rubocop:disable Scalability/BulkPerformWithContext
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -53,7 +53,11 @@ module Users
|
|||
end
|
||||
|
||||
def discard_read_only_attributes
|
||||
discard_synced_attributes
|
||||
if Feature.enabled?(:ldap_readonly_attributes, default_enabled: true)
|
||||
params.reject! { |key, _| @user.read_only_attribute?(key.to_sym) }
|
||||
else
|
||||
discard_synced_attributes
|
||||
end
|
||||
end
|
||||
|
||||
def discard_synced_attributes
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class AdminEmailWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category_not_owned!
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
module Ci
|
||||
class ArchiveTracesCronWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :continuous_integration
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class ContainerExpirationPolicyWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :container_registry
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class ExpireBuildArtifactsWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :continuous_integration
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ class GitlabUsagePingWorker
|
|||
LEASE_TIMEOUT = 86400
|
||||
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category_not_owned!
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class ImportExportProjectCleanupWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :importers
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class IssueDueSchedulerWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :issue_tracking
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ class IssueDueSchedulerWorker
|
|||
def perform
|
||||
project_ids = Issue.opened.due_tomorrow.group(:project_id).pluck(:project_id).map { |id| [id] }
|
||||
|
||||
MailScheduler::IssueDueWorker.bulk_perform_async(project_ids)
|
||||
MailScheduler::IssueDueWorker.bulk_perform_async(project_ids) # rubocop:disable Scalability/BulkPerformWithContext
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
module Namespaces
|
||||
class PruneAggregationSchedulesWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :source_code_management
|
||||
worker_resource_boundary :cpu
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class PagesDomainRemovalCronWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :pages
|
||||
worker_resource_boundary :cpu
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class PagesDomainSslRenewalCronWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :pages
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class PagesDomainVerificationCronWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :pages
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
module PersonalAccessTokens
|
||||
class ExpiringWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :authentication_and_authorization
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class PipelineScheduleWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :continuous_integration
|
||||
worker_resource_boundary :cpu
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class PruneOldEventsWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category_not_owned!
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
# table.
|
||||
class PruneWebHookLogsWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :integrations
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class RemoveExpiredGroupLinksWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :authentication_and_authorization
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class RemoveExpiredMembersWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :authentication_and_authorization
|
||||
worker_resource_boundary :cpu
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class RemoveUnreferencedLfsObjectsWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :source_code_management
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class RepositoryArchiveCacheWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :source_code_management
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
module RepositoryCheck
|
||||
class DispatchWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
include ::EachShardWorker
|
||||
include ExclusiveLeaseGuard
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class RequestsProfilesWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :source_code_management
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class ScheduleMigrateExternalDiffsWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
include Gitlab::ExclusiveLeaseHelpers
|
||||
|
||||
feature_category :source_code_management
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class StuckCiJobsWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :continuous_integration
|
||||
worker_resource_boundary :cpu
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class StuckImportJobsWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :importers
|
||||
worker_resource_boundary :cpu
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class StuckMergeJobsWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :source_code_management
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class TrendingProjectsWorker
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
|
||||
feature_category :source_code_management
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Introduce license_scanning CI template
|
||||
merge_request: 22773
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Close related GitLab issue on Sentry error resolve
|
||||
merge_request: 23610
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Updated package details page header to begin updating the page design.
|
||||
merge_request: 24055
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Make name, email, and location attributes readonly for LDAP enabled instances
|
||||
merge_request: 24049
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class DefaultLockVersionToZeroForMergeRequests < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
with_lock_retries do
|
||||
change_column_default :merge_requests, :lock_version, from: nil, to: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class DefaultLockVersionToZeroForIssues < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
with_lock_retries do
|
||||
change_column_default :issues, :lock_version, from: nil, to: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class DefaultLockVersionToZeroForEpics < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
with_lock_retries do
|
||||
change_column_default :epics, :lock_version, from: nil, to: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class DefaultLockVersionToZeroForCiBuilds < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
with_lock_retries do
|
||||
change_column_default :ci_builds, :lock_version, from: nil, to: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class DefaultLockVersionToZeroForCiStages < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
with_lock_retries do
|
||||
change_column_default :ci_stages, :lock_version, from: nil, to: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class DefaultLockVersionToZeroForCiPipelines < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
with_lock_retries do
|
||||
change_column_default :ci_pipelines, :lock_version, from: nil, to: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
14
db/schema.rb
14
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2020_01_30_161817) do
|
||||
ActiveRecord::Schema.define(version: 2020_02_03_025821) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_trgm"
|
||||
|
|
@ -659,7 +659,7 @@ ActiveRecord::Schema.define(version: 2020_01_30_161817) do
|
|||
t.text "yaml_variables"
|
||||
t.datetime "queued_at"
|
||||
t.string "token"
|
||||
t.integer "lock_version"
|
||||
t.integer "lock_version", default: 0
|
||||
t.string "coverage_regex"
|
||||
t.integer "auto_canceled_by_id"
|
||||
t.boolean "retried"
|
||||
|
|
@ -835,7 +835,7 @@ ActiveRecord::Schema.define(version: 2020_01_30_161817) do
|
|||
t.datetime "finished_at"
|
||||
t.integer "duration"
|
||||
t.integer "user_id"
|
||||
t.integer "lock_version"
|
||||
t.integer "lock_version", default: 0
|
||||
t.integer "auto_canceled_by_id"
|
||||
t.integer "pipeline_schedule_id"
|
||||
t.integer "source"
|
||||
|
|
@ -949,7 +949,7 @@ ActiveRecord::Schema.define(version: 2020_01_30_161817) do
|
|||
t.datetime "updated_at"
|
||||
t.string "name"
|
||||
t.integer "status"
|
||||
t.integer "lock_version"
|
||||
t.integer "lock_version", default: 0
|
||||
t.integer "position"
|
||||
t.index ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", unique: true
|
||||
t.index ["pipeline_id", "position"], name: "index_ci_stages_on_pipeline_id_and_position"
|
||||
|
|
@ -1523,7 +1523,7 @@ ActiveRecord::Schema.define(version: 2020_01_30_161817) do
|
|||
t.integer "cached_markdown_version"
|
||||
t.integer "updated_by_id"
|
||||
t.integer "last_edited_by_id"
|
||||
t.integer "lock_version"
|
||||
t.integer "lock_version", default: 0
|
||||
t.date "start_date"
|
||||
t.date "end_date"
|
||||
t.datetime "last_edited_at"
|
||||
|
|
@ -2140,7 +2140,7 @@ ActiveRecord::Schema.define(version: 2020_01_30_161817) do
|
|||
t.boolean "confidential", default: false, null: false
|
||||
t.date "due_date"
|
||||
t.integer "moved_to_id"
|
||||
t.integer "lock_version"
|
||||
t.integer "lock_version", default: 0
|
||||
t.text "title_html"
|
||||
t.text "description_html"
|
||||
t.integer "time_estimate"
|
||||
|
|
@ -2562,7 +2562,7 @@ ActiveRecord::Schema.define(version: 2020_01_30_161817) do
|
|||
t.integer "approvals_before_merge"
|
||||
t.string "rebase_commit_sha"
|
||||
t.string "in_progress_merge_commit_sha"
|
||||
t.integer "lock_version"
|
||||
t.integer "lock_version", default: 0
|
||||
t.text "title_html"
|
||||
t.text "description_html"
|
||||
t.integer "time_estimate"
|
||||
|
|
|
|||
|
|
@ -276,6 +276,125 @@ class SomeCrossCuttingConcernWorker
|
|||
end
|
||||
```
|
||||
|
||||
## Worker context
|
||||
|
||||
To have some more information about workers in the logs, we add
|
||||
[metadata to the jobs in the form of an
|
||||
`ApplicationContext`](logging.md#logging-context-metadata-through-rails-or-grape-requests).
|
||||
In most cases, when scheduling a job from a request, this context will
|
||||
already be deducted from the request and added to the scheduled
|
||||
job.
|
||||
|
||||
When a job runs, the context that was active when it was scheduled
|
||||
will be restored. This causes the context to be propagated to any job
|
||||
scheduled from within the running job.
|
||||
|
||||
All this means that in most cases, to add context to jobs, we don't
|
||||
need to do anything.
|
||||
|
||||
There are however some instances when there would be no context
|
||||
present when the job is scheduled, or the context that is present is
|
||||
likely to be incorrect. For these instances we've added rubocop-rules
|
||||
to draw attention and avoid incorrect metadata in our logs.
|
||||
|
||||
As with most our cops, there are perfectly valid reasons for disabling
|
||||
them. In this case it could be that the context from the request is
|
||||
correct. Or maybe you've specified a context already in a way that
|
||||
isn't picked up by the cops. In any case, please leave a code-comment
|
||||
pointing to which context will be used when disabling the cops.
|
||||
|
||||
When you do provide objects to the context, please make sure that the
|
||||
route for namespaces and projects is preloaded. This can be done using
|
||||
the `.with_route` scope defined on all `Routable`s.
|
||||
|
||||
### Cron-Workers
|
||||
|
||||
The context is automatically cleared for workers in the cronjob-queue
|
||||
(which `include CronjobQueue`), even when scheduling them from
|
||||
requests. We do this to avoid incorrect metadata when other jobs are
|
||||
scheduled from the cron-worker.
|
||||
|
||||
Cron-Workers themselves run instance wide, so they aren't scoped to
|
||||
users, namespaces, projects or other resources that should be added to
|
||||
the context.
|
||||
|
||||
However, they often schedule other jobs that _do_ require context.
|
||||
|
||||
That is why there needs to be an indication of context somewhere in
|
||||
the worker. This can be done by using one of the following methods
|
||||
somewhere within the worker:
|
||||
|
||||
1. Wrap the code that schedules jobs in the `with_context` helper:
|
||||
|
||||
```ruby
|
||||
def perform
|
||||
deletion_cutoff = Gitlab::CurrentSettings
|
||||
.deletion_adjourned_period.days.ago.to_date
|
||||
projects = Project.with_route.with_namespace
|
||||
.aimed_for_deletion(deletion_cutoff)
|
||||
|
||||
projects.find_each(batch_size: 100).with_index do |project, index|
|
||||
delay = index * INTERVAL
|
||||
|
||||
with_context(project: project) do
|
||||
AdjournedProjectDeletionWorker.perform_in(delay, project.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
1. Use the a batch scheduling method that provides context:
|
||||
|
||||
```ruby
|
||||
def schedule_projects_in_batch(projects)
|
||||
ProjectImportScheduleWorker.bulk_perform_async_with_contexts(
|
||||
projects,
|
||||
arguments_proc: -> (project) { project.id },
|
||||
context_proc: -> (project) { { project: project } }
|
||||
)
|
||||
end
|
||||
```
|
||||
|
||||
or when scheduling with delays:
|
||||
|
||||
```ruby
|
||||
diffs.each_batch(of: BATCH_SIZE) do |diffs, index|
|
||||
DeleteDiffFilesWorker
|
||||
.bulk_perform_in_with_contexts(index * 5.minutes,
|
||||
diffs,
|
||||
arguments_proc: -> (diff) { diff.id },
|
||||
context_proc: -> (diff) { { project: diff.merge_request.target_project } })
|
||||
end
|
||||
```
|
||||
|
||||
### Jobs scheduled in bulk
|
||||
|
||||
Often, when scheduling jobs in bulk, these jobs should have a separate
|
||||
context rather than the overarching context.
|
||||
|
||||
If that is the case, `bulk_perform_async` can be replaced by the
|
||||
`bulk_perform_async_with_context` helper, and instead of
|
||||
`bulk_perform_in` use `bulk_perform_in_with_context`.
|
||||
|
||||
For example:
|
||||
|
||||
```ruby
|
||||
ProjectImportScheduleWorker.bulk_perform_async_with_contexts(
|
||||
projects,
|
||||
arguments_proc: -> (project) { project.id },
|
||||
context_proc: -> (project) { { project: project } }
|
||||
)
|
||||
```
|
||||
|
||||
Each object from the enumerable in the first argument is yielded into 2
|
||||
blocks:
|
||||
|
||||
The `arguments_proc` which needs to return the list of arguments the
|
||||
job needs to be scheduled with.
|
||||
|
||||
The `context_proc` which needs to return a hash with the context
|
||||
information for the job.
|
||||
|
||||
## Tests
|
||||
|
||||
Each Sidekiq worker must be tested using RSpec, just like any other class. These
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/license_management/
|
||||
#
|
||||
# Configure the scanning tool through the environment variables.
|
||||
# List of the variables: https://gitlab.com/gitlab-org/security-products/license-management#settings
|
||||
# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables
|
||||
# Deprecated: https://gitlab.com/gitlab-org/gitlab/issues/14624
|
||||
# Please, use License-Scanning.gitlab-ci.yml template instead
|
||||
|
||||
variables:
|
||||
LICENSE_MANAGEMENT_SETUP_CMD: '' # If needed, specify a command to setup your environment with a custom package manager.
|
||||
|
|
@ -16,6 +13,7 @@ license_management:
|
|||
SETUP_CMD: $LICENSE_MANAGEMENT_SETUP_CMD
|
||||
allow_failure: true
|
||||
script:
|
||||
- echo "This template is deprecated, please use License-Scanning.gitlab-ci.yml template instead."
|
||||
- /run.sh analyze .
|
||||
artifacts:
|
||||
reports:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/license_compliance/
|
||||
#
|
||||
# Configure the scanning tool through the environment variables.
|
||||
# List of the variables: https://gitlab.com/gitlab-org/security-products/license-management#settings
|
||||
# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables
|
||||
|
||||
variables:
|
||||
LICENSE_MANAGEMENT_SETUP_CMD: '' # If needed, specify a command to setup your environment with a custom package manager.
|
||||
|
||||
license_scanning:
|
||||
stage: test
|
||||
image:
|
||||
name: "registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable"
|
||||
entrypoint: [""]
|
||||
variables:
|
||||
SETUP_CMD: $LICENSE_MANAGEMENT_SETUP_CMD
|
||||
allow_failure: true
|
||||
script:
|
||||
- /run.sh analyze .
|
||||
after_script:
|
||||
- mv gl-license-management-report.json gl-license-scanning-report.json
|
||||
artifacts:
|
||||
reports:
|
||||
license_scanning: gl-license-scanning-report.json
|
||||
dependencies: []
|
||||
only:
|
||||
refs:
|
||||
- branches
|
||||
variables:
|
||||
- $GITLAB_FEATURES =~ /\blicense_management\b/
|
||||
except:
|
||||
variables:
|
||||
- $LICENSE_MANAGEMENT_DISABLED
|
||||
|
|
@ -37,7 +37,9 @@ module Gitlab
|
|||
def ==(other)
|
||||
other.is_a?(self.class) &&
|
||||
x == other.x &&
|
||||
y == other.y
|
||||
y == other.y &&
|
||||
width == other.width &&
|
||||
height == other.height
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ module Gitlab
|
|||
def self.execute_all_async(data)
|
||||
args = files.map { |file| [file, data] }
|
||||
|
||||
FileHookWorker.bulk_perform_async(args)
|
||||
FileHookWorker.bulk_perform_async(args) # rubocop:disable Scalability/BulkPerformWithContext
|
||||
end
|
||||
|
||||
def self.execute(file, data)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ module Gitlab
|
|||
def initialize(commit)
|
||||
@commit = commit
|
||||
|
||||
repo = commit.project.repository.raw_repository
|
||||
repo = commit.container.repository.raw_repository
|
||||
@signature_data = Gitlab::Git::Commit.extract_signature_lazily(repo, commit.sha || commit.id)
|
||||
|
||||
lazy_signature
|
||||
|
|
|
|||
|
|
@ -340,6 +340,9 @@ msgstr ""
|
|||
msgid "%{name}'s avatar"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{numberOfDays} days"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -537,6 +540,9 @@ msgstr ""
|
|||
msgid "+%{extraOptionCount} more"
|
||||
msgstr ""
|
||||
|
||||
msgid "+%{tags} more"
|
||||
msgstr ""
|
||||
|
||||
msgid ", or "
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -2146,6 +2152,9 @@ msgstr ""
|
|||
msgid "Are you sure you want to cancel editing this comment?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to delete %{name}?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to delete these artifacts?"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -2411,6 +2420,9 @@ msgstr ""
|
|||
msgid "Author"
|
||||
msgstr ""
|
||||
|
||||
msgid "Authored %{timeago} by %{author}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Authorization code:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -5903,6 +5915,9 @@ msgstr ""
|
|||
msgid "CycleAnalytics|Tasks by type"
|
||||
msgstr ""
|
||||
|
||||
msgid "CycleAnalytics|The given date range is larger than 180 days"
|
||||
msgstr ""
|
||||
|
||||
msgid "CycleAnalytics|Total days to completion"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -6918,6 +6933,9 @@ msgstr ""
|
|||
msgid "Edit your most recent comment in a thread (from an empty textarea)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edited %{timeago}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Editing"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -13266,6 +13284,18 @@ msgstr ""
|
|||
msgid "PackageRegistry|yarn"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageType|Conan"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageType|Maven"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageType|NPM"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageType|NuGet"
|
||||
msgstr ""
|
||||
|
||||
msgid "Packages"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -18669,6 +18699,9 @@ msgstr ""
|
|||
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
|
||||
msgstr ""
|
||||
|
||||
msgid "The associated issue #%{issueId} has been closed as the error is now resolved."
|
||||
msgstr ""
|
||||
|
||||
msgid "The branch for this project has no active pipeline configuration."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -22155,6 +22188,9 @@ msgstr ""
|
|||
msgid "by"
|
||||
msgstr ""
|
||||
|
||||
msgid "by %{user}"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be changed if a personal project has container registry tags."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@
|
|||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-syntax-import-meta": "^7.2.0",
|
||||
"@babel/preset-env": "^7.6.2",
|
||||
"@gitlab/svgs": "^1.91.0",
|
||||
"@gitlab/ui": "^9.0.0",
|
||||
"@gitlab/svgs": "^1.94.0",
|
||||
"@gitlab/ui": "^9.3.0",
|
||||
"@gitlab/visual-review-tools": "1.5.1",
|
||||
"@sentry/browser": "^5.10.2",
|
||||
"@sourcegraph/code-host-integration": "0.0.21",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../migration_helpers'
|
||||
require_relative '../../code_reuse_helpers'
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Scalability
|
||||
class BulkPerformWithContext < RuboCop::Cop::Cop
|
||||
include RuboCop::MigrationHelpers
|
||||
include RuboCop::CodeReuseHelpers
|
||||
|
||||
MSG = <<~MSG
|
||||
Prefer using `Worker.bulk_perform_async_with_contexts` and
|
||||
`Worker.bulk_perform_in_with_context` over the methods without a context
|
||||
if your worker deals with specific projects or namespaces
|
||||
The context is required to add metadata to our logs.
|
||||
|
||||
If there is already a parent context that will apply to the jobs
|
||||
being scheduled, please disable this cop with a comment explaing which
|
||||
context will be applied.
|
||||
|
||||
Read more about it https://docs.gitlab.com/ee/development/sidekiq_style_guide.html#worker-context
|
||||
MSG
|
||||
|
||||
def_node_matcher :schedules_in_batch_without_context?, <<~PATTERN
|
||||
(send (...) {:bulk_perform_async :bulk_perform_in} _*)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
return if in_migration?(node) || in_spec?(node)
|
||||
return unless schedules_in_batch_without_context?(node)
|
||||
return if name_of_receiver(node) == "BackgroundMigrationWorker"
|
||||
|
||||
add_offense(node, location: :expression)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def in_spec?(node)
|
||||
file_path_for_node(node).end_with?("_spec.rb")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Scalability
|
||||
class CronWorkerContext < RuboCop::Cop::Cop
|
||||
MSG = <<~MSG
|
||||
Manually define an ApplicationContext for cronjob-workers. The context
|
||||
is required to add metadata to our logs.
|
||||
|
||||
If there is no relevant metadata, please disable the cop with a comment
|
||||
explaining this.
|
||||
|
||||
Read more about it https://docs.gitlab.com/ee/development/sidekiq_style_guide.html#worker-context
|
||||
MSG
|
||||
|
||||
def_node_matcher :includes_cronjob_queue?, <<~PATTERN
|
||||
(send nil? :include (const nil? :CronjobQueue))
|
||||
PATTERN
|
||||
|
||||
def_node_search :defines_contexts?, <<~PATTERN
|
||||
(send nil? :with_context _)
|
||||
PATTERN
|
||||
|
||||
def_node_search :schedules_with_batch_context?, <<~PATTERN
|
||||
(send (...) {:bulk_perform_async_with_contexts :bulk_perform_in_with_contexts} (...))
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
return unless includes_cronjob_queue?(node)
|
||||
return if defines_contexts?(node.parent)
|
||||
return if schedules_with_batch_context?(node.parent)
|
||||
|
||||
add_offense(node.arguments.first, location: :expression)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -44,6 +44,8 @@ require_relative 'cop/qa/element_with_pattern'
|
|||
require_relative 'cop/qa/ambiguous_page_object_name'
|
||||
require_relative 'cop/sidekiq_options_queue'
|
||||
require_relative 'cop/scalability/file_uploads'
|
||||
require_relative 'cop/scalability/bulk_perform_with_context'
|
||||
require_relative 'cop/scalability/cron_worker_context'
|
||||
require_relative 'cop/destroy_all'
|
||||
require_relative 'cop/ruby_interpolation_in_translation'
|
||||
require_relative 'code_reuse_helpers'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,18 @@ import Icon from '~/vue_shared/components/icon.vue';
|
|||
import DiffStats from '~/diffs/components/diff_stats.vue';
|
||||
|
||||
describe('diff_stats', () => {
|
||||
it('does not render a group if diffFileLengths is empty', () => {
|
||||
const wrapper = shallowMount(DiffStats, {
|
||||
propsData: {
|
||||
addedLines: 1,
|
||||
removedLines: 2,
|
||||
},
|
||||
});
|
||||
const groups = wrapper.findAll('.diff-stats-group');
|
||||
|
||||
expect(groups.length).toBe(2);
|
||||
});
|
||||
|
||||
it('does not render a group if diffFileLengths is not a number', () => {
|
||||
const wrapper = shallowMount(DiffStats, {
|
||||
propsData: {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import Vuex from 'vuex';
|
||||
import { __ } from '~/locale';
|
||||
import { GlLoadingIcon, GlLink, GlBadge, GlFormInput } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlLink, GlBadge, GlFormInput, GlAlert, GlSprintf } from '@gitlab/ui';
|
||||
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
||||
import Stacktrace from '~/error_tracking/components/stacktrace.vue';
|
||||
import ErrorDetails from '~/error_tracking/components/error_details.vue';
|
||||
|
|
@ -28,7 +28,7 @@ describe('ErrorDetails', () => {
|
|||
|
||||
function mountComponent() {
|
||||
wrapper = shallowMount(ErrorDetails, {
|
||||
stubs: { LoadingButton },
|
||||
stubs: { LoadingButton, GlSprintf },
|
||||
localVue,
|
||||
store,
|
||||
mocks,
|
||||
|
|
@ -62,7 +62,7 @@ describe('ErrorDetails', () => {
|
|||
startPollingDetails: () => {},
|
||||
startPollingStacktrace: () => {},
|
||||
updateIgnoreStatus: jest.fn(),
|
||||
updateResolveStatus: jest.fn(),
|
||||
updateResolveStatus: jest.fn().mockResolvedValue({ closed_issue_iid: 1 }),
|
||||
};
|
||||
|
||||
getters = {
|
||||
|
|
@ -313,6 +313,20 @@ describe('ErrorDetails', () => {
|
|||
expect.objectContaining({ status: errorStatus.UNRESOLVED }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should show alert with closed issueId', () => {
|
||||
const findAlert = () => wrapper.find(GlAlert);
|
||||
const closedIssueId = 123;
|
||||
wrapper.setData({
|
||||
isAlertVisible: true,
|
||||
closedIssueId,
|
||||
});
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(findAlert().exists()).toBe(true);
|
||||
expect(findAlert().text()).toContain(`#${closedIssueId}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ exports[`User Operation confirmation modal renders modal with form included 1`]
|
|||
modalid="user-operation-modal"
|
||||
ok-title="action"
|
||||
ok-variant="warning"
|
||||
size="md"
|
||||
title="title"
|
||||
titletag="h4"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ exports[`self monitor component When the self monitor project has not been creat
|
|||
modalid="delete-self-monitor-modal"
|
||||
ok-title="Delete project"
|
||||
ok-variant="danger"
|
||||
size="md"
|
||||
title="Disable self monitoring?"
|
||||
titletag="h4"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -3,20 +3,34 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Diff::Formatters::ImageFormatter do
|
||||
it_behaves_like "position formatter" do
|
||||
let(:base_attrs) do
|
||||
{
|
||||
base_sha: 123,
|
||||
start_sha: 456,
|
||||
head_sha: 789,
|
||||
old_path: 'old_image.png',
|
||||
new_path: 'new_image.png',
|
||||
position_type: 'image'
|
||||
}
|
||||
end
|
||||
let(:base_attrs) do
|
||||
{
|
||||
base_sha: 123,
|
||||
start_sha: 456,
|
||||
head_sha: 789,
|
||||
old_path: 'old_image.png',
|
||||
new_path: 'new_image.png',
|
||||
position_type: 'image'
|
||||
}
|
||||
end
|
||||
|
||||
let(:attrs) do
|
||||
base_attrs.merge(width: 100, height: 100, x: 1, y: 2)
|
||||
let(:attrs) do
|
||||
base_attrs.merge(width: 100, height: 100, x: 1, y: 2)
|
||||
end
|
||||
|
||||
it_behaves_like 'position formatter'
|
||||
|
||||
describe '#==' do
|
||||
subject { described_class.new(attrs) }
|
||||
|
||||
it { is_expected.to eq(subject) }
|
||||
|
||||
[:width, :height, :x, :y].each do |attr|
|
||||
let(:other_formatter) do
|
||||
described_class.new(attrs.merge(attr => 9))
|
||||
end
|
||||
|
||||
it { is_expected.not_to eq(other_formatter) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -106,6 +106,14 @@ describe Project do
|
|||
it { is_expected.to have_many(:sourced_pipelines) }
|
||||
it { is_expected.to have_many(:source_pipelines) }
|
||||
|
||||
it_behaves_like 'model with repository' do
|
||||
let_it_be(:container) { create(:project, :repository, path: 'somewhere') }
|
||||
let(:stubbed_container) { build_stubbed(:project) }
|
||||
let(:expected_full_path) { "#{container.namespace.full_path}/somewhere" }
|
||||
let(:expected_repository_klass) { Repository }
|
||||
let(:expected_storage_klass) { Storage::Hashed }
|
||||
end
|
||||
|
||||
it 'has an inverse relationship with merge requests' do
|
||||
expect(described_class.reflect_on_association(:merge_requests).has_inverse?).to eq(:target_project)
|
||||
end
|
||||
|
|
@ -510,7 +518,6 @@ describe Project do
|
|||
|
||||
describe 'Respond to' do
|
||||
it { is_expected.to respond_to(:url_to_repo) }
|
||||
it { is_expected.to respond_to(:repo_exists?) }
|
||||
it { is_expected.to respond_to(:execute_hooks) }
|
||||
it { is_expected.to respond_to(:owner) }
|
||||
it { is_expected.to respond_to(:path_with_namespace) }
|
||||
|
|
@ -664,44 +671,6 @@ describe Project do
|
|||
expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
|
||||
end
|
||||
|
||||
describe "#web_url" do
|
||||
let(:project) { create(:project, path: "somewhere") }
|
||||
|
||||
context 'when given the only_path option' do
|
||||
subject { project.web_url(only_path: only_path) }
|
||||
|
||||
context 'when only_path is false' do
|
||||
let(:only_path) { false }
|
||||
|
||||
it 'returns the full web URL for this repo' do
|
||||
expect(subject).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when only_path is true' do
|
||||
let(:only_path) { true }
|
||||
|
||||
it 'returns the relative web URL for this repo' do
|
||||
expect(subject).to eq("/#{project.namespace.full_path}/somewhere")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when only_path is nil' do
|
||||
let(:only_path) { nil }
|
||||
|
||||
it 'returns the full web URL for this repo' do
|
||||
expect(subject).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not given the only_path option' do
|
||||
it 'returns the full web URL for this repo' do
|
||||
expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#readme_url" do
|
||||
context 'with a non-existing repository' do
|
||||
let(:project) { create(:project) }
|
||||
|
|
@ -931,14 +900,6 @@ describe Project do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#repository' do
|
||||
let(:project) { create(:project, :repository) }
|
||||
|
||||
it 'returns valid repo' do
|
||||
expect(project.repository).to be_kind_of(Repository)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#default_issues_tracker?' do
|
||||
it "is true if used internal tracker" do
|
||||
project = build(:project)
|
||||
|
|
@ -954,24 +915,6 @@ describe Project do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#empty_repo?' do
|
||||
context 'when the repo does not exist' do
|
||||
let(:project) { build_stubbed(:project) }
|
||||
|
||||
it 'returns true' do
|
||||
expect(project.empty_repo?).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the repo exists' do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:empty_project) { create(:project, :empty_repo) }
|
||||
|
||||
it { expect(empty_project.empty_repo?).to be(true) }
|
||||
it { expect(project.empty_repo?).to be(false) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#external_issue_tracker' do
|
||||
let(:project) { create(:project) }
|
||||
let(:ext_project) { create(:redmine_project) }
|
||||
|
|
@ -3406,59 +3349,6 @@ describe Project do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#http_url_to_repo' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
context 'when a custom HTTP clone URL root is not set' do
|
||||
it 'returns the url to the repo without a username' do
|
||||
expect(project.http_url_to_repo).to eq("#{project.web_url}.git")
|
||||
expect(project.http_url_to_repo).not_to include('@')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a custom HTTP clone URL root is set' do
|
||||
before do
|
||||
stub_application_setting(custom_http_clone_url_root: custom_http_clone_url_root)
|
||||
end
|
||||
|
||||
context 'when custom HTTP clone URL root has a relative URL root' do
|
||||
context 'when custom HTTP clone URL root ends with a slash' do
|
||||
let(:custom_http_clone_url_root) { 'https://git.example.com:51234/mygitlab/' }
|
||||
|
||||
it 'returns the url to the repo, with the root replaced with the custom one' do
|
||||
expect(project.http_url_to_repo).to eq("https://git.example.com:51234/mygitlab/#{project.full_path}.git")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom HTTP clone URL root does not end with a slash' do
|
||||
let(:custom_http_clone_url_root) { 'https://git.example.com:51234/mygitlab' }
|
||||
|
||||
it 'returns the url to the repo, with the root replaced with the custom one' do
|
||||
expect(project.http_url_to_repo).to eq("https://git.example.com:51234/mygitlab/#{project.full_path}.git")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom HTTP clone URL root does not have a relative URL root' do
|
||||
context 'when custom HTTP clone URL root ends with a slash' do
|
||||
let(:custom_http_clone_url_root) { 'https://git.example.com:51234/' }
|
||||
|
||||
it 'returns the url to the repo, with the root replaced with the custom one' do
|
||||
expect(project.http_url_to_repo).to eq("https://git.example.com:51234/#{project.full_path}.git")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom HTTP clone URL root does not end with a slash' do
|
||||
let(:custom_http_clone_url_root) { 'https://git.example.com:51234' }
|
||||
|
||||
it 'returns the url to the repo, with the root replaced with the custom one' do
|
||||
expect(project.http_url_to_repo).to eq("https://git.example.com:51234/#{project.full_path}.git")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#lfs_http_url_to_repo' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
|
|
@ -5054,16 +4944,6 @@ describe Project do
|
|||
end
|
||||
end
|
||||
|
||||
context '#commits_by' do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:commits) { project.repository.commits('HEAD', limit: 3).commits }
|
||||
let(:commit_shas) { commits.map(&:id) }
|
||||
|
||||
it 'retrieves several commits from the repository by oid' do
|
||||
expect(project.commits_by(oids: commit_shas)).to eq commits
|
||||
end
|
||||
end
|
||||
|
||||
context '#members_among' do
|
||||
let(:users) { create_list(:user, 3) }
|
||||
|
||||
|
|
|
|||
|
|
@ -4084,4 +4084,46 @@ describe User, :do_not_mock_admin_mode do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#read_only_attribute?' do
|
||||
context 'when LDAP server is enabled' do
|
||||
before do
|
||||
allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
|
||||
end
|
||||
|
||||
%i[name email location].each do |attribute|
|
||||
it "is true for #{attribute}" do
|
||||
expect(subject.read_only_attribute?(attribute)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'and ldap_readonly_attributes feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ldap_readonly_attributes: false)
|
||||
end
|
||||
|
||||
%i[name email location].each do |attribute|
|
||||
it "is false" do
|
||||
expect(subject.read_only_attribute?(attribute)).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when synced attributes metadata is present' do
|
||||
it 'delegates to synced_attributes_metadata' do
|
||||
subject.build_user_synced_attributes_metadata
|
||||
|
||||
expect(subject.build_user_synced_attributes_metadata)
|
||||
.to receive(:read_only?).with(:email).and_return('return-value')
|
||||
expect(subject.read_only_attribute?(:email)).to eq('return-value')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when synced attributes metadata is present' do
|
||||
it 'is false for any attribute' do
|
||||
expect(subject.read_only_attribute?(:email)).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'rubocop'
|
||||
require_relative '../../../support/helpers/expect_offense'
|
||||
require_relative '../../../../rubocop/cop/scalability/bulk_perform_with_context'
|
||||
|
||||
describe RuboCop::Cop::Scalability::BulkPerformWithContext do
|
||||
include CopHelper
|
||||
include ExpectOffense
|
||||
|
||||
subject(:cop) { described_class.new }
|
||||
|
||||
it "adds an offense when calling bulk_perform_async" do
|
||||
inspect_source(<<~CODE.strip_indent)
|
||||
Worker.bulk_perform_async(args)
|
||||
CODE
|
||||
|
||||
expect(cop.offenses.size).to eq(1)
|
||||
end
|
||||
|
||||
it "adds an offense when calling bulk_perform_in" do
|
||||
inspect_source(<<~CODE.strip_indent)
|
||||
diffs.each_batch(of: BATCH_SIZE) do |relation, index|
|
||||
ids = relation.pluck_primary_key.map { |id| [id] }
|
||||
DeleteDiffFilesWorker.bulk_perform_in(index * 5.minutes, ids)
|
||||
end
|
||||
CODE
|
||||
|
||||
expect(cop.offenses.size).to eq(1)
|
||||
end
|
||||
|
||||
it "does not add an offense for migrations" do
|
||||
allow(cop).to receive(:in_migration?).and_return(true)
|
||||
|
||||
inspect_source(<<~CODE.strip_indent)
|
||||
Worker.bulk_perform_in(args)
|
||||
CODE
|
||||
|
||||
expect(cop.offenses.size).to eq(0)
|
||||
end
|
||||
|
||||
it "does not add an offence for specs" do
|
||||
allow(cop).to receive(:in_spec?).and_return(true)
|
||||
|
||||
inspect_source(<<~CODE.strip_indent)
|
||||
Worker.bulk_perform_in(args)
|
||||
CODE
|
||||
|
||||
expect(cop.offenses.size).to eq(0)
|
||||
end
|
||||
|
||||
it "does not add an offense for scheduling BackgroundMigrations" do
|
||||
inspect_source(<<~CODE.strip_indent)
|
||||
BackgroundMigrationWorker.bulk_perform_in(args)
|
||||
CODE
|
||||
|
||||
expect(cop.offenses.size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'rubocop'
|
||||
require_relative '../../../support/helpers/expect_offense'
|
||||
require_relative '../../../../rubocop/cop/scalability/cron_worker_context'
|
||||
|
||||
describe RuboCop::Cop::Scalability::CronWorkerContext do
|
||||
include CopHelper
|
||||
include ExpectOffense
|
||||
|
||||
subject(:cop) { described_class.new }
|
||||
|
||||
it 'adds an offense when including CronjobQueue' do
|
||||
inspect_source(<<~CODE.strip_indent)
|
||||
class SomeWorker
|
||||
include CronjobQueue
|
||||
end
|
||||
CODE
|
||||
|
||||
expect(cop.offenses.size).to eq(1)
|
||||
end
|
||||
|
||||
it 'does not add offenses for other workers' do
|
||||
expect_no_offenses(<<~CODE.strip_indent)
|
||||
class SomeWorker
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
it 'does not add an offense when the class defines a context' do
|
||||
expect_no_offenses(<<~CODE.strip_indent)
|
||||
class SomeWorker
|
||||
include CronjobQueue
|
||||
|
||||
with_context user: 'bla'
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
it 'does not add an offense when the worker calls `with_context`' do
|
||||
expect_no_offenses(<<~CODE.strip_indent)
|
||||
class SomeWorker
|
||||
include CronjobQueue
|
||||
|
||||
def perform
|
||||
with_context(user: 'bla') do
|
||||
# more work
|
||||
end
|
||||
end
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
it 'does not add an offense when the worker calls `bulk_perform_async_with_contexts`' do
|
||||
expect_no_offenses(<<~CODE.strip_indent)
|
||||
class SomeWorker
|
||||
include CronjobQueue
|
||||
|
||||
def perform
|
||||
SomeOtherWorker.bulk_perform_async_with_contexts(contexts_for_arguments)
|
||||
end
|
||||
end
|
||||
CODE
|
||||
end
|
||||
|
||||
it 'does not add an offense when the worker calls `bulk_perform_in_with_contexts`' do
|
||||
expect_no_offenses(<<~CODE.strip_indent)
|
||||
class SomeWorker
|
||||
include CronjobQueue
|
||||
|
||||
def perform
|
||||
SomeOtherWorker.bulk_perform_in_with_contexts(contexts_for_arguments)
|
||||
end
|
||||
end
|
||||
CODE
|
||||
end
|
||||
end
|
||||
|
|
@ -55,6 +55,15 @@ describe Users::UpdateService do
|
|||
expect(result[:message]).to eq("Emoji is not included in the list")
|
||||
end
|
||||
|
||||
it 'ignores read-only attributes' do
|
||||
allow(user).to receive(:read_only_attribute?).with(:name).and_return(true)
|
||||
|
||||
expect do
|
||||
update_user(user, name: 'changed' + user.name)
|
||||
user.reload
|
||||
end.not_to change { user.name }
|
||||
end
|
||||
|
||||
def update_user(user, opts)
|
||||
described_class.new(user, opts.merge(user: user)).execute
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,171 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'model with repository' do
|
||||
describe '#commits_by' do
|
||||
let(:commits) { container.repository.commits('HEAD', limit: 3).commits }
|
||||
let(:commit_shas) { commits.map(&:id) }
|
||||
|
||||
it 'retrieves several commits from the repository by oid' do
|
||||
expect(container.commits_by(oids: commit_shas)).to eq commits
|
||||
end
|
||||
end
|
||||
|
||||
describe "#web_url" do
|
||||
context 'when given the only_path option' do
|
||||
subject { container.web_url(only_path: only_path) }
|
||||
|
||||
context 'when only_path is false' do
|
||||
let(:only_path) { false }
|
||||
|
||||
it 'returns the full web URL for this repo' do
|
||||
expect(subject).to eq("#{Gitlab.config.gitlab.url}/#{expected_full_path}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when only_path is true' do
|
||||
let(:only_path) { true }
|
||||
|
||||
it 'returns the relative web URL for this repo' do
|
||||
expect(subject).to eq("/#{expected_full_path}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when only_path is nil' do
|
||||
let(:only_path) { nil }
|
||||
|
||||
it 'returns the full web URL for this repo' do
|
||||
expect(subject).to eq("#{Gitlab.config.gitlab.url}/#{expected_full_path}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not given the only_path option' do
|
||||
it 'returns the full web URL for this repo' do
|
||||
expect(container.web_url).to eq("#{Gitlab.config.gitlab.url}/#{expected_full_path}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ssh_url_to_repo' do
|
||||
it 'returns container ssh address' do
|
||||
expect(container.ssh_url_to_repo).to eq container.url_to_repo
|
||||
end
|
||||
end
|
||||
|
||||
describe '#http_url_to_repo' do
|
||||
subject { container.http_url_to_repo }
|
||||
|
||||
context 'when a custom HTTP clone URL root is not set' do
|
||||
it 'returns the url to the repo without a username' do
|
||||
expect(subject).to eq("#{container.web_url}.git")
|
||||
expect(subject).not_to include('@')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a custom HTTP clone URL root is set' do
|
||||
before do
|
||||
stub_application_setting(custom_http_clone_url_root: custom_http_clone_url_root)
|
||||
end
|
||||
|
||||
context 'when custom HTTP clone URL root has a relative URL root' do
|
||||
context 'when custom HTTP clone URL root ends with a slash' do
|
||||
let(:custom_http_clone_url_root) { 'https://git.example.com:51234/mygitlab/' }
|
||||
|
||||
it 'returns the url to the repo, with the root replaced with the custom one' do
|
||||
expect(subject).to eq("#{custom_http_clone_url_root}#{expected_full_path}.git")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom HTTP clone URL root does not end with a slash' do
|
||||
let(:custom_http_clone_url_root) { 'https://git.example.com:51234/mygitlab' }
|
||||
|
||||
it 'returns the url to the repo, with the root replaced with the custom one' do
|
||||
expect(subject).to eq("#{custom_http_clone_url_root}/#{expected_full_path}.git")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom HTTP clone URL root does not have a relative URL root' do
|
||||
context 'when custom HTTP clone URL root ends with a slash' do
|
||||
let(:custom_http_clone_url_root) { 'https://git.example.com:51234/' }
|
||||
|
||||
it 'returns the url to the repo, with the root replaced with the custom one' do
|
||||
expect(subject).to eq("#{custom_http_clone_url_root}#{expected_full_path}.git")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when custom HTTP clone URL root does not end with a slash' do
|
||||
let(:custom_http_clone_url_root) { 'https://git.example.com:51234' }
|
||||
|
||||
it 'returns the url to the repo, with the root replaced with the custom one' do
|
||||
expect(subject).to eq("#{custom_http_clone_url_root}/#{expected_full_path}.git")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#repository' do
|
||||
it 'returns valid repo' do
|
||||
expect(container.repository).to be_kind_of(expected_repository_klass)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#storage' do
|
||||
it 'returns valid storage' do
|
||||
expect(container.storage).to be_kind_of(expected_storage_klass)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#full_path' do
|
||||
it 'returns valid full_path' do
|
||||
expect(container.full_path).to eq(expected_full_path)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#empty_repo?' do
|
||||
context 'when the repo does not exist' do
|
||||
it 'returns true' do
|
||||
expect(stubbed_container.empty_repo?).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the repo exists' do
|
||||
it { expect(container.empty_repo?).to be(false) }
|
||||
|
||||
it 'returns true when repository is empty' do
|
||||
allow(container.repository).to receive(:empty?).and_return(true)
|
||||
|
||||
expect(container.empty_repo?).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid_repo?' do
|
||||
it { expect(stubbed_container.valid_repo?).to be(false)}
|
||||
it { expect(container.valid_repo?).to be(true) }
|
||||
end
|
||||
|
||||
describe '#repository_exists?' do
|
||||
it { expect(stubbed_container.repository_exists?).to be(false)}
|
||||
it { expect(container.repository_exists?).to be(true) }
|
||||
end
|
||||
|
||||
describe '#repo_exists?' do
|
||||
it { expect(stubbed_container.repo_exists?).to be(false)}
|
||||
it { expect(container.repo_exists?).to be(true) }
|
||||
end
|
||||
|
||||
describe '#root_ref' do
|
||||
let(:root_ref) { container.repository.root_ref }
|
||||
|
||||
it { expect(container.root_ref?(root_ref)).to be(true) }
|
||||
it { expect(container.root_ref?('HEAD')).to be(false) }
|
||||
it { expect(container.root_ref?('foo')).to be(false) }
|
||||
end
|
||||
|
||||
describe 'Respond to' do
|
||||
it { is_expected.to respond_to(:base_dir) }
|
||||
it { is_expected.to respond_to(:disk_path) }
|
||||
end
|
||||
end
|
||||
|
|
@ -10,7 +10,7 @@ describe CronjobQueue do
|
|||
end
|
||||
|
||||
include ApplicationWorker
|
||||
include CronjobQueue
|
||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
16
yarn.lock
16
yarn.lock
|
|
@ -736,15 +736,15 @@
|
|||
dependencies:
|
||||
vue-eslint-parser "^6.0.4"
|
||||
|
||||
"@gitlab/svgs@^1.91.0":
|
||||
version "1.91.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.91.0.tgz#cf7b28e43779a929a438dcc0c51f39f92551e58e"
|
||||
integrity sha512-Sz8aaaNnUUtlrk/FPf6FNceu4XsDLBh6g/c6nmzDVGF9kHrfiXfWL0tYAax8vhkrld5vewGHE0bRpq2ZILq0pw==
|
||||
"@gitlab/svgs@^1.94.0":
|
||||
version "1.94.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.94.0.tgz#d6a39f982811f82d942692a91bf2678961752eba"
|
||||
integrity sha512-lB7HTVsNPBLUEgNUXLLC4V/XJsWg7aSO7RBp6cuuL3n6fUS9VGfELH9aBnuPJglTHddktcnElkZ3S54XI8kYHw==
|
||||
|
||||
"@gitlab/ui@^9.0.0":
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.0.0.tgz#16d637f47ba0537100fd1c6d452b56174b50171b"
|
||||
integrity sha512-OfP8UAticpqKkqbPBZ+7bbCBsd9Fxq3eL55Uq5nEwxJ8gGXm+nSc+HFnbzX/ryv0iz5+7nCI4DfIfgy9E4QAeQ==
|
||||
"@gitlab/ui@^9.3.0":
|
||||
version "9.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.3.0.tgz#851b4246c2e661a5a343184f74e0448c597ce28c"
|
||||
integrity sha512-DB9Q8XDLfn3Ui6EfYTVnmHVYPwbukocYTWL+uD6zN3leiamYQqaoYGmtcrXk9oSiAyuJYwaJlCzlblG1GPwnfw==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue