Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-29 12:08:55 +00:00
parent f931527bc5
commit 2dedd78ef5
39 changed files with 648 additions and 319 deletions

View File

@ -448,7 +448,9 @@ db:backup_and_restore:
- date
- bundle exec rake gitlab:backup:restore
rules:
- changes: ["lib/backup/**/*"]
- changes:
- "lib/backup/**/*"
- "lib/tasks/gitlab/backup.rake"
rspec:deprecations:
extends:

View File

@ -46,7 +46,7 @@ review-build-cng:
variables:
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
GITLAB_HELM_CHART_REF: "v4.12.0"
GITLAB_HELM_CHART_REF: "v5.1.0"
environment:
name: review/${CI_COMMIT_REF_SLUG}${FREQUENCY}
url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}

View File

@ -487,7 +487,7 @@ gem 'toml-rb', '~> 2.0'
gem 'flipper', '~> 0.21.0'
gem 'flipper-active_record', '~> 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'
# Structured logging

View File

@ -1317,7 +1317,7 @@ GEM
unicode-display_width (1.7.0)
unicode_utils (1.4.0)
uniform_notifier (1.13.0)
unleash (0.1.5)
unleash (3.2.2)
murmurhash3 (~> 0.1.6)
unparser (0.6.0)
diff-lcs (~> 1.3)
@ -1643,7 +1643,7 @@ DEPENDENCIES
truncato (~> 0.7.11)
u2f (~> 0.2.1)
unf (~> 0.1.4)
unleash (~> 0.1.5)
unleash (~> 3.2.2)
valid_email (~> 0.1)
validates_hostname (~> 1.0.11)
version_sorter (~> 2.2.4)

View File

@ -25,6 +25,8 @@ export default {
data() {
return {
loading: false,
oldIid: null,
isEditing: false,
};
},
computed: {
@ -72,6 +74,15 @@ export default {
return this.labelsFetchPath || projectLabelsFetchPath;
},
},
watch: {
activeBoardItem(_, oldVal) {
if (this.isEditing) {
this.oldIid = oldVal.iid;
} else {
this.oldIid = null;
}
},
},
methods: {
...mapActions(['setActiveBoardItemLabels', 'setError']),
async setLabels(payload) {
@ -84,8 +95,14 @@ export default {
.filter((label) => !payload.find((selected) => selected.id === 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);
this.oldIid = null;
} catch (e) {
this.setError({ error: e, message: __('An error occurred while updating labels.') });
} finally {
@ -115,6 +132,8 @@ export default {
:title="__('Labels')"
:loading="loading"
data-testid="sidebar-labels"
@open="isEditing = true"
@close="isEditing = false"
>
<template #collapsed>
<gl-label

View File

@ -575,7 +575,7 @@ export default {
mutation: issueSetLabelsMutation,
variables: {
input: {
iid: String(activeBoardItem.iid),
iid: input.iid || String(activeBoardItem.iid),
addLabelIds: input.addLabelIds ?? [],
removeLabelIds: input.removeLabelIds ?? [],
projectPath: input.projectPath,
@ -588,7 +588,7 @@ export default {
}
commit(types.UPDATE_BOARD_ITEM_BY_ID, {
itemId: activeBoardItem.id,
itemId: getIdFromGraphQLId(data.updateIssue?.issue?.id) || activeBoardItem.id,
prop: 'labels',
value: data.updateIssue.issue.labels.nodes,
});

View File

@ -175,7 +175,7 @@ export default {
>
<template #actions>
<blob-edit
v-if="!isBinary"
:show-edit-button="!isBinary"
:edit-path="blobInfo.editBlobPath"
:web-ide-path="blobInfo.ideEditPath"
/>

View File

@ -15,6 +15,10 @@ export default {
},
mixins: [glFeatureFlagsMixin()],
props: {
showEditButton: {
type: Boolean,
required: true,
},
editPath: {
type: String,
required: true,
@ -30,17 +34,31 @@ export default {
<template>
<web-ide-link
v-if="glFeatures.consolidatedEditButton"
:show-edit-button="showEditButton"
class="gl-mr-3"
:edit-url="editPath"
:web-ide-url="webIdePath"
:is-blob="true"
/>
<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 }}
</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 }}
</gl-button>
</div>

View File

@ -8,6 +8,21 @@ module Ci
belongs_to :processable, class_name: 'Ci::Processable', foreign_key: 'build_id', inverse_of: :resource
scope :free, -> { where(processable: nil) }
scope :retained, -> { where.not(processable: nil) }
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

View File

@ -58,6 +58,7 @@ class CommitStatus < ApplicationRecord
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
scope :eager_load_pipeline, -> { eager_load(:pipeline, project: { namespace: :route }) }
scope :with_pipeline, -> { joins(:pipeline) }
scope :updated_at_before, ->(date) { where('updated_at < ?', date) }
scope :updated_before, ->(lookback:, timeout:) {
where('(ci_builds.created_at BETWEEN ? AND ?) AND (ci_builds.updated_at BETWEEN ? AND ?)', lookback, timeout, lookback, timeout)
}

View File

@ -93,6 +93,7 @@ module Ci
scope :running_or_pending, -> { with_status(:running, :pending) }
scope :finished, -> { with_status(:success, :failed, :canceled) }
scope :failed_or_canceled, -> { with_status(:failed, :canceled) }
scope :complete, -> { with_status(completed_statuses) }
scope :incomplete, -> { without_statuses(completed_statuses) }
scope :cancelable, -> do

View File

@ -938,8 +938,8 @@ class Repository
end
end
def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil, prune: true)
fetch_remote(remote_name, url: url, refmap: refmap, forced: forced, prune: prune)
def fetch_as_mirror(url, forced: false, refmap: :all_refs, prune: true, http_authorization_header: "")
fetch_remote(url, refmap: refmap, forced: forced, prune: prune, http_authorization_header: http_authorization_header)
end
def fetch_source_branch!(source_repository, source_branch, local_ref)

View File

@ -5,6 +5,8 @@ module Ci
class AssignResourceFromResourceGroupService < ::BaseService
# rubocop: disable CodeReuse/ActiveRecord
def execute(resource_group)
release_resource_from_stale_jobs(resource_group)
free_resources = resource_group.resources.free.count
resource_group.processables.waiting_for_resource.take(free_resources).each do |processable|
@ -12,6 +14,14 @@ module Ci
end
end
# 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

View File

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

View File

@ -40,9 +40,11 @@ The newly created epic opens.
If you select **Inherited**:
- 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.
- For the **due date**: GitLab sets the due date to match the latest due date or
milestone found among its child epics and issues.
and sets the start date to match the earliest start date found in the child epics or the milestone
assigned to the 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:

View File

@ -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).
## By sending an email **(FREE SELF)**
## By sending an email
> The format of the generated email address changed in GitLab 11.7.
The earlier format is still supported so existing aliases

View File

@ -7,7 +7,6 @@ module Gitlab
attr_reader :project, :project_key, :repository_slug, :client, :errors, :users, :already_imported_cache_key
attr_accessor :logger
REMOTE_NAME = 'bitbucket_server'
BATCH_SIZE = 100
# The base cache key to use for tracking already imported objects.
ALREADY_IMPORTED_CACHE_KEY =
@ -142,7 +141,7 @@ module Gitlab
log_info(stage: 'import_repository', message: 'starting import')
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')
rescue Gitlab::Shell::Error => e

View File

@ -29,11 +29,48 @@ module Gitlab
true
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
def single_access_checks!
# Iterate over all changes to find if user allowed all of them to be applied
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
# push by allowing the exception to bubble up
Checks::SingleChangeAccess.new(
@ -41,7 +78,8 @@ module Gitlab
user_access: user_access,
project: project,
protocol: protocol,
logger: logger
logger: logger,
commits: commits
).validate!
end
end

View File

@ -11,7 +11,7 @@ module Gitlab
def initialize(
change, user_access:, project:,
protocol:, logger:
protocol:, logger:, commits: nil
)
@oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref)
@branch_name = Gitlab::Git.branch_name(@ref)
@ -19,6 +19,7 @@ module Gitlab
@user_access = user_access
@project = project
@protocol = protocol
@commits = commits
@logger = logger
@logger.append_message("Running checks for ref: #{@branch_name || @tag_name}")

View File

@ -699,11 +699,11 @@ module Gitlab
write_ref(ref, start_point)
end
def find_remote_root_ref(remote_name, remote_url, authorization = nil)
return unless remote_name.present? && remote_url.present?
def find_remote_root_ref(remote_url, authorization = nil)
return unless remote_url.present?
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
@ -803,18 +803,18 @@ module Gitlab
# no_tags - should we use --no-tags flag?
# prune - should we use --prune flag?
# 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
gitaly_repository_client.fetch_remote(
remote,
url: url,
url,
refmap: refmap,
ssh_auth: ssh_auth,
forced: forced,
no_tags: no_tags,
prune: prune,
check_tags_changed: check_tags_changed,
timeout: GITLAB_PROJECTS_TIMEOUT
timeout: GITLAB_PROJECTS_TIMEOUT,
http_authorization_header: http_authorization_header
)
end
end
@ -924,12 +924,6 @@ module Gitlab
end
end
def delete_config(*keys)
wrapped_gitaly_errors do
gitaly_repository_client.delete_config(keys)
end
end
def disconnect_alternates
wrapped_gitaly_errors do
gitaly_repository_client.disconnect_alternates

View File

@ -26,8 +26,7 @@ module Gitlab
@storage = repository.storage
end
# The remote_name parameter is deprecated and will be removed soon.
def find_remote_root_ref(remote_name, remote_url, authorization)
def find_remote_root_ref(remote_url, authorization)
request = Gitaly::FindRemoteRootRefRequest.new(repository: @gitaly_repo,
remote_url: remote_url,
http_authorization_header: authorization)

View File

@ -73,18 +73,21 @@ module Gitlab
# rubocop: disable Metrics/ParameterLists
# The `remote` parameter is going away soonish anyway, at which point the
# 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(
repository: @gitaly_repo, remote: remote, force: forced,
no_tags: no_tags, timeout: timeout, no_prune: !prune,
check_tags_changed: check_tags_changed
repository: @gitaly_repo,
force: forced,
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_key_auth? && ssh_auth.ssh_private_key.present?
request.ssh_key = ssh_auth.ssh_private_key
@ -297,22 +300,6 @@ module Gitlab
nil
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
request = Gitaly::FindLicenseRequest.new(repository: @gitaly_repo)

View File

@ -40,7 +40,7 @@ module Gitlab
# updating the timestamp.
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

View File

@ -33,41 +33,43 @@ RSpec.describe Ci::RunnersFinder do
end
end
context 'filter by search term' do
it 'calls Ci::Runner.search' do
expect(Ci::Runner).to receive(:search).with('term').and_call_original
context 'filtering' do
context 'by search term' do
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
context 'filter by status' do
Ci::Runner::AVAILABLE_STATUSES.each do |status|
it "calls the corresponding :#{status} scope on Ci::Runner" do
expect(Ci::Runner).to receive(status.to_sym).and_call_original
context 'by status' do
Ci::Runner::AVAILABLE_STATUSES.each do |status|
it "calls the corresponding :#{status} scope on Ci::Runner" do
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
context 'filter 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 '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
context 'sorting' do
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(: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
context 'non admin user' do
context 'by non admin user' do
it 'returns no runners' do
user = create :user
create :ci_runner, active: true
@ -131,7 +133,7 @@ RSpec.describe Ci::RunnersFinder do
end
end
context 'user is nil' do
context 'when user is nil' do
it 'returns no runners' do
user = nil
create :ci_runner, active: true
@ -182,85 +184,69 @@ RSpec.describe Ci::RunnersFinder do
describe '#execute' do
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
group.add_owner(user)
end
it 'returns all 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, runner_sub_group_4, runner_sub_group_3,
runner_sub_group_2, runner_sub_group_1, runner_group])
context 'passing no params' do
it 'returns all descendant 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, 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
context 'with sort param' 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
context 'when user is not group owner' do
where(:user_permission) do
[:maintainer, :developer, :reporter, :guest]
end
@ -276,13 +262,13 @@ RSpec.describe Ci::RunnersFinder do
end
end
context 'user with no access' do
context 'when user has no access' do
it 'returns no runners' do
expect(subject).to be_empty
end
end
context 'user is nil' do
context 'when user is nil' do
let_it_be(:user) { nil }
it 'returns no runners' do
@ -294,7 +280,7 @@ RSpec.describe Ci::RunnersFinder do
describe '#sort_key' do
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
expect(subject).to eq('created_at_desc')
end

View File

@ -97,6 +97,7 @@ describe('~/boards/components/sidebar/board_sidebar_labels_select.vue', () => {
addLabelIds: TEST_LABELS.map((label) => label.id),
projectPath: TEST_ISSUE_FULLPATH,
removeLabelIds: [],
iid: null,
});
});
});
@ -121,6 +122,7 @@ describe('~/boards/components/sidebar/board_sidebar_labels_select.vue', () => {
addLabelIds: [5, 7],
removeLabelIds: [6],
projectPath: TEST_ISSUE_FULLPATH,
iid: null,
});
});
});

View File

@ -309,6 +309,7 @@ describe('Blob content viewer component', () => {
expect(findBlobEdit().props()).toMatchObject({
editPath: editBlobPath,
webIdePath: ideEditPath,
showEditButton: true,
});
});
@ -326,10 +327,11 @@ describe('Blob content viewer component', () => {
expect(findBlobEdit().props()).toMatchObject({
editPath: editBlobPath,
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({
mockData: { blobInfo: richMockData, isBinary: true },
stubs: {
@ -340,7 +342,11 @@ describe('Blob content viewer component', () => {
await nextTick();
expect(findBlobEdit().exists()).toBe(false);
expect(findBlobEdit().props()).toMatchObject({
editPath: editBlobPath,
webIdePath: ideEditPath,
showEditButton: false,
});
});
describe('BlobButtonGroup', () => {

View File

@ -6,6 +6,7 @@ import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
const DEFAULT_PROPS = {
editPath: 'some_file.js/edit',
webIdePath: 'some_file.js/ide/edit',
showEditButton: true,
};
describe('BlobEdit component', () => {
@ -31,8 +32,8 @@ describe('BlobEdit component', () => {
});
const findButtons = () => wrapper.findAll(GlButton);
const findEditButton = () => findButtons().at(0);
const findWebIdeButton = () => findButtons().at(1);
const findEditButton = () => wrapper.find('[data-testid="edit"]');
const findWebIdeButton = () => wrapper.find('[data-testid="web-ide"]');
const findWebIdeLink = () => wrapper.find(WebIdeLink);
it('renders component', () => {
@ -77,6 +78,23 @@ describe('BlobEdit component', () => {
editUrl,
webIdeUrl,
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);
});
});
});

View File

@ -32,8 +32,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
expect(subject).to receive(:delete_temp_branches)
expect(project.repository).to receive(:fetch_as_mirror)
.with('http://bitbucket:test@my-bitbucket',
refmap: [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head'],
remote_name: 'bitbucket_server')
refmap: [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head'])
subject.execute
end

View File

@ -3,40 +3,169 @@
require 'spec_helper'
RSpec.describe Gitlab::Checks::ChangesAccess do
include_context 'changes access checks context'
subject { changes_access }
describe '#validate!' do
include_context 'changes access checks context'
before do
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
shared_examples '#validate!' do
before do
allow(project).to receive(:lfs_enabled?).and_return(true)
end
it 'calls lfs checks' do
expect_next_instance_of(Gitlab::Checks::LfsCheck) do |instance|
expect(instance).to receive(:validate!)
context 'without failed checks' do
it "doesn't raise an error" do
expect { subject.validate! }.not_to raise_error
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
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)
context 'with batched commits enabled' do
before do
stub_feature_flags(changes_batch_commits: true)
end
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
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

View File

@ -58,5 +58,52 @@ RSpec.describe Gitlab::Checks::SingleChangeAccess do
expect { access.validate! }.to raise_error(Gitlab::Checks::TimedLogger::TimeoutError)
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

View File

@ -491,6 +491,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#fetch_remote' do
let(:url) { 'http://example.clom' }
it 'delegates to the gitaly RepositoryService' do
ssh_auth = double(:ssh_auth)
expected_opts = {
@ -500,17 +502,17 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
timeout: described_class::GITLAB_PROJECTS_TIMEOUT,
prune: 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
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
@ -584,29 +586,29 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
.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
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
it 'returns nil when remote name is nil' do
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
.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
it 'returns nil when remote name is empty' do
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
.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
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
@ -1810,34 +1812,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
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
let(:repository) { mutable_repository }
let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }

View File

@ -6,11 +6,9 @@ RSpec.describe Gitlab::GitalyClient::RemoteService do
let(:project) { create(:project) }
let(:storage_name) { project.repository_storage }
let(:relative_path) { project.disk_path + '.git' }
let(:remote_name) { 'my-remote' }
let(:client) { described_class.new(project.repository) }
describe '#find_remote_root_ref' do
let(:remote) { 'origin' }
let(:url) { 'http://git.example.com/my-repo.git' }
let(:auth) { 'Basic secret' }
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))
.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
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))
.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

View File

@ -122,89 +122,75 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do
end
describe '#fetch_remote' do
shared_examples 'a fetch' do
it 'sends a fetch_remote_request message' do
expected_remote_params = Gitaly::Remote.new(
url: url, http_authorization_header: "", mirror_refmaps: [])
let(:url) { 'https://example.com/git/repo.git' }
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,
check_tags_changed: false
)
it 'sends a fetch_remote_request message' 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,
check_tags_changed: false
)
expect_any_instance_of(Gitaly::RepositoryService::Stub)
.to receive(:fetch_remote)
.with(expected_request, kind_of(Hash))
.and_return(double(value: true))
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: 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
client.fetch_remote(url, refmap: nil, ssh_auth: nil, forced: false, no_tags: false, timeout: 1, check_tags_changed: false)
end
context 'with remote' do
it_behaves_like 'a fetch' do
let(:remote) { 'remote-name' }
let(:url) { nil }
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
end
context 'with URL' do
it_behaves_like 'a fetch' do
let(:remote) { "" }
let(:url) { 'https://example.com/git/repo.git' }
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_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

View File

@ -164,7 +164,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsImporter do
expect(project.repository)
.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
importer.update_repository

View File

@ -15,6 +15,22 @@ RSpec.describe Ci::Resource do
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
subject { described_class.retained_by(build) }
@ -25,4 +41,40 @@ RSpec.describe Ci::Resource do
is_expected.to eq([resource])
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

View File

@ -79,6 +79,15 @@ RSpec.describe CommitStatus do
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
let!(:lookback) { 5.days.ago }
let!(:timeout) { 1.day.ago }

View File

@ -351,6 +351,18 @@ RSpec.describe Ci::HasStatus do
it_behaves_like 'not containing the job', status
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
describe '::DEFAULT_STATUS' do

View File

@ -1096,15 +1096,14 @@ RSpec.describe Repository do
describe '#fetch_as_mirror' do
let(:url) { "http://example.com" }
let(:remote_name) { "remote-name" }
it 'fetches the URL without creating a remote' do
expect(repository)
.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)
repository.fetch_as_mirror(url, remote_name: remote_name)
repository.fetch_as_mirror(url)
end
end

View File

@ -51,14 +51,32 @@ RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService do
end
context 'when there are no available resources' do
let!(:other_build) { create(:ci_build) }
before do
resource_group.assign_resource_to(create(:ci_build))
resource_group.assign_resource_to(other_build)
end
it 'does not request resource' do
expect_any_instance_of(Ci::Build).not_to receive(:enqueue_waiting_for_resource)
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