Commit f292f676 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 3ebc63ca 286935e2
......@@ -656,7 +656,7 @@ class Repository
end
end
def tree(sha = :head, path = nil, recursive: false)
def tree(sha = :head, path = nil, recursive: false, pagination_params: nil)
if sha == :head
return unless head_commit
......@@ -667,7 +667,7 @@ class Repository
end
end
Tree.new(self, sha, path, recursive: recursive)
Tree.new(self, sha, path, recursive: recursive, pagination_params: pagination_params)
end
def blob_at_branch(branch_name, path)
......
......@@ -4,9 +4,9 @@ class Tree
include Gitlab::MarkupHelper
include Gitlab::Utils::StrongMemoize
attr_accessor :repository, :sha, :path, :entries
attr_accessor :repository, :sha, :path, :entries, :cursor
def initialize(repository, sha, path = '/', recursive: false)
def initialize(repository, sha, path = '/', recursive: false, pagination_params: nil)
path = '/' if path.blank?
@repository = repository
......@@ -14,7 +14,7 @@ class Tree
@path = path
git_repo = @repository.raw_repository
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path, recursive)
@entries, @cursor = Gitlab::Git::Tree.where(git_repo, @sha, @path, recursive, pagination_params)
end
def readme_path
......
......@@ -6,20 +6,24 @@
.form-check
= f.check_box :auto_devops_enabled, class: 'form-check-input'
= f.label :auto_devops_enabled, class: 'form-check-label' do
%strong= s_('CICD|Default to Auto DevOps pipeline for all projects')
= s_('CICD|Default to Auto DevOps pipeline for all projects')
.form-text.text-muted
= s_('CICD|The Auto DevOps pipeline runs by default in all projects with no CI/CD configuration file.')
= link_to _('What is Auto DevOps?'), help_page_path('topics/autodevops/index.md'), target: '_blank'
.form-group
= f.label :auto_devops_domain, s_('AdminSettings|Auto DevOps domain'), class: 'label-bold'
= f.text_field :auto_devops_domain, class: 'form-control gl-form-input', placeholder: 'domain.com'
= f.text_field :auto_devops_domain, class: 'form-control gl-form-input', placeholder: 'example.com'
.form-text.text-muted
= s_("AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages.")
= s_("AdminSettings|The default domain to use for Auto Review Apps and Auto Deploy stages in all projects.")
= link_to _('Learn more.'), help_page_path('topics/autodevops/stages.md', anchor: 'auto-review-apps'), target: '_blank'
.form-group
.form-check
= f.check_box :shared_runners_enabled, class: 'form-check-input'
= f.label :shared_runners_enabled, class: 'form-check-label' do
= s_("AdminSettings|Enable shared runners for new projects")
.form-text.text-muted
= s_("AdminSettings|All new projects can use the instance's shared runners by default.")
= render_if_exists 'admin/application_settings/shared_runners_minutes_setting', form: f
......@@ -31,32 +35,32 @@
= f.label :max_artifacts_size, _('Maximum artifacts size (MB)'), class: 'label-bold'
= f.number_field :max_artifacts_size, class: 'form-control gl-form-input'
.form-text.text-muted
= _("Set the maximum file size for each job's artifacts")
= link_to sprite_icon('question-o'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size')
= _("The maximum file size for job artifacts.")
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size')
.form-group
= f.label :default_artifacts_expire_in, _('Default artifacts expiration'), class: 'label-bold'
= f.text_field :default_artifacts_expire_in, class: 'form-control gl-form-input'
.form-text.text-muted
= html_escape(_("Set the default expiration time for each job's artifacts. 0 for unlimited. The default unit is in seconds, but you can define an alternative. For example: %{code_open}4 mins 2 sec%{code_close}, %{code_open}2h42min%{code_close}.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to sprite_icon('question-o'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
= html_escape(_("The default expiration time for job artifacts. 0 for unlimited. The default unit is in seconds, but you can use other units, for example %{code_open}4 mins 2 sec%{code_close}, %{code_open}2h42min%{code_close}.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
.form-group
.form-check
= f.check_box :keep_latest_artifact, class: 'form-check-input'
= f.label :keep_latest_artifact, class: 'form-check-label' do
%strong
= s_('AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines')
= s_('AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines')
.form-text.text-muted
= s_('AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire.')
.form-group
= f.label :archive_builds_in_human_readable, _('Archive jobs'), class: 'label-bold'
= f.text_field :archive_builds_in_human_readable, class: 'form-control gl-form-input', placeholder: 'never'
= f.text_field :archive_builds_in_human_readable, class: 'form-control gl-form-input'
.form-text.text-muted
= html_escape(_("Set the duration for which the jobs will be considered as old and expired. Once that time passes, the jobs will be archived and no longer able to be retried. Make it empty to never expire jobs. It has to be no less than 1 day, for example: %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}2 years%{code_close}.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= html_escape(_("Jobs older than the configured time are considered expired and are archived. Archived jobs can no longer be retried. Leave empty to never archive jobs automatically. The default unit is in days, but you can use other units, for example %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}2 years%{code_close}. Minimum value is 1 day.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'archive-jobs')
.form-group
.form-check
= f.check_box :protected_ci_variables, class: 'form-check-input'
= f.label :protected_ci_variables, class: 'form-check-label' do
%strong= s_('AdminSettings|Protect CI/CD variables by default')
= s_('AdminSettings|Protect CI/CD variables by default')
.form-text.text-muted
= s_('AdminSettings|New CI/CD variables in projects and groups default to protected.')
.form-group
......
......@@ -45,7 +45,7 @@ Note that currently on GitLab.com, any messages in `production.log` aren't
indexed by Elasticsearch due to the sheer volume and noise. They
do end up in Google Stackdriver, but it is still harder to search for
logs there. See the [GitLab.com logging
documentation](https://gitlab.com/gitlab-com/runbooks/blob/master/logging/doc/README.md)
documentation](https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs/logging)
for more details.
## Use structured (JSON) logging
......
......@@ -13,7 +13,7 @@ import BurnCharts from 'ee/burndown_chart/components/burn_charts.vue';
import { TYPE_ITERATION } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { formatDate } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale';
import { __, s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { Namespace } from '../constants';
import query from '../queries/iteration.query.graphql';
......@@ -51,7 +51,9 @@ export default {
return data[this.namespaceType]?.iterations?.nodes[0] || {};
},
error(err) {
this.error = err.message;
this.error = s__('Iterations|Unable to find iteration.');
// eslint-disable-next-line no-console
console.error(err.message);
},
},
},
......
......@@ -2,6 +2,7 @@
query Iteration($fullPath: ID!, $id: ID!, $isGroup: Boolean = true) {
group(fullPath: $fullPath) @include(if: $isGroup) {
id
iterations(id: $id, first: 1, includeAncestors: true) {
nodes {
...IterationReport
......@@ -10,6 +11,7 @@ query Iteration($fullPath: ID!, $id: ID!, $isGroup: Boolean = true) {
}
project(fullPath: $fullPath) @skip(if: $isGroup) {
id
iterations(id: $id, first: 1, includeAncestors: true) {
nodes {
...IterationReport
......
......@@ -3,6 +3,7 @@
# todo: should this use IterationsCadenceID! ?
query IterationCadence($fullPath: ID!, $id: ID!) {
group(fullPath: $fullPath) {
id
iterationCadences(id: $id) {
nodes {
...IterationCadence
......
......@@ -10,6 +10,7 @@ query IterationIssues(
$lastPageSize: Int
) {
group(fullPath: $fullPath) @include(if: $isGroup) {
id
issues(
iterationId: [$id]
before: $beforeCursor
......@@ -22,6 +23,7 @@ query IterationIssues(
}
}
project(fullPath: $fullPath) @skip(if: $isGroup) {
id
issues(
iterationId: [$id]
before: $beforeCursor
......
......@@ -11,6 +11,7 @@ query IterationIssuesWithLabelFilter(
$lastPageSize: Int
) {
group(fullPath: $fullPath) @include(if: $isGroup) {
id
issues(
iterationId: [$id]
labelName: $labelName
......@@ -24,6 +25,7 @@ query IterationIssuesWithLabelFilter(
}
}
project(fullPath: $fullPath) @skip(if: $isGroup) {
id
issues(
iterationId: [$id]
labelName: $labelName
......
......@@ -11,6 +11,7 @@ query Iterations(
$lastPageSize: Int
) {
group(fullPath: $fullPath) @include(if: $isGroup) {
id
iterations(
state: $state
before: $beforeCursor
......@@ -27,6 +28,7 @@ query Iterations(
}
}
project(fullPath: $fullPath) @skip(if: $isGroup) {
id
iterations(
state: $state
before: $beforeCursor
......
......@@ -2,5 +2,5 @@
= form.label :shared_runners_minutes, _('Pipeline minutes quota'), class: 'label-bold'
= form.number_field :shared_runners_minutes, class: 'form-control gl-form-input'
.form-text.text-muted
= _('Set the maximum number of pipeline minutes that a group can use on shared Runners per month. 0 for unlimited.')
= link_to sprite_icon('question-o'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'shared-runners-pipeline-minutes-quota'), target: '_blank'
= _('The maximum number of pipeline minutes that a group can use on shared runners per month. 0 for unlimited.')
= link_to _('What are shared runner pipeline minutes?'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'shared-runners-pipeline-minutes-quota'), target: '_blank'
......@@ -51,6 +51,7 @@ describe('Iteration cadence form', () => {
const getCadenceSuccess = {
data: {
group: {
id: 'gid://gitlab/Group/114',
iterationCadences: {
nodes: [iterationCadence],
},
......
......@@ -38,7 +38,9 @@ describe('Iteration Form', () => {
};
const readMutationSuccess = {
data: { group: { iterations: { nodes: [iteration] }, errors: [] } },
data: {
group: { id: 'gid://gitlab/Group/114', iterations: { nodes: [iteration] }, errors: [] },
},
};
const createMutationSuccess = { data: { iterationCreate: { iteration, errors: [] } } };
const createMutationFailure = {
......
......@@ -56,7 +56,6 @@ describe('Iterations report issues', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('shows spinner while loading', () => {
......
......@@ -39,7 +39,8 @@ describe('Iterations report', () => {
const mountComponent = ({
props = defaultProps,
iterationQueryHandler = jest.fn().mockResolvedValue(mockGroupIterations),
mockQueryResponse = mockGroupIterations,
iterationQueryHandler = jest.fn().mockResolvedValue(mockQueryResponse),
} = {}) => {
localVue.use(VueApollo);
mockApollo = createMockApollo([[query, iterationQueryHandler]]);
......@@ -104,7 +105,7 @@ describe('Iterations report', () => {
],
])('when viewing an iteration in a %s', (_, props, mockIteration, expectedParams) => {
it('calls a query with correct parameters', () => {
const iterationQueryHandler = jest.fn();
const iterationQueryHandler = jest.fn().mockResolvedValue(mockIteration);
mountComponent({
props,
iterationQueryHandler,
......@@ -128,7 +129,6 @@ describe('Iterations report', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('empty state', () => {
......@@ -137,6 +137,7 @@ describe('Iterations report', () => {
iterationQueryHandler: jest.fn().mockResolvedValue({
data: {
group: {
id: 'gid://gitlab/Group/1',
iterations: {
nodes: [],
},
......@@ -212,12 +213,18 @@ describe('Iterations report', () => {
'when user $description and they are viewing an iteration within a $namespaceType',
({ canEdit, namespaceType, canEditIteration }) => {
beforeEach(() => {
const mockQueryResponse = {
[Namespace.Group]: mockGroupIterations,
[Namespace.Project]: mockProjectIterations,
}[namespaceType];
mountComponent({
props: {
...defaultProps,
canEditIteration,
namespaceType,
},
mockQueryResponse,
});
});
......
......@@ -14,6 +14,7 @@ export const mockIterationNode = {
export const mockGroupIterations = {
data: {
group: {
id: 'gid://gitlab/Group/114',
iterations: {
nodes: [mockIterationNode],
__typename: 'IterationConnection',
......@@ -26,6 +27,7 @@ export const mockGroupIterations = {
export const mockProjectIterations = {
data: {
project: {
id: 'gid://gitlab/Project/114',
iterations: {
nodes: [mockIterationNode],
__typename: 'IterationConnection',
......
......@@ -14,9 +14,12 @@ module Gitlab
include Gitlab::Git::RuggedImpl::UseRugged
override :tree_entries
def tree_entries(repository, sha, path, recursive)
def tree_entries(repository, sha, path, recursive, pagination_params = nil)
if use_rugged?(repository, :rugged_tree_entries)
execute_rugged_call(:tree_entries_with_flat_path_from_rugged, repository, sha, path, recursive)
[
execute_rugged_call(:tree_entries_with_flat_path_from_rugged, repository, sha, path, recursive),
nil
]
else
super
end
......
......@@ -15,15 +15,15 @@ module Gitlab
# Uses rugged for raw objects
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/320
def where(repository, sha, path = nil, recursive = false)
def where(repository, sha, path = nil, recursive = false, pagination_params = nil)
path = nil if path == '' || path == '/'
tree_entries(repository, sha, path, recursive)
tree_entries(repository, sha, path, recursive, pagination_params)
end
def tree_entries(repository, sha, path, recursive)
def tree_entries(repository, sha, path, recursive, pagination_params = nil)
wrapped_gitaly_errors do
repository.gitaly_commit_client.tree_entries(repository, sha, path, recursive)
repository.gitaly_commit_client.tree_entries(repository, sha, path, recursive, pagination_params)
end
end
......
......@@ -111,17 +111,22 @@ module Gitlab
nil
end
def tree_entries(repository, revision, path, recursive)
def tree_entries(repository, revision, path, recursive, pagination_params)
request = Gitaly::GetTreeEntriesRequest.new(
repository: @gitaly_repo,
revision: encode_binary(revision),
path: path.present? ? encode_binary(path) : '.',
recursive: recursive
recursive: recursive,
pagination_params: pagination_params
)
request.sort = Gitaly::GetTreeEntriesRequest::SortBy::TREES_FIRST if pagination_params
response = GitalyClient.call(@repository.storage, :commit_service, :get_tree_entries, request, timeout: GitalyClient.medium_timeout)
response.flat_map do |message|
cursor = nil
entries = response.flat_map do |message|
cursor = message.pagination_cursor if message.pagination_cursor
message.entries.map do |gitaly_tree_entry|
Gitlab::Git::Tree.new(
id: gitaly_tree_entry.oid,
......@@ -135,6 +140,8 @@ module Gitlab
)
end
end
[entries, cursor]
end
def commit_count(ref, options = {})
......
......@@ -2362,6 +2362,9 @@ msgstr ""
msgid "AdminProjects|Delete Project %{projectName}?"
msgstr ""
msgid "AdminSettings|All new projects can use the instance's shared runners by default."
msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
......@@ -2407,7 +2410,7 @@ msgstr ""
msgid "AdminSettings|Set a CI/CD template as the required pipeline configuration for all projects in the instance. Project CI/CD configuration merges into the required pipeline configuration when the pipeline runs. %{link_start}What is a required pipeline configuration?%{link_end}"
msgstr ""
msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
msgid "AdminSettings|The default domain to use for Auto Review Apps and Auto Deploy stages in all projects."
msgstr ""
msgid "AdminSettings|The default name for the initial branch of new repositories created in the instance."
......@@ -18806,6 +18809,9 @@ msgstr ""
msgid "Jobs fail if they run longer than the timeout time. Input value is in seconds by default. Human readable input is also accepted, for example %{code_open}1 hour%{code_close}."
msgstr ""
msgid "Jobs older than the configured time are considered expired and are archived. Archived jobs can no longer be retried. Leave empty to never archive jobs automatically. The default unit is in days, but you can use other units, for example %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}2 years%{code_close}. Minimum value is 1 day."
msgstr ""
msgid "Jobs|Are you sure you want to proceed?"
msgstr ""
......@@ -30006,24 +30012,12 @@ msgstr ""
msgid "Set the default branch for this project. All merge requests and commits are made against this branch unless you specify a different one."
msgstr ""
msgid "Set the default expiration time for each job's artifacts. 0 for unlimited. The default unit is in seconds, but you can define an alternative. For example: %{code_open}4 mins 2 sec%{code_close}, %{code_open}2h42min%{code_close}."
msgstr ""
msgid "Set the due date to %{due_date}."
msgstr ""
msgid "Set the duration for which the jobs will be considered as old and expired. Once that time passes, the jobs will be archived and no longer able to be retried. Make it empty to never expire jobs. It has to be no less than 1 day, for example: %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}2 years%{code_close}."
msgstr ""
msgid "Set the iteration to %{iteration_reference}."
msgstr ""
msgid "Set the maximum file size for each job's artifacts"
msgstr ""
msgid "Set the maximum number of pipeline minutes that a group can use on shared Runners per month. 0 for unlimited."
msgstr ""
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
......@@ -32690,6 +32684,9 @@ msgstr ""
msgid "The default branch for this project has been changed. Please update your bookmarks."
msgstr ""
msgid "The default expiration time for job artifacts. 0 for unlimited. The default unit is in seconds, but you can use other units, for example %{code_open}4 mins 2 sec%{code_close}, %{code_open}2h42min%{code_close}."
msgstr ""
msgid "The dependency list details information about the components used within your project."
msgstr ""
......@@ -32845,12 +32842,18 @@ msgstr ""
msgid "The maximum file size allowed is %{size}."
msgstr ""
msgid "The maximum file size for job artifacts."
msgstr ""
msgid "The maximum file size in megabytes for individual job artifacts."
msgstr ""
msgid "The maximum file size is %{size}."
msgstr ""
msgid "The maximum number of pipeline minutes that a group can use on shared runners per month. 0 for unlimited."
msgstr ""
msgid "The maximum number of slices allowed to run concurrently during Elasticsearch reindexing. Learn more about %{max_slices_running_link_start}maximum running slices configuration%{max_slices_link_end}."
msgstr ""
......@@ -36968,6 +36971,9 @@ msgstr ""
msgid "What are project audit events?"
msgstr ""
msgid "What are shared runner pipeline minutes?"
msgstr ""
msgid "What are you searching for?"
msgstr ""
......
......@@ -70,7 +70,7 @@ gitlab:
memory: 800M
limits:
cpu: 450m
memory: 1200M
memory: 2000M
webservice:
resources:
requests:
......
......@@ -6,29 +6,44 @@ RSpec.describe Gitlab::Git::Tree, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
shared_examples :repo do
let(:tree) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID) }
subject(:tree) { Gitlab::Git::Tree.where(repository, sha, path, recursive, pagination_params) }
it { expect(tree).to be_kind_of Array }
it { expect(tree.empty?).to be_falsey }
it { expect(tree.count(&:dir?)).to eq(2) }
it { expect(tree.count(&:file?)).to eq(10) }
it { expect(tree.count(&:submodule?)).to eq(2) }
let(:sha) { SeedRepo::Commit::ID }
let(:path) { nil }
let(:recursive) { false }
let(:pagination_params) { nil }
it 'returns an empty array when called with an invalid ref' do
expect(described_class.where(repository, 'foobar-does-not-exist')).to eq([])
let(:entries) { tree.first }
let(:cursor) { tree.second }
it { expect(entries).to be_kind_of Array }
it { expect(entries.empty?).to be_falsey }
it { expect(entries.count(&:dir?)).to eq(2) }
it { expect(entries.count(&:file?)).to eq(10) }
it { expect(entries.count(&:submodule?)).to eq(2) }
it { expect(cursor&.next_cursor).to be_blank }
context 'with an invalid ref' do
let(:sha) { 'foobar-does-not-exist' }
it { expect(entries).to eq([]) }
it { expect(cursor).to be_nil }
end
it 'returns a list of tree objects' do
entries = described_class.where(repository, SeedRepo::Commit::ID, 'files', true)
context 'when path is provided' do
let(:path) { 'files' }
let(:recursive) { true }
expect(entries.map(&:path)).to include('files/html',
'files/markdown/ruby-style-guide.md')
expect(entries.count).to be >= 10
expect(entries).to all(be_a(Gitlab::Git::Tree))
it 'returns a list of tree objects' do
expect(entries.map(&:path)).to include('files/html',
'files/markdown/ruby-style-guide.md')
expect(entries.count).to be >= 10
expect(entries).to all(be_a(Gitlab::Git::Tree))
end
end
describe '#dir?' do
let(:dir) { tree.select(&:dir?).first }
let(:dir) { entries.select(&:dir?).first }
it { expect(dir).to be_kind_of Gitlab::Git::Tree }
it { expect(dir.id).to eq('3c122d2b7830eca25235131070602575cf8b41a1') }
......@@ -41,7 +56,8 @@ RSpec.describe Gitlab::Git::Tree, :seed_helper do
context :subdir do
# rubocop: disable Rails/FindBy
# This is not ActiveRecord where..first
let(:subdir) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files').first }
let(:path) { 'files' }
let(:subdir) { entries.first }
# rubocop: enable Rails/FindBy
it { expect(subdir).to be_kind_of Gitlab::Git::Tree }
......@@ -55,7 +71,8 @@ RSpec.describe Gitlab::Git::Tree, :seed_helper do
context :subdir_file do
# rubocop: disable Rails/FindBy
# This is not ActiveRecord where..first
let(:subdir_file) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first }
let(:path) { 'files/ruby' }
let(:subdir_file) { entries.first }
# rubocop: enable Rails/FindBy
it { expect(subdir_file).to be_kind_of Gitlab::Git::Tree }
......@@ -68,10 +85,11 @@ RSpec.describe Gitlab::Git::Tree, :seed_helper do
context :flat_path do
let(:filename) { 'files/flat/path/correct/content.txt' }
let(:oid) { create_file(filename) }
let(:sha) { create_file(filename) }
let(:path) { 'files/flat' }
# rubocop: disable Rails/FindBy
# This is not ActiveRecord where..first
let(:subdir_file) { Gitlab::Git::Tree.where(repository, oid, 'files/flat').first }
let(:subdir_file) { entries.first }
# rubocop: enable Rails/FindBy
let(:repository_rugged) { Rugged::Repository.new(File.join(SEED_STORAGE_PATH, TEST_REPO_PATH)) }
......@@ -116,7 +134,7 @@ RSpec.describe Gitlab::Git::Tree, :seed_helper do
end
describe '#file?' do
let(:file) { tree.select(&:file?).first }
let(:file) { entries.select(&:file?).first }
it { expect(file).to be_kind_of Gitlab::Git::Tree }
it { expect(file.id).to eq('dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82') }
......@@ -125,21 +143,21 @@ RSpec.describe Gitlab::Git::Tree, :seed_helper do
end
describe '#readme?' do
let(:file) { tree.select(&:readme?).first }
let(:file) { entries.select(&:readme?).first }
it { expect(file).to be_kind_of Gitlab::Git::Tree }
it { expect(file.name).to eq('README.md') }
end
describe '#contributing?' do
let(:file) { tree.select(&:contributing?).first }
let(:file) { entries.select(&:contributing?).first }
it { expect(file).to be_kind_of Gitlab::Git::Tree }
it { expect(file.name).to eq('CONTRIBUTING.md') }
end
describe '#submodule?' do
let(:submodule) { tree.select(&:submodule?).first }
let(:submodule) { entries.select(&:submodule?).first }
it { expect(submodule).to be_kind_of Gitlab::Git::Tree }
it { expect(submodule.id).to eq('79bceae69cb5750d6567b223597999bfa91cb3b9') }
......@@ -149,7 +167,16 @@ RSpec.describe Gitlab::Git::Tree, :seed_helper do
end
describe '.where with Gitaly enabled' do
it_behaves_like :repo
it_behaves_like :repo do
context 'with pagination parameters' do
let(:pagination_params) { { limit: 3, page_token: nil } }
it 'returns paginated list of tree objects' do
expect(entries.count).to eq(3)
expect(cursor.next_cursor).to be_present
end
end
end
end
describe '.where with Rugged enabled', :enable_rugged do
......@@ -161,6 +188,15 @@ RSpec.describe Gitlab::Git::Tree, :seed_helper do
described_class.where(repository, SeedRepo::Commit::ID, 'files', false)
end
it_behaves_like :repo
it_behaves_like :repo do
context 'with pagination parameters' do
let(:pagination_params) { { limit: 3, page_token: nil } }
it 'does not support pagination' do
expect(entries.count).to be >= 10
expect(cursor).to be_nil
end
end
end
end
end
......@@ -169,7 +169,11 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
end
describe '#tree_entries' do
subject { client.tree_entries(repository, revision, path, recursive, pagination_params) }
let(:path) { '/' }
let(:recursive) { false }
let(:pagination_params) { nil }
it 'sends a get_tree_entries message' do
expect_any_instance_of(Gitaly::CommitService::Stub)
......@@ -177,7 +181,7 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return([])
client.tree_entries(repository, revision, path, false)
is_expected.to eq([[], nil])
end
context 'with UTF-8 params strings' do
......@@ -190,7 +194,26 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return([])
client.tree_entries(repository, revision, path, false)
is_expected.to eq([[], nil])
end
end
context 'with pagination parameters' do
let(:pagination_params) { { limit: 3, page_token: nil } }
it 'responds with a pagination cursor' do
pagination_cursor = Gitaly::PaginationCursor.new(next_cursor: 'aabbccdd')
response = Gitaly::GetTreeEntriesResponse.new(
entries: [],
pagination_cursor: pagination_cursor
)
expect_any_instance_of(Gitaly::CommitService::Stub)
.to receive(:get_tree_entries)
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return([response])
is_expected.to eq([[], pagination_cursor])
end
end
end
......
......@@ -2523,24 +2523,46 @@ RSpec.describe Repository do
end
shared_examples '#tree' do
subject { repository.tree(sha, path, recursive: recursive, pagination_params: pagination_params) }
let(:sha) { :head }
let(:path) { nil }
let(:recursive) { false }
let(:pagination_params) { nil }
context 'using a non-existing repository' do
before do
allow(repository).to receive(:head_commit).and_return(nil)
end
it 'returns nil' do
expect(repository.tree(:head)).to be_nil
end
it { is_expected.to be_nil }
context 'when path is defined' do
let(:path) { 'README.md' }
it 'returns nil when using a path' do
expect(repository.tree(:head, 'README.md')).to be_nil
it { is_expected.to be_nil }
end
end
context 'using an existing repository' do
it 'returns a Tree' do
expect(repository.tree(:head)).to be_an_instance_of(Tree)
expect(repository.tree('v1.1.1')).to be_an_instance_of(Tree)
it { is_expected.to be_an_instance_of(Tree) }
context 'when different sha is set' do
let(:sha) { 'v1.1.1' }
it { is_expected.to be_an_instance_of(Tree) }
end
context 'when recursive is true' do
let(:recursive) { true }
it { is_expected.to be_an_instance_of(Tree) }
end
context 'with pagination parameters' do
let(:pagination_params) { { limit: 10, page_token: nil } }
it { is_expected.to be_an_instance_of(Tree) }
end
end
end
......
......@@ -6,7 +6,7 @@ RSpec.describe Tree do
let(:repository) { create(:project, :repository).repository }
let(:sha) { repository.root_ref }
subject { described_class.new(repository, '54fcc214') }
subject(:tree) { described_class.new(repository, '54fcc214') }
describe '#readme' do
before do
......@@ -66,4 +66,10 @@ RSpec.describe Tree do
expect(subject.readme.name).to eq 'README.md'
end
end
describe '#cursor' do
subject { tree.cursor }
it { is_expected.to be_an_instance_of(Gitaly::PaginationCursor) }
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment