Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f931527bc5
commit
2dedd78ef5
|
@ -448,7 +448,9 @@ db:backup_and_restore:
|
||||||
- date
|
- date
|
||||||
- bundle exec rake gitlab:backup:restore
|
- bundle exec rake gitlab:backup:restore
|
||||||
rules:
|
rules:
|
||||||
- changes: ["lib/backup/**/*"]
|
- changes:
|
||||||
|
- "lib/backup/**/*"
|
||||||
|
- "lib/tasks/gitlab/backup.rake"
|
||||||
|
|
||||||
rspec:deprecations:
|
rspec:deprecations:
|
||||||
extends:
|
extends:
|
||||||
|
|
|
@ -46,7 +46,7 @@ review-build-cng:
|
||||||
variables:
|
variables:
|
||||||
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
|
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
|
||||||
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
|
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
|
||||||
GITLAB_HELM_CHART_REF: "v4.12.0"
|
GITLAB_HELM_CHART_REF: "v5.1.0"
|
||||||
environment:
|
environment:
|
||||||
name: review/${CI_COMMIT_REF_SLUG}${FREQUENCY}
|
name: review/${CI_COMMIT_REF_SLUG}${FREQUENCY}
|
||||||
url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}
|
url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -487,7 +487,7 @@ gem 'toml-rb', '~> 2.0'
|
||||||
gem 'flipper', '~> 0.21.0'
|
gem 'flipper', '~> 0.21.0'
|
||||||
gem 'flipper-active_record', '~> 0.21.0'
|
gem 'flipper-active_record', '~> 0.21.0'
|
||||||
gem 'flipper-active_support_cache_store', '~> 0.21.0'
|
gem 'flipper-active_support_cache_store', '~> 0.21.0'
|
||||||
gem 'unleash', '~> 0.1.5'
|
gem 'unleash', '~> 3.2.2'
|
||||||
gem 'gitlab-experiment', '~> 0.6.2'
|
gem 'gitlab-experiment', '~> 0.6.2'
|
||||||
|
|
||||||
# Structured logging
|
# Structured logging
|
||||||
|
|
|
@ -1317,7 +1317,7 @@ GEM
|
||||||
unicode-display_width (1.7.0)
|
unicode-display_width (1.7.0)
|
||||||
unicode_utils (1.4.0)
|
unicode_utils (1.4.0)
|
||||||
uniform_notifier (1.13.0)
|
uniform_notifier (1.13.0)
|
||||||
unleash (0.1.5)
|
unleash (3.2.2)
|
||||||
murmurhash3 (~> 0.1.6)
|
murmurhash3 (~> 0.1.6)
|
||||||
unparser (0.6.0)
|
unparser (0.6.0)
|
||||||
diff-lcs (~> 1.3)
|
diff-lcs (~> 1.3)
|
||||||
|
@ -1643,7 +1643,7 @@ DEPENDENCIES
|
||||||
truncato (~> 0.7.11)
|
truncato (~> 0.7.11)
|
||||||
u2f (~> 0.2.1)
|
u2f (~> 0.2.1)
|
||||||
unf (~> 0.1.4)
|
unf (~> 0.1.4)
|
||||||
unleash (~> 0.1.5)
|
unleash (~> 3.2.2)
|
||||||
valid_email (~> 0.1)
|
valid_email (~> 0.1)
|
||||||
validates_hostname (~> 1.0.11)
|
validates_hostname (~> 1.0.11)
|
||||||
version_sorter (~> 2.2.4)
|
version_sorter (~> 2.2.4)
|
||||||
|
|
|
@ -25,6 +25,8 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
oldIid: null,
|
||||||
|
isEditing: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -72,6 +74,15 @@ export default {
|
||||||
return this.labelsFetchPath || projectLabelsFetchPath;
|
return this.labelsFetchPath || projectLabelsFetchPath;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
activeBoardItem(_, oldVal) {
|
||||||
|
if (this.isEditing) {
|
||||||
|
this.oldIid = oldVal.iid;
|
||||||
|
} else {
|
||||||
|
this.oldIid = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['setActiveBoardItemLabels', 'setError']),
|
...mapActions(['setActiveBoardItemLabels', 'setError']),
|
||||||
async setLabels(payload) {
|
async setLabels(payload) {
|
||||||
|
@ -84,8 +95,14 @@ export default {
|
||||||
.filter((label) => !payload.find((selected) => selected.id === label.id))
|
.filter((label) => !payload.find((selected) => selected.id === label.id))
|
||||||
.map((label) => label.id);
|
.map((label) => label.id);
|
||||||
|
|
||||||
const input = { addLabelIds, removeLabelIds, projectPath: this.projectPathForActiveIssue };
|
const input = {
|
||||||
|
addLabelIds,
|
||||||
|
removeLabelIds,
|
||||||
|
projectPath: this.projectPathForActiveIssue,
|
||||||
|
iid: this.oldIid,
|
||||||
|
};
|
||||||
await this.setActiveBoardItemLabels(input);
|
await this.setActiveBoardItemLabels(input);
|
||||||
|
this.oldIid = null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setError({ error: e, message: __('An error occurred while updating labels.') });
|
this.setError({ error: e, message: __('An error occurred while updating labels.') });
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -115,6 +132,8 @@ export default {
|
||||||
:title="__('Labels')"
|
:title="__('Labels')"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
data-testid="sidebar-labels"
|
data-testid="sidebar-labels"
|
||||||
|
@open="isEditing = true"
|
||||||
|
@close="isEditing = false"
|
||||||
>
|
>
|
||||||
<template #collapsed>
|
<template #collapsed>
|
||||||
<gl-label
|
<gl-label
|
||||||
|
|
|
@ -575,7 +575,7 @@ export default {
|
||||||
mutation: issueSetLabelsMutation,
|
mutation: issueSetLabelsMutation,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
iid: String(activeBoardItem.iid),
|
iid: input.iid || String(activeBoardItem.iid),
|
||||||
addLabelIds: input.addLabelIds ?? [],
|
addLabelIds: input.addLabelIds ?? [],
|
||||||
removeLabelIds: input.removeLabelIds ?? [],
|
removeLabelIds: input.removeLabelIds ?? [],
|
||||||
projectPath: input.projectPath,
|
projectPath: input.projectPath,
|
||||||
|
@ -588,7 +588,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
commit(types.UPDATE_BOARD_ITEM_BY_ID, {
|
commit(types.UPDATE_BOARD_ITEM_BY_ID, {
|
||||||
itemId: activeBoardItem.id,
|
itemId: getIdFromGraphQLId(data.updateIssue?.issue?.id) || activeBoardItem.id,
|
||||||
prop: 'labels',
|
prop: 'labels',
|
||||||
value: data.updateIssue.issue.labels.nodes,
|
value: data.updateIssue.issue.labels.nodes,
|
||||||
});
|
});
|
||||||
|
|
|
@ -175,7 +175,7 @@ export default {
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<blob-edit
|
<blob-edit
|
||||||
v-if="!isBinary"
|
:show-edit-button="!isBinary"
|
||||||
:edit-path="blobInfo.editBlobPath"
|
:edit-path="blobInfo.editBlobPath"
|
||||||
:web-ide-path="blobInfo.ideEditPath"
|
:web-ide-path="blobInfo.ideEditPath"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -15,6 +15,10 @@ export default {
|
||||||
},
|
},
|
||||||
mixins: [glFeatureFlagsMixin()],
|
mixins: [glFeatureFlagsMixin()],
|
||||||
props: {
|
props: {
|
||||||
|
showEditButton: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
editPath: {
|
editPath: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -30,17 +34,31 @@ export default {
|
||||||
<template>
|
<template>
|
||||||
<web-ide-link
|
<web-ide-link
|
||||||
v-if="glFeatures.consolidatedEditButton"
|
v-if="glFeatures.consolidatedEditButton"
|
||||||
|
:show-edit-button="showEditButton"
|
||||||
class="gl-mr-3"
|
class="gl-mr-3"
|
||||||
:edit-url="editPath"
|
:edit-url="editPath"
|
||||||
:web-ide-url="webIdePath"
|
:web-ide-url="webIdePath"
|
||||||
:is-blob="true"
|
:is-blob="true"
|
||||||
/>
|
/>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<gl-button class="gl-mr-2" category="primary" variant="confirm" :href="editPath">
|
<gl-button
|
||||||
|
v-if="showEditButton"
|
||||||
|
class="gl-mr-2"
|
||||||
|
category="primary"
|
||||||
|
variant="confirm"
|
||||||
|
:href="editPath"
|
||||||
|
data-testid="edit"
|
||||||
|
>
|
||||||
{{ $options.i18n.edit }}
|
{{ $options.i18n.edit }}
|
||||||
</gl-button>
|
</gl-button>
|
||||||
|
|
||||||
<gl-button class="gl-mr-3" category="primary" variant="confirm" :href="webIdePath">
|
<gl-button
|
||||||
|
class="gl-mr-3"
|
||||||
|
category="primary"
|
||||||
|
variant="confirm"
|
||||||
|
:href="webIdePath"
|
||||||
|
data-testid="web-ide"
|
||||||
|
>
|
||||||
{{ $options.i18n.webIde }}
|
{{ $options.i18n.webIde }}
|
||||||
</gl-button>
|
</gl-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,21 @@ module Ci
|
||||||
belongs_to :processable, class_name: 'Ci::Processable', foreign_key: 'build_id', inverse_of: :resource
|
belongs_to :processable, class_name: 'Ci::Processable', foreign_key: 'build_id', inverse_of: :resource
|
||||||
|
|
||||||
scope :free, -> { where(processable: nil) }
|
scope :free, -> { where(processable: nil) }
|
||||||
|
scope :retained, -> { where.not(processable: nil) }
|
||||||
scope :retained_by, -> (processable) { where(processable: processable) }
|
scope :retained_by, -> (processable) { where(processable: processable) }
|
||||||
|
|
||||||
|
class << self
|
||||||
|
# In some cases, state machine hooks in `Ci::Build` are skipped
|
||||||
|
# even if the job status transitions to a complete state.
|
||||||
|
# For example, `Ci::Build#doom!` (a.k.a `data_integrity_failure`) doesn't execute state machine hooks.
|
||||||
|
# To handle these edge cases, we check the staleness of the jobs that currently
|
||||||
|
# assigned to the resources, and release if it's stale.
|
||||||
|
# See https://gitlab.com/gitlab-org/gitlab/-/issues/335537#note_632925914 for more information.
|
||||||
|
def stale_processables
|
||||||
|
Ci::Processable.where(id: retained.select(:build_id))
|
||||||
|
.complete
|
||||||
|
.updated_at_before(5.minutes.ago)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,6 +58,7 @@ class CommitStatus < ApplicationRecord
|
||||||
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
|
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
|
||||||
scope :eager_load_pipeline, -> { eager_load(:pipeline, project: { namespace: :route }) }
|
scope :eager_load_pipeline, -> { eager_load(:pipeline, project: { namespace: :route }) }
|
||||||
scope :with_pipeline, -> { joins(:pipeline) }
|
scope :with_pipeline, -> { joins(:pipeline) }
|
||||||
|
scope :updated_at_before, ->(date) { where('updated_at < ?', date) }
|
||||||
scope :updated_before, ->(lookback:, timeout:) {
|
scope :updated_before, ->(lookback:, timeout:) {
|
||||||
where('(ci_builds.created_at BETWEEN ? AND ?) AND (ci_builds.updated_at BETWEEN ? AND ?)', lookback, timeout, lookback, timeout)
|
where('(ci_builds.created_at BETWEEN ? AND ?) AND (ci_builds.updated_at BETWEEN ? AND ?)', lookback, timeout, lookback, timeout)
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,7 @@ module Ci
|
||||||
scope :running_or_pending, -> { with_status(:running, :pending) }
|
scope :running_or_pending, -> { with_status(:running, :pending) }
|
||||||
scope :finished, -> { with_status(:success, :failed, :canceled) }
|
scope :finished, -> { with_status(:success, :failed, :canceled) }
|
||||||
scope :failed_or_canceled, -> { with_status(:failed, :canceled) }
|
scope :failed_or_canceled, -> { with_status(:failed, :canceled) }
|
||||||
|
scope :complete, -> { with_status(completed_statuses) }
|
||||||
scope :incomplete, -> { without_statuses(completed_statuses) }
|
scope :incomplete, -> { without_statuses(completed_statuses) }
|
||||||
|
|
||||||
scope :cancelable, -> do
|
scope :cancelable, -> do
|
||||||
|
|
|
@ -938,8 +938,8 @@ class Repository
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil, prune: true)
|
def fetch_as_mirror(url, forced: false, refmap: :all_refs, prune: true, http_authorization_header: "")
|
||||||
fetch_remote(remote_name, url: url, refmap: refmap, forced: forced, prune: prune)
|
fetch_remote(url, refmap: refmap, forced: forced, prune: prune, http_authorization_header: http_authorization_header)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_source_branch!(source_repository, source_branch, local_ref)
|
def fetch_source_branch!(source_repository, source_branch, local_ref)
|
||||||
|
|
|
@ -5,6 +5,8 @@ module Ci
|
||||||
class AssignResourceFromResourceGroupService < ::BaseService
|
class AssignResourceFromResourceGroupService < ::BaseService
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
def execute(resource_group)
|
def execute(resource_group)
|
||||||
|
release_resource_from_stale_jobs(resource_group)
|
||||||
|
|
||||||
free_resources = resource_group.resources.free.count
|
free_resources = resource_group.resources.free.count
|
||||||
|
|
||||||
resource_group.processables.waiting_for_resource.take(free_resources).each do |processable|
|
resource_group.processables.waiting_for_resource.take(free_resources).each do |processable|
|
||||||
|
@ -12,6 +14,14 @@ module Ci
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def release_resource_from_stale_jobs(resource_group)
|
||||||
|
resource_group.resources.stale_processables.find_each do |processable|
|
||||||
|
resource_group.release_resource_from(processable)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: changes_batch_commits
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66731
|
||||||
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/336992
|
||||||
|
milestone: '14.2'
|
||||||
|
type: development
|
||||||
|
group: group::gitaly
|
||||||
|
default_enabled: false
|
|
@ -40,9 +40,11 @@ The newly created epic opens.
|
||||||
If you select **Inherited**:
|
If you select **Inherited**:
|
||||||
|
|
||||||
- For the **start date**: GitLab scans all child epics and issues assigned to the epic,
|
- For the **start date**: GitLab scans all child epics and issues assigned to the epic,
|
||||||
and sets the start date to match the earliest found start date or milestone.
|
and sets the start date to match the earliest start date found in the child epics or the milestone
|
||||||
- For the **due date**: GitLab sets the due date to match the latest due date or
|
assigned to the issues.
|
||||||
milestone found among its child epics and issues.
|
- For the **due date**: GitLab scans all child epics and issues assigned to the epic,
|
||||||
|
and sets the due date to match the latest due date found in the child epics or the milestone
|
||||||
|
assigned to the issues.
|
||||||
|
|
||||||
These are dynamic dates and recalculated if any of the following occur:
|
These are dynamic dates and recalculated if any of the following occur:
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ fork from its upstream project. Go to **Settings > Advanced Settings** and
|
||||||
|
|
||||||
For more information, [see the forking workflow documentation](../repository/forking_workflow.md).
|
For more information, [see the forking workflow documentation](../repository/forking_workflow.md).
|
||||||
|
|
||||||
## By sending an email **(FREE SELF)**
|
## By sending an email
|
||||||
|
|
||||||
> The format of the generated email address changed in GitLab 11.7.
|
> The format of the generated email address changed in GitLab 11.7.
|
||||||
The earlier format is still supported so existing aliases
|
The earlier format is still supported so existing aliases
|
||||||
|
|
|
@ -7,7 +7,6 @@ module Gitlab
|
||||||
attr_reader :project, :project_key, :repository_slug, :client, :errors, :users, :already_imported_cache_key
|
attr_reader :project, :project_key, :repository_slug, :client, :errors, :users, :already_imported_cache_key
|
||||||
attr_accessor :logger
|
attr_accessor :logger
|
||||||
|
|
||||||
REMOTE_NAME = 'bitbucket_server'
|
|
||||||
BATCH_SIZE = 100
|
BATCH_SIZE = 100
|
||||||
# The base cache key to use for tracking already imported objects.
|
# The base cache key to use for tracking already imported objects.
|
||||||
ALREADY_IMPORTED_CACHE_KEY =
|
ALREADY_IMPORTED_CACHE_KEY =
|
||||||
|
@ -142,7 +141,7 @@ module Gitlab
|
||||||
log_info(stage: 'import_repository', message: 'starting import')
|
log_info(stage: 'import_repository', message: 'starting import')
|
||||||
|
|
||||||
project.ensure_repository
|
project.ensure_repository
|
||||||
project.repository.fetch_as_mirror(project.import_url, refmap: self.class.refmap, remote_name: REMOTE_NAME)
|
project.repository.fetch_as_mirror(project.import_url, refmap: self.class.refmap)
|
||||||
|
|
||||||
log_info(stage: 'import_repository', message: 'finished import')
|
log_info(stage: 'import_repository', message: 'finished import')
|
||||||
rescue Gitlab::Shell::Error => e
|
rescue Gitlab::Shell::Error => e
|
||||||
|
|
|
@ -29,11 +29,48 @@ module Gitlab
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# All commits which have been newly introduced via any of the given
|
||||||
|
# changes. This set may also contain commits which are not referenced by
|
||||||
|
# any of the new revisions.
|
||||||
|
def commits
|
||||||
|
newrevs = @changes.map do |change|
|
||||||
|
newrev = change[:newrev]
|
||||||
|
newrev unless newrev.blank? || Gitlab::Git.blank_ref?(newrev)
|
||||||
|
end.compact
|
||||||
|
|
||||||
|
return [] if newrevs.empty?
|
||||||
|
|
||||||
|
@commits ||= project.repository.new_commits(newrevs)
|
||||||
|
end
|
||||||
|
|
||||||
|
# All commits which have been newly introduced via the given revision.
|
||||||
|
def commits_for(newrev)
|
||||||
|
commits_by_id = commits.index_by(&:id)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
pending = [newrev]
|
||||||
|
|
||||||
|
# We go up the parent chain of our newrev and collect all commits which
|
||||||
|
# are new. In case a commit's ID cannot be found in the set of new
|
||||||
|
# commits, then it must already be a preexisting commit.
|
||||||
|
pending.each do |rev|
|
||||||
|
commit = commits_by_id[rev]
|
||||||
|
next if commit.nil?
|
||||||
|
|
||||||
|
pending.push(*commit.parent_ids)
|
||||||
|
result << commit
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def single_access_checks!
|
def single_access_checks!
|
||||||
# Iterate over all changes to find if user allowed all of them to be applied
|
# Iterate over all changes to find if user allowed all of them to be applied
|
||||||
changes.each do |change|
|
changes.each do |change|
|
||||||
|
commits = Gitlab::Lazy.new { commits_for(change[:newrev]) } if Feature.enabled?(:changes_batch_commits)
|
||||||
|
|
||||||
# If user does not have access to make at least one change, cancel all
|
# If user does not have access to make at least one change, cancel all
|
||||||
# push by allowing the exception to bubble up
|
# push by allowing the exception to bubble up
|
||||||
Checks::SingleChangeAccess.new(
|
Checks::SingleChangeAccess.new(
|
||||||
|
@ -41,7 +78,8 @@ module Gitlab
|
||||||
user_access: user_access,
|
user_access: user_access,
|
||||||
project: project,
|
project: project,
|
||||||
protocol: protocol,
|
protocol: protocol,
|
||||||
logger: logger
|
logger: logger,
|
||||||
|
commits: commits
|
||||||
).validate!
|
).validate!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Gitlab
|
||||||
|
|
||||||
def initialize(
|
def initialize(
|
||||||
change, user_access:, project:,
|
change, user_access:, project:,
|
||||||
protocol:, logger:
|
protocol:, logger:, commits: nil
|
||||||
)
|
)
|
||||||
@oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref)
|
@oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref)
|
||||||
@branch_name = Gitlab::Git.branch_name(@ref)
|
@branch_name = Gitlab::Git.branch_name(@ref)
|
||||||
|
@ -19,6 +19,7 @@ module Gitlab
|
||||||
@user_access = user_access
|
@user_access = user_access
|
||||||
@project = project
|
@project = project
|
||||||
@protocol = protocol
|
@protocol = protocol
|
||||||
|
@commits = commits
|
||||||
|
|
||||||
@logger = logger
|
@logger = logger
|
||||||
@logger.append_message("Running checks for ref: #{@branch_name || @tag_name}")
|
@logger.append_message("Running checks for ref: #{@branch_name || @tag_name}")
|
||||||
|
|
|
@ -699,11 +699,11 @@ module Gitlab
|
||||||
write_ref(ref, start_point)
|
write_ref(ref, start_point)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_remote_root_ref(remote_name, remote_url, authorization = nil)
|
def find_remote_root_ref(remote_url, authorization = nil)
|
||||||
return unless remote_name.present? && remote_url.present?
|
return unless remote_url.present?
|
||||||
|
|
||||||
wrapped_gitaly_errors do
|
wrapped_gitaly_errors do
|
||||||
gitaly_remote_client.find_remote_root_ref(remote_name, remote_url, authorization)
|
gitaly_remote_client.find_remote_root_ref(remote_url, authorization)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -803,18 +803,18 @@ module Gitlab
|
||||||
# no_tags - should we use --no-tags flag?
|
# no_tags - should we use --no-tags flag?
|
||||||
# prune - should we use --prune flag?
|
# prune - should we use --prune flag?
|
||||||
# check_tags_changed - should we ask gitaly to calculate whether any tags changed?
|
# check_tags_changed - should we ask gitaly to calculate whether any tags changed?
|
||||||
def fetch_remote(remote, url: nil, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, prune: true, check_tags_changed: false)
|
def fetch_remote(url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, prune: true, check_tags_changed: false, http_authorization_header: "")
|
||||||
wrapped_gitaly_errors do
|
wrapped_gitaly_errors do
|
||||||
gitaly_repository_client.fetch_remote(
|
gitaly_repository_client.fetch_remote(
|
||||||
remote,
|
url,
|
||||||
url: url,
|
|
||||||
refmap: refmap,
|
refmap: refmap,
|
||||||
ssh_auth: ssh_auth,
|
ssh_auth: ssh_auth,
|
||||||
forced: forced,
|
forced: forced,
|
||||||
no_tags: no_tags,
|
no_tags: no_tags,
|
||||||
prune: prune,
|
prune: prune,
|
||||||
check_tags_changed: check_tags_changed,
|
check_tags_changed: check_tags_changed,
|
||||||
timeout: GITLAB_PROJECTS_TIMEOUT
|
timeout: GITLAB_PROJECTS_TIMEOUT,
|
||||||
|
http_authorization_header: http_authorization_header
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -924,12 +924,6 @@ module Gitlab
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_config(*keys)
|
|
||||||
wrapped_gitaly_errors do
|
|
||||||
gitaly_repository_client.delete_config(keys)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def disconnect_alternates
|
def disconnect_alternates
|
||||||
wrapped_gitaly_errors do
|
wrapped_gitaly_errors do
|
||||||
gitaly_repository_client.disconnect_alternates
|
gitaly_repository_client.disconnect_alternates
|
||||||
|
|
|
@ -26,8 +26,7 @@ module Gitlab
|
||||||
@storage = repository.storage
|
@storage = repository.storage
|
||||||
end
|
end
|
||||||
|
|
||||||
# The remote_name parameter is deprecated and will be removed soon.
|
def find_remote_root_ref(remote_url, authorization)
|
||||||
def find_remote_root_ref(remote_name, remote_url, authorization)
|
|
||||||
request = Gitaly::FindRemoteRootRefRequest.new(repository: @gitaly_repo,
|
request = Gitaly::FindRemoteRootRefRequest.new(repository: @gitaly_repo,
|
||||||
remote_url: remote_url,
|
remote_url: remote_url,
|
||||||
http_authorization_header: authorization)
|
http_authorization_header: authorization)
|
||||||
|
|
|
@ -73,18 +73,21 @@ module Gitlab
|
||||||
# rubocop: disable Metrics/ParameterLists
|
# rubocop: disable Metrics/ParameterLists
|
||||||
# The `remote` parameter is going away soonish anyway, at which point the
|
# The `remote` parameter is going away soonish anyway, at which point the
|
||||||
# Rubocop warning can be enabled again.
|
# Rubocop warning can be enabled again.
|
||||||
def fetch_remote(remote, url:, refmap:, ssh_auth:, forced:, no_tags:, timeout:, prune: true, check_tags_changed: false)
|
def fetch_remote(url, refmap:, ssh_auth:, forced:, no_tags:, timeout:, prune: true, check_tags_changed: false, http_authorization_header: "")
|
||||||
request = Gitaly::FetchRemoteRequest.new(
|
request = Gitaly::FetchRemoteRequest.new(
|
||||||
repository: @gitaly_repo, remote: remote, force: forced,
|
repository: @gitaly_repo,
|
||||||
no_tags: no_tags, timeout: timeout, no_prune: !prune,
|
force: forced,
|
||||||
check_tags_changed: check_tags_changed
|
no_tags: no_tags,
|
||||||
|
timeout: timeout,
|
||||||
|
no_prune: !prune,
|
||||||
|
check_tags_changed: check_tags_changed,
|
||||||
|
remote_params: Gitaly::Remote.new(
|
||||||
|
url: url,
|
||||||
|
mirror_refmaps: Array.wrap(refmap).map(&:to_s),
|
||||||
|
http_authorization_header: http_authorization_header
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if url
|
|
||||||
request.remote_params = Gitaly::Remote.new(url: url,
|
|
||||||
mirror_refmaps: Array.wrap(refmap).map(&:to_s))
|
|
||||||
end
|
|
||||||
|
|
||||||
if ssh_auth&.ssh_mirror_url?
|
if ssh_auth&.ssh_mirror_url?
|
||||||
if ssh_auth.ssh_key_auth? && ssh_auth.ssh_private_key.present?
|
if ssh_auth.ssh_key_auth? && ssh_auth.ssh_private_key.present?
|
||||||
request.ssh_key = ssh_auth.ssh_private_key
|
request.ssh_key = ssh_auth.ssh_private_key
|
||||||
|
@ -297,22 +300,6 @@ module Gitlab
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_config(keys)
|
|
||||||
return if keys.empty?
|
|
||||||
|
|
||||||
request = Gitaly::DeleteConfigRequest.new(repository: @gitaly_repo, keys: keys)
|
|
||||||
|
|
||||||
GitalyClient.call(
|
|
||||||
@storage,
|
|
||||||
:repository_service,
|
|
||||||
:delete_config,
|
|
||||||
request,
|
|
||||||
timeout: GitalyClient.fast_timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def license_short_name
|
def license_short_name
|
||||||
request = Gitaly::FindLicenseRequest.new(repository: @gitaly_repo)
|
request = Gitaly::FindLicenseRequest.new(repository: @gitaly_repo)
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ module Gitlab
|
||||||
# updating the timestamp.
|
# updating the timestamp.
|
||||||
project.update_column(:last_repository_updated_at, Time.zone.now)
|
project.update_column(:last_repository_updated_at, Time.zone.now)
|
||||||
|
|
||||||
project.repository.fetch_remote('github', url: project.import_url, refmap: Gitlab::GithubImport.refmap, forced: false)
|
project.repository.fetch_remote(project.import_url, refmap: Gitlab::GithubImport.refmap, forced: false)
|
||||||
|
|
||||||
pname = project.path_with_namespace
|
pname = project.path_with_namespace
|
||||||
|
|
||||||
|
|
|
@ -33,41 +33,43 @@ RSpec.describe Ci::RunnersFinder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'filter by search term' do
|
context 'filtering' do
|
||||||
it 'calls Ci::Runner.search' do
|
context 'by search term' do
|
||||||
expect(Ci::Runner).to receive(:search).with('term').and_call_original
|
it 'calls Ci::Runner.search' do
|
||||||
|
expect(Ci::Runner).to receive(:search).with('term').and_call_original
|
||||||
|
|
||||||
described_class.new(current_user: admin, params: { search: 'term' }).execute
|
described_class.new(current_user: admin, params: { search: 'term' }).execute
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
context 'filter by status' do
|
context 'by status' do
|
||||||
Ci::Runner::AVAILABLE_STATUSES.each do |status|
|
Ci::Runner::AVAILABLE_STATUSES.each do |status|
|
||||||
it "calls the corresponding :#{status} scope on Ci::Runner" do
|
it "calls the corresponding :#{status} scope on Ci::Runner" do
|
||||||
expect(Ci::Runner).to receive(status.to_sym).and_call_original
|
expect(Ci::Runner).to receive(status.to_sym).and_call_original
|
||||||
|
|
||||||
described_class.new(current_user: admin, params: { status_status: status }).execute
|
described_class.new(current_user: admin, params: { status_status: status }).execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'by runner type' do
|
||||||
|
it 'calls the corresponding scope on Ci::Runner' do
|
||||||
|
expect(Ci::Runner).to receive(:project_type).and_call_original
|
||||||
|
|
||||||
|
described_class.new(current_user: admin, params: { type_type: 'project_type' }).execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'by tag_name' do
|
||||||
|
it 'calls the corresponding scope on Ci::Runner' do
|
||||||
|
expect(Ci::Runner).to receive(:tagged_with).with(%w[tag1 tag2]).and_call_original
|
||||||
|
|
||||||
|
described_class.new(current_user: admin, params: { tag_name: %w[tag1 tag2] }).execute
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'filter by runner type' do
|
context 'sorting' do
|
||||||
it 'calls the corresponding scope on Ci::Runner' do
|
|
||||||
expect(Ci::Runner).to receive(:project_type).and_call_original
|
|
||||||
|
|
||||||
described_class.new(current_user: admin, params: { type_type: 'project_type' }).execute
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'filter by tag_name' do
|
|
||||||
it 'calls the corresponding scope on Ci::Runner' do
|
|
||||||
expect(Ci::Runner).to receive(:tagged_with).with(%w[tag1 tag2]).and_call_original
|
|
||||||
|
|
||||||
described_class.new(current_user: admin, params: { tag_name: %w[tag1 tag2] }).execute
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'sort' do
|
|
||||||
let_it_be(:runner1) { create :ci_runner, created_at: '2018-07-12 07:00', contacted_at: 1.minute.ago }
|
let_it_be(:runner1) { create :ci_runner, created_at: '2018-07-12 07:00', contacted_at: 1.minute.ago }
|
||||||
let_it_be(:runner2) { create :ci_runner, created_at: '2018-07-12 08:00', contacted_at: 3.minutes.ago }
|
let_it_be(:runner2) { create :ci_runner, created_at: '2018-07-12 08:00', contacted_at: 3.minutes.ago }
|
||||||
let_it_be(:runner3) { create :ci_runner, created_at: '2018-07-12 09:00', contacted_at: 2.minutes.ago }
|
let_it_be(:runner3) { create :ci_runner, created_at: '2018-07-12 09:00', contacted_at: 2.minutes.ago }
|
||||||
|
@ -121,7 +123,7 @@ RSpec.describe Ci::RunnersFinder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'non admin user' do
|
context 'by non admin user' do
|
||||||
it 'returns no runners' do
|
it 'returns no runners' do
|
||||||
user = create :user
|
user = create :user
|
||||||
create :ci_runner, active: true
|
create :ci_runner, active: true
|
||||||
|
@ -131,7 +133,7 @@ RSpec.describe Ci::RunnersFinder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'user is nil' do
|
context 'when user is nil' do
|
||||||
it 'returns no runners' do
|
it 'returns no runners' do
|
||||||
user = nil
|
user = nil
|
||||||
create :ci_runner, active: true
|
create :ci_runner, active: true
|
||||||
|
@ -182,85 +184,69 @@ RSpec.describe Ci::RunnersFinder do
|
||||||
describe '#execute' do
|
describe '#execute' do
|
||||||
subject { described_class.new(current_user: user, group: group, params: params).execute }
|
subject { described_class.new(current_user: user, group: group, params: params).execute }
|
||||||
|
|
||||||
context 'no params' do
|
context 'with user as group owner' do
|
||||||
before do
|
before do
|
||||||
group.add_owner(user)
|
group.add_owner(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns all runners' do
|
context 'passing no params' do
|
||||||
expect(subject).to eq([runner_project_7, runner_project_6, runner_project_5,
|
it 'returns all descendant runners' do
|
||||||
runner_project_4, runner_project_3, runner_project_2,
|
expect(subject).to eq([runner_project_7, runner_project_6, runner_project_5,
|
||||||
runner_project_1, runner_sub_group_4, runner_sub_group_3,
|
runner_project_4, runner_project_3, runner_project_2,
|
||||||
runner_sub_group_2, runner_sub_group_1, runner_group])
|
runner_project_1, runner_sub_group_4, runner_sub_group_3,
|
||||||
|
runner_sub_group_2, runner_sub_group_1, runner_group])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with sort param' do
|
||||||
|
let(:params) { { sort: 'contacted_asc' } }
|
||||||
|
|
||||||
|
it 'sorts by specified attribute' do
|
||||||
|
expect(subject).to eq([runner_group, runner_sub_group_1, runner_sub_group_2,
|
||||||
|
runner_sub_group_3, runner_sub_group_4, runner_project_1,
|
||||||
|
runner_project_2, runner_project_3, runner_project_4,
|
||||||
|
runner_project_5, runner_project_6, runner_project_7])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'filtering' do
|
||||||
|
context 'by search term' do
|
||||||
|
let(:params) { { search: 'runner_project_search' } }
|
||||||
|
|
||||||
|
it 'returns correct runner' do
|
||||||
|
expect(subject).to eq([runner_project_3])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'by status' do
|
||||||
|
let(:params) { { status_status: 'paused' } }
|
||||||
|
|
||||||
|
it 'returns correct runner' do
|
||||||
|
expect(subject).to eq([runner_sub_group_1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'by tag_name' do
|
||||||
|
let(:params) { { tag_name: %w[runner_tag] } }
|
||||||
|
|
||||||
|
it 'returns correct runner' do
|
||||||
|
expect(subject).to eq([runner_project_5])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'by runner type' do
|
||||||
|
let(:params) { { type_type: 'project_type' } }
|
||||||
|
|
||||||
|
it 'returns correct runners' do
|
||||||
|
expect(subject).to eq([runner_project_7, runner_project_6,
|
||||||
|
runner_project_5, runner_project_4,
|
||||||
|
runner_project_3, runner_project_2, runner_project_1])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with sort param' do
|
context 'when user is not group owner' do
|
||||||
let(:params) { { sort: 'contacted_asc' } }
|
|
||||||
|
|
||||||
before do
|
|
||||||
group.add_owner(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'sorts by specified attribute' do
|
|
||||||
expect(subject).to eq([runner_group, runner_sub_group_1, runner_sub_group_2,
|
|
||||||
runner_sub_group_3, runner_sub_group_4, runner_project_1,
|
|
||||||
runner_project_2, runner_project_3, runner_project_4,
|
|
||||||
runner_project_5, runner_project_6, runner_project_7])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'filter by search term' do
|
|
||||||
let(:params) { { search: 'runner_project_search' } }
|
|
||||||
|
|
||||||
before do
|
|
||||||
group.add_owner(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns correct runner' do
|
|
||||||
expect(subject).to eq([runner_project_3])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'filter by status' do
|
|
||||||
let(:params) { { status_status: 'paused' } }
|
|
||||||
|
|
||||||
before do
|
|
||||||
group.add_owner(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns correct runner' do
|
|
||||||
expect(subject).to eq([runner_sub_group_1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'filter by tag_name' do
|
|
||||||
let(:params) { { tag_name: %w[runner_tag] } }
|
|
||||||
|
|
||||||
before do
|
|
||||||
group.add_owner(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns correct runner' do
|
|
||||||
expect(subject).to eq([runner_project_5])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'filter by runner type' do
|
|
||||||
let(:params) { { type_type: 'project_type' } }
|
|
||||||
|
|
||||||
before do
|
|
||||||
group.add_owner(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns correct runners' do
|
|
||||||
expect(subject).to eq([runner_project_7, runner_project_6,
|
|
||||||
runner_project_5, runner_project_4,
|
|
||||||
runner_project_3, runner_project_2, runner_project_1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'user has no access to runners' do
|
|
||||||
where(:user_permission) do
|
where(:user_permission) do
|
||||||
[:maintainer, :developer, :reporter, :guest]
|
[:maintainer, :developer, :reporter, :guest]
|
||||||
end
|
end
|
||||||
|
@ -276,13 +262,13 @@ RSpec.describe Ci::RunnersFinder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'user with no access' do
|
context 'when user has no access' do
|
||||||
it 'returns no runners' do
|
it 'returns no runners' do
|
||||||
expect(subject).to be_empty
|
expect(subject).to be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'user is nil' do
|
context 'when user is nil' do
|
||||||
let_it_be(:user) { nil }
|
let_it_be(:user) { nil }
|
||||||
|
|
||||||
it 'returns no runners' do
|
it 'returns no runners' do
|
||||||
|
@ -294,7 +280,7 @@ RSpec.describe Ci::RunnersFinder do
|
||||||
describe '#sort_key' do
|
describe '#sort_key' do
|
||||||
subject { described_class.new(current_user: user, group: group, params: params).sort_key }
|
subject { described_class.new(current_user: user, group: group, params: params).sort_key }
|
||||||
|
|
||||||
context 'no params' do
|
context 'without params' do
|
||||||
it 'returns created_at_desc' do
|
it 'returns created_at_desc' do
|
||||||
expect(subject).to eq('created_at_desc')
|
expect(subject).to eq('created_at_desc')
|
||||||
end
|
end
|
||||||
|
|
|
@ -97,6 +97,7 @@ describe('~/boards/components/sidebar/board_sidebar_labels_select.vue', () => {
|
||||||
addLabelIds: TEST_LABELS.map((label) => label.id),
|
addLabelIds: TEST_LABELS.map((label) => label.id),
|
||||||
projectPath: TEST_ISSUE_FULLPATH,
|
projectPath: TEST_ISSUE_FULLPATH,
|
||||||
removeLabelIds: [],
|
removeLabelIds: [],
|
||||||
|
iid: null,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -121,6 +122,7 @@ describe('~/boards/components/sidebar/board_sidebar_labels_select.vue', () => {
|
||||||
addLabelIds: [5, 7],
|
addLabelIds: [5, 7],
|
||||||
removeLabelIds: [6],
|
removeLabelIds: [6],
|
||||||
projectPath: TEST_ISSUE_FULLPATH,
|
projectPath: TEST_ISSUE_FULLPATH,
|
||||||
|
iid: null,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -309,6 +309,7 @@ describe('Blob content viewer component', () => {
|
||||||
expect(findBlobEdit().props()).toMatchObject({
|
expect(findBlobEdit().props()).toMatchObject({
|
||||||
editPath: editBlobPath,
|
editPath: editBlobPath,
|
||||||
webIdePath: ideEditPath,
|
webIdePath: ideEditPath,
|
||||||
|
showEditButton: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -326,10 +327,11 @@ describe('Blob content viewer component', () => {
|
||||||
expect(findBlobEdit().props()).toMatchObject({
|
expect(findBlobEdit().props()).toMatchObject({
|
||||||
editPath: editBlobPath,
|
editPath: editBlobPath,
|
||||||
webIdePath: ideEditPath,
|
webIdePath: ideEditPath,
|
||||||
|
showEditButton: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not render BlobHeaderEdit button when viewing a binary file', async () => {
|
it('renders BlobHeaderEdit button for binary files', async () => {
|
||||||
fullFactory({
|
fullFactory({
|
||||||
mockData: { blobInfo: richMockData, isBinary: true },
|
mockData: { blobInfo: richMockData, isBinary: true },
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -340,7 +342,11 @@ describe('Blob content viewer component', () => {
|
||||||
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
expect(findBlobEdit().exists()).toBe(false);
|
expect(findBlobEdit().props()).toMatchObject({
|
||||||
|
editPath: editBlobPath,
|
||||||
|
webIdePath: ideEditPath,
|
||||||
|
showEditButton: false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('BlobButtonGroup', () => {
|
describe('BlobButtonGroup', () => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
|
||||||
const DEFAULT_PROPS = {
|
const DEFAULT_PROPS = {
|
||||||
editPath: 'some_file.js/edit',
|
editPath: 'some_file.js/edit',
|
||||||
webIdePath: 'some_file.js/ide/edit',
|
webIdePath: 'some_file.js/ide/edit',
|
||||||
|
showEditButton: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('BlobEdit component', () => {
|
describe('BlobEdit component', () => {
|
||||||
|
@ -31,8 +32,8 @@ describe('BlobEdit component', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const findButtons = () => wrapper.findAll(GlButton);
|
const findButtons = () => wrapper.findAll(GlButton);
|
||||||
const findEditButton = () => findButtons().at(0);
|
const findEditButton = () => wrapper.find('[data-testid="edit"]');
|
||||||
const findWebIdeButton = () => findButtons().at(1);
|
const findWebIdeButton = () => wrapper.find('[data-testid="web-ide"]');
|
||||||
const findWebIdeLink = () => wrapper.find(WebIdeLink);
|
const findWebIdeLink = () => wrapper.find(WebIdeLink);
|
||||||
|
|
||||||
it('renders component', () => {
|
it('renders component', () => {
|
||||||
|
@ -77,6 +78,23 @@ describe('BlobEdit component', () => {
|
||||||
editUrl,
|
editUrl,
|
||||||
webIdeUrl,
|
webIdeUrl,
|
||||||
isBlob: true,
|
isBlob: true,
|
||||||
|
showEditButton: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Without Edit button', () => {
|
||||||
|
const showEditButton = false;
|
||||||
|
|
||||||
|
it('renders WebIdeLink component without an edit button', () => {
|
||||||
|
createComponent(true, { showEditButton });
|
||||||
|
|
||||||
|
expect(findWebIdeLink().props()).toMatchObject({ showEditButton });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render an Edit button', () => {
|
||||||
|
createComponent(false, { showEditButton });
|
||||||
|
|
||||||
|
expect(findEditButton().exists()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,8 +32,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
||||||
expect(subject).to receive(:delete_temp_branches)
|
expect(subject).to receive(:delete_temp_branches)
|
||||||
expect(project.repository).to receive(:fetch_as_mirror)
|
expect(project.repository).to receive(:fetch_as_mirror)
|
||||||
.with('http://bitbucket:test@my-bitbucket',
|
.with('http://bitbucket:test@my-bitbucket',
|
||||||
refmap: [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head'],
|
refmap: [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head'])
|
||||||
remote_name: 'bitbucket_server')
|
|
||||||
|
|
||||||
subject.execute
|
subject.execute
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,40 +3,169 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Checks::ChangesAccess do
|
RSpec.describe Gitlab::Checks::ChangesAccess do
|
||||||
|
include_context 'changes access checks context'
|
||||||
|
|
||||||
|
subject { changes_access }
|
||||||
|
|
||||||
describe '#validate!' do
|
describe '#validate!' do
|
||||||
include_context 'changes access checks context'
|
shared_examples '#validate!' do
|
||||||
|
before do
|
||||||
before do
|
allow(project).to receive(:lfs_enabled?).and_return(true)
|
||||||
allow(project).to receive(:lfs_enabled?).and_return(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject { changes_access }
|
|
||||||
|
|
||||||
context 'without failed checks' do
|
|
||||||
it "doesn't raise an error" do
|
|
||||||
expect { subject.validate! }.not_to raise_error
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'calls lfs checks' do
|
context 'without failed checks' do
|
||||||
expect_next_instance_of(Gitlab::Checks::LfsCheck) do |instance|
|
it "doesn't raise an error" do
|
||||||
expect(instance).to receive(:validate!)
|
expect { subject.validate! }.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
subject.validate!
|
it 'calls lfs checks' do
|
||||||
|
expect_next_instance_of(Gitlab::Checks::LfsCheck) do |instance|
|
||||||
|
expect(instance).to receive(:validate!)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject.validate!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when time limit was reached' do
|
||||||
|
it 'raises a TimeoutError' do
|
||||||
|
logger = Gitlab::Checks::TimedLogger.new(start_time: timeout.ago, timeout: timeout)
|
||||||
|
access = described_class.new(changes,
|
||||||
|
project: project,
|
||||||
|
user_access: user_access,
|
||||||
|
protocol: protocol,
|
||||||
|
logger: logger)
|
||||||
|
|
||||||
|
expect { access.validate! }.to raise_error(Gitlab::Checks::TimedLogger::TimeoutError)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when time limit was reached' do
|
context 'with batched commits enabled' do
|
||||||
it 'raises a TimeoutError' do
|
before do
|
||||||
logger = Gitlab::Checks::TimedLogger.new(start_time: timeout.ago, timeout: timeout)
|
stub_feature_flags(changes_batch_commits: true)
|
||||||
access = described_class.new(changes,
|
end
|
||||||
project: project,
|
|
||||||
user_access: user_access,
|
|
||||||
protocol: protocol,
|
|
||||||
logger: logger)
|
|
||||||
|
|
||||||
expect { access.validate! }.to raise_error(Gitlab::Checks::TimedLogger::TimeoutError)
|
it_behaves_like '#validate!'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with batched commits disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(changes_batch_commits: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like '#validate!'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#commits' do
|
||||||
|
it 'calls #new_commits' do
|
||||||
|
expect(project.repository).to receive(:new_commits).and_return([])
|
||||||
|
|
||||||
|
expect(subject.commits).to eq([])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when changes contain empty revisions' do
|
||||||
|
let(:changes) { [{ newrev: newrev }, { newrev: '' }, { newrev: Gitlab::Git::BLANK_SHA }] }
|
||||||
|
let(:expected_commit) { instance_double(Commit) }
|
||||||
|
|
||||||
|
it 'returns only commits with non empty revisions' do
|
||||||
|
expect(project.repository).to receive(:new_commits).with([newrev]) { [expected_commit] }
|
||||||
|
expect(subject.commits).to eq([expected_commit])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#commits_for' do
|
||||||
|
let(:new_commits) { [] }
|
||||||
|
let(:expected_commits) { [] }
|
||||||
|
|
||||||
|
shared_examples 'a listing of new commits' do
|
||||||
|
it 'returns expected commits' do
|
||||||
|
expect(subject).to receive(:commits).and_return(new_commits)
|
||||||
|
|
||||||
|
expect(subject.commits_for(newrev)).to eq(expected_commits)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with no commits' do
|
||||||
|
it_behaves_like 'a listing of new commits'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with unrelated commits' do
|
||||||
|
let(:new_commits) { [create_commit('1234', %w[1111 2222])] }
|
||||||
|
|
||||||
|
it_behaves_like 'a listing of new commits'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with single related commit' do
|
||||||
|
let(:new_commits) { [create_commit(newrev, %w[1111 2222])] }
|
||||||
|
let(:expected_commits) { new_commits }
|
||||||
|
|
||||||
|
it_behaves_like 'a listing of new commits'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with single related and unrelated commit' do
|
||||||
|
let(:new_commits) do
|
||||||
|
[
|
||||||
|
create_commit(newrev, %w[1111 2222]),
|
||||||
|
create_commit('abcd', %w[1111 2222])
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:expected_commits) do
|
||||||
|
[create_commit(newrev, %w[1111 2222])]
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'a listing of new commits'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with multiple related commits' do
|
||||||
|
let(:new_commits) do
|
||||||
|
[
|
||||||
|
create_commit(newrev, %w[1111]),
|
||||||
|
create_commit('1111', %w[2222]),
|
||||||
|
create_commit('abcd', [])
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:expected_commits) do
|
||||||
|
[
|
||||||
|
create_commit(newrev, %w[1111]),
|
||||||
|
create_commit('1111', %w[2222])
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'a listing of new commits'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with merge commits' do
|
||||||
|
let(:new_commits) do
|
||||||
|
[
|
||||||
|
create_commit(newrev, %w[1111 2222 3333]),
|
||||||
|
create_commit('1111', []),
|
||||||
|
create_commit('3333', %w[4444]),
|
||||||
|
create_commit('4444', [])
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:expected_commits) do
|
||||||
|
[
|
||||||
|
create_commit(newrev, %w[1111 2222 3333]),
|
||||||
|
create_commit('1111', []),
|
||||||
|
create_commit('3333', %w[4444]),
|
||||||
|
create_commit('4444', [])
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'a listing of new commits'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_commit(id, parent_ids)
|
||||||
|
Gitlab::Git::Commit.new(project.repository, {
|
||||||
|
id: id,
|
||||||
|
parent_ids: parent_ids
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,5 +58,52 @@ RSpec.describe Gitlab::Checks::SingleChangeAccess do
|
||||||
expect { access.validate! }.to raise_error(Gitlab::Checks::TimedLogger::TimeoutError)
|
expect { access.validate! }.to raise_error(Gitlab::Checks::TimedLogger::TimeoutError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#commits' do
|
||||||
|
let(:expected_commits) { [Gitlab::Git::Commit.new(project.repository, { id: "1234" })] }
|
||||||
|
|
||||||
|
let(:access) do
|
||||||
|
described_class.new(changes,
|
||||||
|
project: project,
|
||||||
|
user_access: user_access,
|
||||||
|
protocol: protocol,
|
||||||
|
logger: logger,
|
||||||
|
commits: provided_commits)
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples '#commits' do
|
||||||
|
it 'returns expected commits' do
|
||||||
|
expect(access.commits).to eq(expected_commits)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns expected commits on repeated calls' do
|
||||||
|
expect(access.commits).to eq(expected_commits)
|
||||||
|
expect(access.commits).to eq(expected_commits)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with provided commits' do
|
||||||
|
let(:provided_commits) { expected_commits }
|
||||||
|
|
||||||
|
before do
|
||||||
|
expect(project.repository).not_to receive(:new_commits)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like '#commits'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without provided commits' do
|
||||||
|
let(:provided_commits) { nil }
|
||||||
|
|
||||||
|
before do
|
||||||
|
expect(project.repository)
|
||||||
|
.to receive(:new_commits)
|
||||||
|
.once
|
||||||
|
.and_return(expected_commits)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like '#commits'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -491,6 +491,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#fetch_remote' do
|
describe '#fetch_remote' do
|
||||||
|
let(:url) { 'http://example.clom' }
|
||||||
|
|
||||||
it 'delegates to the gitaly RepositoryService' do
|
it 'delegates to the gitaly RepositoryService' do
|
||||||
ssh_auth = double(:ssh_auth)
|
ssh_auth = double(:ssh_auth)
|
||||||
expected_opts = {
|
expected_opts = {
|
||||||
|
@ -500,17 +502,17 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
|
||||||
timeout: described_class::GITLAB_PROJECTS_TIMEOUT,
|
timeout: described_class::GITLAB_PROJECTS_TIMEOUT,
|
||||||
prune: false,
|
prune: false,
|
||||||
check_tags_changed: false,
|
check_tags_changed: false,
|
||||||
url: nil,
|
refmap: nil,
|
||||||
refmap: nil
|
http_authorization_header: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(repository.gitaly_repository_client).to receive(:fetch_remote).with('remote-name', expected_opts)
|
expect(repository.gitaly_repository_client).to receive(:fetch_remote).with(url, expected_opts)
|
||||||
|
|
||||||
repository.fetch_remote('remote-name', ssh_auth: ssh_auth, forced: true, no_tags: true, prune: false, check_tags_changed: false)
|
repository.fetch_remote(url, ssh_auth: ssh_auth, forced: true, no_tags: true, prune: false, check_tags_changed: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RepositoryService, :fetch_remote do
|
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RepositoryService, :fetch_remote do
|
||||||
subject { repository.fetch_remote('remote-name') }
|
subject { repository.fetch_remote(url) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -584,29 +586,29 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
|
||||||
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
|
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
|
||||||
.to receive(:find_remote_root_ref).and_call_original
|
.to receive(:find_remote_root_ref).and_call_original
|
||||||
|
|
||||||
expect(repository.find_remote_root_ref('origin', SeedHelper::GITLAB_GIT_TEST_REPO_URL)).to eq 'master'
|
expect(repository.find_remote_root_ref(SeedHelper::GITLAB_GIT_TEST_REPO_URL)).to eq 'master'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns UTF-8' do
|
it 'returns UTF-8' do
|
||||||
expect(repository.find_remote_root_ref('origin', SeedHelper::GITLAB_GIT_TEST_REPO_URL)).to be_utf8
|
expect(repository.find_remote_root_ref(SeedHelper::GITLAB_GIT_TEST_REPO_URL)).to be_utf8
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns nil when remote name is nil' do
|
it 'returns nil when remote name is nil' do
|
||||||
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
|
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
|
||||||
.not_to receive(:find_remote_root_ref)
|
.not_to receive(:find_remote_root_ref)
|
||||||
|
|
||||||
expect(repository.find_remote_root_ref(nil, nil)).to be_nil
|
expect(repository.find_remote_root_ref(nil)).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns nil when remote name is empty' do
|
it 'returns nil when remote name is empty' do
|
||||||
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
|
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
|
||||||
.not_to receive(:find_remote_root_ref)
|
.not_to receive(:find_remote_root_ref)
|
||||||
|
|
||||||
expect(repository.find_remote_root_ref('', '')).to be_nil
|
expect(repository.find_remote_root_ref('')).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RemoteService, :find_remote_root_ref do
|
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RemoteService, :find_remote_root_ref do
|
||||||
subject { repository.find_remote_root_ref('origin', SeedHelper::GITLAB_GIT_TEST_REPO_URL) }
|
subject { repository.find_remote_root_ref(SeedHelper::GITLAB_GIT_TEST_REPO_URL) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1810,34 +1812,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#delete_config' do
|
|
||||||
let(:repository) { mutable_repository }
|
|
||||||
let(:entries) do
|
|
||||||
{
|
|
||||||
'test.foo1' => 'bla bla',
|
|
||||||
'test.foo2' => 1234,
|
|
||||||
'test.foo3' => true
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'can delete config settings' do
|
|
||||||
entries.each do |key, value|
|
|
||||||
repository_rugged.config[key] = value
|
|
||||||
end
|
|
||||||
|
|
||||||
expect(repository.delete_config(*%w[does.not.exist test.foo1 test.foo2])).to be_nil
|
|
||||||
|
|
||||||
# Workaround for https://github.com/libgit2/rugged/issues/785: If
|
|
||||||
# Gitaly changes .gitconfig while Rugged has the file loaded
|
|
||||||
# Rugged::Repository#each_key will report stale values unless a
|
|
||||||
# lookup is done first.
|
|
||||||
expect(repository_rugged.config['test.foo1']).to be_nil
|
|
||||||
config_keys = repository_rugged.config.each_key.to_a
|
|
||||||
expect(config_keys).not_to include('test.foo1')
|
|
||||||
expect(config_keys).not_to include('test.foo2')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#merge_to_ref' do
|
describe '#merge_to_ref' do
|
||||||
let(:repository) { mutable_repository }
|
let(:repository) { mutable_repository }
|
||||||
let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
|
let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
|
||||||
|
|
|
@ -6,11 +6,9 @@ RSpec.describe Gitlab::GitalyClient::RemoteService do
|
||||||
let(:project) { create(:project) }
|
let(:project) { create(:project) }
|
||||||
let(:storage_name) { project.repository_storage }
|
let(:storage_name) { project.repository_storage }
|
||||||
let(:relative_path) { project.disk_path + '.git' }
|
let(:relative_path) { project.disk_path + '.git' }
|
||||||
let(:remote_name) { 'my-remote' }
|
|
||||||
let(:client) { described_class.new(project.repository) }
|
let(:client) { described_class.new(project.repository) }
|
||||||
|
|
||||||
describe '#find_remote_root_ref' do
|
describe '#find_remote_root_ref' do
|
||||||
let(:remote) { 'origin' }
|
|
||||||
let(:url) { 'http://git.example.com/my-repo.git' }
|
let(:url) { 'http://git.example.com/my-repo.git' }
|
||||||
let(:auth) { 'Basic secret' }
|
let(:auth) { 'Basic secret' }
|
||||||
let(:expected_params) { { remote_url: url, http_authorization_header: auth } }
|
let(:expected_params) { { remote_url: url, http_authorization_header: auth } }
|
||||||
|
@ -22,7 +20,7 @@ RSpec.describe Gitlab::GitalyClient::RemoteService do
|
||||||
.with(gitaly_request_with_params(expected_params), kind_of(Hash))
|
.with(gitaly_request_with_params(expected_params), kind_of(Hash))
|
||||||
.and_return(double(ref: 'master'))
|
.and_return(double(ref: 'master'))
|
||||||
|
|
||||||
expect(client.find_remote_root_ref(remote, url, auth)).to eq 'master'
|
expect(client.find_remote_root_ref(url, auth)).to eq 'master'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ensure ref is a valid UTF-8 string' do
|
it 'ensure ref is a valid UTF-8 string' do
|
||||||
|
@ -32,7 +30,7 @@ RSpec.describe Gitlab::GitalyClient::RemoteService do
|
||||||
.with(gitaly_request_with_params(expected_params), kind_of(Hash))
|
.with(gitaly_request_with_params(expected_params), kind_of(Hash))
|
||||||
.and_return(double(ref: "an_invalid_ref_\xE5"))
|
.and_return(double(ref: "an_invalid_ref_\xE5"))
|
||||||
|
|
||||||
expect(client.find_remote_root_ref(remote, url, auth)).to eq "an_invalid_ref_å"
|
expect(client.find_remote_root_ref(url, auth)).to eq "an_invalid_ref_å"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -122,89 +122,75 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#fetch_remote' do
|
describe '#fetch_remote' do
|
||||||
shared_examples 'a fetch' do
|
let(:url) { 'https://example.com/git/repo.git' }
|
||||||
it 'sends a fetch_remote_request message' do
|
|
||||||
expected_remote_params = Gitaly::Remote.new(
|
|
||||||
url: url, http_authorization_header: "", mirror_refmaps: [])
|
|
||||||
|
|
||||||
expected_request = gitaly_request_with_params(
|
it 'sends a fetch_remote_request message' do
|
||||||
remote: remote,
|
expected_request = gitaly_request_with_params(
|
||||||
remote_params: url ? expected_remote_params : nil,
|
remote_params: Gitaly::Remote.new(
|
||||||
ssh_key: '',
|
url: url,
|
||||||
known_hosts: '',
|
http_authorization_header: "",
|
||||||
force: false,
|
mirror_refmaps: []
|
||||||
no_tags: false,
|
),
|
||||||
no_prune: false,
|
ssh_key: '',
|
||||||
check_tags_changed: false
|
known_hosts: '',
|
||||||
)
|
force: false,
|
||||||
|
no_tags: false,
|
||||||
|
no_prune: false,
|
||||||
|
check_tags_changed: false
|
||||||
|
)
|
||||||
|
|
||||||
expect_any_instance_of(Gitaly::RepositoryService::Stub)
|
expect_any_instance_of(Gitaly::RepositoryService::Stub)
|
||||||
.to receive(:fetch_remote)
|
.to receive(:fetch_remote)
|
||||||
.with(expected_request, kind_of(Hash))
|
.with(expected_request, kind_of(Hash))
|
||||||
.and_return(double(value: true))
|
.and_return(double(value: true))
|
||||||
|
|
||||||
client.fetch_remote(remote, url: url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, timeout: 1, check_tags_changed: false)
|
client.fetch_remote(url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, timeout: 1, check_tags_changed: false)
|
||||||
end
|
|
||||||
|
|
||||||
context 'SSH auth' do
|
|
||||||
where(:ssh_mirror_url, :ssh_key_auth, :ssh_private_key, :ssh_known_hosts, :expected_params) do
|
|
||||||
false | false | 'key' | 'known_hosts' | {}
|
|
||||||
false | true | 'key' | 'known_hosts' | {}
|
|
||||||
true | false | 'key' | 'known_hosts' | { known_hosts: 'known_hosts' }
|
|
||||||
true | true | 'key' | 'known_hosts' | { ssh_key: 'key', known_hosts: 'known_hosts' }
|
|
||||||
true | true | 'key' | nil | { ssh_key: 'key' }
|
|
||||||
true | true | nil | 'known_hosts' | { known_hosts: 'known_hosts' }
|
|
||||||
true | true | nil | nil | {}
|
|
||||||
true | true | '' | '' | {}
|
|
||||||
end
|
|
||||||
|
|
||||||
with_them do
|
|
||||||
let(:ssh_auth) do
|
|
||||||
double(
|
|
||||||
:ssh_auth,
|
|
||||||
ssh_mirror_url?: ssh_mirror_url,
|
|
||||||
ssh_key_auth?: ssh_key_auth,
|
|
||||||
ssh_private_key: ssh_private_key,
|
|
||||||
ssh_known_hosts: ssh_known_hosts
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it do
|
|
||||||
expected_remote_params = Gitaly::Remote.new(
|
|
||||||
url: url, http_authorization_header: "", mirror_refmaps: [])
|
|
||||||
|
|
||||||
expected_request = gitaly_request_with_params({
|
|
||||||
remote: remote,
|
|
||||||
remote_params: url ? expected_remote_params : nil,
|
|
||||||
ssh_key: '',
|
|
||||||
known_hosts: '',
|
|
||||||
force: false,
|
|
||||||
no_tags: false,
|
|
||||||
no_prune: false
|
|
||||||
}.update(expected_params))
|
|
||||||
|
|
||||||
expect_any_instance_of(Gitaly::RepositoryService::Stub)
|
|
||||||
.to receive(:fetch_remote)
|
|
||||||
.with(expected_request, kind_of(Hash))
|
|
||||||
.and_return(double(value: true))
|
|
||||||
|
|
||||||
client.fetch_remote(remote, url: url, refmap: nil, ssh_auth: ssh_auth, forced: false, no_tags: false, timeout: 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with remote' do
|
context 'SSH auth' do
|
||||||
it_behaves_like 'a fetch' do
|
where(:ssh_mirror_url, :ssh_key_auth, :ssh_private_key, :ssh_known_hosts, :expected_params) do
|
||||||
let(:remote) { 'remote-name' }
|
false | false | 'key' | 'known_hosts' | {}
|
||||||
let(:url) { nil }
|
false | true | 'key' | 'known_hosts' | {}
|
||||||
|
true | false | 'key' | 'known_hosts' | { known_hosts: 'known_hosts' }
|
||||||
|
true | true | 'key' | 'known_hosts' | { ssh_key: 'key', known_hosts: 'known_hosts' }
|
||||||
|
true | true | 'key' | nil | { ssh_key: 'key' }
|
||||||
|
true | true | nil | 'known_hosts' | { known_hosts: 'known_hosts' }
|
||||||
|
true | true | nil | nil | {}
|
||||||
|
true | true | '' | '' | {}
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
context 'with URL' do
|
with_them do
|
||||||
it_behaves_like 'a fetch' do
|
let(:ssh_auth) do
|
||||||
let(:remote) { "" }
|
double(
|
||||||
let(:url) { 'https://example.com/git/repo.git' }
|
:ssh_auth,
|
||||||
|
ssh_mirror_url?: ssh_mirror_url,
|
||||||
|
ssh_key_auth?: ssh_key_auth,
|
||||||
|
ssh_private_key: ssh_private_key,
|
||||||
|
ssh_known_hosts: ssh_known_hosts
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it do
|
||||||
|
expected_request = gitaly_request_with_params({
|
||||||
|
remote_params: Gitaly::Remote.new(
|
||||||
|
url: url,
|
||||||
|
http_authorization_header: "",
|
||||||
|
mirror_refmaps: []
|
||||||
|
),
|
||||||
|
ssh_key: '',
|
||||||
|
known_hosts: '',
|
||||||
|
force: false,
|
||||||
|
no_tags: false,
|
||||||
|
no_prune: false
|
||||||
|
}.update(expected_params))
|
||||||
|
|
||||||
|
expect_any_instance_of(Gitaly::RepositoryService::Stub)
|
||||||
|
.to receive(:fetch_remote)
|
||||||
|
.with(expected_request, kind_of(Hash))
|
||||||
|
.and_return(double(value: true))
|
||||||
|
|
||||||
|
client.fetch_remote(url, refmap: nil, ssh_auth: ssh_auth, forced: false, no_tags: false, timeout: 1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -164,7 +164,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do
|
||||||
|
|
||||||
expect(project.repository)
|
expect(project.repository)
|
||||||
.to receive(:fetch_remote)
|
.to receive(:fetch_remote)
|
||||||
.with('github', forced: false, url: url, refmap: Gitlab::GithubImport.refmap)
|
.with(url, forced: false, refmap: Gitlab::GithubImport.refmap)
|
||||||
|
|
||||||
freeze_time do
|
freeze_time do
|
||||||
importer.update_repository
|
importer.update_repository
|
||||||
|
|
|
@ -15,6 +15,22 @@ RSpec.describe Ci::Resource do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.retained' do
|
||||||
|
subject { described_class.retained }
|
||||||
|
|
||||||
|
it "returns the resource if it's retained" do
|
||||||
|
resource = create(:ci_resource, processable: create(:ci_build))
|
||||||
|
|
||||||
|
is_expected.to eq([resource])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns empty if it's not retained" do
|
||||||
|
create(:ci_resource, processable: nil)
|
||||||
|
|
||||||
|
is_expected.to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '.retained_by' do
|
describe '.retained_by' do
|
||||||
subject { described_class.retained_by(build) }
|
subject { described_class.retained_by(build) }
|
||||||
|
|
||||||
|
@ -25,4 +41,40 @@ RSpec.describe Ci::Resource do
|
||||||
is_expected.to eq([resource])
|
is_expected.to eq([resource])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.stale_processables' do
|
||||||
|
subject { resource_group.resources.stale_processables }
|
||||||
|
|
||||||
|
let!(:resource_group) { create(:ci_resource_group) }
|
||||||
|
let!(:resource) { create(:ci_resource, processable: build, resource_group: resource_group) }
|
||||||
|
|
||||||
|
context 'when the processable is running' do
|
||||||
|
let!(:build) { create(:ci_build, :running, resource_group: resource_group) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
# Creating unrelated builds to make sure the `retained` scope is working
|
||||||
|
create(:ci_build, :running, resource_group: resource_group)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns empty' do
|
||||||
|
is_expected.to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and doomed' do
|
||||||
|
before do
|
||||||
|
build.doom!
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns empty' do
|
||||||
|
is_expected.to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the stale prosessable a few minutes later' do
|
||||||
|
travel_to(10.minutes.since) do
|
||||||
|
is_expected.to eq([build])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -79,6 +79,15 @@ RSpec.describe CommitStatus do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.updated_at_before' do
|
||||||
|
it 'finds the relevant records' do
|
||||||
|
status = create(:commit_status, updated_at: 1.day.ago, project: project)
|
||||||
|
create(:commit_status, updated_at: 1.day.since, project: project)
|
||||||
|
|
||||||
|
expect(described_class.updated_at_before(Time.current)).to eq([status])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '.updated_before' do
|
describe '.updated_before' do
|
||||||
let!(:lookback) { 5.days.ago }
|
let!(:lookback) { 5.days.ago }
|
||||||
let!(:timeout) { 1.day.ago }
|
let!(:timeout) { 1.day.ago }
|
||||||
|
|
|
@ -351,6 +351,18 @@ RSpec.describe Ci::HasStatus do
|
||||||
it_behaves_like 'not containing the job', status
|
it_behaves_like 'not containing the job', status
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.complete' do
|
||||||
|
subject { CommitStatus.complete }
|
||||||
|
|
||||||
|
described_class::COMPLETED_STATUSES.each do |status|
|
||||||
|
it_behaves_like 'containing the job', status
|
||||||
|
end
|
||||||
|
|
||||||
|
described_class::ACTIVE_STATUSES.each do |status|
|
||||||
|
it_behaves_like 'not containing the job', status
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '::DEFAULT_STATUS' do
|
describe '::DEFAULT_STATUS' do
|
||||||
|
|
|
@ -1096,15 +1096,14 @@ RSpec.describe Repository do
|
||||||
|
|
||||||
describe '#fetch_as_mirror' do
|
describe '#fetch_as_mirror' do
|
||||||
let(:url) { "http://example.com" }
|
let(:url) { "http://example.com" }
|
||||||
let(:remote_name) { "remote-name" }
|
|
||||||
|
|
||||||
it 'fetches the URL without creating a remote' do
|
it 'fetches the URL without creating a remote' do
|
||||||
expect(repository)
|
expect(repository)
|
||||||
.to receive(:fetch_remote)
|
.to receive(:fetch_remote)
|
||||||
.with(remote_name, url: url, forced: false, prune: true, refmap: :all_refs)
|
.with(url, forced: false, prune: true, refmap: :all_refs, http_authorization_header: "")
|
||||||
.and_return(nil)
|
.and_return(nil)
|
||||||
|
|
||||||
repository.fetch_as_mirror(url, remote_name: remote_name)
|
repository.fetch_as_mirror(url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -51,14 +51,32 @@ RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there are no available resources' do
|
context 'when there are no available resources' do
|
||||||
|
let!(:other_build) { create(:ci_build) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
resource_group.assign_resource_to(create(:ci_build))
|
resource_group.assign_resource_to(other_build)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not request resource' do
|
it 'does not request resource' do
|
||||||
expect_any_instance_of(Ci::Build).not_to receive(:enqueue_waiting_for_resource)
|
expect_any_instance_of(Ci::Build).not_to receive(:enqueue_waiting_for_resource)
|
||||||
|
|
||||||
subject
|
subject
|
||||||
|
|
||||||
|
expect(build.reload).to be_waiting_for_resource
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there is a stale build assigned to a resource' do
|
||||||
|
before do
|
||||||
|
other_build.doom!
|
||||||
|
other_build.update_column(:updated_at, 10.minutes.ago)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'releases the resource from the stale build and assignes to the waiting build' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(build.reload).to be_pending
|
||||||
|
expect(build.resource).to be_present
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue