summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.codeclimate.yml2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js17
-rw-r--r--app/assets/javascripts/project_find_file.js24
-rw-r--r--app/assets/javascripts/render_math.js38
-rw-r--r--app/assets/javascripts/users/activity_calendar.js23
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js37
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue43
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/dependencies.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js3
-rw-r--r--app/controllers/admin/gitaly_servers_controller.rb5
-rw-r--r--app/controllers/groups_controller.rb5
-rw-r--r--app/models/repository.rb4
-rw-r--r--app/views/admin/dashboard/index.html.haml10
-rw-r--r--app/views/admin/gitaly_servers/index.html.haml31
-rw-r--r--changelogs/unreleased/fix-install-docs.yml5
-rw-r--r--changelogs/unreleased/sh-fix-events-collection.yml5
-rw-r--r--changelogs/unreleased/sh-fix-project-members-api-perf.yml6
-rw-r--r--changelogs/unreleased/zj-gitaly-server-info.yml5
-rw-r--r--config/routes/admin.rb2
-rw-r--r--doc/install/installation.md8
-rw-r--r--lib/api/access_requests.rb6
-rw-r--r--lib/api/entities.rb21
-rw-r--r--lib/api/members.rb13
-rw-r--r--lib/api/v3/members.rb15
-rw-r--r--lib/gitaly/server.rb43
-rw-r--r--lib/gitlab/git/repository.rb43
-rw-r--r--lib/gitlab/git/tree.rb2
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/server_service.rb16
-rw-r--r--lib/system_check/app/git_version_check.rb2
-rw-r--r--locale/gitlab.pot492
-rw-r--r--qa/qa.rb7
-rw-r--r--qa/qa/page/base.rb15
-rw-r--r--qa/qa/page/component/dropzone.rb29
-rw-r--r--qa/qa/page/project/issue/show.rb7
-rw-r--r--spec/controllers/admin/gitaly_servers_controller_spec.rb15
-rw-r--r--spec/controllers/groups_controller_spec.rb24
-rw-r--r--spec/javascripts/job_spec.js21
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js39
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js131
-rw-r--r--spec/lib/gitaly/server_spec.rb30
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb38
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb20
-rw-r--r--spec/models/repository_spec.rb8
-rw-r--r--spec/requests/api/members_spec.rb15
46 files changed, 896 insertions, 437 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml
index dc8ac60fb44..ecac24b68d7 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -1,7 +1,5 @@
---
engines:
- brakeman:
- enabled: true
bundler-audit:
enabled: true
duplication:
diff --git a/Gemfile.lock b/Gemfile.lock
index 4558b43af48..f770a7019e7 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -340,7 +340,7 @@ GEM
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
- google-protobuf (3.4.1.1)
+ google-protobuf (3.5.1.1-universal-darwin)
googleapis-common-protos-types (1.0.1)
google-protobuf (~> 3.0)
googleauth (0.5.3)
@@ -369,7 +369,7 @@ GEM
rake
grape_logging (1.7.0)
grape
- grpc (1.8.3)
+ grpc (1.8.3-universal-darwin)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
googleauth (>= 0.5.1, < 0.7)
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 03918762842..8018ec411c1 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -1,4 +1,5 @@
import { getLocationHash } from './url_utility';
+import axios from './axios_utils';
export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index];
@@ -382,22 +383,16 @@ export const resetFavicon = () => {
}
};
-export const setCiStatusFavicon = (pageUrl) => {
- $.ajax({
- url: pageUrl,
- dataType: 'json',
- success: (data) => {
+export const setCiStatusFavicon = pageUrl =>
+ axios.get(pageUrl)
+ .then(({ data }) => {
if (data && data.favicon) {
setFavicon(data.favicon);
} else {
resetFavicon();
}
- },
- error: () => {
- resetFavicon();
- },
- });
-};
+ })
+ .catch(resetFavicon);
export const spriteIcon = (icon, className = '') => {
const classAttribute = className.length > 0 ? `class="${className}"` : '';
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 0da32b4a3cc..586d188350f 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -1,6 +1,9 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, object-shorthand, no-param-reassign, comma-dangle, prefer-template, no-unused-vars, no-return-assign */
import fuzzaldrinPlus from 'fuzzaldrin-plus';
+import axios from '~/lib/utils/axios_utils';
+import flash from '~/flash';
+import { __ } from '~/locale';
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
const highlighter = function(element, text, matches) {
@@ -72,19 +75,14 @@ export default class ProjectFindFile {
// files pathes load
load(url) {
- return $.ajax({
- url: url,
- method: "get",
- dataType: "json",
- success: (function(_this) {
- return function(data) {
- _this.element.find(".loading").hide();
- _this.filePaths = data;
- _this.findFile();
- return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus();
- };
- })(this)
- });
+ axios.get(url)
+ .then(({ data }) => {
+ this.element.find('.loading').hide();
+ this.filePaths = data;
+ this.findFile();
+ this.element.find('.files-slider tr.tree-item').eq(0).addClass('selected').focus();
+ })
+ .catch(() => flash(__('An error occurred while loading filenames')));
}
// render result
diff --git a/app/assets/javascripts/render_math.js b/app/assets/javascripts/render_math.js
index 15205d8a4e2..73b6aafdd12 100644
--- a/app/assets/javascripts/render_math.js
+++ b/app/assets/javascripts/render_math.js
@@ -7,7 +7,12 @@
//
// <code class="js-render-math"></div>
//
- // Only load once
+
+import { __ } from './locale';
+import axios from './lib/utils/axios_utils';
+import flash from './flash';
+
+// Only load once
let katexLoaded = false;
// Loop over all math elements and render math
@@ -33,19 +38,26 @@ export default function renderMath($els) {
if (katexLoaded) {
renderWithKaTeX($els);
} else {
- $.get(gon.katex_css_url, () => {
- const css = $('<link>', {
- rel: 'stylesheet',
- type: 'text/css',
- href: gon.katex_css_url,
- });
- css.appendTo('head');
-
- // Load KaTeX js
- $.getScript(gon.katex_js_url, () => {
+ axios.get(gon.katex_css_url)
+ .then(() => {
+ const css = $('<link>', {
+ rel: 'stylesheet',
+ type: 'text/css',
+ href: gon.katex_css_url,
+ });
+ css.appendTo('head');
+ })
+ .then(() => axios.get(gon.katex_js_url, {
+ responseType: 'text',
+ }))
+ .then(({ data }) => {
+ // Add katex js to our document
+ $.globalEval(data);
+ })
+ .then(() => {
katexLoaded = true;
renderWithKaTeX($els); // Run KaTeX
- });
- });
+ })
+ .catch(() => flash(__('An error occurred while rendering KaTeX')));
}
}
diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js
index 0ca54faa71c..57306322aa4 100644
--- a/app/assets/javascripts/users/activity_calendar.js
+++ b/app/assets/javascripts/users/activity_calendar.js
@@ -1,7 +1,10 @@
import _ from 'underscore';
import { scaleLinear, scaleThreshold } from 'd3-scale';
import { select } from 'd3-selection';
-import { getDayName, getDayDifference } from '../lib/utils/datetime_utility';
+import { getDayName, getDayDifference } from '~/lib/utils/datetime_utility';
+import axios from '~/lib/utils/axios_utils';
+import flash from '~/flash';
+import { __ } from '~/locale';
const d3 = { select, scaleLinear, scaleThreshold };
@@ -221,14 +224,16 @@ export default class ActivityCalendar {
this.currentSelectedDate.getDate(),
].join('-');
- $.ajax({
- url: this.calendarActivitiesPath,
- data: { date },
- cache: false,
- dataType: 'html',
- beforeSend: () => $('.user-calendar-activities').html(LOADING_HTML),
- success: data => $('.user-calendar-activities').html(data),
- });
+ $('.user-calendar-activities').html(LOADING_HTML);
+
+ axios.get(this.calendarActivitiesPath, {
+ params: {
+ date,
+ },
+ responseType: 'text',
+ })
+ .then(({ data }) => $('.user-calendar-activities').html(data))
+ .catch(() => flash(__('An error occurred while retrieving calendar activity')));
} else {
this.currentSelectedDate = '';
$('.user-calendar-activities').html('');
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js
deleted file mode 100644
index 563267ad044..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.js
+++ /dev/null
@@ -1,37 +0,0 @@
-export default {
- name: 'MRWidgetRelatedLinks',
- props: {
- relatedLinks: { type: Object, required: true },
- state: { type: String, required: false },
- },
- computed: {
- hasLinks() {
- const { closing, mentioned, assignToMe } = this.relatedLinks;
- return closing || mentioned || assignToMe;
- },
- closesText() {
- if (this.state === 'merged') {
- return 'Closed';
- }
- if (this.state === 'closed') {
- return 'Did not close';
- }
- return 'Closes';
- },
- },
- template: `
- <section
- v-if="hasLinks"
- class="mr-info-list mr-links">
- <p v-if="relatedLinks.closing">
- {{closesText}} <span v-html="relatedLinks.closing"></span>
- </p>
- <p v-if="relatedLinks.mentioned">
- Mentions <span v-html="relatedLinks.mentioned"></span>
- </p>
- <p v-if="relatedLinks.assignToMe">
- <span v-html="relatedLinks.assignToMe"></span>
- </p>
- </section>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue
new file mode 100644
index 00000000000..88d0fcd70f5
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue
@@ -0,0 +1,43 @@
+<script>
+ import { s__ } from '~/locale';
+
+ export default {
+ name: 'MRWidgetRelatedLinks',
+ props: {
+ relatedLinks: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ state: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ closesText() {
+ if (this.state === 'merged') {
+ return s__('mrWidget|Closed');
+ }
+ if (this.state === 'closed') {
+ return s__('mrWidget|Did not close');
+ }
+ return s__('mrWidget|Closes');
+ },
+ },
+ };
+</script>
+<template>
+ <section class="mr-info-list mr-links">
+ <p v-if="relatedLinks.closing">
+ {{ closesText }} <span v-html="relatedLinks.closing"></span>
+ </p>
+ <p v-if="relatedLinks.mentioned">
+ {{ s__("mrWidget|Mentions") }} <span v-html="relatedLinks.mentioned"></span>
+ </p>
+ <p v-if="relatedLinks.assignToMe">
+ <span v-html="relatedLinks.assignToMe"></span>
+ </p>
+ </section>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
index 2917090e073..515eb32f5d6 100644
--- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js
+++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
@@ -15,7 +15,7 @@ export { default as WidgetHeader } from './components/mr_widget_header';
export { default as WidgetMergeHelp } from './components/mr_widget_merge_help';
export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue';
export { default as WidgetDeployment } from './components/mr_widget_deployment';
-export { default as WidgetRelatedLinks } from './components/mr_widget_related_links';
+export { default as WidgetRelatedLinks } from './components/mr_widget_related_links.vue';
export { default as MergedState } from './components/states/mr_widget_merged.vue';
export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue';
export { default as ClosedState } from './components/states/mr_widget_closed.vue';
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
index 98d33f9efaa..edf67fcd0a7 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
@@ -257,7 +257,8 @@ export default {
<mr-widget-related-links
v-if="shouldRenderRelatedLinks"
:state="mr.state"
- :related-links="mr.relatedLinks" />
+ :related-links="mr.relatedLinks"
+ />
</div>
<div
class="mr-widget-footer"
diff --git a/app/controllers/admin/gitaly_servers_controller.rb b/app/controllers/admin/gitaly_servers_controller.rb
new file mode 100644
index 00000000000..11c4dfe3d8d
--- /dev/null
+++ b/app/controllers/admin/gitaly_servers_controller.rb
@@ -0,0 +1,5 @@
+class Admin::GitalyServersController < Admin::ApplicationController
+ def index
+ @gitaly_servers = Gitaly::Server.all
+ end
+end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index eb53a522f90..bb652832cb1 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -118,10 +118,10 @@ class GroupsController < Groups::ApplicationController
end
def group_params
- params.require(:group).permit(group_params_ce)
+ params.require(:group).permit(group_params_attributes)
end
- def group_params_ce
+ def group_params_attributes
[
:avatar,
:description,
@@ -150,7 +150,6 @@ class GroupsController < Groups::ApplicationController
@projects = GroupProjectsFinder.new(params: params, group: group, options: options, current_user: current_user)
.execute
.includes(:namespace)
- .page(params[:page])
@events = EventCollection
.new(@projects, offset: params[:offset].to_i, filter: event_filter)
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 6c776301ac2..edfb236a91a 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -852,7 +852,7 @@ class Repository
@root_ref_sha ||= commit(root_ref).sha
end
- delegate :merged_branch_names, to: :raw_repository
+ delegate :merged_branch_names, :can_be_merged?, to: :raw_repository
def merge_base(first_commit_id, second_commit_id)
first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
@@ -951,7 +951,7 @@ class Repository
end
instance_variable_set(ivar, value)
- rescue Rugged::ReferenceError, Gitlab::Git::Repository::NoRepository
+ rescue Gitlab::Git::Repository::NoRepository
# Even if the above `#exists?` check passes these errors might still
# occur (for example because of a non-existing HEAD). We want to
# gracefully handle this and not cache anything
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 509f559c120..d251f75a8fd 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -138,20 +138,12 @@
GitLab API
%span.pull-right
= API::API::version
- %p
- Gitaly
- %span.pull-right
- = Gitlab::GitalyClient.expected_server_version
- if Gitlab.config.pages.enabled
%p
GitLab Pages
%span.pull-right
= Gitlab::Pages::VERSION
%p
- Git
- %span.pull-right
- = Gitlab::Git.version
- %p
Ruby
%span.pull-right
#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
@@ -163,6 +155,8 @@
= Gitlab::Database.adapter_name
%span.pull-right
= Gitlab::Database.version
+ %p
+ = link_to "Gitaly Servers", admin_gitaly_servers_path
.row
.col-md-4
.info-well
diff --git a/app/views/admin/gitaly_servers/index.html.haml b/app/views/admin/gitaly_servers/index.html.haml
new file mode 100644
index 00000000000..231f94dc95d
--- /dev/null
+++ b/app/views/admin/gitaly_servers/index.html.haml
@@ -0,0 +1,31 @@
+- breadcrumb_title _("Gitaly Servers")
+
+%h3.page-title= _("Gitaly Servers")
+%hr
+.gitaly_servers
+ - if @gitaly_servers.any?
+ .table-holder
+ %table.table.responsive-table
+ %thead.hidden-sm.hidden-xs
+ %tr
+ %th= _("Storage")
+ %th= n_("Gitaly|Address")
+ %th= _("Server version")
+ %th= _("Git version")
+ %th= _("Up to date")
+ - @gitaly_servers.each do |server|
+ %tr
+ %td
+ = server.storage
+ %td
+ = server.address
+ %td
+ = server.server_version
+ %td
+ = server.git_binary_version
+ %td
+ = boolean_to_icon(server.up_to_date?)
+ - else
+ .empty-state
+ .text-center
+ %h4= _("No connection could be made to a Gitaly Server, please check your logs!")
diff --git a/changelogs/unreleased/fix-install-docs.yml b/changelogs/unreleased/fix-install-docs.yml
new file mode 100644
index 00000000000..c2c0dd1364b
--- /dev/null
+++ b/changelogs/unreleased/fix-install-docs.yml
@@ -0,0 +1,5 @@
+---
+title: Update minimum git version to 2.9.5
+merge_request: 16683
+author:
+type: other
diff --git a/changelogs/unreleased/sh-fix-events-collection.yml b/changelogs/unreleased/sh-fix-events-collection.yml
new file mode 100644
index 00000000000..50af39d9caf
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-events-collection.yml
@@ -0,0 +1,5 @@
+---
+title: Fix not all events being shown in group dashboard
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-project-members-api-perf.yml b/changelogs/unreleased/sh-fix-project-members-api-perf.yml
new file mode 100644
index 00000000000..c3fff933547
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-project-members-api-perf.yml
@@ -0,0 +1,6 @@
+---
+title: Remove N+1 queries with /projects/:project_id/{access_requests,members} API
+ endpoints
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/zj-gitaly-server-info.yml b/changelogs/unreleased/zj-gitaly-server-info.yml
new file mode 100644
index 00000000000..cf6295f2bbc
--- /dev/null
+++ b/changelogs/unreleased/zj-gitaly-server-info.yml
@@ -0,0 +1,5 @@
+---
+title: Add Gitaly Servers admin dashboard
+merge_request:
+author:
+type: added
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index e22fb440abc..57e401c9b89 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -24,6 +24,8 @@ namespace :admin do
resource :impersonation, only: :destroy
resources :abuse_reports, only: [:index, :destroy]
+ resources :gitaly_servers, only: [:index]
+
resources :spam_logs, only: [:index, :destroy] do
member do
post :mark_as_ham
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 18e29271d0f..6eb8890cc4f 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -80,7 +80,7 @@ Make sure you have the right version of Git installed
# Install Git
sudo apt-get install -y git-core
- # Make sure Git is version 2.14.3 or higher
+ # Make sure Git is version 2.9.5 or higher
git --version
Is the system packaged Git too old? Remove it and compile from source.
@@ -93,9 +93,9 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source
cd /tmp
- curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.8.4.tar.gz
- echo '626e319f8a24fc0866167ea5f6bf3e2f38f69d6cb2e59e150f13709ca3ebf301 git-2.8.4.tar.gz' | shasum -a256 -c - && tar -xzf git-2.8.4.tar.gz
- cd git-2.8.4/
+ curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.14.3.tar.gz
+ echo '023ffff6d3ba8a1bea779dfecc0ed0bb4ad68ab8601d14435dd8c08416f78d7f git-2.14.3.tar.gz' | shasum -a256 -c - && tar -xzf git-2.14.3.tar.gz
+ cd git-2.14.3/
./configure
make prefix=/usr/local all
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb
index 374b611f55e..60ae5e6b9a2 100644
--- a/lib/api/access_requests.rb
+++ b/lib/api/access_requests.rb
@@ -24,7 +24,7 @@ module API
access_requesters = AccessRequestsFinder.new(source).execute!(current_user)
access_requesters = paginate(access_requesters.includes(:user))
- present access_requesters.map(&:user), with: Entities::AccessRequester, source: source
+ present access_requesters, with: Entities::AccessRequester
end
desc "Requests access for the authenticated user to a #{source_type}." do
@@ -36,7 +36,7 @@ module API
access_requester = source.request_access(current_user)
if access_requester.persisted?
- present access_requester.user, with: Entities::AccessRequester, access_requester: access_requester
+ present access_requester, with: Entities::AccessRequester
else
render_validation_error!(access_requester)
end
@@ -56,7 +56,7 @@ module API
member = ::Members::ApproveAccessRequestService.new(source, current_user, declared_params).execute
status :created
- present member.user, with: Entities::Member, member: member
+ present member, with: Entities::Member
end
desc 'Denies an access request for the given user.' do
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index cb222697f32..e13463ec66b 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -205,22 +205,15 @@ module API
expose :build_artifacts_size, as: :job_artifacts_size
end
- class Member < UserBasic
- expose :access_level do |user, options|
- member = options[:member] || options[:source].members.find_by(user_id: user.id)
- member.access_level
- end
- expose :expires_at do |user, options|
- member = options[:member] || options[:source].members.find_by(user_id: user.id)
- member.expires_at
- end
+ class Member < Grape::Entity
+ expose :user, merge: true, using: UserBasic
+ expose :access_level
+ expose :expires_at
end
- class AccessRequester < UserBasic
- expose :requested_at do |user, options|
- access_requester = options[:access_requester] || options[:source].requesters.find_by(user_id: user.id)
- access_requester.requested_at
- end
+ class AccessRequester < Grape::Entity
+ expose :user, merge: true, using: UserBasic
+ expose :requested_at
end
class Group < Grape::Entity
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 130c6d6da71..bc1de37284a 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -21,10 +21,11 @@ module API
get ":id/members" do
source = find_source(source_type, params[:id])
- users = source.users
- users = users.merge(User.search(params[:query])) if params[:query].present?
+ members = source.members.where.not(user_id: nil).includes(:user)
+ members = members.joins(:user).merge(User.search(params[:query])) if params[:query].present?
+ members = paginate(members)
- present paginate(users), with: Entities::Member, source: source
+ present members, with: Entities::Member
end
desc 'Gets a member of a group or project.' do
@@ -39,7 +40,7 @@ module API
members = source.members
member = members.find_by!(user_id: params[:user_id])
- present member.user, with: Entities::Member, member: member
+ present member, with: Entities::Member
end
desc 'Adds a member to a group or project.' do
@@ -62,7 +63,7 @@ module API
if !member
not_allowed! # This currently can only be reached in EE
elsif member.persisted? && member.valid?
- present member.user, with: Entities::Member, member: member
+ present member, with: Entities::Member
else
render_validation_error!(member)
end
@@ -83,7 +84,7 @@ module API
member = source.members.find_by!(user_id: params.delete(:user_id))
if member.update_attributes(declared_params(include_missing: false))
- present member.user, with: Entities::Member, member: member
+ present member, with: Entities::Member
else
render_validation_error!(member)
end
diff --git a/lib/api/v3/members.rb b/lib/api/v3/members.rb
index 46145cac7a5..d7bde8ceb89 100644
--- a/lib/api/v3/members.rb
+++ b/lib/api/v3/members.rb
@@ -22,10 +22,11 @@ module API
get ":id/members" do
source = find_source(source_type, params[:id])
- users = source.users
- users = users.merge(User.search(params[:query])) if params[:query].present?
+ members = source.members.where.not(user_id: nil).includes(:user)
+ members = members.joins(:user).merge(User.search(params[:query])) if params[:query].present?
+ members = paginate(members)
- present paginate(users), with: ::API::Entities::Member, source: source
+ present members, with: ::API::Entities::Member
end
desc 'Gets a member of a group or project.' do
@@ -40,7 +41,7 @@ module API
members = source.members
member = members.find_by!(user_id: params[:user_id])
- present member.user, with: ::API::Entities::Member, member: member
+ present member, with: ::API::Entities::Member
end
desc 'Adds a member to a group or project.' do
@@ -69,7 +70,7 @@ module API
end
if member.persisted? && member.valid?
- present member.user, with: ::API::Entities::Member, member: member
+ present member, with: ::API::Entities::Member
else
# This is to ensure back-compatibility but 400 behavior should be used
# for all validation errors in 9.0!
@@ -93,7 +94,7 @@ module API
member = source.members.find_by!(user_id: params.delete(:user_id))
if member.update_attributes(declared_params(include_missing: false))
- present member.user, with: ::API::Entities::Member, member: member
+ present member, with: ::API::Entities::Member
else
# This is to ensure back-compatibility but 400 behavior should be used
# for all validation errors in 9.0!
@@ -125,7 +126,7 @@ module API
else
::Members::DestroyService.new(source, current_user, declared_params).execute
- present member.user, with: ::API::Entities::Member, member: member
+ present member, with: ::API::Entities::Member
end
end
end
diff --git a/lib/gitaly/server.rb b/lib/gitaly/server.rb
new file mode 100644
index 00000000000..605e93022e7
--- /dev/null
+++ b/lib/gitaly/server.rb
@@ -0,0 +1,43 @@
+module Gitaly
+ class Server
+ def self.all
+ Gitlab.config.repositories.storages.keys.map { |s| Gitaly::Server.new(s) }
+ end
+
+ attr_reader :storage
+
+ def initialize(storage)
+ @storage = storage
+ end
+
+ def server_version
+ info.server_version
+ end
+
+ def git_binary_version
+ info.git_version
+ end
+
+ def up_to_date?
+ server_version == Gitlab::GitalyClient.expected_server_version
+ end
+
+ def address
+ Gitlab::GitalyClient.address(@storage)
+ rescue RuntimeError => e
+ "Error getting the address: #{e.message}"
+ end
+
+ private
+
+ def info
+ @info ||=
+ begin
+ Gitlab::GitalyClient::ServerService.new(@storage).info
+ rescue GRPC::Unavailable, GRPC::GRPC::DeadlineExceeded
+ # This will show the server as being out of date
+ Gitaly::ServerInfoResponse.new(git_version: '', server_version: '')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 7127f7858ee..932552d3e80 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -462,7 +462,6 @@ module Gitlab
path: nil,
follow: false,
skip_merges: false,
- disable_walk: false,
after: nil,
before: nil
}
@@ -494,11 +493,7 @@ module Gitlab
return []
end
- if log_using_shell?(options)
- log_by_shell(sha, options)
- else
- log_by_walk(sha, options)
- end
+ log_by_shell(sha, options)
end
def count_commits(options)
@@ -1386,6 +1381,16 @@ module Gitlab
run_git(args).first.scrub.split(/^--$/)
end
+ def can_be_merged?(source_sha, target_branch)
+ gitaly_migrate(:can_be_merged) do |is_enabled|
+ if is_enabled
+ gitaly_can_be_merged?(source_sha, find_branch(target_branch, true).target)
+ else
+ rugged_can_be_merged?(source_sha, target_branch)
+ end
+ end
+ end
+
def search_files_by_name(query, ref)
safe_query = Regexp.escape(query.sub(/^\/*/, ""))
@@ -1635,24 +1640,6 @@ module Gitlab
end
end
- def log_using_shell?(options)
- options[:path].present? ||
- options[:disable_walk] ||
- options[:skip_merges] ||
- options[:after] ||
- options[:before]
- end
-
- def log_by_walk(sha, options)
- walk_options = {
- show: sha,
- sort: Rugged::SORT_NONE,
- limit: options[:limit],
- offset: options[:offset]
- }
- Rugged::Walker.walk(rugged, walk_options).to_a
- end
-
# Gitaly note: JV: although #log_by_shell shells out to Git I think the
# complexity is such that we should migrate it as Ruby before trying to
# do it in Go.
@@ -2280,6 +2267,14 @@ module Gitlab
run_git(['fetch', remote_name], env: env).last.zero?
end
+ def gitaly_can_be_merged?(their_commit, our_commit)
+ !gitaly_conflicts_client(our_commit, their_commit).conflicts?
+ end
+
+ def rugged_can_be_merged?(their_commit, our_commit)
+ !rugged.merge_commits(our_commit, their_commit).conflicts?
+ end
+
def gitlab_projects_error
raise CommandError, @gitlab_projects.output
end
diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb
index 5cf336af3c6..ba6058fd3c9 100644
--- a/lib/gitlab/git/tree.rb
+++ b/lib/gitlab/git/tree.rb
@@ -83,6 +83,8 @@ module Gitlab
commit_id: sha
)
end
+ rescue Rugged::ReferenceError
+ []
end
end
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index cadc7149301..5767f06b0ce 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -257,7 +257,7 @@ module Gitlab
offset: options[:offset],
follow: options[:follow],
skip_merges: options[:skip_merges],
- disable_walk: options[:disable_walk]
+ disable_walk: true # This option is deprecated. The 'walk' implementation is being removed.
)
request.after = GitalyClient.timestamp(options[:after]) if options[:after]
request.before = GitalyClient.timestamp(options[:before]) if options[:before]
diff --git a/lib/gitlab/gitaly_client/server_service.rb b/lib/gitlab/gitaly_client/server_service.rb
new file mode 100644
index 00000000000..2e1076d1f66
--- /dev/null
+++ b/lib/gitlab/gitaly_client/server_service.rb
@@ -0,0 +1,16 @@
+module Gitlab
+ module GitalyClient
+ # Meant for extraction of server data, and later maybe to perform misc task
+ #
+ # Not meant for connection logic, look in Gitlab::GitalyClient
+ class ServerService
+ def initialize(storage)
+ @storage = storage
+ end
+
+ def info
+ GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new)
+ end
+ end
+ end
+end
diff --git a/lib/system_check/app/git_version_check.rb b/lib/system_check/app/git_version_check.rb
index 6ee8c8874ec..44ec888c197 100644
--- a/lib/system_check/app/git_version_check.rb
+++ b/lib/system_check/app/git_version_check.rb
@@ -5,7 +5,7 @@ module SystemCheck
set_check_pass -> { "yes (#{self.current_version})" }
def self.required_version
- @required_version ||= Gitlab::VersionInfo.new(2, 7, 3)
+ @required_version ||= Gitlab::VersionInfo.new(2, 9, 5)
end
def self.current_version
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 74d76caf47d..94458d60e01 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-05 20:31+0100\n"
-"PO-Revision-Date: 2017-12-05 20:31+0100\n"
+"POT-Creation-Date: 2018-01-30 14:59+0100\n"
+"PO-Revision-Date: 2018-01-30 14:59+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -23,17 +23,27 @@ msgid_plural "%d commits"
msgstr[0] ""
msgstr[1] ""
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] ""
msgstr[1] ""
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] ""
msgstr[1] ""
-msgid "%{commit_author_link} committed %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
msgid "%{count} participant"
@@ -47,9 +57,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr ""
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr ""
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr ""
@@ -117,6 +124,21 @@ msgstr ""
msgid "Add new directory"
msgstr ""
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr ""
@@ -126,12 +148,18 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr ""
@@ -156,9 +184,6 @@ msgstr ""
msgid "Are you sure you want to discard your changes?"
msgstr ""
-msgid "Are you sure you want to leave this group?"
-msgstr ""
-
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -186,13 +211,13 @@ msgstr ""
msgid "Author"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -216,6 +241,9 @@ msgstr ""
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] ""
@@ -359,15 +387,24 @@ msgstr ""
msgid "ChangeTypeAction|Revert"
msgstr ""
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr ""
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -380,6 +417,15 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
msgid "CiStatusLabel|canceled"
msgstr ""
@@ -437,6 +483,9 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
+msgid "Click to expand text"
+msgstr ""
+
msgid "Clone repository"
msgstr ""
@@ -446,27 +495,24 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
-msgstr ""
-
msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Active"
-msgstr ""
-
msgid "ClusterIntegration|Add an existing cluster"
msgstr ""
msgid "ClusterIntegration|Add cluster"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "ClusterIntegration|Advanced options on this cluster's integration"
msgstr ""
msgid "ClusterIntegration|Applications"
msgstr ""
+msgid "ClusterIntegration|Are you sure you want to remove this cluster's integration? This will not delete your actual cluster."
+msgstr ""
+
msgid "ClusterIntegration|CA Certificate"
msgstr ""
@@ -476,6 +522,9 @@ msgstr ""
msgid "ClusterIntegration|Choose how to set up cluster integration"
msgstr ""
+msgid "ClusterIntegration|Choose which of your project's environments will use this cluster."
+msgstr ""
+
msgid "ClusterIntegration|Cluster"
msgstr ""
@@ -506,6 +555,12 @@ msgstr ""
msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
msgstr ""
+msgid "ClusterIntegration|Clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
+
+msgid "ClusterIntegration|Control how your cluster integrates with GitLab"
+msgstr ""
+
msgid "ClusterIntegration|Copy API URL"
msgstr ""
@@ -518,7 +573,7 @@ msgstr ""
msgid "ClusterIntegration|Copy cluster name"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create a new cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create cluster"
@@ -530,19 +585,16 @@ msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Enter the details for your cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -560,18 +612,12 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -581,27 +627,27 @@ msgstr ""
msgid "ClusterIntegration|Integrate cluster automation"
msgstr ""
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
msgid "ClusterIntegration|Learn more about Clusters"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
msgstr ""
msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
-msgstr ""
-
msgid "ClusterIntegration|Note:"
msgstr ""
@@ -614,12 +660,6 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
-msgstr ""
-
-msgid "ClusterIntegration|Problem setting up the clusters list"
-msgstr ""
-
msgid "ClusterIntegration|Project ID"
msgstr ""
@@ -629,6 +669,9 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
msgstr ""
@@ -638,7 +681,7 @@ msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this cluster's configuration from this project. This will not delete your actual cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -674,9 +717,6 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
-msgstr ""
-
msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
msgstr ""
@@ -698,6 +738,9 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
+
msgid "ClusterIntegration|cluster"
msgstr ""
@@ -745,15 +788,42 @@ msgstr ""
msgid "Commits feed"
msgstr ""
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
msgid "Commits|History"
msgstr ""
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr ""
msgid "Compare"
msgstr ""
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -805,6 +875,9 @@ msgstr ""
msgid "Contributors"
msgstr ""
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -874,9 +947,6 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr ""
-
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -930,6 +1000,9 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr ""
@@ -996,9 +1069,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1029,6 +1099,9 @@ msgstr ""
msgid "Environments|You don't have any environments right now."
msgstr ""
+msgid "Error fetching refs"
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -1121,12 +1194,21 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
+msgid "Generate a default set of labels"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr ""
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
@@ -1172,10 +1254,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTreeRole|as"
-msgstr ""
-
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1223,6 +1302,11 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "History"
msgstr ""
@@ -1235,6 +1319,9 @@ msgstr ""
msgid "Install a Runner compatible with GitLab CI"
msgstr ""
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1256,6 +1343,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1283,6 +1373,9 @@ msgstr ""
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -1327,10 +1420,8 @@ msgstr ""
msgid "Leave project"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] ""
-msgstr[1] ""
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
@@ -1368,9 +1459,27 @@ msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr ""
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -1409,6 +1518,9 @@ msgstr ""
msgid "New issue"
msgstr ""
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr ""
@@ -1427,7 +1539,10 @@ msgstr ""
msgid "New tag"
msgstr ""
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1505,6 +1620,12 @@ msgstr ""
msgid "Notifications"
msgstr ""
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1514,9 +1635,6 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
-msgstr ""
-
msgid "Oct"
msgstr ""
@@ -1559,9 +1677,6 @@ msgstr ""
msgid "Password"
msgstr ""
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr ""
@@ -1646,6 +1761,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr ""
@@ -1658,6 +1779,15 @@ msgstr ""
msgid "Pipeline|with stages"
msgstr ""
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a cluster</a>, then try again."
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr ""
+
msgid "Preferences"
msgstr ""
@@ -1721,6 +1851,15 @@ msgstr ""
msgid "Project access must be granted explicitly to each user."
msgstr ""
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr ""
@@ -1760,12 +1899,6 @@ msgstr ""
msgid "ProjectNetworkGraph|Graph"
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "Projects"
msgstr ""
@@ -1844,6 +1977,9 @@ msgstr ""
msgid "RefSwitcher|Tags"
msgstr ""
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Related Commits"
msgstr ""
@@ -1865,6 +2001,9 @@ msgstr ""
msgid "Remind later"
msgstr ""
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr ""
@@ -1883,6 +2022,11 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Revert this commit"
msgstr ""
@@ -1892,9 +2036,6 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
@@ -1916,9 +2057,6 @@ msgstr ""
msgid "Seconds before reseting failure information"
msgstr ""
-msgid "Seconds to wait after a storage failure"
-msgstr ""
-
msgid "Seconds to wait for a storage access attempt"
msgstr ""
@@ -1928,6 +2066,9 @@ msgstr ""
msgid "Select a timezone"
msgstr ""
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr ""
@@ -1937,6 +2078,9 @@ msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
@@ -1975,7 +2119,7 @@ msgstr ""
msgid "Something went wrong on our end."
msgstr ""
-msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
+msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -1984,6 +2128,9 @@ msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2104,10 +2251,10 @@ msgstr ""
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2199,7 +2346,10 @@ msgstr ""
msgid "Team"
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2214,10 +2364,10 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2226,9 +2376,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr ""
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -2259,16 +2406,25 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2283,12 +2439,36 @@ msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Time before an issue gets scheduled"
msgstr ""
@@ -2437,6 +2617,12 @@ msgstr[1] ""
msgid "Time|s"
msgstr ""
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr ""
@@ -2446,6 +2632,12 @@ msgstr ""
msgid "Total test time for all commits/merges"
msgstr ""
+msgid "Trigger this manual action"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
msgid "Unlock"
msgstr ""
@@ -2455,7 +2647,7 @@ msgstr ""
msgid "Unstar"
msgstr ""
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upload New File"
@@ -2464,6 +2656,9 @@ msgstr ""
msgid "Upload file"
msgstr ""
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr ""
@@ -2497,10 +2692,13 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
+msgid "We want to be sure it is you, please confirm you are not a robot."
msgstr ""
msgid "Wiki"
@@ -2623,9 +2821,15 @@ msgstr ""
msgid "You are on a read-only GitLab instance."
msgstr ""
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr ""
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
@@ -2662,6 +2866,9 @@ msgstr ""
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2682,12 +2889,113 @@ msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr ""
msgid "notification emails"
msgstr ""
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
diff --git a/qa/qa.rb b/qa/qa.rb
index bd24f241747..8630e2a522c 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -150,6 +150,13 @@ module QA
autoload :Main, 'qa/page/mattermost/main'
autoload :Login, 'qa/page/mattermost/login'
end
+
+ ##
+ # Classes describing components that are used by several pages.
+ #
+ module Component
+ autoload :Dropzone, 'qa/page/component/dropzone'
+ end
end
##
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 7a2d9731205..5c3af4b9115 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -97,21 +97,6 @@ module QA
views.map(&:errors).flatten
end
- # Not tested and not expected to work with multiple dropzones
- # instantiated on one page because there is no distinguishing
- # attribute per dropzone file field.
- def attach_file_to_dropzone(attachment, dropzone_form_container)
- filename = File.basename(attachment)
-
- field_style = { visibility: 'visible', height: '', width: '' }
- attach_file(attachment, class: 'dz-hidden-input', make_visible: field_style)
-
- # Wait for link to be appended to dropzone text
- wait(reload: false) do
- find("#{dropzone_form_container} textarea").value.match(filename)
- end
- end
-
class DSL
attr_reader :views
diff --git a/qa/qa/page/component/dropzone.rb b/qa/qa/page/component/dropzone.rb
new file mode 100644
index 00000000000..5e6fdff20eb
--- /dev/null
+++ b/qa/qa/page/component/dropzone.rb
@@ -0,0 +1,29 @@
+module QA
+ module Page
+ module Component
+ class Dropzone
+ attr_reader :page, :container
+
+ def initialize(page, container)
+ @page = page
+ @container = container
+ end
+
+ # Not tested and not expected to work with multiple dropzones
+ # instantiated on one page because there is no distinguishing
+ # attribute per dropzone file field.
+ def attach_file(attachment)
+ filename = File.basename(attachment)
+
+ field_style = { visibility: 'visible', height: '', width: '' }
+ page.attach_file(attachment, class: 'dz-hidden-input', make_visible: field_style)
+
+ # Wait for link to be appended to dropzone text
+ page.wait(reload: false) do
+ page.find("#{container} textarea").value.match(filename)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index 10644c0fecc..364a2c61665 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -23,10 +23,13 @@ module QA
# Adds a comment to an issue
# attachment option should be an absolute path
- def comment(text, attachment:)
+ def comment(text, attachment: nil)
fill_in(with: text, name: 'note[note]')
- attach_file_to_dropzone(attachment, '.new-note') if attachment
+ unless attachment.nil?
+ QA::Page::Component::Dropzone.new(page, '.new-note')
+ .attach_file(attachment)
+ end
click_on 'Comment'
end
diff --git a/spec/controllers/admin/gitaly_servers_controller_spec.rb b/spec/controllers/admin/gitaly_servers_controller_spec.rb
new file mode 100644
index 00000000000..b7378aa37d0
--- /dev/null
+++ b/spec/controllers/admin/gitaly_servers_controller_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe Admin::GitalyServersController do
+ describe '#index' do
+ before do
+ sign_in(create(:admin))
+ end
+
+ it 'shows the gitaly servers page' do
+ get :index
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+end
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index a9cfd964dd5..492fed42d31 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -85,6 +85,30 @@ describe GroupsController do
end
end
+ describe 'GET #activity' do
+ render_views
+
+ before do
+ sign_in(user)
+ project
+ end
+
+ context 'as json' do
+ it 'includes all projects in event feed' do
+ 3.times do
+ project = create(:project, group: group)
+ create(:event, project: project)
+ end
+
+ get :activity, id: group.to_param, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['count']).to eq(3)
+ expect(assigns(:projects).limit_value).to be_nil
+ end
+ end
+ end
+
describe 'POST #create' do
context 'when creating subgroups', :nested_groups do
[true, false].each do |can_create_group_status|
diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js
index feb341d22e6..0452934ea9e 100644
--- a/spec/javascripts/job_spec.js
+++ b/spec/javascripts/job_spec.js
@@ -58,8 +58,7 @@ describe('Job', () => {
it('updates the build trace on an interval', function () {
const deferred1 = $.Deferred();
const deferred2 = $.Deferred();
- const deferred3 = $.Deferred();
- spyOn($, 'ajax').and.returnValues(deferred1.promise(), deferred2.promise(), deferred3.promise());
+ spyOn($, 'ajax').and.returnValues(deferred1.promise(), deferred2.promise());
spyOn(urlUtils, 'visitUrl');
deferred1.resolve({
@@ -70,9 +69,7 @@ describe('Job', () => {
complete: false,
});
- deferred2.resolve();
-
- deferred3.resolve({
+ deferred2.resolve({
html: '<span>More</span>',
status: 'running',
state: 'finalstate',
@@ -94,9 +91,8 @@ describe('Job', () => {
it('replaces the entire build trace', () => {
const deferred1 = $.Deferred();
const deferred2 = $.Deferred();
- const deferred3 = $.Deferred();
- spyOn($, 'ajax').and.returnValues(deferred1.promise(), deferred2.promise(), deferred3.promise());
+ spyOn($, 'ajax').and.returnValues(deferred1.promise(), deferred2.promise());
spyOn(urlUtils, 'visitUrl');
@@ -107,9 +103,7 @@ describe('Job', () => {
complete: false,
});
- deferred2.resolve();
-
- deferred3.resolve({
+ deferred2.resolve({
html: '<span>Different</span>',
status: 'running',
append: false,
@@ -170,9 +164,8 @@ describe('Job', () => {
it('shows incremented size', () => {
const deferred1 = $.Deferred();
const deferred2 = $.Deferred();
- const deferred3 = $.Deferred();
- spyOn($, 'ajax').and.returnValues(deferred1.promise(), deferred2.promise(), deferred3.promise());
+ spyOn($, 'ajax').and.returnValues(deferred1.promise(), deferred2.promise());
spyOn(urlUtils, 'visitUrl');
@@ -184,8 +177,6 @@ describe('Job', () => {
total: 100,
});
- deferred2.resolve();
-
this.job = new Job();
expect(
@@ -194,7 +185,7 @@ describe('Job', () => {
jasmine.clock().tick(4001);
- deferred3.resolve({
+ deferred2.resolve({
html: '<span>Update</span>',
status: 'success',
append: true,
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 0a9d815f469..1052b4e7c20 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -1,6 +1,8 @@
/* eslint-disable promise/catch-or-return */
import * as commonUtils from '~/lib/utils/common_utils';
+import axios from '~/lib/utils/axios_utils';
+import MockAdapter from 'axios-mock-adapter';
describe('common_utils', () => {
describe('parseUrl', () => {
@@ -413,37 +415,48 @@ describe('common_utils', () => {
describe('setCiStatusFavicon', () => {
const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1/status.json`;
+ let mock;
beforeEach(() => {
const favicon = document.createElement('link');
favicon.setAttribute('id', 'favicon');
document.body.appendChild(favicon);
+ mock = new MockAdapter(axios);
});
afterEach(() => {
+ mock.restore();
document.body.removeChild(document.getElementById('favicon'));
});
- it('should reset favicon in case of error', () => {
- const favicon = document.getElementById('favicon');
- spyOn($, 'ajax').and.callFake(function (options) {
- options.error();
- expect(favicon.getAttribute('href')).toEqual('null');
- });
+ it('should reset favicon in case of error', (done) => {
+ mock.onGet(BUILD_URL).networkError();
- commonUtils.setCiStatusFavicon(BUILD_URL);
+ commonUtils.setCiStatusFavicon(BUILD_URL)
+ .then(() => {
+ const favicon = document.getElementById('favicon');
+ expect(favicon.getAttribute('href')).toEqual('null');
+ done();
+ })
+ // Error is already caught in catch() block of setCiStatusFavicon,
+ // It won't throw another error for us to catch
+ .catch(done.fail);
});
- it('should set page favicon to CI status favicon based on provided status', () => {
+ it('should set page favicon to CI status favicon based on provided status', (done) => {
const FAVICON_PATH = '//icon_status_success';
- const favicon = document.getElementById('favicon');
- spyOn($, 'ajax').and.callFake(function (options) {
- options.success({ favicon: FAVICON_PATH });
- expect(favicon.getAttribute('href')).toEqual(FAVICON_PATH);
+ mock.onGet(BUILD_URL).reply(200, {
+ favicon: FAVICON_PATH,
});
- commonUtils.setCiStatusFavicon(BUILD_URL);
+ commonUtils.setCiStatusFavicon(BUILD_URL)
+ .then(() => {
+ const favicon = document.getElementById('favicon');
+ expect(favicon.getAttribute('href')).toEqual(FAVICON_PATH);
+ done();
+ })
+ .catch(done.fail);
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
index f86fb6a0b4b..637bf483deb 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
@@ -1,117 +1,82 @@
import Vue from 'vue';
-import relatedLinksComponent from '~/vue_merge_request_widget/components/mr_widget_related_links';
+import relatedLinksComponent from '~/vue_merge_request_widget/components/mr_widget_related_links.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
-const createComponent = (data) => {
- const Component = Vue.extend(relatedLinksComponent);
+describe('MRWidgetRelatedLinks', () => {
+ let vm;
- return new Component({
- el: document.createElement('div'),
- propsData: data,
- });
-};
+ const createComponent = (data) => {
+ const Component = Vue.extend(relatedLinksComponent);
-describe('MRWidgetRelatedLinks', () => {
- describe('props', () => {
- it('should have props', () => {
- const { relatedLinks } = relatedLinksComponent.props;
+ return mountComponent(Component, data);
+ };
- expect(relatedLinks).toBeDefined();
- expect(relatedLinks.type instanceof Object).toBeTruthy();
- expect(relatedLinks.required).toBeTruthy();
- });
+ afterEach(() => {
+ vm.$destroy();
});
describe('computed', () => {
- const data = {
- relatedLinks: {
- closing: '/foo',
- mentioned: '/foo',
- assignToMe: '/foo',
- },
- };
-
- describe('hasLinks', () => {
- it('should return correct value when we have links reference', () => {
- const vm = createComponent(data);
- expect(vm.hasLinks).toBeTruthy();
-
- vm.relatedLinks.closing = null;
- expect(vm.hasLinks).toBeTruthy();
-
- vm.relatedLinks.mentioned = null;
- expect(vm.hasLinks).toBeTruthy();
-
- vm.relatedLinks.assignToMe = null;
- expect(vm.hasLinks).toBeFalsy();
- });
- });
-
describe('closesText', () => {
- it('returns correct text for open merge request', () => {
- data.state = 'open';
- const vm = createComponent(data);
+ it('returns Closes text for open merge request', () => {
+ vm = createComponent({ state: 'open', relatedLinks: {} });
expect(vm.closesText).toEqual('Closes');
});
it('returns correct text for closed merge request', () => {
- data.state = 'closed';
- const vm = createComponent(data);
+ vm = createComponent({ state: 'closed', relatedLinks: {} });
expect(vm.closesText).toEqual('Did not close');
});
it('returns correct tense for merged request', () => {
- data.state = 'merged';
- const vm = createComponent(data);
+ vm = createComponent({ state: 'merged', relatedLinks: {} });
expect(vm.closesText).toEqual('Closed');
});
});
});
- describe('template', () => {
- it('should have only have closing issues text', () => {
- const vm = createComponent({
- relatedLinks: {
- closing: '<a href="#">#23</a> and <a>#42</a>',
- },
- });
- const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
-
- expect(content).toContain('Closes #23 and #42');
- expect(content).not.toContain('Mentions');
+ it('should have only have closing issues text', () => {
+ vm = createComponent({
+ relatedLinks: {
+ closing: '<a href="#">#23</a> and <a>#42</a>',
+ },
});
+ const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
- it('should have only have mentioned issues text', () => {
- const vm = createComponent({
- relatedLinks: {
- mentioned: '<a href="#">#7</a>',
- },
- });
+ expect(content).toContain('Closes #23 and #42');
+ expect(content).not.toContain('Mentions');
+ });
- expect(vm.$el.innerText).toContain('Mentions #7');
- expect(vm.$el.innerText).not.toContain('Closes');
+ it('should have only have mentioned issues text', () => {
+ vm = createComponent({
+ relatedLinks: {
+ mentioned: '<a href="#">#7</a>',
+ },
});
- it('should have closing and mentioned issues at the same time', () => {
- const vm = createComponent({
- relatedLinks: {
- closing: '<a href="#">#7</a>',
- mentioned: '<a href="#">#23</a> and <a>#42</a>',
- },
- });
- const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
+ expect(vm.$el.innerText).toContain('Mentions #7');
+ expect(vm.$el.innerText).not.toContain('Closes');
+ });
- expect(content).toContain('Closes #7');
- expect(content).toContain('Mentions #23 and #42');
+ it('should have closing and mentioned issues at the same time', () => {
+ vm = createComponent({
+ relatedLinks: {
+ closing: '<a href="#">#7</a>',
+ mentioned: '<a href="#">#23</a> and <a>#42</a>',
+ },
});
+ const content = vm.$el.textContent.replace(/\n(\s)+/g, ' ').trim();
- it('should have assing issues link', () => {
- const vm = createComponent({
- relatedLinks: {
- assignToMe: '<a href="#">Assign yourself to these issues</a>',
- },
- });
+ expect(content).toContain('Closes #7');
+ expect(content).toContain('Mentions #23 and #42');
+ });
- expect(vm.$el.innerText).toContain('Assign yourself to these issues');
+ it('should have assing issues link', () => {
+ vm = createComponent({
+ relatedLinks: {
+ assignToMe: '<a href="#">Assign yourself to these issues</a>',
+ },
});
+
+ expect(vm.$el.innerText).toContain('Assign yourself to these issues');
});
});
diff --git a/spec/lib/gitaly/server_spec.rb b/spec/lib/gitaly/server_spec.rb
new file mode 100644
index 00000000000..ed5d56e91d4
--- /dev/null
+++ b/spec/lib/gitaly/server_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+describe Gitaly::Server do
+ describe '.all' do
+ let(:storages) { Gitlab.config.repositories.storages }
+
+ it 'includes all storages' do
+ expect(storages.count).to eq(described_class.all.count)
+ expect(storages.keys).to eq(described_class.all.map(&:storage))
+ end
+ end
+
+ subject { described_class.all.first }
+
+ it { is_expected.to respond_to(:server_version) }
+ it { is_expected.to respond_to(:git_binary_version) }
+ it { is_expected.to respond_to(:up_to_date?) }
+ it { is_expected.to respond_to(:address) }
+
+ describe 'request memoization' do
+ context 'when requesting multiple properties', :request_store do
+ it 'uses memoization for the info request' do
+ expect do
+ subject.server_version
+ subject.up_to_date?
+ end.to change { Gitlab::GitalyClient.get_request_count }.by(1)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index bf01e6ef8e8..e8e4a832115 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -905,44 +905,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context "compare results between log_by_walk and log_by_shell" do
- let(:options) { { ref: "master" } }
- let(:commits_by_walk) { repository.log(options).map(&:id) }
- let(:commits_by_shell) { repository.log(options.merge({ disable_walk: true })).map(&:id) }
-
- it { expect(commits_by_walk).to eq(commits_by_shell) }
-
- context "with limit" do
- let(:options) { { ref: "master", limit: 1 } }
-
- it { expect(commits_by_walk).to eq(commits_by_shell) }
- end
-
- context "with offset" do
- let(:options) { { ref: "master", offset: 1 } }
-
- it { expect(commits_by_walk).to eq(commits_by_shell) }
- end
-
- context "with skip_merges" do
- let(:options) { { ref: "master", skip_merges: true } }
-
- it { expect(commits_by_walk).to eq(commits_by_shell) }
- end
-
- context "with path" do
- let(:options) { { ref: "master", path: "encoding" } }
-
- it { expect(commits_by_walk).to eq(commits_by_shell) }
-
- context "with follow" do
- let(:options) { { ref: "master", path: "encoding", follow: true } }
-
- it { expect(commits_by_walk).to eq(commits_by_shell) }
- end
- end
- end
-
context "where provides 'after' timestamp" do
options = { after: Time.iso8601('2014-03-03T20:15:01+00:00') }
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 86f7bcb8e38..001e406a930 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -80,22 +80,18 @@ describe Gitlab::Git::Tree, seed_helper: true do
end
describe '#where' do
- context 'with gitaly disabled' do
- before do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
- end
-
- it 'calls #tree_entries_from_rugged' do
- expect(described_class).to receive(:tree_entries_from_rugged)
-
- described_class.where(repository, SeedRepo::Commit::ID, '/')
+ shared_examples '#where' do
+ it 'returns an empty array when called with an invalid ref' do
+ expect(described_class.where(repository, 'foobar-does-not-exist')).to eq([])
end
end
- it 'gets the tree entries from GitalyClient' do
- expect_any_instance_of(Gitlab::GitalyClient::CommitService).to receive(:tree_entries)
+ context 'with gitaly' do
+ it_behaves_like '#where'
+ end
- described_class.where(repository, SeedRepo::Commit::ID, '/')
+ context 'without gitaly', :skip_gitaly_mock do
+ it_behaves_like '#where'
end
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index d4070b320ed..1102b1c9006 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -772,8 +772,7 @@ describe Repository do
user, 'LICENSE', 'Copyright!',
message: 'Add LICENSE', branch_name: 'master')
- allow(repository).to receive(:file_on_head)
- .and_raise(Rugged::ReferenceError)
+ allow(repository).to receive(:root_ref).and_raise(Gitlab::Git::Repository::NoRepository)
expect(repository.license_blob).to be_nil
end
@@ -885,7 +884,7 @@ describe Repository do
end
it 'returns nil for empty repository' do
- allow(repository).to receive(:file_on_head).and_raise(Rugged::ReferenceError)
+ allow(repository).to receive(:root_ref).and_raise(Gitlab::Git::Repository::NoRepository)
expect(repository.gitlab_ci_yml).to be_nil
end
end
@@ -1937,8 +1936,7 @@ describe Repository do
describe '#avatar' do
it 'returns nil if repo does not exist' do
- expect(repository).to receive(:file_on_head)
- .and_raise(Rugged::ReferenceError)
+ allow(repository).to receive(:root_ref).and_raise(Gitlab::Git::Repository::NoRepository)
expect(repository.avatar).to eq(nil)
end
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 73bd4785b11..ec500838eb2 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -44,6 +44,21 @@ describe API::Members do
end
end
+ it 'avoids N+1 queries' do
+ # Establish baseline
+ get api("/#{source_type.pluralize}/#{source.id}/members", master)
+
+ control = ActiveRecord::QueryRecorder.new do
+ get api("/#{source_type.pluralize}/#{source.id}/members", master)
+ end
+
+ project.add_developer(create(:user))
+
+ expect do
+ get api("/#{source_type.pluralize}/#{source.id}/members", master)
+ end.not_to exceed_query_limit(control)
+ end
+
it 'does not return invitees' do
create(:"#{source_type}_member", invite_token: '123', invite_email: 'test@abc.com', source: source, user: nil)