Commit 92edb3ed authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into issue-discussions-refactor

# Conflicts:
#	app/models/issue.rb
parents 7c215dd2 9aef0427
......@@ -624,7 +624,7 @@ Style/PredicateName:
# branches, and conditions.
Metrics/AbcSize:
Enabled: true
Max: 56.96
Max: 55.25
# This cop checks if the length of a block exceeds some maximum value.
Metrics/BlockLength:
......@@ -665,7 +665,7 @@ Metrics/ParameterLists:
# A complexity metric geared towards measuring complexity for a human reader.
Metrics/PerceivedComplexity:
Enabled: true
Max: 18
Max: 17
# Lint ########################################################################
......
......@@ -27,7 +27,7 @@ gem 'doorkeeper-openid_connect', '~> 1.1.0'
gem 'omniauth', '~> 1.4.2'
gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-cas3', '~> 1.1.4'
gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.2'
......@@ -126,12 +126,9 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2'
gem 'asciidoctor-plantuml', '0.0.7'
gem 'rouge', '~> 2.0'
gem 'truncato', '~> 0.7.8'
gem 'truncato', '~> 0.7.9'
gem 'bootstrap_form', '~> 2.7.0'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
gem 'nokogiri', '~> 1.6.7', '>= 1.6.7.2'
gem 'nokogiri', '~> 1.8.0'
# Diffs
gem 'diffy', '~> 3.1.0'
......@@ -245,7 +242,7 @@ gem 'uglifier', '~> 2.7.2'
gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.7'
gem 'gemojione', '~> 3.0'
gem 'gemojione', '~> 3.3'
gem 'gon', '~> 6.1.0'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0'
......
......@@ -261,7 +261,7 @@ GEM
ruby-progressbar (~> 1.4)
gemnasium-gitlab-service (0.2.6)
rugged (~> 0.21)
gemojione (3.0.1)
gemojione (3.3.0)
json
get_process_mem (0.2.0)
gettext (3.2.2)
......@@ -283,7 +283,7 @@ GEM
escape_utils (~> 1.1.0)
mime-types (>= 1.19)
rugged (>= 0.23.0b)
github-markup (1.4.0)
github-markup (1.6.1)
gitlab-flowdock-git-hook (1.0.1)
flowdock (~> 0.7)
gitlab-grit (>= 2.4.1)
......@@ -303,13 +303,14 @@ GEM
activesupport (>= 4.1.0)
gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1)
gollum-lib (4.2.1)
github-markup (~> 1.4.0)
gollum-lib (4.2.7)
gemojione (~> 3.2)
github-markup (~> 1.6)
gollum-grit_adapter (~> 1.0)
nokogiri (~> 1.6.4)
rouge (~> 2.0)
sanitize (~> 2.1.0)
stringex (~> 2.5.1)
nokogiri (>= 1.6.1, < 2.0)
rouge (~> 2.1)
sanitize (~> 2.1)
stringex (~> 2.6)
gollum-rugged_adapter (0.4.4)
mime-types (>= 1.15)
rugged (~> 0.25)
......@@ -329,7 +330,7 @@ GEM
multi_json (~> 1.10)
retriable (~> 1.4)
signet (~> 0.6)
google-protobuf (3.3.0)
google-protobuf (3.4.0.2)
googleauth (0.5.1)
faraday (~> 0.9)
jwt (~> 1.4)
......@@ -468,7 +469,7 @@ GEM
railties (>= 4, < 5.2)
loofah (2.0.3)
nokogiri (>= 1.5.9)
mail (2.6.5)
mail (2.6.6)
mime-types (>= 1.16, < 4)
mail_room (0.9.1)
memoist (0.15.0)
......@@ -477,7 +478,7 @@ GEM
method_source (0.8.2)
mime-types (2.99.3)
mimemagic (0.3.0)
mini_portile2 (2.1.0)
mini_portile2 (2.2.0)
minitest (5.7.0)
mmap2 (2.2.7)
mousetrap-rails (1.4.6)
......@@ -491,8 +492,8 @@ GEM
net-ldap (0.16.0)
net-ssh (4.1.0)
netrc (0.11.0)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
nokogiri (1.8.0)
mini_portile2 (~> 2.2.0)
numerizer (0.1.1)
oauth (0.5.1)
oauth2 (1.4.0)
......@@ -515,9 +516,9 @@ GEM
jwt (~> 1.0)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-cas3 (1.1.3)
omniauth-cas3 (1.1.4)
addressable (~> 2.3)
nokogiri (~> 1.6.6)
nokogiri (~> 1.7, >= 1.7.1)
omniauth (~> 1.2)
omniauth-facebook (4.0.0)
omniauth-oauth2 (~> 1.2)
......@@ -605,7 +606,7 @@ GEM
cliver (~> 0.3.1)
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
posix-spawn (0.3.11)
posix-spawn (0.3.13)
powerpack (0.1.1)
premailer (1.10.4)
addressable
......@@ -871,7 +872,7 @@ GEM
state_machines-activerecord (0.4.0)
activerecord (>= 4.1, < 5.1)
state_machines-activemodel (>= 0.3.0)
stringex (2.5.2)
stringex (2.7.1)
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
......@@ -890,9 +891,9 @@ GEM
timfel-krb5-auth (0.8.3)
toml-rb (0.3.15)
citrus (~> 3.0, > 3.0)
truncato (0.7.8)
truncato (0.7.10)
htmlentities (~> 4.3.1)
nokogiri (~> 1.6.1)
nokogiri (~> 1.8.0, >= 1.7.0)
tzinfo (1.2.3)
thread_safe (~> 0.1)
u2f (0.2.1)
......@@ -1014,7 +1015,7 @@ DEPENDENCIES
foreman (~> 0.78.0)
fuubar (~> 2.2.0)
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.0)
gemojione (~> 3.3)
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
......@@ -1060,7 +1061,7 @@ DEPENDENCIES
mysql2 (~> 0.4.5)
net-ldap
net-ssh (~> 4.1.0)
nokogiri (~> 1.6.7, >= 1.6.7.2)
nokogiri (~> 1.8.0)
oauth2 (~> 1.4)
octokit (~> 4.6.2)
oj (~> 2.17.4)
......@@ -1068,7 +1069,7 @@ DEPENDENCIES
omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.3.1)
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-cas3 (~> 1.1.2)
omniauth-cas3 (~> 1.1.4)
omniauth-facebook (~> 4.0.0)
omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.2)
......@@ -1159,7 +1160,7 @@ DEPENDENCIES
thin (~> 1.7.0)
timecop (~> 0.8.0)
toml-rb (~> 0.3.15)
truncato (~> 0.7.8)
truncato (~> 0.7.9)
u2f (~> 0.2.1)
uglifier (~> 2.7.2)
unf (~> 0.1.4)
......
import Cookies from 'js-cookie';
import bp from './breakpoints';
const HIDE_INTERVAL_TIMEOUT = 300;
......@@ -8,9 +7,11 @@ const IS_SHOWING_FLY_OUT_CLASS = 'is-showing-fly-out';
let currentOpenMenu = null;
let menuCornerLocs;
let timeoutId;
let sidebar;
export const mousePos = [];
export const setSidebar = (el) => { sidebar = el; };
export const setOpenMenu = (menu = null) => { currentOpenMenu = menu; };
export const slope = (a, b) => (b.y - a.y) / (b.x - a.x);
......@@ -20,10 +21,8 @@ let headerHeight = 50;
export const getHeaderHeight = () => headerHeight;
export const canShowActiveSubItems = (el) => {
const isHiddenByMedia = bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md';
if (el.classList.contains('active') && !isHiddenByMedia) {
return Cookies.get('sidebar_collapsed') === 'true';
if (el.classList.contains('active') && (sidebar && !sidebar.classList.contains('sidebar-icons-only'))) {
return false;
}
return true;
......@@ -143,13 +142,13 @@ export const documentMouseMove = (e) => {
};
export default () => {
const sidebar = document.querySelector('.sidebar-top-level-items');
sidebar = document.querySelector('.nav-sidebar');
if (!sidebar) return;
const items = [...sidebar.querySelectorAll('.sidebar-top-level-items > li')];
sidebar.addEventListener('mouseleave', () => {
sidebar.querySelector('.sidebar-top-level-items').addEventListener('mouseleave', () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
......
......@@ -37,7 +37,7 @@ export default {
Edited
<time-ago-tooltip
v-if="updatedAt"
placement="bottom"
tooltip-placement="bottom"
:time="updatedAt"
/>
<span
......
......@@ -3,8 +3,9 @@
import _ from 'underscore';
import statusCodes from '../../lib/utils/http_status';
import MonitoringService from '../services/monitoring_service';
import monitoringRow from './monitoring_row.vue';
import monitoringState from './monitoring_state.vue';
import GraphGroup from './graph_group.vue';
import GraphRow from './graph_row.vue';
import EmptyState from './empty_state.vue';
import MonitoringStore from '../stores/monitoring_store';
import eventHub from '../event_hub';
......@@ -31,8 +32,9 @@
},
components: {
monitoringRow,
monitoringState,
GraphGroup,
GraphRow,
EmptyState,
},
methods: {
......@@ -94,7 +96,6 @@
this.updatedAspectRatios = 0;
}
},
},
created() {
......@@ -118,40 +119,27 @@
},
};
</script>
<template>
<div
class="prometheus-graphs"
v-if="!showEmptyState">
<div
class="row"
<div v-if="!showEmptyState" class="prometheus-graphs">
<graph-group
v-for="(groupData, index) in store.groups"
:key="index">
<div
class="col-md-12">
<div
class="panel panel-default prometheus-panel">
<div
class="panel-heading">
<h4>{{groupData.group}}</h4>
</div>
<div
class="panel-body">
<monitoring-row
:key="index"
:name="groupData.group"
>
<graph-row
v-for="(row, index) in groupData.metrics"
:key="index"
:row-data="row"
:update-aspect-ratio="updateAspectRatio"
:deployment-data="store.deploymentData"
/>
</graph-group>
</div>
</div>
</div>
</div>
</div>
<monitoring-state
<empty-state
v-else
:selected-state="state"
:documentation-path="documentationPath"
:settings-path="settingsPath"
v-else
/>
</template>
......@@ -62,48 +62,32 @@
},
};
</script>
<template>
<div
class="prometheus-state">
<div
class="row">
<div
class="col-md-4 col-md-offset-4 state-svg"
v-html="currentState.svg">
</div>
<div class="prometheus-state">
<div class="row">
<div class="col-md-4 col-md-offset-4 state-svg" v-html="currentState.svg"></div>
</div>
<div
class="row">
<div
class="col-md-6 col-md-offset-3">
<h4
class="text-center state-title">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h4 class="text-center state-title">
{{currentState.title}}
</h4>
</div>
</div>
<div
class="row">
<div
class="col-md-6 col-md-offset-3">
<div
class="description-text text-center state-description">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="description-text text-center state-description">
{{currentState.description}}
<a
:href="settingsPath"
v-if="showButtonDescription">
<a v-if="showButtonDescription" :href="settingsPath">
Prometheus server
</a>
</div>
</div>
</div>
<div
class="row state-button-section">
<div
class="col-md-4 col-md-offset-4 text-center state-button">
<a
class="btn btn-success"
:href="buttonPath">
<div class="row state-button-section">
<div class="col-md-4 col-md-offset-4 text-center state-button">
<a class="btn btn-success" :href="buttonPath">
{{currentState.buttonText}}
</a>
</div>
......
<script>
import d3 from 'd3';
import monitoringLegends from './monitoring_legends.vue';
import monitoringFlag from './monitoring_flag.vue';
import monitoringDeployment from './monitoring_deployment.vue';
import GraphLegend from './graph/legend.vue';
import GraphFlag from './graph/flag.vue';
import GraphDeployment from './graph/deployment.vue';
import MonitoringMixin from '../mixins/monitoring_mixins';
import eventHub from '../event_hub';
import measurements from '../utils/measurements';
......@@ -14,7 +14,7 @@
export default {
props: {
columnData: {
graphData: {
type: Object,
required: true,
},
......@@ -66,9 +66,9 @@
},
components: {
monitoringLegends,
monitoringFlag,
monitoringDeployment,
GraphLegend,
GraphFlag,
GraphDeployment,
},
computed: {
......@@ -97,7 +97,7 @@
methods: {
draw() {
const breakpointSize = bp.getBreakpointSize();
const query = this.columnData.queries[0];
const query = this.graphData.queries[0];
this.margin = measurements.large.margin;
if (breakpointSize === 'xs' || breakpointSize === 'sm') {
this.graphHeight = 300;
......@@ -106,7 +106,7 @@
}
this.data = query.result[0].values;
this.unitOfDisplay = query.unit || '';
this.yAxisLabel = this.columnData.y_label || 'Values';
this.yAxisLabel = this.graphData.y_label || 'Values';
this.legendTitle = query.label || 'Average';
this.graphWidth = this.$refs.baseSvg.clientWidth -
this.margin.left - this.margin.right;
......@@ -224,7 +224,7 @@
:class="classType">
<h5
class="text-center graph-title">
{{columnData.title}}
{{graphData.title}}
</h5>
<div
class="prometheus-svg-container"
......@@ -240,7 +240,7 @@
class="y-axis"
transform="translate(70, 20)">
</g>
<monitoring-legends
<graph-legend
:graph-width="graphWidth"
:graph-height="graphHeight"
:margin="margin"
......@@ -268,13 +268,13 @@
stroke-width="2"
transform="translate(-5, 20)">
</path>
<monitoring-deployment
<graph-deployment
:show-deploy-info="showDeployInfo"
:deployment-data="reducedDeploymentData"
:graph-height="graphHeight"
:graph-height-offset="graphHeightOffset"
/>
<monitoring-flag
<graph-flag
v-if="showFlag"
:current-x-coordinate="currentXCoordinate"
:current-y-coordinate="currentYCoordinate"
......
<script>
import { dateFormat, timeFormat } from '../utils/date_time_formatters';
import { dateFormat, timeFormat } from '../../utils/date_time_formatters';
export default {
props: {
......
<script>
import { dateFormat, timeFormat } from '../utils/date_time_formatters';
import { dateFormat, timeFormat } from '../../utils/date_time_formatters';
export default {
props: {
......
<script>
export default {
props: {
name: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="panel panel-default prometheus-panel">
<div class="panel-heading">
<h4>{{name}}</h4>
</div>
<div class="panel-body">
<slot />
</div>
</div>
</template>
<script>
import monitoringColumn from './monitoring_column.vue';
import Graph from './graph.vue';
export default {
props: {
......@@ -17,7 +17,7 @@
},
},
components: {
monitoringColumn,
Graph,
},
computed: {
bootstrapClass() {
......@@ -26,12 +26,12 @@
},
};
</script>
<template>
<div
class="prometheus-row row">
<monitoring-column
v-for="(column, index) in rowData"
:column-data="column"
<div class="prometheus-row row">
<graph
v-for="(graphData, index) in rowData"
:graph-data="graphData"
:class-type="bootstrapClass"
:key="index"
:update-aspect-ratio="updateAspectRatio"
......
import Vue from 'vue';
import Monitoring from './components/monitoring.vue';
import Dashboard from './components/dashboard.vue';
document.addEventListener('DOMContentLoaded', () => new Vue({
el: '#prometheus-graphs',
components: {
'monitoring-dashboard': Monitoring,
Dashboard,
},
render: createElement => createElement('monitoring-dashboard'),
render: createElement => createElement('dashboard'),
}));
......@@ -14,7 +14,14 @@ export default class ProjectSelectComboButton {
bindEvents() {
this.projectSelectInput.siblings('.new-project-item-select-button')
.on('click', this.openDropdown);
.on('click', e => this.openDropdown(e));
this.newItemBtn.on('click', (e) => {
if (!this.getProjectFromLocalStorage()) {
e.preventDefault();
this.openDropdown(e);
}
});
this.projectSelectInput.on('change', () => this.selectProject());
}
......@@ -28,8 +35,9 @@ export default class ProjectSelectComboButton {
}
}
openDropdown() {
$(this).siblings('.project-item-select').select2('open');
// eslint-disable-next-line class-methods-use-this
openDropdown(event) {
$(event.currentTarget).siblings('.project-item-select').select2('open');
}
selectProject() {
......@@ -56,10 +64,8 @@ export default class ProjectSelectComboButton {
if (project) {
this.newItemBtn.attr('href', project.url);
this.newItemBtn.text(`${this.formattedText.defaultTextPrefix} in ${project.name}`);
this.newItemBtn.enable();
} else {
this.newItemBtn.text(`Select project to create ${this.formattedText.presetTextSuffix}`);
this.newItemBtn.disable();
}
}
......
<script>
import commitIconSvg from 'icons/_icon_commit.svg';
import userAvatarLink from './user_avatar/user_avatar_link.vue';
import tooltip from '../directives/tooltip';
export default {
props: {
......@@ -100,17 +101,22 @@
this.author.username ? `${this.author.username}'s avatar` : null;
},
},
data() {
return { commitIconSvg };
directives: {
tooltip,
},
components: {
userAvatarLink,
},
created() {
this.commitIconSvg = commitIconSvg;
},
};
</script>
<template>
<div class="branch-commit">
<div v-if="hasCommitRef" class="icon-container hidden-xs">
<div
v-if="hasCommitRef"
class="icon-container hidden-xs">
<i
v-if="tag"
class="fa fa-tag"
......@@ -126,7 +132,10 @@
<a
v-if="hasCommitRef"
class="ref-name hidden-xs"
:href="commitRef.ref_url">
:href="commitRef.ref_url"
v-tooltip
data-container="body"
:title="commitRef.name">
{{commitRef.name}}
</a>
......@@ -153,7 +162,8 @@
:img-alt="userImageAltDescription"
:tooltip-text="author.username"
/>
<a class="commit-row-message"
<a
class="commit-row-message"
:href="commitUrl">
{{title}}
</a>
......
......@@ -189,7 +189,7 @@
width: auto;
top: 100%;
left: 0;
z-index: 9;
z-index: 200;
min-width: 240px;
max-width: 500px;
margin-top: 2px;
......@@ -801,3 +801,5 @@
margin-top: 2px;
}
}
@include new-style-dropdown('.js-namespace-select + ');
......@@ -267,6 +267,7 @@
// TODO: change global style
.ajax-project-dropdown,
body[data-page="projects:new"] #select2-drop,
body[data-page="projects:blob:new"] #select2-drop,
body[data-page="profiles:show"] #select2-drop,
body[data-page="projects:blob:edit"] #select2-drop {
......
......@@ -612,6 +612,8 @@
}
.mr-version-controls {
@include new-style-dropdown;
position: relative;
background: $gray-light;
color: $gl-text-color;
......
......@@ -117,11 +117,14 @@ class Admin::UsersController < Admin::ApplicationController
user_params_with_pass = user_params.dup
if params[:user][:password].present?
user_params_with_pass.merge!(
password_params = {
password: params[:user][:password],
password_confirmation: params[:user][:password_confirmation],
password_expires_at: Time.now
)
password_confirmation: params[:user][:password_confirmation]
}
password_params[:password_expires_at] = Time.now unless changing_own_password?
user_params_with_pass.merge!(password_params)
end
respond_to do |format|
......@@ -167,6 +170,10 @@ class Admin::UsersController < Admin::ApplicationController
protected
def changing_own_password?
user == current_user
end
def user
@user ||= User.find_by!(username: params[:id])
end
......
module RequiresWhitelistedMonitoringClient
extend ActiveSupport::Concern
include Gitlab::CurrentSettings
included do
before_action :validate_ip_whitelisted_or_valid_token!
end
......
......@@ -94,6 +94,6 @@ class Projects::ApplicationController < ApplicationController
end
def require_pages_enabled!
not_found unless Gitlab.config.pages.enabled
not_found unless @project.pages_available?
end
end
......@@ -202,7 +202,7 @@ module ApplicationHelper
end
def support_url
current_application_settings.help_page_support_url.presence || promo_url + '/getting-help/'
Gitlab::CurrentSettings.current_application_settings.help_page_support_url.presence || promo_url + '/getting-help/'
end
def page_filter_path(options = {})
......
module ApplicationSettingsHelper
extend self
include Gitlab::CurrentSettings
delegate :gravatar_enabled?,
:signup_enabled?,
:password_authentication_enabled?,
......
module AuthHelper
include Gitlab::CurrentSettings
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq).freeze
FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze
......
module ProjectsHelper
include Gitlab::CurrentSettings
def link_to_project(project)
link_to [project.namespace.becomes(Namespace), project], title: h(project.name) do
title = content_tag(:span, project.name, class: 'project-name')
......
class BaseMailer < ActionMailer::Base
include Gitlab::CurrentSettings
around_action :render_with_default_locale
helper ApplicationHelper
helper MarkupHelper
attr_accessor :current_user
helper_method :current_user, :can?
helper_method :current_user, :can?, :current_application_settings
default from: proc { default_sender_address.format }
default reply_to: proc { default_reply_to_address.format }
......
......@@ -387,7 +387,9 @@ module Ci
[
{ key: 'GITLAB_USER_ID', value: user.id.to_s, public: true },
{ key: 'GITLAB_USER_EMAIL', value: user.email, public: true }
{ key: 'GITLAB_USER_EMAIL', value: user.email, public: true },
{ key: 'GITLAB_USER_LOGIN', value: user.username, public: true },
{ key: 'GITLAB_USER_NAME', value: user.name, public: true }
]
end
......
......@@ -28,7 +28,7 @@ module Spammable
def submittable_as_spam?
if user_agent_detail
user_agent_detail.submittable? && current_application_settings.akismet_enabled
user_agent_detail.submittable? && Gitlab::CurrentSettings.current_application_settings.akismet_enabled
else
false
end
......
......@@ -273,7 +273,13 @@ class Issue < ActiveRecord::Base
true
end
def update_project_counter_caches?
state_changed? || confidential_changed?
end
def update_project_counter_caches
return unless update_project_counter_caches?
Projects::OpenIssuesCountService.new(project).refresh_cache
end
......
......@@ -942,7 +942,13 @@ class MergeRequest < ActiveRecord::Base
true
end
def update_project_counter_caches?
state_changed?
end
def update_project_counter_caches
return unless update_project_counter_caches?
Projects::OpenMergeRequestsCountService.new(target_project).refresh_cache
end
......
......@@ -195,6 +195,10 @@ class Namespace < ActiveRecord::Base
parent.present?
end
def subgroup?
has_parent?
end
def soft_delete_without_removing_associations
# We can't use paranoia's `#destroy` since this will hard-delete projects.
# Project uses `pending_delete` instead of the acts_as_paranoia gem.
......
......@@ -19,6 +19,7 @@ class Project < ActiveRecord::Base
include Routable
extend Gitlab::ConfigHelper
extend Gitlab::CurrentSettings
BoardLimitExceeded = Class.new(StandardError)
......@@ -1231,6 +1232,10 @@ class Project < ActiveRecord::Base
File.join(pages_path, 'public')
end
def pages_available?
Gitlab.config.pages.enabled && !namespace.subgroup?
end
def remove_private_deploy_keys
exclude_keys_linked_to_other_projects = <<-SQL
NOT EXISTS (
......
......@@ -2,6 +2,8 @@ class ProtectedBranch < ActiveRecord::Base
include Gitlab::ShellAdapter
include ProtectedRef
extend Gitlab::CurrentSettings
protected_ref_access_levels :merge, :push
# Check if branch name is marked as protected in the system
......
......@@ -1044,7 +1044,7 @@ class Repository
end
def fetch_remote(remote, forced: false, no_tags: false)
gitlab_shell.fetch_remote(repository_storage_path, disk_path, remote, forced: forced, no_tags: no_tags)
gitlab_shell.fetch_remote(raw_repository, remote, forced: forced, no_tags: no_tags)
end
def fetch_ref(source_path, source_ref, target_ref)
......
......@@ -10,6 +10,8 @@ class Snippet < ActiveRecord::Base
include Spammable
include Editable
extend Gitlab::CurrentSettings
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description
cache_markdown_field :content
......
......@@ -2,6 +2,7 @@ require 'carrierwave/orm/activerecord'
class User < ActiveRecord::Base
extend Gitlab::ConfigHelper
extend Gitlab::CurrentSettings
include Gitlab::ConfigHelper
include Gitlab::CurrentSettings
......
require_dependency 'declarative_policy'
class BasePolicy < DeclarativePolicy::Base
include Gitlab::CurrentSettings
desc "User is an instance admin"
with_options scope: :user, score: 0
condition(:admin) { @user&.admin? }
......@@ -15,6 +13,6 @@ class BasePolicy < DeclarativePolicy::Base
desc "The application is restricted from public visibility"
condition(:restricted_public_level, scope: :global) do
current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
Gitlab::CurrentSettings.current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
end
end
class AkismetService
include Gitlab::CurrentSettings
attr_accessor :owner, :text, :options
def initialize(owner, text, options = {})
......
module Auth
class ContainerRegistryAuthenticationService < BaseService
include Gitlab::CurrentSettings
extend Gitlab::CurrentSettings
AUDIENCE = 'container_registry'.freeze
......
module Projects
class UpdatePagesService < BaseService
include Gitlab::CurrentSettings
BLOCK_SIZE = 32.kilobytes
MAX_SIZE = 1.terabyte
SITE_PATH = 'public/'.freeze
......
class UploadService
include Gitlab::CurrentSettings
def initialize(model, file, uploader_class = FileUploader)
@model, @file, @uploader_class = model, file, uploader_class
end
......
module Users
class BuildService < BaseService
include Gitlab::CurrentSettings
def initialize(current_user, params = {})
@current_user = current_user
@params = params.dup
......
......@@ -208,7 +208,7 @@
= link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do
%span
CI / CD
- if Gitlab.config.pages.enabled
- if @project.pages_available?
= nav_link(controller: :pages) do
= link_to project_pages_path(@project), title: 'Pages' do
%span
......
......@@ -23,7 +23,7 @@
= link_to project_settings_ci_cd_path(@project), title: 'Pipelines' do
%span
Pipelines
- if Gitlab.config.pages.enabled
- if @project.pages_available?
= nav_link(controller: :pages) do
= link_to project_pages_path(@project), title: 'Pages' do
%span
......
---
title: Add CI/CD job predefined variables with user name and login
merge_request: 13824
author:
type: added
---
title: Decrease ABC threshold to 55.25
merge_request: 13904
author: Maxim Rydkin
type: other
---
title: Changes the password change workflow for admins.
merge_request: 13901
author:
type: fixed
---
title: Fix the /projects/:id/repository/files/:file_path/raw endpoint to handle dots in the file_path
merge_request: 13512
author: mahcsig
type: fixed
---
title: Adds tooltip to the branch name and improves performance
merge_request:
author:
type: fixed
---
title: Removes disabled state from dashboard project button
merge_request:
author:
type: fixed
---
title: Upgrade mail and nokogiri gems due to security issues
merge_request: 13662
author: Markus Koller
type: security
---
title: Default LDAP config "verify_certificates" to true for security
merge_request: 13915
author:
type: changed
---
title: Filter additional secrets from Rails logs
merge_request:
author:
type: security
---
title: Only update the sidebar count caches when needed
merge_request:
author:
type: other
---
title: Remove pages settings when not available
merge_request:
author:
type: changed
......@@ -51,31 +51,24 @@ module Gitlab
# Configure sensitive parameters which will be filtered from the log file.
#
# Parameters filtered:
# - Password (:password, :password_confirmation)
# - Private tokens
# - Any parameter ending with `_token`
# - Any parameter containing `password`
# - Any parameter containing `secret`
# - Two-factor tokens (:otp_attempt)
# - Repo/Project Import URLs (:import_url)
# - Build variables (:variables)
# - GitLab Pages SSL cert/key info (:certificate, :encrypted_key)
# - Webhook URLs (:hook)
# - GitLab-shell secret token (:secret_token)
# - Sentry DSN (:sentry_dsn)
# - Deploy keys (:key)
config.filter_parameters += [/_token$/, /password/, /secret/]
config.filter_parameters += %i(
authentication_token
certificate
encrypted_key
hook
import_url
incoming_email_token
rss_token
key
otp_attempt
password
password_confirmation
private_token
runners_token
secret_token
sentry_dsn
variables
)
......
......@@ -273,9 +273,8 @@ production: &base
encryption: 'plain'
# Enables SSL certificate verification if encryption method is
# "start_tls" or "simple_tls". (Defaults to false for backward-
# compatibility)
verify_certificates: false
# "start_tls" or "simple_tls". Defaults to true.
verify_certificates: true
# Specifies the path to a file containing a PEM-format CA certificate,
# e.g. if you need to use an internal CA.
......
......@@ -155,18 +155,11 @@ if Settings.ldap['enabled'] || Rails.env.test?
server['encryption'] = 'simple_tls' if server['encryption'] == 'ssl'
server['encryption'] = 'start_tls' if server['encryption'] == 'tls'
# Certificates are not verified for backwards compatibility.
# This default should be flipped to true in 9.5.
if server['verify_certificates'].nil?
server['verify_certificates'] = false
message = <<-MSG.strip_heredoc
LDAP SSL certificate verification is disabled for backwards-compatibility.
Please add the "verify_certificates" option to gitlab.yml for each LDAP
server. Certificate verification will be enabled by default in GitLab 9.5.
MSG
Rails.logger.warn(message)
end
# Certificate verification was added in 9.4.2, and defaulted to false for
# backwards-compatibility.
#
# Since GitLab 10.0, verify_certificates defaults to true for security.
server['verify_certificates'] = true if server['verify_certificates'].nil?
Settings.ldap['servers'][key] = server
end
......
# Be sure to restart your server when you modify this file.
require 'gitlab/current_settings'
include Gitlab::CurrentSettings
if Rails.env.production?
# allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done
begin
sentry_enabled = current_application_settings.sentry_enabled
sentry_enabled = Gitlab::CurrentSettings.current_application_settings.sentry_enabled
rescue
sentry_enabled = false
end
if sentry_enabled
Raven.configure do |config|
config.dsn = current_application_settings.sentry_dsn
config.dsn = Gitlab::CurrentSettings.current_application_settings.sentry_dsn
config.release = Gitlab::REVISION
# Sanitize fields based on those sanitized from Rails.
......
# Be sure to restart your server when you modify this file.
require 'gitlab/current_settings'
include Gitlab::CurrentSettings
# allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done
begin
Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay || 10080
Settings.gitlab['session_expire_delay'] = Gitlab::CurrentSettings.current_application_settings.session_expire_delay || 10080
rescue
Settings.gitlab['session_expire_delay'] ||= 10080
end
......
......@@ -87,9 +87,12 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
encryption: 'plain'
# Enables SSL certificate verification if encryption method is
# "start_tls" or "simple_tls". (Defaults to false for backward-
# compatibility)
verify_certificates: false
# "start_tls" or "simple_tls". Defaults to true since GitLab 10.0 for
# security. This may break installations upon upgrade to 10.0, that did
# not know their LDAP SSL certificates were not setup properly. For
# example, when using self-signed certificates, the ca_file path may
# need to be specified.
verify_certificates: true
# Specifies the path to a file containing a PEM-format CA certificate,
# e.g. if you need to use an internal CA.
......
......@@ -42,7 +42,7 @@ It is also good practice to check the server's own public key to make sure you
are not being targeted by a man-in-the-middle attack. To do this, add another
variable named `SSH_SERVER_HOSTKEYS`. To find out the hostkeys of your server, run
the `ssh-keyscan YOUR_SERVER` command from a trusted network (ideally, from the
server itself), and paste its output into the `SSH_SERVER_HOSTKEY` variable. If
server itself), and paste its output into the `SSH_SERVER_HOSTKEYS` variable. If
you need to connect to multiple servers, concatenate all the server public keys
that you collected into the **Value** of the variable. There must be one key per
line.
......
......@@ -78,6 +78,8 @@ future GitLab releases.**
| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment |
| **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the job |
| **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the job |
| **GITLAB_USER_LOGIN** | 10.0 | all | The login username of the user who started the job |
| **GITLAB_USER_NAME** | 10.0 | all | The real name of the user who started the job |
| **RESTORE_CACHE_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to restore the cache running a job |
## 9.0 Renaming
......
# GitLab Helm Chart
> These Helm charts are in beta. GitLab is working on a [cloud-native](http://docs.gitlab.com/omnibus/package-information/cloud_native.html) set of [Charts](https://gitlab.com/charts/helm.gitlab.io) which will replace these.
> Officially supported cloud providers are Google Container Service and Azure Container Service.
> **Note:**
* GitLab is working on a [cloud native set of Charts](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md) which will replace these.
* Officially supported cloud providers are Google Container Service and Azure Container Service.
The `gitlab` Helm chart deploys GitLab into your Kubernetes cluster.
......@@ -22,9 +22,7 @@ This chart includes the following:
- [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) provisioner support in the underlying infrastructure
- The ability to point a DNS entry or URL at your GitLab install
- The `kubectl` CLI installed locally and authenticated for the cluster
- The Helm Client installed locally
- The Helm Server (Tiller) already installed and running in the cluster, by running `helm init`
- The GitLab Helm Repo [added to your Helm Client](index.md#add-the-gitlab-helm-repository)
- The [Helm client](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) installed locally on your machine
## Configuring GitLab
......@@ -428,7 +426,7 @@ ingress:
## Installing GitLab using the Helm Chart
> You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically restart. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage.
Ensure the GitLab repo has been added and re-initialize Helm:
Add the GitLab Helm repository and initialize Helm:
```bash
helm repo add gitlab https://charts.gitlab.io
......
# GitLab-Omnibus Helm Chart
> These Helm charts are in beta. GitLab is working on a [cloud-native](http://docs.gitlab.com/omnibus/package-information/cloud_native.html) set of [Charts](https://gitlab.com/charts/helm.gitlab.io) which will replace these.
> Officially supported cloud providers are Google Container Service and Azure Container Service.
> **Note:**
* This Helm chart is in beta, while [additional features](https://gitlab.com/charts/charts.gitlab.io/issues/68) are being worked on.
* GitLab is working on a [cloud native set of Charts](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md) which will eventually replace these.
* Officially supported cloud providers are Google Container Service and Azure Container Service.
This work is based partially on: https://github.com/lwolf/kubernetes-gitlab/. GitLab would like to thank Sergey Nuzhdin for his work.
......@@ -29,53 +30,51 @@ Terms:
## Prerequisites
- _At least_ 4 GB of RAM available on your cluster, in chunks of 1 GB. 41GB of storage and 2 CPU are also required.
- _At least_ 4 GB of RAM available on your cluster. 41GB of storage and 2 CPU are also required.
- Kubernetes 1.4+ with Beta APIs enabled
- [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) provisioner support in the underlying infrastructure
- An [external IP address](#networking-prerequisites)
- A [wildcard DNS entry](#networking-prerequisites), which resolves to the external IP address
- The `kubectl` CLI installed locally and authenticated for the cluster
- The Helm Client installed locally
- The Helm Server (Tiller) already installed and running in the cluster, by running `helm init`
- The GitLab Helm Repo [added to your Helm Client](index.md#add-the-gitlab-helm-repository)
- The [Helm client](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) installed locally on your machine
### Networking Prerequisites
This chart configures a GitLab server and Kubernetes cluster which can support dynamic [Review Apps](https://docs.gitlab.com/ee/ci/review_apps/index.html), as well as services like the integrated [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html) and [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/).
To support the GitLab services and dynamic environments, a wildcard DNS entry is required which resolves to the external Load Balancer IP.
To support the GitLab services and dynamic environments, a wildcard DNS entry is required which resolves to the [Load Balancer](#load-balancer-ip) or [External IP](#external-ip). Configuration of the DNS entry will depend upon the DNS service being used.
#### External IP (Recommended)
To provision an external IP on GCP and Azure, simply request a new address from the Networking section. Ensure that the region matches the region your container cluster is created in. Note, it is important that the IP is not assigned at this point in time. It will be automatically assigned once the Helm chart is installed, and assigned to the Load Balancer.
Now that an external IP address has been allocated, ensure that the wildcard DNS entry you would like to use resolves to this IP. Please consult the documentation for your DNS service for more information on creating DNS records.
Finally, set the `baseIP` setting to this IP address when [deploying GitLab](#configuring-and-installing-gitlab).
#### Load Balancer IP
If you do not specify a `baseIP`, an ephemeral IP will be assigned to the Load Balancer or Ingress. You can retrieve this IP by running the following command *after* deploying GitLab:
`kubectl get svc -w --namespace nginx-ingress nginx`
The IP address will be displayed in the `EXTERNAL-IP` field, and should be used to configure the Wildcard DNS entry. For more information on creating a wildcard DNS entry, consult the documentation for the DNS server you are using.
For production deployments of GitLab, we strongly recommend using an [External IP](#external-ip).
## Configuring and Installing GitLab
For most installations, only two parameters are required:
- `baseIP`: the desired [external IP address](#networking-prerequisites)
- `baseDomain`: the [base domain](#networking-prerequisites) with the wildcard host entry resolving to the `baseIP`. For example, `mycompany.io`.
- `legoEmail`: Email address to use when requesting new SSL certificates from Let's Encrypt
Other common configuration options:
- `baseIP`: the desired [external IP address](#networking-prerequisites)
- `gitlab`: Choose the [desired edition](https://about.gitlab.com/products), either `ee` or `ce`. `ce` is the default.
- `gitlabEELicense`: For Enterprise Edition, the [license](https://docs.gitlab.com/ee/user/admin_area/license.html) can be installed directly via the Chart
- `provider`: Optimizes the deployment for a cloud provider. The default is `gke` for GCP, with `acs` also supported for Azure.
- `legoEmail`: Email address to use when requesting new SSL certificates from Let's Encrypt
For additional configuration options, consult the [values.yaml](https://gitlab.com/charts/charts.gitlab.io/blob/master/charts/gitlab-omnibus/values.yaml).
These settings can either be passed directly on the command line:
```bash
helm install --name gitlab --set baseDomain=gitlab.io,baseIP=1.1.1.1,gitlab=ee,gitlabEELicense=$LICENSE,legoEmail=email@gitlab.com gitlab/gitlab-omnibus
```
or within a YAML file:
```bash
helm install --name gitlab -f values.yaml gitlab/gitlab-omnibus
```
> **Note:**
If you are using a machine type with support for less than 4 attached disks, like an Azure trial, you should disable dedicated storage for [Postgres and Redis](#persistent-storage).
### Choosing a different GitLab release version
The version of GitLab installed is based on the `gitlab` setting (see [section](#choosing-gitlab-edition) above), and
......@@ -95,6 +94,8 @@ There is no guarantee that other release versions of GitLab, other than what are
used by default in the chart, will be supported by a chart install.
### Persistent storage
> **Note:**
If you are using a machine type with support for less than 4 attached disks, like an Azure trial, you should disable dedicated storage for [Postgres and Redis](#persistent-storage).
By default, persistent storage is enabled for GitLab and the charts it depends
on (Redis and PostgreSQL).
......@@ -124,9 +125,10 @@ Ingress routing and SSL are automatically configured within this Chart. An NGINX
Let's Encrypt limits a single TLD to five certificate requests within a single week. This means that common DNS wildcard services like [xip.io](http://xip.io) and [nip.io](http://nip.io) are unlikely to work.
## Installing GitLab using the Helm Chart
> You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically restart. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage.
> **Note:**
You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically start. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage.
Ensure the GitLab repo has been added and re-initialize Helm:
Add the GitLab Helm repository and initialize Helm:
```bash
helm repo add gitlab https://charts.gitlab.io
......
# GitLab Runner Helm Chart
> These Helm charts are in beta. GitLab is working on a [cloud-native](http://docs.gitlab.com/omnibus/package-information/cloud_native.html) set of [Charts](https://gitlab.com/charts/helm.gitlab.io) which will replace these.
> Officially supported cloud providers are Google Container Service and Azure Container Service.
> **Note:**
Officially supported cloud providers are Google Container Service and Azure Container Service.
The `gitlab-runner` Helm chart deploys a GitLab Runner instance into your
Kubernetes cluster.
......@@ -17,9 +16,7 @@ This chart configures the Runner to:
- Your GitLab Server's API is reachable from the cluster
- Kubernetes 1.4+ with Beta APIs enabled
- The `kubectl` CLI installed locally and authenticated for the cluster
- The Helm Client installed locally
- The Helm Server (Tiller) already installed and running in the cluster, by running `helm init`
- The GitLab Helm Repo added to your Helm Client. See [Adding GitLab Helm Repo](index.md#add-the-gitlab-helm-repository)
- The [Helm client](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) installed locally on your machine
## Configuring GitLab Runner using the Helm Chart
......@@ -36,6 +33,8 @@ In order for GitLab Runner to function, your config file **must** specify the fo
- `runnerRegistrationToken` - The Registration Token for adding new Runners to the GitLab Server. This must be
retrieved from your GitLab Instance. See the [GitLab Runner Documentation](../../ci/runners/README.md#creating-and-registering-a-runner) for more information.
Unless you need to specify additional configuration, you are [ready to install](#installing-gitlab-runner-using-the-helm-chart).
### Other configuration
The rest of the configuration is [documented in the `values.yaml`](https://gitlab.com/charts/charts.gitlab.io/blob/master/charts/gitlab-runner/values.yaml) in the chart repository.
......@@ -115,6 +114,17 @@ runners:
```
### Controlling maximum Runner concurrency
A single GitLab Runner deployed on Kubernetes is able to execute multiple jobs in parallel by automatically starting additional Runner pods. The [`concurrent` setting](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section) controls the maximum number of pods allowed at a single time, and defaults to `10`.
```yaml
## Configure the maximum number of concurrent jobs
## ref: https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section
##
concurrent: 10
```
### Running Docker-in-Docker containers with GitLab Runners
See [Running Privileged Containers for the Runners](#running-privileged-containers-for-the-runners) for how to enable it,
......@@ -190,7 +200,7 @@ certsSecretName: <SECRET NAME>
## Installing GitLab Runner using the Helm Chart
Ensure the GitLab repo has been added and re-initialize Helm:
Add the GitLab Helm repository and initialize Helm:
```bash
helm repo add gitlab https://charts.gitlab.io
......
# Installing GitLab on Kubernetes
> These Helm charts are in beta. GitLab is working on a [cloud-native](http://docs.gitlab.com/omnibus/package-information/cloud_native.html) set of [Charts](https://gitlab.com/charts/helm.gitlab.io) which will replace these.
> Officially supported cloud providers are Google Container Service and Azure Container Service.
The easiest method to deploy GitLab in [Kubernetes](https://kubernetes.io/) is
to take advantage of the official GitLab Helm charts. [Helm] is a package
to take advantage of GitLab's Helm charts. [Helm] is a package
management tool for Kubernetes, allowing apps to be easily managed via their
Charts. A [Chart] is a detailed description of the application including how it
should be deployed, upgraded, and configured.
The GitLab Helm repository is located at https://charts.gitlab.io.
You can report any issues related to GitLab's Helm Charts at
GitLab provides [official Helm Charts](#official-gitlab-helm-charts-recommended) which is the recommended way to run GitLab with Kubernetes.
There are also two other sets of charts:
* Our [upcoming cloud native Charts](#upcoming-cloud-native-helm-charts), which are in development but will eventually replace the current official charts.
* [Community contributed charts](#community-contributed-helm-charts). These charts should be considered deprecated, in favor of the official charts.
## Official GitLab Helm Charts (Recommended)
These charts utilize our [GitLab Omnibus Docker images](https://docs.gitlab.com/omnibus/docker/README.html). You can report any issues and feedback related to these charts at
https://gitlab.com/charts/charts.gitlab.io/issues.
Contributions and improvements are also very welcome.
## Prerequisites
### Deploying GitLab on Kubernetes (Recommended)
> *Note*: This chart will eventually be replaced by the [cloud native charts](#upcoming-cloud-native-helm-charts), which are presently in development.
The best way to deploy GitLab on Kubernetes is to use the [gitlab-omnibus](gitlab_omnibus.md) chart. It includes everything needed to run GitLab, including: a [Runner](https://docs.gitlab.com/runner/), [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html#gitlab-container-registry), [automatic SSL](https://github.com/kubernetes/charts/tree/master/stable/kube-lego), and an [Ingress](https://github.com/kubernetes/ingress/tree/master/controllers/nginx). This chart is in beta while [additional features](https://gitlab.com/charts/charts.gitlab.io/issues/68) are being completed.
To use the charts, the Helm tool must be installed and initialized. The best
place to start is by reviewing the [Helm Quick Start Guide][helm-quick].
### Deploying just the GitLab Runner
## Add the GitLab Helm repository
To deploy just the GitLab Runner, utilize the [gitlab-runner](gitlab_runner_chart.md) chart. It offers a quick way to configure and deploy the Runner on Kubernetes, regardless of where your GitLab server may be running.
Once Helm has been installed, the GitLab chart repository must be added:
### Advanced deployment of GitLab (Not recommended)
> *Note*: This chart will eventually be replaced by the [cloud native charts](#upcoming-cloud-native-helm-charts), which are presently in development.
```bash
helm repo add gitlab https://charts.gitlab.io
```
If advanced configuration of GitLab is required, the beta [gitlab](gitlab_chart.md) chart can be used which deploys the GitLab service along with optional Postgres and Redis. It offers extensive configuration, but requires deep knowledge of Kubernetes and Helm to use.
After adding the repository, Helm must be re-initialized:
## Upcoming Cloud Native Helm Charts
```bash
helm init
```
GitLab is working towards a building a [cloud native deployment method](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md). A key part of this effort is to isolate each service into it's [own Docker container and Helm chart](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420), rather than utilizing the all-in-one container image of the [current charts](#official-gitlab-helm-charts-recommended).
## Using the GitLab Helm Charts
By offering individual containers and charts, we will be able to provide a number of benefits:
* Easier horizontal scaling of each service
* Smaller more efficient images
* Potential for rolling updates and canaries within a service
* and plenty more.
GitLab makes available three Helm Charts.
This is a large project and will be worked on over the span of multiple releases. For the most up to date status and release information, please see our [tracking issue](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420).
- [gitlab-omnibus](gitlab_omnibus.md): **Recommended** and the easiest way to get started. Includes everything needed to run GitLab, including: a [Runner](https://docs.gitlab.com/runner/), [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html#gitlab-container-registry), [automatic SSL](https://github.com/kubernetes/charts/tree/master/stable/kube-lego), and an [Ingress](https://github.com/kubernetes/ingress/tree/master/controllers/nginx).
- [gitlab](gitlab_chart.md): Just the GitLab service, with optional Postgres and Redis.
- [gitlab-runner](gitlab_runner_chart.md): GitLab Runner, to process CI jobs.
## Community Contributed Helm Charts
We are also working on a new set of [cloud native Charts](https://gitlab.com/charts/helm.gitlab.io) which will eventually replace these.
The community has also [contributed GitLab charts](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ce) to the [Helm Stable Repository](https://github.com/kubernetes/charts#repository-structure). These charts should be considered [deprecated](https://github.com/kubernetes/charts/issues/1138) in favor of the [official Charts](#official-gitlab-helm-charts-recommended).
[chart]: https://github.com/kubernetes/charts
[helm-quick]: https://github.com/kubernetes/helm/blob/master/docs/quickstart.md
[helm]: https://github.com/kubernetes/helm/blob/master/README.md
......@@ -82,7 +82,9 @@ errors during usage.
We recommend having at least 2GB of swap on your server, even if you currently have
enough available RAM. Having swap will help reduce the chance of errors occurring
if your available memory changes.
if your available memory changes. We also recommend [configuring the kernels swappiness setting](https://askubuntu.com/a/103916)
to a low value like `10` to make the most of your RAM while still having the swap
available when needed.
Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about many you need of those.
......
......@@ -100,16 +100,21 @@ inside GitLab that make that possible.
It is possible to download the latest artifacts of a job via a well known URL
so you can use it for scripting purposes.
>**Note:**
The latest artifacts are considered as the artifacts created by jobs in the
latest pipeline that succeeded for the specific ref.
Artifacts for other pipelines can be accessed with direct access to them.
The structure of the URL to download the whole artifacts archive is the following:
```
https://example.com/<namespace>/<project>/builds/artifacts/<ref>/download?job=<job_name>
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/download?job=<job_name>
```
To download a single file from the artifacts use the following URL:
```
https://example.com/<namespace>/<project>/builds/artifacts/<ref>/raw/<path_to_file>?job=<job_name>
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/raw/<path_to_file>?job=<job_name>
```
For example, to download the latest artifacts of the job named `coverage` of
......@@ -117,26 +122,26 @@ the `master` branch of the `gitlab-ce` project that belongs to the `gitlab-org`
namespace, the URL would be:
```
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/download?job=coverage
https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/artifacts/master/download?job=coverage
```
To download the file `coverage/index.html` from the same
artifacts use the following URL:
```
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/raw/coverage/index.html?job=coverage
https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/artifacts/master/raw/coverage/index.html?job=coverage
```
There is also a URL to browse the latest job artifacts:
```
https://example.com/<namespace>/<project>/builds/artifacts/<ref>/browse?job=<job_name>
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/browse?job=<job_name>
```
For example:
```
https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/browse?job=coverage
https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/artifacts/master/browse?job=coverage
```
The latest builds are also exposed in the UI in various places. Specifically,
......
module API
class Files < Grape::API
FILE_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(file_path: API::NO_SLASH_URL_PART_REGEX)
# Prevents returning plain/text responses for files with .txt extension
after_validation { content_type "application/json" }
......@@ -58,13 +60,13 @@ module API
params do
requires :id, type: String, desc: 'The project ID'
end
resource :projects, requirements: { id: %r{[^/]+} } do
resource :projects, requirements: FILE_ENDPOINT_REQUIREMENTS do
desc 'Get raw file contents from the repository'
params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
requires :ref, type: String, desc: 'The name of branch, tag commit'
end
get ":id/repository/files/:file_path/raw" do
get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
send_git_blob @repo, @blob
......@@ -75,7 +77,7 @@ module API
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
requires :ref, type: String, desc: 'The name of branch, tag or commit'
end
get ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
get ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
{
......@@ -95,7 +97,7 @@ module API
params do
use :extended_file_params
end
post ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
post ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
authorize! :push_code, user_project
file_params = declared_params(include_missing: false)
......@@ -113,7 +115,7 @@ module API
params do
use :extended_file_params
end
put ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
put ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
authorize! :push_code, user_project
file_params = declared_params(include_missing: false)
......@@ -137,7 +139,7 @@ module API
params do
use :simple_file_params
end
delete ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
delete ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
authorize! :push_code, user_project
file_params = declared_params(include_missing: false)
......
module API
module Helpers
module Runner
include Gitlab::CurrentSettings
JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN'.freeze
JOB_TOKEN_PARAM = :token
UPDATE_RUNNER_EVERY = 10 * 60
......
# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
class EmailTemplateInterceptor
include Gitlab::CurrentSettings
extend Gitlab::CurrentSettings
def self.delivering_email(message)
# Remove HTML part if HTML emails are disabled.
......
......@@ -226,7 +226,13 @@ module Github
while url
response = Github::Client.new(options).get(url, state: :all, sort: :created, direction: :asc)
response.body.each do |raw|
response.body.each { |raw| populate_issue(raw) }
url = response.rels[:next]
end
end
def populate_issue(raw)
representation = Github::Representation::Issue.new(raw, options)
begin
......@@ -235,12 +241,12 @@ module Github
# for both features, like manipulating assignees, labels
# and milestones, are provided within the Issues API.
if representation.pull_request?
next unless representation.has_labels?
return unless representation.has_labels?
merge_request = MergeRequest.find_by!(target_project_id: project.id, iid: representation.iid)
merge_request.update_attribute(:label_ids, label_ids(representation.labels))
else
next if Issue.where(iid: representation.iid, project_id: project.id).exists?
return if Issue.where(iid: representation.iid, project_id: project.id).exists?
author_id = user_id(representation.author, project.creator_id)
issue = Issue.new
......@@ -268,10 +274,6 @@ module Github
end
end
url = response.rels[:next]
end
end
def fetch_comments(noteable, type, url, klass = Note)
while url
comments = Github::Client.new(options).get(url)
......
......@@ -6,6 +6,8 @@ module Gitlab
# Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters
# the resulting HTML through HTML pipeline filters.
module Asciidoc
extend Gitlab::CurrentSettings
DEFAULT_ADOC_ATTRS = [
'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab',
'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font'
......
......@@ -19,6 +19,8 @@ module Gitlab
OPTIONAL_SCOPES = (AVAILABLE_SCOPES + OPENID_SCOPES - DEFAULT_SCOPES).freeze
class << self
include Gitlab::CurrentSettings
def find_for_git_client(login, password, project:, ip:)
raise "Must provide an IP for rate limiting" if ip.nil?
......
......@@ -8,6 +8,10 @@ module Gitlab
class_methods do
def attributes(*attributes)
attributes.flatten.each do |attribute|
if method_defined?(attribute)
raise ArgumentError, 'Method already defined!'
end
define_method(attribute) do
return unless config.is_a?(Hash)
......
......@@ -15,9 +15,10 @@ module Gitlab
#
module Configurable
extend ActiveSupport::Concern
include Validatable
included do
include Validatable
validations do
validates :config, type: Hash
end
......
......@@ -59,10 +59,10 @@ module Gitlab
entry :services, Entry::Services,
description: 'Services that will be used to execute this job.'
entry :only, Entry::Trigger,
entry :only, Entry::Policy,
description: 'Refs policy this job will be executed for.'
entry :except, Entry::Trigger,
entry :except, Entry::Policy,
description: 'Refs policy this job will be executed for.'
entry :variables, Entry::Variables,
......
......@@ -16,8 +16,9 @@ module Gitlab
@metadata = metadata
@entries = {}
@validator = self.class.validator.new(self)
@validator.validate(:new)
self.class.aspects.to_a.each do |aspect|
instance_exec(&aspect)
end
end
def [](key)
......@@ -47,7 +48,7 @@ module Gitlab
end
def errors
@validator.messages + descendants.flat_map(&:errors)
[]
end
def value
......@@ -70,6 +71,13 @@ module Gitlab
true
end
def location
name = @key.presence || self.class.name.to_s.demodulize
.underscore.humanize.downcase
ancestors.map(&:key).append(name).compact.join(':')
end
def inspect
val = leaf? ? config : descendants
unspecified = specified? ? '' : '(unspecified) '
......@@ -79,8 +87,8 @@ module Gitlab
def self.default
end
def self.validator
Validator
def self.aspects
@aspects ||= []
end
end
end
......
module Gitlab
module Ci
class Config
module Entry
##
# Entry that represents an only/except trigger policy for the job.
#
class Policy < Simplifiable
strategy :RefsPolicy, if: -> (config) { config.is_a?(Array) }
class RefsPolicy < Entry::Node
include Entry::Validatable
validations do
validates :config, array_of_strings_or_regexps: true
end
end
class UnknownStrategy < Entry::Node
def errors
["#{location} has to be either an array of conditions or a hash"]
end
end
def self.default
end
end
end
end
end
end
module Gitlab
module Ci
class Config
module Entry
class Simplifiable < SimpleDelegator
EntryStrategy = Struct.new(:name, :condition)
def initialize(config, **metadata)
unless self.class.const_defined?(:UnknownStrategy)
raise ArgumentError, 'UndefinedStrategy not available!'
end
strategy = self.class.strategies.find do |variant|
variant.condition.call(config)
end
entry = self.class.entry_class(strategy)
super(entry.new(config, metadata))
end
def self.strategy(name, **opts)
EntryStrategy.new(name, opts.fetch(:if)).tap do |strategy|
strategies.append(strategy)
end
end
def self.strategies
@strategies ||= []
end
def self.entry_class(strategy)
if strategy.present?
self.const_get(strategy.name)
else
self::UnknownStrategy
end
end
end
end
end
end
end
module Gitlab
module Ci
class Config
module Entry
##
# Entry that represents a trigger policy for the job.
#
class Trigger < Node
include Validatable
validations do
validates :config, array_of_strings_or_regexps: true
end
end
end
end
end
end
......@@ -5,6 +5,17 @@ module Gitlab
module Validatable
extend ActiveSupport::Concern
def self.included(node)
node.aspects.append -> do
@validator = self.class.validator.new(self)
@validator.validate(:new)
end
end
def errors
@validator.messages + descendants.flat_map(&:errors)
end
class_methods do
def validator
@validator ||= Class.new(Entry::Validator).tap do |validator|
......
......@@ -8,7 +8,6 @@ module Gitlab
def initialize(entry)
super(entry)
@entry = entry
end
def messages
......@@ -20,21 +19,6 @@ module Gitlab
def self.name
'Validator'
end
private
def location
predecessors = ancestors.map(&:key).compact
predecessors.append(key_name).join(':')
end
def key_name
if key.blank?
@entry.class.name.demodulize.underscore.humanize
else
key
end
end
end
end
end
......
module Gitlab
module CurrentSettings
extend self
def current_application_settings
if RequestStore.active?
RequestStore.fetch(:current_application_settings) { ensure_application_settings! }
......
......@@ -47,6 +47,9 @@ module Gitlab
# Directory name of repo
attr_reader :name
# Relative path of repo
attr_reader :relative_path
# Rugged repo object
attr_reader :rugged
......
......@@ -37,6 +37,22 @@ module Gitlab
request = Gitaly::ApplyGitattributesRequest.new(repository: @gitaly_repo, revision: revision)
GitalyClient.call(@storage, :repository_service, :apply_gitattributes, request)
end
def fetch_remote(remote, ssh_auth: nil, forced: false, no_tags: false)
request = Gitaly::FetchRemoteRequest.new(repository: @gitaly_repo, remote: remote, force: forced, no_tags: no_tags)
if ssh_auth&.ssh_import?
if ssh_auth.ssh_key_auth? && ssh_auth.ssh_private_key.present?
request.ssh_key = ssh_auth.ssh_private_key
end
if ssh_auth.ssh_known_hosts.present?
request.known_hosts = ssh_auth.ssh_known_hosts
end
end
GitalyClient.call(@storage, :repository_service, :fetch_remote, request)
end
end
end
end
......@@ -3,6 +3,7 @@
module Gitlab
module GonHelper
include WebpackHelper
include Gitlab::CurrentSettings
def add_gon_variables
gon.api_version = 'v4'
......
module Gitlab
module Metrics
module InfluxDb
extend Gitlab::CurrentSettings
include Gitlab::CurrentSettings
extend self
MUTEX = Mutex.new
......
module Gitlab
module PerformanceBar
include Gitlab::CurrentSettings
extend Gitlab::CurrentSettings
ALLOWED_USER_IDS_KEY = 'performance_bar_allowed_user_ids:v2'.freeze
EXPIRY_TIME = 5.minutes
......
module Gitlab
class PollingInterval
include Gitlab::CurrentSettings
extend Gitlab::CurrentSettings
HEADER_NAME = 'Poll-Interval'.freeze
......
module Gitlab
module ProtocolAccess
extend Gitlab::CurrentSettings
def self.allowed?(protocol)
if protocol == 'web'
true
......
module Gitlab
module Recaptcha
extend Gitlab::CurrentSettings
def self.load_configurations!
if current_application_settings.recaptcha_enabled
::Recaptcha.configure do |config|
......
module Gitlab
module Sentry
extend Gitlab::CurrentSettings
def self.enabled?
Rails.env.production? && current_application_settings.sentry_enabled?
end
......
......@@ -98,35 +98,26 @@ module Gitlab
# Fetch remote for repository
#
# name - project path with namespace
# repository - an instance of Git::Repository
# remote - remote name
# forced - should we use --force flag?
# no_tags - should we use --no-tags flag?
#
# Ex.
# fetch_remote("gitlab/gitlab-ci", "upstream")
# fetch_remote(my_repo, "upstream")
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387
def fetch_remote(storage, name, remote, ssh_auth: nil, forced: false, no_tags: false)
args = [gitlab_shell_projects_path, 'fetch-remote', storage, "#{name}.git", remote, "#{Gitlab.config.gitlab_shell.git_timeout}"]
args << '--force' if forced
args << '--no-tags' if no_tags
vars = {}
if ssh_auth&.ssh_import?
if ssh_auth.ssh_key_auth? && ssh_auth.ssh_private_key.present?
vars['GITLAB_SHELL_SSH_KEY'] = ssh_auth.ssh_private_key
def fetch_remote(repository, remote, ssh_auth: nil, forced: false, no_tags: false)
gitaly_migrate(:fetch_remote) do |is_enabled|
if is_enabled
repository.gitaly_repository_client.fetch_remote(remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags)
else
storage_path = Gitlab.config.repositories.storages[repository.storage]["path"]
local_fetch_remote(storage_path, repository.relative_path, remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags)
end
if ssh_auth.ssh_known_hosts.present?
vars['GITLAB_SHELL_KNOWN_HOSTS'] = ssh_auth.ssh_known_hosts
end
end
gitlab_shell_fast_execute_raise_error(args, vars)
end
# Move repository
# storage - project's storage path
# path - project path with namespace
......@@ -302,6 +293,26 @@ module Gitlab
private
def local_fetch_remote(storage, name, remote, ssh_auth: nil, forced: false, no_tags: false)
args = [gitlab_shell_projects_path, 'fetch-remote', storage, name, remote, "#{Gitlab.config.gitlab_shell.git_timeout}"]
args << '--force' if forced
args << '--no-tags' if no_tags
vars = {}
if ssh_auth&.ssh_import?
if ssh_auth.ssh_key_auth? && ssh_auth.ssh_private_key.present?
vars['GITLAB_SHELL_SSH_KEY'] = ssh_auth.ssh_private_key
end
if ssh_auth.ssh_known_hosts.present?
vars['GITLAB_SHELL_KNOWN_HOSTS'] = ssh_auth.ssh_known_hosts
end
end
gitlab_shell_fast_execute_raise_error(args, vars)
end
def gitlab_shell_fast_execute(cmd)
output, status = gitlab_shell_fast_execute_helper(cmd)
......@@ -325,5 +336,13 @@ module Gitlab
# from wasting I/O by searching through GEM_PATH
Bundler.with_original_env { Popen.popen(cmd, nil, vars) }
end
def gitaly_migrate(method, &block)
Gitlab::GitalyClient.migrate(method, &block)
rescue GRPC::NotFound, GRPC::BadStatus => e
# Old Popen code returns [Error, output] to the caller, so we
# need to do the same here...
raise Error, e
end
end
end
module Gitlab
class UsageData
class << self
include Gitlab::CurrentSettings
class << self
def data(force_refresh: false)
Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) { uncached_data }
end
......
......@@ -80,7 +80,7 @@ class GithubImport
end
def visibility_level
@repo['private'] ? Gitlab::VisibilityLevel::PRIVATE : current_application_settings.default_project_visibility
@repo['private'] ? Gitlab::VisibilityLevel::PRIVATE : Gitlab::CurrentSettings.current_application_settings.default_project_visibility
end
end
......
......@@ -3,7 +3,7 @@
require ::File.expand_path('../lib/gitlab/popen', __dir__)
tasks = [
%w[bundle exec bundle-audit check --update --ignore CVE-2016-4658 CVE-2017-5029],
%w[bundle exec bundle-audit check --update],
%w[bundle exec rake config_lint],
%w[bundle exec rake flay],
%w[bundle exec rake haml_lint],
......
......@@ -150,6 +150,18 @@ describe Admin::UsersController do
post :update, params
end
context 'when the admin changes his own password' do
it 'updates the password' do
expect { update_password(admin, 'AValidPassword1') }
.to change { admin.reload.encrypted_password }
end
it 'does not set the new password to expire immediately' do
expect { update_password(admin, 'AValidPassword1') }
.not_to change { admin.reload.password_expires_at }
end
end
context 'when the new password is valid' do
it 'redirects to the user' do
update_password(user, 'AValidPassword1')
......@@ -158,15 +170,13 @@ describe Admin::UsersController do
end
it 'updates the password' do
update_password(user, 'AValidPassword1')
expect { user.reload }.to change { user.encrypted_password }
expect { update_password(user, 'AValidPassword1') }
.to change { user.reload.encrypted_password }
end
it 'sets the new password to expire immediately' do
update_password(user, 'AValidPassword1')
expect { user.reload }.to change { user.password_expires_at }.to(a_value <= Time.now)
expect { update_password(user, 'AValidPassword1') }
.to change { user.reload.password_expires_at }.to be_within(2.seconds).of(Time.now)
end
end
......@@ -184,9 +194,8 @@ describe Admin::UsersController do
end
it 'does not update the password' do
update_password(user, 'invalid')
expect { user.reload }.not_to change { user.encrypted_password }
expect { update_password(user, 'invalid') }
.not_to change { user.reload.encrypted_password }
end
end
......@@ -204,9 +213,8 @@ describe Admin::UsersController do
end
it 'does not update the password' do
update_password(user, 'AValidPassword1', 'AValidPassword2')
expect { user.reload }.not_to change { user.encrypted_password }
expect { update_password(user, 'AValidPassword1', 'AValidPassword2') }
.not_to change { user.reload.encrypted_password }
end
end
end
......
......@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::PagesController do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :access_requestable) }
let(:project) { create(:project, :public) }
let(:request_params) do
{
......@@ -23,6 +23,17 @@ describe Projects::PagesController do
expect(response).to have_http_status(200)
end
context 'when the project is in a subgroup' do
let(:group) { create(:group, :nested) }
let(:project) { create(:project, namespace: group) }
it 'returns a 404 status code' do
get :show, request_params
expect(response).to have_http_status(404)
end
end
end
describe 'DELETE destroy' do
......
......@@ -4,7 +4,7 @@ describe VersionCheckHelper do
describe '#version_status_badge' do
it 'should return nil if not dev environment and not enabled' do
allow(Rails.env).to receive(:production?) { false }
allow(current_application_settings).to receive(:version_check_enabled) { false }
allow(helper.current_application_settings).to receive(:version_check_enabled) { false }
expect(helper.version_status_badge).to be(nil)
end
......@@ -12,7 +12,7 @@ describe VersionCheckHelper do
context 'when production and enabled' do
before do
allow(Rails.env).to receive(:production?) { true }
allow(current_application_settings).to receive(:version_check_enabled) { true }
allow(helper.current_application_settings).to receive(:version_check_enabled) { true }
allow_any_instance_of(VersionCheck).to receive(:url) { 'https://version.host.com/check.svg?gitlab_info=xxx' }
@image_tag = helper.version_status_badge
......
import Cookies from 'js-cookie';
import {
calculateTop,
showSubLevelItems,
......@@ -11,6 +10,7 @@ import {
getHideSubItemsInterval,
documentMouseMove,
getHeaderHeight,
setSidebar,
} from '~/fly_out_nav';
import bp from '~/breakpoints';
......@@ -113,7 +113,6 @@ describe('Fly out sidebar navigation', () => {
clientX: el.getBoundingClientRect().left + 20,
clientY: el.getBoundingClientRect().top + 10,
});
console.log(el);
expect(
getHideSubItemsInterval(),
......@@ -283,7 +282,7 @@ describe('Fly out sidebar navigation', () => {
describe('canShowActiveSubItems', () => {
afterEach(() => {
Cookies.remove('sidebar_collapsed');
setSidebar(null);
});
it('returns true by default', () => {
......@@ -292,36 +291,23 @@ describe('Fly out sidebar navigation', () => {
).toBeTruthy();
});
it('returns false when cookie is false & element is active', () => {
Cookies.set('sidebar_collapsed', 'false');
it('returns false when active & expanded sidebar', () => {
const sidebar = document.createElement('div');
el.classList.add('active');
expect(
canShowActiveSubItems(el),
).toBeFalsy();
});
it('returns true when cookie is false & element is active', () => {
Cookies.set('sidebar_collapsed', 'true');
el.classList.add('active');
setSidebar(sidebar);
expect(
canShowActiveSubItems(el),
).toBeTruthy();
).toBeFalsy();
});
it('returns true when element is active & breakpoint is sm', () => {
breakpointSize = 'sm';
it('returns true when active & collapsed sidebar', () => {
const sidebar = document.createElement('div');
sidebar.classList.add('sidebar-icons-only');
el.classList.add('active');
expect(
canShowActiveSubItems(el),
).toBeTruthy();
});
it('returns true when element is active & breakpoint is md', () => {
breakpointSize = 'md';
el.classList.add('active');
setSidebar(sidebar);
expect(
canShowActiveSubItems(el),
......
......@@ -46,4 +46,14 @@ describe('edited', () => {
expect(editedComponent.$el.querySelector('.author_link')).toBeFalsy();
expect(editedComponent.$el.querySelector('time')).toBeTruthy();
});
it('renders time ago tooltip at the bottom', () => {
const editedComponent = new EditedComponent({
propsData: {
updatedAt: '2017-05-15T12:31:04.428Z',
},
}).$mount();
expect(editedComponent.$el.querySelector('time').dataset.placement).toEqual('bottom');
});
});
import Vue from 'vue';
import Monitoring from '~/monitoring/components/monitoring.vue';
import Dashboard from '~/monitoring/components/dashboard.vue';
import { MonitorMockInterceptor } from './mock_data';
describe('Monitoring', () => {
describe('Dashboard', () => {
const fixtureName = 'environments/metrics/metrics.html.raw';
let MonitoringComponent;
let DashboardComponent;
let component;
preloadFixtures(fixtureName);
beforeEach(() => {
loadFixtures(fixtureName);
MonitoringComponent = Vue.extend(Monitoring);
DashboardComponent = Vue.extend(Dashboard);
});
describe('no metrics are available yet', () => {
it('shows a getting started empty state when no metrics are present', () => {
component = new MonitoringComponent({
component = new DashboardComponent({
el: document.querySelector('#prometheus-graphs'),
});
......@@ -36,7 +36,7 @@ describe('Monitoring', () => {
});
it('shows up a loading state', (done) => {
component = new MonitoringComponent({
component = new DashboardComponent({
el: document.querySelector('#prometheus-graphs'),
});
component.$mount();
......
import Vue from 'vue';
import MonitoringState from '~/monitoring/components/monitoring_state.vue';
import EmptyState from '~/monitoring/components/empty_state.vue';
import { statePaths } from './mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(MonitoringState);
const Component = Vue.extend(EmptyState);
return new Component({
propsData,
......@@ -14,7 +14,7 @@ function getTextFromNode(component, selector) {
return component.$el.querySelector(selector).firstChild.nodeValue.trim();
}
describe('MonitoringState', () => {
describe('EmptyState', () => {
describe('Computed props', () => {
it('currentState', () => {
const component = createComponent({
......
import Vue from 'vue';
import MonitoringState from '~/monitoring/components/monitoring_deployment.vue';
import { deploymentData } from './mock_data';
import GraphDeployment from '~/monitoring/components/graph/deployment.vue';
import { deploymentData } from '../mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(MonitoringState);
const Component = Vue.extend(GraphDeployment);
return new Component({
propsData,
......
import Vue from 'vue';
import MonitoringFlag from '~/monitoring/components/monitoring_flag.vue';
import GraphFlag from '~/monitoring/components/graph/flag.vue';
const createComponent = (propsData) => {
const Component = Vue.extend(MonitoringFlag);
const Component = Vue.extend(GraphFlag);
return new Component({
propsData,
......@@ -14,7 +14,7 @@ function getCoordinate(component, selector, coordinate) {
return parseInt(coordinateVal, 10);
}
describe('MonitoringFlag', () => {
describe('GraphFlag', () => {
it('has a line and a circle located at the currentXCoordinate and currentYCoordinate', () => {
const component = createComponent({
currentXCoordinate: 200,
......
import Vue from 'vue';
import MonitoringLegends from '~/monitoring/components/monitoring_legends.vue';
import GraphLegend from '~/monitoring/components/graph/legend.vue';
import measurements from '~/monitoring/utils/measurements';
const createComponent = (propsData) => {
const Component = Vue.extend(MonitoringLegends);
const Component = Vue.extend(GraphLegend);
return new Component({
propsData,
......@@ -14,7 +14,7 @@ function getTextFromNode(component, selector) {
return component.$el.querySelector(selector).firstChild.nodeValue.trim();
}
describe('MonitoringLegends', () => {
describe('GraphLegend', () => {
describe('Computed props', () => {
it('textTransform', () => {
const component = createComponent({
......
import Vue from 'vue';
import MonitoringRow from '~/monitoring/components/monitoring_row.vue';
import GraphRow from '~/monitoring/components/graph_row.vue';
import MonitoringMixins from '~/monitoring/mixins/monitoring_mixins';
import { deploymentData, singleRowMetrics } from './mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(MonitoringRow);
const Component = Vue.extend(GraphRow);
return new Component({
propsData,
}).$mount();
};
describe('MonitoringRow', () => {
describe('GraphRow', () => {
beforeEach(() => {
spyOn(MonitoringMixins.methods, 'formatDeployments').and.returnValue({});
});
describe('Computed props', () => {
it('bootstrapClass is set to col-md-6 when rowData is higher/equal to 2', () => {
const component = createComponent({
......
import Vue from 'vue';
import _ from 'underscore';
import MonitoringColumn from '~/monitoring/components/monitoring_column.vue';
import Graph from '~/monitoring/components/graph.vue';
import MonitoringMixins from '~/monitoring/mixins/monitoring_mixins';
import eventHub from '~/monitoring/event_hub';
import { deploymentData, singleRowMetrics } from './mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(MonitoringColumn);
const Component = Vue.extend(Graph);
return new Component({
propsData,
}).$mount();
};
describe('MonitoringColumn', () => {
describe('Graph', () => {
beforeEach(() => {
spyOn(MonitoringMixins.methods, 'formatDeployments').and.callFake(function fakeFormat() {
return {};
});
spyOn(MonitoringMixins.methods, 'formatDeployments').and.returnValue({});
});
it('has a title', () => {
const component = createComponent({
columnData: singleRowMetrics[0],
graphData: singleRowMetrics[0],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
});
expect(component.$el.querySelector('.text-center').innerText.trim()).toBe(component.columnData.title);
expect(component.$el.querySelector('.text-center').innerText.trim()).toBe(component.graphData.title);
});
it('creates a path for the line and area of the graph', (done) => {
const component = createComponent({
columnData: singleRowMetrics[0],
graphData: singleRowMetrics[0],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
......@@ -53,7 +51,7 @@ describe('MonitoringColumn', () => {
describe('Computed props', () => {
it('axisTransform translates an element Y position depending of its height', () => {
const component = createComponent({
columnData: singleRowMetrics[0],
graphData: singleRowMetrics[0],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
......@@ -66,7 +64,7 @@ describe('MonitoringColumn', () => {
it('outterViewBox gets a width and height property based on the DOM size of the element', () => {
const component = createComponent({
columnData: singleRowMetrics[0],
graphData: singleRowMetrics[0],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
......@@ -81,7 +79,7 @@ describe('MonitoringColumn', () => {
it('sends an event to the eventhub when it has finished resizing', (done) => {
const component = createComponent({
columnData: singleRowMetrics[0],
graphData: singleRowMetrics[0],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
......@@ -97,13 +95,13 @@ describe('MonitoringColumn', () => {
it('has a title for the y-axis and the chart legend that comes from the backend', () => {
const component = createComponent({
columnData: singleRowMetrics[0],
graphData: singleRowMetrics[0],
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
});
expect(component.yAxisLabel).toEqual(component.columnData.y_label);
expect(component.legendTitle).toEqual(component.columnData.queries[0].label);
expect(component.yAxisLabel).toEqual(component.graphData.y_label);
expect(component.legendTitle).toEqual(component.graphData.queries[0].label);
});
});
......@@ -32,11 +32,6 @@ describe('Project Select Combo Button', function () {
this.comboButton = new ProjectSelectComboButton(this.projectSelectInput);
});
it('newItemBtn is disabled', function () {
expect(this.newItemBtn.hasAttribute('disabled')).toBe(true);
expect(this.newItemBtn.classList.contains('disabled')).toBe(true);
});
it('newItemBtn href is null', function () {
expect(this.newItemBtn.getAttribute('href')).toBe('');
});
......@@ -53,11 +48,6 @@ describe('Project Select Combo Button', function () {
this.comboButton = new ProjectSelectComboButton(this.projectSelectInput);
});
it('newItemBtn is not disabled', function () {
expect(this.newItemBtn.hasAttribute('disabled')).toBe(false);
expect(this.newItemBtn.classList.contains('disabled')).toBe(false);
});
it('newItemBtn href is correctly set', function () {
expect(this.newItemBtn.getAttribute('href')).toBe(this.defaults.projectMeta.url);
});
......@@ -82,11 +72,6 @@ describe('Project Select Combo Button', function () {
.trigger('change');
});
it('newItemBtn is not disabled', function () {
expect(this.newItemBtn.hasAttribute('disabled')).toBe(false);
expect(this.newItemBtn.classList.contains('disabled')).toBe(false);
});
it('newItemBtn href is correctly set', function () {
expect(this.newItemBtn.getAttribute('href'))
.toBe('http://myothercoolproject.com/issues/new');
......
......@@ -344,28 +344,31 @@ module Ci
let(:config) { { rspec: { script: "rspec", type: "test", only: only } } }
let(:processor) { GitlabCiYamlProcessor.new(YAML.dump(config)) }
shared_examples 'raises an error' do
it do
expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError, 'jobs:rspec:only config should be an array of strings or regexps')
end
end
context 'when it is integer' do
let(:only) { 1 }
it_behaves_like 'raises an error'
it do
expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError,
'jobs:rspec:only has to be either an array of conditions or a hash')
end
end
context 'when it is an array of integers' do
let(:only) { [1, 1] }
it_behaves_like 'raises an error'
it do
expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError,
'jobs:rspec:only config should be an array of strings or regexps')
end
end
context 'when it is invalid regex' do
let(:only) { ["/*invalid/"] }
it_behaves_like 'raises an error'
it do
expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError,
'jobs:rspec:only config should be an array of strings or regexps')
end
end
end
end
......@@ -518,28 +521,31 @@ module Ci
let(:config) { { rspec: { script: "rspec", except: except } } }
let(:processor) { GitlabCiYamlProcessor.new(YAML.dump(config)) }
shared_examples 'raises an error' do
it do
expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError, 'jobs:rspec:except config should be an array of strings or regexps')
end
end
context 'when it is integer' do
let(:except) { 1 }
it_behaves_like 'raises an error'
it do
expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError,
'jobs:rspec:except has to be either an array of conditions or a hash')
end
end
context 'when it is an array of integers' do
let(:except) { [1, 1] }
it_behaves_like 'raises an error'
it do
expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError,
'jobs:rspec:except config should be an array of strings or regexps')
end
end
context 'when it is invalid regex' do
let(:except) { ["/*invalid/"] }
it_behaves_like 'raises an error'
it do
expect { processor }.to raise_error(GitlabCiYamlProcessor::ValidationError,
'jobs:rspec:except config should be an array of strings or regexps')
end
end
end
end
......
......@@ -41,7 +41,7 @@ describe Gitlab::Auth::UniqueIpsLimiter, :clean_gitlab_redis_shared_state do
context 'allow 2 unique ips' do
before do
current_application_settings.update!(unique_ips_limit_per_user: 2)
Gitlab::CurrentSettings.current_application_settings.update!(unique_ips_limit_per_user: 2)
end
it 'blocks user trying to login from third ip' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Attributable do
let(:node) { Class.new }
let(:node) do
Class.new do
include Gitlab::Ci::Config::Entry::Attributable
end
end
let(:instance) { node.new }
before do
node.include(described_class)
node.class_eval do
attributes :name, :test
end
end
context 'config is a hash' do
context 'when config is a hash' do
before do
allow(instance)
.to receive(:config)
......@@ -29,7 +32,7 @@ describe Gitlab::Ci::Config::Entry::Attributable do
end
end
context 'config is not a hash' do
context 'when config is not a hash' do
before do
allow(instance)
.to receive(:config)
......@@ -40,4 +43,18 @@ describe Gitlab::Ci::Config::Entry::Attributable do
expect(instance.test).to be_nil
end
end
context 'when method is already defined in a superclass' do
it 'raises an error' do
expectation = expect do
Class.new(String) do
include Gitlab::Ci::Config::Entry::Attributable
attributes :length
end
end
expectation.to raise_error(ArgumentError, 'Method already defined!')
end
end
end
require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Configurable do
let(:entry) { Class.new }
before do
entry.include(described_class)
let(:entry) do
Class.new(Gitlab::Ci::Config::Entry::Node) do
include Gitlab::Ci::Config::Entry::Configurable
end
end
describe 'validations' do
let(:validator) { entry.validator.new(instance) }
before do
entry.class_eval do
attr_reader :config
context 'when entry is a hash' do
let(:instance) { entry.new(key: 'value') }
def initialize(config)
@config = config
it 'correctly validates an instance' do
expect(instance).to be_valid
end
end
validator.validate
end
context 'when entry validator is invalid' do
context 'when entry is not a hash' do
let(:instance) { entry.new('ls') }
it 'returns invalid validator' do
expect(validator).to be_invalid
end
end
context 'when entry instance is valid' do
let(:instance) { entry.new(key: 'value') }
it 'returns valid validator' do
expect(validator).to be_valid
it 'invalidates the instance' do
expect(instance).not_to be_valid
end
end
end
......
require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Policy do
let(:entry) { described_class.new(config) }
context 'when using simplified policy' do
describe 'validations' do
context 'when entry config value is valid' do
context 'when config is a branch or tag name' do
let(:config) { %w[master feature/branch] }
describe '#valid?' do
it 'is valid' do
expect(entry).to be_valid
end
end
describe '#value' do
it 'returns key value' do
expect(entry.value).to eq config
end
end
end
context 'when config is a regexp' do
let(:config) { ['/^issue-.*$/'] }
describe '#valid?' do
it 'is valid' do
expect(entry).to be_valid
end
end
end
context 'when config is a special keyword' do
let(:config) { %w[tags triggers branches] }
describe '#valid?' do
it 'is valid' do
expect(entry).to be_valid
end
end
end
end
context 'when entry value is not valid' do
let(:config) { [1] }
describe '#errors' do
it 'saves errors' do
expect(entry.errors)
.to include /policy config should be an array of strings or regexps/
end
end
end
end
end
context 'when policy strategy does not match' do
let(:config) { 'string strategy' }
it 'returns information about errors' do
expect(entry.errors)
.to include /has to be either an array of conditions or a hash/
end
end
describe '.default' do
it 'does not have a default value' do
expect(described_class.default).to be_nil
end
end
end
require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Simplifiable do
describe '.strategy' do
let(:entry) do
Class.new(described_class) do
strategy :Something, if: -> { 'condition' }
strategy :DifferentOne, if: -> { 'condition' }
end
end
it 'defines entry strategies' do
expect(entry.strategies.size).to eq 2
expect(entry.strategies.map(&:name))
.to eq %i[Something DifferentOne]
end
end
describe 'setting strategy by a condition' do
let(:first) { double('first strategy') }
let(:second) { double('second strategy') }
let(:unknown) { double('unknown strategy') }
before do
stub_const("#{described_class.name}::Something", first)
stub_const("#{described_class.name}::DifferentOne", second)
stub_const("#{described_class.name}::UnknownStrategy", unknown)
end
context 'when first strategy should be used' do
let(:entry) do
Class.new(described_class) do
strategy :Something, if: -> (arg) { arg == 'something' }
strategy :DifferentOne, if: -> (*) { false }
end
end
it 'attemps to load a first strategy' do
expect(first).to receive(:new).with('something', anything)
entry.new('something')
end
end
context 'when second strategy should be used' do
let(:entry) do
Class.new(described_class) do
strategy :Something, if: -> (arg) { arg == 'something' }
strategy :DifferentOne, if: -> (arg) { arg == 'test' }
end
end
it 'attemps to load a second strategy' do
expect(second).to receive(:new).with('test', anything)
entry.new('test')
end
end
context 'when neither one is a valid strategy' do
let(:entry) do
Class.new(described_class) do
strategy :Something, if: -> (*) { false }
strategy :DifferentOne, if: -> (*) { false }
end
end
it 'instantiates an unknown strategy' do
expect(unknown).to receive(:new).with('test', anything)
entry.new('test')
end
end
end
context 'when a unknown strategy class is not defined' do
let(:entry) do
Class.new(described_class) do
strategy :String, if: -> (*) { true }
end
end
it 'raises an error when being initialized' do
expect { entry.new('something') }
.to raise_error ArgumentError, /UndefinedStrategy not available!/
end
end
end
require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Trigger do
let(:entry) { described_class.new(config) }
describe 'validations' do
context 'when entry config value is valid' do
context 'when config is a branch or tag name' do
let(:config) { %w[master feature/branch] }
describe '#valid?' do
it 'is valid' do
expect(entry).to be_valid
end
end
describe '#value' do
it 'returns key value' do
expect(entry.value).to eq config
end
end
end
context 'when config is a regexp' do
let(:config) { ['/^issue-.*$/'] }
describe '#valid?' do
it 'is valid' do
expect(entry).to be_valid
end
end
end
context 'when config is a special keyword' do
let(:config) { %w[tags triggers branches] }
describe '#valid?' do
it 'is valid' do
expect(entry).to be_valid
end
end
end
end
context 'when entry value is not valid' do
let(:config) { [1] }
describe '#errors' do
it 'saves errors' do
expect(entry.errors)
.to include 'trigger config should be an array of strings or regexps'
end
end
end
end
end
require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Validatable do
let(:entry) { Class.new }
before do
entry.include(described_class)
let(:entry) do
Class.new(Gitlab::Ci::Config::Entry::Node) do
include Gitlab::Ci::Config::Entry::Validatable
end
end
describe '.validator' do
......@@ -28,7 +28,7 @@ describe Gitlab::Ci::Config::Entry::Validatable do
end
context 'when validating entry instance' do
let(:entry_instance) { entry.new }
let(:entry_instance) { entry.new('something') }
context 'when attribute is valid' do
before do
......
......@@ -48,7 +48,7 @@ describe Gitlab::Ci::Config::Entry::Validator do
validator_instance.validate
expect(validator_instance.messages)
.to include "node test attribute can't be blank"
.to include /test attribute can't be blank/
end
end
end
......
......@@ -186,22 +186,48 @@ describe Gitlab::Shell do
end
end
describe '#fetch_remote' do
shared_examples 'fetch_remote' do |gitaly_on|
let(:project2) { create(:project, :repository) }
let(:repository) { project2.repository }
def fetch_remote(ssh_auth = nil)
gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage', ssh_auth: ssh_auth)
gitlab_shell.fetch_remote(repository.raw_repository, 'new/storage', ssh_auth: ssh_auth)
end
def expect_popen(vars = {})
def expect_popen(fail = false, vars = {})
popen_args = [
projects_path,
'fetch-remote',
'current/storage',
'project/path.git',
TestEnv.repos_path,
repository.relative_path,
'new/storage',
Gitlab.config.gitlab_shell.git_timeout.to_s
]
expect(Gitlab::Popen).to receive(:popen).with(popen_args, nil, popen_vars.merge(vars))
return_value = fail ? ["error", 1] : [nil, 0]
expect(Gitlab::Popen).to receive(:popen).with(popen_args, nil, popen_vars.merge(vars)).and_return(return_value)
end
def expect_gitaly_call(fail, vars = {})
receive_fetch_remote =
if fail
receive(:fetch_remote).and_raise(GRPC::NotFound)
else
receive(:fetch_remote).and_return(true)
end
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive_fetch_remote
end
if gitaly_on
def expect_call(fail, vars = {})
expect_gitaly_call(fail, vars)
end
else
def expect_call(fail, vars = {})
expect_popen(fail, vars)
end
end
def build_ssh_auth(opts = {})
......@@ -216,20 +242,20 @@ describe Gitlab::Shell do
end
it 'returns true when the command succeeds' do
expect_popen.and_return([nil, 0])
expect_call(false)
expect(fetch_remote).to be_truthy
end
it 'raises an exception when the command fails' do
expect_popen.and_return(["error", 1])
expect_call(true)
expect { fetch_remote }.to raise_error(Gitlab::Shell::Error, "error")
expect { fetch_remote }.to raise_error(Gitlab::Shell::Error)
end
context 'SSH auth' do
it 'passes the SSH key if specified' do
expect_popen('GITLAB_SHELL_SSH_KEY' => 'foo').and_return([nil, 0])
expect_call(false, 'GITLAB_SHELL_SSH_KEY' => 'foo')
ssh_auth = build_ssh_auth(ssh_key_auth?: true, ssh_private_key: 'foo')
......@@ -237,7 +263,7 @@ describe Gitlab::Shell do
end
it 'does not pass an empty SSH key' do
expect_popen.and_return([nil, 0])
expect_call(false)
ssh_auth = build_ssh_auth(ssh_key_auth: true, ssh_private_key: '')
......@@ -245,7 +271,7 @@ describe Gitlab::Shell do
end
it 'does not pass the key unless SSH key auth is to be used' do
expect_popen.and_return([nil, 0])
expect_call(false)
ssh_auth = build_ssh_auth(ssh_key_auth: false, ssh_private_key: 'foo')
......@@ -253,7 +279,7 @@ describe Gitlab::Shell do
end
it 'passes the known_hosts data if specified' do
expect_popen('GITLAB_SHELL_KNOWN_HOSTS' => 'foo').and_return([nil, 0])
expect_call(false, 'GITLAB_SHELL_KNOWN_HOSTS' => 'foo')
ssh_auth = build_ssh_auth(ssh_known_hosts: 'foo')
......@@ -261,7 +287,7 @@ describe Gitlab::Shell do
end
it 'does not pass empty known_hosts data' do
expect_popen.and_return([nil, 0])
expect_call(false)
ssh_auth = build_ssh_auth(ssh_known_hosts: '')
......@@ -269,7 +295,7 @@ describe Gitlab::Shell do
end
it 'does not pass known_hosts data unless SSH is to be used' do
expect_popen(popen_vars).and_return([nil, 0])
expect_call(false, popen_vars)
ssh_auth = build_ssh_auth(ssh_import?: false, ssh_known_hosts: 'foo')
......@@ -278,6 +304,14 @@ describe Gitlab::Shell do
end
end
describe '#fetch_remote local', skip_gitaly_mock: true do
it_should_behave_like 'fetch_remote', false
end
describe '#fetch_remote gitaly' do
it_should_behave_like 'fetch_remote', true
end
describe '#import_repository' do
it 'returns true when the command succeeds' do
expect(Gitlab::Popen).to receive(:popen)
......
......@@ -1257,8 +1257,12 @@ describe Ci::Build do
context 'when build has user' do
let(:user_variables) do
[{ key: 'GITLAB_USER_ID', value: user.id.to_s, public: true },
{ key: 'GITLAB_USER_EMAIL', value: user.email, public: true }]
[
{ key: 'GITLAB_USER_ID', value: user.id.to_s, public: true },
{ key: 'GITLAB_USER_EMAIL', value: user.email, public: true },
{ key: 'GITLAB_USER_LOGIN', value: user.username, public: true },
{ key: 'GITLAB_USER_NAME', value: user.name, public: true }
]
end
before do
......
......@@ -769,4 +769,22 @@ describe Issue do
expect(described_class.public_only).to eq([public_issue])
end
end
describe '#update_project_counter_caches?' do
it 'returns true when the state changes' do
subject.state = 'closed'
expect(subject.update_project_counter_caches?).to eq(true)
end
it 'returns true when the confidential flag changes' do
subject.confidential = true
expect(subject.update_project_counter_caches?).to eq(true)
end
it 'returns false when the state or confidential flag did not change' do
expect(subject.update_project_counter_caches?).to eq(false)
end
end
end
......@@ -1700,4 +1700,16 @@ describe MergeRequest do
.to change { project.open_merge_requests_count }.from(1).to(0)
end
end
describe '#update_project_counter_caches?' do
it 'returns true when the state changes' do
subject.state = 'closed'
expect(subject.update_project_counter_caches?).to eq(true)
end
it 'returns false when the state did not change' do
expect(subject.update_project_counter_caches?).to eq(false)
end
end
end
......@@ -2234,6 +2234,28 @@ describe Project do
end
end
describe '#pages_available?' do
let(:project) { create(:project, group: group) }
subject { project.pages_available? }
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
end
context 'when the project is in a top level namespace' do
let(:group) { create(:group) }
it { is_expected.to be(true) }
end
context 'when the project is in a subgroup' do
let(:group) { create(:group, :nested) }
it { is_expected.to be(false) }
end
end
describe '#remove_private_deploy_keys' do
let!(:project) { create(:project) }
......
......@@ -125,6 +125,15 @@ describe API::Files do
expect(response).to have_http_status(200)
end
it 'returns raw file info for files with dots' do
url = route('.gitignore') + "/raw"
expect(Gitlab::Workhorse).to receive(:send_git_blob)
get api(url, current_user), params
expect(response).to have_http_status(200)
end
it 'returns file by commit sha' do
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
......
......@@ -3,9 +3,9 @@ require 'spec_helper'
describe SystemNoteService do
include Gitlab::Routing
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let(:author) { create(:user) }
set(:group) { create(:group) }
set(:project) { create(:project, :repository, group: group) }
set(:author) { create(:user) }
let(:noteable) { create(:issue, project: project) }
let(:issue) { noteable }
......@@ -29,8 +29,7 @@ describe SystemNoteService do
describe '.add_commits' do
subject { described_class.add_commits(noteable, project, author, new_commits, old_commits, oldrev) }
let(:project) { create(:project, :repository) }
let(:noteable) { create(:merge_request, source_project: project) }
let(:noteable) { create(:merge_request, source_project: project, target_project: project) }
let(:new_commits) { noteable.commits }
let(:old_commits) { [] }
let(:oldrev) { nil }
......@@ -185,7 +184,7 @@ describe SystemNoteService do
describe '.change_label' do
subject { described_class.change_label(noteable, project, author, added, removed) }
let(:labels) { create_list(:label, 2) }
let(:labels) { create_list(:label, 2, project: project) }
let(:added) { [] }
let(:removed) { [] }
......@@ -294,7 +293,6 @@ describe SystemNoteService do
end
describe '.merge_when_pipeline_succeeds' do
let(:project) { create(:project, :repository) }
let(:pipeline) { build(:ci_pipeline_without_jobs )}
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
......@@ -312,7 +310,6 @@ describe SystemNoteService do
end
describe '.cancel_merge_when_pipeline_succeeds' do
let(:project) { create(:project, :repository) }
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
......@@ -390,7 +387,6 @@ describe SystemNoteService do
describe '.change_branch' do
subject { described_class.change_branch(noteable, project, author, 'target', old_branch, new_branch) }
let(:project) { create(:project, :repository) }
let(:old_branch) { 'old_branch'}
let(:new_branch) { 'new_branch'}
......@@ -408,8 +404,6 @@ describe SystemNoteService do
describe '.change_branch_presence' do
subject { described_class.change_branch_presence(noteable, project, author, :source, 'feature', :delete) }
let(:project) { create(:project, :repository) }
it_behaves_like 'a system note' do
let(:action) { 'branch' }
end
......@@ -424,8 +418,6 @@ describe SystemNoteService do
describe '.new_issue_branch' do
subject { described_class.new_issue_branch(noteable, project, author, "1-mepmep") }
let(:project) { create(:project, :repository) }
it_behaves_like 'a system note' do
let(:action) { 'branch' }
end
......@@ -491,7 +483,6 @@ describe SystemNoteService do
context 'within the same project' do
context 'from Commit' do
let(:project) { create(:project, :repository) }
let(:mentioner) { project.repository.commit }
it 'references the mentioning commit' do
......@@ -533,7 +524,6 @@ describe SystemNoteService do
end
context 'when mentioner is a MergeRequest' do
let(:project) { create(:project, :repository) }
let(:mentioner) { create(:merge_request, :simple, source_project: project) }
let(:noteable) { project.commit }
......@@ -561,7 +551,6 @@ describe SystemNoteService do
end
describe '.cross_reference_exists?' do
let(:project) { create(:project, :repository) }
let(:commit0) { project.commit }
let(:commit1) { project.commit('HEAD~2') }
......@@ -899,9 +888,8 @@ describe SystemNoteService do
end
describe '.discussion_continued_in_issue' do
let(:discussion) { create(:diff_note_on_merge_request).to_discussion }
let(:discussion) { create(:diff_note_on_merge_request, project: project).to_discussion }
let(:merge_request) { discussion.noteable }
let(:project) { merge_request.source_project }
let(:issue) { create(:issue, project: project) }
def reloaded_merge_request
......@@ -1023,7 +1011,6 @@ describe SystemNoteService do
end
describe '.add_merge_request_wip_from_commit' do
let(:project) { create(:project, :repository) }
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
......@@ -1078,9 +1065,8 @@ describe SystemNoteService do
end
describe '.diff_discussion_outdated' do
let(:discussion) { create(:diff_note_on_merge_request).to_discussion }
let(:discussion) { create(:diff_note_on_merge_request, project: project).to_discussion }
let(:merge_request) { discussion.noteable }
let(:project) { merge_request.source_project }
let(:change_position) { discussion.position }
def reloaded_merge_request
......
# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
module StubENV
include Gitlab::CurrentSettings
def stub_env(key_or_hash, value = nil)
init_stub unless env_stubbed?
if key_or_hash.is_a? Hash
......
......@@ -9,6 +9,7 @@ describe 'admin/dashboard/index.html.haml' do
assign(:groups, create_list(:group, 1))
allow(view).to receive(:admin?).and_return(true)
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
it "shows version of GitLab Workhorse" do
......
......@@ -5,6 +5,7 @@ describe 'devise/shared/_signin_box' do
before do
stub_devise
assign(:ldap_servers, [])
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
it 'is shown when Crowd is enabled' do
......
......@@ -37,5 +37,6 @@ describe 'help/index' do
def stub_helpers
allow(view).to receive(:markdown).and_return('')
allow(view).to receive(:version_status_badge).and_return('')
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
end
require 'spec_helper'
describe 'layouts/_head' do
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
it 'escapes HTML-safe strings in page_title' do
stub_helper_with_safe_string(:page_title)
......
require 'spec_helper'
describe 'projects/commits/_commit.html.haml' do
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
context 'with a singed commit' do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
......
......@@ -10,7 +10,9 @@ describe 'projects/edit' do
assign(:project, project)
allow(controller).to receive(:current_user).and_return(user)
allow(view).to receive_messages(current_user: user, can?: true)
allow(view).to receive_messages(current_user: user,
can?: true,
current_application_settings: Gitlab::CurrentSettings.current_application_settings)
end
context 'LFS enabled setting' do
......
......@@ -14,6 +14,7 @@ describe 'projects/merge_requests/creations/_new_submit.html.haml' do
allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:url_for).and_return('#')
allow(view).to receive(:current_user).and_return(merge_request.author)
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
context 'when there are pipelines for merge request but no pipeline for last commit' do
......
......@@ -25,7 +25,9 @@ describe 'projects/merge_requests/show.html.haml' do
assign(:notes, [])
assign(:pipelines, Ci::Pipeline.none)
allow(view).to receive_messages(current_user: user, can?: true)
allow(view).to receive_messages(current_user: user,
can?: true,
current_application_settings: Gitlab::CurrentSettings.current_application_settings)
end
context 'when the merge request is closed' do
......
......@@ -12,6 +12,7 @@ describe 'projects/tree/show' do
allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:can_collaborate_with_project?).and_return(true)
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
context 'for branch names ending on .json' do
......
......@@ -3,6 +3,10 @@ require 'spec_helper'
describe 'shared/projects/_project.html.haml' do
let(:project) { create(:project) }
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
it 'should render creator avatar if project has a creator' do
render 'shared/projects/project', use_creator_avatar: true, project: project
......
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