Commit 572e10e8 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-02-07

# Conflicts:
#	app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
#	app/assets/javascripts/filtered_search/filtered_search_manager.js
#	app/assets/javascripts/pages/groups/issues/index.js
#	app/assets/javascripts/pages/projects/issues/index/index.js
#	app/assets/javascripts/pages/search/init_filtered_search.js
#	app/controllers/projects/variables_controller.rb
#	app/models/ci/pipeline.rb
#	app/models/ci/runner.rb
#	app/models/group.rb
#	app/serializers/variable_entity.rb
#	app/services/lfs/lock_file_service.rb
#	app/services/lfs/unlock_file_service.rb
#	config/routes/project.rb
#	db/schema.rb
#	doc/administration/job_artifacts.md
#	doc/api/search.md
#	doc/ci/examples/browser_performance.md
#	doc/ci/variables/README.md
#	doc/topics/autodevops/index.md
#	doc/user/group/index.md
#	doc/user/permissions.md
#	doc/user/project/index.md
#	doc/user/project/issue_board.md
#	doc/user/project/issues/index.md
#	doc/user/project/merge_requests/index.md
#	lib/api/search.rb
#	locale/gitlab.pot
#	spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
#	spec/javascripts/fixtures/projects.rb
#	spec/lib/gitlab/import_export/all_models.yml
#	spec/requests/api/search_spec.rb
#	spec/services/lfs/lock_file_service_spec.rb
#	spec/services/lfs/unlock_file_service_spec.rb

[ci skip]
parents bd50198b fb89f417
...@@ -32,6 +32,7 @@ export default class Clusters { ...@@ -32,6 +32,7 @@ export default class Clusters {
installIngressPath, installIngressPath,
installRunnerPath, installRunnerPath,
installPrometheusPath, installPrometheusPath,
managePrometheusPath,
clusterStatus, clusterStatus,
clusterStatusReason, clusterStatusReason,
helpPath, helpPath,
...@@ -40,6 +41,7 @@ export default class Clusters { ...@@ -40,6 +41,7 @@ export default class Clusters {
this.store = new ClustersStore(); this.store = new ClustersStore();
this.store.setHelpPaths(helpPath, ingressHelpPath); this.store.setHelpPaths(helpPath, ingressHelpPath);
this.store.setManagePrometheusPath(managePrometheusPath);
this.store.updateStatus(clusterStatus); this.store.updateStatus(clusterStatus);
this.store.updateStatusReason(clusterStatusReason); this.store.updateStatusReason(clusterStatusReason);
this.service = new ClustersService({ this.service = new ClustersService({
...@@ -95,6 +97,7 @@ export default class Clusters { ...@@ -95,6 +97,7 @@ export default class Clusters {
applications: this.state.applications, applications: this.state.applications,
helpPath: this.state.helpPath, helpPath: this.state.helpPath,
ingressHelpPath: this.state.ingressHelpPath, ingressHelpPath: this.state.ingressHelpPath,
managePrometheusPath: this.state.managePrometheusPath,
}, },
}); });
}, },
......
...@@ -32,6 +32,10 @@ ...@@ -32,6 +32,10 @@
type: String, type: String,
required: false, required: false,
}, },
manageLink: {
type: String,
required: false,
},
description: { description: {
type: String, type: String,
required: true, required: true,
...@@ -89,6 +93,12 @@ ...@@ -89,6 +93,12 @@
return label; return label;
}, },
showManageButton() {
return this.manageLink && this.status === APPLICATION_INSTALLED;
},
manageButtonLabel() {
return s__('ClusterIntegration|Manage');
},
hasError() { hasError() {
return this.status === APPLICATION_ERROR || return this.status === APPLICATION_ERROR ||
this.requestStatus === REQUEST_FAILURE; this.requestStatus === REQUEST_FAILURE;
...@@ -141,9 +151,21 @@ ...@@ -141,9 +151,21 @@
<div v-html="description"></div> <div v-html="description"></div>
</div> </div>
<div <div
class="table-section table-button-footer section-15 section-align-top" class="table-section table-button-footer section-align-top"
:class="{ 'section-20': showManageButton, 'section-15': !showManageButton }"
role="gridcell" role="gridcell"
> >
<div
v-if="showManageButton"
class="btn-group table-action-buttons"
>
<a
class="btn"
:href="manageLink"
>
{{ manageButtonLabel }}
</a>
</div>
<div class="btn-group table-action-buttons"> <div class="btn-group table-action-buttons">
<loading-button <loading-button
class="js-cluster-application-install-button" class="js-cluster-application-install-button"
......
...@@ -23,13 +23,19 @@ ...@@ -23,13 +23,19 @@
required: false, required: false,
default: '', default: '',
}, },
managePrometheusPath: {
type: String,
required: false,
default: '',
},
}, },
computed: { computed: {
generalApplicationDescription() { generalApplicationDescription() {
return sprintf( return sprintf(
_.escape(s__(`ClusterIntegration|Install applications on your Kubernetes cluster. _.escape(s__(
Read more about %{helpLink}`)), `ClusterIntegration|Install applications on your Kubernetes cluster.
{ Read more about %{helpLink}`,
)), {
helpLink: `<a href="${this.helpPath}"> helpLink: `<a href="${this.helpPath}">
${_.escape(s__('ClusterIntegration|installing applications'))} ${_.escape(s__('ClusterIntegration|installing applications'))}
</a>`, </a>`,
...@@ -96,11 +102,12 @@ ...@@ -96,11 +102,12 @@
}, },
prometheusDescription() { prometheusDescription() {
return sprintf( return sprintf(
_.escape(s__(`ClusterIntegration|Prometheus is an open-source monitoring system _.escape(s__(
with %{gitlabIntegrationLink} to monitor deployed applications.`)), `ClusterIntegration|Prometheus is an open-source monitoring system
{ with %{gitlabIntegrationLink} to monitor deployed applications.`,
)), {
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html" gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html"
target="_blank" rel="noopener noreferrer"> target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`, ${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`,
}, },
false, false,
...@@ -149,6 +156,7 @@ target="_blank" rel="noopener noreferrer"> ...@@ -149,6 +156,7 @@ target="_blank" rel="noopener noreferrer">
id="prometheus" id="prometheus"
:title="applications.prometheus.title" :title="applications.prometheus.title"
title-link="https://prometheus.io/docs/introduction/overview/" title-link="https://prometheus.io/docs/introduction/overview/"
:manage-link="managePrometheusPath"
:description="prometheusDescription" :description="prometheusDescription"
:status="applications.prometheus.status" :status="applications.prometheus.status"
:status-reason="applications.prometheus.statusReason" :status-reason="applications.prometheus.statusReason"
......
...@@ -45,6 +45,10 @@ export default class ClusterStore { ...@@ -45,6 +45,10 @@ export default class ClusterStore {
this.state.ingressHelpPath = ingressHelpPath; this.state.ingressHelpPath = ingressHelpPath;
} }
setManagePrometheusPath(managePrometheusPath) {
this.state.managePrometheusPath = managePrometheusPath;
}
updateStatus(status) { updateStatus(status) {
this.state.status = status; this.state.status = status;
} }
......
...@@ -73,11 +73,14 @@ class FilteredSearchDropdownManager { ...@@ -73,11 +73,14 @@ class FilteredSearchDropdownManager {
gl: 'DropdownEmoji', gl: 'DropdownEmoji',
element: this.container.querySelector('#js-dropdown-my-reaction'), element: this.container.querySelector('#js-dropdown-my-reaction'),
}, },
<<<<<<< HEAD
weight: { weight: {
reference: null, reference: null,
gl: 'DropdownNonUser', gl: 'DropdownNonUser',
element: this.container.querySelector('#js-dropdown-weight'), element: this.container.querySelector('#js-dropdown-weight'),
}, },
=======
>>>>>>> upstream/master
}; };
supportedTokens.forEach((type) => { supportedTokens.forEach((type) => {
......
...@@ -30,9 +30,12 @@ class FilteredSearchManager { ...@@ -30,9 +30,12 @@ class FilteredSearchManager {
issues: 'issue-recent-searches', issues: 'issue-recent-searches',
merge_requests: 'merge-request-recent-searches', merge_requests: 'merge-request-recent-searches',
}; };
<<<<<<< HEAD
// EE specific setup // EE specific setup
this.initEE(); this.initEE();
=======
>>>>>>> upstream/master
this.recentSearchesStore = new RecentSearchesStore({ this.recentSearchesStore = new RecentSearchesStore({
isLocalStorageAvailable: RecentSearchesService.isAvailable(), isLocalStorageAvailable: RecentSearchesService.isAvailable(),
......
import axios from './axios_utils'; import axios from './axios_utils';
import { getLocationHash } from './url_utility'; import { getLocationHash } from './url_utility';
import { convertToCamelCase } from './text_utility';
export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index]; export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index];
...@@ -395,6 +396,26 @@ export const spriteIcon = (icon, className = '') => { ...@@ -395,6 +396,26 @@ export const spriteIcon = (icon, className = '') => {
return `<svg ${classAttribute}><use xlink:href="${gon.sprite_icons}#${icon}" /></svg>`; return `<svg ${classAttribute}><use xlink:href="${gon.sprite_icons}#${icon}" /></svg>`;
}; };
/**
* This method takes in object with snake_case property names
* and returns new object with camelCase property names
*
* Reasoning for this method is to ensure consistent property
* naming conventions across JS code.
*/
export const convertObjectPropsToCamelCase = (obj = {}) => {
if (obj === null) {
return {};
}
return Object.keys(obj).reduce((acc, prop) => {
const result = acc;
result[convertToCamelCase(prop)] = obj[prop];
return acc;
}, {});
};
export const imagePath = imgUrl => `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`; export const imagePath = imgUrl => `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`;
window.gl = window.gl || {}; window.gl = window.gl || {};
......
...@@ -9,6 +9,20 @@ import { ...@@ -9,6 +9,20 @@ import {
window.timeago = timeago; window.timeago = timeago;
window.dateFormat = dateFormat; window.dateFormat = dateFormat;
/**
* Returns i18n month names array.
* If `abbreviated` is provided, returns abbreviated
* name.
*
* @param {Boolean} abbreviated
*/
const getMonthNames = (abbreviated) => {
if (abbreviated) {
return [s__('Jan'), s__('Feb'), s__('Mar'), s__('Apr'), s__('May'), s__('Jun'), s__('Jul'), s__('Aug'), s__('Sep'), s__('Oct'), s__('Nov'), s__('Dec')];
}
return [s__('January'), s__('February'), s__('March'), s__('April'), s__('May'), s__('June'), s__('July'), s__('August'), s__('September'), s__('October'), s__('November'), s__('December')];
};
/** /**
* Given a date object returns the day of the week in English * Given a date object returns the day of the week in English
* @param {date} date * @param {date} date
...@@ -157,7 +171,7 @@ export function timeIntervalInWords(intervalInSeconds) { ...@@ -157,7 +171,7 @@ export function timeIntervalInWords(intervalInSeconds) {
return text; return text;
} }
export function dateInWords(date, abbreviated = false) { export function dateInWords(date, abbreviated = false, hideYear = false) {
if (!date) return date; if (!date) return date;
const month = date.getMonth(); const month = date.getMonth();
...@@ -168,9 +182,115 @@ export function dateInWords(date, abbreviated = false) { ...@@ -168,9 +182,115 @@ export function dateInWords(date, abbreviated = false) {
const monthName = abbreviated ? monthNamesAbbr[month] : monthNames[month]; const monthName = abbreviated ? monthNamesAbbr[month] : monthNames[month];
if (hideYear) {
return `${monthName} ${date.getDate()}`;
}
return `${monthName} ${date.getDate()}, ${year}`; return `${monthName} ${date.getDate()}, ${year}`;
} }
/**
* Returns month name based on provided date.
*
* @param {Date} date
* @param {Boolean} abbreviated
*/
export const monthInWords = (date, abbreviated = false) => {
if (!date) {
return '';
}
return getMonthNames(abbreviated)[date.getMonth()];
};
/**
* Returns number of days in a month for provided date.
* courtesy: https://stacko(verflow.com/a/1185804/414749
*
* @param {Date} date
*/
export const totalDaysInMonth = (date) => {
if (!date) {
return 0;
}
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
};
/**
* Returns list of Dates referring to Sundays of the month
* based on provided date
*
* @param {Date} date
*/
export const getSundays = (date) => {
if (!date) {
return [];
}
const daysToSunday = ['Saturday', 'Friday', 'Thursday', 'Wednesday', 'Tuesday', 'Monday', 'Sunday'];
const month = date.getMonth();
const year = date.getFullYear();
const sundays = [];
const dateOfMonth = new Date(year, month, 1);
while (dateOfMonth.getMonth() === month) {
const dayName = getDayName(dateOfMonth);
if (dayName === 'Sunday') {
sundays.push(new Date(dateOfMonth.getTime()));
}
const daysUntilNextSunday = daysToSunday.indexOf(dayName) + 1;
dateOfMonth.setDate(dateOfMonth.getDate() + daysUntilNextSunday);
}
return sundays;
};
/**
* Returns list of Dates representing a timeframe of Months from month of provided date (inclusive)
* up to provided length
*
* For eg;
* If current month is January 2018 and `length` provided is `6`
* Then this method will return list of Date objects as follows;
*
* [ October 2017, November 2017, December 2017, January 2018, February 2018, March 2018 ]
*
* If current month is March 2018 and `length` provided is `3`
* Then this method will return list of Date objects as follows;
*
* [ February 2018, March 2018, April 2018 ]
*
* @param {Number} length
* @param {Date} date
*/
export const getTimeframeWindow = (length, date) => {
if (!length) {
return [];
}
const currentDate = date instanceof Date ? date : new Date();
const currentMonthIndex = Math.floor(length / 2);
const timeframe = [];
// Move date object backward to the first month of timeframe
currentDate.setDate(1);
currentDate.setMonth(currentDate.getMonth() - currentMonthIndex);
// Iterate and update date for the size of length
// and push date reference to timeframe list
for (let i = 0; i < length; i += 1) {
timeframe.push(new Date(currentDate.getTime()));
currentDate.setMonth(currentDate.getMonth() + 1);
}
// Change date of last timeframe item to last date of the month
timeframe[length - 1].setDate(totalDaysInMonth(timeframe[length - 1]));
return timeframe;
};
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.utils = { window.gl.utils = {
...(window.gl.utils || {}), ...(window.gl.utils || {}),
......
...@@ -73,3 +73,10 @@ export function capitalizeFirstCharacter(text) { ...@@ -73,3 +73,10 @@ export function capitalizeFirstCharacter(text) {
* @returns {String} * @returns {String}
*/ */
export const stripHtml = (string, replace = '') => string.replace(/<[^>]*>/g, replace); export const stripHtml = (string, replace = '') => string.replace(/<[^>]*>/g, replace);
/**
* Converts snake_case string to camelCase
*
* @param {*} string
*/
export const convertToCamelCase = string => string.replace(/(_\w)/g, s => s[1].toUpperCase());
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
hasMetrics: convertPermissionToBoolean(metricsData.hasMetrics), hasMetrics: convertPermissionToBoolean(metricsData.hasMetrics),
documentationPath: metricsData.documentationPath, documentationPath: metricsData.documentationPath,
settingsPath: metricsData.settingsPath, settingsPath: metricsData.settingsPath,
clustersPath: metricsData.clustersPath,
tagsPath: metricsData.tagsPath, tagsPath: metricsData.tagsPath,
projectPath: metricsData.projectPath, projectPath: metricsData.projectPath,
metricsEndpoint: metricsData.additionalMetrics, metricsEndpoint: metricsData.additionalMetrics,
...@@ -132,6 +133,7 @@ ...@@ -132,6 +133,7 @@
:selected-state="state" :selected-state="state"
:documentation-path="documentationPath" :documentation-path="documentationPath"
:settings-path="settingsPath" :settings-path="settingsPath"
:clusters-path="clustersPath"
:empty-getting-started-svg-path="emptyGettingStartedSvgPath" :empty-getting-started-svg-path="emptyGettingStartedSvgPath"
:empty-loading-svg-path="emptyLoadingSvgPath" :empty-loading-svg-path="emptyLoadingSvgPath"
:empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath" :empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath"
......
...@@ -10,6 +10,11 @@ ...@@ -10,6 +10,11 @@
required: false, required: false,
default: '', default: '',
}, },
clustersPath: {
type: String,
required: false,
default: '',
},
selectedState: { selectedState: {
type: String, type: String,
required: true, required: true,
...@@ -35,7 +40,10 @@ ...@@ -35,7 +40,10 @@
title: 'Get started with performance monitoring', title: 'Get started with performance monitoring',
description: `Stay updated about the performance and health description: `Stay updated about the performance and health
of your environment by configuring Prometheus to monitor your deployments.`, of your environment by configuring Prometheus to monitor your deployments.`,
buttonText: 'Configure Prometheus', buttonText: 'Install Prometheus on clusters',
buttonPath: this.clustersPath,
secondaryButtonText: 'Configure existing Prometheus',
secondaryButtonPath: this.settingsPath,
}, },
loading: { loading: {
svgUrl: this.emptyLoadingSvgPath, svgUrl: this.emptyLoadingSvgPath,
...@@ -43,6 +51,7 @@ ...@@ -43,6 +51,7 @@
description: `Creating graphs uses the data from the Prometheus server. description: `Creating graphs uses the data from the Prometheus server.
If this takes a long time, ensure that data is available.`, If this takes a long time, ensure that data is available.`,
buttonText: 'View documentation', buttonText: 'View documentation',
buttonPath: this.documentationPath,
}, },
noData: { noData: {
svgUrl: this.emptyUnableToConnectSvgPath, svgUrl: this.emptyUnableToConnectSvgPath,
...@@ -50,12 +59,14 @@ ...@@ -50,12 +59,14 @@
description: `You are connected to the Prometheus server, but there is currently description: `You are connected to the Prometheus server, but there is currently
no data to display.`, no data to display.`,
buttonText: 'Configure Prometheus', buttonText: 'Configure Prometheus',
buttonPath: this.settingsPath,
}, },
unableToConnect: { unableToConnect: {
svgUrl: this.emptyUnableToConnectSvgPath, svgUrl: this.emptyUnableToConnectSvgPath,
title: 'Unable to connect to Prometheus server', title: 'Unable to connect to Prometheus server',
description: 'Ensure connectivity is available from the GitLab server to the ', description: 'Ensure connectivity is available from the GitLab server to the ',
buttonText: 'View documentation', buttonText: 'View documentation',
buttonPath: this.documentationPath,
}, },
}, },
}; };
...@@ -65,13 +76,6 @@ ...@@ -65,13 +76,6 @@
return this.states[this.selectedState]; return this.states[this.selectedState];
}, },
buttonPath() {
if (this.selectedState === 'gettingStarted') {
return this.settingsPath;
}
return this.documentationPath;
},
showButtonDescription() { showButtonDescription() {
if (this.selectedState === 'unableToConnect') return true; if (this.selectedState === 'unableToConnect') return true;
return false; return false;
...@@ -99,11 +103,21 @@ ...@@ -99,11 +103,21 @@
</p> </p>
<div class="state-button"> <div class="state-button">
<a <a
v-if="currentState.buttonPath"
class="btn btn-success" class="btn btn-success"
:href="buttonPath" :href="currentState.buttonPath"
> >
{{ currentState.buttonText }} {{ currentState.buttonText }}
</a> </a>
</div> </div>
<div class="state-button">
<a
v-if="currentState.secondaryButtonPath"
class="btn"
:href="currentState.secondaryButtonPath"
>
{{ currentState.secondaryButtonText }}
</a>
</div>
</div> </div>
</template> </template>
...@@ -6,7 +6,10 @@ import FilteredSearchTokenKeysIssues from 'ee/filtered_search/filtered_search_to ...@@ -6,7 +6,10 @@ import FilteredSearchTokenKeysIssues from 'ee/filtered_search/filtered_search_to
export default () => { export default () => {
initFilteredSearch({ initFilteredSearch({
page: FILTERED_SEARCH.ISSUES, page: FILTERED_SEARCH.ISSUES,
<<<<<<< HEAD
filteredSearchTokenKeys: FilteredSearchTokenKeysIssues, filteredSearchTokenKeys: FilteredSearchTokenKeysIssues,
=======
>>>>>>> upstream/master
}); });
projectSelect(); projectSelect();
}; };
...@@ -11,7 +11,10 @@ import FilteredSearchTokenKeysIssues from 'ee/filtered_search/filtered_search_to ...@@ -11,7 +11,10 @@ import FilteredSearchTokenKeysIssues from 'ee/filtered_search/filtered_search_to
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({ initFilteredSearch({
page: FILTERED_SEARCH.ISSUES, page: FILTERED_SEARCH.ISSUES,
<<<<<<< HEAD
filteredSearchTokenKeys: FilteredSearchTokenKeysIssues, filteredSearchTokenKeys: FilteredSearchTokenKeysIssues,
=======
>>>>>>> upstream/master
}); });
new IssuableIndex(ISSUABLE_INDEX.ISSUE); new IssuableIndex(ISSUABLE_INDEX.ISSUE);
......
<<<<<<< HEAD
export default ({ page, filteredSearchTokenKeys, stateFiltersSelector }) => { export default ({ page, filteredSearchTokenKeys, stateFiltersSelector }) => {
const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search'); const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
if (filteredSearchEnabled) { if (filteredSearchEnabled) {
...@@ -6,6 +7,12 @@ export default ({ page, filteredSearchTokenKeys, stateFiltersSelector }) => { ...@@ -6,6 +7,12 @@ export default ({ page, filteredSearchTokenKeys, stateFiltersSelector }) => {
filteredSearchTokenKeys, filteredSearchTokenKeys,
stateFiltersSelector, stateFiltersSelector,
}); });
=======
export default ({ page }) => {
const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
if (filteredSearchEnabled) {
const filteredSearchManager = new gl.FilteredSearchManager({ page });
>>>>>>> upstream/master
filteredSearchManager.setup(); filteredSearchManager.setup();
} }
}; };
/* eslint-disable no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, no-cond-assign, consistent-return, object-shorthand, prefer-arrow-callback, func-names, space-before-function-paren, prefer-template, quotes, class-methods-use-this, no-sequences, wrap-iife, no-lonely-if, no-else-return, no-param-reassign, vars-on-top, max-len */ /* eslint-disable no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, no-cond-assign, consistent-return, object-shorthand, prefer-arrow-callback, func-names, space-before-function-paren, prefer-template, quotes, class-methods-use-this, no-sequences, wrap-iife, no-lonely-if, no-else-return, no-param-reassign, vars-on-top, max-len */
import axios from './lib/utils/axios_utils';
import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from './lib/utils/common_utils'; import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from './lib/utils/common_utils';
/** /**
...@@ -146,23 +147,25 @@ export default class SearchAutocomplete { ...@@ -146,23 +147,25 @@ export default class SearchAutocomplete {
this.loadingSuggestions = true; this.loadingSuggestions = true;
return $.get(this.autocompletePath, { return axios.get(this.autocompletePath, {
project_id: this.projectId, params: {
project_ref: this.projectRef, project_id: this.projectId,
term: term, project_ref: this.projectRef,
}, (response) => { term: term,
var firstCategory, i, lastCategory, len, suggestion; },
}).then((response) => {
// Hide dropdown menu if no suggestions returns // Hide dropdown menu if no suggestions returns
if (!response.length) { if (!response.data.length) {
this.disableAutocomplete(); this.disableAutocomplete();
return; return;
} }
const data = []; const data = [];
// List results // List results
firstCategory = true; let firstCategory = true;
for (i = 0, len = response.length; i < len; i += 1) { let lastCategory;
suggestion = response[i]; for (let i = 0, len = response.data.length; i < len; i += 1) {
const suggestion = response.data[i];
// Add group header before list each group // Add group header before list each group
if (lastCategory !== suggestion.category) { if (lastCategory !== suggestion.category) {
if (!firstCategory) { if (!firstCategory) {
...@@ -177,7 +180,7 @@ export default class SearchAutocomplete { ...@@ -177,7 +180,7 @@ export default class SearchAutocomplete {
lastCategory = suggestion.category; lastCategory = suggestion.category;
} }
data.push({ data.push({
id: (suggestion.category.toLowerCase()) + "-" + suggestion.id, id: `${suggestion.category.toLowerCase()}-${suggestion.id}`,
category: suggestion.category, category: suggestion.category,
text: suggestion.label, text: suggestion.label,
url: suggestion.url, url: suggestion.url,
...@@ -187,13 +190,17 @@ export default class SearchAutocomplete { ...@@ -187,13 +190,17 @@ export default class SearchAutocomplete {
if (data.length) { if (data.length) {
data.push('separator'); data.push('separator');
data.push({ data.push({
text: "Result name contains \"" + term + "\"", text: `Result name contains "${term}"`,
url: "/search?search=" + term + "&project_id=" + (this.projectInputEl.val()) + "&group_id=" + (this.groupInputEl.val()), url: `/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`,
}); });
} }
return callback(data);
}) callback(data);
.always(() => { this.loadingSuggestions = false; });
this.loadingSuggestions = false;
}).catch(() => {
this.loadingSuggestions = false;
});
} }
getCategoryContents() { getCategoryContents() {
......
...@@ -215,6 +215,7 @@ $tooltip-font-size: 12px; ...@@ -215,6 +215,7 @@ $tooltip-font-size: 12px;
* Padding * Padding
*/ */
$gl-padding: 16px; $gl-padding: 16px;
$gl-padding-8: 8px;
$gl-col-padding: 15px; $gl-col-padding: 15px;
$gl-btn-padding: 10px; $gl-btn-padding: 10px;
$gl-input-padding: 10px; $gl-input-padding: 10px;
......
...@@ -365,7 +365,7 @@ ...@@ -365,7 +365,7 @@
} }
.prometheus-state { .prometheus-state {
max-width: 430px; max-width: 460px;
margin: 10px auto; margin: 10px auto;
text-align: center; text-align: center;
...@@ -373,6 +373,10 @@ ...@@ -373,6 +373,10 @@
max-width: 80vw; max-width: 80vw;
margin: 0 auto; margin: 0 auto;
} }
.state-button {
padding: $gl-padding / 2;
}
} }
.environments-actions { .environments-actions {
......
...@@ -135,6 +135,17 @@ ...@@ -135,6 +135,17 @@
padding-top: 0; padding-top: 0;
} }
.integration-settings-form {
.well {
padding: $gl-padding / 2;
box-shadow: none;
}
.svg-container {
max-width: 150px;
}
}
.token-token-container { .token-token-container {
#impersonation-token-token { #impersonation-token-token {
width: 80%; width: 80%;
......
...@@ -16,10 +16,7 @@ module Ci ...@@ -16,10 +16,7 @@ module Ci
@builds = @config_processor.builds @builds = @config_processor.builds
@jobs = @config_processor.jobs @jobs = @config_processor.jobs
end end
rescue
@error = 'Undefined error'
@status = false
ensure
render :show render :show
end end
end end
......
...@@ -33,6 +33,7 @@ module ServiceParams ...@@ -33,6 +33,7 @@ module ServiceParams
:issues_events, :issues_events,
:issues_url, :issues_url,
:jira_issue_transition_id, :jira_issue_transition_id,
:manual_configuration,
:merge_requests_events, :merge_requests_events,
:mock_service_url, :mock_service_url,
:namespace, :namespace,
......
...@@ -53,10 +53,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -53,10 +53,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
set_pipeline_variables set_pipeline_variables
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37432 render
Gitlab::GitalyClient.allow_n_plus_1_calls do
render
end
end end
format.json do format.json do
......
class Projects::VariablesController < Projects::ApplicationController class Projects::VariablesController < Projects::ApplicationController
<<<<<<< HEAD
prepend ::EE::Projects::VariablesController prepend ::EE::Projects::VariablesController
=======
>>>>>>> upstream/master
before_action :authorize_admin_build! before_action :authorize_admin_build!
def show def show
......
...@@ -28,7 +28,7 @@ class SnippetsFinder < UnionFinder ...@@ -28,7 +28,7 @@ class SnippetsFinder < UnionFinder
segments << items.public_to_user(current_user) segments << items.public_to_user(current_user)
segments << authorized_to_user(items) if current_user segments << authorized_to_user(items) if current_user
find_union(segments, Snippet) find_union(segments, Snippet.includes(:author))
end end
def authorized_to_user(items) def authorized_to_user(items)
......
module GraphHelper module GraphHelper
def get_refs(repo, commit) def refs(repo, commit)
refs = "" refs = commit.ref_names(repo).join(' ')
# Commit::ref_names already strips the refs/XXX from important refs (e.g. refs/heads/XXX)
# so anything leftover is internally used by GitLab
commit_refs = commit.ref_names(repo).reject { |name| name.starts_with?('refs/') }
refs << commit_refs.join(' ')
# append note count # append note count
notes_count = @graph.notes[commit.id] notes_count = @graph.notes[commit.id]
......
...@@ -406,8 +406,13 @@ module Ci ...@@ -406,8 +406,13 @@ module Ci
return @config_processor if defined?(@config_processor) return @config_processor if defined?(@config_processor)
@config_processor ||= begin @config_processor ||= begin
<<<<<<< HEAD
initialize_yaml_processor initialize_yaml_processor
rescue Gitlab::Ci::YamlProcessor::ValidationError, Psych::SyntaxError => e rescue Gitlab::Ci::YamlProcessor::ValidationError, Psych::SyntaxError => e
=======
Gitlab::Ci::YamlProcessor.new(ci_yaml_file)
rescue Gitlab::Ci::YamlProcessor::ValidationError => e
>>>>>>> upstream/master
self.yaml_errors = e.message self.yaml_errors = e.message
nil nil
rescue rescue
......
...@@ -3,7 +3,10 @@ module Ci ...@@ -3,7 +3,10 @@ module Ci
extend Gitlab::Ci::Model extend Gitlab::Ci::Model
include Gitlab::SQL::Pattern include Gitlab::SQL::Pattern
include RedisCacheable include RedisCacheable
<<<<<<< HEAD
prepend EE::Ci::Runner prepend EE::Ci::Runner
=======
>>>>>>> upstream/master
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
ONLINE_CONTACT_TIMEOUT = 1.hour ONLINE_CONTACT_TIMEOUT = 1.hour
......
...@@ -10,10 +10,26 @@ module Clusters ...@@ -10,10 +10,26 @@ module Clusters
default_value_for :version, VERSION default_value_for :version, VERSION
state_machine :status do
after_transition any => [:installed] do |application|
application.cluster.projects.each do |project|
project.find_or_initialize_service('prometheus').update(active: true)
end
end
end
def chart def chart
'stable/prometheus' 'stable/prometheus'
end end
def service_name
'prometheus-prometheus-server'
end
def service_port
80
end
def chart_values_file def chart_values_file
"#{Rails.root}/vendor/#{name}/values.yaml" "#{Rails.root}/vendor/#{name}/values.yaml"
end end
...@@ -21,6 +37,22 @@ module Clusters ...@@ -21,6 +37,22 @@ module Clusters
def install_command def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart, chart_values_file: chart_values_file) Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart, chart_values_file: chart_values_file)
end end
def proxy_client
return unless kube_client
proxy_url = kube_client.proxy_url('service', service_name, service_port, Gitlab::Kubernetes::Helm::NAMESPACE)
# ensures headers containing auth data are appended to original k8s client options
options = kube_client.rest_client.options.merge(headers: kube_client.headers)
RestClient::Resource.new(proxy_url, options)
end
private
def kube_client
cluster&.kubeclient
end
end end
end end
end end
...@@ -51,6 +51,9 @@ module Clusters ...@@ -51,6 +51,9 @@ module Clusters
scope :enabled, -> { where(enabled: true) } scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) } scope :disabled, -> { where(enabled: false) }
scope :for_environment, -> (env) { where(environment_scope: ['*', '', env.slug]) }
scope :for_all_environments, -> { where(environment_scope: ['*', '']) }
def status_name def status_name
if provider if provider
provider.status_name provider.status_name
......
...@@ -36,10 +36,13 @@ class Group < Namespace ...@@ -36,10 +36,13 @@ class Group < Namespace
has_many :hooks, dependent: :destroy, class_name: 'GroupHook' # rubocop:disable Cop/ActiveRecordDependent has_many :hooks, dependent: :destroy, class_name: 'GroupHook' # rubocop:disable Cop/ActiveRecordDependent
has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
<<<<<<< HEAD
# We cannot simply set `has_many :audit_events, as: :entity, dependent: :destroy` # We cannot simply set `has_many :audit_events, as: :entity, dependent: :destroy`
# here since Group inherits from Namespace, the entity_type would be set to `Namespace`. # here since Group inherits from Namespace, the entity_type would be set to `Namespace`.
has_many :audit_events, -> { where(entity_type: Group) }, foreign_key: 'entity_id' has_many :audit_events, -> { where(entity_type: Group) }, foreign_key: 'entity_id'
=======
>>>>>>> upstream/master
accepts_nested_attributes_for :variables, allow_destroy: true accepts_nested_attributes_for :variables, allow_destroy: true
validate :visibility_level_allowed_by_projects validate :visibility_level_allowed_by_projects
......
...@@ -290,7 +290,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -290,7 +290,7 @@ class MergeRequestDiff < ActiveRecord::Base
end end
def keep_around_commits def keep_around_commits
[repository, merge_request.source_project.repository].each do |repo| [repository, merge_request.source_project.repository].uniq.each do |repo|
repo.keep_around(start_commit_sha) repo.keep_around(start_commit_sha)
repo.keep_around(head_commit_sha) repo.keep_around(head_commit_sha)
repo.keep_around(base_commit_sha) repo.keep_around(base_commit_sha)
......
...@@ -7,11 +7,14 @@ class PrometheusService < MonitoringService ...@@ -7,11 +7,14 @@ class PrometheusService < MonitoringService
# Access to prometheus is directly through the API # Access to prometheus is directly through the API
prop_accessor :api_url prop_accessor :api_url
boolean_accessor :manual_configuration
with_options presence: true, if: :activated? do with_options presence: true, if: :manual_configuration? do
validates :api_url, url: true validates :api_url, url: true
end end
before_save :synchronize_service_state!
after_save :clear_reactive_cache! after_save :clear_reactive_cache!
def initialize_properties def initialize_properties
...@@ -20,12 +23,20 @@ class PrometheusService < MonitoringService ...@@ -20,12 +23,20 @@ class PrometheusService < MonitoringService
end end
end end
def show_active_box?
false
end
def editable?
manual_configuration? || !prometheus_installed?
end
def title def title
'Prometheus' 'Prometheus'
end end
def description def description
s_('PrometheusService|Prometheus monitoring') s_('PrometheusService|Time-series monitoring service')
end end
def self.to_param def self.to_param
...@@ -33,7 +44,15 @@ class PrometheusService < MonitoringService ...@@ -33,7 +44,15 @@ class PrometheusService < MonitoringService
end end
def fields def fields
return [] unless editable?
[ [
{
type: 'checkbox',
name: 'manual_configuration',
title: s_('PrometheusService|Active'),
required: true
},
{ {
type: 'text', type: 'text',
name: 'api_url', name: 'api_url',
...@@ -59,7 +78,7 @@ class PrometheusService < MonitoringService ...@@ -59,7 +78,7 @@ class PrometheusService < MonitoringService
end end
def deployment_metrics(deployment) def deployment_metrics(deployment)
metrics = with_reactive_cache(Gitlab::Prometheus::Queries::DeploymentQuery.name, deployment.id, &method(:rename_data_to_metrics)) metrics = with_reactive_cache(Gitlab::Prometheus::Queries::DeploymentQuery.name, deployment.environment.id, deployment.id, &method(:rename_data_to_metrics))
metrics&.merge(deployment_time: deployment.created_at.to_i) || {} metrics&.merge(deployment_time: deployment.created_at.to_i) || {}
end end
...@@ -68,7 +87,7 @@ class PrometheusService < MonitoringService ...@@ -68,7 +87,7 @@ class PrometheusService < MonitoringService
end end
def additional_deployment_metrics(deployment) def additional_deployment_metrics(deployment)
with_reactive_cache(Gitlab::Prometheus::Queries::AdditionalMetricsDeploymentQuery.name, deployment.id, &:itself) with_reactive_cache(Gitlab::Prometheus::Queries::AdditionalMetricsDeploymentQuery.name, deployment.environment.id, deployment.id, &:itself)
end end
def matched_metrics def matched_metrics
...@@ -79,6 +98,9 @@ class PrometheusService < MonitoringService ...@@ -79,6 +98,9 @@ class PrometheusService < MonitoringService
def calculate_reactive_cache(query_class_name, *args) def calculate_reactive_cache(query_class_name, *args)
return unless active? && project && !project.pending_delete? return unless active? && project && !project.pending_delete?
environment_id = args.first
client = client(environment_id)
data = Kernel.const_get(query_class_name).new(client).query(*args) data = Kernel.const_get(query_class_name).new(client).query(*args)
{ {
success: true, success: true,
...@@ -89,14 +111,55 @@ class PrometheusService < MonitoringService ...@@ -89,14 +111,55 @@ class PrometheusService < MonitoringService
{ success: false, result: err.message } { success: false, result: err.message }
end end
def client def client(environment_id = nil)
@prometheus ||= Gitlab::PrometheusClient.new(api_url: api_url) if manual_configuration?
Gitlab::PrometheusClient.new(RestClient::Resource.new(api_url))
else
cluster = cluster_with_prometheus(environment_id)
raise Gitlab::PrometheusError, "couldn't find cluster with Prometheus installed" unless cluster
rest_client = client_from_cluster(cluster)
raise Gitlab::PrometheusError, "couldn't create proxy Prometheus client" unless rest_client
Gitlab::PrometheusClient.new(rest_client)
end
end
def prometheus_installed?
return false if template?
return false unless project
project.clusters.enabled.any? { |cluster| cluster.application_prometheus&.installed? }
end end
private private
def cluster_with_prometheus(environment_id = nil)
clusters = if environment_id
::Environment.find_by(id: environment_id).try do |env|
# sort results by descending order based on environment_scope being longer
# thus more closely matching environment slug
project.clusters.enabled.for_environment(env).sort_by { |c| c.environment_scope&.length }.reverse!
end
else
project.clusters.enabled.for_all_environments
end
clusters&.detect { |cluster| cluster.application_prometheus&.installed? }
end
def client_from_cluster(cluster)
cluster.application_prometheus.proxy_client
end
def rename_data_to_metrics(metrics) def rename_data_to_metrics(metrics)
metrics[:metrics] = metrics.delete :data metrics[:metrics] = metrics.delete :data
metrics metrics
end end
def synchronize_service_state!
self.active = prometheus_installed? || manual_configuration?
true
end
end end
...@@ -3,6 +3,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated ...@@ -3,6 +3,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
include GitlabRoutingHelper include GitlabRoutingHelper
include MarkupHelper include MarkupHelper
include TreeHelper include TreeHelper
include Gitlab::Utils::StrongMemoize
presents :merge_request presents :merge_request
...@@ -43,7 +44,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated ...@@ -43,7 +44,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end end
def revert_in_fork_path def revert_in_fork_path
if user_can_fork_project? && can_be_reverted?(current_user) if user_can_fork_project? && cached_can_be_reverted?
continue_params = { continue_params = {
to: merge_request_path(merge_request), to: merge_request_path(merge_request),
notice: "#{edit_in_new_fork_notice} Try to cherry-pick this commit again.", notice: "#{edit_in_new_fork_notice} Try to cherry-pick this commit again.",
...@@ -157,7 +158,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated ...@@ -157,7 +158,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end end
def can_revert_on_current_merge_request? def can_revert_on_current_merge_request?
user_can_collaborate_with_project? && can_be_reverted?(current_user) user_can_collaborate_with_project? && cached_can_be_reverted?
end end
def can_cherry_pick_on_current_merge_request? def can_cherry_pick_on_current_merge_request?
...@@ -170,6 +171,12 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated ...@@ -170,6 +171,12 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
private private
def cached_can_be_reverted?
strong_memoize(:can_be_reverted) do
can_be_reverted?(current_user)
end
end
def conflicts def conflicts
@conflicts ||= MergeRequests::Conflicts::ListService.new(merge_request) @conflicts ||= MergeRequests::Conflicts::ListService.new(merge_request)
end end
......
class VariableEntity < Grape::Entity class VariableEntity < Grape::Entity
<<<<<<< HEAD
prepend ::EE::VariableEntity prepend ::EE::VariableEntity
=======
>>>>>>> upstream/master
expose :id expose :id
expose :key expose :key
expose :value expose :value
......
...@@ -7,6 +7,8 @@ module Ci ...@@ -7,6 +7,8 @@ module Ci
# stage. # stage.
# #
class EnsureStageService < BaseService class EnsureStageService < BaseService
EnsureStageError = Class.new(StandardError)
def execute(build) def execute(build)
@build = build @build = build
...@@ -22,8 +24,16 @@ module Ci ...@@ -22,8 +24,16 @@ module Ci
private private
def ensure_stage def ensure_stage(attempts: 2)
find_stage || create_stage find_stage || create_stage
rescue ActiveRecord::RecordNotUnique
retry if (attempts -= 1) > 0
raise EnsureStageError, <<~EOS
We failed to find or create a unique pipeline stage after 2 retries.
This should never happen and is most likely the result of a bug in
the database load balancing code.
EOS
end end
def find_stage def find_stage
......
module Ci module Ci
class RetryBuildService < ::BaseService class RetryBuildService < ::BaseService
CLONE_ACCESSORS = %i[pipeline project ref tag options commands name CLONE_ACCESSORS = %i[pipeline project ref tag options commands name
allow_failure stage_id stage stage_idx trigger_request allow_failure stage stage_id stage_idx trigger_request
yaml_variables when environment coverage_regex yaml_variables when environment coverage_regex
description tag_list protected].freeze description tag_list protected].freeze
......
module Lfs module Lfs
class LockFileService < BaseService class LockFileService < BaseService
<<<<<<< HEAD
prepend EE::Lfs::LockFileService prepend EE::Lfs::LockFileService
=======
>>>>>>> upstream/master
def execute def execute
unless can?(current_user, :push_code, project) unless can?(current_user, :push_code, project)
raise Gitlab::GitAccess::UnauthorizedError, 'You have no permissions' raise Gitlab::GitAccess::UnauthorizedError, 'You have no permissions'
......
module Lfs module Lfs
class UnlockFileService < BaseService class UnlockFileService < BaseService
<<<<<<< HEAD
prepend EE::Lfs::UnlockFileService prepend EE::Lfs::UnlockFileService
=======
>>>>>>> upstream/master
def execute def execute
unless can?(current_user, :push_code, project) unless can?(current_user, :push_code, project)
raise Gitlab::GitAccess::UnauthorizedError, 'You have no permissions' raise Gitlab::GitAccess::UnauthorizedError, 'You have no permissions'
......
...@@ -9,10 +9,7 @@ module MergeRequests ...@@ -9,10 +9,7 @@ module MergeRequests
merge_request.source_branch = params[:source_branch] merge_request.source_branch = params[:source_branch]
merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch)
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37439 create(merge_request)
Gitlab::GitalyClient.allow_n_plus_1_calls do
create(merge_request)
end
end end
def before_create(merge_request) def before_create(merge_request)
......
.layout-page{ class: page_with_sidebar_class } .layout-page{ class: page_with_sidebar_class }
- if defined?(nav) && nav - if defined?(nav) && nav
= render "layouts/nav/sidebar/#{nav}" = render "layouts/nav/sidebar/#{nav}"
.content-wrapper .content-wrapper{ class: "#{@content_wrapper_class}" }
= render 'shared/outdated_browser' = render 'shared/outdated_browser'
.mobile-overlay .mobile-overlay
.alert-wrapper .alert-wrapper
......
...@@ -14,7 +14,8 @@ ...@@ -14,7 +14,8 @@
cluster_status: @cluster.status_name, cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason, cluster_status_reason: @cluster.status_reason,
help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'), help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'),
ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-ip-address') } } ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-ip-address'),
manage_prometheus_path: edit_project_service_path(@cluster.project, 'prometheus') } }
.js-cluster-application-notice .js-cluster-application-notice
.flash-container .flash-container
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
= link_to @environment.name, environment_path(@environment) = link_to @environment.name, environment_path(@environment)
#prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'), #prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'),
"clusters-path": project_clusters_path(@project),
"documentation-path": help_page_path('administration/monitoring/prometheus/index.md'), "documentation-path": help_page_path('administration/monitoring/prometheus/index.md'),
"empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'), "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'),
"empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'), "empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'),
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
}, },
time: c.time, time: c.time,
space: c.spaces.first, space: c.spaces.first,
refs: get_refs(@graph.repo, c), refs: refs(@graph.repo, c),
id: c.sha, id: c.sha,
date: c.date, date: c.date,
message: c.message, message: c.message,
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
%p= @service.description %p= @service.description
.col-lg-9 .col-lg-9
= form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'gl-show-field-errors form-horizontal js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form| = form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'gl-show-field-errors form-horizontal integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, subject: @service = render 'shared/service_settings', form: form, subject: @service
- if @service.editable? - if @service.editable?
.footer-block.row-content-block .footer-block.row-content-block
......
%h4
= s_('PrometheusService|Auto configuration')
- if @service.manual_configuration?
.well
= s_('PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below')
- else
.container-fluid
.row
- if @service.prometheus_installed?
.col-sm-2
.svg-container
= image_tag 'illustrations/monitoring/getting_started.svg'
.col-sm-10
%p.text-success.prepend-top-default
= s_('PrometheusService|Prometheus is being automatically managed on your clusters')
= link_to s_('PrometheusService|Manage clusters'), project_clusters_path(@project), class: 'btn'
- else
.col-sm-2
= image_tag 'illustrations/monitoring/loading.svg'
.col-sm-10
%p.prepend-top-default
= s_('PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments')
= link_to s_('PrometheusService|Install Prometheus on clusters'), project_clusters_path(@project), class: 'btn btn-success'
%hr
%h4.append-bottom-default
= s_('PrometheusService|Manual configuration')
- unless @service.editable?
.well
= s_('PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters')
- link_project = local_assigns.fetch(:link_project, false) - link_project = local_assigns.fetch(:link_project, false)
%li.snippet-row %li.snippet-row
= image_tag avatar_icon(snippet.author_email), class: "avatar s40 hidden-xs", alt: '' = image_tag avatar_icon(snippet.author), class: "avatar s40 hidden-xs", alt: ''
.title .title
= link_to reliable_snippet_path(snippet) do = link_to reliable_snippet_path(snippet) do
......
---
title: Fix N+1 query problem for snippets dashboard.
merge_request: 16944
author:
type: performance
---
title: 'Handle all Psych YAML parser exceptions (fixes #41209)'
merge_request:
author:
type: fixed
---
title: Remove duplicate calls of MergeRequest#can_be_reverted?
merge_request:
author:
type: performance
---
title: Implement multi server support and use kube proxy to connect to Prometheus
servers inside K8S cluster
merge_request: 16182
author:
type: added
...@@ -170,6 +170,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -170,6 +170,7 @@ constraints(ProjectUrlConstrainer.new) do
end end
end end
<<<<<<< HEAD
## EE-specific ## EE-specific
resources :path_locks, only: [:index, :destroy] do resources :path_locks, only: [:index, :destroy] do
collection do collection do
...@@ -181,6 +182,8 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -181,6 +182,8 @@ constraints(ProjectUrlConstrainer.new) do
get '/service_desk' => 'service_desk#show', as: :service_desk get '/service_desk' => 'service_desk#show', as: :service_desk
put '/service_desk' => 'service_desk#update', as: :service_desk_refresh put '/service_desk' => 'service_desk#update', as: :service_desk_refresh
=======
>>>>>>> upstream/master
resource :variables, only: [:show, :update] resource :variables, only: [:show, :update]
resources :triggers, only: [:index, :create, :edit, :update, :destroy] do resources :triggers, only: [:index, :create, :edit, :update, :destroy] do
......
class RemoveRedundantPipelineStages < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up(attempts: 100)
remove_redundant_pipeline_stages!
remove_outdated_index!
add_unique_index!
rescue ActiveRecord::RecordNotUnique
retry if (attempts -= 1) > 0
raise StandardError, <<~EOS
Failed to add an unique index to ci_stages, despite retrying the
migration 100 times.
See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/16580.
EOS
end
def down
remove_concurrent_index :ci_stages, [:pipeline_id, :name], unique: true
add_concurrent_index :ci_stages, [:pipeline_id, :name]
end
private
def remove_outdated_index!
return unless index_exists?(:ci_stages, [:pipeline_id, :name])
remove_concurrent_index :ci_stages, [:pipeline_id, :name]
end
def add_unique_index!
add_concurrent_index :ci_stages, [:pipeline_id, :name], unique: true
end
def remove_redundant_pipeline_stages!
disable_statement_timeout
redundant_stages_ids = <<~SQL
SELECT id FROM ci_stages WHERE (pipeline_id, name) IN (
SELECT pipeline_id, name FROM ci_stages
GROUP BY pipeline_id, name HAVING COUNT(*) > 1
)
SQL
execute <<~SQL
UPDATE ci_builds SET stage_id = NULL WHERE stage_id IN (#{redundant_stages_ids})
SQL
if Gitlab::Database.postgresql?
execute <<~SQL
DELETE FROM ci_stages WHERE id IN (#{redundant_stages_ids})
SQL
else # We can't modify a table we are selecting from on MySQL
execute <<~SQL
DELETE a FROM ci_stages AS a, ci_stages AS b
WHERE a.pipeline_id = b.pipeline_id AND a.name = b.name
AND a.id <> b.id
SQL
end
end
end
...@@ -177,6 +177,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do ...@@ -177,6 +177,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do
t.integer "gitaly_timeout_default", default: 55, null: false t.integer "gitaly_timeout_default", default: 55, null: false
t.integer "gitaly_timeout_medium", default: 30, null: false t.integer "gitaly_timeout_medium", default: 30, null: false
t.integer "gitaly_timeout_fast", default: 10, null: false t.integer "gitaly_timeout_fast", default: 10, null: false
<<<<<<< HEAD
t.boolean "mirror_available", default: true, null: false t.boolean "mirror_available", default: true, null: false
t.string "auto_devops_domain" t.string "auto_devops_domain"
t.integer "default_project_creation", default: 2, null: false t.integer "default_project_creation", default: 2, null: false
...@@ -187,6 +188,10 @@ ActiveRecord::Schema.define(version: 20180206200543) do ...@@ -187,6 +188,10 @@ ActiveRecord::Schema.define(version: 20180206200543) do
t.integer "user_id", null: false t.integer "user_id", null: false
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
=======
t.boolean "authorized_keys_enabled", default: true, null: false
t.string "auto_devops_domain"
>>>>>>> upstream/master
end end
add_index "approvals", ["merge_request_id"], name: "index_approvals_on_merge_request_id", using: :btree add_index "approvals", ["merge_request_id"], name: "index_approvals_on_merge_request_id", using: :btree
...@@ -543,7 +548,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do ...@@ -543,7 +548,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do
t.integer "lock_version" t.integer "lock_version"
end end
add_index "ci_stages", ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", using: :btree add_index "ci_stages", ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", unique: true, using: :btree
add_index "ci_stages", ["pipeline_id"], name: "index_ci_stages_on_pipeline_id", using: :btree add_index "ci_stages", ["pipeline_id"], name: "index_ci_stages_on_pipeline_id", using: :btree
add_index "ci_stages", ["project_id"], name: "index_ci_stages_on_project_id", using: :btree add_index "ci_stages", ["project_id"], name: "index_ci_stages_on_project_id", using: :btree
...@@ -1303,6 +1308,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do ...@@ -1303,6 +1308,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do
add_index "labels", ["title"], name: "index_labels_on_title", using: :btree add_index "labels", ["title"], name: "index_labels_on_title", using: :btree
add_index "labels", ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree add_index "labels", ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree
<<<<<<< HEAD
create_table "ldap_group_links", force: :cascade do |t| create_table "ldap_group_links", force: :cascade do |t|
t.string "cn" t.string "cn"
t.integer "group_access", null: false t.integer "group_access", null: false
...@@ -1313,6 +1319,8 @@ ActiveRecord::Schema.define(version: 20180206200543) do ...@@ -1313,6 +1319,8 @@ ActiveRecord::Schema.define(version: 20180206200543) do
t.string "filter" t.string "filter"
end end
=======
>>>>>>> upstream/master
create_table "lfs_file_locks", force: :cascade do |t| create_table "lfs_file_locks", force: :cascade do |t|
t.integer "project_id", null: false t.integer "project_id", null: false
t.integer "user_id", null: false t.integer "user_id", null: false
......
...@@ -87,6 +87,7 @@ _The artifacts are stored by default in ...@@ -87,6 +87,7 @@ _The artifacts are stored by default in
### Using object storage ### Using object storage
<<<<<<< HEAD
>**Notes:** >**Notes:**
- [Introduced][ee-1762] in [GitLab Premium][eep] 9.4. - [Introduced][ee-1762] in [GitLab Premium][eep] 9.4.
- Since version 9.5, artifacts are [browsable], when object storage is enabled. - Since version 9.5, artifacts are [browsable], when object storage is enabled.
...@@ -178,6 +179,12 @@ _The artifacts are stored by default in ...@@ -178,6 +179,12 @@ _The artifacts are stored by default in
artifacts will still be stored on the local disk. In the future artifacts will still be stored on the local disk. In the future
you will be given an option to define a default storage artifacts for all you will be given an option to define a default storage artifacts for all
new files. new files.
=======
> Available in [GitLab Premium](https://about.gitlab.com/products/) and
[GitLab.com Silver](https://about.gitlab.com/gitlab-com/).
Use an [Object storage option][ee-os] like AWS S3 to store job artifacts.
>>>>>>> upstream/master
## Expiring artifacts ## Expiring artifacts
......
...@@ -19,8 +19,11 @@ GET /search ...@@ -19,8 +19,11 @@ GET /search
Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs. Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs.
<<<<<<< HEAD
If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md).
=======
>>>>>>> upstream/master
The response depends on the requested scope. The response depends on the requested scope.
### Scope: projects ### Scope: projects
...@@ -283,6 +286,7 @@ Example response: ...@@ -283,6 +286,7 @@ Example response:
] ]
``` ```
<<<<<<< HEAD
### Scope: wiki_blobs ### Scope: wiki_blobs
This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled.
...@@ -363,6 +367,8 @@ Example response: ...@@ -363,6 +367,8 @@ Example response:
] ]
``` ```
=======
>>>>>>> upstream/master
## Group Search API ## Group Search API
...@@ -382,8 +388,11 @@ GET /groups/:id/-/search ...@@ -382,8 +388,11 @@ GET /groups/:id/-/search
Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones. Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones.
<<<<<<< HEAD
If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md).
=======
>>>>>>> upstream/master
The response depends on the requested scope. The response depends on the requested scope.
### Scope: projects ### Scope: projects
...@@ -584,6 +593,7 @@ Example response: ...@@ -584,6 +593,7 @@ Example response:
] ]
``` ```
<<<<<<< HEAD
### Scope: wiki_blobs ### Scope: wiki_blobs
This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled. This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled.
...@@ -665,6 +675,8 @@ Example response: ...@@ -665,6 +675,8 @@ Example response:
``` ```
=======
>>>>>>> upstream/master
## Project Search API ## Project Search API
Search within the specified project. Search within the specified project.
......
...@@ -26,7 +26,11 @@ Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `performan ...@@ -26,7 +26,11 @@ Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `performan
This will create a `performance` job in your CI/CD pipeline and will run Sitespeed.io against the webpage you define. The GitLab plugin for Sitespeed.io downloaded in order to export the results to JSON. For further customization options of Sitespeed.io, including the ability to provide a list of URLs to test, please consult their [documentation](https://www.sitespeed.io/documentation/sitespeed.io/configuration/). This will create a `performance` job in your CI/CD pipeline and will run Sitespeed.io against the webpage you define. The GitLab plugin for Sitespeed.io downloaded in order to export the results to JSON. For further customization options of Sitespeed.io, including the ability to provide a list of URLs to test, please consult their [documentation](https://www.sitespeed.io/documentation/sitespeed.io/configuration/).
For [GitLab Premium](https://about.gitlab.com/products/) users, a performance score can be automatically For [GitLab Premium](https://about.gitlab.com/products/) users, a performance score can be automatically
<<<<<<< HEAD
extracted and shown right in the merge request widget. [Learn more on performance diffs in merge requests](../../user/project/merge_requests/browser_performance_testing.md). extracted and shown right in the merge request widget. [Learn more on performance diffs in merge requests](../../user/project/merge_requests/browser_performance_testing.md).
=======
extracted and shown right in the merge request widget. Learn more about [Browser Performance Testing](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html).
>>>>>>> upstream/master
## Performance testing on Review Apps ## Performance testing on Review Apps
......
...@@ -467,7 +467,11 @@ export CI_REGISTRY_PASSWORD="longalfanumstring" ...@@ -467,7 +467,11 @@ export CI_REGISTRY_PASSWORD="longalfanumstring"
[ee-2112]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2112 [ee-2112]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2112
[ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 "Simple protection of CI secret variables" [ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 "Simple protection of CI secret variables"
<<<<<<< HEAD
[premium]: https://about.gitlab.com/products/ "Available only in GitLab Premium" [premium]: https://about.gitlab.com/products/ "Available only in GitLab Premium"
=======
[eep]: https://about.gitlab.com/products/ "Available only in GitLab Premium"
>>>>>>> upstream/master
[envs]: ../environments.md [envs]: ../environments.md
[protected branches]: ../../user/project/protected_branches.md [protected branches]: ../../user/project/protected_branches.md
[protected tags]: ../../user/project/protected_tags.md [protected tags]: ../../user/project/protected_tags.md
......
...@@ -213,7 +213,11 @@ report is created, it's uploaded as an artifact which you can later download and ...@@ -213,7 +213,11 @@ report is created, it's uploaded as an artifact which you can later download and
check out. check out.
In GitLab Ultimate, any security warnings are also In GitLab Ultimate, any security warnings are also
<<<<<<< HEAD
[shown in the merge request widget](../../user/project/merge_requests/sast.md). [shown in the merge request widget](../../user/project/merge_requests/sast.md).
=======
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/sast.html).
>>>>>>> upstream/master
### Auto SAST for Docker images ### Auto SAST for Docker images
...@@ -226,7 +230,11 @@ created, it's uploaded as an artifact which you can later download and ...@@ -226,7 +230,11 @@ created, it's uploaded as an artifact which you can later download and
check out. check out.
In GitLab Ultimate, any security warnings are also In GitLab Ultimate, any security warnings are also
<<<<<<< HEAD
[shown in the merge request widget](../../user/project/merge_requests/sast_docker.md). [shown in the merge request widget](../../user/project/merge_requests/sast_docker.md).
=======
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/sast_docker.html).
>>>>>>> upstream/master
### Auto Review Apps ### Auto Review Apps
...@@ -265,7 +273,11 @@ issues. Once the report is created, it's uploaded as an artifact which you can ...@@ -265,7 +273,11 @@ issues. Once the report is created, it's uploaded as an artifact which you can
later download and check out. later download and check out.
In GitLab Ultimate, any security warnings are also In GitLab Ultimate, any security warnings are also
<<<<<<< HEAD
[shown in the merge request widget](../../user/project/merge_requests/dast.md). [shown in the merge request widget](../../user/project/merge_requests/dast.md).
=======
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
>>>>>>> upstream/master
### Auto Browser Performance Testing ### Auto Browser Performance Testing
...@@ -279,8 +291,12 @@ Auto Browser Performance Testing utilizes the [Sitespeed.io container](https://h ...@@ -279,8 +291,12 @@ Auto Browser Performance Testing utilizes the [Sitespeed.io container](https://h
/direction /direction
``` ```
<<<<<<< HEAD
In GitLab Premium, performance differences between the source In GitLab Premium, performance differences between the source
and target branches are [shown in the merge request widget](../../user/project/merge_requests/browser_performance_testing.md). and target branches are [shown in the merge request widget](../../user/project/merge_requests/browser_performance_testing.md).
=======
In GitLab Premium, performance differences between the source and target branches are [shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html).
>>>>>>> upstream/master
### Auto Deploy ### Auto Deploy
......
...@@ -102,6 +102,11 @@ running: ...@@ -102,6 +102,11 @@ running:
kubectl get svc ruby-app-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}' kubectl get svc ruby-app-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
``` ```
NOTE: **Note:**
If your ingress controller has been installed in a different way, you can find
how to get the external IP address in the
[Cluster documentation](../../user/project/clusters/index.md#getting-the-external-ip-address).
Use this IP address to configure your DNS. This part heavily depends on your Use this IP address to configure your DNS. This part heavily depends on your
preferences and domain provider. But in case you are not sure, just create an preferences and domain provider. But in case you are not sure, just create an
A record with a wildcard host like `*.<your-domain>`. A record with a wildcard host like `*.<your-domain>`.
......
...@@ -268,6 +268,7 @@ To enable this feature, navigate to the group settings page. Select ...@@ -268,6 +268,7 @@ To enable this feature, navigate to the group settings page. Select
![Checkbox for share with group lock](img/share_with_group_lock.png) ![Checkbox for share with group lock](img/share_with_group_lock.png)
#### Member Lock #### Member Lock
<<<<<<< HEAD
> Available in [GitLab Starter](https://about.gitlab.com/products/) and > Available in [GitLab Starter](https://about.gitlab.com/products/) and
[GitLab.com Bronze](https://about.gitlab.com/gitlab-com/). [GitLab.com Bronze](https://about.gitlab.com/gitlab-com/).
...@@ -291,12 +292,23 @@ and **Save group**. ...@@ -291,12 +292,23 @@ and **Save group**.
This will disable the option for all users who previously had permissions to This will disable the option for all users who previously had permissions to
operate project memberships so no new users can be added. Furthermore, any operate project memberships so no new users can be added. Furthermore, any
request to add new user to project through API will not be possible. request to add new user to project through API will not be possible.
=======
> Available in [GitLab Starter](https://about.gitlab.com/products/) and
[GitLab.com Bronze](https://about.gitlab.com/gitlab-com/).
With **Member Lock** it is possible to lock membership in project to the
level of members in group.
Learn more about [Member Lock](https://docs.gitlab.com/ee/user/group/index.html#member-lock).
>>>>>>> upstream/master
### Advanced settings ### Advanced settings
- **Projects**: view all projects within that group, add members to each project, - **Projects**: view all projects within that group, add members to each project,
access each project's settings, and remove any project from the same screen. access each project's settings, and remove any project from the same screen.
- **Webhooks**: configure [webhooks](../project/integrations/webhooks.md) - **Webhooks**: configure [webhooks](../project/integrations/webhooks.md)
<<<<<<< HEAD
and [push rules](../../push_rules/push_rules.md) to your group and [push rules](../../push_rules/push_rules.md) to your group
(Push Rules is available in [GitLab Starter][ee].) (Push Rules is available in [GitLab Starter][ee].)
- **Audit Events**: view [Audit Events](../../administration/audit_events.md) - **Audit Events**: view [Audit Events](../../administration/audit_events.md)
...@@ -314,3 +326,9 @@ you have an overview of the contributions (pushes, merge requests, ...@@ -314,3 +326,9 @@ you have an overview of the contributions (pushes, merge requests,
and issues) performed my your group members. and issues) performed my your group members.
[ee]: https://about.gitlab.com/products/ [ee]: https://about.gitlab.com/products/
=======
and [push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html#push-rules) to your group (Push Rules is available in [GitLab Starter](https://about.gitlab.com/products/).)
- **Audit Events**: view [Audit Events](https://docs.gitlab.com/ee/administration/audit_events.html#audit-events)
for the group (GitLab admins only, available in [GitLab Starter][ee]).
- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group
>>>>>>> upstream/master
...@@ -123,7 +123,11 @@ to learn more. ...@@ -123,7 +123,11 @@ to learn more.
### File Locking permissions ### File Locking permissions
<<<<<<< HEAD
> Available in [GitLab Premium](https://about.gitlab.com/products/) and [GitLab.com Silver](https://about.gitlab.com/gitlab-com/). > Available in [GitLab Premium](https://about.gitlab.com/products/) and [GitLab.com Silver](https://about.gitlab.com/gitlab-com/).
=======
> Available in [GitLab Premium](https://about.gitlab.com/products/).
>>>>>>> upstream/master
The user that locks a file or directory is the only one that can edit and push their changes back to the repository where the locked objects are located. The user that locks a file or directory is the only one that can edit and push their changes back to the repository where the locked objects are located.
...@@ -283,6 +287,11 @@ Read through the documentation on [LDAP users permissions](../administration/aut ...@@ -283,6 +287,11 @@ Read through the documentation on [LDAP users permissions](../administration/aut
## Auditor users permissions ## Auditor users permissions
<<<<<<< HEAD
=======
## Auditor users permissions
>>>>>>> upstream/master
> Available in [GitLab Premium](https://about.gitlab.com/products/). > Available in [GitLab Premium](https://about.gitlab.com/products/).
An Auditor user should be able to access all projects and groups of a GitLab instance An Auditor user should be able to access all projects and groups of a GitLab instance
......
...@@ -134,6 +134,41 @@ added directly to your configured cluster. Those applications are needed for ...@@ -134,6 +134,41 @@ added directly to your configured cluster. Those applications are needed for
| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps](../../../topics/autodevops/index.md) or deploy your own web apps. | | [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps](../../../topics/autodevops/index.md) or deploy your own web apps. |
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications | | [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications |
## Getting the external IP address
NOTE: **Note:**
You need a load balancer installed in your cluster in order to obtain the
external IP address with the following procedure. It can be deployed using the
**Ingress** application described in the previous section.
In order to publish your web application, you first need to find the external IP
address associated to your load balancer.
If the cluster is on GKE, click on the **Google Kubernetes Engine** link in the
**Advanced settings**, or go directly to the
[Google Kubernetes Engine dashboard](https://console.cloud.google.com/kubernetes/)
and select the proper project and cluster. Then click on **Connect** and execute
the `gcloud` command in a local terminal or using the **Cloud Shell**.
If the cluster is not on GKE, follow the specific instructions for your
Kubernetes provider to configure `kubectl` with the right credentials.
If you installed the Ingress using the **Applications** section, run the following command:
```bash
kubectl get svc --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
```
Otherwise, you can list the IP addresses of all load balancers:
```bash
kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalancer.ingress)]}{.status.loadBalancer.ingress[*].ip} '
```
The output is the external IP address of your cluster. This information can then
be used to set up DNS entries and forwarding rules that allow external access to
your deployed applications.
## Setting the environment scope ## Setting the environment scope
When adding more than one clusters, you need to differentiate them with an When adding more than one clusters, you need to differentiate them with an
......
...@@ -17,7 +17,11 @@ When you create a project in GitLab, you'll have access to a large number of ...@@ -17,7 +17,11 @@ When you create a project in GitLab, you'll have access to a large number of
- [Issue tracker](issues/index.md): Discuss implementations with your team within issues - [Issue tracker](issues/index.md): Discuss implementations with your team within issues
- [Issue Boards](issue_board.md): Organize and prioritize your workflow - [Issue Boards](issue_board.md): Organize and prioritize your workflow
<<<<<<< HEAD
- [Multiple Issue Boards](issue_board.md#multiple-issue-boards) (**Starter/Premium**): Allow your teams to create their own workflows (Issue Boards) for the same project - [Multiple Issue Boards](issue_board.md#multiple-issue-boards) (**Starter/Premium**): Allow your teams to create their own workflows (Issue Boards) for the same project
=======
- [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards) (**Starter/Premium**): Allow your teams to create their own workflows (Issue Boards) for the same project
>>>>>>> upstream/master
- [Repositories](repository/index.md): Host your code in a fully - [Repositories](repository/index.md): Host your code in a fully
integrated platform integrated platform
- [Branches](repository/branches/index.md): use Git branching strategies to - [Branches](repository/branches/index.md): use Git branching strategies to
...@@ -36,7 +40,11 @@ integrated platform ...@@ -36,7 +40,11 @@ integrated platform
- [Multiple Issue Boards](issue_board.md#multiple-issue-boards) (**Starter/Premium**): Allow your teams to create their own workflows (Issue Boards) for the same project - [Multiple Issue Boards](issue_board.md#multiple-issue-boards) (**Starter/Premium**): Allow your teams to create their own workflows (Issue Boards) for the same project
- [Merge Requests](merge_requests/index.md): Apply your branching - [Merge Requests](merge_requests/index.md): Apply your branching
strategy and get reviewed by your team strategy and get reviewed by your team
<<<<<<< HEAD
- [Merge Request Approvals](merge_requests/merge_request_approvals.md) (**Starter/Premium**): Ask for approval before - [Merge Request Approvals](merge_requests/merge_request_approvals.md) (**Starter/Premium**): Ask for approval before
=======
- [Merge Request Approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) (**Starter/Premium**): Ask for approval before
>>>>>>> upstream/master
implementing a change implementing a change
- [Fix merge conflicts from the UI](merge_requests/resolve_conflicts.md): - [Fix merge conflicts from the UI](merge_requests/resolve_conflicts.md):
Your Git diff tool right from GitLab's UI Your Git diff tool right from GitLab's UI
......
...@@ -33,8 +33,13 @@ You create issues, host code, perform reviews, build, test, ...@@ -33,8 +33,13 @@ You create issues, host code, perform reviews, build, test,
and deploy from one single platform. Issue Boards help you to visualize and deploy from one single platform. Issue Boards help you to visualize
and manage the entire process _in_ GitLab. and manage the entire process _in_ GitLab.
<<<<<<< HEAD
With [Multiple Issue Boards](#multiple-issue-boards), available With [Multiple Issue Boards](#multiple-issue-boards), available
only in [GitLab Enterprise Edition](https://about.gitlab.com/products/), only in [GitLab Enterprise Edition](https://about.gitlab.com/products/),
=======
With [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards), available
only in [GitLab Ultimate](https://about.gitlab.com/products/),
>>>>>>> upstream/master
you go even further, as you can not only keep yourself and your project you go even further, as you can not only keep yourself and your project
organized from a broader perspective with one Issue Board per project, organized from a broader perspective with one Issue Board per project,
but also allow your team members to organize their own workflow by creating but also allow your team members to organize their own workflow by creating
......
...@@ -34,7 +34,11 @@ your project public, open to collaboration. ...@@ -34,7 +34,11 @@ your project public, open to collaboration.
### Streamline collaboration ### Streamline collaboration
<<<<<<< HEAD
With [Multiple Assignees for Issues](multiple_assignees_for_issues.md), With [Multiple Assignees for Issues](multiple_assignees_for_issues.md),
=======
With [Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html),
>>>>>>> upstream/master
available in [GitLab Starter](https://about.gitlab.com/products/) available in [GitLab Starter](https://about.gitlab.com/products/)
you can streamline collaboration and allow shared responsibilities to be clearly displayed. you can streamline collaboration and allow shared responsibilities to be clearly displayed.
All assignees are shown across your workflows and receive notifications (as they All assignees are shown across your workflows and receive notifications (as they
...@@ -141,6 +145,7 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue ...@@ -141,6 +145,7 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue
Read through the documentation for [Issue Boards](../issue_board.md) Read through the documentation for [Issue Boards](../issue_board.md)
to find out more about this feature. to find out more about this feature.
<<<<<<< HEAD
### Export Issues to CSV ### Export Issues to CSV
> Available in [GitLab Starter](https://about.gitlab.com/products/) and > Available in [GitLab Starter](https://about.gitlab.com/products/) and
...@@ -160,6 +165,10 @@ and appear in a block below the issue description. Issues can be across groups ...@@ -160,6 +165,10 @@ and appear in a block below the issue description. Issues can be across groups
and projects. and projects.
Read more about [Related Issues](related_issues.md). Read more about [Related Issues](related_issues.md).
=======
With [GitLab Starter](https://about.gitlab.com/products/), you can also
create various boards per project with [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards).
>>>>>>> upstream/master
### External Issue Tracker ### External Issue Tracker
......
...@@ -31,11 +31,18 @@ With GitLab merge requests, you can: ...@@ -31,11 +31,18 @@ With GitLab merge requests, you can:
With **[GitLab Enterprise Edition][ee]**, you can also: With **[GitLab Enterprise Edition][ee]**, you can also:
<<<<<<< HEAD
- View the deployment process across projects with [Multi-Project Pipeline Graphs](../../../ci/multi_project_pipeline_graphs.md) (available only in GitLab Premium) - View the deployment process across projects with [Multi-Project Pipeline Graphs](../../../ci/multi_project_pipeline_graphs.md) (available only in GitLab Premium)
- Request [approvals](#merge-request-approvals) from your managers (available in GitLab Starter) - Request [approvals](#merge-request-approvals) from your managers (available in GitLab Starter)
- [Squash and merge](#squash-and-merge) for a cleaner commit history (available in GitLab Starter) - [Squash and merge](#squash-and-merge) for a cleaner commit history (available in GitLab Starter)
- Analyze the impact of your changes with [Code Quality reports](#code-quality-reports) (available in GitLab Starter) - Analyze the impact of your changes with [Code Quality reports](#code-quality-reports) (available in GitLab Starter)
- Determine the performance impact of changes with [Browser Performance Testing](#browser-performance-testing) (available in GitLab Premium) - Determine the performance impact of changes with [Browser Performance Testing](#browser-performance-testing) (available in GitLab Premium)
=======
- View the deployment process across projects with [Multi-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html#multi-project-pipeline-graphs) (available only in GitLab Premium)
- Request [approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers (available in GitLab Starter)
- [Squash and merge](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html) for a cleaner commit history (available in GitLab Starter)
- Analise the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Starter)
>>>>>>> upstream/master
## Use cases ## Use cases
...@@ -43,10 +50,17 @@ A. Consider you are a software developer working in a team: ...@@ -43,10 +50,17 @@ A. Consider you are a software developer working in a team:
1. You checkout a new branch, and submit your changes through a merge request 1. You checkout a new branch, and submit your changes through a merge request
1. You gather feedback from your team 1. You gather feedback from your team
<<<<<<< HEAD
1. You work on the implementation optimizing code with [Code Quality reports](#code-quality-reports) 1. You work on the implementation optimizing code with [Code Quality reports](#code-quality-reports)
1. You build and test your changes with GitLab CI/CD 1. You build and test your changes with GitLab CI/CD
1. You request the [approval](#merge-request-approvals) from your manager 1. You request the [approval](#merge-request-approvals) from your manager
1. Your manager pushes a commit with his final review, [approves the merge request](#merge-request-approvals), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) 1. Your manager pushes a commit with his final review, [approves the merge request](#merge-request-approvals), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds)
=======
1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Starter)
1. You build and test your changes with GitLab CI/CD
1. You request the approval from your manager
1. Your manager pushes a commit with his final review, [approves the merge request](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Starter)
>>>>>>> upstream/master
1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#manual-actions) for GitLab CI/CD 1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#manual-actions) for GitLab CI/CD
1. Your implementations were successfully shipped to your customer 1. Your implementations were successfully shipped to your customer
...@@ -56,8 +70,13 @@ B. Consider you're a web developer writing a webpage for your company's website: ...@@ -56,8 +70,13 @@ B. Consider you're a web developer writing a webpage for your company's website:
1. You gather feedback from your reviewers 1. You gather feedback from your reviewers
1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md) 1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md)
1. You request your web designers for their implementation 1. You request your web designers for their implementation
<<<<<<< HEAD
1. You request the [approval](#merge-request-approvals) from your manager 1. You request the [approval](#merge-request-approvals) from your manager
1. Once approved, your merge request is [squashed and merged](#squash-and-merge), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) 1. Once approved, your merge request is [squashed and merged](#squash-and-merge), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
=======
1. You request the [approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your manager (available in GitLab Starter)
1. Once approved, your merge request is [squashed and merged](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) (Squash and Merge is available in GitLab Starter)
>>>>>>> upstream/master
1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production 1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production
## Merge requests per project ## Merge requests per project
...@@ -358,9 +377,13 @@ git checkout origin/merge-requests/1 ...@@ -358,9 +377,13 @@ git checkout origin/merge-requests/1
[products]: https://about.gitlab.com/products/ "GitLab products page" [products]: https://about.gitlab.com/products/ "GitLab products page"
[protected branches]: ../protected_branches.md [protected branches]: ../protected_branches.md
<<<<<<< HEAD
[ci]: ../../../ci/README.md [ci]: ../../../ci/README.md
[cc]: https://codeclimate.com/ [cc]: https://codeclimate.com/
[cd]: https://hub.docker.com/r/codeclimate/codeclimate/ [cd]: https://hub.docker.com/r/codeclimate/codeclimate/
[ee]: https://about.gitlab.com/products/ "GitLab Enterprise Edition" [ee]: https://about.gitlab.com/products/ "GitLab Enterprise Edition"
[sitespeed]: https://www.sitespeed.io [sitespeed]: https://www.sitespeed.io
[sitespeed-container]: https://hub.docker.com/r/sitespeedio/sitespeed.io/ [sitespeed-container]: https://hub.docker.com/r/sitespeedio/sitespeed.io/
=======
[ee]: https://about.gitlab.com/products/ "GitLab Enterprise Edition"
>>>>>>> upstream/master
...@@ -316,6 +316,47 @@ or various static site generators. Contributions are very welcome. ...@@ -316,6 +316,47 @@ or various static site generators. Contributions are very welcome.
Visit the GitLab Pages group for a full list of example projects: Visit the GitLab Pages group for a full list of example projects:
<https://gitlab.com/groups/pages>. <https://gitlab.com/groups/pages>.
### Serving compressed assets
Most modern browsers support downloading files in a compressed format. This
speeds up downloads by reducing the size of files.
Before serving an uncompressed file, Pages will check whether the same file
exists with a `.gz` extension. If it does, and the browser supports receiving
compressed files, it will serve that version instead of the uncompressed one.
To take advantage of this feature, the artifact you upload to the Pages should
have this structure:
```
public/
├─┬ index.html
│ └ index.html.gz
├── css/
│   └─┬ main.css
│ └ main.css.gz
└── js/
└─┬ main.js
└ main.js.gz
```
This can be achieved by including a `script:` command like this in your
`.gitlab-ci.yml` pages job:
```yaml
pages:
# Other directives
script:
- # build the public/ directory first
- find public -type f -iregex '.*\.\(htm\|html\|txt\|text\|js\|css\)$' -execdir gzip -f --keep {} \;
```
By pre-compressing the files and including both versions in the artifact, Pages
can serve requests for both compressed and uncompressed content without
needing to compress files on-demand.
### Add a custom domain to your Pages website ### Add a custom domain to your Pages website
For a complete guide on Pages domains, read through the article For a complete guide on Pages domains, read through the article
......
...@@ -68,7 +68,7 @@ You can live preview changes submitted to a new branch with ...@@ -68,7 +68,7 @@ You can live preview changes submitted to a new branch with
With [GitLab Enterprise Edition](https://about.gitlab.com/products/) With [GitLab Enterprise Edition](https://about.gitlab.com/products/)
subscriptions, you can also request subscriptions, you can also request
[approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html#merge-request-approvals) from your managers. [approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers.
To create, delete, and [branches](branches/index.md) via GitLab's UI: To create, delete, and [branches](branches/index.md) via GitLab's UI:
......
...@@ -18,8 +18,11 @@ module API ...@@ -18,8 +18,11 @@ module API
snippet_blobs: Entities::Snippet snippet_blobs: Entities::Snippet
}.freeze }.freeze
<<<<<<< HEAD
ELASTICSEARCH_SCOPES = %w(wiki_blobs blobs commits).freeze ELASTICSEARCH_SCOPES = %w(wiki_blobs blobs commits).freeze
=======
>>>>>>> upstream/master
def search(additional_params = {}) def search(additional_params = {})
search_params = { search_params = {
scope: params[:scope], scope: params[:scope],
...@@ -35,12 +38,15 @@ module API ...@@ -35,12 +38,15 @@ module API
end end
def process_results(results) def process_results(results)
<<<<<<< HEAD
return [] if results.empty? return [] if results.empty?
if results.is_a?(Elasticsearch::Model::Response::Response) if results.is_a?(Elasticsearch::Model::Response::Response)
return paginate(results).map { |blob| Gitlab::Elastic::SearchResults.parse_search_result(blob) } return paginate(results).map { |blob| Gitlab::Elastic::SearchResults.parse_search_result(blob) }
end end
=======
>>>>>>> upstream/master
case params[:scope] case params[:scope]
when 'wiki_blobs' when 'wiki_blobs'
paginate(results).map { |blob| Gitlab::ProjectSearchResults.parse_search_result(blob) } paginate(results).map { |blob| Gitlab::ProjectSearchResults.parse_search_result(blob) }
...@@ -58,6 +64,7 @@ module API ...@@ -58,6 +64,7 @@ module API
def entity def entity
SCOPE_ENTITY[params[:scope].to_sym] SCOPE_ENTITY[params[:scope].to_sym]
end end
<<<<<<< HEAD
def check_elasticsearch_scope! def check_elasticsearch_scope!
if ELASTICSEARCH_SCOPES.include?(params[:scope]) && !elasticsearch? if ELASTICSEARCH_SCOPES.include?(params[:scope]) && !elasticsearch?
...@@ -68,6 +75,8 @@ module API ...@@ -68,6 +75,8 @@ module API
def elasticsearch? def elasticsearch?
Gitlab::CurrentSettings.elasticsearch_search? Gitlab::CurrentSettings.elasticsearch_search?
end end
=======
>>>>>>> upstream/master
end end
resource :search do resource :search do
...@@ -79,6 +88,7 @@ module API ...@@ -79,6 +88,7 @@ module API
requires :scope, requires :scope,
type: String, type: String,
desc: 'The scope of search, available scopes: desc: 'The scope of search, available scopes:
<<<<<<< HEAD
projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs, projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs,
if Elasticsearch enabled: wiki_blobs, blobs, commits', if Elasticsearch enabled: wiki_blobs, blobs, commits',
values: %w(projects issues merge_requests milestones snippet_titles snippet_blobs values: %w(projects issues merge_requests milestones snippet_titles snippet_blobs
...@@ -88,6 +98,13 @@ module API ...@@ -88,6 +98,13 @@ module API
get do get do
check_elasticsearch_scope! check_elasticsearch_scope!
=======
projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs',
values: %w(projects issues merge_requests milestones snippet_titles snippet_blobs)
use :pagination
end
get do
>>>>>>> upstream/master
present search, with: entity present search, with: entity
end end
end end
...@@ -102,6 +119,7 @@ module API ...@@ -102,6 +119,7 @@ module API
requires :scope, requires :scope,
type: String, type: String,
desc: 'The scope of search, available scopes: desc: 'The scope of search, available scopes:
<<<<<<< HEAD
projects, issues, merge_requests, milestones, projects, issues, merge_requests, milestones,
if Elasticsearch enabled: wiki_blobs, blobs, commits', if Elasticsearch enabled: wiki_blobs, blobs, commits',
values: %w(projects issues merge_requests milestones wiki_blobs blobs commits) values: %w(projects issues merge_requests milestones wiki_blobs blobs commits)
...@@ -109,6 +127,13 @@ module API ...@@ -109,6 +127,13 @@ module API
end end
get ':id/-/search' do get ':id/-/search' do
check_elasticsearch_scope! check_elasticsearch_scope!
=======
projects, issues, merge_requests, milestones',
values: %w(projects issues merge_requests milestones)
use :pagination
end
get ':id/-/search' do
>>>>>>> upstream/master
find_group!(params[:id]) find_group!(params[:id])
present search(group_id: params[:id]), with: entity present search(group_id: params[:id]), with: entity
......
...@@ -6,6 +6,8 @@ module Gitlab ...@@ -6,6 +6,8 @@ module Gitlab
def initialize(config) def initialize(config)
@config = YAML.safe_load(config, [Symbol], [], true) @config = YAML.safe_load(config, [Symbol], [], true)
rescue Psych::Exception => e
raise FormatError, e.message
end end
def valid? def valid?
......
...@@ -85,7 +85,7 @@ module Gitlab ...@@ -85,7 +85,7 @@ module Gitlab
begin begin
Gitlab::Ci::YamlProcessor.new(content, opts) Gitlab::Ci::YamlProcessor.new(content, opts)
nil nil
rescue ValidationError, Psych::SyntaxError => e rescue ValidationError => e
e.message e.message
end end
end end
......
...@@ -402,15 +402,6 @@ module Gitlab ...@@ -402,15 +402,6 @@ module Gitlab
end end
end end
# Get a collection of Rugged::Reference objects for this commit.
#
# Ex.
# commit.ref(repo)
#
def refs(repo)
repo.refs_hash[id]
end
# Get ref names collection # Get ref names collection
# #
# Ex. # Ex.
...@@ -418,7 +409,7 @@ module Gitlab ...@@ -418,7 +409,7 @@ module Gitlab
# #
def ref_names(repo) def ref_names(repo)
refs(repo).map do |ref| refs(repo).map do |ref|
ref.name.sub(%r{^refs/(heads|remotes|tags)/}, "") ref.sub(%r{^refs/(heads|remotes|tags)/}, "")
end end
end end
...@@ -553,6 +544,15 @@ module Gitlab ...@@ -553,6 +544,15 @@ module Gitlab
date: Google::Protobuf::Timestamp.new(seconds: author_or_committer[:time].to_i) date: Google::Protobuf::Timestamp.new(seconds: author_or_committer[:time].to_i)
) )
end end
# Get a collection of Gitlab::Git::Ref objects for this commit.
#
# Ex.
# commit.ref(repo)
#
def refs(repo)
repo.refs_hash[id]
end
end end
end end
end end
...@@ -631,21 +631,18 @@ module Gitlab ...@@ -631,21 +631,18 @@ module Gitlab
end end
end end
# Get refs hash which key is SHA1 # Get refs hash which key is is the commit id
# and value is a Rugged::Reference # and value is a Gitlab::Git::Tag or Gitlab::Git::Branch
# Note that both inherit from Gitlab::Git::Ref
def refs_hash def refs_hash
# Initialize only when first call return @refs_hash if @refs_hash
if @refs_hash.nil?
@refs_hash = Hash.new { |h, k| h[k] = [] } @refs_hash = Hash.new { |h, k| h[k] = [] }
rugged.references.each do |r| (tags + branches).each do |ref|
# Symbolic/remote references may not have an OID; skip over them next unless ref.target && ref.name
target_oid = r.target.try(:oid)
if target_oid @refs_hash[ref.dereferenced_target.id] << ref.name
sha = rev_parse_target(target_oid).oid
@refs_hash[sha] << r
end
end
end end
@refs_hash @refs_hash
......
...@@ -25,9 +25,8 @@ module Gitlab ...@@ -25,9 +25,8 @@ module Gitlab
@repository.exists? @repository.exists?
end end
# Disabled because of https://gitlab.com/gitlab-org/gitaly/merge_requests/539
def write_page(name, format, content, commit_details) def write_page(name, format, content, commit_details)
@repository.gitaly_migrate(:wiki_write_page, status: Gitlab::GitalyClient::MigrationStatus::DISABLED) do |is_enabled| @repository.gitaly_migrate(:wiki_write_page) do |is_enabled|
if is_enabled if is_enabled
gitaly_write_page(name, format, content, commit_details) gitaly_write_page(name, format, content, commit_details)
gollum_wiki.clear_cache gollum_wiki.clear_cache
...@@ -48,9 +47,8 @@ module Gitlab ...@@ -48,9 +47,8 @@ module Gitlab
end end
end end
# Disable because of https://gitlab.com/gitlab-org/gitlab-ce/issues/42094
def update_page(page_path, title, format, content, commit_details) def update_page(page_path, title, format, content, commit_details)
@repository.gitaly_migrate(:wiki_update_page, status: Gitlab::GitalyClient::MigrationStatus::DISABLED) do |is_enabled| @repository.gitaly_migrate(:wiki_update_page) do |is_enabled|
if is_enabled if is_enabled
gitaly_update_page(page_path, title, format, content, commit_details) gitaly_update_page(page_path, title, format, content, commit_details)
gollum_wiki.clear_cache gollum_wiki.clear_cache
......
...@@ -222,14 +222,25 @@ module Gitlab ...@@ -222,14 +222,25 @@ module Gitlab
end end
def find_commit(revision) def find_commit(revision)
request = Gitaly::FindCommitRequest.new( if RequestStore.active?
repository: @gitaly_repo, # We don't use RequeStstore.fetch(key) { ... } directly because `revision`
revision: encode_binary(revision) # can be a branch name, so we can't use it as a key as it could point
) # to another commit later on (happens a lot in tests).
key = {
response = GitalyClient.call(@repository.storage, :commit_service, :find_commit, request, timeout: GitalyClient.medium_timeout) storage: @gitaly_repo.storage_name,
relative_path: @gitaly_repo.relative_path,
response.commit commit_id: revision
}
return RequestStore[key] if RequestStore.exist?(key)
commit = call_find_commit(revision)
return unless commit
key[:commit_id] = commit.id
RequestStore[key] = commit
else
call_find_commit(revision)
end
end end
def patch(revision) def patch(revision)
...@@ -346,6 +357,17 @@ module Gitlab ...@@ -346,6 +357,17 @@ module Gitlab
def encode_repeated(a) def encode_repeated(a)
Google::Protobuf::RepeatedField.new(:bytes, a.map { |s| encode_binary(s) } ) Google::Protobuf::RepeatedField.new(:bytes, a.map { |s| encode_binary(s) } )
end end
def call_find_commit(revision)
request = Gitaly::FindCommitRequest.new(
repository: @gitaly_repo,
revision: encode_binary(revision)
)
response = GitalyClient.call(@repository.storage, :commit_service, :find_commit, request, timeout: GitalyClient.medium_timeout)
response.commit
end
end end
end end
end end
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
class AdditionalMetricsDeploymentQuery < BaseQuery class AdditionalMetricsDeploymentQuery < BaseQuery
include QueryAdditionalMetrics include QueryAdditionalMetrics
def query(deployment_id) def query(environment_id, deployment_id)
Deployment.find_by(id: deployment_id).try do |deployment| Deployment.find_by(id: deployment_id).try do |deployment|
query_metrics( query_metrics(
common_query_context( common_query_context(
......
...@@ -2,7 +2,7 @@ module Gitlab ...@@ -2,7 +2,7 @@ module Gitlab
module Prometheus module Prometheus
module Queries module Queries
class DeploymentQuery < BaseQuery class DeploymentQuery < BaseQuery
def query(deployment_id) def query(environment_id, deployment_id)
Deployment.find_by(id: deployment_id).try do |deployment| Deployment.find_by(id: deployment_id).try do |deployment|
environment_slug = deployment.environment.slug environment_slug = deployment.environment.slug
......
...@@ -3,10 +3,10 @@ module Gitlab ...@@ -3,10 +3,10 @@ module Gitlab
# Helper methods to interact with Prometheus network services & resources # Helper methods to interact with Prometheus network services & resources
class PrometheusClient class PrometheusClient
attr_reader :api_url attr_reader :rest_client, :headers
def initialize(api_url:) def initialize(rest_client)
@api_url = api_url @rest_client = rest_client
end end
def ping def ping
...@@ -40,37 +40,40 @@ module Gitlab ...@@ -40,37 +40,40 @@ module Gitlab
private private
def json_api_get(type, args = {}) def json_api_get(type, args = {})
get(join_api_url(type, args)) path = ['api', 'v1', type].join('/')
get(path, args)
rescue JSON::ParserError
raise PrometheusError, 'Parsing response failed'
rescue Errno::ECONNREFUSED rescue Errno::ECONNREFUSED
raise PrometheusError, 'Connection refused' raise PrometheusError, 'Connection refused'
end end
def join_api_url(type, args = {}) def get(path, args)
url = URI.parse(api_url) response = rest_client[path].get(params: args)
rescue URI::Error handle_response(response)
raise PrometheusError, "Invalid API URL: #{api_url}"
else
url.path = [url.path.sub(%r{/+\z}, ''), 'api', 'v1', type].join('/')
url.query = args.to_query
url.to_s
end
def get(url)
handle_response(HTTParty.get(url))
rescue SocketError rescue SocketError
raise PrometheusError, "Can't connect to #{url}" raise PrometheusError, "Can't connect to #{rest_client.url}"
rescue OpenSSL::SSL::SSLError rescue OpenSSL::SSL::SSLError
raise PrometheusError, "#{url} contains invalid SSL data" raise PrometheusError, "#{rest_client.url} contains invalid SSL data"
rescue HTTParty::Error rescue RestClient::ExceptionWithResponse => ex
handle_exception_response(ex.response)
rescue RestClient::Exception
raise PrometheusError, "Network connection error" raise PrometheusError, "Network connection error"
end end
def handle_response(response) def handle_response(response)
if response.code == 200 && response['status'] == 'success' json_data = JSON.parse(response.body)
response['data'] || {} if response.code == 200 && json_data['status'] == 'success'
elsif response.code == 400 json_data['data'] || {}
raise PrometheusError, response['error'] || 'Bad data received' else
raise PrometheusError, "#{response.code} - #{response.body}"
end
end
def handle_exception_response(response)
if response.code == 400
json_data = JSON.parse(response.body)
raise PrometheusError, json_data['error'] || 'Bad data received'
else else
raise PrometheusError, "#{response.code} - #{response.body}" raise PrometheusError, "#{response.code} - #{response.body}"
end end
......
...@@ -2018,7 +2018,7 @@ msgstr "" ...@@ -2018,7 +2018,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2018,7 +2018,7 @@ msgstr "" ...@@ -2018,7 +2018,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2018,7 +2018,7 @@ msgstr "" ...@@ -2018,7 +2018,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2018,7 +2018,7 @@ msgstr "" ...@@ -2018,7 +2018,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2018,7 +2018,7 @@ msgstr "" ...@@ -2018,7 +2018,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -8,8 +8,13 @@ msgid "" ...@@ -8,8 +8,13 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gitlab 1.0.0\n" "Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
<<<<<<< HEAD
"POT-Creation-Date: 2018-02-07 14:09+0100\n" "POT-Creation-Date: 2018-02-07 14:09+0100\n"
"PO-Revision-Date: 2018-02-07 14:09+0100\n" "PO-Revision-Date: 2018-02-07 14:09+0100\n"
=======
"POT-Creation-Date: 2018-02-07 13:35+0100\n"
"PO-Revision-Date: 2018-02-07 13:35+0100\n"
>>>>>>> upstream/master
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
...@@ -638,6 +643,7 @@ msgstr "" ...@@ -638,6 +643,7 @@ msgstr ""
msgid "CiVariable|All environments" msgid "CiVariable|All environments"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "CiVariable|Create wildcard" msgid "CiVariable|Create wildcard"
msgstr "" msgstr ""
...@@ -647,6 +653,11 @@ msgstr "" ...@@ -647,6 +653,11 @@ msgstr ""
msgid "CiVariable|New environment" msgid "CiVariable|New environment"
msgstr "" msgstr ""
=======
msgid "CiVariable|Error occured while saving variables"
msgstr ""
>>>>>>> upstream/master
msgid "CiVariable|Protected" msgid "CiVariable|Protected"
msgstr "" msgstr ""
...@@ -833,9 +844,12 @@ msgstr "" ...@@ -833,9 +844,12 @@ msgstr ""
msgid "ClusterIntegration|More information" msgid "ClusterIntegration|More information"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate" msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "ClusterIntegration|Note:" msgid "ClusterIntegration|Note:"
msgstr "" msgstr ""
...@@ -1901,9 +1915,12 @@ msgstr "" ...@@ -1901,9 +1915,12 @@ msgstr ""
msgid "Login" msgid "Login"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos." msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Manage labels" msgid "Manage labels"
msgstr "" msgstr ""
...@@ -1979,9 +1996,12 @@ msgstr "" ...@@ -1979,9 +1996,12 @@ msgstr ""
msgid "Move issue" msgid "Move issue"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Multiple issue boards" msgid "Multiple issue boards"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Name new label" msgid "Name new label"
msgstr "" msgstr ""
...@@ -2044,9 +2064,12 @@ msgstr "" ...@@ -2044,9 +2064,12 @@ msgstr ""
msgid "No assignee" msgid "No assignee"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "No changes" msgid "No changes"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "No connection could be made to a Gitaly Server, please check your logs!" msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "" msgstr ""
...@@ -2509,7 +2532,7 @@ msgstr "" ...@@ -2509,7 +2532,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
...@@ -2530,9 +2553,12 @@ msgstr "" ...@@ -2530,9 +2553,12 @@ msgstr ""
msgid "Push events" msgid "Push events"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "PushRule|Committer restriction" msgid "PushRule|Committer restriction"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Quick actions can be used in the issues description and comment boxes." msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "" msgstr ""
...@@ -2753,9 +2779,12 @@ msgstr "" ...@@ -2753,9 +2779,12 @@ msgstr ""
msgid "Something went wrong trying to change the confidentiality of this issue" msgid "Something went wrong trying to change the confidentiality of this issue"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}" msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Something went wrong when toggling the button" msgid "Something went wrong when toggling the button"
msgstr "" msgstr ""
...@@ -3308,9 +3337,12 @@ msgstr[1] "" ...@@ -3308,9 +3337,12 @@ msgstr[1] ""
msgid "Time|s" msgid "Time|s"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Title" msgid "Title"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Todo" msgid "Todo"
msgstr "" msgstr ""
...@@ -3332,12 +3364,15 @@ msgstr "" ...@@ -3332,12 +3364,15 @@ msgstr ""
msgid "Total test time for all commits/merges" msgid "Total test time for all commits/merges"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "Track activity with Contribution Analytics." msgid "Track activity with Contribution Analytics."
msgstr "" msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones" msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "Track time with quick actions" msgid "Track time with quick actions"
msgstr "" msgstr ""
...@@ -3650,6 +3685,7 @@ msgstr "" ...@@ -3650,6 +3685,7 @@ msgstr ""
msgid "branch name" msgid "branch name"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "by" msgid "by"
msgstr "" msgstr ""
...@@ -3701,6 +3737,8 @@ msgstr "" ...@@ -3701,6 +3737,8 @@ msgstr ""
msgid "commit" msgid "commit"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue." msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
msgstr "" msgstr ""
...@@ -3878,9 +3916,12 @@ msgstr "" ...@@ -3878,9 +3916,12 @@ msgstr ""
msgid "spendCommand|%{slash_command} will update the sum of the time spent." msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "to help your contributors communicate effectively!" msgid "to help your contributors communicate effectively!"
msgstr "" msgstr ""
=======
>>>>>>> upstream/master
msgid "username" msgid "username"
msgstr "" msgstr ""
......
...@@ -2018,7 +2018,7 @@ msgstr "Nessuna metrica è stata monitorata. Per iniziare a monitorare, rilascia ...@@ -2018,7 +2018,7 @@ msgstr "Nessuna metrica è stata monitorata. Per iniziare a monitorare, rilascia
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2004,7 +2004,7 @@ msgstr "" ...@@ -2004,7 +2004,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2004,7 +2004,7 @@ msgstr "" ...@@ -2004,7 +2004,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2018,7 +2018,7 @@ msgstr "" ...@@ -2018,7 +2018,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2032,7 +2032,7 @@ msgstr "" ...@@ -2032,7 +2032,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2018,7 +2018,7 @@ msgstr "Nenhuma métrica está sendo monitorada. Para inicar o monitoramento, fa ...@@ -2018,7 +2018,7 @@ msgstr "Nenhuma métrica está sendo monitorada. Para inicar o monitoramento, fa
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "URL da API base do Prometheus. como http://prometheus.example.com/" msgstr "URL da API base do Prometheus. como http://prometheus.example.com/"
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "Monitoramento com Prometheus" msgstr "Monitoramento com Prometheus"
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2032,7 +2032,7 @@ msgstr "" ...@@ -2032,7 +2032,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2032,7 +2032,7 @@ msgstr "Жодні метрики не відслідковуються. Для ...@@ -2032,7 +2032,7 @@ msgstr "Жодні метрики не відслідковуються. Для
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "Базова адреса Prometheus API, наприклад http://prometheus.example.com/" msgstr "Базова адреса Prometheus API, наприклад http://prometheus.example.com/"
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "Моніторинг Prometheus" msgstr "Моніторинг Prometheus"
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2004,7 +2004,7 @@ msgstr "没有监测指标。要开始监测,请部署到环境中。" ...@@ -2004,7 +2004,7 @@ msgstr "没有监测指标。要开始监测,请部署到环境中。"
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "Prometheus API 地址,例如 http://prometheus.example.com/" msgstr "Prometheus API 地址,例如 http://prometheus.example.com/"
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "Prometheus 监测" msgstr "Prometheus 监测"
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2004,7 +2004,7 @@ msgstr "" ...@@ -2004,7 +2004,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -2004,7 +2004,7 @@ msgstr "" ...@@ -2004,7 +2004,7 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/" msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr "" msgstr ""
msgid "PrometheusService|Prometheus monitoring" msgid "PrometheusService|Time-series monitoring service"
msgstr "" msgstr ""
msgid "PrometheusService|View environments" msgid "PrometheusService|View environments"
......
...@@ -297,7 +297,8 @@ FactoryBot.define do ...@@ -297,7 +297,8 @@ FactoryBot.define do
project.create_prometheus_service( project.create_prometheus_service(
active: true, active: true,
properties: { properties: {
api_url: 'https://prometheus.example.com' api_url: 'https://prometheus.example.com/',
manual_configuration: true
} }
) )
end end
......
...@@ -30,7 +30,8 @@ FactoryBot.define do ...@@ -30,7 +30,8 @@ FactoryBot.define do
project project
active true active true
properties({ properties({
api_url: 'https://prometheus.example.com/' api_url: 'https://prometheus.example.com/',
manual_configuration: true
}) })
end end
......
require 'spec_helper' require 'spec_helper'
# Remove skip_gitaly_mock flag when gitaly_update_page implements moving pages describe 'User views a wiki page' do
describe 'User views a wiki page', :skip_gitaly_mock do shared_examples 'wiki page user view' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
let(:wiki_page) do let(:wiki_page) do
create(:wiki_page, create(:wiki_page,
wiki: project.wiki, wiki: project.wiki,
attrs: { title: 'home', content: 'Look at this [image](image.jpg)\n\n ![alt text](image.jpg)' }) attrs: { title: 'home', content: 'Look at this [image](image.jpg)\n\n ![alt text](image.jpg)' })
end end
before do
project.add_master(user)
sign_in(user)
end
context 'when wiki is empty' do
before do before do
visit(project_wikis_path(project)) project.add_master(user)
sign_in(user)
end
click_on('New page') context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
page.within('#modal-new-wiki') do click_on('New page')
fill_in(:new_wiki_path, with: 'one/two/three-test')
click_on('Create page')
end
page.within('.wiki-form') do page.within('#modal-new-wiki') do
fill_in(:wiki_content, with: 'wiki content') fill_in(:new_wiki_path, with: 'one/two/three-test')
click_on('Create page') click_on('Create page')
end
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'wiki content')
click_on('Create page')
end
end end
end
it 'shows the history of a page that has a path', :js do it 'shows the history of a page that has a path', :js do
expect(current_path).to include('one/two/three-test') expect(current_path).to include('one/two/three-test')
first(:link, text: 'Three').click first(:link, text: 'Three').click
click_on('Page history') click_on('Page history')
expect(current_path).to include('one/two/three-test') expect(current_path).to include('one/two/three-test')
page.within(:css, '.nav-text') do page.within(:css, '.nav-text') do
expect(page).to have_content('History') expect(page).to have_content('History')
end
end end
end
it 'shows an old version of a page', :js do it 'shows an old version of a page', :js do
expect(current_path).to include('one/two/three-test') expect(current_path).to include('one/two/three-test')
expect(find('.wiki-pages')).to have_content('Three') expect(find('.wiki-pages')).to have_content('Three')
first(:link, text: 'Three').click first(:link, text: 'Three').click
expect(find('.nav-text')).to have_content('Three') expect(find('.nav-text')).to have_content('Three')
click_on('Edit') click_on('Edit')
expect(current_path).to include('one/two/three-test') expect(current_path).to include('one/two/three-test')
expect(page).to have_content('Edit Page') expect(page).to have_content('Edit Page')
fill_in('Content', with: 'Updated Wiki Content') fill_in('Content', with: 'Updated Wiki Content')
click_on('Save changes') click_on('Save changes')
click_on('Page history') click_on('Page history')
page.within(:css, '.nav-text') do page.within(:css, '.nav-text') do
expect(page).to have_content('History') expect(page).to have_content('History')
end end
find('a[href*="?version_id"]') find('a[href*="?version_id"]')
end
end end
end
context 'when a page does not have history' do context 'when a page does not have history' do
before do before do
visit(project_wiki_path(project, wiki_page)) visit(project_wiki_path(project, wiki_page))
end end
it 'shows all the pages' do it 'shows all the pages' do
expect(page).to have_content(user.name) expect(page).to have_content(user.name)
expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize) expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize)
end end
it 'shows a file stored in a page' do it 'shows a file stored in a page' do
gollum_file_double = double('Gollum::File', gollum_file_double = double('Gollum::File',
mime_type: 'image/jpeg', mime_type: 'image/jpeg',
name: 'images/image.jpg', name: 'images/image.jpg',
path: 'images/image.jpg', path: 'images/image.jpg',
raw_data: '') raw_data: '')
wiki_file = Gitlab::Git::WikiFile.new(gollum_file_double) wiki_file = Gitlab::Git::WikiFile.new(gollum_file_double)
allow(wiki_file).to receive(:mime_type).and_return('image/jpeg') allow(wiki_file).to receive(:mime_type).and_return('image/jpeg')
allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file) allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file)
expect(page).to have_xpath('//img[@data-src="image.jpg"]') expect(page).to have_xpath('//img[@data-src="image.jpg"]')
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg") expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
click_on('image') click_on('image')
expect(current_path).to match('wikis/image.jpg') expect(current_path).to match('wikis/image.jpg')
expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
end end
it 'shows the creation page if file does not exist' do it 'shows the creation page if file does not exist' do
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg") expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
click_on('image') click_on('image')
expect(current_path).to match('wikis/image.jpg') expect(current_path).to match('wikis/image.jpg')
expect(page).to have_content('New Wiki Page') expect(page).to have_content('New Wiki Page')
expect(page).to have_content('Create page') expect(page).to have_content('Create page')
end
end end
end
context 'when a page has history' do context 'when a page has history' do
before do before do
wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)') wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)')
end end
it 'shows the page history' do it 'shows the page history' do
visit(project_wiki_path(project, wiki_page)) visit(project_wiki_path(project, wiki_page))
expect(page).to have_selector('a.btn', text: 'Edit') expect(page).to have_selector('a.btn', text: 'Edit')
click_on('Page history') click_on('Page history')
expect(page).to have_content(user.name) expect(page).to have_content(user.name)
expect(page).to have_content("#{user.username} created page: home") expect(page).to have_content("#{user.username} created page: home")
expect(page).to have_content('updated home') expect(page).to have_content('updated home')
end
it 'does not show the "Edit" button' do
visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id))
expect(page).not_to have_selector('a.btn', text: 'Edit')
end
end end
it 'does not show the "Edit" button' do it 'opens a default wiki page', :js do
visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id)) visit(project_path(project))
expect(page).not_to have_selector('a.btn', text: 'Edit') find('.shortcuts-wiki').click
expect(page).to have_content('Home · Create Page')
end end
end end
it 'opens a default wiki page', :js do context 'when Gitaly is enabled' do
visit(project_path(project)) it_behaves_like 'wiki page user view'
end
find('.shortcuts-wiki').click
expect(page).to have_content('Home · Create Page') context 'when Gitaly is disabled', :skip_gitaly_mock do
it_behaves_like 'wiki page user view'
end end
end end
...@@ -7,10 +7,10 @@ describe GraphHelper do ...@@ -7,10 +7,10 @@ describe GraphHelper do
let(:graph) { Network::Graph.new(project, 'master', commit, '') } let(:graph) { Network::Graph.new(project, 'master', commit, '') }
it 'filters our refs used by GitLab' do it 'filters our refs used by GitLab' do
allow(commit).to receive(:ref_names).and_return(['refs/merge-requests/abc', 'master', 'refs/tmp/xyz'])
self.instance_variable_set(:@graph, graph) self.instance_variable_set(:@graph, graph)
refs = get_refs(project.repository, commit) refs = refs(project.repository, commit)
expect(refs).to eq('master')
expect(refs).to match('master')
end end
end end
end end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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