Commit 766a3e85 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'ce-to-ee-2018-03-28' into 'master'

CE upstream - 2018-03-28 12:06 UTC

Closes #5461

See merge request gitlab-org/gitlab-ee!5145
parents 60e7308e bdc72a02
...@@ -51,6 +51,7 @@ eslint-report.html ...@@ -51,6 +51,7 @@ eslint-report.html
/db/data.yml /db/data.yml
/doc/code/* /doc/code/*
/dump.rdb /dump.rdb
/jsconfig.json
/log/*.log* /log/*.log*
/node_modules/ /node_modules/
/nohup.out /nohup.out
......
...@@ -156,8 +156,8 @@ gem 'rdoc', '~> 4.2' ...@@ -156,8 +156,8 @@ gem 'rdoc', '~> 4.2'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.6'
gem 'asciidoctor-plantuml', '0.0.7' gem 'asciidoctor-plantuml', '0.0.8'
gem 'rouge', '~> 2.0' gem 'rouge', '~> 2.0'
gem 'truncato', '~> 0.7.9' gem 'truncato', '~> 0.7.9'
gem 'bootstrap_form', '~> 2.7.0' gem 'bootstrap_form', '~> 2.7.0'
......
...@@ -56,8 +56,8 @@ GEM ...@@ -56,8 +56,8 @@ GEM
faraday_middleware (~> 0.9) faraday_middleware (~> 0.9)
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.3) asciidoctor (1.5.6.2)
asciidoctor-plantuml (0.0.7) asciidoctor-plantuml (0.0.8)
asciidoctor (~> 1.5) asciidoctor (~> 1.5)
asset_sync (2.2.0) asset_sync (2.2.0)
activemodel (>= 4.1.0) activemodel (>= 4.1.0)
...@@ -1028,8 +1028,8 @@ DEPENDENCIES ...@@ -1028,8 +1028,8 @@ DEPENDENCIES
akismet (~> 2.0) akismet (~> 2.0)
allocations (~> 1.0) allocations (~> 1.0)
asana (~> 0.6.0) asana (~> 0.6.0)
asciidoctor (~> 1.5.2) asciidoctor (~> 1.5.6)
asciidoctor-plantuml (= 0.0.7) asciidoctor-plantuml (= 0.0.8)
asset_sync (~> 2.2.0) asset_sync (~> 2.2.0)
attr_encrypted (~> 3.0.0) attr_encrypted (~> 3.0.0)
awesome_print (~> 1.2.0) awesome_print (~> 1.2.0)
......
...@@ -757,7 +757,7 @@ GitLabDropdown = (function() { ...@@ -757,7 +757,7 @@ GitLabDropdown = (function() {
} }
if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) { if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) {
return; return [selectedObject];
} }
if (el.hasClass(ACTIVE_CLASS) && value !== 0) { if (el.hasClass(ACTIVE_CLASS) && value !== 0) {
......
...@@ -6,6 +6,7 @@ export const defaultEditorOptions = { ...@@ -6,6 +6,7 @@ export const defaultEditorOptions = {
minimap: { minimap: {
enabled: false, enabled: false,
}, },
wordWrap: 'bounded',
}; };
export default [ export default [
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import Flash from '../../flash'; import Flash from '../../flash';
import MonitoringService from '../services/monitoring_service'; import MonitoringService from '../services/monitoring_service';
import GraphGroup from './graph_group.vue'; import GraphGroup from './graph_group.vue';
import Graph from './graph.vue'; import Graph from './graph.vue';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import MonitoringStore from '../stores/monitoring_store'; import MonitoringStore from '../stores/monitoring_store';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
export default { export default {
components: { components: {
Graph, Graph,
GraphGroup, GraphGroup,
EmptyState, EmptyState,
}, },
props: { props: {
hasMetrics: { hasMetrics: {
type: Boolean, type: Boolean,
...@@ -82,7 +81,6 @@ ...@@ -82,7 +81,6 @@
required: true, required: true,
}, },
}, },
data() { data() {
return { return {
store: new MonitoringStore(), store: new MonitoringStore(),
...@@ -94,7 +92,6 @@ ...@@ -94,7 +92,6 @@
resizeThrottled: {}, resizeThrottled: {},
}; };
}, },
created() { created() {
this.service = new MonitoringService({ this.service = new MonitoringService({
metricsEndpoint: this.metricsEndpoint, metricsEndpoint: this.metricsEndpoint,
...@@ -103,13 +100,11 @@ ...@@ -103,13 +100,11 @@
eventHub.$on('toggleAspectRatio', this.toggleAspectRatio); eventHub.$on('toggleAspectRatio', this.toggleAspectRatio);
eventHub.$on('hoverChanged', this.hoverChanged); eventHub.$on('hoverChanged', this.hoverChanged);
}, },
beforeDestroy() { beforeDestroy() {
eventHub.$off('toggleAspectRatio', this.toggleAspectRatio); eventHub.$off('toggleAspectRatio', this.toggleAspectRatio);
eventHub.$off('hoverChanged', this.hoverChanged); eventHub.$off('hoverChanged', this.hoverChanged);
window.removeEventListener('resize', this.resizeThrottled, false); window.removeEventListener('resize', this.resizeThrottled, false);
}, },
mounted() { mounted() {
this.resizeThrottled = _.throttle(this.resize, 600); this.resizeThrottled = _.throttle(this.resize, 600);
if (!this.hasMetrics) { if (!this.hasMetrics) {
...@@ -119,14 +114,13 @@ ...@@ -119,14 +114,13 @@
window.addEventListener('resize', this.resizeThrottled, false); window.addEventListener('resize', this.resizeThrottled, false);
} }
}, },
methods: { methods: {
getGraphsData() { getGraphsData() {
this.state = 'loading'; this.state = 'loading';
Promise.all([ Promise.all([
this.service.getGraphsData() this.service.getGraphsData().then(data => this.store.storeMetrics(data)),
.then(data => this.store.storeMetrics(data)), this.service
this.service.getDeploymentData() .getDeploymentData()
.then(data => this.store.storeDeploymentData(data)) .then(data => this.store.storeDeploymentData(data))
.catch(() => new Flash('Error getting deployment information.')), .catch(() => new Flash('Error getting deployment information.')),
]) ])
...@@ -137,13 +131,13 @@ ...@@ -137,13 +131,13 @@
} }
this.showEmptyState = false; this.showEmptyState = false;
}) })
.catch(() => { this.state = 'unableToConnect'; }); .catch(() => {
this.state = 'unableToConnect';
});
}, },
resize() { resize() {
this.updateAspectRatio = true; this.updateAspectRatio = true;
}, },
toggleAspectRatio() { toggleAspectRatio() {
this.updatedAspectRatios = this.updatedAspectRatios += 1; this.updatedAspectRatios = this.updatedAspectRatios += 1;
if (this.store.getMetricsCount() === this.updatedAspectRatios) { if (this.store.getMetricsCount() === this.updatedAspectRatios) {
...@@ -151,12 +145,11 @@ ...@@ -151,12 +145,11 @@
this.updatedAspectRatios = 0; this.updatedAspectRatios = 0;
} }
}, },
hoverChanged(data) { hoverChanged(data) {
this.hoverData = data; this.hoverData = data;
}, },
}, },
}; };
</script> </script>
<template> <template>
......
<script> <script>
export default { export default {
props: { props: {
documentationPath: { documentationPath: {
type: String, type: String,
...@@ -79,13 +79,12 @@ ...@@ -79,13 +79,12 @@
currentState() { currentState() {
return this.states[this.selectedState]; return this.states[this.selectedState];
}, },
showButtonDescription() { showButtonDescription() {
if (this.selectedState === 'unableToConnect') return true; if (this.selectedState === 'unableToConnect') return true;
return false; return false;
}, },
}, },
}; };
</script> </script>
<template> <template>
......
<script> <script>
import { scaleLinear, scaleTime } from 'd3-scale'; import { scaleLinear, scaleTime } from 'd3-scale';
import { axisLeft, axisBottom } from 'd3-axis'; import { axisLeft, axisBottom } from 'd3-axis';
import { max, extent } from 'd3-array'; import { max, extent } from 'd3-array';
import { select } from 'd3-selection'; import { select } from 'd3-selection';
import GraphLegend from './graph/legend.vue'; import GraphLegend from './graph/legend.vue';
import GraphFlag from './graph/flag.vue'; import GraphFlag from './graph/flag.vue';
import GraphDeployment from './graph/deployment.vue'; import GraphDeployment from './graph/deployment.vue';
import GraphPath from './graph/path.vue'; import GraphPath from './graph/path.vue';
import MonitoringMixin from '../mixins/monitoring_mixins'; import MonitoringMixin from '../mixins/monitoring_mixins';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import measurements from '../utils/measurements'; import measurements from '../utils/measurements';
import { bisectDate, timeScaleFormat } from '../utils/date_time_formatters'; import { bisectDate, timeScaleFormat } from '../utils/date_time_formatters';
import createTimeSeries from '../utils/multiple_time_series'; import createTimeSeries from '../utils/multiple_time_series';
import bp from '../../breakpoints'; import bp from '../../breakpoints';
const d3 = { scaleLinear, scaleTime, axisLeft, axisBottom, max, extent, select }; const d3 = { scaleLinear, scaleTime, axisLeft, axisBottom, max, extent, select };
export default { export default {
components: { components: {
GraphLegend, GraphLegend,
GraphFlag, GraphFlag,
GraphDeployment, GraphDeployment,
GraphPath, GraphPath,
}, },
mixins: [MonitoringMixin], mixins: [MonitoringMixin],
props: { props: {
graphData: { graphData: {
type: Object, type: Object,
...@@ -63,7 +61,6 @@ ...@@ -63,7 +61,6 @@
default: false, default: false,
}, },
}, },
data() { data() {
return { return {
baseGraphHeight: 450, baseGraphHeight: 450,
...@@ -90,31 +87,25 @@ ...@@ -90,31 +87,25 @@
realPixelRatio: 1, realPixelRatio: 1,
}; };
}, },
computed: { computed: {
outerViewBox() { outerViewBox() {
return `0 0 ${this.baseGraphWidth} ${this.baseGraphHeight}`; return `0 0 ${this.baseGraphWidth} ${this.baseGraphHeight}`;
}, },
innerViewBox() { innerViewBox() {
return `0 0 ${this.baseGraphWidth - 150} ${this.baseGraphHeight}`; return `0 0 ${this.baseGraphWidth - 150} ${this.baseGraphHeight}`;
}, },
axisTransform() { axisTransform() {
return `translate(70, ${this.graphHeight - 100})`; return `translate(70, ${this.graphHeight - 100})`;
}, },
paddingBottomRootSvg() { paddingBottomRootSvg() {
return { return {
paddingBottom: `${(Math.ceil(this.baseGraphHeight * 100) / this.baseGraphWidth) || 0}%`, paddingBottom: `${Math.ceil(this.baseGraphHeight * 100) / this.baseGraphWidth || 0}%`,
}; };
}, },
deploymentFlagData() { deploymentFlagData() {
return this.reducedDeploymentData.find(deployment => deployment.showDeploymentFlag); return this.reducedDeploymentData.find(deployment => deployment.showDeploymentFlag);
}, },
}, },
watch: { watch: {
updateAspectRatio() { updateAspectRatio() {
if (this.updateAspectRatio) { if (this.updateAspectRatio) {
...@@ -125,16 +116,13 @@ ...@@ -125,16 +116,13 @@
eventHub.$emit('toggleAspectRatio'); eventHub.$emit('toggleAspectRatio');
} }
}, },
hoverData() { hoverData() {
this.positionFlag(); this.positionFlag();
}, },
}, },
mounted() { mounted() {
this.draw(); this.draw();
}, },
methods: { methods: {
draw() { draw() {
const breakpointSize = bp.getBreakpointSize(); const breakpointSize = bp.getBreakpointSize();
...@@ -148,19 +136,17 @@ ...@@ -148,19 +136,17 @@
this.unitOfDisplay = query.unit || ''; this.unitOfDisplay = query.unit || '';
this.yAxisLabel = this.graphData.y_label || 'Values'; this.yAxisLabel = this.graphData.y_label || 'Values';
this.legendTitle = query.label || 'Average'; this.legendTitle = query.label || 'Average';
this.graphWidth = this.$refs.baseSvg.clientWidth - this.graphWidth = this.$refs.baseSvg.clientWidth - this.margin.left - this.margin.right;
this.margin.left - this.margin.right;
this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom; this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom;
this.baseGraphHeight = this.graphHeight; this.baseGraphHeight = this.graphHeight;
this.baseGraphWidth = this.graphWidth; this.baseGraphWidth = this.graphWidth;
// pixel offsets inside the svg and outside are not 1:1 // pixel offsets inside the svg and outside are not 1:1
this.realPixelRatio = (this.$refs.baseSvg.clientWidth / this.baseGraphWidth); this.realPixelRatio = this.$refs.baseSvg.clientWidth / this.baseGraphWidth;
this.renderAxesPaths(); this.renderAxesPaths();
this.formatDeployments(); this.formatDeployments();
}, },
handleMouseOverGraph(e) { handleMouseOverGraph(e) {
let point = this.$refs.graphData.createSVGPoint(); let point = this.$refs.graphData.createSVGPoint();
point.x = e.clientX; point.x = e.clientX;
...@@ -174,7 +160,7 @@ ...@@ -174,7 +160,7 @@
const d1 = firstTimeSeries.values[overlayIndex]; const d1 = firstTimeSeries.values[overlayIndex];
if (d0 === undefined || d1 === undefined) return; if (d0 === undefined || d1 === undefined) return;
const evalTime = timeValueOverlay - d0[0] > d1[0] - timeValueOverlay; const evalTime = timeValueOverlay - d0[0] > d1[0] - timeValueOverlay;
const hoveredDataIndex = evalTime ? overlayIndex : (overlayIndex - 1); const hoveredDataIndex = evalTime ? overlayIndex : overlayIndex - 1;
const hoveredDate = firstTimeSeries.values[hoveredDataIndex].time; const hoveredDate = firstTimeSeries.values[hoveredDataIndex].time;
const currentDeployXPos = this.mouseOverDeployInfo(point.x); const currentDeployXPos = this.mouseOverDeployInfo(point.x);
...@@ -183,7 +169,6 @@ ...@@ -183,7 +169,6 @@
currentDeployXPos, currentDeployXPos,
}); });
}, },
renderAxesPaths() { renderAxesPaths() {
this.timeSeries = createTimeSeries( this.timeSeries = createTimeSeries(
this.graphData.queries, this.graphData.queries,
...@@ -198,39 +183,47 @@ ...@@ -198,39 +183,47 @@
this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 3) * 20; this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 3) * 20;
} }
const axisXScale = d3.scaleTime() const axisXScale = d3.scaleTime().range([0, this.graphWidth - 70]);
.range([0, this.graphWidth - 70]); const axisYScale = d3.scaleLinear().range([this.graphHeight - this.graphHeightOffset, 0]);
const axisYScale = d3.scaleLinear()
.range([this.graphHeight - this.graphHeightOffset, 0]);
const allValues = this.timeSeries.reduce((all, { values }) => all.concat(values), []); const allValues = this.timeSeries.reduce((all, { values }) => all.concat(values), []);
axisXScale.domain(d3.extent(allValues, d => d.time)); axisXScale.domain(d3.extent(allValues, d => d.time));
axisYScale.domain([0, d3.max(allValues.map(d => d.value))]); axisYScale.domain([0, d3.max(allValues.map(d => d.value))]);
const xAxis = d3.axisBottom() const xAxis = d3
.axisBottom()
.scale(axisXScale) .scale(axisXScale)
.ticks(this.graphWidth / 120) .ticks(this.graphWidth / 120)
.tickFormat(timeScaleFormat); .tickFormat(timeScaleFormat);
const yAxis = d3.axisLeft() const yAxis = d3
.axisLeft()
.scale(axisYScale) .scale(axisYScale)
.ticks(measurements.yTicks); .ticks(measurements.yTicks);
d3.select(this.$refs.baseSvg).select('.x-axis').call(xAxis); d3
.select(this.$refs.baseSvg)
.select('.x-axis')
.call(xAxis);
const width = this.graphWidth; const width = this.graphWidth;
d3.select(this.$refs.baseSvg).select('.y-axis').call(yAxis) d3
.select(this.$refs.baseSvg)
.select('.y-axis')
.call(yAxis)
.selectAll('.tick') .selectAll('.tick')
.each(function createTickLines(d, i) { .each(function createTickLines(d, i) {
if (i > 0) { if (i > 0) {
d3.select(this).select('line') d3
.select(this)
.select('line')
.attr('x2', width) .attr('x2', width)
.attr('class', 'axis-tick'); .attr('class', 'axis-tick');
} // Avoid adding the class to the first tick, to prevent coloring } // Avoid adding the class to the first tick, to prevent coloring
}); // This will select all of the ticks once they're rendered }); // This will select all of the ticks once they're rendered
}, },
}, },
}; };
</script> </script>
<template> <template>
......
<script> <script>
export default { export default {
props: { props: {
deploymentData: { deploymentData: {
type: Array, type: Array,
...@@ -14,19 +14,17 @@ ...@@ -14,19 +14,17 @@
required: true, required: true,
}, },
}, },
computed: { computed: {
calculatedHeight() { calculatedHeight() {
return this.graphHeight - this.graphHeightOffset; return this.graphHeight - this.graphHeightOffset;
}, },
}, },
methods: { methods: {
transformDeploymentGroup(deployment) { transformDeploymentGroup(deployment) {
return `translate(${Math.floor(deployment.xPos) - 5}, 20)`; return `translate(${Math.floor(deployment.xPos) - 5}, 20)`;
}, },
}, },
}; };
</script> </script>
<template> <template>
<g class="deploy-info"> <g class="deploy-info">
......
<script> <script>
import { dateFormat, timeFormat } from '../../utils/date_time_formatters'; import { dateFormat, timeFormat } from '../../utils/date_time_formatters';
import { formatRelevantDigits } from '../../../lib/utils/number_utils'; import { formatRelevantDigits } from '../../../lib/utils/number_utils';
import icon from '../../../vue_shared/components/icon.vue'; import icon from '../../../vue_shared/components/icon.vue';
export default { export default {
components: { components: {
icon, icon,
}, },
...@@ -54,24 +54,21 @@ ...@@ -54,24 +54,21 @@
required: true, required: true,
}, },
}, },
computed: { computed: {
formatTime() { formatTime() {
return this.deploymentFlagData ? return this.deploymentFlagData
timeFormat(this.deploymentFlagData.time) : ? timeFormat(this.deploymentFlagData.time)
timeFormat(this.currentData.time); : timeFormat(this.currentData.time);
}, },
formatDate() { formatDate() {
return this.deploymentFlagData ? return this.deploymentFlagData
dateFormat(this.deploymentFlagData.time) : ? dateFormat(this.deploymentFlagData.time)
dateFormat(this.currentData.time); : dateFormat(this.currentData.time);
}, },
cursorStyle() { cursorStyle() {
const xCoordinate = this.deploymentFlagData ? const xCoordinate = this.deploymentFlagData
this.deploymentFlagData.xPos : ? this.deploymentFlagData.xPos
this.currentXCoordinate; : this.currentXCoordinate;
const offsetTop = 20 * this.realPixelRatio; const offsetTop = 20 * this.realPixelRatio;
const offsetLeft = (70 + xCoordinate) * this.realPixelRatio; const offsetLeft = (70 + xCoordinate) * this.realPixelRatio;
...@@ -83,7 +80,6 @@ ...@@ -83,7 +80,6 @@
height: `${height}px`, height: `${height}px`,
}; };
}, },
flagOrientation() { flagOrientation() {
if (this.currentXCoordinate * this.realPixelRatio > 120) { if (this.currentXCoordinate * this.realPixelRatio > 120) {
return 'left'; return 'left';
...@@ -91,20 +87,17 @@ ...@@ -91,20 +87,17 @@
return 'right'; return 'right';
}, },
}, },
methods: { methods: {
seriesMetricValue(series) { seriesMetricValue(series) {
const index = this.deploymentFlagData ? const index = this.deploymentFlagData
this.deploymentFlagData.seriesIndex : ? this.deploymentFlagData.seriesIndex
this.currentDataIndex; : this.currentDataIndex;
const value = series.values[index] && const value = series.values[index] && series.values[index].value;
series.values[index].value;
if (isNaN(value)) { if (isNaN(value)) {
return '-'; return '-';
} }
return `${formatRelevantDigits(value)}${this.unitOfDisplay}`; return `${formatRelevantDigits(value)}${this.unitOfDisplay}`;
}, },
seriesMetricLabel(index, series) { seriesMetricLabel(index, series) {
if (this.timeSeries.length < 2) { if (this.timeSeries.length < 2) {
return this.legendTitle; return this.legendTitle;
...@@ -114,14 +107,13 @@ ...@@ -114,14 +107,13 @@
} }
return `series ${index + 1}`; return `series ${index + 1}`;
}, },
strokeDashArray(type) { strokeDashArray(type) {
if (type === 'dashed') return '6, 3'; if (type === 'dashed') return '6, 3';
if (type === 'dotted') return '3, 3'; if (type === 'dotted') return '3, 3';
return null; return null;
}, },
}, },
}; };
</script> </script>
<template> <template>
......
<script> <script>
import { formatRelevantDigits } from '../../../lib/utils/number_utils'; import { formatRelevantDigits } from '../../../lib/utils/number_utils';
export default { export default {
props: { props: {
graphWidth: { graphWidth: {
type: Number, type: Number,
...@@ -55,29 +55,24 @@ ...@@ -55,29 +55,24 @@
}, },
computed: { computed: {
textTransform() { textTransform() {
const yCoordinate = (((this.graphHeight - this.margin.top) const yCoordinate =
+ this.measurements.axisLabelLineOffset) / 2) || 0; (this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset) / 2 || 0;
return `translate(15, ${yCoordinate}) rotate(-90)`; return `translate(15, ${yCoordinate}) rotate(-90)`;
}, },
rectTransform() { rectTransform() {
const yCoordinate = (((this.graphHeight - this.margin.top) const yCoordinate =
+ this.measurements.axisLabelLineOffset) / 2) (this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset) / 2 +
+ (this.yLabelWidth / 2) || 0; this.yLabelWidth / 2 || 0;
return `translate(0, ${yCoordinate}) rotate(-90)`; return `translate(0, ${yCoordinate}) rotate(-90)`;
}, },
xPosition() { xPosition() {
return (((this.graphWidth + this.measurements.axisLabelLineOffset) / 2) return (this.graphWidth + this.measurements.axisLabelLineOffset) / 2 - this.margin.right || 0;
- this.margin.right) || 0;
}, },
yPosition() { yPosition() {
return ((this.graphHeight - this.margin.top) + this.measurements.axisLabelLineOffset) || 0; return this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset || 0;
}, },
}, },
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
...@@ -96,32 +91,29 @@ ...@@ -96,32 +91,29 @@
}, },
methods: { methods: {
translateLegendGroup(index) { translateLegendGroup(index) {
return `translate(0, ${12 * (index)})`; return `translate(0, ${12 * index})`;
}, },
formatMetricUsage(series) { formatMetricUsage(series) {
const value = series.values[this.currentDataIndex] && const value =
series.values[this.currentDataIndex].value; series.values[this.currentDataIndex] && series.values[this.currentDataIndex].value;
if (isNaN(value)) { if (isNaN(value)) {
return '-'; return '-';
} }
return `${formatRelevantDigits(value)} ${this.unitOfDisplay}`; return `${formatRelevantDigits(value)} ${this.unitOfDisplay}`;
}, },
createSeriesString(index, series) { createSeriesString(index, series) {
if (series.metricTag) { if (series.metricTag) {
return `${series.metricTag} ${this.formatMetricUsage(series)}`; return `${series.metricTag} ${this.formatMetricUsage(series)}`;
} }
return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(series)}`; return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(series)}`;
}, },
strokeDashArray(type) { strokeDashArray(type) {
if (type === 'dashed') return '6, 3'; if (type === 'dashed') return '6, 3';
if (type === 'dotted') return '3, 3'; if (type === 'dotted') return '3, 3';
return null; return null;
}, },
}, },
}; };
</script> </script>
<template> <template>
<g class="axis-label-container"> <g class="axis-label-container">
......
<script> <script>
export default { export default {
props: { props: {
generatedLinePath: { generatedLinePath: {
type: String, type: String,
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
return null; return null;
}, },
}, },
}; };
</script> </script>
<template> <template>
<g> <g>
......
<script> <script>
export default { export default {
props: { props: {
name: { name: {
type: String, type: String,
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
default: true, default: true,
}, },
}, },
}; };
</script> </script>
<template> <template>
......
<script>
import { parseSeconds, stringifyTime } from '../../../lib/utils/pretty_time'; import { parseSeconds, stringifyTime } from '../../../lib/utils/pretty_time';
export default { export default {
name: 'time-tracking-comparison-pane', name: 'TimeTrackingComparisonPane',
props: { props: {
timeSpent: { timeSpent: {
type: Number, type: Number,
...@@ -43,7 +44,10 @@ export default { ...@@ -43,7 +44,10 @@ export default {
return this.timeEstimate >= this.timeSpent ? 'within_estimate' : 'over_estimate'; return this.timeEstimate >= this.timeSpent ? 'within_estimate' : 'over_estimate';
}, },
}, },
template: ` };
</script>
<template>
<div class="time-tracking-comparison-pane"> <div class="time-tracking-comparison-pane">
<div <div
class="compare-meter" class="compare-meter"
...@@ -63,7 +67,8 @@ export default { ...@@ -63,7 +67,8 @@ export default {
<div <div
:style="{ width: timeRemainingPercent }" :style="{ width: timeRemainingPercent }"
class="meter-fill" class="meter-fill"
/> >
</div>
</div> </div>
<div class="compare-display-container"> <div class="compare-display-container">
<div class="compare-display pull-left"> <div class="compare-display pull-left">
...@@ -85,5 +90,4 @@ export default { ...@@ -85,5 +90,4 @@ export default {
</div> </div>
</div> </div>
</div> </div>
`, </template>
};
...@@ -4,7 +4,7 @@ import TimeTrackingCollapsedState from './collapsed_state.vue'; ...@@ -4,7 +4,7 @@ import TimeTrackingCollapsedState from './collapsed_state.vue';
import timeTrackingSpentOnlyPane from './spent_only_pane'; import timeTrackingSpentOnlyPane from './spent_only_pane';
import timeTrackingNoTrackingPane from './no_tracking_pane'; import timeTrackingNoTrackingPane from './no_tracking_pane';
import timeTrackingEstimateOnlyPane from './estimate_only_pane'; import timeTrackingEstimateOnlyPane from './estimate_only_pane';
import timeTrackingComparisonPane from './comparison_pane'; import TimeTrackingComparisonPane from './comparison_pane.vue';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
...@@ -15,7 +15,7 @@ export default { ...@@ -15,7 +15,7 @@ export default {
'time-tracking-estimate-only-pane': timeTrackingEstimateOnlyPane, 'time-tracking-estimate-only-pane': timeTrackingEstimateOnlyPane,
'time-tracking-spent-only-pane': timeTrackingSpentOnlyPane, 'time-tracking-spent-only-pane': timeTrackingSpentOnlyPane,
'time-tracking-no-tracking-pane': timeTrackingNoTrackingPane, 'time-tracking-no-tracking-pane': timeTrackingNoTrackingPane,
'time-tracking-comparison-pane': timeTrackingComparisonPane, TimeTrackingComparisonPane,
'time-tracking-help-state': timeTrackingHelpState, 'time-tracking-help-state': timeTrackingHelpState,
}, },
props: { props: {
......
...@@ -17,8 +17,8 @@ export default { ...@@ -17,8 +17,8 @@ export default {
/> />
<div class="media-body space-children"> <div class="media-body space-children">
<span class="bold"> <span class="bold">
The source branch HEAD has recently changed. {{ s__(`mrWidget|The source branch HEAD has recently changed.
Please reload the page and review the changes before merging. Please reload the page and review the changes before merging`) }}
</span> </span>
</div> </div>
</div> </div>
......
...@@ -36,7 +36,7 @@ class GemnasiumService < Service ...@@ -36,7 +36,7 @@ class GemnasiumService < Service
after: data[:after], after: data[:after],
token: token, token: token,
api_key: api_key, api_key: api_key,
repo: project.repository.path_to_repo repo: project.repository.path_to_repo # Gitaly: fixed by https://gitlab.com/gitlab-org/security-products/gemnasium-migration/issues/9
) )
end end
end end
...@@ -100,10 +100,6 @@ class Repository ...@@ -100,10 +100,6 @@ class Repository
"#<#{self.class.name}:#{@disk_path}>" "#<#{self.class.name}:#{@disk_path}>"
end end
def create_hooks
Gitlab::Git::Repository.create_hooks(path_to_repo, Gitlab.config.gitlab_shell.hooks_path)
end
def commit(ref = 'HEAD') def commit(ref = 'HEAD')
return nil unless exists? return nil unless exists?
return ref if ref.is_a?(::Commit) return ref if ref.is_a?(::Commit)
......
...@@ -34,7 +34,8 @@ class VerifyPagesDomainService < BaseService ...@@ -34,7 +34,8 @@ class VerifyPagesDomainService < BaseService
# Prevent any pre-existing grace period from being truncated # Prevent any pre-existing grace period from being truncated
reverify = [domain.enabled_until, VERIFICATION_PERIOD.from_now].compact.max reverify = [domain.enabled_until, VERIFICATION_PERIOD.from_now].compact.max
domain.update!(verified_at: Time.now, enabled_until: reverify) domain.assign_attributes(verified_at: Time.now, enabled_until: reverify)
domain.save!(validate: false)
if was_disabled if was_disabled
notify(:enabled) notify(:enabled)
...@@ -47,7 +48,9 @@ class VerifyPagesDomainService < BaseService ...@@ -47,7 +48,9 @@ class VerifyPagesDomainService < BaseService
def unverify_domain! def unverify_domain!
if domain.verified? if domain.verified?
domain.update!(verified_at: nil) domain.assign_attributes(verified_at: nil)
domain.save!(validate: false)
notify(:verification_failed) notify(:verification_failed)
end end
...@@ -55,7 +58,8 @@ class VerifyPagesDomainService < BaseService ...@@ -55,7 +58,8 @@ class VerifyPagesDomainService < BaseService
end end
def disable_domain! def disable_domain!
domain.update!(verified_at: nil, enabled_until: nil) domain.assign_attributes(verified_at: nil, enabled_until: nil)
domain.save!(validate: false)
notify(:disabled) notify(:disabled)
......
...@@ -62,12 +62,16 @@ ...@@ -62,12 +62,16 @@
= link_to @project.ssh_url_to_repo, project_path(@project) = link_to @project.ssh_url_to_repo, project_path(@project)
- if @project.repository.exists? - if @project.repository.exists?
%li %li
%span.light fs: %span.light Gitaly storage name:
%strong %strong
= @project.repository.path_to_repo = @project.repository.storage
%li
%span.light Gitaly relative path:
%strong
= @project.repository.relative_path
%li %li
%span.light Storage: %span.light Storage used:
%strong= storage_counter(@project.statistics.storage_size) %strong= storage_counter(@project.statistics.storage_size)
( (
= storage_counter(@project.statistics.repository_size) = storage_counter(@project.statistics.repository_size)
......
%h4.prepend-top-20 %h4.prepend-top-20
= s_('ClusterIntegration|Enter the details for your Kubernetes cluster') = s_('ClusterIntegration|Enter the details for your Kubernetes cluster')
%p %p
- link_to_help_page = link_to(s_('ClusterIntegration|documentation'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer') - link_to_help_page = link_to(s_('ClusterIntegration|documentation'), help_page_path('user/project/clusters/index', anchor: 'adding-an-existing-kubernetes-cluster'), target: '_blank', rel: 'noopener noreferrer')
= s_('ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes').html_safe % { link_to_help_page: link_to_help_page } = s_('ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes').html_safe % { link_to_help_page: link_to_help_page }
...@@ -8,3 +8,5 @@ ...@@ -8,3 +8,5 @@
= render 'form', { f: f } = render 'form', { f: f }
.form-actions .form-actions
= f.submit 'Create New Domain', class: "btn btn-save" = f.submit 'Create New Domain', class: "btn btn-save"
.pull-right
= link_to _('Cancel'), project_pages_path(@project), class: 'btn btn-cancel'
...@@ -28,16 +28,17 @@ class GitGarbageCollectWorker ...@@ -28,16 +28,17 @@ class GitGarbageCollectWorker
task = task.to_sym task = task.to_sym
cmd = command(task) cmd = command(task)
repo_path = project.repository.path_to_repo
description = "'#{cmd.join(' ')}' in #{repo_path}"
Gitlab::GitLogger.info(description)
gitaly_migrate(GITALY_MIGRATED_TASKS[task]) do |is_enabled| gitaly_migrate(GITALY_MIGRATED_TASKS[task]) do |is_enabled|
if is_enabled if is_enabled
gitaly_call(task, project.repository.raw_repository) gitaly_call(task, project.repository.raw_repository)
else else
repo_path = project.repository.path_to_repo
description = "'#{cmd.join(' ')}' in #{repo_path}"
Gitlab::GitLogger.info(description)
output, status = Gitlab::Popen.popen(cmd, repo_path) output, status = Gitlab::Popen.popen(cmd, repo_path)
Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero? Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero?
end end
end end
......
# Gitaly issue: https://gitlab.com/gitlab-org/gitaly/issues/1110
class RepositoryForkWorker class RepositoryForkWorker
include ApplicationWorker include ApplicationWorker
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
......
---
title: Adds cancel btn to new pages domain page
merge_request: 18026
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: Fixed bug in dropdown selector when selecting the same selection again
merge_request: 14631
author: bitsapien
type: fixed
---
title: Avoid validation errors when running the Pages domain verification service
merge_request: 17992
author:
type: fixed
---
title: Update asciidoctor-plantuml to 0.0.8
merge_request: 18022
author: Takuya Noguchi
type: performance
---
title: Fix listing commit branch/tags that contain special characters
merge_request:
author:
type: fixed
---
title: Move TimeTrackingComparisonPane vue component
merge_request: 17931
author: George Tsiolis
type: performance
---
title: Add i18n and update specs for ShaMismatch vue component
merge_request: 17870
author: George Tsiolis
type: performance
...@@ -63,7 +63,7 @@ module Gitlab ...@@ -63,7 +63,7 @@ module Gitlab
log " * Created #{project.name} (#{project_full_path})".color(:green) log " * Created #{project.name} (#{project_full_path})".color(:green)
project.write_repository_config project.write_repository_config
project.repository.create_hooks Gitlab::Git::Repository.create_hooks(project.repository.path_to_repo, Gitlab.config.gitlab_shell.hooks_path)
ProjectCacheWorker.perform_async(project.id) ProjectCacheWorker.perform_async(project.id)
else else
......
...@@ -6,6 +6,7 @@ module Gitlab ...@@ -6,6 +6,7 @@ module Gitlab
class Config class Config
prepend EE::Gitlab::Ci::Config prepend EE::Gitlab::Ci::Config
# EE would override this and utilize opts argument
def initialize(config, opts = {}) def initialize(config, opts = {})
@config = build_config(config, opts) @config = build_config(config, opts)
@global = Entry::Global.new(@config) @global = Entry::Global.new(@config)
......
...@@ -8,6 +8,7 @@ module Gitlab ...@@ -8,6 +8,7 @@ module Gitlab
class Repository class Repository
include Gitlab::Git::RepositoryMirroring include Gitlab::Git::RepositoryMirroring
include Gitlab::Git::Popen include Gitlab::Git::Popen
include Gitlab::EncodingHelper
ALLOWED_OBJECT_DIRECTORIES_VARIABLES = %w[ ALLOWED_OBJECT_DIRECTORIES_VARIABLES = %w[
GIT_OBJECT_DIRECTORY GIT_OBJECT_DIRECTORY
...@@ -1493,7 +1494,7 @@ module Gitlab ...@@ -1493,7 +1494,7 @@ module Gitlab
names.lines.each do |line| names.lines.each do |line|
next unless line.start_with?(refs_prefix) next unless line.start_with?(refs_prefix)
refs << line.rstrip[left_slice_count..-1] refs << encode_utf8(line.rstrip[left_slice_count..-1])
end end
refs refs
......
...@@ -21,22 +21,19 @@ module Gitlab ...@@ -21,22 +21,19 @@ module Gitlab
raise "Unsupported action: #{action}" unless ALLOWED_GIT_HTTP_ACTIONS.include?(action.to_s) raise "Unsupported action: #{action}" unless ALLOWED_GIT_HTTP_ACTIONS.include?(action.to_s)
project = repository.project project = repository.project
repo_path = repository.path_to_repo
params = { {
GL_ID: Gitlab::GlId.gl_id(user), GL_ID: Gitlab::GlId.gl_id(user),
GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki), GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki),
GL_USERNAME: user&.username, GL_USERNAME: user&.username,
RepoPath: repo_path, ShowAllRefs: show_all_refs,
ShowAllRefs: show_all_refs Repository: repository.gitaly_repository.to_h,
} RepoPath: 'ignored but not allowed to be empty in gitlab-workhorse',
server = { GitalyServer: {
address: Gitlab::GitalyClient.address(project.repository_storage), address: Gitlab::GitalyClient.address(project.repository_storage),
token: Gitlab::GitalyClient.token(project.repository_storage) token: Gitlab::GitalyClient.token(project.repository_storage)
} }
params[:Repository] = repository.gitaly_repository.to_h }
params[:GitalyServer] = server
params
end end
def artifact_upload_ok def artifact_upload_ok
......
%div %div
.dropdown.inline .dropdown.inline
%button#js-project-dropdown.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}} %button#js-project-dropdown.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
.dropdown-toggle-text
Projects Projects
%i.fa.fa-chevron-down.dropdown-toggle-caret.js-projects-dropdown-toggle %i.fa.fa-chevron-down.dropdown-toggle-caret.js-projects-dropdown-toggle
.dropdown-menu.dropdown-select.dropdown-menu-selectable .dropdown-menu.dropdown-select.dropdown-menu-selectable
......
...@@ -256,4 +256,29 @@ describe('glDropdown', function describeDropdown() { ...@@ -256,4 +256,29 @@ describe('glDropdown', function describeDropdown() {
}); });
}); });
}); });
it('should keep selected item after selecting a second time', () => {
const options = {
isSelectable(item, $el) {
return !$el.hasClass('is-active');
},
toggleLabel(item) {
return item && item.id;
},
};
initDropDown.call(this, false, false, options);
const $item = $(`${ITEM_SELECTOR}:first() a`, this.$dropdownMenuElement);
// select item the first time
this.dropdownButtonElement.click();
$item.click();
expect($item).toHaveClass('is-active');
// select item the second time
this.dropdownButtonElement.click();
$item.click();
expect($item).toHaveClass('is-active');
expect($('.dropdown-toggle-text')).toHaveText(this.projectsData[0].id.toString());
});
}); });
export default function removeBreakLine (data) {
return data.replace(/\r?\n|\r/g, ' ');
}
...@@ -64,9 +64,7 @@ describe('Multi-file editor library', () => { ...@@ -64,9 +64,7 @@ describe('Multi-file editor library', () => {
instance.createDiffInstance(holder); instance.createDiffInstance(holder);
expect(instance.monaco.editor.createDiffEditor).toHaveBeenCalledWith( expect(instance.monaco.editor.createDiffEditor).toHaveBeenCalledWith(holder, {
holder,
{
model: null, model: null,
contextmenu: true, contextmenu: true,
minimap: { minimap: {
...@@ -78,8 +76,8 @@ describe('Multi-file editor library', () => { ...@@ -78,8 +76,8 @@ describe('Multi-file editor library', () => {
occurrencesHighlight: false, occurrencesHighlight: false,
renderLineHighlight: 'none', renderLineHighlight: 'none',
hideCursorInOverviewRuler: true, hideCursorInOverviewRuler: true,
}, wordWrap: 'bounded',
); });
}); });
}); });
...@@ -117,9 +115,7 @@ describe('Multi-file editor library', () => { ...@@ -117,9 +115,7 @@ describe('Multi-file editor library', () => {
}); });
it('sets original & modified when diff editor', () => { it('sets original & modified when diff editor', () => {
spyOn(instance.instance, 'getEditorType').and.returnValue( spyOn(instance.instance, 'getEditorType').and.returnValue('vs.editor.IDiffEditor');
'vs.editor.IDiffEditor',
);
spyOn(instance.instance, 'setModel'); spyOn(instance.instance, 'setModel');
instance.attachModel(model); instance.attachModel(model);
...@@ -135,9 +131,7 @@ describe('Multi-file editor library', () => { ...@@ -135,9 +131,7 @@ describe('Multi-file editor library', () => {
instance.attachModel(model); instance.attachModel(model);
expect(instance.dirtyDiffController.attachModel).toHaveBeenCalledWith( expect(instance.dirtyDiffController.attachModel).toHaveBeenCalledWith(model);
model,
);
}); });
it('re-decorates with the dirty diff controller', () => { it('re-decorates with the dirty diff controller', () => {
...@@ -145,9 +139,7 @@ describe('Multi-file editor library', () => { ...@@ -145,9 +139,7 @@ describe('Multi-file editor library', () => {
instance.attachModel(model); instance.attachModel(model);
expect(instance.dirtyDiffController.reDecorate).toHaveBeenCalledWith( expect(instance.dirtyDiffController.reDecorate).toHaveBeenCalledWith(model);
model,
);
}); });
}); });
......
import Vue from 'vue'; import Vue from 'vue';
import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts.vue'; import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import removeBreakLine from 'spec/helpers/vue_component_helper';
describe('MRWidgetConflicts', () => { describe('MRWidgetConflicts', () => {
let Component; let Component;
...@@ -78,8 +79,9 @@ describe('MRWidgetConflicts', () => { ...@@ -78,8 +79,9 @@ describe('MRWidgetConflicts', () => {
}); });
it('should tell you to rebase locally', () => { it('should tell you to rebase locally', () => {
expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('Fast-forward merge is not possible.'); expect(
expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('To merge this request, first rebase locally'); removeBreakLine(vm.$el.textContent).trim(),
).toContain('Fast-forward merge is not possible. To merge this request, first rebase locally.');
}); });
}); });
}); });
import Vue from 'vue'; import Vue from 'vue';
import pipelineBlockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue'; import pipelineBlockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import removeBreakLine from 'spec/helpers/vue_component_helper';
describe('MRWidgetPipelineBlocked', () => { describe('MRWidgetPipelineBlocked', () => {
let vm; let vm;
...@@ -18,6 +19,8 @@ describe('MRWidgetPipelineBlocked', () => { ...@@ -18,6 +19,8 @@ describe('MRWidgetPipelineBlocked', () => {
}); });
it('renders information text', () => { it('renders information text', () => {
expect(vm.$el.textContent.trim().replace(/[\r\n]+/g, ' ')).toContain('Pipeline blocked. The pipeline for this merge request requires a manual action to proceed'); expect(
removeBreakLine(vm.$el.textContent).trim(),
).toContain('Pipeline blocked. The pipeline for this merge request requires a manual action to proceed');
}); });
}); });
import Vue from 'vue'; import Vue from 'vue';
import ShaMismatch from '~/vue_merge_request_widget/components/states/sha_mismatch.vue'; import ShaMismatch from '~/vue_merge_request_widget/components/states/sha_mismatch.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import removeBreakLine from 'spec/helpers/vue_component_helper';
describe('ShaMismatch', () => { describe('ShaMismatch', () => {
describe('template', () => { let vm;
beforeEach(() => {
const Component = Vue.extend(ShaMismatch); const Component = Vue.extend(ShaMismatch);
const vm = new Component({ vm = mountComponent(Component);
el: document.createElement('div'),
}); });
it('should have correct elements', () => {
expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy(); afterEach(() => {
expect(vm.$el.querySelector('button').getAttribute('disabled')).toBeTruthy(); vm.$destroy();
expect(vm.$el.innerText).toContain('The source branch HEAD has recently changed.');
expect(vm.$el.innerText).toContain('Please reload the page and review the changes before merging.');
}); });
it('should render information message', () => {
expect(vm.$el.querySelector('button').disabled).toEqual(true);
expect(
removeBreakLine(vm.$el.textContent).trim(),
).toContain('The source branch HEAD has recently changed. Please reload the page and review the changes before merging');
}); });
}); });
...@@ -48,7 +48,7 @@ module Gitlab ...@@ -48,7 +48,7 @@ module Gitlab
}, },
'images' => { 'images' => {
input: 'image:https://localhost.com/image.png[Alt text" onerror="alert(7)]', input: 'image:https://localhost.com/image.png[Alt text" onerror="alert(7)]',
output: "<img src=\"https://localhost.com/image.png\" alt=\"Alt text\">" output: "<div>\n<p><span><img src=\"https://localhost.com/image.png\" alt='Alt text\" onerror=\"alert(7)'></span></p>\n</div>"
}, },
'pre' => { 'pre' => {
input: '```mypre"><script>alert(3)</script>', input: '```mypre"><script>alert(3)</script>',
......
...@@ -604,17 +604,20 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -604,17 +604,20 @@ describe Gitlab::Git::Repository, seed_helper: true do
shared_examples 'returning the right branches' do shared_examples 'returning the right branches' do
let(:head_id) { repository.rugged.head.target.oid } let(:head_id) { repository.rugged.head.target.oid }
let(:new_branch) { head_id } let(:new_branch) { head_id }
let(:utf8_branch) { 'branch-é' }
before do before do
repository.create_branch(new_branch, 'master') repository.create_branch(new_branch, 'master')
repository.create_branch(utf8_branch, 'master')
end end
after do after do
repository.delete_branch(new_branch) repository.delete_branch(new_branch)
repository.delete_branch(utf8_branch)
end end
it 'displays that branch' do it 'displays that branch' do
expect(repository.branch_names_contains_sha(head_id)).to include('master', new_branch) expect(repository.branch_names_contains_sha(head_id)).to include('master', new_branch, utf8_branch)
end end
end end
......
...@@ -275,7 +275,7 @@ describe Gitlab::Workhorse do ...@@ -275,7 +275,7 @@ describe Gitlab::Workhorse do
describe '.git_http_ok' do describe '.git_http_ok' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:repo_path) { repository.path_to_repo } let(:repo_path) { 'ignored but not allowed to be empty in gitlab-workhorse' }
let(:action) { 'info_refs' } let(:action) { 'info_refs' }
let(:params) do let(:params) do
{ {
......
...@@ -501,28 +501,6 @@ describe Repository do ...@@ -501,28 +501,6 @@ describe Repository do
end end
end end
describe '#create_hooks' do
let(:hook_path) { File.join(repository.path_to_repo, 'hooks') }
it 'symlinks the global hooks directory' do
repository.create_hooks
expect(File.symlink?(hook_path)).to be true
expect(File.readlink(hook_path)).to eq(Gitlab.config.gitlab_shell.hooks_path)
end
it 'replaces existing symlink with the right directory' do
FileUtils.mkdir_p(hook_path)
expect(File.symlink?(hook_path)).to be false
repository.create_hooks
expect(File.symlink?(hook_path)).to be true
expect(File.readlink(hook_path)).to eq(Gitlab.config.gitlab_shell.hooks_path)
end
end
describe "#create_dir" do describe "#create_dir" do
it "commits a change that creates a new directory" do it "commits a change that creates a new directory" do
expect do expect do
......
...@@ -163,7 +163,7 @@ describe 'Git HTTP requests' do ...@@ -163,7 +163,7 @@ describe 'Git HTTP requests' do
download(path) do |response| download(path) do |response|
json_body = ActiveSupport::JSON.decode(response.body) json_body = ActiveSupport::JSON.decode(response.body)
expect(json_body['RepoPath']).to include(wiki.repository.disk_path) expect(json_body['Repository']['relative_path']).to eq(wiki.repository.relative_path)
end end
end end
end end
......
...@@ -93,6 +93,25 @@ describe VerifyPagesDomainService do ...@@ -93,6 +93,25 @@ describe VerifyPagesDomainService do
expect(domain).not_to be_enabled expect(domain).not_to be_enabled
end end
end end
context 'invalid domain' do
let(:domain) { build(:pages_domain, :expired, :with_missing_chain) }
before do
domain.save(validate: false)
end
it 'can be disabled' do
error_status[:message] += '. It is now disabled.'
stub_resolver
expect(service.execute).to eq(error_status)
expect(domain).not_to be_verified
expect(domain).not_to be_enabled
end
end
end end
context 'timeout behaviour' do context 'timeout behaviour' do
......
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