Commit 22a9f43d authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into ce_upstream[ci skip]

parents 67e51e9c b22d4c2e
...@@ -376,8 +376,15 @@ trigger_docs: ...@@ -376,8 +376,15 @@ trigger_docs:
cache: {} cache: {}
artifacts: {} artifacts: {}
script: script:
<<<<<<< HEAD
- "curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master -F variables[PROJECT]=ee https://gitlab.com/api/v3/projects/1794617/trigger/builds" - "curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master -F variables[PROJECT]=ee https://gitlab.com/api/v3/projects/1794617/trigger/builds"
only: only:
=======
- "HTTP_STATUS=$(curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master -F variables[PROJECT]=${CI_PROJECT_NAME} --silent --output curl.log --write-out '%{http_code}' https://gitlab.com/api/v3/projects/1794617/trigger/builds)"
- if [ "${HTTP_STATUS}" -ne "201" ]; then echo "Error ${HTTP_STATUS}"; cat curl.log; echo; exit 1; fi
only:
- master@gitlab-org/gitlab-ce
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
- master@gitlab-org/gitlab-ee - master@gitlab-org/gitlab-ee
# Notify slack in the end # Notify slack in the end
......
...@@ -217,6 +217,14 @@ entry. ...@@ -217,6 +217,14 @@ entry.
- Requeue pending deletion projects. - Requeue pending deletion projects.
## 8.16.8 (2017-03-19) ## 8.16.8 (2017-03-19)
<<<<<<< HEAD
=======
- Only show public emails in atom feeds.
- To protect against Server-side Request Forgery project import URLs are now prohibited against localhost or the server IP except for the assigned instance URL and port. Imports are also prohibited from ports below 1024 with the exception of ports 22, 80, and 443.
## 8.16.7 (2017-02-27)
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
- No changes. - No changes.
- No changes. - No changes.
...@@ -425,9 +433,12 @@ entry. ...@@ -425,9 +433,12 @@ entry.
## 8.15.8 (2017-03-19) ## 8.15.8 (2017-03-19)
<<<<<<< HEAD
- No changes. - No changes.
- No changes. - No changes.
- Read true-up info from license and validate it. !1159 - Read true-up info from license and validate it. !1159
=======
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
- Only show public emails in atom feeds. - Only show public emails in atom feeds.
- To protect against Server-side Request Forgery project import URLs are now prohibited against localhost or the server IP except for the assigned instance URL and port. Imports are also prohibited from ports below 1024 with the exception of ports 22, 80, and 443. - To protect against Server-side Request Forgery project import URLs are now prohibited against localhost or the server IP except for the assigned instance URL and port. Imports are also prohibited from ports below 1024 with the exception of ports 22, 80, and 443.
......
...@@ -64,6 +64,7 @@ require('./empty_state'); ...@@ -64,6 +64,7 @@ require('./empty_state');
}, },
filter: { filter: {
handler() { handler() {
this.page = 1;
this.loadIssues(true); this.loadIssues(true);
}, },
deep: true, deep: true,
...@@ -115,6 +116,9 @@ require('./empty_state'); ...@@ -115,6 +116,9 @@ require('./empty_state');
return this.activeTab === 'selected' && this.selectedIssues.length === 0; return this.activeTab === 'selected' && this.selectedIssues.length === 0;
}, },
}, },
created() {
this.page = 1;
},
components: { components: {
'modal-header': gl.issueBoards.ModalHeader, 'modal-header': gl.issueBoards.ModalHeader,
'modal-list': gl.issueBoards.ModalList, 'modal-list': gl.issueBoards.ModalList,
......
import PrometheusGraph from './monitoring/prometheus_graph'; // TODO: Maybe Make this a bundle
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
/* global UsernameValidator */ /* global UsernameValidator */
/* global ActiveTabMemoizer */ /* global ActiveTabMemoizer */
...@@ -341,8 +340,6 @@ const UserCallout = require('./user_callout'); ...@@ -341,8 +340,6 @@ const UserCallout = require('./user_callout');
case 'ci:lints:show': case 'ci:lints:show':
new gl.CILintEditor(); new gl.CILintEditor();
break; break;
case 'projects:environments:metrics':
new PrometheusGraph();
case 'users:show': case 'users:show':
new UserCallout(); new UserCallout();
break; break;
......
...@@ -14,6 +14,7 @@ export default { ...@@ -14,6 +14,7 @@ export default {
class="btn external_url" class="btn external_url"
:href="externalUrl" :href="externalUrl"
target="_blank" target="_blank"
rel="noopener noreferrer"
title="Environment external URL"> title="Environment external URL">
<i class="fa fa-external-link" aria-hidden="true"></i> <i class="fa fa-external-link" aria-hidden="true"></i>
</a> </a>
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true); gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true);
} }
this.resetFilters();
this.dismissDropdown(); this.dismissDropdown();
this.dispatchInputEvent(); this.dispatchInputEvent();
} }
...@@ -107,10 +108,14 @@ ...@@ -107,10 +108,14 @@
const hook = this.getCurrentHook(); const hook = this.getCurrentHook();
if (hook) { if (hook) {
<<<<<<< HEAD
const data = hook.list.data; const data = hook.list.data;
if (!data) return; if (!data) return;
=======
const data = hook.list.data || [];
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
const results = data.map((o) => { const results = data.map((o) => {
const updated = o; const updated = o;
updated.droplab_hidden = false; updated.droplab_hidden = false;
......
...@@ -44,6 +44,8 @@ import FilteredSearchContainer from './container'; ...@@ -44,6 +44,8 @@ import FilteredSearchContainer from './container';
this.unselectEditTokensWrapper = this.unselectEditTokens.bind(this); this.unselectEditTokensWrapper = this.unselectEditTokens.bind(this);
this.editTokenWrapper = this.editToken.bind(this); this.editTokenWrapper = this.editToken.bind(this);
this.tokenChange = this.tokenChange.bind(this); this.tokenChange = this.tokenChange.bind(this);
this.addInputContainerFocusWrapper = this.addInputContainerFocus.bind(this);
this.removeInputContainerFocusWrapper = this.removeInputContainerFocus.bind(this);
this.filteredSearchInputForm = this.filteredSearchInput.form; this.filteredSearchInputForm = this.filteredSearchInput.form;
this.filteredSearchInputForm.addEventListener('submit', this.handleFormSubmit); this.filteredSearchInputForm.addEventListener('submit', this.handleFormSubmit);
...@@ -55,11 +57,13 @@ import FilteredSearchContainer from './container'; ...@@ -55,11 +57,13 @@ import FilteredSearchContainer from './container';
this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper); this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper);
this.filteredSearchInput.addEventListener('click', this.tokenChange); this.filteredSearchInput.addEventListener('click', this.tokenChange);
this.filteredSearchInput.addEventListener('keyup', this.tokenChange); this.filteredSearchInput.addEventListener('keyup', this.tokenChange);
this.filteredSearchInput.addEventListener('focus', this.addInputContainerFocusWrapper);
this.tokensContainer.addEventListener('click', FilteredSearchManager.selectToken); this.tokensContainer.addEventListener('click', FilteredSearchManager.selectToken);
this.tokensContainer.addEventListener('dblclick', this.editTokenWrapper); this.tokensContainer.addEventListener('dblclick', this.editTokenWrapper);
this.clearSearchButton.addEventListener('click', this.clearSearchWrapper); this.clearSearchButton.addEventListener('click', this.clearSearchWrapper);
document.addEventListener('click', gl.FilteredSearchVisualTokens.unselectTokens); document.addEventListener('click', gl.FilteredSearchVisualTokens.unselectTokens);
document.addEventListener('click', this.unselectEditTokensWrapper); document.addEventListener('click', this.unselectEditTokensWrapper);
document.addEventListener('click', this.removeInputContainerFocusWrapper);
document.addEventListener('keydown', this.removeSelectedTokenWrapper); document.addEventListener('keydown', this.removeSelectedTokenWrapper);
} }
...@@ -73,11 +77,13 @@ import FilteredSearchContainer from './container'; ...@@ -73,11 +77,13 @@ import FilteredSearchContainer from './container';
this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper); this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper);
this.filteredSearchInput.removeEventListener('click', this.tokenChange); this.filteredSearchInput.removeEventListener('click', this.tokenChange);
this.filteredSearchInput.removeEventListener('keyup', this.tokenChange); this.filteredSearchInput.removeEventListener('keyup', this.tokenChange);
this.filteredSearchInput.removeEventListener('focus', this.addInputContainerFocusWrapper);
this.tokensContainer.removeEventListener('click', FilteredSearchManager.selectToken); this.tokensContainer.removeEventListener('click', FilteredSearchManager.selectToken);
this.tokensContainer.removeEventListener('dblclick', this.editTokenWrapper); this.tokensContainer.removeEventListener('dblclick', this.editTokenWrapper);
this.clearSearchButton.removeEventListener('click', this.clearSearchWrapper); this.clearSearchButton.removeEventListener('click', this.clearSearchWrapper);
document.removeEventListener('click', gl.FilteredSearchVisualTokens.unselectTokens); document.removeEventListener('click', gl.FilteredSearchVisualTokens.unselectTokens);
document.removeEventListener('click', this.unselectEditTokensWrapper); document.removeEventListener('click', this.unselectEditTokensWrapper);
document.removeEventListener('click', this.removeInputContainerFocusWrapper);
document.removeEventListener('keydown', this.removeSelectedTokenWrapper); document.removeEventListener('keydown', this.removeSelectedTokenWrapper);
} }
...@@ -130,6 +136,26 @@ import FilteredSearchContainer from './container'; ...@@ -130,6 +136,26 @@ import FilteredSearchContainer from './container';
} }
} }
addInputContainerFocus() {
const inputContainer = this.filteredSearchInput.closest('.filtered-search-input-container');
if (inputContainer) {
inputContainer.classList.add('focus');
}
}
removeInputContainerFocus(e) {
const inputContainer = this.filteredSearchInput.closest('.filtered-search-input-container');
const isElementInFilteredSearch = inputContainer && inputContainer.contains(e.target);
const isElementInDynamicFilterDropdown = e.target.closest('.filter-dropdown') !== null;
const isElementInStaticFilterDropdown = e.target.closest('ul[data-dropdown]') !== null;
if (!isElementInFilteredSearch && !isElementInDynamicFilterDropdown &&
!isElementInStaticFilterDropdown && inputContainer) {
inputContainer.classList.remove('focus');
}
}
static selectToken(e) { static selectToken(e) {
const button = e.target.closest('.selectable'); const button = e.target.closest('.selectable');
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* global Flash */ /* global Flash */
require('./flash'); require('./flash');
require('~/lib/utils/text_utility');
require('vendor/jquery.waitforimages'); require('vendor/jquery.waitforimages');
require('./task_list'); require('./task_list');
...@@ -50,20 +51,21 @@ class Issue { ...@@ -50,20 +51,21 @@ class Issue {
success: function(data, textStatus, jqXHR) { success: function(data, textStatus, jqXHR) {
if ('id' in data) { if ('id' in data) {
$(document).trigger('issuable:change'); $(document).trigger('issuable:change');
const currentTotal = Number($('.issue_counter').text()); let total = Number($('.issue_counter').text().replace(/[^\d]/, ''));
if (isClose) { if (isClose) {
$('a.btn-close').addClass('hidden'); $('a.btn-close').addClass('hidden');
$('a.btn-reopen').removeClass('hidden'); $('a.btn-reopen').removeClass('hidden');
$('div.status-box-closed').removeClass('hidden'); $('div.status-box-closed').removeClass('hidden');
$('div.status-box-open').addClass('hidden'); $('div.status-box-open').addClass('hidden');
$('.issue_counter').text(currentTotal - 1); total -= 1;
} else { } else {
$('a.btn-reopen').addClass('hidden'); $('a.btn-reopen').addClass('hidden');
$('a.btn-close').removeClass('hidden'); $('a.btn-close').removeClass('hidden');
$('div.status-box-closed').addClass('hidden'); $('div.status-box-closed').addClass('hidden');
$('div.status-box-open').removeClass('hidden'); $('div.status-box-open').removeClass('hidden');
$('.issue_counter').text(currentTotal + 1); total += 1;
} }
$('.issue_counter').text(gl.text.addDelimiter(total));
} else { } else {
new Flash(issueFailMessage, 'alert'); new Flash(issueFailMessage, 'alert');
} }
......
...@@ -14,13 +14,13 @@ import MiniPipelineGraph from './mini_pipeline_graph_dropdown'; ...@@ -14,13 +14,13 @@ import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
<%= ci_success_icon %> <%= ci_success_icon %>
<span> <span>
Deployed to Deployed to
<a href="<%- url %>" target="_blank" class="environment"> <a href="<%- url %>" target="_blank" rel="noopener noreferrer" class="environment">
<%- name %> <%- name %>
</a> </a>
<span class="js-environment-timeago" data-toggle="tooltip" data-placement="top" data-title="<%- deployed_at_formatted %>"> <span class="js-environment-timeago" data-toggle="tooltip" data-placement="top" data-title="<%- deployed_at_formatted %>">
<%- deployed_at %> <%- deployed_at %>
</span> </span>
<a class="js-environment-link" href="<%- external_url %>" target="_blank"> <a class="js-environment-link" href="<%- external_url %>" target="_blank" rel="noopener noreferrer">
<i class="fa fa-external-link"></i> <i class="fa fa-external-link"></i>
View on <%- external_url_formatted %> View on <%- external_url_formatted %>
</a> </a>
......
import PrometheusGraph from './prometheus_graph';
document.addEventListener('DOMContentLoaded', function onLoad() {
document.removeEventListener('DOMContentLoaded', onLoad, false);
return new PrometheusGraph();
}, false);
...@@ -2,10 +2,9 @@ ...@@ -2,10 +2,9 @@
/* global Flash */ /* global Flash */
import d3 from 'd3'; import d3 from 'd3';
import _ from 'underscore';
import statusCodes from '~/lib/utils/http_status'; import statusCodes from '~/lib/utils/http_status';
import '~/lib/utils/common_utils'; import '../lib/utils/common_utils';
import '~/flash'; import '../flash';
const prometheusGraphsContainer = '.prometheus-graph'; const prometheusGraphsContainer = '.prometheus-graph';
const metricsEndpoint = 'metrics.json'; const metricsEndpoint = 'metrics.json';
...@@ -31,22 +30,21 @@ class PrometheusGraph { ...@@ -31,22 +30,21 @@ class PrometheusGraph {
} }
createGraph() { createGraph() {
const self = this; Object.keys(this.data).forEach((key) => {
_.each(this.data, (value, key) => { const value = this.data[key];
if (value.length > 0 && (key === 'cpu_values' || key === 'memory_values')) { if (value.length > 0) {
self.plotValues(value, key); this.plotValues(value, key);
} }
}); });
} }
init() { init() {
const self = this;
this.getData().then((metricsResponse) => { this.getData().then((metricsResponse) => {
if (metricsResponse === {}) { if (Object.keys(metricsResponse).length === 0) {
new Flash('Empty metrics', 'alert'); new Flash('Empty metrics', 'alert');
} else { } else {
self.transformData(metricsResponse); this.transformData(metricsResponse);
self.createGraph(); this.createGraph();
} }
}); });
} }
...@@ -321,12 +319,14 @@ class PrometheusGraph { ...@@ -321,12 +319,14 @@ class PrometheusGraph {
transformData(metricsResponse) { transformData(metricsResponse) {
const metricTypes = {}; const metricTypes = {};
_.each(metricsResponse.metrics, (value, key) => { Object.keys(metricsResponse.metrics).forEach((key) => {
const metricValues = value[0].values; if (key === 'cpu_values' || key === 'memory_values') {
metricTypes[key] = _.map(metricValues, metric => ({ const metricValues = (metricsResponse.metrics[key])[0];
time: new Date(metric[0] * 1000), metricTypes[key] = metricValues.values.map(metric => ({
value: metric[1], time: new Date(metric[0] * 1000),
})); value: metric[1],
}));
}
}); });
this.data = metricTypes; this.data = metricTypes;
} }
......
...@@ -433,3 +433,9 @@ table { ...@@ -433,3 +433,9 @@ table {
@include str-truncated(100%); @include str-truncated(100%);
} }
} }
.tooltip {
.tooltip-inner {
word-wrap: break-word;
}
}
...@@ -76,12 +76,14 @@ ...@@ -76,12 +76,14 @@
} }
.input-token { .input-token {
flex: 1; max-width: 200px;
-webkit-flex: 1;
} }
.filtered-search-token + .input-token:not(:last-child) { .input-token:only-child,
max-width: 200px; .input-token:last-child {
flex: 1;
-webkit-flex: 1;
max-width: initial;
} }
} }
...@@ -158,8 +160,8 @@ ...@@ -158,8 +160,8 @@
background-color: $white-light; background-color: $white-light;
@media (max-width: $screen-xs-min) { @media (max-width: $screen-xs-min) {
-webkit-flex: 1 1 100%; -webkit-flex: 1 1 auto;
flex: 1 1 100%; flex: 1 1 auto;
margin-bottom: 10px; margin-bottom: 10px;
.dropdown-menu { .dropdown-menu {
...@@ -171,17 +173,26 @@ ...@@ -171,17 +173,26 @@
} }
} }
&:hover {
@extend .form-control:hover;
}
&.focus,
&.focus:hover {
border-color: $dropdown-input-focus-border;
box-shadow: 0 0 4px $search-input-focus-shadow-color;
}
&.focus .fa-filter {
color: $common-gray-dark;
}
.form-control { .form-control {
position: relative; position: relative;
min-width: 200px; min-width: 200px;
padding-left: 0; padding: 5px 25px 6px 0;
padding-right: 25px;
border-color: transparent; border-color: transparent;
&:focus ~ .fa-filter {
color: $common-gray-dark;
}
&:focus, &:focus,
&:hover { &:hover {
outline: none; outline: none;
...@@ -221,6 +232,10 @@ ...@@ -221,6 +232,10 @@
.filter-dropdown-container { .filter-dropdown-container {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
.dropdown-toggle {
line-height: 22px;
}
} }
.dropdown-menu .filter-dropdown-item { .dropdown-menu .filter-dropdown-item {
...@@ -246,7 +261,9 @@ ...@@ -246,7 +261,9 @@
background-color: $white-light; background-color: $white-light;
border-top: 0; border-top: 0;
} }
}
@media (max-width: $screen-xs) {
.filter-dropdown-container { .filter-dropdown-container {
.dropdown-toggle, .dropdown-toggle,
.dropdown { .dropdown {
......
...@@ -148,6 +148,18 @@ ...@@ -148,6 +148,18 @@
.error-alert > .alert { .error-alert > .alert {
margin-top: 5px; margin-top: 5px;
margin-bottom: 5px; margin-bottom: 5px;
&.alert-dismissable {
.close {
color: $white-light;
opacity: 0.85;
font-weight: normal;
&:hover {
opacity: 1;
}
}
}
} }
.discussion-body, .discussion-body,
......
...@@ -89,4 +89,9 @@ class Projects::ApplicationController < ApplicationController ...@@ -89,4 +89,9 @@ class Projects::ApplicationController < ApplicationController
def builds_enabled def builds_enabled
return render_404 unless @project.feature_available?(:builds, current_user) return render_404 unless @project.feature_available?(:builds, current_user)
end end
def update_ref
branch_exists = @repository.find_branch(@target_branch)
@ref = @target_branch if branch_exists
end
end end
...@@ -89,11 +89,6 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -89,11 +89,6 @@ class Projects::BlobController < Projects::ApplicationController
private private
def update_ref
branch_exists = @repository.find_branch(@target_branch)
@ref = @target_branch if branch_exists
end
def blob def blob
@blob ||= Blob.decorate(@repository.blob_at(@commit.id, @path)) @blob ||= Blob.decorate(@repository.blob_at(@commit.id, @path))
......
...@@ -6,7 +6,11 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -6,7 +6,11 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuableCollections include IssuableCollections
include SpammableActions include SpammableActions
<<<<<<< HEAD
prepend_before_action :authenticate_user!, only: [:export_csv] prepend_before_action :authenticate_user!, only: [:export_csv]
=======
prepend_before_action :authenticate_user!, only: [:new]
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
before_action :redirect_to_external_issue_tracker, only: [:index, :new] before_action :redirect_to_external_issue_tracker, only: [:index, :new]
before_action :module_enabled before_action :module_enabled
...@@ -154,7 +158,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -154,7 +158,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
format.json do format.json do
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short]) render json: @issue.to_json(include: { milestone: {}, assignee: { only: [:name, :username], methods: [:avatar_url] }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end end
end end
......
...@@ -319,7 +319,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -319,7 +319,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
format.json do format.json do
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short]) render json: @merge_request.to_json(include: { milestone: {}, assignee: { only: [:name, :username], methods: [:avatar_url] }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end end
end end
rescue ActiveRecord::StaleObjectError rescue ActiveRecord::StaleObjectError
......
...@@ -9,6 +9,7 @@ module Projects ...@@ -9,6 +9,7 @@ module Projects
@skip_groups = @group_links.pluck(:group_id) @skip_groups = @group_links.pluck(:group_id)
@skip_groups << @project.namespace_id unless @project.personal? @skip_groups << @project.namespace_id unless @project.personal?
@skip_groups += @project.group.ancestors.pluck(:id) if @project.group
@project_members = MembersFinder.new(@project, current_user).execute @project_members = MembersFinder.new(@project, current_user).execute
......
...@@ -34,6 +34,7 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -34,6 +34,7 @@ class Projects::TreeController < Projects::ApplicationController
def create_dir def create_dir
return render_404 unless @commit_params.values.all? return render_404 unless @commit_params.values.all?
update_ref
create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.", create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.",
success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@target_branch, @dir_name)), success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@target_branch, @dir_name)),
failure_path: namespace_project_tree_path(@project.namespace, @project, @ref)) failure_path: namespace_project_tree_path(@project.namespace, @project, @ref))
......
...@@ -215,6 +215,6 @@ module BlobHelper ...@@ -215,6 +215,6 @@ module BlobHelper
end end
def open_raw_file_button(path) def open_raw_file_button(path)
link_to icon('file-code-o'), path, class: 'btn btn-sm has-tooltip', target: '_blank', title: 'Open raw', data: { container: 'body' } link_to icon('file-code-o'), path, class: 'btn btn-sm has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw', data: { container: 'body' }
end end
end end
...@@ -211,7 +211,7 @@ module CommitsHelper ...@@ -211,7 +211,7 @@ module CommitsHelper
external_url = environment.external_url_for(diff_new_path, commit_sha) external_url = environment.external_url_for(diff_new_path, commit_sha)
return unless external_url return unless external_url
link_to(external_url, class: 'btn btn-file-option has-tooltip', target: '_blank', title: "View on #{environment.formatted_external_url}", data: { container: 'body' }) do link_to(external_url, class: 'btn btn-file-option has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: "View on #{environment.formatted_external_url}", data: { container: 'body' }) do
icon('external-link') icon('external-link')
end end
end end
......
...@@ -7,7 +7,7 @@ module ImportHelper ...@@ -7,7 +7,7 @@ module ImportHelper
def provider_project_link(provider, path_with_namespace) def provider_project_link(provider, path_with_namespace)
url = __send__("#{provider}_project_url", path_with_namespace) url = __send__("#{provider}_project_url", path_with_namespace)
link_to path_with_namespace, url, target: '_blank' link_to path_with_namespace, url, target: '_blank', rel: 'noopener noreferrer'
end end
private private
......
...@@ -182,6 +182,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -182,6 +182,8 @@ class ApplicationSetting < ActiveRecord::Base
end end
def self.current def self.current
ensure_cache_setup
Rails.cache.fetch(CACHE_KEY) do Rails.cache.fetch(CACHE_KEY) do
ApplicationSetting.last ApplicationSetting.last
end end
...@@ -195,9 +197,16 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -195,9 +197,16 @@ class ApplicationSetting < ActiveRecord::Base
end end
def self.cached def self.cached
ensure_cache_setup
Rails.cache.fetch(CACHE_KEY) Rails.cache.fetch(CACHE_KEY)
end end
def self.ensure_cache_setup
# This is a workaround for a Rails bug that causes attribute methods not
# to be loaded when read from cache: https://github.com/rails/rails/issues/27348
ApplicationSetting.define_attribute_methods
end
def self.defaults_ce def self.defaults_ce
{ {
after_sign_up_text: nil, after_sign_up_text: nil,
......
...@@ -31,6 +31,7 @@ module HasStatus ...@@ -31,6 +31,7 @@ module HasStatus
WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending' WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
WHEN (#{running})+(#{pending})>0 THEN 'running' WHEN (#{running})+(#{pending})>0 THEN 'running'
WHEN (#{manual})>0 THEN 'manual' WHEN (#{manual})>0 THEN 'manual'
WHEN (#{created})>0 THEN 'running'
ELSE 'failed' ELSE 'failed'
END)" END)"
end end
......
...@@ -48,12 +48,14 @@ module Issuable ...@@ -48,12 +48,14 @@ module Issuable
delegate :name, delegate :name,
:email, :email,
:public_email,
to: :author, to: :author,
allow_nil: true, allow_nil: true,
prefix: true prefix: true
delegate :name, delegate :name,
:email, :email,
:public_email,
to: :assignee, to: :assignee,
allow_nil: true, allow_nil: true,
prefix: true prefix: true
......
...@@ -16,7 +16,7 @@ class Event < ActiveRecord::Base ...@@ -16,7 +16,7 @@ class Event < ActiveRecord::Base
RESET_PROJECT_ACTIVITY_INTERVAL = 1.hour RESET_PROJECT_ACTIVITY_INTERVAL = 1.hour
delegate :name, :email, to: :author, prefix: true, allow_nil: true delegate :name, :email, :public_email, to: :author, prefix: true, allow_nil: true
delegate :title, to: :issue, prefix: true, allow_nil: true delegate :title, to: :issue, prefix: true, allow_nil: true
delegate :title, to: :merge_request, prefix: true, allow_nil: true delegate :title, to: :merge_request, prefix: true, allow_nil: true
delegate :title, to: :note, prefix: true, allow_nil: true delegate :title, to: :note, prefix: true, allow_nil: true
......
...@@ -251,7 +251,7 @@ class Group < Namespace ...@@ -251,7 +251,7 @@ class Group < Namespace
end end
def members_with_parents def members_with_parents
GroupMember.non_request.where(source_id: ancestors.map(&:id).push(id)) GroupMember.non_request.where(source_id: ancestors.pluck(:id).push(id))
end end
def users_with_parents def users_with_parents
......
...@@ -205,6 +205,7 @@ class Project < ActiveRecord::Base ...@@ -205,6 +205,7 @@ class Project < ActiveRecord::Base
validates :name, uniqueness: { scope: :namespace_id } validates :name, uniqueness: { scope: :namespace_id }
validates :path, uniqueness: { scope: :namespace_id } validates :path, uniqueness: { scope: :namespace_id }
validates :import_url, addressable_url: true, if: :external_import? validates :import_url, addressable_url: true, if: :external_import?
validates :import_url, importable_url: true, if: [:external_import?, :import_url_changed?]
validates :star_count, numericality: { greater_than_or_equal_to: 0 } validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create validate :check_limit, on: :create
validate :avatar_type, validate :avatar_type,
...@@ -996,11 +997,14 @@ class Project < ActiveRecord::Base ...@@ -996,11 +997,14 @@ class Project < ActiveRecord::Base
credentials = Gitlab::UrlSanitizer.http_credentials_for_user(user) credentials = Gitlab::UrlSanitizer.http_credentials_for_user(user)
Gitlab::UrlSanitizer.new("#{web_url}.git", credentials: credentials).full_url Gitlab::UrlSanitizer.new("#{web_url}.git", credentials: credentials).full_url
<<<<<<< HEAD
end end
# No need to have a Kerberos Web url. Kerberos URL will be used only to clone # No need to have a Kerberos Web url. Kerberos URL will be used only to clone
def kerberos_url_to_repo def kerberos_url_to_repo
"#{Gitlab.config.build_gitlab_kerberos_url + Gitlab::Application.routes.url_helpers.namespace_project_path(self.namespace, self)}.git" "#{Gitlab.config.build_gitlab_kerberos_url + Gitlab::Application.routes.url_helpers.namespace_project_path(self.namespace, self)}.git"
=======
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
end end
# Check if current branch name is marked as protected in the system # Check if current branch name is marked as protected in the system
......
...@@ -30,7 +30,14 @@ class PrometheusService < MonitoringService ...@@ -30,7 +30,14 @@ class PrometheusService < MonitoringService
end end
def help def help
'Retrieves `container_cpu_usage_seconds_total` and `container_memory_usage_bytes` from the configured Prometheus server. An `environment` label is required on each metric to identify the Environment.' <<-MD.strip_heredoc
Retrieves the Kubernetes node metrics `container_cpu_usage_seconds_total`
and `container_memory_usage_bytes` from the configured Prometheus server.
If you are not using [Auto-Deploy](https://docs.gitlab.com/ee/ci/autodeploy/index.html)
or have set up your own Prometheus server, an `environment` label is required on each metric to
[identify the Environment](https://docs.gitlab.com/ce/user/project/integrations/prometheus.html#metrics-and-labels).
MD
end end
def self.to_param def self.to_param
......
...@@ -49,11 +49,14 @@ class ProjectWiki ...@@ -49,11 +49,14 @@ class ProjectWiki
credentials = Gitlab::UrlSanitizer.http_credentials_for_user(user) credentials = Gitlab::UrlSanitizer.http_credentials_for_user(user)
Gitlab::UrlSanitizer.new(url, credentials: credentials).full_url Gitlab::UrlSanitizer.new(url, credentials: credentials).full_url
<<<<<<< HEAD
end end
# No need to have a Kerberos Web url. Kerberos URL will be used only to clone # No need to have a Kerberos Web url. Kerberos URL will be used only to clone
def kerberos_url_to_repo def kerberos_url_to_repo
[Gitlab.config.build_gitlab_kerberos_url, "/", path_with_namespace, ".git"].join('') [Gitlab.config.build_gitlab_kerberos_url, "/", path_with_namespace, ".git"].join('')
=======
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
end end
def wiki_base_path def wiki_base_path
......
...@@ -21,7 +21,7 @@ class Route < ActiveRecord::Base ...@@ -21,7 +21,7 @@ class Route < ActiveRecord::Base
attributes[:path] = route.path.sub(path_was, path) attributes[:path] = route.path.sub(path_was, path)
end end
if name_changed? && route.name.present? if name_changed? && name_was.present? && route.name.present?
attributes[:name] = route.name.sub(name_was, name) attributes[:name] = route.name.sub(name_was, name)
end end
......
...@@ -25,12 +25,12 @@ class CreateBranchService < BaseService ...@@ -25,12 +25,12 @@ class CreateBranchService < BaseService
private private
def create_master_branch def create_master_branch
project.repository.commit_file( project.repository.create_file(
current_user, current_user,
'/README.md', '/README.md',
'', '',
message: 'Add README.md', message: 'Add README.md',
branch_name: 'master', branch_name: 'master'
update: false) )
end end
end end
...@@ -33,6 +33,7 @@ module Projects ...@@ -33,6 +33,7 @@ module Projects
def import_repository def import_repository
begin begin
raise Error, "Blocked import URL." if Gitlab::UrlBlocker.blocked_url?(project.import_url)
gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url) gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
rescue => e rescue => e
# Expire cache to prevent scenarios such as: # Expire cache to prevent scenarios such as:
...@@ -40,7 +41,7 @@ module Projects ...@@ -40,7 +41,7 @@ module Projects
# 2. Retried import, repo is broken or not imported but +exists?+ still returns true # 2. Retried import, repo is broken or not imported but +exists?+ still returns true
project.repository.before_import if project.repository_exists? project.repository.before_import if project.repository_exists?
raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}" raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
end end
end end
......
...@@ -24,10 +24,9 @@ class SystemHooksService ...@@ -24,10 +24,9 @@ class SystemHooksService
key: model.key, key: model.key,
id: model.id id: model.id
) )
if model.user if model.user
data.merge!( data[:username] = model.user.username
username: model.user.username
)
end end
when Project when Project
data.merge!(project_data(model)) data.merge!(project_data(model))
...@@ -35,8 +34,6 @@ class SystemHooksService ...@@ -35,8 +34,6 @@ class SystemHooksService
if event == :rename || event == :transfer if event == :rename || event == :transfer
data[:old_path_with_namespace] = model.old_path_with_namespace data[:old_path_with_namespace] = model.old_path_with_namespace
end end
data
when User when User
data.merge!({ data.merge!({
name: model.name, name: model.name,
...@@ -59,6 +56,8 @@ class SystemHooksService ...@@ -59,6 +56,8 @@ class SystemHooksService
when GroupMember when GroupMember
data.merge!(group_member_data(model)) data.merge!(group_member_data(model))
end end
data
end end
def build_event_name(model, event) def build_event_name(model, event)
......
# ImportableUrlValidator
#
# This validator blocks projects from using dangerous import_urls to help
# protect against Server-side Request Forgery (SSRF).
class ImportableUrlValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if Gitlab::UrlBlocker.blocked_url?(value)
record.errors.add(attribute, "imports are not allowed from that URL")
end
end
end
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save append-right-10' = f.submit 'Save', class: 'btn btn-save append-right-10'
- if @appearance.persisted? - if @appearance.persisted?
= link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank' = link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank', rel: 'noopener noreferrer'
- if @appearance.updated_at - if @appearance.updated_at
%span.pull-right %span.pull-right
......
...@@ -424,7 +424,7 @@ ...@@ -424,7 +424,7 @@
Enable Sentry Enable Sentry
.help-block .help-block
Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here: Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here:
%a{ href: 'https://getsentry.com', target: '_blank' } https://getsentry.com %a{ href: 'https://getsentry.com', target: '_blank', rel: 'noopener noreferrer' } https://getsentry.com
.form-group .form-group
= f.label :sentry_dsn, 'Sentry DSN', class: 'control-label col-sm-2' = f.label :sentry_dsn, 'Sentry DSN', class: 'control-label col-sm-2'
......
...@@ -9,7 +9,7 @@ xml.entry do ...@@ -9,7 +9,7 @@ xml.entry do
xml.author do xml.author do
xml.name event.author_name xml.name event.author_name
xml.email event.author_email xml.email event.author_public_email
end end
xml.summary(type: "xhtml") do |summary| xml.summary(type: "xhtml") do |summary|
......
...@@ -15,6 +15,6 @@ ...@@ -15,6 +15,6 @@
= link_to note.attachment.url, target: '_blank' do = link_to note.attachment.url, target: '_blank' do
= image_tag note.attachment.url, class: 'note-image-attach' = image_tag note.attachment.url, class: 'note-image-attach'
- else - else
= link_to note.attachment.url, target: "_blank", class: 'note-file-attach' do = link_to note.attachment.url, target: '_blank', class: 'note-file-attach' do
%i.fa.fa-paperclip %i.fa.fa-paperclip
= note.attachment_identifier = note.attachment_identifier
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
- if user_signed_in? - if user_signed_in?
%span= Gitlab::VERSION %span= Gitlab::VERSION
%small= Gitlab::REVISION %small= Gitlab::REVISION
<<<<<<< HEAD
- if current_application_settings.version_check_enabled - if current_application_settings.version_check_enabled
= version_status_badge = version_status_badge
%br %br
...@@ -25,6 +26,21 @@ ...@@ -25,6 +26,21 @@
%br %br
Used by more than 100,000 organizations, GitLab is the most popular solution to manage git repositories on-premises. Used by more than 100,000 organizations, GitLab is the most popular solution to manage git repositories on-premises.
=======
= version_status_badge
%p.slead
GitLab is open source software to collaborate on code.
%br
Manage git repositories with fine-grained access controls that keep your code secure.
%br
Perform code reviews and enhance collaboration with merge requests.
%br
Each project can also have an issue tracker and a wiki.
%br
Used by more than 100,000 organizations, GitLab is the most popular solution to manage git repositories on-premises.
%br
Read more about GitLab at #{link_to promo_host, promo_url, target: '_blank', rel: 'noopener noreferrer'}.
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
- if current_application_settings.help_page_text.present? - if current_application_settings.help_page_text.present?
%hr %hr
= markdown_field(current_application_settings, :help_page_text) = markdown_field(current_application_settings, :help_page_text)
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
- @already_added_projects.each do |project| - @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td %td
= link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank' = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank', rel: 'noopener noreferrer'
%td %td
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status %td.job-status
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
- @repos.each do |repo| - @repos.each do |repo|
%tr{ id: "repo_#{repo.owner}___#{repo.slug}" } %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
%td %td
= link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: "_blank" = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
%td.import-target %td.import-target
%fieldset.row %fieldset.row
.input-group .input-group
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
- @incompatible_repos.each do |repo| - @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.owner}___#{repo.slug}" } %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
%td %td
= link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank' = link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank', rel: 'noopener noreferrer'
%td.import-target %td.import-target
%td.import-actions-job-status %td.import-actions-job-status
= label_tag 'Incompatible Project', nil, class: 'label label-danger' = label_tag 'Incompatible Project', nil, class: 'label label-danger'
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
- @repos.each do |repo| - @repos.each do |repo|
%tr{ id: "repo_#{repo["id"]}" } %tr{ id: "repo_#{repo["id"]}" }
%td %td
= link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank" = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank", rel: 'noopener noreferrer'
%td.import-target %td.import-target
= import_project_target(repo['namespace']['path'], repo['name']) = import_project_target(repo['namespace']['path'], repo['name'])
%td.import-actions.job-status %td.import-actions.job-status
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
%li %li
%p %p
Go to Go to
#{link_to "Google Takeout", "https://www.google.com/settings/takeout", target: "_blank"}. #{link_to "Google Takeout", "https://www.google.com/settings/takeout", target: '_blank', rel: 'noopener noreferrer'}.
%li %li
%p %p
Make sure you're logged into the account that owns the projects you'd like to import. Make sure you're logged into the account that owns the projects you'd like to import.
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
- @already_added_projects.each do |project| - @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td %td
= link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank" = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank", rel: 'noopener noreferrer'
%td %td
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status %td.job-status
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
- @repos.each do |repo| - @repos.each do |repo|
%tr{ id: "repo_#{repo.id}" } %tr{ id: "repo_#{repo.id}" }
%td %td
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank" = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank", rel: 'noopener noreferrer'
%td.import-target %td.import-target
#{current_user.username}/#{repo.name} #{current_user.username}/#{repo.name}
%td.import-actions.job-status %td.import-actions.job-status
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
- @incompatible_repos.each do |repo| - @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.id}" } %tr{ id: "repo_#{repo.id}" }
%td %td
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank" = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank", rel: 'noopener noreferrer'
%td.import-target %td.import-target
%td.import-actions-job-status %td.import-actions-job-status
= label_tag "Incompatible Project", nil, class: "label label-danger" = label_tag "Incompatible Project", nil, class: "label label-danger"
......
...@@ -7,7 +7,7 @@ xml.entry do ...@@ -7,7 +7,7 @@ xml.entry do
xml.author do xml.author do
xml.name issue.author_name xml.name issue.author_name
xml.email issue.author_email xml.email issue.author_public_email
end end
xml.summary issue.title xml.summary issue.title
...@@ -26,7 +26,7 @@ xml.entry do ...@@ -26,7 +26,7 @@ xml.entry do
if issue.assignee if issue.assignee
xml.assignee do xml.assignee do
xml.name issue.assignee.name xml.name issue.assignee.name
xml.email issue.assignee.email xml.email issue.assignee_public_email
end end
end end
end end
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
%p %p
= icon('circle', class: 'cgreen') = icon('circle', class: 'cgreen')
Integration is active for Integration is active for
= link_to koding_project_url, target: '_blank' do = link_to koding_project_url, target: '_blank', rel: 'noopener noreferrer' do
#{current_application_settings.koding_url} #{current_application_settings.koding_url}
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host} or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
.col-lg-9 .col-lg-9
.clearfix.avatar-image.append-bottom-default .clearfix.avatar-image.append-bottom-default
= link_to avatar_icon(@user, 400), target: '_blank' do = link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
= image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160' = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160'
%h5.prepend-top-0 %h5.prepend-top-0
Upload new avatar Upload new avatar
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
- else - else
.nothing-here-block .nothing-here-block
The SVG could not be displayed as it is too large, you can The SVG could not be displayed as it is too large, you can
#{link_to('view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank')} #{link_to('view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank', rel: 'noopener noreferrer')}
instead. instead.
- else - else
%img{ src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path)), alt: "#{blob.name}" } %img{ src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path)), alt: "#{blob.name}" }
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.nothing-here-block .nothing-here-block
File too large, you can File too large, you can
= succeed '.' do = succeed '.' do
= link_to 'view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank' = link_to 'view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank', rel: 'noopener noreferrer'
- else - else
- blob.load_all_data!(@repository) - blob.load_all_data!(@repository)
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
- if @conflict - if @conflict
.alert.alert-danger .alert.alert-danger
Someone edited the file the same time you did. Please check out Someone edited the file the same time you did. Please check out
= link_to "the file", namespace_project_blob_path(@project.namespace, @project, tree_join(@target_branch, @file_path)), target: "_blank" = link_to "the file", namespace_project_blob_path(@project.namespace, @project, tree_join(@target_branch, @file_path)), target: "_blank", rel: 'noopener noreferrer'
and make sure your changes will not unintentionally remove theirs. and make sure your changes will not unintentionally remove theirs.
.file-editor .file-editor
......
- if koding_enabled? && current_user && @repository.koding_yml && can_push_branch?(@project, @project.default_branch) - if koding_enabled? && current_user && @repository.koding_yml && can_push_branch?(@project, @project.default_branch)
= link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank' do = link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank', rel: 'noopener noreferrer' do
Run in IDE (Koding) Run in IDE (Koding)
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project. Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.
To set up CA, you must first define a production environment by setting up your CI and then deploy to production. To set up CA, you must first define a production environment by setting up your CI and then deploy to production.
%p %p
%a.btn{ href: help_page_path('user/project/cycle_analytics'), target: "_blank" } Read more %a.btn{ href: help_page_path('user/project/cycle_analytics'), target: '_blank' } Read more
.col-md-6.overview-image .col-md-6.overview-image
%span.overview-icon %span.overview-icon
= custom_icon ('icon_cycle_analytics_overview') = custom_icon ('icon_cycle_analytics_overview')
- if environment.external_url && can?(current_user, :read_environment, environment) - if environment.external_url && can?(current_user, :read_environment, environment)
= link_to environment.external_url, target: '_blank', class: 'btn external-url' do = link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url' do
= icon('external-link') = icon('external-link')
- @no_container = true - @no_container = true
- page_title "Metrics for environment", @environment.name - page_title "Metrics for environment", @environment.name
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('monitoring')
= render "projects/pipelines/head" = render "projects/pipelines/head"
%div{ class: container_class } %div{ class: container_class }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
.nav-controls .nav-controls
= link_to params.merge(rss_url_options), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do = link_to params.merge(rss_url_options), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do
= icon('rss') = icon('rss')
<<<<<<< HEAD
- if current_user - if current_user
%button.csv_download_link.btn.append-right-10.has-tooltip{ title: 'Export as CSV' } %button.csv_download_link.btn.append-right-10.has-tooltip{ title: 'Export as CSV' }
= icon('download') = icon('download')
...@@ -34,6 +35,16 @@ ...@@ -34,6 +35,16 @@
title: "New Issue", title: "New Issue",
id: "new_issue_link" do id: "new_issue_link" do
New Issue New Issue
=======
= link_to new_namespace_project_issue_path(@project.namespace,
@project,
issue: { assignee_id: issues_finder.assignee.try(:id),
milestone_id: issues_finder.milestones.first.try(:id) }),
class: "btn btn-new",
title: "New Issue",
id: "new_issue_link" do
New Issue
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
= render 'shared/issuable/search_bar', type: :issues = render 'shared/issuable/search_bar', type: :issues
.issues-holder .issues-holder
......
...@@ -20,37 +20,34 @@ ...@@ -20,37 +20,34 @@
= confidential_icon(@issue) = confidential_icon(@issue)
= issuable_meta(@issue, @project, "Issue") = issuable_meta(@issue, @project, "Issue")
- if can?(current_user, :create_issue, @project) || can?(current_user, :update_issue, @issue) .issuable-actions
.issuable-actions .clearfix.issue-btn-group.dropdown
.clearfix.issue-btn-group.dropdown %button.btn.btn-default.pull-left.hidden-md.hidden-lg{ type: "button", data: { toggle: "dropdown" } }
%button.btn.btn-default.pull-left.hidden-md.hidden-lg{ type: "button", data: { toggle: "dropdown" } } Options
Options = icon('caret-down')
= icon('caret-down') .dropdown-menu.dropdown-menu-align-right.hidden-lg
.dropdown-menu.dropdown-menu-align-right.hidden-lg %ul
%ul %li
- if can?(current_user, :create_issue, @project) = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link'
%li - if can?(current_user, :update_issue, @issue)
= link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project), title: 'New issue', id: 'new_issue_link' %li
- if can?(current_user, :update_issue, @issue) = link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
%li %li
= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' = link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
%li %li
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
%li
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue)
- if @issue.submittable_as_spam_by?(current_user)
%li
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam'
- if can?(current_user, :create_issue, @project)
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do
New issue
- if can?(current_user, :update_issue, @issue)
= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
- if @issue.submittable_as_spam_by?(current_user) - if @issue.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam' %li
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' = link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam'
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do
New issue
- if can?(current_user, :update_issue, @issue)
= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
- if @issue.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_namespace_project_issue_path(@project.namespace, @project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam'
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit'
.issue-details.issuable-details .issue-details.issuable-details
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.pull-right .pull-right
- if @merge_request.source_branch_exists? - if @merge_request.source_branch_exists?
- if koding_enabled? && @repository.koding_yml - if koding_enabled? && @repository.koding_yml
= link_to koding_project_url(@merge_request.source_project, @merge_request.source_branch, @merge_request.commits.first.short_id), class: "btn inline btn-grouped btn-sm", target: '_blank' do = link_to koding_project_url(@merge_request.source_project, @merge_request.source_branch, @merge_request.commits.first.short_id), class: "btn inline btn-grouped btn-sm", target: '_blank', rel: 'noopener noreferrer' do
Run in IDE (Koding) Run in IDE (Koding)
= link_to "#modal_merge_info", class: "btn inline btn-grouped btn-sm", "data-toggle" => "modal" do = link_to "#modal_merge_info", class: "btn inline btn-grouped btn-sm", "data-toggle" => "modal" do
Check out branch Check out branch
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
%strong Tip: %strong Tip:
= succeed '.' do = succeed '.' do
You can also checkout merge requests locally by You can also checkout merge requests locally by
= link_to 'following these guidelines', help_page_path('user/project/merge_requests.md', anchor: "checkout-merge-requests-locally"), target: '_blank' = link_to 'following these guidelines', help_page_path('user/project/merge_requests.md', anchor: "checkout-merge-requests-locally"), target: '_blank', rel: 'noopener noreferrer'
:javascript :javascript
$(function(){ $(function(){
......
...@@ -4,13 +4,13 @@ ...@@ -4,13 +4,13 @@
%ul.list-unstyled.indent-list %ul.list-unstyled.indent-list
%li %li
1. 1.
= link_to 'https://docs.mattermost.com/developer/slash-commands.html#enabling-custom-commands', target: '_blank', rel: 'noreferrer noopener nofollow' do = link_to 'https://docs.mattermost.com/developer/slash-commands.html#enabling-custom-commands', target: '_blank', rel: 'noopener noreferrer nofollow' do
Enable custom slash commands Enable custom slash commands
= icon('external-link') = icon('external-link')
on your Mattermost installation on your Mattermost installation
%li %li
2. 2.
= link_to 'https://docs.mattermost.com/developer/slash-commands.html#set-up-a-custom-command', target: '_blank', rel: 'noreferrer noopener nofollow' do = link_to 'https://docs.mattermost.com/developer/slash-commands.html#set-up-a-custom-command', target: '_blank', rel: 'noopener noreferrer nofollow' do
Add a slash command Add a slash command
= icon('external-link') = icon('external-link')
in your Mattermost team with these options: in your Mattermost team with these options:
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%p %p
This service allows users to perform common operations on this This service allows users to perform common operations on this
project by entering slash commands in Mattermost. project by entering slash commands in Mattermost.
= link_to help_page_path('user/project/integrations/mattermost_slash_commands.md'), target: '_blank', ref: 'noreferrer nofollow noopener' do = link_to help_page_path('user/project/integrations/mattermost_slash_commands.md'), target: '_blank' do
View documentation View documentation
= icon('external-link') = icon('external-link')
%p.inline %p.inline
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%p %p
This service allows users to perform common operations on this This service allows users to perform common operations on this
project by entering slash commands in Slack. project by entering slash commands in Slack.
= link_to help_page_path('user/project/integrations/slack_slash_commands.md'), target: '_blank', ref: 'noreferrer nofollow noopener' do = link_to help_page_path('user/project/integrations/slack_slash_commands.md'), target: '_blank' do
View documentation View documentation
= icon('external-link') = icon('external-link')
%p.inline %p.inline
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
= label_tag nil, 'Customize icon', class: 'col-sm-2 col-xs-12 control-label' = label_tag nil, 'Customize icon', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block .col-sm-10.col-xs-12.text-block
= image_tag(asset_url('slash-command-logo.png'), width: 36, height: 36) = image_tag(asset_url('slash-command-logo.png'), width: 36, height: 36)
= link_to('Download image', asset_url('gitlab_logo.png'), class: 'btn btn-sm', target: '_blank') = link_to('Download image', asset_url('gitlab_logo.png'), class: 'btn btn-sm', target: '_blank', rel: 'noopener noreferrer')
.form-group .form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label' = label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
......
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%button.dropdown-toggle{ type: 'button', data: {toggle: 'dropdown' } } %button.dropdown-toggle{ type: 'button', data: {toggle: 'dropdown' } }
%span.light
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
- else - else
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
Also, issues are searchable and filterable. Also, issues are searchable and filterable.
- if project_select_button - if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue' = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue'
- else
= link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
- else - else
%h4.text-center There are no issues to show. %h4 There are no issues to show.
= link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.alert.alert-danger .alert.alert-danger
Someone edited the #{issuable.class.model_name.human.downcase} the same time you did. Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
Please check out Please check out
= link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), target: "_blank" = link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), target: "_blank", rel: 'noopener noreferrer'
and make sure your changes will not unintentionally remove theirs and make sure your changes will not unintentionally remove theirs
.form-group .form-group
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
.profile-header .profile-header
.avatar-holder .avatar-holder
= link_to avatar_icon(@user, 400), target: '_blank' do = link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
= image_tag avatar_icon(@user, 90), class: "avatar s90", alt: '' = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: ''
.user-info .user-info
......
--- ---
title: API: Add environment stop action title: 'API: Add environment stop action'
merge_request: 8808 merge_request: 8808
author: author:
---
title: Only show public emails in atom feeds
merge_request:
author:
---
title: Fixes large file name tooltip cutoff in diff header
merge_request: 9529
author:
---
title: Fixes dismissable error close is not visible enough
merge_request: 9516
author:
---
title: New directory from interface on existing branch
merge_request: 9921
author: Jacopo Beschi @jacopo-beschi
---
title: Added tests for the w.gl.utils.backOff promise
merge_request:
author:
---
title: Return 404 in project issues API endpoint when project cannot be found
merge_request: 10093
author:
---
title: Fix bug when system hook for deploy key
merge_request: 9796
author: billy.lb
---
title: Hide ancestor groups in the share group dropdown list
merge_request: 9965
author:
---
title: New rake task to reset all email and private tokens
merge_request:
author:
---
title: Removed d3 from the main application.js bundle
merge_request: 10062
author:
---
title: Simplify trigger_docs build job for CE and EE
merge_request: 9820
author: winniehell
---
title: To protect against Server-side Request Forgery project import URLs are now prohibited against localhost or the server IP except for the assigned instance URL and port. Imports are also prohibited from ports below 1024 with the exception of ports 22, 80, and 443.
merge_request:
author:
...@@ -36,7 +36,11 @@ var config = { ...@@ -36,7 +36,11 @@ var config = {
issuable: './issuable/issuable_bundle.js', issuable: './issuable/issuable_bundle.js',
merge_conflicts: './merge_conflicts/merge_conflicts_bundle.js', merge_conflicts: './merge_conflicts/merge_conflicts_bundle.js',
merge_request_widget: './merge_request_widget/ci_bundle.js', merge_request_widget: './merge_request_widget/ci_bundle.js',
<<<<<<< HEAD
mr_widget_ee: './merge_request_widget/widget_bundle.js', mr_widget_ee: './merge_request_widget/widget_bundle.js',
=======
monitoring: './monitoring/monitoring_bundle.js',
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
network: './network/network_bundle.js', network: './network/network_bundle.js',
profile: './profile/profile_bundle.js', profile: './profile/profile_bundle.js',
protected_branches: './protected_branches/protected_branches_bundle.js', protected_branches: './protected_branches/protected_branches_bundle.js',
...@@ -117,7 +121,7 @@ var config = { ...@@ -117,7 +121,7 @@ var config = {
// create cacheable common library bundle for all d3 chunks // create cacheable common library bundle for all d3 chunks
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
name: 'common_d3', name: 'common_d3',
chunks: ['graphs', 'users'], chunks: ['graphs', 'users', 'monitoring'],
}), }),
// create cacheable common library bundles // create cacheable common library bundles
......
...@@ -155,7 +155,7 @@ class Gitlab::Seeder::CycleAnalytics ...@@ -155,7 +155,7 @@ class Gitlab::Seeder::CycleAnalytics
issue.project.repository.add_branch(@user, branch_name, 'master') issue.project.repository.add_branch(@user, branch_name, 'master')
commit_sha = issue.project.repository.create_file(@user, filename, "content", options, message: "Commit for ##{issue.iid}", branch_name: branch_name) commit_sha = issue.project.repository.create_file(@user, filename, "content", message: "Commit for ##{issue.iid}", branch_name: branch_name)
issue.project.repository.commit(commit_sha) issue.project.repository.commit(commit_sha)
GitPushService.new(issue.project, GitPushService.new(issue.project,
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIndexToUserGhost < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
disable_ddl_transaction!
def up
add_concurrent_index :users, :ghost
end
def down
remove_index :users, :ghost
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class IndexRoutesPathForLike < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
INDEX_NAME = 'index_routes_on_path_text_pattern_ops'
disable_ddl_transaction!
def up
return unless Gitlab::Database.postgresql?
unless index_exists?(:routes, name: INDEX_NAME)
execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON routes (path varchar_pattern_ops);")
end
end
def down
return unless Gitlab::Database.postgresql?
if index_exists?(:routes, name: INDEX_NAME)
execute("DROP INDEX CONCURRENTLY #{INDEX_NAME};")
end
end
end
...@@ -6,41 +6,12 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration ...@@ -6,41 +6,12 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration
DOWNTIME = false DOWNTIME = false
THREAD_COUNT = 8
KNOWN_PATHS = %w(artifacts graphs refs badges).freeze KNOWN_PATHS = %w(artifacts graphs refs badges).freeze
def up def up
queues = Array.new(THREAD_COUNT) { Queue.new }
start = false
threads = Array.new(THREAD_COUNT) do |index|
Thread.new do
queue = queues[index]
# Wait until we have input to process.
until start; end
rename_projects(queue.pop) until queue.empty?
end
end
enum = queues.each
reserved_projects.each_slice(100) do |slice| reserved_projects.each_slice(100) do |slice|
begin rename_projects(slice)
queue = enum.next
rescue StopIteration
enum.rewind
retry
end
queue << slice
end end
start = true
threads.each(&:join)
end end
def down def down
......
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
<<<<<<< HEAD
ActiveRecord::Schema.define(version: 20170315194013) do ActiveRecord::Schema.define(version: 20170315194013) do
=======
ActiveRecord::Schema.define(version: 20170317203554) do
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -1182,6 +1186,7 @@ ActiveRecord::Schema.define(version: 20170315194013) do ...@@ -1182,6 +1186,7 @@ ActiveRecord::Schema.define(version: 20170315194013) do
end end
add_index "routes", ["path"], name: "index_routes_on_path", unique: true, using: :btree add_index "routes", ["path"], name: "index_routes_on_path", unique: true, using: :btree
add_index "routes", ["path"], name: "index_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"}
add_index "routes", ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree add_index "routes", ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree
create_table "sent_notifications", force: :cascade do |t| create_table "sent_notifications", force: :cascade do |t|
...@@ -1444,6 +1449,7 @@ ActiveRecord::Schema.define(version: 20170315194013) do ...@@ -1444,6 +1449,7 @@ ActiveRecord::Schema.define(version: 20170315194013) do
add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"} add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"}
add_index "users", ["ghost"], name: "index_users_on_ghost", using: :btree
add_index "users", ["incoming_email_token"], name: "index_users_on_incoming_email_token", using: :btree add_index "users", ["incoming_email_token"], name: "index_users_on_incoming_email_token", using: :btree
add_index "users", ["name"], name: "index_users_on_name", using: :btree add_index "users", ["name"], name: "index_users_on_name", using: :btree
add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
......
...@@ -96,21 +96,15 @@ Sample Prometheus queries: ...@@ -96,21 +96,15 @@ Sample Prometheus queries:
> Introduced in GitLab 9.0. > Introduced in GitLab 9.0.
If your GitLab server is running within Kubernetes, an option is now available If your GitLab server is running within Kubernetes, Prometheus will collect metrics from the Nodes in the cluster including performance data on each container. This is particularly helpful if your CI/CD environments run in the same cluster, as you can use the [Prometheus project integration][] to monitor them.
to monitor the health of each node in the cluster. This is particularly helpful
if your CI/CD environments run in the same cluster, and you would like enable
[Prometheus integration][] to monitor them.
When enabled, the bundled Prometheus server monitors Kubernetes and automatically To disable the monitoring of Kubernetes:
[collects metrics][prometheus-cadvisor-metrics] from each Node in the cluster.
To enable the Kubernetes monitoring:
1. Edit `/etc/gitlab/gitlab.rb` 1. Edit `/etc/gitlab/gitlab.rb`
1. Add or find and uncomment the following line: 1. Add or find and uncomment the following line and set it to `false`:
```ruby ```ruby
prometheus['monitor_kubernetes'] = true prometheus['monitor_kubernetes'] = false
``` ```
1. Save the file and [reconfigure GitLab][reconfigure] for the changes to 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to
...@@ -165,4 +159,4 @@ The GitLab monitor exporter allows you to measure various GitLab metrics. ...@@ -165,4 +159,4 @@ The GitLab monitor exporter allows you to measure various GitLab metrics.
[reconfigure]: ../../restart_gitlab.md#omnibus-gitlab-reconfigure [reconfigure]: ../../restart_gitlab.md#omnibus-gitlab-reconfigure
[1261]: https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1261 [1261]: https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1261
[prometheus integration]: ../../../user/project/integrations/prometheus.md [prometheus integration]: ../../../user/project/integrations/prometheus.md
[rometheus-cadvisor-metrics]: https://github.com/google/cadvisor/blob/master/docs/storage/prometheus.md [prometheus-cadvisor-metrics]: https://github.com/google/cadvisor/blob/master/docs/storage/prometheus.md
...@@ -8,16 +8,16 @@ Below are the changes made between V3 and V4. ...@@ -8,16 +8,16 @@ Below are the changes made between V3 and V4.
### 8.17 ### 8.17
- Removed `/projects/:search` (use: `/projects?search=x`) [!8877](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8877) - Removed `GET /projects/:search` (use: `GET /projects?search=x`) [!8877](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8877)
- `iid` filter has been removed from `projects/:id/issues` [!8967](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8967) - `iid` filter has been removed from `GET /projects/:id/issues` [!8967](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8967)
- `projects/:id/merge_requests?iid[]=x&iid[]=y` array filter has been renamed to `iids` [!8793](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8793) - `GET /projects/:id/merge_requests?iid[]=x&iid[]=y` array filter has been renamed to `iids` [!8793](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8793)
- Endpoints under `projects/merge_request/:id` have been removed (use: `projects/merge_requests/:id`) [!8793](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8793) - Endpoints under `GET /projects/merge_request/:id` have been removed (use: `GET /projects/merge_requests/:id`) [!8793](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8793)
- Project snippets do not return deprecated field `expires_at` [!8723](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8723) - Project snippets do not return deprecated field `expires_at` [!8723](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8723)
- Endpoints under `projects/:id/keys` have been removed (use `projects/:id/deploy_keys`) [!8716](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8716) - Endpoints under `GET /projects/:id/keys` have been removed (use `GET /projects/:id/deploy_keys`) [!8716](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8716)
### 9.0 ### 9.0
- Status 409 returned for POST `project/:id/members` when a member already exists [!9093](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9093) - Status 409 returned for `POST /projects/:id/members` when a member already exists [!9093](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9093)
- Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar` [!9328](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9328) - Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar` [!9328](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9328)
- Removed the following deprecated Templates endpoints (these are still accessible with `/templates` prefix) [!8853](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8853) - Removed the following deprecated Templates endpoints (these are still accessible with `/templates` prefix) [!8853](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8853)
- `/licences` - `/licences`
...@@ -28,31 +28,31 @@ Below are the changes made between V3 and V4. ...@@ -28,31 +28,31 @@ Below are the changes made between V3 and V4.
- `/gitignores/:key` - `/gitignores/:key`
- `/gitlab_ci_ymls/:key` - `/gitlab_ci_ymls/:key`
- `/dockerfiles/:key` - `/dockerfiles/:key`
- Moved `/projects/fork/:id` to `/projects/:id/fork` [!8940](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8940) - Moved `POST /projects/fork/:id` to `POST /projects/:id/fork` [!8940](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8940)
- Moved `DELETE /todos` to `POST /todos/mark_as_done` and `DELETE /todos/:todo_id` to `POST /todos/:todo_id/mark_as_done` [!9410](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9410) - Moved `DELETE /todos` to `POST /todos/mark_as_done` and `DELETE /todos/:todo_id` to `POST /todos/:todo_id/mark_as_done` [!9410](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9410)
- Project filters are no longer available as `GET /projects/foo`, but as `GET /projects?foo=true` instead [!8962](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8962) - Project filters are no longer available as `GET /projects/foo`, but as `GET /projects?foo=true` instead [!8962](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8962)
- `GET /projects/visible` & `GET /projects/all` are consolidated into `GET /projects` and can be used with or without authorization - `GET /projects/visible` & `GET /projects/all` are consolidated into `GET /projects` and can be used with or without authorization
- `GET /projects/owned` moved to `GET /projects?owned=true` - `GET /projects/owned` moved to `GET /projects?owned=true`
- `GET /projects/starred` moved to `GET /projects?starred=true` - `GET /projects/starred` moved to `GET /projects?starred=true`
- `GET /projects` returns all projects visible to current user, even if the user is not a member [!9674](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9674) - `GET /projects` returns all projects visible to current user, even if the user is not a member [!9674](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9674)
- To get projects the user is a member of, use `/projects?membership=true` - To get projects the user is a member of, use `GET /projects?membership=true`
- Return pagination headers for all endpoints that return an array [!8606](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8606) - Return pagination headers for all endpoints that return an array [!8606](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8606)
- Added `POST /environments/:environment_id/stop` to stop an environment [!8808](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8808) - Added `POST /environments/:environment_id/stop` to stop an environment [!8808](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8808)
- Removed `DELETE projects/:id/deploy_keys/:key_id/disable`. Use `DELETE projects/:id/deploy_keys/:key_id` instead [!9366](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9366) - Removed `DELETE /projects/:id/deploy_keys/:key_id/disable`. Use `DELETE /projects/:id/deploy_keys/:key_id` instead [!9366](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9366)
- Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` [!9371](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9371) - Moved `PUT /users/:id/(block|unblock)` to `POST /users/:id/(block|unblock)` [!9371](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9371)
- Make subscription API more RESTful. Use `post ":project_id/:subscribable_type/:subscribable_id/subscribe"` to subscribe and `post ":project_id/:subscribable_type/:subscribable_id/unsubscribe"` to unsubscribe from a resource. [!9325](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9325) - Make subscription API more RESTful. Use `POST /projects/:id/:subscribable_type/:subscribable_id/subscribe` to subscribe and `POST /projects/:id/:subscribable_type/:subscribable_id/unsubscribe` to unsubscribe from a resource. [!9325](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9325)
- Labels filter on `projects/:id/issues` and `/issues` now matches only issues containing all labels (i.e.: Logical AND, not OR) [!8849](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8849) - Labels filter on `GET /projects/:id/issues` and `GET /issues` now matches only issues containing all labels (i.e.: Logical AND, not OR) [!8849](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8849)
- Renamed param `branch_name` to `branch` on the following endpoints [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936) - Renamed param `branch_name` to `branch` on the following endpoints [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936)
- POST `:id/repository/branches` - `POST /projects/:id/repository/branches`
- POST `:id/repository/commits` - `POST /projects/:id/repository/commits`
- POST/PUT/DELETE `:id/repository/files` - `POST/PUT/DELETE :id/repository/files`
- Renamed `merge when build succeeds` to merge `when pipeline succeeds parameters` on the following endpoints: [!9335](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/) - Renamed the `merge_when_build_succeeds` parameter to `merge_when_pipeline_succeeds` on the following endpoints: [!9335](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/)
- PUT `projects/:id/merge_requests/:merge_request_id/merge` - `PUT /projects/:id/merge_requests/:merge_request_id/merge`
- POST `projects/:id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds` - `POST /projects/:id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds`
- POST `projects` - `POST /projects`
- POST `projects/user/:user_id` - `POST /projects/user/:user_id`
- PUT `projects/:id` - `PUT /projects/:id`
- Renamed `branch_name` to `branch` on DELETE `id/repository/branches/:branch` response [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936) - Renamed `branch_name` to `branch` on `DELETE /projects/:id/repository/branches/:branch` response [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936)
- Remove `public` param from create and edit actions of projects [!8736](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8736) - Remove `public` param from create and edit actions of projects [!8736](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8736)
- Remove `subscribed` field from responses returning list of issues or merge - Remove `subscribed` field from responses returning list of issues or merge
requests. Fetch individual issues or merge requests to obtain the value requests. Fetch individual issues or merge requests to obtain the value
...@@ -62,21 +62,22 @@ Below are the changes made between V3 and V4. ...@@ -62,21 +62,22 @@ Below are the changes made between V3 and V4.
- Notes do not return deprecated field `upvote` and `downvote` [!9384](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9384) - Notes do not return deprecated field `upvote` and `downvote` [!9384](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9384)
- Return HTTP status code `400` for all validation errors when creating or updating a member instead of sometimes `422` error. [!9523](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9523) - Return HTTP status code `400` for all validation errors when creating or updating a member instead of sometimes `422` error. [!9523](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9523)
- Remove `GET /groups/owned`. Use `GET /groups?owned=true` instead [!9505](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9505) - Remove `GET /groups/owned`. Use `GET /groups?owned=true` instead [!9505](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9505)
- Return 202 with JSON body on async removals on V4 API (DELETE `/projects/:id/repository/merged_branches` and DELETE `/projects/:id`) [!9449](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9449) - Return 202 with JSON body on async removals on V4 API (`DELETE /projects/:id/repository/merged_branches` and `DELETE /projects/:id`) [!9449](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9449)
- `projects/:id/milestones?iid[]=x&iid[]=y` array filter has been renamed to `iids` [!9096](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9096) - `GET /projects/:id/milestones?iid[]=x&iid[]=y` array filter has been renamed to `iids` [!9096](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9096)
- Return basic info about pipeline in `GET /projects/:id/pipelines` [!8875](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8875) - Return basic info about pipeline in `GET /projects/:id/pipelines` [!8875](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8875)
- Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675) - Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675)
- Renamed all `build` references to `job` [!9463](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9463) - Renamed all `build` references to `job` [!9463](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9463)
- Drop GET '/projects/:id/repository/commits/:sha/jobs' [!9463](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9463) - Drop `GET /projects/:id/repository/commits/:sha/jobs` [!9463](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9463)
- Rename Build Triggers to be Pipeline Triggers API [!9713](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9713) - Rename Build Triggers to be Pipeline Triggers API [!9713](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9713)
- `POST /projects/:id/trigger/builds` to `POST /projects/:id/trigger/pipeline` - `POST /projects/:id/trigger/builds` to `POST /projects/:id/trigger/pipeline`
- Require description when creating a new trigger `POST /projects/:id/triggers` - Require description when creating a new trigger `POST /projects/:id/triggers`
- Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675) - Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675)
- API uses merge request `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the merge requests, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530) - API uses merge request `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the merge requests, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
- API uses issue `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the issues, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530) - API uses issue `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the issues, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
- Change initial page from `0` to `1` on `GET projects/:id/repository/commits` (like on the rest of the API) [!9679] (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679) - Change initial page from `0` to `1` on `GET /projects/:id/repository/commits` (like on the rest of the API) [!9679] (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679)
- Return correct `Link` header data for `GET projects/:id/repository/commits` [!9679] (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679) - Return correct `Link` header data for `GET /projects/:id/repository/commits` [!9679] (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679)
- Update endpoints for repository files [!9637](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9637) - Update endpoints for repository files [!9637](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9637)
<<<<<<< HEAD
- Moved `/projects/:id/repository/files?file_path=:file_path` to `/projects/:id/repository/files/:file_path` (`:file_path` should be URL-encoded) - Moved `/projects/:id/repository/files?file_path=:file_path` to `/projects/:id/repository/files/:file_path` (`:file_path` should be URL-encoded)
- `/projects/:id/repository/blobs/:sha` now returns JSON attributes for the blob identified by `:sha`, instead of finding the commit identified by `:sha` and returning the raw content of the blob in that commit identified by the required `?filepath=:filepath` - `/projects/:id/repository/blobs/:sha` now returns JSON attributes for the blob identified by `:sha`, instead of finding the commit identified by `:sha` and returning the raw content of the blob in that commit identified by the required `?filepath=:filepath`
- Moved `/projects/:id/repository/commits/:sha/blob?file_path=:file_path` and `/projects/:id/repository/blobs/:sha?file_path=:file_path` to `/projects/:id/repository/files/:file_path/raw?ref=:sha` - Moved `/projects/:id/repository/commits/:sha/blob?file_path=:file_path` and `/projects/:id/repository/blobs/:sha?file_path=:file_path` to `/projects/:id/repository/files/:file_path/raw?ref=:sha`
...@@ -87,3 +88,9 @@ Below are the changes made between V3 and V4. ...@@ -87,3 +88,9 @@ Below are the changes made between V3 and V4.
- Remove the ProjectGitHook API. Use the ProjectPushRule API instead [!1301](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1301) - Remove the ProjectGitHook API. Use the ProjectPushRule API instead [!1301](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1301)
- Removed `repository_storage` from `PUT /application/settings` and `GET /application/settings` (use `repository_storages` instead) [!1307](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1307) - Removed `repository_storage` from `PUT /application/settings` and `GET /application/settings` (use `repository_storages` instead) [!1307](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1307)
- Removed `elasticsearch_host` and `elasticsearch_port` from `PUT /application/settings` (use `elasticsearch_url` instead) [!1305](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1305) - Removed `elasticsearch_host` and `elasticsearch_port` from `PUT /application/settings` (use `elasticsearch_url` instead) [!1305](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1305)
=======
- Moved `GET /projects/:id/repository/files?file_path=:file_path` to `GET /projects/:id/repository/files/:file_path` (`:file_path` should be URL-encoded)
- `GET /projects/:id/repository/blobs/:sha` now returns JSON attributes for the blob identified by `:sha`, instead of finding the commit identified by `:sha` and returning the raw content of the blob in that commit identified by the required `?filepath=:filepath`
- Moved `GET /projects/:id/repository/commits/:sha/blob?file_path=:file_path` and `GET /projects/:id/repository/blobs/:sha?file_path=:file_path` to `GET /projects/:id/repository/files/:file_path/raw?ref=:sha`
- `GET /projects/:id/repository/tree` parameter `ref_name` has been renamed to `ref` for consistency
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
...@@ -170,12 +170,12 @@ Integration (CI) server. By using deploy keys, you don't have to setup a ...@@ -170,12 +170,12 @@ Integration (CI) server. By using deploy keys, you don't have to setup a
dummy user account. dummy user account.
If you are a project master or owner, you can add a deploy key in the If you are a project master or owner, you can add a deploy key in the
project settings under the section 'Deploy Keys'. Press the 'New Deploy project settings under the section 'Repository'. Specify a title for the new
Key' button and upload a public SSH key. After this, the machine that uses deploy key and paste a public SSH key. After this, the machine that uses
the corresponding private SSH key has read-only or read-write (if enabled) the corresponding private SSH key has read-only or read-write (if enabled)
access to the project. access to the project.
You can't add the same deploy key twice with the 'New Deploy Key' option. You can't add the same deploy key twice using the form.
If you want to add the same key to another project, please enable it in the If you want to add the same key to another project, please enable it in the
list that says 'Deploy keys from projects available to you'. All the deploy list that says 'Deploy keys from projects available to you'. All the deploy
keys of all the projects you have access to are available. This project keys of all the projects you have access to are available. This project
......
...@@ -115,11 +115,11 @@ sudo -u git -H bundle clean ...@@ -115,11 +115,11 @@ sudo -u git -H bundle clean
# Run database migrations # Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Install/update frontend asset dependencies # Update node dependencies and recompile assets
sudo -u git -H npm install --production sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
# Clean up assets and cache # Clean up cache
sudo -u git -H bundle exec rake gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
``` ```
**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md). **MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
......
...@@ -93,7 +93,7 @@ module API ...@@ -93,7 +93,7 @@ module API
use :issues_params use :issues_params
end end
get ":id/issues" do get ":id/issues" do
project = find_project(params[:id]) project = find_project!(params[:id])
issues = find_issues(project_id: project.id) issues = find_issues(project_id: project.id)
......
...@@ -104,7 +104,7 @@ module API ...@@ -104,7 +104,7 @@ module API
use :issues_params use :issues_params
end end
get ":id/issues" do get ":id/issues" do
project = find_project(params[:id]) project = find_project!(params[:id])
issues = find_issues(project_id: project.id) issues = find_issues(project_id: project.id)
......
...@@ -2,7 +2,6 @@ module Banzai ...@@ -2,7 +2,6 @@ module Banzai
module Filter module Filter
# HTML filter that wraps links around inline images. # HTML filter that wraps links around inline images.
class ImageLinkFilter < HTML::Pipeline::Filter class ImageLinkFilter < HTML::Pipeline::Filter
# Find every image that isn't already wrapped in an `a` tag, create # Find every image that isn't already wrapped in an `a` tag, create
# a new node (a link to the image source), copy the image as a child # a new node (a link to the image source), copy the image as a child
# of the anchor, and then replace the img with the link-wrapped version. # of the anchor, and then replace the img with the link-wrapped version.
...@@ -12,7 +11,8 @@ module Banzai ...@@ -12,7 +11,8 @@ module Banzai
'a', 'a',
class: 'no-attachment-icon', class: 'no-attachment-icon',
href: img['src'], href: img['src'],
target: '_blank' target: '_blank',
rel: 'noopener noreferrer'
) )
link.children = img.clone link.children = img.clone
......
...@@ -43,6 +43,7 @@ module Banzai ...@@ -43,6 +43,7 @@ module Banzai
element['title'] || element['alt'], element['title'] || element['alt'],
href: element['src'], href: element['src'],
target: '_blank', target: '_blank',
rel: 'noopener noreferrer',
title: "Download '#{element['title'] || element['alt']}'") title: "Download '#{element['title'] || element['alt']}'")
download_paragraph = doc.document.create_element('p') download_paragraph = doc.document.create_element('p')
download_paragraph.children = link download_paragraph.children = link
......
require 'resolv'
module Gitlab
class UrlBlocker
class << self
# Used to specify what hosts and port numbers should be prohibited for project
# imports.
VALID_PORTS = [22, 80, 443].freeze
def blocked_url?(url)
return false if url.nil?
blocked_ips = ["127.0.0.1", "::1", "0.0.0.0"]
blocked_ips.concat(Socket.ip_address_list.map(&:ip_address))
begin
uri = Addressable::URI.parse(url)
# Allow imports from the GitLab instance itself but only from the configured ports
return false if internal?(uri)
return true if blocked_port?(uri.port)
server_ips = Resolv.getaddresses(uri.hostname)
return true if (blocked_ips & server_ips).any?
rescue Addressable::URI::InvalidURIError
return true
end
false
end
private
def blocked_port?(port)
return false if port.blank?
port < 1024 && !VALID_PORTS.include?(port)
end
def internal?(uri)
internal_web?(uri) || internal_shell?(uri)
end
def internal_web?(uri)
uri.hostname == config.gitlab.host &&
(uri.port.blank? || uri.port == config.gitlab.port)
end
def internal_shell?(uri)
uri.hostname == config.gitlab_shell.ssh_host &&
(uri.port.blank? || uri.port == config.gitlab_shell.ssh_port)
end
def config
Gitlab.config
end
end
end
end
...@@ -96,8 +96,8 @@ module Gitlab ...@@ -96,8 +96,8 @@ module Gitlab
end end
def level_value(level) def level_value(level)
return string_options[level] if level.is_a? String return level.to_i if level.to_i.to_s == level.to_s && string_options.key(level.to_i)
level string_options[level] || PRIVATE
end end
def string_level(level) def string_level(level)
......
...@@ -3,16 +3,16 @@ namespace :gitlab do ...@@ -3,16 +3,16 @@ namespace :gitlab do
desc 'GitLab | Assets | Compile all frontend assets' desc 'GitLab | Assets | Compile all frontend assets'
task compile: [ task compile: [
'yarn:check', 'yarn:check',
'assets:precompile', 'rake:assets:precompile',
'webpack:compile', 'webpack:compile',
'gitlab:assets:fix_urls' 'fix_urls'
] ]
desc 'GitLab | Assets | Clean up old compiled frontend assets' desc 'GitLab | Assets | Clean up old compiled frontend assets'
task clean: ['assets:clean'] task clean: ['rake:assets:clean']
desc 'GitLab | Assets | Remove all compiled frontend assets' desc 'GitLab | Assets | Remove all compiled frontend assets'
task purge: ['assets:clobber'] task purge: ['rake:assets:clobber']
desc 'GitLab | Assets | Uninstall frontend dependencies' desc 'GitLab | Assets | Uninstall frontend dependencies'
task purge_modules: ['yarn:clobber'] task purge_modules: ['yarn:clobber']
......
...@@ -3,10 +3,12 @@ require Rails.root.join('lib/gitlab/database/migration_helpers') ...@@ -3,10 +3,12 @@ require Rails.root.join('lib/gitlab/database/migration_helpers')
require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lower_indexes') require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lower_indexes')
require Rails.root.join('db/migrate/20151008110232_add_users_lower_username_email_indexes') require Rails.root.join('db/migrate/20151008110232_add_users_lower_username_email_indexes')
require Rails.root.join('db/migrate/20161212142807_add_lower_path_index_to_routes') require Rails.root.join('db/migrate/20161212142807_add_lower_path_index_to_routes')
require Rails.root.join('db/migrate/20170317203554_index_routes_path_for_like')
desc 'GitLab | Sets up PostgreSQL' desc 'GitLab | Sets up PostgreSQL'
task setup_postgresql: :environment do task setup_postgresql: :environment do
NamespacesProjectsPathLowerIndexes.new.up NamespacesProjectsPathLowerIndexes.new.up
AddUsersLowerUsernameEmailIndexes.new.up AddUsersLowerUsernameEmailIndexes.new.up
AddLowerPathIndexToRoutes.new.up AddLowerPathIndexToRoutes.new.up
IndexRoutesPathForLike.new.up
end end
require_relative '../../app/models/concerns/token_authenticatable.rb'
namespace :tokens do
desc "Reset all GitLab user auth tokens"
task reset_all_auth: :environment do
reset_all_users_token(:reset_authentication_token!)
end
desc "Reset all GitLab email tokens"
task reset_all_email: :environment do
reset_all_users_token(:reset_incoming_email_token!)
end
def reset_all_users_token(reset_token_method)
TmpUser.find_in_batches do |batch|
puts "Processing batch starting with user ID: #{batch.first.id}"
STDOUT.flush
batch.each(&reset_token_method)
end
end
end
class TmpUser < ActiveRecord::Base
include TokenAuthenticatable
self.table_name = 'users'
def reset_authentication_token!
write_new_token(:authentication_token)
save!(validate: false)
end
def reset_incoming_email_token!
write_new_token(:incoming_email_token)
save!(validate: false)
end
end
...@@ -3,6 +3,7 @@ require 'spec_helper' ...@@ -3,6 +3,7 @@ require 'spec_helper'
describe Admin::ApplicationSettingsController do describe Admin::ApplicationSettingsController do
include StubENV include StubENV
<<<<<<< HEAD
let(:group) { create(:group) } let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) } let(:project) { create(:project, namespace: group) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
...@@ -73,6 +74,28 @@ describe Admin::ApplicationSettingsController do ...@@ -73,6 +74,28 @@ describe Admin::ApplicationSettingsController do
expect(body["version"]).to eq(Gitlab::VERSION) expect(body["version"]).to eq(Gitlab::VERSION)
expect(body).to include('counts') expect(body).to include('counts')
expect(response.status).to eq(200) expect(response.status).to eq(200)
=======
let(:admin) { create(:admin) }
before do
sign_in(admin)
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
describe 'PATCH #update' do
it 'updates the default_project_visibility for string value' do
patch :update, application_setting: { default_project_visibility: "20" }
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.default_project_visibility).to eq Gitlab::VisibilityLevel::PUBLIC
end
it 'falls back to default with default_project_visibility setting is omitted' do
patch :update, application_setting: {}
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.default_project_visibility).to eq Gitlab::VisibilityLevel::PRIVATE
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
end end
end end
end end
...@@ -87,6 +87,12 @@ describe Projects::IssuesController do ...@@ -87,6 +87,12 @@ describe Projects::IssuesController do
end end
describe 'GET #new' do describe 'GET #new' do
it 'redirects to signin if not logged in' do
get :new, namespace_id: project.namespace, project_id: project
expect(response).to redirect_to(new_user_session_path)
end
context 'internal issue tracker' do context 'internal issue tracker' do
before do before do
sign_in(user) sign_in(user)
...@@ -121,6 +127,11 @@ describe Projects::IssuesController do ...@@ -121,6 +127,11 @@ describe Projects::IssuesController do
end end
context 'external issue tracker' do context 'external issue tracker' do
before do
sign_in(user)
project.team << [user, :developer]
end
it 'redirects to the external issue tracker' do it 'redirects to the external issue tracker' do
external = double(new_issue_path: 'https://example.com/issues/new') external = double(new_issue_path: 'https://example.com/issues/new')
allow(project).to receive(:external_issue_tracker).and_return(external) allow(project).to receive(:external_issue_tracker).and_return(external)
...@@ -141,6 +152,24 @@ describe Projects::IssuesController do ...@@ -141,6 +152,24 @@ describe Projects::IssuesController do
it_behaves_like 'update invalid issuable', Issue it_behaves_like 'update invalid issuable', Issue
context 'changing the assignee' do
it 'limits the attributes exposed on the assignee' do
assignee = create(:user)
project.add_developer(assignee)
put :update,
namespace_id: project.namespace.to_param,
project_id: project,
id: issue.iid,
issue: { assignee_id: assignee.id },
format: :json
body = JSON.parse(response.body)
expect(body['assignee'].keys)
.to match_array(%w(name username avatar_url))
end
end
context 'when moving issue to another private project' do context 'when moving issue to another private project' do
let(:another_project) { create(:empty_project, :private) } let(:another_project) { create(:empty_project, :private) }
......
...@@ -366,12 +366,31 @@ describe Projects::MergeRequestsController do ...@@ -366,12 +366,31 @@ describe Projects::MergeRequestsController do
end end
describe 'PUT update' do describe 'PUT update' do
<<<<<<< HEAD
def update_merge_request(params = {}) def update_merge_request(params = {})
post :update, post :update,
namespace_id: project.namespace.to_param, namespace_id: project.namespace.to_param,
project_id: project.to_param, project_id: project.to_param,
id: merge_request.iid, id: merge_request.iid,
merge_request: params merge_request: params
=======
context 'changing the assignee' do
it 'limits the attributes exposed on the assignee' do
assignee = create(:user)
project.add_developer(assignee)
put :update,
namespace_id: project.namespace.to_param,
project_id: project,
id: merge_request.iid,
merge_request: { assignee_id: assignee.id },
format: :json
body = JSON.parse(response.body)
expect(body['assignee'].keys)
.to match_array(%w(name username avatar_url))
end
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
end end
context 'there is no source project' do context 'there is no source project' do
......
...@@ -9,6 +9,13 @@ feature 'Admin updates settings', feature: true do ...@@ -9,6 +9,13 @@ feature 'Admin updates settings', feature: true do
visit admin_application_settings_path visit admin_application_settings_path
end end
scenario 'Change visibility settings' do
choose "application_setting_default_project_visibility_20"
click_button 'Save'
expect(page).to have_content "Application settings saved successfully"
end
scenario 'Change application settings' do scenario 'Change application settings' do
uncheck 'Gravatar enabled' uncheck 'Gravatar enabled'
fill_in 'Home page URL', with: 'https://about.gitlab.com/' fill_in 'Home page URL', with: 'https://about.gitlab.com/'
......
...@@ -2,7 +2,8 @@ require 'spec_helper' ...@@ -2,7 +2,8 @@ require 'spec_helper'
describe "Dashboard Issues Feed", feature: true do describe "Dashboard Issues Feed", feature: true do
describe "GET /issues" do describe "GET /issues" do
let!(:user) { create(:user) } let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') }
let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') }
let!(:project1) { create(:project) } let!(:project1) { create(:project) }
let!(:project2) { create(:project) } let!(:project2) { create(:project) }
...@@ -31,7 +32,7 @@ describe "Dashboard Issues Feed", feature: true do ...@@ -31,7 +32,7 @@ describe "Dashboard Issues Feed", feature: true do
end end
context "issue with basic fields" do context "issue with basic fields" do
let!(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'test desc') } let!(:issue2) { create(:issue, author: user, assignee: assignee, project: project2, description: 'test desc') }
it "renders issue fields" do it "renders issue fields" do
visit issues_dashboard_path(:atom, private_token: user.private_token) visit issues_dashboard_path(:atom, private_token: user.private_token)
...@@ -39,8 +40,8 @@ describe "Dashboard Issues Feed", feature: true do ...@@ -39,8 +40,8 @@ describe "Dashboard Issues Feed", feature: true do
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]") entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]")
expect(entry).to be_present expect(entry).to be_present
expect(entry).to have_selector('author email', text: issue2.author_email) expect(entry).to have_selector('author email', text: issue2.author_public_email)
expect(entry).to have_selector('assignee email', text: issue2.author_email) expect(entry).to have_selector('assignee email', text: issue2.assignee_public_email)
expect(entry).not_to have_selector('labels') expect(entry).not_to have_selector('labels')
expect(entry).not_to have_selector('milestone') expect(entry).not_to have_selector('milestone')
expect(entry).to have_selector('description', text: issue2.description) expect(entry).to have_selector('description', text: issue2.description)
...@@ -50,7 +51,7 @@ describe "Dashboard Issues Feed", feature: true do ...@@ -50,7 +51,7 @@ describe "Dashboard Issues Feed", feature: true do
context "issue with label and milestone" do context "issue with label and milestone" do
let!(:milestone1) { create(:milestone, project: project1, title: 'v1') } let!(:milestone1) { create(:milestone, project: project1, title: 'v1') }
let!(:label1) { create(:label, project: project1, title: 'label1') } let!(:label1) { create(:label, project: project1, title: 'label1') }
let!(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone1) } let!(:issue1) { create(:issue, author: user, assignee: assignee, project: project1, milestone: milestone1) }
before do before do
issue1.labels << label1 issue1.labels << label1
...@@ -62,8 +63,8 @@ describe "Dashboard Issues Feed", feature: true do ...@@ -62,8 +63,8 @@ describe "Dashboard Issues Feed", feature: true do
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]") entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]")
expect(entry).to be_present expect(entry).to be_present
expect(entry).to have_selector('author email', text: issue1.author_email) expect(entry).to have_selector('author email', text: issue1.author_public_email)
expect(entry).to have_selector('assignee email', text: issue1.author_email) expect(entry).to have_selector('assignee email', text: issue1.assignee_public_email)
expect(entry).to have_selector('labels label', text: label1.title) expect(entry).to have_selector('labels label', text: label1.title)
expect(entry).to have_selector('milestone', text: milestone1.title) expect(entry).to have_selector('milestone', text: milestone1.title)
expect(entry).not_to have_selector('description') expect(entry).not_to have_selector('description')
......
...@@ -2,10 +2,11 @@ require 'spec_helper' ...@@ -2,10 +2,11 @@ require 'spec_helper'
describe 'Issues Feed', feature: true do describe 'Issues Feed', feature: true do
describe 'GET /issues' do describe 'GET /issues' do
let!(:user) { create(:user) } let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') }
let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') }
let!(:group) { create(:group) } let!(:group) { create(:group) }
let!(:project) { create(:project) } let!(:project) { create(:project) }
let!(:issue) { create(:issue, author: user, project: project) } let!(:issue) { create(:issue, author: user, assignee: assignee, project: project) }
before do before do
project.team << [user, :developer] project.team << [user, :developer]
...@@ -20,7 +21,8 @@ describe 'Issues Feed', feature: true do ...@@ -20,7 +21,8 @@ describe 'Issues Feed', feature: true do
expect(response_headers['Content-Type']). expect(response_headers['Content-Type']).
to have_content('application/atom+xml') to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{project.name} issues") expect(body).to have_selector('title', text: "#{project.name} issues")
expect(body).to have_selector('author email', text: issue.author_email) expect(body).to have_selector('author email', text: issue.author_public_email)
expect(body).to have_selector('assignee email', text: issue.author_public_email)
expect(body).to have_selector('entry summary', text: issue.title) expect(body).to have_selector('entry summary', text: issue.title)
end end
end end
...@@ -33,7 +35,8 @@ describe 'Issues Feed', feature: true do ...@@ -33,7 +35,8 @@ describe 'Issues Feed', feature: true do
expect(response_headers['Content-Type']). expect(response_headers['Content-Type']).
to have_content('application/atom+xml') to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{project.name} issues") expect(body).to have_selector('title', text: "#{project.name} issues")
expect(body).to have_selector('author email', text: issue.author_email) expect(body).to have_selector('author email', text: issue.author_public_email)
expect(body).to have_selector('assignee email', text: issue.author_public_email)
expect(body).to have_selector('entry summary', text: issue.title) expect(body).to have_selector('entry summary', text: issue.title)
end end
end end
......
require 'spec_helper' require 'spec_helper'
feature 'Group name toggle', js: true do feature 'Group name toggle', feature: true, js: true do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:nested_group_1) { create(:group, parent: group) } let(:nested_group_1) { create(:group, parent: group) }
let(:nested_group_2) { create(:group, parent: nested_group_1) } let(:nested_group_2) { create(:group, parent: nested_group_1) }
......
...@@ -6,7 +6,7 @@ describe 'Issues', feature: true do ...@@ -6,7 +6,7 @@ describe 'Issues', feature: true do
include SortingHelper include SortingHelper
include WaitForAjax include WaitForAjax
let(:project) { create(:project) } let(:project) { create(:project, :public) }
before do before do
login_as :user login_as :user
...@@ -597,6 +597,24 @@ describe 'Issues', feature: true do ...@@ -597,6 +597,24 @@ describe 'Issues', feature: true do
end end
describe 'new issue' do describe 'new issue' do
context 'by unauthenticated user' do
before do
logout
end
it 'redirects to signin then back to new issue after signin' do
visit namespace_project_issues_path(project.namespace, project)
click_link 'New issue'
expect(current_path).to eq new_user_session_path
login_as :user
expect(current_path).to eq new_namespace_project_issue_path(project.namespace, project)
end
end
context 'dropzone upload file', js: true do context 'dropzone upload file', js: true do
before do before do
visit new_namespace_project_issue_path(project.namespace, project) visit new_namespace_project_issue_path(project.namespace, project)
......
...@@ -49,6 +49,26 @@ feature 'Merge requests filter clear button', feature: true, js: true do ...@@ -49,6 +49,26 @@ feature 'Merge requests filter clear button', feature: true, js: true do
end end
end end
context 'when multiple label filters have been applied' do
let!(:label) { create(:label, project: project, name: 'Frontend') }
let(:filter_dropdown) { find("#js-dropdown-label .filter-dropdown") }
before do
visit_merge_requests(project)
init_label_search
end
it 'filters bug label' do
filtered_search.set('~bug')
filter_dropdown.find('.filter-dropdown-item', text: bug.title).click
init_label_search
expect(filter_dropdown.find('.filter-dropdown-item', text: bug.title)).to be_visible
expect(filter_dropdown.find('.filter-dropdown-item', text: label.title)).to be_visible
end
end
context 'when a text search has been conducted' do context 'when a text search has been conducted' do
it 'resets the text search filter' do it 'resets the text search filter' do
visit_merge_requests(project, search: 'Bug') visit_merge_requests(project, search: 'Bug')
......
...@@ -2,6 +2,7 @@ require 'spec_helper' ...@@ -2,6 +2,7 @@ require 'spec_helper'
feature 'New blob creation', feature: true, js: true do feature 'New blob creation', feature: true, js: true do
include WaitForAjax include WaitForAjax
include TargetBranchHelpers
given(:user) { create(:user) } given(:user) { create(:user) }
given(:role) { :developer } given(:role) { :developer }
...@@ -20,19 +21,6 @@ feature 'New blob creation', feature: true, js: true do ...@@ -20,19 +21,6 @@ feature 'New blob creation', feature: true, js: true do
execute_script("ace.edit('editor').setValue('#{content}')") execute_script("ace.edit('editor').setValue('#{content}')")
end end
def select_branch_index(index)
first('button.js-target-branch').click
wait_for_ajax
all('a[data-group="Branches"]')[index].click
end
def create_new_branch(name)
first('button.js-target-branch').click
click_link 'Create new branch'
fill_in 'new_branch_name', with: name
click_button 'Create'
end
def commit_file def commit_file
click_button 'Commit Changes' click_button 'Commit Changes'
end end
...@@ -53,12 +41,12 @@ feature 'New blob creation', feature: true, js: true do ...@@ -53,12 +41,12 @@ feature 'New blob creation', feature: true, js: true do
context 'with different target branch' do context 'with different target branch' do
background do background do
edit_file edit_file
select_branch_index(0) select_branch('feature')
commit_file commit_file
end end
scenario 'creates the blob in the different branch' do scenario 'creates the blob in the different branch' do
expect(page).to have_content 'test' expect(page).to have_content 'feature'
expect(page).to have_content 'successfully created' expect(page).to have_content 'successfully created'
end end
end end
......
...@@ -8,7 +8,7 @@ feature 'Project group links', feature: true, js: true do ...@@ -8,7 +8,7 @@ feature 'Project group links', feature: true, js: true do
let!(:group) { create(:group) } let!(:group) { create(:group) }
background do background do
project.team << [master, :master] project.add_master(master)
login_as(master) login_as(master)
end end
...@@ -29,4 +29,26 @@ feature 'Project group links', feature: true, js: true do ...@@ -29,4 +29,26 @@ feature 'Project group links', feature: true, js: true do
end end
end end
end end
context 'nested group project' do
let!(:nested_group) { create(:group, parent: group) }
let!(:another_group) { create(:group) }
let!(:project) { create(:project, namespace: nested_group) }
background do
group.add_master(master)
another_group.add_master(master)
end
it 'does not show ancestors' do
visit namespace_project_settings_members_path(project.namespace, project)
click_link 'Search for a group'
page.within '.select2-drop' do
expect(page).to have_content(another_group.name)
expect(page).not_to have_content(group.name)
end
end
end
end end
require 'spec_helper'
feature 'New directory creation', feature: true, js: true do
include WaitForAjax
include TargetBranchHelpers
given(:user) { create(:user) }
given(:role) { :developer }
given(:project) { create(:project) }
background do
login_as(user)
project.team << [user, role]
visit namespace_project_tree_path(project.namespace, project, 'master')
open_new_directory_modal
fill_in 'dir_name', with: 'new_directory'
end
def open_new_directory_modal
first('.add-to-tree').click
click_link 'New directory'
end
def create_directory
click_button 'Create directory'
end
context 'with default target branch' do
background do
create_directory
end
scenario 'creates the directory in the default branch' do
expect(page).to have_content 'master'
expect(page).to have_content 'The directory has been successfully created'
expect(page).to have_content 'new_directory'
end
end
context 'with different target branch' do
background do
select_branch('feature')
create_directory
end
scenario 'creates the directory in the different branch' do
expect(page).to have_content 'feature'
expect(page).to have_content 'The directory has been successfully created'
end
end
context 'with a new target branch' do
given(:new_branch_name) { 'new-feature' }
background do
create_new_branch(new_branch_name)
create_directory
end
scenario 'creates the directory in the new branch' do
expect(page).to have_content new_branch_name
expect(page).to have_content 'The directory has been successfully created'
end
scenario 'redirects to the merge request' do
expect(page).to have_content 'New Merge Request'
expect(page).to have_content "From #{new_branch_name} into master"
expect(page).to have_content 'Add new directory'
expect(current_path).to eq(new_namespace_project_merge_request_path(project.namespace, project))
end
end
end
...@@ -246,5 +246,17 @@ const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper ...@@ -246,5 +246,17 @@ const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper
expect(gl.FilteredSearchVisualTokens.unselectTokens).toHaveBeenCalled(); expect(gl.FilteredSearchVisualTokens.unselectTokens).toHaveBeenCalled();
}); });
}); });
describe('toggleInputContainerFocus', () => {
it('toggles on focus', () => {
input.focus();
expect(document.querySelector('.filtered-search-input-container').classList.contains('focus')).toEqual(true);
});
it('toggles on blur', () => {
input.blur();
expect(document.querySelector('.filtered-search-input-container').classList.contains('focus')).toEqual(false);
});
});
}); });
})(); })();
...@@ -136,6 +136,21 @@ describe('Issue', function() { ...@@ -136,6 +136,21 @@ describe('Issue', function() {
expectErrorMessage(); expectErrorMessage();
expect($('.issue_counter')).toHaveText(1); expect($('.issue_counter')).toHaveText(1);
}); });
it('updates counter', () => {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expectPendingRequest(req, $btnClose);
req.success({
id: 34
});
});
expect($('.issue_counter')).toHaveText(1);
$('.issue_counter').text('1,001');
expect($('.issue_counter').text()).toEqual('1,001');
$btnClose.trigger('click');
expect($('.issue_counter').text()).toEqual('1,000');
});
}); });
describe('reopen issue', function() { describe('reopen issue', function() {
......
...@@ -163,5 +163,72 @@ require('~/lib/utils/common_utils'); ...@@ -163,5 +163,72 @@ require('~/lib/utils/common_utils');
expect(gl.utils.isMetaClick(e)).toBe(true); expect(gl.utils.isMetaClick(e)).toBe(true);
}); });
}); });
describe('gl.utils.backOff', () => {
it('solves the promise from the callback', (done) => {
const expectedResponseValue = 'Success!';
gl.utils.backOff((next, stop) => (
new Promise((resolve) => {
resolve(expectedResponseValue);
}).then((resp) => {
stop(resp);
})
)).then((respBackoff) => {
expect(respBackoff).toBe(expectedResponseValue);
done();
});
});
it('catches the rejected promise from the callback ', (done) => {
const errorMessage = 'Mistakes were made!';
gl.utils.backOff((next, stop) => {
new Promise((resolve, reject) => {
reject(new Error(errorMessage));
}).then((resp) => {
stop(resp);
}).catch(err => stop(err));
}).catch((errBackoffResp) => {
expect(errBackoffResp instanceof Error).toBe(true);
expect(errBackoffResp.message).toBe(errorMessage);
done();
});
});
it('solves the promise correctly after retrying a third time', (done) => {
let numberOfCalls = 1;
const expectedResponseValue = 'Success!';
gl.utils.backOff((next, stop) => (
new Promise((resolve) => {
resolve(expectedResponseValue);
}).then((resp) => {
if (numberOfCalls < 3) {
numberOfCalls += 1;
next();
} else {
stop(resp);
}
})
)).then((respBackoff) => {
expect(respBackoff).toBe(expectedResponseValue);
expect(numberOfCalls).toBe(3);
done();
});
}, 10000);
it('rejects the backOff promise after timing out', (done) => {
const expectedResponseValue = 'Success!';
gl.utils.backOff(next => (
new Promise((resolve) => {
resolve(expectedResponseValue);
}).then(() => {
setTimeout(next(), 5000); // it will time out
})
), 3000).catch((errBackoffResp) => {
expect(errBackoffResp instanceof Error).toBe(true);
expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT');
done();
});
}, 10000);
});
}); });
})(); })();
require 'spec_helper'
describe Gitlab::UrlBlocker, lib: true do
describe '#blocked_url?' do
it 'allows imports from configured web host and port' do
import_url = "http://#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.port}/t.git"
expect(described_class.blocked_url?(import_url)).to be false
end
it 'allows imports from configured SSH host and port' do
import_url = "http://#{Gitlab.config.gitlab_shell.ssh_host}:#{Gitlab.config.gitlab_shell.ssh_port}/t.git"
expect(described_class.blocked_url?(import_url)).to be false
end
it 'returns true for bad localhost hostname' do
expect(described_class.blocked_url?('https://localhost:65535/foo/foo.git')).to be true
end
it 'returns true for bad port' do
expect(described_class.blocked_url?('https://gitlab.com:25/foo/foo.git')).to be true
end
it 'returns true for invalid URL' do
expect(described_class.blocked_url?('http://:8080')).to be true
end
it 'returns false for legitimate URL' do
expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git')).to be false
end
end
end
require 'spec_helper'
describe Gitlab::VisibilityLevel, lib: true do
describe '.level_value' do
it 'converts "public" to integer value' do
expect(described_class.level_value('public')).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
it 'converts string integer to integer value' do
expect(described_class.level_value('20')).to eq(20)
end
it 'defaults to PRIVATE when string value is not valid' do
expect(described_class.level_value('invalid')).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
it 'defaults to PRIVATE when integer value is not valid' do
expect(described_class.level_value(100)).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end
end
...@@ -110,6 +110,24 @@ describe HasStatus do ...@@ -110,6 +110,24 @@ describe HasStatus do
it { is_expected.to eq 'running' } it { is_expected.to eq 'running' }
end end
context 'when one status finished and second is still created' do
let!(:statuses) do
[create(type, status: :success), create(type, status: :created)]
end
it { is_expected.to eq 'running' }
end
context 'when there is a manual status before created status' do
let!(:statuses) do
[create(type, status: :success),
create(type, status: :manual, allow_failure: false),
create(type, status: :created)]
end
it { is_expected.to eq 'manual' }
end
context 'when one status is a blocking manual action' do context 'when one status is a blocking manual action' do
let!(:statuses) do let!(:statuses) do
[create(type, status: :failed), [create(type, status: :failed),
......
...@@ -230,6 +230,20 @@ describe Project, models: true do ...@@ -230,6 +230,20 @@ describe Project, models: true do
expect(project2.import_data).to be_nil expect(project2.import_data).to be_nil
end end
it "does not allow blocked import_url localhost" do
project2 = build(:empty_project, import_url: 'http://localhost:9000/t.git')
expect(project2).to be_invalid
expect(project2.errors[:import_url]).to include('imports are not allowed from that URL')
end
it "does not allow blocked import_url port" do
project2 = build(:empty_project, import_url: 'http://github.com:25/t.git')
expect(project2).to be_invalid
expect(project2.errors[:import_url]).to include('imports are not allowed from that URL')
end
describe 'project pending deletion' do describe 'project pending deletion' do
let!(:project_pending_deletion) do let!(:project_pending_deletion) do
create(:empty_project, create(:empty_project,
......
...@@ -52,6 +52,7 @@ describe ProjectWiki, models: true do ...@@ -52,6 +52,7 @@ describe ProjectWiki, models: true do
expect(project_wiki.http_url_to_repo(user)).to start_with("http://#{user.username}@") expect(project_wiki.http_url_to_repo(user)).to start_with("http://#{user.username}@")
end end
<<<<<<< HEAD
end end
end end
...@@ -60,6 +61,8 @@ describe ProjectWiki, models: true do ...@@ -60,6 +61,8 @@ describe ProjectWiki, models: true do
gitlab_kerberos_url = Gitlab.config.build_gitlab_kerberos_url gitlab_kerberos_url = Gitlab.config.build_gitlab_kerberos_url
repo_kerberos_url = "#{gitlab_kerberos_url}/#{subject.path_with_namespace}.git" repo_kerberos_url = "#{gitlab_kerberos_url}/#{subject.path_with_namespace}.git"
expect(subject.kerberos_url_to_repo).to eq(repo_kerberos_url) expect(subject.kerberos_url_to_repo).to eq(repo_kerberos_url)
=======
>>>>>>> b22d4c2e9f171b6cabeb537f3a3a0a688a4e0cc3
end end
end end
......
...@@ -43,14 +43,22 @@ describe Route, models: true do ...@@ -43,14 +43,22 @@ describe Route, models: true do
end end
context 'name update' do context 'name update' do
before { route.update_attributes(name: 'bar') }
it "updates children routes with new path" do it "updates children routes with new path" do
route.update_attributes(name: 'bar')
expect(described_class.exists?(name: 'bar')).to be_truthy expect(described_class.exists?(name: 'bar')).to be_truthy
expect(described_class.exists?(name: 'bar / test')).to be_truthy expect(described_class.exists?(name: 'bar / test')).to be_truthy
expect(described_class.exists?(name: 'bar / test / foo')).to be_truthy expect(described_class.exists?(name: 'bar / test / foo')).to be_truthy
expect(described_class.exists?(name: 'gitlab-org')).to be_truthy expect(described_class.exists?(name: 'gitlab-org')).to be_truthy
end end
it 'handles a rename from nil' do
# Note: using `update_columns` to skip all validation and callbacks
route.update_columns(name: nil)
expect { route.update_attributes(name: 'bar') }
.to change { route.name }.from(nil).to('bar')
end
end end
end end
end end
...@@ -534,6 +534,12 @@ describe API::Issues, api: true do ...@@ -534,6 +534,12 @@ describe API::Issues, api: true do
describe "GET /projects/:id/issues" do describe "GET /projects/:id/issues" do
let(:base_url) { "/projects/#{project.id}" } let(:base_url) { "/projects/#{project.id}" }
it 'returns 404 when project does not exist' do
get api('/projects/1000/issues', non_member)
expect(response).to have_http_status(404)
end
it "returns 404 on private projects for other users" do it "returns 404 on private projects for other users" do
private_project = create(:empty_project, :private) private_project = create(:empty_project, :private)
create(:issue, project: private_project) create(:issue, project: private_project)
......
...@@ -439,6 +439,12 @@ describe API::V3::Issues, api: true do ...@@ -439,6 +439,12 @@ describe API::V3::Issues, api: true do
describe "GET /projects/:id/issues" do describe "GET /projects/:id/issues" do
let(:base_url) { "/projects/#{project.id}" } let(:base_url) { "/projects/#{project.id}" }
it 'returns 404 when project does not exist' do
get v3_api('/projects/1000/issues', non_member)
expect(response).to have_http_status(404)
end
it "returns 404 on private projects for other users" do it "returns 404 on private projects for other users" do
private_project = create(:empty_project, :private) private_project = create(:empty_project, :private)
create(:issue, project: private_project) create(:issue, project: private_project)
......
require 'spec_helper'
describe CreateBranchService, services: true do
let(:user) { create(:user) }
let(:service) { described_class.new(project, user) }
describe '#execute' do
context 'when repository is empty' do
let(:project) { create(:project_empty_repo) }
it 'creates master branch' do
service.execute('my-feature', 'master')
expect(project.repository.branch_exists?('master')).to be_truthy
end
it 'creates my-feature branch' do
service.execute('my-feature', 'master')
expect(project.repository.branch_exists?('my-feature')).to be_truthy
end
end
end
end
...@@ -120,6 +120,26 @@ describe Projects::ImportService, services: true do ...@@ -120,6 +120,26 @@ describe Projects::ImportService, services: true do
end end
end end
context 'with blocked import_URL' do
it 'fails with localhost' do
project.import_url = 'https://localhost:9000/vim/vim.git'
result = described_class.new(project, user).execute
expect(result[:status]).to eq :error
expect(result[:message]).to end_with 'Blocked import URL.'
end
it 'fails with port 25' do
project.import_url = "https://github.com:25/vim/vim.git"
result = described_class.new(project, user).execute
expect(result[:status]).to eq :error
expect(result[:message]).to end_with 'Blocked import URL.'
end
end
def stub_github_omniauth_provider def stub_github_omniauth_provider
provider = OpenStruct.new( provider = OpenStruct.new(
'name' => 'github', 'name' => 'github',
......
...@@ -5,6 +5,7 @@ describe SystemHooksService, services: true do ...@@ -5,6 +5,7 @@ describe SystemHooksService, services: true do
let(:project) { create :project } let(:project) { create :project }
let(:project_member) { create :project_member } let(:project_member) { create :project_member }
let(:key) { create(:key, user: user) } let(:key) { create(:key, user: user) }
let(:deploy_key) { create(:key) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:group_member) { create(:group_member) } let(:group_member) { create(:group_member) }
...@@ -18,6 +19,8 @@ describe SystemHooksService, services: true do ...@@ -18,6 +19,8 @@ describe SystemHooksService, services: true do
it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) } it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) }
it { expect(event_data(key, :create)).to include(:username, :key, :id) } it { expect(event_data(key, :create)).to include(:username, :key, :id) }
it { expect(event_data(key, :destroy)).to include(:username, :key, :id) } it { expect(event_data(key, :destroy)).to include(:username, :key, :id) }
it { expect(event_data(deploy_key, :create)).to include(:key, :id) }
it { expect(event_data(deploy_key, :destroy)).to include(:key, :id) }
it do it do
project.old_path_with_namespace = 'renamed_from_path' project.old_path_with_namespace = 'renamed_from_path'
......
module TargetBranchHelpers
def select_branch(name)
first('button.js-target-branch').click
wait_for_ajax
all('a[data-group="Branches"]').find do |el|
el.text == name
end.click
end
def create_new_branch(name)
first('button.js-target-branch').click
click_link 'Create new branch'
fill_in 'new_branch_name', with: name
click_button 'Create'
end
end
require 'rake_helper'
describe 'tokens rake tasks' do
let!(:user) { create(:user) }
before do
Rake.application.rake_require 'tasks/tokens'
end
describe 'reset_all task' do
it 'invokes create_hooks task' do
expect { run_rake_task('tokens:reset_all_auth') }.to change { user.reload.authentication_token }
end
end
describe 'reset_all_email task' do
it 'invokes create_hooks task' do
expect { run_rake_task('tokens:reset_all_email') }.to change { user.reload.incoming_email_token }
end
end
end
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