Commit f8eb89db authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'rc/ce-to-ee-wednesday' into 'master'

CE Upstream - Wednesday

Closes gitaly#136

See merge request !1489
parents 8d2c04ec 4ab0822f
......@@ -22,7 +22,7 @@ before_script:
- source ./scripts/prepare_build.sh
- cp config/gitlab.yml.example config/gitlab.yml
- bundle --version
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) $FLAGS'
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) --clean $FLAGS'
- retry gem install knapsack
- '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate add_limits_mysql'
- '[ "$SETUP_DB" != "true" ] || bundle exec rake geo:db:drop geo:db:create geo:db:schema:load geo:db:migrate'
......
This diff is collapsed.
<svg width="12" height="15" viewBox="0 0 12 15" xmlns="http://www.w3.org/2000/svg"><path d="M10.267 11.028V5.167c-.028-.728-.318-1.372-.878-1.923-.56-.55-1.194-.85-1.922-.877h-.934V.5l-2.8 2.8 2.8 2.8V4.233h.934a.976.976 0 0 1 .644.29.88.88 0 0 1 .289.644v5.861a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472zM3.733 3.3a1.86 1.86 0 0 0-1.866-1.867 1.86 1.86 0 0 0-.934 3.472v6.123a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472V4.905c.55-.317.933-.914.933-1.605z" fill-rule="nonzero"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m5 5.563v4.875c1.024.4 1.75 1.397 1.75 2.563 0 1.519-1.231 2.75-2.75 2.75-1.519 0-2.75-1.231-2.75-2.75 0-1.166.726-2.162 1.75-2.563v-4.875c-1.024-.4-1.75-1.397-1.75-2.563 0-1.519 1.231-2.75 2.75-2.75 1.519 0 2.75 1.231 2.75 2.75 0 1.166-.726 2.162-1.75 2.563m-1 8.687c.69 0 1.25-.56 1.25-1.25 0-.69-.56-1.25-1.25-1.25-.69 0-1.25.56-1.25 1.25 0 .69.56 1.25 1.25 1.25m0-10c.69 0 1.25-.56 1.25-1.25 0-.69-.56-1.25-1.25-1.25-.69 0-1.25.56-1.25 1.25 0 .69.56 1.25 1.25 1.25"/><path d="m10.501 2c1.381.001 2.499 1.125 2.499 2.506v5.931c1.024.4 1.75 1.397 1.75 2.563 0 1.519-1.231 2.75-2.75 2.75-1.519 0-2.75-1.231-2.75-2.75 0-1.166.726-2.162 1.75-2.563v-5.931c0-.279-.225-.506-.499-.506v.926c0 .346-.244.474-.569.271l-2.952-1.844c-.314-.196-.325-.507 0-.71l2.952-1.844c.314-.196.569-.081.569.271v.93m1.499 12.25c.69 0 1.25-.56 1.25-1.25 0-.69-.56-1.25-1.25-1.25-.69 0-1.25.56-1.25 1.25 0 .69.56 1.25 1.25 1.25"/></svg>
\ No newline at end of file
/* global merge_request_widget */
const Vue = require('vue');
import Vue from 'vue';
require('./widget_store');
require('./approvals/approvals_bundle');
......
/* eslint-disable no-new */
/* eslint-disable no-new*/
/* global Flash */
import d3 from 'd3';
......@@ -180,7 +180,7 @@ class PrometheusGraph {
// Metric Usage
axisLabelContainer.append('rect')
.attr('x', this.originalWidth - 170)
.attr('y', (this.originalHeight / 2) - 80)
.attr('y', (this.originalHeight / 2) - 60)
.style('fill', graphSpecifics.area_fill_color)
.attr('width', 20)
.attr('height', 35);
......@@ -188,13 +188,13 @@ class PrometheusGraph {
axisLabelContainer.append('text')
.attr('class', 'label-axis-text')
.attr('x', this.originalWidth - 140)
.attr('y', (this.originalHeight / 2) - 65)
.text(graphSpecifics.graph_legend_title);
.attr('y', (this.originalHeight / 2) - 50)
.text('Average');
axisLabelContainer.append('text')
.attr('class', 'text-metric-usage')
.attr('x', this.originalWidth - 140)
.attr('y', (this.originalHeight / 2) - 50);
.attr('y', (this.originalHeight / 2) - 25);
}
handleMouseOverGraph(x, y, valuesToPlot, chart, prometheusGraphContainer, key) {
......@@ -263,12 +263,12 @@ class PrometheusGraph {
cpu_values: {
area_fill_color: '#edf3fc',
line_color: '#5b99f7',
graph_legend_title: 'CPU Usage (Cores)',
graph_legend_title: 'CPU utilization (%)',
},
memory_values: {
area_fill_color: '#fca326',
line_color: '#fc6d26',
graph_legend_title: 'Memory Usage (MB)',
graph_legend_title: 'Memory usage (MB)',
},
};
......
......@@ -22,6 +22,9 @@ require('./shortcuts');
Mousetrap.bind('g m', function() {
return ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-merge_requests');
});
Mousetrap.bind('g t', function() {
return ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-todos');
});
Mousetrap.bind('g p', function() {
return ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-projects');
});
......
......@@ -43,6 +43,9 @@ require('./shortcuts');
Mousetrap.bind('g m', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-merge_requests');
});
Mousetrap.bind('g t', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-todos');
});
Mousetrap.bind('g w', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-wiki');
});
......
/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign */
/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign, class-methods-use-this */
/*
UserTabs
......@@ -82,8 +82,19 @@ content on the Users#show page.
}
bindEvents() {
return this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this);
this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
.on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event));
this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper);
}
changeProjectsPage(e) {
e.preventDefault();
$('.tab-pane.active').empty();
this.loadTab($(e.target).attr('href'), this.getCurrentAction());
}
tabShown(event) {
......@@ -119,7 +130,7 @@ content on the Users#show page.
complete: () => this.toggleLoading(false),
dataType: 'json',
type: 'GET',
url: `${source}.json`,
url: source,
success: (data) => {
const tabSelector = `div#${action}`;
this.$parentEl.find(tabSelector).html(data.html);
......@@ -153,6 +164,10 @@ content on the Users#show page.
}, document.title, new_state);
return new_state;
}
getCurrentAction() {
return this.$parentEl.find('.nav-links .active a').data('action');
}
}
global.UserTabs = UserTabs;
})(window.gl || (window.gl = {}));
import Vue from 'vue';
import './vue_resource_interceptor';
if (process.env.NODE_ENV !== 'production') {
Vue.config.productionTip = false;
}
......@@ -92,6 +92,10 @@
.award-menu-holder {
display: inline-block;
position: relative;
.tooltip {
white-space: nowrap;
}
}
.award-control {
......@@ -124,6 +128,10 @@
&:focus {
outline: 0;
}
.award-control-icon {
margin: 0;
}
}
&.is-loading {
......@@ -153,6 +161,7 @@
.award-control-icon {
color: $border-gray-normal;
margin-top: 1px;
padding: 0 2px;
}
.award-control-text {
......
......@@ -23,6 +23,10 @@ body {
}
}
.content-wrapper {
padding-bottom: 100px;
}
.container {
padding-top: 0;
z-index: 5;
......
......@@ -51,7 +51,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
private
def find_todos
@todos ||= TodosFinder.new(current_user, params.merge(include_associations: true)).execute
@todos ||= TodosFinder.new(current_user, params).execute
end
def todos_counts
......
......@@ -44,15 +44,15 @@ class Import::BitbucketController < Import::BaseController
repo_owner = repo.owner
repo_owner = current_user.username if repo_owner == bitbucket_client.user.username
@target_namespace = params[:new_namespace].presence || repo_owner
namespace_path = params[:new_namespace].presence || repo_owner
namespace = find_or_create_namespace(@target_namespace, current_user)
@target_namespace = find_or_create_namespace(namespace_path, current_user)
if current_user.can?(:create_projects, namespace)
if current_user.can?(:create_projects, @target_namespace)
# The token in a session can be expired, we need to get most recent one because
# Bitbucket::Connection class refreshes it.
session[:bitbucket_token] = bitbucket_client.connection.token
@project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @project_name, namespace, current_user, credentials).execute
@project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, credentials).execute
else
render 'unauthorized'
end
......
......@@ -154,7 +154,14 @@ class Projects::IssuesController < Projects::ApplicationController
end
format.json do
render json: @issue.to_json(include: { milestone: {}, assignee: { only: [:name, :username], methods: [:avatar_url] }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
if @issue.valid?
render json: @issue.to_json(methods: [:task_status, :task_status_short],
include: { milestone: {},
assignee: { only: [:name, :username], methods: [:avatar_url] },
labels: { methods: :text_color } })
else
render json: { errors: @issue.errors.full_messages }, status: :unprocessable_entity
end
end
end
......
......@@ -39,7 +39,7 @@ class UsersController < ApplicationController
format.html { render 'show' }
format.json do
render json: {
html: view_to_html_string("shared/projects/_list", projects: @projects, remote: true)
html: view_to_html_string("shared/projects/_list", projects: @projects)
}
end
end
......@@ -65,7 +65,7 @@ class UsersController < ApplicationController
format.html { render 'show' }
format.json do
render json: {
html: view_to_html_string("snippets/_snippets", collection: @snippets, remote: true)
html: view_to_html_string("snippets/_snippets", collection: @snippets)
}
end
end
......
......@@ -24,7 +24,6 @@ class TodosFinder
def execute
items = current_user.todos
items = include_associations(items)
items = by_action_id(items)
items = by_action(items)
items = by_author(items)
......@@ -39,17 +38,6 @@ class TodosFinder
private
def include_associations(items)
return items unless params[:include_associations]
items.includes(
[
target: { project: [:route, namespace: :route] },
author: { namespace: :route },
]
)
end
def action_id?
action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
end
......
......@@ -39,13 +39,9 @@ module TodosHelper
namespace_project_commit_path(todo.project.namespace.becomes(Namespace), todo.project,
todo.target, anchor: anchor)
else
if todo.build_failed?
# associated namespace and route would be loaded from the db again if todo.project was used
project = todo.target.project
path = [:pipelines, project.namespace.becomes(Namespace), project, todo.target]
else
path = [todo.target]
end
path = [todo.project.namespace.becomes(Namespace), todo.project, todo.target]
path.unshift(:pipelines) if todo.build_failed?
polymorphic_path(path, anchor: anchor)
end
......
......@@ -41,7 +41,7 @@ module Spammable
def check_for_spam
error_msg = if Gitlab::Recaptcha.enabled?
"Your #{spammable_entity_type} has been recognized as spam. "\
"You can still submit it by solving Captcha."
"Please, change the content or solve the reCAPTCHA to proceed."
else
"Your #{spammable_entity_type} has been recognized as spam and has been discarded."
end
......
......@@ -229,9 +229,8 @@ class Issue < ActiveRecord::Base
due_date.try(:past?) || false
end
# Only issues on public projects should be checked for spam
def check_for_spam?
project.public?
project.public? && (title_changed? || description_changed?)
end
def as_json(options = {})
......
......@@ -9,7 +9,6 @@ class MergeRequest < ActiveRecord::Base
belongs_to :target_project, class_name: "Project"
belongs_to :source_project, class_name: "Project"
belongs_to :project, foreign_key: :target_project_id
belongs_to :merge_user, class_name: "User"
has_many :approvals, dependent: :destroy
......@@ -564,6 +563,10 @@ class MergeRequest < ActiveRecord::Base
target_project != source_project
end
def project
target_project
end
# If the merge request closes any issues, save this information in the
# `MergeRequestsClosingIssues` model. This is a performance optimization.
# Calculating this information for a number of merge requests requires
......
......@@ -200,7 +200,7 @@ class Namespace < ActiveRecord::Base
# Scopes the model on direct and indirect children of the record
def descendants
self.class.joins(:route).where('routes.path LIKE ?', "#{route.path}/%").reorder('routes.path ASC')
self.class.joins(:route).merge(Route.inside_path(route.path)).reorder('routes.path ASC')
end
def user_ids_for_project_authorizations
......
......@@ -263,7 +263,7 @@ class Project < ActiveRecord::Base
# We need routes alias rs for JOIN so it does not conflict with
# includes(:route) which we use in ProjectsFinder.
joins("INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'").
where('rs.path LIKE ?', "#{path}/%")
where('rs.path LIKE ?', "#{sanitize_sql_like(path)}/%")
end
# "enabled" here means "not disabled". It includes private features!
......
......@@ -74,16 +74,16 @@ class PrometheusService < MonitoringService
def calculate_reactive_cache(environment_slug)
return unless active? && project && !project.pending_delete?
memory_query = %{sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})/1024/1024}
cpu_query = %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m]))}
memory_query = %{(sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"}) / count(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})) /1024/1024}
cpu_query = %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m])) / count(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}) * 100}
{
success: true,
metrics: {
# Memory used in MB
# Average Memory used in MB
memory_values: client.query_range(memory_query, start: 8.hours.ago),
memory_current: client.query(memory_query),
# CPU Usage rate in cores.
# Average CPU Utilization
cpu_values: client.query_range(cpu_query, start: 8.hours.ago),
cpu_current: client.query(cpu_query)
},
......
......@@ -10,9 +10,11 @@ class Route < ActiveRecord::Base
after_update :rename_descendants
scope :inside_path, -> (path) { where('routes.path LIKE ?', "#{sanitize_sql_like(path)}/%") }
def rename_descendants
if path_changed? || name_changed?
descendants = Route.where('path LIKE ?', "#{path_was}/%")
descendants = self.class.inside_path(path_was)
descendants.each do |route|
attributes = {}
......
......@@ -133,7 +133,8 @@ class Snippet < ActiveRecord::Base
end
def check_for_spam?
public?
visibility_level_changed?(to: Snippet::PUBLIC) ||
(public? && (title_changed? || content_changed?))
end
def spammable_entity_type
......
......@@ -14,6 +14,9 @@ module SpamCheckService
@spam_log_id = params.delete(:spam_log_id)
end
# In order to be proceed to the spam check process, @spammable has to be
# a dirty instance, which means it should be already assigned with the new
# attribute values.
def spam_check(spammable, user)
spam_service = SpamService.new(spammable, @request)
......
......@@ -10,8 +10,8 @@
- if current_user
.award-menu-holder.js-award-holder
%button.btn.award-control.js-add-award{ type: "button" }
%button.btn.award-control.has-tooltip.js-add-award{ type: 'button',
'aria-label': 'Add emoji',
data: { title: 'Add emoji', placement: "bottom" } }
= icon('smile-o', class: "award-control-icon award-control-icon-normal")
= icon('spinner spin', class: "award-control-icon award-control-icon-loading")
%span.award-control-text
Add
......@@ -6,7 +6,7 @@
- tooltip = "#{subject.name} - #{status.label}"
- if status.has_details?
= link_to status.details_path, class: 'build-content has-tooltip', data: { toggle: 'tooltip', title: tooltip } do
= link_to status.details_path, class: 'build-content has-tooltip', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do
%span{ class: klass }= custom_icon(status.icon)
.ci-status-text= subject.name
- else
......@@ -15,6 +15,6 @@
.ci-status-text= subject.name
- if status.has_action?
= link_to status.action_path, class: 'ci-action-icon-container has-tooltip', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title } do
= link_to status.action_path, class: 'ci-action-icon-container has-tooltip', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title, container: 'body' } do
%i.ci-action-icon-wrapper{ class: "js-#{status.action_icon.dasherize}" }
= custom_icon(status.action_icon)
......@@ -118,6 +118,12 @@
.key m
%td
Go to merge requests
%tr
%td.shortcut
.key g
.key t
%td
Go to todos
%tbody
%tr
%th
......
......@@ -32,7 +32,7 @@
= link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw')
%li
= link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('bell fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
= todos_count_format(todos_pending_count)
......
......@@ -18,7 +18,11 @@
= render 'projects/deployments/actions', deployment: @environment.last_deployment
.row
.col-sm-12
%h4
CPU utilization
%svg.prometheus-graph{ 'graph-type' => 'cpu_values' }
.row
.col-sm-12
%h4
Memory usage
%svg.prometheus-graph{ 'graph-type' => 'memory_values' }
......@@ -21,7 +21,7 @@
selected: f.object.source_project_id
.merge-request-select.dropdown
= f.hidden_field :source_branch
= dropdown_toggle local_assigns.fetch(f.object.source_branch, "Select source branch"), { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" }
= dropdown_toggle f.object.source_branch || "Select source branch", { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" }
.dropdown-menu.dropdown-menu-selectable.dropdown-source-branch
= dropdown_title("Select source branch")
= dropdown_filter("Search branches")
......
......@@ -37,7 +37,7 @@
":can-resolve" => can_resolve,
":author-name" => "'#{j(note.author.name)}'",
"author-avatar" => note.author.avatar_url,
":note-truncated" => "'#{truncate(note.note, length: 17)}'",
":note-truncated" => "'#{j(truncate(note.note, length: 17))}'",
":resolved-by" => "'#{j(note.resolved_by.try(:name))}'",
"v-show" => "#{can_resolve || note.resolved?}",
"inline-template" => true,
......
- group_status = CommitStatus.where(id: subject).status
%button.dropdown-menu-toggle.build-content.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } }
%button.dropdown-menu-toggle.build-content.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}", container: 'body' } }
%span{ class: "ci-status-icon ci-status-icon-#{group_status}" }
= ci_icon_for_status(group_status)
%span.ci-status-text
......
#!/bin/sh
# Usage: with_env ENV_FILE COMMAND [ARGS...]
#
# This script lets you modify the environment of an executable before
# launching it. It uses an 'env file' which must contain lines like
# 'MY_VARIABLE="my value"'.
#
env_file=$1
shift
# Use set -a to export all variables defined in env_file.
set -a
. "${env_file}"
set +a
exec "$@"
---
title: Fixes large file name tooltip cutoff in diff header
merge_request: 9529
author:
---
title: Upgrade VueJS to v2.2.4 and disable dev mode warnings
merge_request: 9981
author:
---
title: Spam check only when spammable attributes have changed
merge_request:
author:
---
title: Add `g t` global shortcut to go to todos
merge_request:
author:
---
title: Return 404 in project issues API endpoint when project cannot be found
merge_request: 10093
author:
---
title: Removed unnecessary 'add' text in additional award emoji button
merge_request:
author:
---
title: Hide ancestor groups in the share group dropdown list
merge_request: 9965
author:
---
title: Removed d3 from the main application.js bundle
merge_request: 10062
author:
---
title: Only show public emails in atom feeds
title: Fixed job tooltip being cut-off
merge_request:
author:
---
title: Replace closing MR icon
merge_request: 10103
author: blackst0ne
---
title: To protect against Server-side Request Forgery project import URLs are now prohibited against localhost or the server IP except for the assigned instance URL and port. Imports are also prohibited from ports below 1024 with the exception of ports 22, 80, and 443.
merge_request:
author:
......@@ -537,7 +537,7 @@ production: &base
# This setting is obsolete because we expect it to be moved under
# repositories/storages in GitLab 9.1.
#
# socket_path: tmp/sockets/gitaly.socket
# socket_path: tmp/sockets/private/gitaly.socket
#
# 4. Advanced settings
......
......@@ -18,7 +18,7 @@ var config = {
context: path.join(ROOT_PATH, 'app/assets/javascripts'),
entry: {
common: './commons/index.js',
common_vue: ['vue', 'vue-resource'],
common_vue: ['vue', './vue_shared/common_vue.js'],
common_d3: ['d3'],
main: './main.js',
blob_edit: './blob_edit/blob_edit_bundle.js',
......@@ -135,7 +135,7 @@ var config = {
'empty_states': path.join(ROOT_PATH, 'app/views/shared/empty_states'),
'icons': path.join(ROOT_PATH, 'app/views/shared/icons'),
'vendor': path.join(ROOT_PATH, 'vendor/assets/javascripts'),
'vue$': 'vue/dist/vue.common.js',
'vue$': 'vue/dist/vue.esm.js',
}
}
}
......
......@@ -14,7 +14,7 @@ class IndexRoutesPathForLike < ActiveRecord::Migration
def up
return unless Gitlab::Database.postgresql?
unless index_exists?(:routes, name: INDEX_NAME)
unless index_exists?(:routes, :path, name: INDEX_NAME)
execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON routes (path varchar_pattern_ops);")
end
end
......@@ -22,7 +22,7 @@ class IndexRoutesPathForLike < ActiveRecord::Migration
def down
return unless Gitlab::Database.postgresql?
if index_exists?(:routes, name: INDEX_NAME)
if index_exists?(:routes, :path, name: INDEX_NAME)
execute("DROP INDEX CONCURRENTLY #{INDEX_NAME};")
end
end
......
# Gitaly
[Gitaly](https://gitlab.com/gitlab-org/gitlay) (introduced in GitLab
9.0) is a service that provides high-level RPC access to Git
repositories. As of GitLab 9.0 it is still an optional component with
limited scope.
GitLab components that access Git repositories (gitlab-rails,
gitlab-shell, gitlab-workhorse) act as clients to Gitaly. End users do
not have direct access to Gitaly.
## Configuring Gitaly
The Gitaly service itself is configured via environment variables.
These variables are documented [in the gitaly
repository](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/configuration/README.md).
To change a Gitaly environment variable in Omnibus you can use
`gitaly['env']` in `/etc/gitlab/gitlab.rb`. Changes will be applied
when you run `gitlab-ctl reconfigure`.
```ruby
gitaly['env'] = {
'GITALY_MY_VARIABLE' => 'value'
}
```
To change a Gitaly environment variable in installations from source
you can edit `/home/git/gitaly/env`.
```shell
GITALY_MY_VARIABLE='value'
```
Changes to `/home/git/gitaly/env` are applied when you run `service
gitlab restart`.
## Configuring GitLab to not use Gitaly
Gitaly is still an optional component in GitLab 9.0. This means you
can choose to not use it.
In Omnibus you can make the following change in
`/etc/gitlab/gitlab.rb` and reconfigure. This will both disable the
Gitaly service and configure the rest of GitLab not to use it.
```ruby
gitaly['enable'] = false
```
In source installations, edit `/home/git/gitlab/config/gitlab.yml` and
make sure `socket_path` in the `gitaly` section is commented out. This
does not disable the Gitaly service; it only prevents it from being
used.
Apply the change with `service gitlab restart`.
```yaml
gitaly:
# socket_path: tmp/sockets/private/gitlay.socket
```
## Disabling or enabling the Gitaly service
Be careful: if you disable Gitaly without instructing the rest of your
GitLab installation not to use Gitaly, you may end up with errors
because GitLab tries to access a service that is not running.
To disable the Gitaly service in your Omnibus installation, add the
following line to `/etc/gitlab/gitlab.rb`:
```ruby
gitaly['enable'] = false
```
When you run `gitlab-ctl reconfigure` the Gitaly service will be
disabled.
To disable the Gitaly service in an installation from source, add the
following to `/etc/default/gitlab`:
```shell
gitaly_enabled=false
```
When you run `service gitlab restart` Gitaly will be disabled.
\ No newline at end of file
......@@ -456,6 +456,36 @@ Make GitLab start on boot:
sudo update-rc.d gitlab defaults 21
### Install Gitaly
As of GitLab 9.0 Gitaly is an **optional** component. Its
configuration is expected to change in GitLab 9.1. It is OK to wait
with setting up Gitaly until you upgrade to GitLab 9.1 or later.
# Fetch Gitaly source with Git and compile with Go
sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly]" RAILS_ENV=production
# Restrict Gitaly socket access
sudo chmod 0700 /home/git/gitlab/tmp/sockets/private
sudo chown git /home/git/gitlab/tmp/sockets/private
# Configure Gitaly
echo 'GITALY_SOCKET_PATH=/home/git/gitlab/tmp/sockets/private/gitaly.socket' | \
sudo -u git tee -a /home/git/gitaly/env
# Enable Gitaly in the init script
echo 'gitaly_enabled=true' | sudo tee -a /etc/default/gitlab
Next, edit `/home/git/gitlab/config/gitlab.yml` and make sure `socket_path` in
the `gitaly:` section is uncommented.
# <- gitlab.yml indentation starts here
gitaly:
socket_path: tmp/sockets/private/gitaly.socket
For more information about configuring Gitaly see
[doc/administration/gitaly](../administration/gitaly).
### Setup Logrotate
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
......
......@@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-14-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v4.0.3
sudo -u git -H git checkout v4.1.1
```
### 6. Update gitlab-workhorse
......
......@@ -145,7 +145,15 @@ sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v5.0.0
```
### 9. Update configuration files
### 9. Optional: install Gitaly
Gitaly is still an optional component of GitLab. If you want to save time
during your 9.0 upgrade **you can skip this step**.
If you do want to set up Gitaly in GitLab 9.0 then follow [Gitaly section of the installation
guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/9-0-stable/doc/install/installation.md#install-gitaly).
### 10. Update configuration files
#### New configuration options for `gitlab.yml`
......@@ -282,14 +290,14 @@ For Ubuntu 16.04.1 LTS:
sudo systemctl daemon-reload
```
### 10. Start application
### 11. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
### 11. Check application status
### 12. Check application status
Check if GitLab and its environment are configured correctly:
......
......@@ -36,6 +36,7 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?'
| <kbd>g</kbd> + <kbd>p</kbd> | Go to projects |
| <kbd>g</kbd> + <kbd>i</kbd> | Go to issues |
| <kbd>g</kbd> + <kbd>m</kbd> | Go to merge requests |
| <kbd>g</kbd> + <kbd>t</kbd> | Go to todos |
## Project
......
......@@ -2,6 +2,8 @@ require 'gitaly'
module Gitlab
module GitalyClient
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'.freeze
def self.gitaly_address
if Gitlab.config.gitaly.socket_path
"unix://#{Gitlab.config.gitaly.socket_path}"
......@@ -39,5 +41,10 @@ module Gitlab
yield is_enabled
end
end
def self.expected_server_version
path = Rails.root.join(SERVER_VERSION_FILE)
path.read.chomp
end
end
end
......@@ -48,6 +48,10 @@ gitlab_pages_pid_path="$pid_path/gitlab-pages.pid"
gitlab_pages_options="-pages-domain example.com -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090"
gitlab_pages_log="$app_root/log/gitlab-pages.log"
shell_path="/bin/bash"
gitaly_enabled=false
gitaly_dir=$(cd $app_root/../gitaly 2> /dev/null && pwd)
gitaly_pid_path="$pid_path/gitaly.pid"
gitaly_log="$app_root/log/gitaly.log"
# Read configuration variable file if it is present
test -f /etc/default/gitlab && . /etc/default/gitlab
......@@ -101,13 +105,20 @@ check_pids(){
gppid=0
fi
fi
if [ "$gitaly_enabled" = true ]; then
if [ -f "$gitaly_pid_path" ]; then
gapid=$(cat "$gitaly_pid_path")
else
gapid=0
fi
fi
}
## Called when we have started the two processes and are waiting for their pid files.
wait_for_pids(){
# We are sleeping a bit here mostly because sidekiq is slow at writing its pid
i=0;
while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; } || { [ "$gitlab_pages_enabled" = true ] && [ ! -f $gitlab_pages_pid_path ]; }; do
while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; } || { [ "$gitlab_pages_enabled" = true ] && [ ! -f $gitlab_pages_pid_path ]; } || { [ "$gitaly_enabled" = true ] && [ ! -f $gitaly_pid_path ]; }; do
sleep 0.1;
i=$((i+1))
if [ $((i%10)) = 0 ]; then
......@@ -164,7 +175,15 @@ check_status(){
gitlab_pages_status="-1"
fi
fi
if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; } && { [ "$gitlab_pages_enabled" != true ] || [ $gitlab_pages_status = 0 ]; }; then
if [ "$gitaly_enabled" = true ]; then
if [ $gapid -ne 0 ]; then
kill -0 "$gapid" 2>/dev/null
gitaly_status="$?"
else
gitaly_status="-1"
fi
fi
if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; } && { [ "$gitlab_pages_enabled" != true ] || [ $gitlab_pages_status = 0 ]; } && { [ "$gitaly_enabled" != true ] || [ $gitaly_status = 0 ]; }; then
gitlab_status=0
else
# http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
......@@ -213,12 +232,19 @@ check_stale_pids(){
exit 1
fi
fi
if [ "$gitaly_enabled" = true ] && [ "$gapid" != "0" ] && [ "$gitaly_status" != "0" ]; then
echo "Removing stale Gitaly pid. This is most likely caused by Gitaly crashing the last time it ran."
if ! rm "$gitaly_pid_path"; then
echo "Unable to remove stale pid, exiting"
exit 1
fi
fi
}
## If no parts of the service is running, bail out.
exit_if_not_running(){
check_stale_pids
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; } && { [ "$gitaly_enabled" != true ] || [ "$gitaly_status" != "0" ]; }; then
echo "GitLab is not running."
exit
fi
......@@ -243,6 +269,9 @@ start_gitlab() {
if [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" != "0" ]; then
echo "Starting GitLab Pages"
fi
if [ "$gitaly_enabled" = true ] && [ "$gitaly_status" != "0" ]; then
echo "Starting Gitaly"
fi
# Then check if the service is running. If it is: don't start again.
if [ "$web_status" = "0" ]; then
......@@ -292,6 +321,16 @@ start_gitlab() {
fi
fi
if [ "$gitaly_enabled" = true ]; then
if [ "$gitaly_status" = "0" ]; then
echo "Gitaly is already running with pid $gapid, not restarting"
else
$app_root/bin/daemon_with_pidfile $gitaly_pid_path \
$app_root/bin/with_env $gitaly_dir/env \
$gitaly_dir/gitaly >> $gitaly_log 2>&1 &
fi
fi
# Wait for the pids to be planted
wait_for_pids
# Finally check the status to tell wether or not GitLab is running
......@@ -322,13 +361,17 @@ stop_gitlab() {
echo "Shutting down gitlab-pages"
kill -- $(cat $gitlab_pages_pid_path)
fi
if [ "$gitaly_status" = "0" ]; then
echo "Shutting down Gitaly"
kill -- $(cat $gitaly_pid_path)
fi
# If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script.
while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; do
while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; } || { [ "$gitaly_enabled" = true ] && [ "$gitaly_status" = "0" ]; }; do
sleep 1
check_status
printf "."
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; } && { [ "$gitaly_enabled" != true ] || [ "$gitaly_status" != "0" ]; }; then
printf "\n"
break
fi
......@@ -343,6 +386,7 @@ stop_gitlab() {
rm "$mail_room_pid_path" 2>/dev/null
fi
rm -f "$gitlab_pages_pid_path"
rm -f "$gitaly_pid_path"
print_status
}
......@@ -350,7 +394,7 @@ stop_gitlab() {
## Prints the status of GitLab and its components.
print_status() {
check_status
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; } && { [ "$gitaly_enabled" != true ] || [ "$gitaly_status" != "0" ]; }; then
echo "GitLab is not running."
return
fi
......@@ -383,7 +427,14 @@ print_status() {
printf "The GitLab Pages is \033[31mnot running\033[0m.\n"
fi
fi
if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" = "0" ]; }; then
if [ "$gitaly_enabled" = true ]; then
if [ "$gitaly_status" = "0" ]; then
echo "Gitaly with pid $gapid is running."
else
printf "Gitaly is \033[31mnot running\033[0m.\n"
fi
fi
if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" = "0" ]; } && { [ "$gitaly_enabled" != true ] || [ "$gitaly_status" = "0" ]; }; then
printf "GitLab and all its components are \033[32mup and running\033[0m.\n"
fi
}
......@@ -414,7 +465,7 @@ reload_gitlab(){
## Restarts Sidekiq and Unicorn.
restart_gitlab(){
check_status
if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; then
if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; } || { [ "$gitaly_enabled" = true ] && [ "$gitaly_status" = "0" ]; }; then
stop_gitlab
fi
start_gitlab
......
......@@ -84,3 +84,7 @@ mail_room_pid_path="$pid_path/mail_room.pid"
# shell other than "bash"
# The default is "/bin/bash"
shell_path="/bin/bash"
# This variable controls whether the init script starts/stops Gitaly
gitaly_enabled=false
gitaly_log="$app_root/log/gitaly.log"
namespace :gitlab do
namespace :gitaly do
desc "GitLab | Install or upgrade gitaly"
task :install, [:dir] => :environment do |t, args|
warn_user_is_not_gitlab
unless args.dir.present?
abort %(Please specify the directory where you want to install gitaly:\n rake "gitlab:gitaly:install[/home/git/gitaly]")
end
tag = "v#{Gitlab::GitalyClient.expected_server_version}"
repo = 'https://gitlab.com/gitlab-org/gitaly.git'
checkout_or_clone_tag(tag: tag, repo: repo, target_dir: args.dir)
_, status = Gitlab::Popen.popen(%w[which gmake])
command = status.zero? ? 'gmake' : 'make'
Dir.chdir(args.dir) do
run_command!([command])
end
end
end
end
......@@ -81,7 +81,7 @@ module Gitlab
def run_command!(command)
output, status = Gitlab::Popen.popen(command)
raise Gitlab::TaskFailedError unless status.zero?
raise Gitlab::TaskFailedError.new(output) unless status.zero?
output
end
......
......@@ -112,6 +112,17 @@ describe Import::BitbucketController do
post :create, format: :js
end
end
context 'when the Bitbucket user is unauthorized' do
render_views
it 'returns unauthorized' do
allow(controller).to receive(:current_user).and_return(user)
allow(user).to receive(:can?).and_return(false)
post :create, format: :js
end
end
end
context "when the repository owner is not the Bitbucket user" do
......
......@@ -241,10 +241,27 @@ describe Projects::IssuesController do
expect(spam_logs.first.recaptcha_verified).to be_falsey
end
it 'renders verify template' do
update_spam_issue
context 'as HTML' do
it 'renders verify template' do
update_spam_issue
expect(response).to render_template(:verify)
end
end
context 'as JSON' do
before do
update_issue({ title: 'Spam Title', description: 'Spam lives here' }, format: :json)
end
it 'renders json errors' do
expect(json_response)
.to eql("errors" => ["Your issue has been recognized as spam. Please, change the content or solve the reCAPTCHA to proceed."])
end
expect(response).to render_template(:verify)
it 'returns 422 status' do
expect(response).to have_http_status(422)
end
end
end
......
......@@ -4,7 +4,6 @@ FactoryGirl.define do
author
association :source_project, :repository, factory: :project
target_project { source_project }
project { target_project }
# $ git log --pretty=oneline feature..master
# 5937ac0a7beb003549fc5fd26fc247adbce4a52e Add submodule from gitlab.com
......
......@@ -21,6 +21,11 @@ feature 'Dashboard shortcuts', feature: true, js: true do
find('body').native.send_key('m')
check_page_title('Merge Requests')
find('body').native.send_key('g')
find('body').native.send_key('t')
check_page_title('Todos')
end
def check_page_title(title)
......
......@@ -75,6 +75,12 @@ feature 'Create New Merge Request', feature: true, js: true do
end
end
it 'populates source branch button' do
visit new_namespace_project_merge_request_path(project.namespace, project, change_branches: true, merge_request: { target_branch: 'master', source_branch: 'fix' })
expect(find('.js-source-branch')).to have_content('fix')
end
it 'allows to change the diff view' do
visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_branch: 'master', source_branch: 'fix' })
......
require 'spec_helper'
describe 'Projects tab on a user profile', :feature, :js do
include WaitForAjax
let(:user) { create(:user) }
let!(:project) { create(:empty_project, namespace: user.namespace) }
let!(:project2) { create(:empty_project, namespace: user.namespace) }
before do
allow(Project).to receive(:default_per_page).and_return(1)
login_as(user)
visit user_path(user)
page.within('.user-profile-nav') do
click_link('Personal projects')
end
wait_for_ajax
end
it 'paginates results' do
expect(page).to have_content(project2.name)
click_link('Next')
expect(page).to have_content(project.name)
end
end
require "spec_helper"
describe TodosHelper do
include GitlabRoutingHelper
describe '#todo_target_path' do
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, target_project: project, source_project: project) }
let(:issue) { create(:issue, project: project) }
let(:note) { create(:note_on_issue, noteable: issue, project: project) }
let(:mr_todo) { build(:todo, project: project, target: merge_request) }
let(:issue_todo) { build(:todo, project: project, target: issue) }
let(:note_todo) { build(:todo, project: project, target: issue, note: note) }
let(:build_failed_todo) { build(:todo, :build_failed, project: project, target: merge_request) }
it 'returns correct path to the todo MR' do
expect(todo_target_path(mr_todo)).
to eq("/#{project.full_path}/merge_requests/#{merge_request.iid}")
end
it 'returns correct path to the todo issue' do
expect(todo_target_path(issue_todo)).
to eq("/#{project.full_path}/issues/#{issue.iid}")
end
it 'returns correct path to the todo note' do
expect(todo_target_path(note_todo)).
to eq("/#{project.full_path}/issues/#{issue.iid}#note_#{note.id}")
end
it 'returns correct path to build_todo MR when pipeline failed' do
expect(todo_target_path(build_failed_todo)).
to eq("/#{project.full_path}/merge_requests/#{merge_request.iid}/pipelines")
end
end
describe '#todo_projects_options' do
let(:projects) { create_list(:empty_project, 3) }
let(:user) { create(:user) }
......
......@@ -670,4 +670,41 @@ describe Issue, models: true do
expect(attrs_hash).to include('time_estimate')
end
end
describe '#check_for_spam' do
let(:project) { create :project, visibility_level: visibility_level }
let(:issue) { create :issue, project: project }
subject do
issue.assign_attributes(description: description)
issue.check_for_spam?
end
context 'when project is public and spammable attributes changed' do
let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
let(:description) { 'woo' }
it 'returns true' do
is_expected.to be_truthy
end
end
context 'when project is private' do
let(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
let(:description) { issue.description }
it 'returns false' do
is_expected.to be_falsey
end
end
context 'when spammable attributes have not changed' do
let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
let(:description) { issue.description }
it 'returns false' do
is_expected.to be_falsey
end
end
end
end
......@@ -227,10 +227,12 @@ describe Namespace, models: true do
end
describe '#descendants' do
let!(:group) { create(:group) }
let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
let!(:deep_nested_group) { create(:group, parent: nested_group) }
let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
let!(:another_group) { create(:group, path: 'gitllab') }
let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }
it 'returns the correct descendants' do
expect(very_deep_nested_group.descendants.to_a).to eq([])
......
......@@ -2154,11 +2154,14 @@ describe Project, models: true do
end
describe 'inside_path' do
let!(:project1) { create(:empty_project) }
let!(:project1) { create(:empty_project, namespace: create(:namespace, path: 'name_pace')) }
let!(:project2) { create(:empty_project) }
let!(:project3) { create(:empty_project, namespace: create(:namespace, path: 'namespace')) }
let!(:path) { project1.namespace.full_path }
it { expect(Project.inside_path(path)).to eq([project1]) }
it 'returns correct project' do
expect(Project.inside_path(path)).to eq([project1])
end
end
describe '#route_map_for' do
......
require 'spec_helper'
describe Route, models: true do
let!(:group) { create(:group, path: 'gitlab', name: 'gitlab') }
let!(:group) { create(:group, path: 'git_lab', name: 'git_lab') }
let!(:route) { group.route }
describe 'relationships' do
......@@ -14,10 +14,24 @@ describe Route, models: true do
it { is_expected.to validate_uniqueness_of(:path) }
end
describe '.inside_path' do
let!(:nested_group) { create(:group, path: 'test', name: 'test', parent: group) }
let!(:deep_nested_group) { create(:group, path: 'foo', name: 'foo', parent: nested_group) }
let!(:another_group) { create(:group, path: 'other') }
let!(:similar_group) { create(:group, path: 'gitllab') }
let!(:another_group_nested) { create(:group, path: 'another', name: 'another', parent: similar_group) }
it 'returns correct routes' do
expect(Route.inside_path('git_lab')).to match_array([nested_group.route, deep_nested_group.route])
end
end
describe '#rename_descendants' do
let!(:nested_group) { create(:group, path: 'test', name: 'test', parent: group) }
let!(:deep_nested_group) { create(:group, path: 'foo', name: 'foo', parent: nested_group) }
let!(:similar_group) { create(:group, path: 'gitlab-org', name: 'gitlab-org') }
let!(:another_group) { create(:group, path: 'gittlab', name: 'gitllab') }
let!(:another_group_nested) { create(:group, path: 'git_lab', name: 'git_lab', parent: another_group) }
context 'path update' do
context 'when route name is set' do
......@@ -28,6 +42,8 @@ describe Route, models: true do
expect(described_class.exists?(path: 'bar/test')).to be_truthy
expect(described_class.exists?(path: 'bar/test/foo')).to be_truthy
expect(described_class.exists?(path: 'gitlab-org')).to be_truthy
expect(described_class.exists?(path: 'gittlab')).to be_truthy
expect(described_class.exists?(path: 'gittlab/git_lab')).to be_truthy
end
end
......@@ -44,7 +60,7 @@ describe Route, models: true do
context 'name update' do
it "updates children routes with new path" do
route.update_attributes(name: 'bar')
route.update_attributes(name: 'bar')
expect(described_class.exists?(name: 'bar')).to be_truthy
expect(described_class.exists?(name: 'bar / test')).to be_truthy
......
......@@ -198,4 +198,47 @@ describe Snippet, models: true do
expect(snippet.participants).to include(note1.author, note2.author)
end
end
describe '#check_for_spam' do
let(:snippet) { create :snippet, visibility_level: visibility_level }
subject do
snippet.assign_attributes(title: title)
snippet.check_for_spam?
end
context 'when public and spammable attributes changed' do
let(:visibility_level) { Snippet::PUBLIC }
let(:title) { 'woo' }
it 'returns true' do
is_expected.to be_truthy
end
end
context 'when private' do
let(:visibility_level) { Snippet::PRIVATE }
let(:title) { snippet.title }
it 'returns false' do
is_expected.to be_falsey
end
it 'returns true when switching to public' do
snippet.save!
snippet.visibility_level = Snippet::PUBLIC
expect(snippet.check_for_spam?).to be_truthy
end
end
context 'when spammable attributes have not changed' do
let(:visibility_level) { Snippet::PUBLIC }
let(:title) { snippet.title }
it 'returns false' do
is_expected.to be_falsey
end
end
end
end
......@@ -19,42 +19,67 @@ describe SpamService, services: true do
let(:issue) { create(:issue, project: project) }
let(:request) { double(:request, env: {}) }
context 'when indicated as spam by akismet' do
before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: true)) }
context 'when spammable attributes have not changed' do
before do
issue.closed_at = Time.zone.now
it 'doesnt check as spam when request is missing' do
check_spam(issue, nil, false)
expect(issue.spam).to be_falsey
allow(AkismetService).to receive(:new).and_return(double(is_spam?: true))
end
it 'checks as spam' do
check_spam(issue, request, false)
expect(issue.spam).to be_truthy
it 'returns false' do
expect(check_spam(issue, request, false)).to be_falsey
end
it 'creates a spam log' do
it 'does not create a spam log' do
expect { check_spam(issue, request, false) }
.to change { SpamLog.count }.from(0).to(1)
.not_to change { SpamLog.count }
end
end
it 'doesnt yield block' do
expect(check_spam(issue, request, false))
.to eql(SpamLog.last)
context 'when spammable attributes have changed' do
before do
issue.description = 'SPAM!'
end
end
context 'when not indicated as spam by akismet' do
before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
context 'when indicated as spam by akismet' do
before do
allow(AkismetService).to receive(:new).and_return(double(is_spam?: true))
end
it 'returns false' do
expect(check_spam(issue, request, false)).to be_falsey
it 'doesnt check as spam when request is missing' do
check_spam(issue, nil, false)
expect(issue.spam).to be_falsey
end
it 'checks as spam' do
check_spam(issue, request, false)
expect(issue.spam).to be_truthy
end
it 'creates a spam log' do
expect { check_spam(issue, request, false) }
.to change { SpamLog.count }.from(0).to(1)
end
it 'doesnt yield block' do
expect(check_spam(issue, request, false))
.to eql(SpamLog.last)
end
end
it 'does not create a spam log' do
expect { check_spam(issue, request, false) }
.not_to change { SpamLog.count }
context 'when not indicated as spam by akismet' do
before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
it 'returns false' do
expect(check_spam(issue, request, false)).to be_falsey
end
it 'does not create a spam log' do
expect { check_spam(issue, request, false) }
.not_to change { SpamLog.count }
end
end
end
end
......
module PrometheusHelpers
def prometheus_memory_query(environment_slug)
%{sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})/1024/1024}
%{(sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"}) / count(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})) /1024/1024}
end
def prometheus_cpu_query(environment_slug)
%{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m]))}
%{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m])) / count(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}) * 100}
end
def prometheus_query_url(prometheus_query)
......
require 'rake_helper'
describe 'gitlab:gitaly namespace rake task' do
before :all do
Rake.application.rake_require 'tasks/gitlab/gitaly'
end
describe 'install' do
let(:repo) { 'https://gitlab.com/gitlab-org/gitaly.git' }
let(:clone_path) { Rails.root.join('tmp/tests/gitaly').to_s }
let(:tag) { "v#{File.read(Rails.root.join(Gitlab::GitalyClient::SERVER_VERSION_FILE)).chomp}" }
context 'no dir given' do
it 'aborts and display a help message' do
# avoid writing task output to spec progress
allow($stderr).to receive :write
expect { run_rake_task('gitlab:gitaly:install') }.to raise_error /Please specify the directory where you want to install gitaly/
end
end
context 'when an underlying Git command fail' do
it 'aborts and display a help message' do
expect_any_instance_of(Object).
to receive(:checkout_or_clone_tag).and_raise 'Git error'
expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error 'Git error'
end
end
describe 'checkout or clone' do
before do
expect(Dir).to receive(:chdir).with(clone_path)
end
it 'calls checkout_or_clone_tag with the right arguments' do
expect_any_instance_of(Object).
to receive(:checkout_or_clone_tag).with(tag: tag, repo: repo, target_dir: clone_path)
run_rake_task('gitlab:gitaly:install', clone_path)
end
end
describe 'gmake/make' do
before do
FileUtils.mkdir_p(clone_path)
expect(Dir).to receive(:chdir).with(clone_path).and_call_original
end
context 'gmake is available' do
before do
expect_any_instance_of(Object).to receive(:checkout_or_clone_tag)
allow_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
end
it 'calls gmake in the gitaly directory' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
expect_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
run_rake_task('gitlab:gitaly:install', clone_path)
end
end
context 'gmake is not available' do
before do
expect_any_instance_of(Object).to receive(:checkout_or_clone_tag)
allow_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
end
it 'calls make in the gitaly directory' do
expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42])
expect_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
run_rake_task('gitlab:gitaly:install', clone_path)
end
end
end
end
end
......@@ -9,9 +9,6 @@ describe 'gitlab:workhorse namespace rake task' do
let(:repo) { 'https://gitlab.com/gitlab-org/gitlab-workhorse.git' }
let(:clone_path) { Rails.root.join('tmp/tests/gitlab-workhorse').to_s }
let(:tag) { "v#{File.read(Rails.root.join(Gitlab::Workhorse::VERSION_FILE)).chomp}" }
before do
allow(ENV).to receive(:[])
end
context 'no dir given' do
it 'aborts and display a help message' do
......
......@@ -4464,9 +4464,9 @@ vue-resource@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-0.9.3.tgz#ab46e1c44ea219142dcc28ae4043b3b04c80959d"
vue@^2.1.10:
version "2.1.10"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.1.10.tgz#c9235ca48c7925137be5807832ac4e3ac180427b"
vue@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.2.4.tgz#d0a3a050a80a12356d7950ae5a7b3131048209cc"
watchpack@^1.2.0:
version "1.2.1"
......
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