Commit 061bba7e authored by Rémy Coutable's avatar Rémy Coutable

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

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parents 332dad45 c62314ab
......@@ -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'
......
......@@ -4,7 +4,10 @@ entry.
## 9.0.0 (2017-03-22)
<<<<<<< HEAD
- Remove the newrelic gem. !1335 (Robert Schilling)
=======
>>>>>>> ce/master
- Fix inconsistent naming for services that delete things. !5803 (dixpac)
- UI: Allow a project variable to be set to an empty value. !6044 (Lukáš Nový)
- Align task list checkboxes. !6487 (Jared Deckard <jared.deckard@gmail.com>)
......@@ -110,8 +113,13 @@ entry.
- API: Use `visibility` as string parameter everywhere. !9337
- Add the Username to the HTTP(S) clone URL of a Repository. !9347 (Jan Christophersen)
- Add spec for todo with target_type Commit. !9351 (George Andrinopoulos)
<<<<<<< HEAD
- Fixes includes line number during unfold copy n paste in parallel diff view. !9365
- API: Remove `DELETE projects/:id/deploy_keys/:key_id/disable`. !9365 (Robert Schilling)
=======
- API: Remove `DELETE projects/:id/deploy_keys/:key_id/disable`. !9365 (Robert Schilling)
- Fixes includes line number during unfold copy n paste in parallel diff view. !9365
>>>>>>> ce/master
- API: Use POST to (un)block a user. !9371 (Robert Schilling)
- Remove markup that was showing in tooltip for renamed files. !9374
- Drop unused ci_projects table and some unused project_id columns, then rename gl_project_id to project_id. Stop exporting job trace when exporting projects. !9378 (David Wagner)
......@@ -148,6 +156,10 @@ entry.
- Fix updaing commit status when using optional attributes. !9618
- Add filter and sorting to dashboard groups page. !9619
- Remove deprecated build status badge and related services. !9620
<<<<<<< HEAD
=======
- Remove the newrelic gem. !9622 (Robert Schilling)
>>>>>>> ce/master
- Rename table ci_commits to ci_pipelines. !9638
- Remove various unused CI tables and columns. !9639
- Use webpack CommonsChunkPlugin to place common javascript libraries in their own bundles. !9647
......@@ -180,6 +192,7 @@ entry.
- Allow creating merge request even if target branch is not specified in query params. !9968
- Removed d3 from the main application.js bundle. !10062
- Return 404 in project issues API endpoint when project cannot be found. !10093
<<<<<<< HEAD
- Add frequently used emojis back to awards menu.
- Add filtered search to MR page.
- Re-add the New Project button in nav bar.
......@@ -208,6 +221,21 @@ entry.
- Re-add Assign to me link to Merge Request and Issues.
- Added option to update to owner for group members.
- Add badges to global dropdown.
=======
- Fix positioning of `Scroll to top` button.
- Add limit to the number of events showed in cycle analytics.
- Only run timeago loops after rendering timeago components.
- Increase right side of file header to button stays on same line.
- Centers loading icon vertically and horizontally in pipelines table in commit view.
- Fix issues mentioned but not closed for external issue trackers.
- fix milestone does not automatically assign when create issue from milestone.
- Re-add Assign to me link to Merge Request and Issues.
- Format timeago date to short format.
- Fix errors in slash commands matcher, add simple test coverage. (YarNayar)
- Make Git history follow renames again by performing the --skip in Ruby.
- Added option to update to owner for group members.
- Pick up option from GDK to disable webpack dev server livereload.
>>>>>>> ce/master
- Introduce Pipeline Triggers that are user-aware.
- Fixed loading spinner position on issue template toggle.
- Removed duplicate "Visibility Level" label on New Project page. (Robert Marcano)
......@@ -216,6 +244,7 @@ entry.
- Fix issuable stale object error handler for js when updating tasklists.
- Gather issuable metadata to avoid n+1 queries on index view.
- Remove JIRA closed status icon.
<<<<<<< HEAD
- Changed coverage reg expression placeholder text to be more like a placeholder.
- Make Git history follow renames again by performing the --skip in Ruby.
- Fixed long file names overflowing under action buttons.
......@@ -250,11 +279,44 @@ entry.
- Remove remnants of git annex support.
- Pick up option from GDK to disable webpack dev server livereload.
- Fix z index issues with sidebar.
=======
- Fix z index issues with sidebar.
- Fixed long file names overflowing under action buttons.
- Only show public emails in atom feeds.
- Add Mock CI service/integration for development.
- Move tag services to Tags namespace. (dixpac)
- Set Auto-Submitted header to mails. (Semyon Pupkov)
- Improved diff comment button UX.
- Adds API endpoint to fetch all merge request for a single milestone. (Joren De Groof)
- Only create unmergeable todos once when MR fails to merge.
- Only yield valid references in ReferenceFilter.references_in.
- Add member: Always return 409 when a member exists.
- Remove plus icon from MR button on compare view.
- Re-add the New Project button in nav bar.
- Default to subtle MR mege button until CI status is available.
- Rename priority sorting option to label priority.
- Added headers to protected branch access dropdowns.
- Hide issue info when project issues are disabled. (George Andrinopoulos)
- removed unused parameter 'status_only: true'.
- Left align logo.
- Replaced jQuery UI datepicker.
- Removed jQuery UI highlight & autocomplete.
- Replaced jQuery UI sortable.
- Remove readme-only project view preference.
- Remove tooltips from label subscription buttons.
- Rename retry failed button on pipeline page to just retry.
- Align bulk update issues button to the right.
- Remove remnants of git annex support.
- Dispatch needed JS when creating a new MR in diff view.
>>>>>>> ce/master
- Change project count limit from 10 to 100000.
- Remove repeated routes.path check for postgresql database. (mhasbini)
- Fixed RSS button alignment on activity pages.
- Seed abuse reports for development.
<<<<<<< HEAD
- Only show public emails in atom feeds.
=======
>>>>>>> ce/master
- Bump Hashie to 3.5.5 and omniauth to 1.4.2 to eliminate warning noise.
- Add user deletion permission check in `Users::DestroyService`.
- Fix snippets search result spacing.
......@@ -263,6 +325,7 @@ entry.
- 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.
- Remove fixed positioning from top nav.
- Deduplicate markdown task lists.
<<<<<<< HEAD
- Default to subtle MR mege button until CI status is available.
- Update code editor (ACE) to 1.2.6, to fix input problems with compose key.
- update issue count when closing/reopening an issue.
......@@ -284,6 +347,51 @@ entry.
- Use full group name in GFM group reference title.
- Adds Pending and Finished tabs to pipelines page.
- don't animate logo when downloading files.
=======
- update issue count when closing/reopening an issue.
- Update code editor (ACE) to 1.2.6, to fix input problems with compose key.
- Improves a11y in sidebar by adding aria-hidden attributes in i tags and by fixing two broken aria-hidden attributes.
- Use redis channel to post notifications.
- Removed top border from user contribution calendar.
- Added user callouts to the projects dashboard and user profile.
- Removes label when moving issue to another list that it is currently in.
- Return 202 with JSON body on async removals on V4 API.
- Add filtered search to MR page.
- Add frequently used emojis back to awards menu.
- don't animate logo when downloading files.
- Stop setting Strict-Transport-Securty header from within the app.
- Use "branch_name" instead "branch" on V3 branch creation API.
- Fix archive prefix bug for refs containing dots.
- ensure MR widget dropdown is same color as button.
- Adds Pending and Finished tabs to pipelines page.
- Decrease tanuki logo size.
- Add all available statuses to scope filter for project builds endpoint. (George Andrinopoulos)
- Add filter param for project membership for current_user in API v4.
- Remove help link from right dropdown.
- Fix jobs table header height.
- Combined deploy keys, push rules, protect branches and mirror repository settings options into a single one called Repository.
- Add storage class configuration option for Amazon S3 remote backups. (Jon Keys)
- Specify in the documentation that only projects owners can transfer projects.
- Use native unicode emojis.
- Clear ActiveRecord connections before starting Sidekiq.
- Update account view to display new username.
- Narrow environment payload by using basic project details resource.
- Creating a new branch from an issue will automatically initialize a repository if one doesn't already exist.
- Dashboard project search keeps selected sort & filters.
- Visually show expanded diff lines cant have comments.
- Use full group name in GFM group reference title.
- Make a default namespace of Kubernetes service to contain project ID.
- Present GitLab version for each V3 to V4 API change on v3_to_v4.md.
- Add badges to global dropdown.
- Changed coverage reg expression placeholder text to be more like a placeholder.
- Show members of parent groups on project members page.
- Fix grammer issue in admin/runners.
- Allow slashes in slash command arguments.
- Adds paginationd and folders view to environments table.
- hide loading spinners for server-rendered sidebar fields.
- Change development tanuki favicon colors to match logo color order.
- API issues - support filtering by iids.
>>>>>>> ce/master
## 8.17.4 (2017-03-19)
......
<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
......@@ -4,6 +4,11 @@
import Vue from 'vue';
<<<<<<< HEAD
=======
import Vue from 'vue';
>>>>>>> ce/master
require('./models/discussion');
require('./models/note');
require('./stores/comments');
......
/* 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