summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.babelrc21
-rw-r--r--.gitlab-ci.yml6
-rw-r--r--CHANGELOG.md15
-rw-r--r--app/assets/javascripts/boards/components/modal/index.js4
-rw-r--r--app/assets/javascripts/environments/components/environment_external_url.js1
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown.js3
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js26
-rw-r--r--app/assets/javascripts/issue.js8
-rw-r--r--app/assets/javascripts/merge_request_widget.js4
-rw-r--r--app/assets/stylesheets/framework/common.scss6
-rw-r--r--app/assets/stylesheets/framework/filters.scss21
-rw-r--r--app/assets/stylesheets/pages/note_form.scss12
-rw-r--r--app/controllers/projects/application_controller.rb5
-rw-r--r--app/controllers/projects/blob_controller.rb5
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb2
-rw-r--r--app/controllers/projects/settings/members_controller.rb1
-rw-r--r--app/controllers/projects/tree_controller.rb1
-rw-r--r--app/helpers/blob_helper.rb2
-rw-r--r--app/helpers/commits_helper.rb2
-rw-r--r--app/helpers/import_helper.rb2
-rw-r--r--app/models/application_setting.rb9
-rw-r--r--app/models/concerns/has_status.rb1
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/group.rb2
-rw-r--r--app/models/project.rb9
-rw-r--r--app/models/project_services/prometheus_service.rb9
-rw-r--r--app/models/project_wiki.rb7
-rw-r--r--app/models/route.rb2
-rw-r--r--app/services/create_branch_service.rb6
-rw-r--r--app/services/projects/import_service.rb3
-rw-r--r--app/services/system_hooks_service.rb9
-rw-r--r--app/validators/importable_url_validator.rb11
-rw-r--r--app/views/admin/appearances/_form.html.haml2
-rw-r--r--app/views/admin/application_settings/_form.html.haml2
-rw-r--r--app/views/events/_event.atom.builder2
-rw-r--r--app/views/events/event/_note.html.haml2
-rw-r--r--app/views/help/index.html.haml2
-rw-r--r--app/views/import/bitbucket/status.html.haml6
-rw-r--r--app/views/import/gitlab/status.html.haml2
-rw-r--r--app/views/import/google_code/new.html.haml2
-rw-r--r--app/views/import/google_code/status.html.haml6
-rw-r--r--app/views/issues/_issue.atom.builder4
-rw-r--r--app/views/koding/index.html.haml2
-rw-r--r--app/views/profiles/show.html.haml2
-rw-r--r--app/views/projects/blob/_image.html.haml2
-rw-r--r--app/views/projects/blob/_text.html.haml2
-rw-r--r--app/views/projects/blob/edit.html.haml2
-rw-r--r--app/views/projects/buttons/_koding.html.haml2
-rw-r--r--app/views/projects/cycle_analytics/_overview.html.haml2
-rw-r--r--app/views/projects/environments/_external_url.html.haml2
-rw-r--r--app/views/projects/merge_requests/_show.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_how_to_merge.html.haml2
-rw-r--r--app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml4
-rw-r--r--app/views/projects/services/mattermost_slash_commands/_help.html.haml2
-rw-r--r--app/views/projects/services/slack_slash_commands/_help.html.haml4
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml2
-rw-r--r--changelogs/unreleased/28058-hide-emails-in-atom-feeds.yml4
-rw-r--r--changelogs/unreleased/28499-fix-large-text-tooltip-in-diff-file-name.yml4
-rw-r--r--changelogs/unreleased/28660-fix-dismissable-error-close-not-visible-enough.yml4
-rw-r--r--changelogs/unreleased/29428-new-directory-from-existing-branch.yml4
-rw-r--r--changelogs/unreleased/add-test-backoff-util.yml4
-rw-r--r--changelogs/unreleased/api-project-issues-404.yml4
-rw-r--r--changelogs/unreleased/bugfix-systemhook.yml4
-rw-r--r--changelogs/unreleased/dz-hide-share-group-ancestors.yml4
-rw-r--r--changelogs/unreleased/feature-tokens-rake-task.yml4
-rw-r--r--changelogs/unreleased/make-karma-fast-again.yml4
-rw-r--r--changelogs/unreleased/simplify-docs-trigger.yml4
-rw-r--r--changelogs/unreleased/ssrf-protections.yml4
-rw-r--r--config/karma.config.js32
-rw-r--r--config/webpack.config.js8
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb2
-rw-r--r--db/migrate/20170313213916_add_index_to_user_ghost.rb24
-rw-r--r--db/migrate/20170317203554_index_routes_path_for_like.rb29
-rw-r--r--db/post_migrate/20170313133418_rename_more_reserved_project_names.rb31
-rw-r--r--db/schema.rb4
-rw-r--r--doc/api/v3_to_v4.md60
-rw-r--r--doc/update/8.17-to-9.0.md8
-rw-r--r--lib/api/issues.rb2
-rw-r--r--lib/api/v3/issues.rb2
-rw-r--r--lib/banzai/filter/image_link_filter.rb4
-rw-r--r--lib/banzai/filter/video_link_filter.rb1
-rw-r--r--lib/gitlab/url_blocker.rb59
-rw-r--r--lib/gitlab/url_sanitizer.rb6
-rw-r--r--lib/gitlab/visibility_level.rb4
-rw-r--r--lib/tasks/gitlab/assets.rake8
-rw-r--r--lib/tasks/migrate/setup_postgresql.rake2
-rw-r--r--lib/tasks/tokens.rake38
-rw-r--r--package.json11
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb28
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb18
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb18
-rw-r--r--spec/features/admin/admin_settings_spec.rb7
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb15
-rw-r--r--spec/features/atom/issues_spec.rb11
-rw-r--r--spec/features/groups/group_name_toggle_spec.rb (renamed from spec/features/groups/group_name_toggle.rb)2
-rw-r--r--spec/features/merge_requests/reset_filters_spec.rb20
-rw-r--r--spec/features/projects/blobs/user_create_spec.rb18
-rw-r--r--spec/features/projects/group_links_spec.rb24
-rw-r--r--spec/features/projects/user_create_dir_spec.rb72
-rw-r--r--spec/features/projects/wiki/user_git_access_wiki_page_spec.rb26
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js12
-rw-r--r--spec/javascripts/issue_spec.js15
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js67
-rw-r--r--spec/javascripts/test_bundle.js39
-rw-r--r--spec/lib/gitlab/url_blocker_spec.rb31
-rw-r--r--spec/lib/gitlab/url_sanitizer_spec.rb34
-rw-r--r--spec/lib/gitlab/visibility_level_spec.rb21
-rw-r--r--spec/models/concerns/has_status_spec.rb18
-rw-r--r--spec/models/project_spec.rb22
-rw-r--r--spec/models/project_wiki_spec.rb21
-rw-r--r--spec/models/route_spec.rb12
-rw-r--r--spec/requests/api/issues_spec.rb6
-rw-r--r--spec/requests/api/v3/issues_spec.rb6
-rw-r--r--spec/services/create_branch_service_spec.rb24
-rw-r--r--spec/services/projects/import_service_spec.rb20
-rw-r--r--spec/services/system_hooks_service_spec.rb3
-rw-r--r--spec/support/target_branch_helpers.rb16
-rw-r--r--spec/tasks/tokens_spec.rb21
-rw-r--r--yarn.lock70
123 files changed, 1121 insertions, 266 deletions
diff --git a/.babelrc b/.babelrc
new file mode 100644
index 00000000000..ee4c391da30
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,21 @@
+{
+ "presets": [
+ ["latest", { "es2015": { "modules": false } }],
+ "stage-2"
+ ],
+ "env": {
+ "coverage": {
+ "plugins": [
+ ["istanbul", {
+ "exclude": [
+ "app/assets/javascripts/droplab/**/*",
+ "spec/javascripts/**/*"
+ ]
+ }],
+ ["transform-define", {
+ "process.env.BABEL_ENV": "coverage"
+ }]
+ ]
+ }
+ }
+}
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 080d8cd6c7f..34c10b3b77f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -277,6 +277,8 @@ rake karma:
stage: test
<<: *use-db
<<: *dedicated-runner
+ variables:
+ BABEL_ENV: "coverage"
script:
- bundle exec rake karma
artifacts:
@@ -389,9 +391,11 @@ trigger_docs:
cache: {}
artifacts: {}
script:
- - "curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master -F variables[PROJECT]=ce https://gitlab.com/api/v3/projects/1794617/trigger/builds"
+ - "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
+ - master@gitlab-org/gitlab-ee
# Notify slack in the end
notify:slack:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 42e094bdfc6..da1898e3770 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 8.17.4 (2017-03-19)
+
+- 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.17.3 (2017-03-07)
- Fix the redirect to custom home page URL. !9518
@@ -210,6 +215,11 @@ entry.
- Remove deprecated GitlabCiService.
- Requeue pending deletion projects.
+## 8.16.8 (2017-03-19)
+
+- 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)
- No changes.
@@ -411,6 +421,11 @@ entry.
- Add margin to markdown math blocks.
- Add hover state to MR comment reply button.
+## 8.15.8 (2017-03-19)
+
+- 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.15.7 (2017-02-15)
- No changes.
diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js
index 1b66c8b922d..4240c97617d 100644
--- a/app/assets/javascripts/boards/components/modal/index.js
+++ b/app/assets/javascripts/boards/components/modal/index.js
@@ -64,6 +64,7 @@ require('./empty_state');
},
filter: {
handler() {
+ this.page = 1;
this.loadIssues(true);
},
deep: true,
@@ -115,6 +116,9 @@ require('./empty_state');
return this.activeTab === 'selected' && this.selectedIssues.length === 0;
},
},
+ created() {
+ this.page = 1;
+ },
components: {
'modal-header': gl.issueBoards.ModalHeader,
'modal-list': gl.issueBoards.ModalList,
diff --git a/app/assets/javascripts/environments/components/environment_external_url.js b/app/assets/javascripts/environments/components/environment_external_url.js
index a554998f52c..b4f9eb357fd 100644
--- a/app/assets/javascripts/environments/components/environment_external_url.js
+++ b/app/assets/javascripts/environments/components/environment_external_url.js
@@ -14,6 +14,7 @@ export default {
class="btn external_url"
:href="externalUrl"
target="_blank"
+ rel="noopener noreferrer"
title="Environment external URL">
<i class="fa fa-external-link" aria-hidden="true"></i>
</a>
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
index 134bdc6ad80..e7bf530d343 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
@@ -38,6 +38,7 @@
gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true);
}
+ this.resetFilters();
this.dismissDropdown();
this.dispatchInputEvent();
}
@@ -107,7 +108,7 @@
const hook = this.getCurrentHook();
if (hook) {
- const data = hook.list.data;
+ const data = hook.list.data || [];
const results = data.map((o) => {
const updated = o;
updated.droplab_hidden = false;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 7ace51748aa..c6bb7fda8f2 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -40,6 +40,8 @@ import FilteredSearchContainer from './container';
this.unselectEditTokensWrapper = this.unselectEditTokens.bind(this);
this.editTokenWrapper = this.editToken.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.addEventListener('submit', this.handleFormSubmit);
@@ -51,11 +53,13 @@ import FilteredSearchContainer from './container';
this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper);
this.filteredSearchInput.addEventListener('click', this.tokenChange);
this.filteredSearchInput.addEventListener('keyup', this.tokenChange);
+ this.filteredSearchInput.addEventListener('focus', this.addInputContainerFocusWrapper);
this.tokensContainer.addEventListener('click', FilteredSearchManager.selectToken);
this.tokensContainer.addEventListener('dblclick', this.editTokenWrapper);
this.clearSearchButton.addEventListener('click', this.clearSearchWrapper);
document.addEventListener('click', gl.FilteredSearchVisualTokens.unselectTokens);
document.addEventListener('click', this.unselectEditTokensWrapper);
+ document.addEventListener('click', this.removeInputContainerFocusWrapper);
document.addEventListener('keydown', this.removeSelectedTokenWrapper);
}
@@ -69,11 +73,13 @@ import FilteredSearchContainer from './container';
this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper);
this.filteredSearchInput.removeEventListener('click', this.tokenChange);
this.filteredSearchInput.removeEventListener('keyup', this.tokenChange);
+ this.filteredSearchInput.removeEventListener('focus', this.addInputContainerFocusWrapper);
this.tokensContainer.removeEventListener('click', FilteredSearchManager.selectToken);
this.tokensContainer.removeEventListener('dblclick', this.editTokenWrapper);
this.clearSearchButton.removeEventListener('click', this.clearSearchWrapper);
document.removeEventListener('click', gl.FilteredSearchVisualTokens.unselectTokens);
document.removeEventListener('click', this.unselectEditTokensWrapper);
+ document.removeEventListener('click', this.removeInputContainerFocusWrapper);
document.removeEventListener('keydown', this.removeSelectedTokenWrapper);
}
@@ -124,6 +130,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) {
const button = e.target.closest('.selectable');
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index ef4029a8623..47e675f537e 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -2,6 +2,7 @@
/* global Flash */
require('./flash');
+require('~/lib/utils/text_utility');
require('vendor/jquery.waitforimages');
require('./task_list');
@@ -50,20 +51,21 @@ class Issue {
success: function(data, textStatus, jqXHR) {
if ('id' in data) {
$(document).trigger('issuable:change');
- const currentTotal = Number($('.issue_counter').text());
+ let total = Number($('.issue_counter').text().replace(/[^\d]/, ''));
if (isClose) {
$('a.btn-close').addClass('hidden');
$('a.btn-reopen').removeClass('hidden');
$('div.status-box-closed').removeClass('hidden');
$('div.status-box-open').addClass('hidden');
- $('.issue_counter').text(currentTotal - 1);
+ total -= 1;
} else {
$('a.btn-reopen').addClass('hidden');
$('a.btn-close').removeClass('hidden');
$('div.status-box-closed').addClass('hidden');
$('div.status-box-open').removeClass('hidden');
- $('.issue_counter').text(currentTotal + 1);
+ total += 1;
}
+ $('.issue_counter').text(gl.text.addDelimiter(total));
} else {
new Flash(issueFailMessage, 'alert');
}
diff --git a/app/assets/javascripts/merge_request_widget.js b/app/assets/javascripts/merge_request_widget.js
index 94a4f24f1d7..0e2af3df071 100644
--- a/app/assets/javascripts/merge_request_widget.js
+++ b/app/assets/javascripts/merge_request_widget.js
@@ -14,13 +14,13 @@ import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
<%= ci_success_icon %>
<span>
Deployed to
- <a href="<%- url %>" target="_blank" class="environment">
+ <a href="<%- url %>" target="_blank" rel="noopener noreferrer" class="environment">
<%- name %>
</a>
<span class="js-environment-timeago" data-toggle="tooltip" data-placement="top" data-title="<%- deployed_at_formatted %>">
<%- deployed_at %>
</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>
View on <%- external_url_formatted %>
</a>
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index a4b38723bbd..2c33b235980 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -429,3 +429,9 @@ table {
@include str-truncated(100%);
}
}
+
+.tooltip {
+ .tooltip-inner {
+ word-wrap: break-word;
+ }
+}
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 6bdaa7cdd6f..51805c5d734 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -173,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 {
position: relative;
min-width: 200px;
- padding-left: 0;
- padding-right: 25px;
+ padding: 5px 25px 6px 0;
border-color: transparent;
- &:focus ~ .fa-filter {
- color: $common-gray-dark;
- }
-
&:focus,
&:hover {
outline: none;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index c2156a5ac69..927bf9805ce 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -148,6 +148,18 @@
.error-alert > .alert {
margin-top: 5px;
margin-bottom: 5px;
+
+ &.alert-dismissable {
+ .close {
+ color: $white-light;
+ opacity: 0.85;
+ font-weight: normal;
+
+ &:hover {
+ opacity: 1;
+ }
+ }
+ }
}
.discussion-body,
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index e2f81b09adc..f1a93ccb3ad 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -89,4 +89,9 @@ class Projects::ApplicationController < ApplicationController
def builds_enabled
return render_404 unless @project.feature_available?(:builds, current_user)
end
+
+ def update_ref
+ branch_exists = @repository.find_branch(@target_branch)
+ @ref = @target_branch if branch_exists
+ end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 52fc67d162c..80a95c6158b 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -89,11 +89,6 @@ class Projects::BlobController < Projects::ApplicationController
private
- def update_ref
- branch_exists = @repository.find_branch(@target_branch)
- @ref = @target_branch if branch_exists
- end
-
def blob
@blob ||= Blob.decorate(@repository.blob_at(@commit.id, @path))
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 491cd5dc351..cdb5b4173d3 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -148,7 +148,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
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
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 82f9b6e06db..677a8a1a73a 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -308,7 +308,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
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
rescue ActiveRecord::StaleObjectError
diff --git a/app/controllers/projects/settings/members_controller.rb b/app/controllers/projects/settings/members_controller.rb
index cbfa2afa959..54f9dceddef 100644
--- a/app/controllers/projects/settings/members_controller.rb
+++ b/app/controllers/projects/settings/members_controller.rb
@@ -9,6 +9,7 @@ module Projects
@skip_groups = @group_links.pluck(:group_id)
@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
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 4f094146348..637b61504d8 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -34,6 +34,7 @@ class Projects::TreeController < Projects::ApplicationController
def create_dir
return render_404 unless @commit_params.values.all?
+ update_ref
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)),
failure_path: namespace_project_tree_path(@project.namespace, @project, @ref))
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 0b0c6a07efd..8631bc54509 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -215,6 +215,6 @@ module BlobHelper
end
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
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 8aad39e148b..cef624430da 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -211,7 +211,7 @@ module CommitsHelper
external_url = environment.external_url_for(diff_new_path, commit_sha)
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')
end
end
diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb
index a0642a1894b..a57b5a8fea5 100644
--- a/app/helpers/import_helper.rb
+++ b/app/helpers/import_helper.rb
@@ -7,7 +7,7 @@ module ImportHelper
def provider_project_link(provider, 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
private
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index be632930895..671a0fe98cc 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -163,6 +163,8 @@ class ApplicationSetting < ActiveRecord::Base
end
def self.current
+ ensure_cache_setup
+
Rails.cache.fetch(CACHE_KEY) do
ApplicationSetting.last
end
@@ -176,9 +178,16 @@ class ApplicationSetting < ActiveRecord::Base
end
def self.cached
+ ensure_cache_setup
Rails.cache.fetch(CACHE_KEY)
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
{
after_sign_up_text: nil,
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index 5101cc7e687..0a1a65da05a 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -31,6 +31,7 @@ module HasStatus
WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
WHEN (#{running})+(#{pending})>0 THEN 'running'
WHEN (#{manual})>0 THEN 'manual'
+ WHEN (#{created})>0 THEN 'running'
ELSE 'failed'
END)"
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 91f4eb13ecc..e7bd20b322a 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -48,11 +48,13 @@ module Issuable
delegate :name,
:email,
+ :public_email,
to: :author,
prefix: true
delegate :name,
:email,
+ :public_email,
to: :assignee,
allow_nil: true,
prefix: true
diff --git a/app/models/event.rb b/app/models/event.rb
index d7ca8e3c599..5c34844b5d3 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -16,7 +16,7 @@ class Event < ActiveRecord::Base
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: :merge_request, prefix: true, allow_nil: true
delegate :title, to: :note, prefix: true, allow_nil: true
diff --git a/app/models/group.rb b/app/models/group.rb
index bd0ecae3da4..60274386103 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -207,7 +207,7 @@ class Group < Namespace
end
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
def users_with_parents
diff --git a/app/models/project.rb b/app/models/project.rb
index 17cf8226bcc..da4704554b3 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -196,6 +196,7 @@ class Project < ActiveRecord::Base
validates :name, uniqueness: { scope: :namespace_id }
validates :path, uniqueness: { scope: :namespace_id }
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 }
validate :check_limit, on: :create
validate :avatar_type,
@@ -880,13 +881,9 @@ class Project < ActiveRecord::Base
end
def http_url_to_repo(user = nil)
- url = web_url
+ credentials = Gitlab::UrlSanitizer.http_credentials_for_user(user)
- if user
- url.sub!(%r{\Ahttps?://}) { |protocol| "#{protocol}#{user.username}@" }
- end
-
- "#{url}.git"
+ Gitlab::UrlSanitizer.new("#{web_url}.git", credentials: credentials).full_url
end
# Check if current branch name is marked as protected in the system
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index 375966b9efc..4d7c81a721f 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -30,7 +30,14 @@ class PrometheusService < MonitoringService
end
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
def self.to_param
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 539b31780b3..70eef359cdd 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -42,8 +42,11 @@ class ProjectWiki
url_to_repo
end
- def http_url_to_repo
- [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
+ def http_url_to_repo(user = nil)
+ url = "#{Gitlab.config.gitlab.url}/#{path_with_namespace}.git"
+ credentials = Gitlab::UrlSanitizer.http_credentials_for_user(user)
+
+ Gitlab::UrlSanitizer.new(url, credentials: credentials).full_url
end
def wiki_base_path
diff --git a/app/models/route.rb b/app/models/route.rb
index 73574a6206b..41e6eb7cb73 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -21,7 +21,7 @@ class Route < ActiveRecord::Base
attributes[:path] = route.path.sub(path_was, path)
end
- if name_changed? && route.name.present?
+ if name_changed? && name_was.present? && route.name.present?
attributes[:name] = route.name.sub(name_was, name)
end
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index b07338d500a..673ed02f952 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -25,12 +25,12 @@ class CreateBranchService < BaseService
private
def create_master_branch
- project.repository.commit_file(
+ project.repository.create_file(
current_user,
'/README.md',
'',
message: 'Add README.md',
- branch_name: 'master',
- update: false)
+ branch_name: 'master'
+ )
end
end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index 1c5a549feb9..d484a96f785 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -33,6 +33,7 @@ module Projects
def import_repository
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)
rescue => e
# Expire cache to prevent scenarios such as:
@@ -40,7 +41,7 @@ module Projects
# 2. Retried import, repo is broken or not imported but +exists?+ still returns true
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
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 868fa7b3f21..af0ddbe5934 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -24,10 +24,9 @@ class SystemHooksService
key: model.key,
id: model.id
)
+
if model.user
- data.merge!(
- username: model.user.username
- )
+ data[:username] = model.user.username
end
when Project
data.merge!(project_data(model))
@@ -35,8 +34,6 @@ class SystemHooksService
if event == :rename || event == :transfer
data[:old_path_with_namespace] = model.old_path_with_namespace
end
-
- data
when User
data.merge!({
name: model.name,
@@ -59,6 +56,8 @@ class SystemHooksService
when GroupMember
data.merge!(group_member_data(model))
end
+
+ data
end
def build_event_name(model, event)
diff --git a/app/validators/importable_url_validator.rb b/app/validators/importable_url_validator.rb
new file mode 100644
index 00000000000..37a314adee6
--- /dev/null
+++ b/app/validators/importable_url_validator.rb
@@ -0,0 +1,11 @@
+# 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
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index 9175b3d3f96..e403a9da616 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -48,7 +48,7 @@
.form-actions
= f.submit 'Save', class: 'btn btn-save append-right-10'
- 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
%span.pull-right
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 00366b0a8c9..3eab065bb9f 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -404,7 +404,7 @@
Enable Sentry
.help-block
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
= f.label :sentry_dsn, 'Sentry DSN', class: 'control-label col-sm-2'
diff --git a/app/views/events/_event.atom.builder b/app/views/events/_event.atom.builder
index 43a52cf3002..158061579f6 100644
--- a/app/views/events/_event.atom.builder
+++ b/app/views/events/_event.atom.builder
@@ -9,7 +9,7 @@ xml.entry do
xml.author do
xml.name event.author_name
- xml.email event.author_email
+ xml.email event.author_public_email
end
xml.summary(type: "xhtml") do |summary|
diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml
index f08c96df309..64b5a733b77 100644
--- a/app/views/events/event/_note.html.haml
+++ b/app/views/events/event/_note.html.haml
@@ -15,6 +15,6 @@
= link_to note.attachment.url, target: '_blank' do
= image_tag note.attachment.url, class: 'note-image-attach'
- 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
= note.attachment_identifier
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 31631887317..f93b6b63426 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -17,7 +17,7 @@
%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'}.
+ Read more about GitLab at #{link_to promo_host, promo_url, target: '_blank', rel: 'noopener noreferrer'}.
- if current_application_settings.help_page_text.present?
%hr
= markdown_field(current_application_settings, :help_page_text)
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index e18bd47798b..e6058617ac9 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -33,7 +33,7 @@
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%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
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
@@ -50,7 +50,7 @@
- @repos.each do |repo|
%tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
%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
%fieldset.row
.input-group
@@ -70,7 +70,7 @@
- @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
%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-actions-job-status
= label_tag 'Incompatible Project', nil, class: 'label label-danger'
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index d5b88709a34..7456799ca0e 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -43,7 +43,7 @@
- @repos.each do |repo|
%tr{ id: "repo_#{repo["id"]}" }
%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
= import_project_target(repo['namespace']['path'], repo['name'])
%td.import-actions.job-status
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
index 336becd229e..c5800a1cca0 100644
--- a/app/views/import/google_code/new.html.haml
+++ b/app/views/import/google_code/new.html.haml
@@ -13,7 +13,7 @@
%li
%p
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
%p
Make sure you're logged into the account that owns the projects you'd like to import.
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index 5e01af008be..60de6bfe816 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -36,7 +36,7 @@
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%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
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
@@ -53,7 +53,7 @@
- @repos.each do |repo|
%tr{ id: "repo_#{repo.id}" }
%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
#{current_user.username}/#{repo.name}
%td.import-actions.job-status
@@ -63,7 +63,7 @@
- @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.id}" }
%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-actions-job-status
= label_tag "Incompatible Project", nil, class: "label label-danger"
diff --git a/app/views/issues/_issue.atom.builder b/app/views/issues/_issue.atom.builder
index fcd30c8c765..23a88448055 100644
--- a/app/views/issues/_issue.atom.builder
+++ b/app/views/issues/_issue.atom.builder
@@ -7,7 +7,7 @@ xml.entry do
xml.author do
xml.name issue.author_name
- xml.email issue.author_email
+ xml.email issue.author_public_email
end
xml.summary issue.title
@@ -26,7 +26,7 @@ xml.entry do
if issue.assignee
xml.assignee do
xml.name issue.assignee.name
- xml.email issue.assignee.email
+ xml.email issue.assignee_public_email
end
end
end
diff --git a/app/views/koding/index.html.haml b/app/views/koding/index.html.haml
index 65887aacbaf..04e2d4b63e6 100644
--- a/app/views/koding/index.html.haml
+++ b/app/views/koding/index.html.haml
@@ -2,5 +2,5 @@
%p
= icon('circle', class: 'cgreen')
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}
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index d551754a2e5..c74b3249a13 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -18,7 +18,7 @@
or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
.col-lg-9
.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'
%h5.prepend-top-0
Upload new avatar
diff --git a/app/views/projects/blob/_image.html.haml b/app/views/projects/blob/_image.html.haml
index f864702d862..ea3cecb86a9 100644
--- a/app/views/projects/blob/_image.html.haml
+++ b/app/views/projects/blob/_image.html.haml
@@ -9,7 +9,7 @@
- else
.nothing-here-block
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.
- else
%img{ src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path)), alt: "#{blob.name}" }
diff --git a/app/views/projects/blob/_text.html.haml b/app/views/projects/blob/_text.html.haml
index b1e1be49de9..7b16d266982 100644
--- a/app/views/projects/blob/_text.html.haml
+++ b/app/views/projects/blob/_text.html.haml
@@ -3,7 +3,7 @@
.nothing-here-block
File too large, you can
= 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
- blob.load_all_data!(@repository)
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 8853801016b..3bcddcb37f1 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -9,7 +9,7 @@
- if @conflict
.alert.alert-danger
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.
.file-editor
diff --git a/app/views/projects/buttons/_koding.html.haml b/app/views/projects/buttons/_koding.html.haml
index 5d9a776da89..a5a9e4d0621 100644
--- a/app/views/projects/buttons/_koding.html.haml
+++ b/app/views/projects/buttons/_koding.html.haml
@@ -1,3 +1,3 @@
- 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)
diff --git a/app/views/projects/cycle_analytics/_overview.html.haml b/app/views/projects/cycle_analytics/_overview.html.haml
index c8f0b547f80..9007f2c24ba 100644
--- a/app/views/projects/cycle_analytics/_overview.html.haml
+++ b/app/views/projects/cycle_analytics/_overview.html.haml
@@ -9,7 +9,7 @@
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.
%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
%span.overview-icon
= custom_icon ('icon_cycle_analytics_overview')
diff --git a/app/views/projects/environments/_external_url.html.haml b/app/views/projects/environments/_external_url.html.haml
index 4c8fe1c271b..bf0f1819073 100644
--- a/app/views/projects/environments/_external_url.html.haml
+++ b/app/views/projects/environments/_external_url.html.haml
@@ -1,3 +1,3 @@
- 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')
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index c8f097c69da..6682a85ffa6 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -16,7 +16,7 @@
.pull-right
- if @merge_request.source_branch_exists?
- 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)
= link_to "#modal_merge_info", class: "btn inline btn-grouped btn-sm", "data-toggle" => "modal" do
Check out branch
diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
index 93ed4b68e0e..cde0ce08e14 100644
--- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml
+++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
@@ -49,7 +49,7 @@
%strong Tip:
= succeed '.' do
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
$(function(){
diff --git a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
index 3a323d94cc2..2fb88297fb3 100644
--- a/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
+++ b/app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml
@@ -4,13 +4,13 @@
%ul.list-unstyled.indent-list
%li
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
= icon('external-link')
on your Mattermost installation
%li
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
= icon('external-link')
in your Mattermost team with these options:
diff --git a/app/views/projects/services/mattermost_slash_commands/_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_help.html.haml
index a04fd5035a6..2a1b9d4c465 100644
--- a/app/views/projects/services/mattermost_slash_commands/_help.html.haml
+++ b/app/views/projects/services/mattermost_slash_commands/_help.html.haml
@@ -4,7 +4,7 @@
%p
This service allows users to perform common operations on this
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
= icon('external-link')
%p.inline
diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml
index 0d973a20d4c..078b7be6865 100644
--- a/app/views/projects/services/slack_slash_commands/_help.html.haml
+++ b/app/views/projects/services/slack_slash_commands/_help.html.haml
@@ -5,7 +5,7 @@
%p
This service allows users to perform common operations on this
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
= icon('external-link')
%p.inline
@@ -57,7 +57,7 @@
= label_tag nil, 'Customize icon', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block
= 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
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 0b0f2c9cd1a..17107f55a2d 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -8,7 +8,7 @@
.alert.alert-danger
Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
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
.form-group
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 76cd330e80a..dc9a3b0d0df 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -33,7 +33,7 @@
.profile-header
.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: ''
.user-info
diff --git a/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml b/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml
index ee236310a71..dd649dcde7e 100644
--- a/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml
+++ b/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml
@@ -1,4 +1,4 @@
---
-title: API: Add environment stop action
+title: 'API: Add environment stop action'
merge_request: 8808
author:
diff --git a/changelogs/unreleased/28058-hide-emails-in-atom-feeds.yml b/changelogs/unreleased/28058-hide-emails-in-atom-feeds.yml
new file mode 100644
index 00000000000..e0e826a67f8
--- /dev/null
+++ b/changelogs/unreleased/28058-hide-emails-in-atom-feeds.yml
@@ -0,0 +1,4 @@
+---
+title: Only show public emails in atom feeds
+merge_request:
+author:
diff --git a/changelogs/unreleased/28499-fix-large-text-tooltip-in-diff-file-name.yml b/changelogs/unreleased/28499-fix-large-text-tooltip-in-diff-file-name.yml
new file mode 100644
index 00000000000..660a881e094
--- /dev/null
+++ b/changelogs/unreleased/28499-fix-large-text-tooltip-in-diff-file-name.yml
@@ -0,0 +1,4 @@
+---
+title: Fixes large file name tooltip cutoff in diff header
+merge_request: 9529
+author:
diff --git a/changelogs/unreleased/28660-fix-dismissable-error-close-not-visible-enough.yml b/changelogs/unreleased/28660-fix-dismissable-error-close-not-visible-enough.yml
new file mode 100644
index 00000000000..8b592766bf3
--- /dev/null
+++ b/changelogs/unreleased/28660-fix-dismissable-error-close-not-visible-enough.yml
@@ -0,0 +1,4 @@
+---
+title: Fixes dismissable error close is not visible enough
+merge_request: 9516
+author:
diff --git a/changelogs/unreleased/29428-new-directory-from-existing-branch.yml b/changelogs/unreleased/29428-new-directory-from-existing-branch.yml
new file mode 100644
index 00000000000..b3f7cd1f8f8
--- /dev/null
+++ b/changelogs/unreleased/29428-new-directory-from-existing-branch.yml
@@ -0,0 +1,4 @@
+---
+title: New directory from interface on existing branch
+merge_request: 9921
+author: Jacopo Beschi @jacopo-beschi
diff --git a/changelogs/unreleased/add-test-backoff-util.yml b/changelogs/unreleased/add-test-backoff-util.yml
new file mode 100644
index 00000000000..f3f3b99caec
--- /dev/null
+++ b/changelogs/unreleased/add-test-backoff-util.yml
@@ -0,0 +1,4 @@
+---
+title: Added tests for the w.gl.utils.backOff promise
+merge_request:
+author:
diff --git a/changelogs/unreleased/api-project-issues-404.yml b/changelogs/unreleased/api-project-issues-404.yml
new file mode 100644
index 00000000000..ce40395c99e
--- /dev/null
+++ b/changelogs/unreleased/api-project-issues-404.yml
@@ -0,0 +1,4 @@
+---
+title: Return 404 in project issues API endpoint when project cannot be found
+merge_request: 10093
+author:
diff --git a/changelogs/unreleased/bugfix-systemhook.yml b/changelogs/unreleased/bugfix-systemhook.yml
new file mode 100644
index 00000000000..4c4d0dcc7a2
--- /dev/null
+++ b/changelogs/unreleased/bugfix-systemhook.yml
@@ -0,0 +1,4 @@
+---
+title: Fix bug when system hook for deploy key
+merge_request: 9796
+author: billy.lb
diff --git a/changelogs/unreleased/dz-hide-share-group-ancestors.yml b/changelogs/unreleased/dz-hide-share-group-ancestors.yml
new file mode 100644
index 00000000000..743c4fd56ca
--- /dev/null
+++ b/changelogs/unreleased/dz-hide-share-group-ancestors.yml
@@ -0,0 +1,4 @@
+---
+title: Hide ancestor groups in the share group dropdown list
+merge_request: 9965
+author:
diff --git a/changelogs/unreleased/feature-tokens-rake-task.yml b/changelogs/unreleased/feature-tokens-rake-task.yml
new file mode 100644
index 00000000000..6c3845757db
--- /dev/null
+++ b/changelogs/unreleased/feature-tokens-rake-task.yml
@@ -0,0 +1,4 @@
+---
+title: New rake task to reset all email and private tokens
+merge_request:
+author:
diff --git a/changelogs/unreleased/make-karma-fast-again.yml b/changelogs/unreleased/make-karma-fast-again.yml
new file mode 100644
index 00000000000..9b95e06954a
--- /dev/null
+++ b/changelogs/unreleased/make-karma-fast-again.yml
@@ -0,0 +1,4 @@
+---
+title: Only add code coverage instrumentation when generating coverage report
+merge_request: 9987
+author:
diff --git a/changelogs/unreleased/simplify-docs-trigger.yml b/changelogs/unreleased/simplify-docs-trigger.yml
new file mode 100644
index 00000000000..062626359ef
--- /dev/null
+++ b/changelogs/unreleased/simplify-docs-trigger.yml
@@ -0,0 +1,4 @@
+---
+title: Simplify trigger_docs build job for CE and EE
+merge_request: 9820
+author: winniehell
diff --git a/changelogs/unreleased/ssrf-protections.yml b/changelogs/unreleased/ssrf-protections.yml
new file mode 100644
index 00000000000..8d803738009
--- /dev/null
+++ b/changelogs/unreleased/ssrf-protections.yml
@@ -0,0 +1,4 @@
+---
+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:
diff --git a/config/karma.config.js b/config/karma.config.js
index c1d3751d88f..eb082dd28bf 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -3,17 +3,6 @@ var webpack = require('webpack');
var webpackConfig = require('./webpack.config.js');
var ROOT_PATH = path.resolve(__dirname, '..');
-// add coverage instrumentation to babel config
-if (webpackConfig.module && webpackConfig.module.rules) {
- var babelConfig = webpackConfig.module.rules.find(function (rule) {
- return rule.loader === 'babel-loader';
- });
-
- babelConfig.options = babelConfig.options || {};
- babelConfig.options.plugins = babelConfig.options.plugins || [];
- babelConfig.options.plugins.push('istanbul');
-}
-
// remove problematic plugins
if (webpackConfig.plugins) {
webpackConfig.plugins = webpackConfig.plugins.filter(function (plugin) {
@@ -27,7 +16,8 @@ if (webpackConfig.plugins) {
// Karma configuration
module.exports = function(config) {
var progressReporter = process.env.CI ? 'mocha' : 'progress';
- config.set({
+
+ var karmaConfig = {
basePath: ROOT_PATH,
browsers: ['PhantomJS'],
frameworks: ['jasmine'],
@@ -38,14 +28,20 @@ module.exports = function(config) {
preprocessors: {
'spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
},
- reporters: [progressReporter, 'coverage-istanbul'],
- coverageIstanbulReporter: {
+ reporters: [progressReporter],
+ webpack: webpackConfig,
+ webpackMiddleware: { stats: 'errors-only' },
+ };
+
+ if (process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage') {
+ karmaConfig.reporters.push('coverage-istanbul');
+ karmaConfig.coverageIstanbulReporter = {
reports: ['html', 'text-summary'],
dir: 'coverage-javascript/',
subdir: '.',
fixWebpackSourcePaths: true
- },
- webpack: webpackConfig,
- webpackMiddleware: { stats: 'errors-only' },
- });
+ };
+ }
+
+ config.set(karmaConfig);
};
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 92746211cad..c6794d6b944 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -59,13 +59,7 @@ var config = {
{
test: /\.js$/,
exclude: /(node_modules|vendor\/assets)/,
- loader: 'babel-loader',
- options: {
- presets: [
- ["es2015", {"modules": false}],
- 'stage-2'
- ]
- }
+ loader: 'babel-loader'
},
{
test: /\.svg$/,
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index aea0a72b633..4bc735916c1 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -155,7 +155,7 @@ class Gitlab::Seeder::CycleAnalytics
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)
GitPushService.new(issue.project,
diff --git a/db/migrate/20170313213916_add_index_to_user_ghost.rb b/db/migrate/20170313213916_add_index_to_user_ghost.rb
new file mode 100644
index 00000000000..c429039c275
--- /dev/null
+++ b/db/migrate/20170313213916_add_index_to_user_ghost.rb
@@ -0,0 +1,24 @@
+# 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
diff --git a/db/migrate/20170317203554_index_routes_path_for_like.rb b/db/migrate/20170317203554_index_routes_path_for_like.rb
new file mode 100644
index 00000000000..264ecd322c3
--- /dev/null
+++ b/db/migrate/20170317203554_index_routes_path_for_like.rb
@@ -0,0 +1,29 @@
+# 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
diff --git a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
index 9dfe77bedb7..44c688fa134 100644
--- a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
+++ b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
@@ -6,41 +6,12 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration
DOWNTIME = false
- THREAD_COUNT = 8
-
KNOWN_PATHS = %w(artifacts graphs refs badges).freeze
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|
- begin
- queue = enum.next
- rescue StopIteration
- enum.rewind
- retry
- end
-
- queue << slice
+ rename_projects(slice)
end
-
- start = true
-
- threads.each(&:join)
end
def down
diff --git a/db/schema.rb b/db/schema.rb
index ee5000ea64c..904fef4a381 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170315194013) do
+ActiveRecord::Schema.define(version: 20170317203554) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -985,6 +985,7 @@ ActiveRecord::Schema.define(version: 20170315194013) do
end
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
create_table "sent_notifications", force: :cascade do |t|
@@ -1244,6 +1245,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", ["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", ["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", ["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"}
diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md
index 0794156bc39..7f4426ee85d 100644
--- a/doc/api/v3_to_v4.md
+++ b/doc/api/v3_to_v4.md
@@ -8,16 +8,16 @@ Below are the changes made between V3 and V4.
### 8.17
-- Removed `/projects/:search` (use: `/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)
-- `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)
+- 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 `GET /projects/:id/issues` [!8967](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8967)
+- `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 `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)
-- 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
-- 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)
- 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`
@@ -28,31 +28,31 @@ Below are the changes made between V3 and V4.
- `/gitignores/:key`
- `/gitlab_ci_ymls/: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)
- 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/owned` moved to `GET /projects?owned=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)
- - 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)
- 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)
-- 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)
-- 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)
+- 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 `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)
- - POST `:id/repository/branches`
- - POST `:id/repository/commits`
- - 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/)
- - PUT `projects/:id/merge_requests/:merge_request_id/merge`
- - POST `projects/:id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds`
- - POST `projects`
- - POST `projects/user/:user_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)
+ - `POST /projects/:id/repository/branches`
+ - `POST /projects/:id/repository/commits`
+ - `POST/PUT/DELETE :id/repository/files`
+- 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`
+ - `POST /projects/:id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds`
+ - `POST /projects`
+ - `POST /projects/user/:user_id`
+ - `PUT /projects/:id`
+- 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 `subscribed` field from responses returning list of issues or merge
requests. Fetch individual issues or merge requests to obtain the value
@@ -62,21 +62,21 @@ 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)
- 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)
-- 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)
+- 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)
+- `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)
- 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)
- `POST /projects/:id/trigger/builds` to `POST /projects/:id/trigger/pipeline`
- 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)
- 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)
-- 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)
+- 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)
- Update endpoints for repository files [!9637](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9637)
- - 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`
- - 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`
- - `/projects/:id/repository/tree` parameter `ref_name` has been renamed to `ref` for consistency
+ - 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
diff --git a/doc/update/8.17-to-9.0.md b/doc/update/8.17-to-9.0.md
index 626507c0482..b7ba970031c 100644
--- a/doc/update/8.17-to-9.0.md
+++ b/doc/update/8.17-to-9.0.md
@@ -115,11 +115,11 @@ sudo -u git -H bundle clean
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
-# Install/update frontend asset dependencies
-sudo -u git -H npm install --production
+# Update node dependencies and recompile assets
+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
-sudo -u git -H bundle exec rake gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production
+# Clean up cache
+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).
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index b3183357625..fd2674910d2 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -91,7 +91,7 @@ module API
use :issues_params
end
get ":id/issues" do
- project = find_project(params[:id])
+ project = find_project!(params[:id])
issues = find_issues(project_id: project.id)
diff --git a/lib/api/v3/issues.rb b/lib/api/v3/issues.rb
index cead03b1e6b..54c6a8060b8 100644
--- a/lib/api/v3/issues.rb
+++ b/lib/api/v3/issues.rb
@@ -103,7 +103,7 @@ module API
use :issues_params
end
get ":id/issues" do
- project = find_project(params[:id])
+ project = find_project!(params[:id])
issues = find_issues(project_id: project.id)
diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb
index 651b55523c0..123c92fd250 100644
--- a/lib/banzai/filter/image_link_filter.rb
+++ b/lib/banzai/filter/image_link_filter.rb
@@ -2,7 +2,6 @@ module Banzai
module Filter
# HTML filter that wraps links around inline images.
class ImageLinkFilter < HTML::Pipeline::Filter
-
# 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
# of the anchor, and then replace the img with the link-wrapped version.
@@ -12,7 +11,8 @@ module Banzai
'a',
class: 'no-attachment-icon',
href: img['src'],
- target: '_blank'
+ target: '_blank',
+ rel: 'noopener noreferrer'
)
link.children = img.clone
diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb
index b64a1287d4d..35cb10eae5d 100644
--- a/lib/banzai/filter/video_link_filter.rb
+++ b/lib/banzai/filter/video_link_filter.rb
@@ -43,6 +43,7 @@ module Banzai
element['title'] || element['alt'],
href: element['src'],
target: '_blank',
+ rel: 'noopener noreferrer',
title: "Download '#{element['title'] || element['alt']}'")
download_paragraph = doc.document.create_element('p')
download_paragraph.children = link
diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb
new file mode 100644
index 00000000000..7e14a566696
--- /dev/null
+++ b/lib/gitlab/url_blocker.rb
@@ -0,0 +1,59 @@
+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
diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb
index c81dc7e30d0..9ce13feb79a 100644
--- a/lib/gitlab/url_sanitizer.rb
+++ b/lib/gitlab/url_sanitizer.rb
@@ -18,6 +18,12 @@ module Gitlab
false
end
+ def self.http_credentials_for_user(user)
+ return {} unless user.respond_to?(:username)
+
+ { user: user.username }
+ end
+
def initialize(url, credentials: nil)
@url = Addressable::URI.parse(url.strip)
@credentials = credentials
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 2248763c106..8f1d1fdc02e 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -96,8 +96,8 @@ module Gitlab
end
def level_value(level)
- return string_options[level] if level.is_a? String
- level
+ return level.to_i if level.to_i.to_s == level.to_s && string_options.key(level.to_i)
+ string_options[level] || PRIVATE
end
def string_level(level)
diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake
index 098f9851b45..003d57adbbd 100644
--- a/lib/tasks/gitlab/assets.rake
+++ b/lib/tasks/gitlab/assets.rake
@@ -3,16 +3,16 @@ namespace :gitlab do
desc 'GitLab | Assets | Compile all frontend assets'
task compile: [
'yarn:check',
- 'assets:precompile',
+ 'rake:assets:precompile',
'webpack:compile',
- 'gitlab:assets:fix_urls'
+ 'fix_urls'
]
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'
- task purge: ['assets:clobber']
+ task purge: ['rake:assets:clobber']
desc 'GitLab | Assets | Uninstall frontend dependencies'
task purge_modules: ['yarn:clobber']
diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake
index f5caca3ddbf..8938bc515f5 100644
--- a/lib/tasks/migrate/setup_postgresql.rake
+++ b/lib/tasks/migrate/setup_postgresql.rake
@@ -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/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/20170317203554_index_routes_path_for_like')
desc 'GitLab | Sets up PostgreSQL'
task setup_postgresql: :environment do
NamespacesProjectsPathLowerIndexes.new.up
AddUsersLowerUsernameEmailIndexes.new.up
AddLowerPathIndexToRoutes.new.up
+ IndexRoutesPathForLike.new.up
end
diff --git a/lib/tasks/tokens.rake b/lib/tasks/tokens.rake
new file mode 100644
index 00000000000..95735f43802
--- /dev/null
+++ b/lib/tasks/tokens.rake
@@ -0,0 +1,38 @@
+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
diff --git a/package.json b/package.json
index 1048e29d0ac..b3d038bd3d1 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"eslint-fix": "eslint --max-warnings 0 --ext .js --fix .",
"eslint-report": "eslint --max-warnings 0 --ext .js --format html --output-file ./eslint-report.html .",
"karma": "karma start config/karma.config.js --single-run",
+ "karma-coverage": "BABEL_ENV=coverage karma start config/karma.config.js --single-run",
"karma-start": "karma start config/karma.config.js",
"webpack": "webpack --config config/webpack.config.js",
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
@@ -13,7 +14,8 @@
"dependencies": {
"babel-core": "^6.22.1",
"babel-loader": "^6.2.10",
- "babel-preset-es2015": "^6.22.0",
+ "babel-plugin-transform-define": "^1.2.0",
+ "babel-preset-latest": "^6.24.0",
"babel-preset-stage-2": "^6.22.0",
"bootstrap-sass": "^3.3.6",
"compression-webpack-plugin": "^0.3.2",
@@ -57,12 +59,5 @@
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.2",
"webpack-dev-server": "^2.3.0"
- },
- "nyc": {
- "exclude": [
- "spec/javascripts/test_bundle.js",
- "spec/javascripts/**/*_spec.js",
- "app/assets/javascripts/droplab/**/*"
- ]
}
}
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
new file mode 100644
index 00000000000..84a1ce773a1
--- /dev/null
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Admin::ApplicationSettingsController do
+ include StubENV
+
+ 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
+ end
+ end
+end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 8263301c439..57a921e3676 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -152,6 +152,24 @@ describe Projects::IssuesController do
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
let(:another_project) { create(:empty_project, :private) }
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 250d64f7055..c310d830e81 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -203,6 +203,24 @@ describe Projects::MergeRequestsController do
end
describe 'PUT update' do
+ 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
+ end
+
context 'there is no source project' do
let(:project) { create(:project) }
let(:fork_project) { create(:forked_project_with_submodules) }
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index b4095095887..03daab12c8f 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -9,6 +9,13 @@ feature 'Admin updates settings', feature: true do
visit admin_application_settings_path
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
uncheck 'Gravatar enabled'
fill_in 'Home page URL', with: 'https://about.gitlab.com/'
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index a7c22615b89..58b14e09740 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -2,7 +2,8 @@ require 'spec_helper'
describe "Dashboard Issues Feed", feature: true 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!(:project2) { create(:project) }
@@ -31,7 +32,7 @@ describe "Dashboard Issues Feed", feature: true do
end
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
visit issues_dashboard_path(:atom, private_token: user.private_token)
@@ -39,8 +40,8 @@ describe "Dashboard Issues Feed", feature: true do
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]")
expect(entry).to be_present
- expect(entry).to have_selector('author email', text: issue2.author_email)
- expect(entry).to have_selector('assignee 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.assignee_public_email)
expect(entry).not_to have_selector('labels')
expect(entry).not_to have_selector('milestone')
expect(entry).to have_selector('description', text: issue2.description)
@@ -50,7 +51,7 @@ describe "Dashboard Issues Feed", feature: true do
context "issue with label and milestone" do
let!(:milestone1) { create(:milestone, project: project1, title: 'v1') }
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
issue1.labels << label1
@@ -62,8 +63,8 @@ describe "Dashboard Issues Feed", feature: true do
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]")
expect(entry).to be_present
- expect(entry).to have_selector('author email', text: issue1.author_email)
- expect(entry).to have_selector('assignee 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.assignee_public_email)
expect(entry).to have_selector('labels label', text: label1.title)
expect(entry).to have_selector('milestone', text: milestone1.title)
expect(entry).not_to have_selector('description')
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index a01a050a013..b3903ec2faf 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -2,10 +2,11 @@ require 'spec_helper'
describe 'Issues Feed', feature: true 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!(:project) { create(:project) }
- let!(:issue) { create(:issue, author: user, project: project) }
+ let!(:issue) { create(:issue, author: user, assignee: assignee, project: project) }
before do
project.team << [user, :developer]
@@ -20,7 +21,8 @@ describe 'Issues Feed', feature: true do
expect(response_headers['Content-Type']).
to have_content('application/atom+xml')
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)
end
end
@@ -33,7 +35,8 @@ describe 'Issues Feed', feature: true do
expect(response_headers['Content-Type']).
to have_content('application/atom+xml')
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)
end
end
diff --git a/spec/features/groups/group_name_toggle.rb b/spec/features/groups/group_name_toggle_spec.rb
index ada4ac66e04..8528718a2f7 100644
--- a/spec/features/groups/group_name_toggle.rb
+++ b/spec/features/groups/group_name_toggle_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Group name toggle', js: true do
+feature 'Group name toggle', feature: true, js: true do
let(:group) { create(:group) }
let(:nested_group_1) { create(:group, parent: group) }
let(:nested_group_2) { create(:group, parent: nested_group_1) }
diff --git a/spec/features/merge_requests/reset_filters_spec.rb b/spec/features/merge_requests/reset_filters_spec.rb
index 6fed1568fcf..14511707af4 100644
--- a/spec/features/merge_requests/reset_filters_spec.rb
+++ b/spec/features/merge_requests/reset_filters_spec.rb
@@ -49,6 +49,26 @@ feature 'Merge requests filter clear button', feature: true, js: true do
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
it 'resets the text search filter' do
visit_merge_requests(project, search: 'Bug')
diff --git a/spec/features/projects/blobs/user_create_spec.rb b/spec/features/projects/blobs/user_create_spec.rb
index 03d08c12612..5686868a0c4 100644
--- a/spec/features/projects/blobs/user_create_spec.rb
+++ b/spec/features/projects/blobs/user_create_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
feature 'New blob creation', feature: true, js: true do
include WaitForAjax
+ include TargetBranchHelpers
given(:user) { create(:user) }
given(:role) { :developer }
@@ -20,19 +21,6 @@ feature 'New blob creation', feature: true, js: true do
execute_script("ace.edit('editor').setValue('#{content}')")
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
click_button 'Commit Changes'
end
@@ -53,12 +41,12 @@ feature 'New blob creation', feature: true, js: true do
context 'with different target branch' do
background do
edit_file
- select_branch_index(0)
+ select_branch('feature')
commit_file
end
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'
end
end
diff --git a/spec/features/projects/group_links_spec.rb b/spec/features/projects/group_links_spec.rb
index 8b302a6aa23..4c28205da9b 100644
--- a/spec/features/projects/group_links_spec.rb
+++ b/spec/features/projects/group_links_spec.rb
@@ -8,7 +8,7 @@ feature 'Project group links', feature: true, js: true do
let!(:group) { create(:group) }
background do
- project.team << [master, :master]
+ project.add_master(master)
login_as(master)
end
@@ -29,4 +29,26 @@ feature 'Project group links', feature: true, js: true do
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
diff --git a/spec/features/projects/user_create_dir_spec.rb b/spec/features/projects/user_create_dir_spec.rb
new file mode 100644
index 00000000000..2065abfb248
--- /dev/null
+++ b/spec/features/projects/user_create_dir_spec.rb
@@ -0,0 +1,72 @@
+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
diff --git a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
new file mode 100644
index 00000000000..6825b95c8aa
--- /dev/null
+++ b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe 'Projects > Wiki > User views Git access wiki page', :feature do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:wiki_page) do
+ WikiPages::CreateService.new(
+ project,
+ user,
+ title: 'home',
+ content: '[some link](other-page)'
+ ).execute
+ end
+
+ before do
+ login_as(user)
+ end
+
+ scenario 'Visit Wiki Page Current Commit' do
+ visit namespace_project_wiki_path(project.namespace, project, wiki_page)
+
+ click_link 'Clone repository'
+ expect(page).to have_text("Clone repository #{project.wiki.path_with_namespace}")
+ expect(page).to have_text(project.wiki.http_url_to_repo(user))
+ end
+end
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index ae9c263d1d7..113161c21c6 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -246,5 +246,17 @@ const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper
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);
+ });
+ });
});
})();
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index 8d25500b9fd..aabc8bea12f 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -136,6 +136,21 @@ describe('Issue', function() {
expectErrorMessage();
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() {
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index f4d3e77e515..d2e24eb7eb2 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -163,5 +163,72 @@ require('~/lib/utils/common_utils');
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);
+ });
});
})();
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index c12b44cea89..5cdb6473eda 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -32,10 +32,11 @@ testsContext.keys().forEach(function (path) {
}
});
-// workaround: include all source files to find files with 0% coverage
-// see also https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15
-describe('Uncovered files', function () {
- // the following files throw errors because of undefined variables
+// if we're generating coverage reports, make sure to include all files so
+// that we can catch files with 0% coverage
+// see: https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15
+if (process.env.BABEL_ENV === 'coverage') {
+ // exempt these files from the coverage report
const troubleMakers = [
'./blob_edit/blob_edit_bundle.js',
'./cycle_analytics/components/stage_plan_component.js',
@@ -48,21 +49,23 @@ describe('Uncovered files', function () {
'./network/branch_graph.js',
];
- const sourceFiles = require.context('~', true, /^\.\/(?!application\.js).*\.js$/);
- sourceFiles.keys().forEach(function (path) {
- // ignore if there is a matching spec file
- if (testsContext.keys().indexOf(`${path.replace(/\.js$/, '')}_spec`) > -1) {
- return;
- }
+ describe('Uncovered files', function () {
+ const sourceFiles = require.context('~', true, /\.js$/);
+ sourceFiles.keys().forEach(function (path) {
+ // ignore if there is a matching spec file
+ if (testsContext.keys().indexOf(`${path.replace(/\.js$/, '')}_spec`) > -1) {
+ return;
+ }
- it(`includes '${path}'`, function () {
- try {
- sourceFiles(path);
- } catch (err) {
- if (troubleMakers.indexOf(path) === -1) {
- expect(err).toBeNull();
+ it(`includes '${path}'`, function () {
+ try {
+ sourceFiles(path);
+ } catch (err) {
+ if (troubleMakers.indexOf(path) === -1) {
+ expect(err).toBeNull();
+ }
}
- }
+ });
});
});
-});
+}
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
new file mode 100644
index 00000000000..a504d299307
--- /dev/null
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -0,0 +1,31 @@
+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
diff --git a/spec/lib/gitlab/url_sanitizer_spec.rb b/spec/lib/gitlab/url_sanitizer_spec.rb
index 3fd361de458..fc144a2556a 100644
--- a/spec/lib/gitlab/url_sanitizer_spec.rb
+++ b/spec/lib/gitlab/url_sanitizer_spec.rb
@@ -5,6 +5,7 @@ describe Gitlab::UrlSanitizer, lib: true do
let(:url_sanitizer) do
described_class.new("https://github.com/me/project.git", credentials: credentials)
end
+ let(:user) { double(:user, username: 'john.doe') }
describe '.sanitize' do
def sanitize_url(url)
@@ -53,12 +54,33 @@ describe Gitlab::UrlSanitizer, lib: true do
end
end
+ describe '.valid?' do
+ it 'validates url strings' do
+ expect(described_class.valid?(nil)).to be(false)
+ expect(described_class.valid?('valid@project:url.git')).to be(true)
+ expect(described_class.valid?('123://invalid:url')).to be(false)
+ end
+ end
+
+ describe '.http_credentials_for_user' do
+ it { expect(described_class.http_credentials_for_user(user)).to eq({ user: 'john.doe' }) }
+ it { expect(described_class.http_credentials_for_user('foo')).to eq({}) }
+ end
+
describe '#sanitized_url' do
it { expect(url_sanitizer.sanitized_url).to eq("https://github.com/me/project.git") }
end
describe '#credentials' do
it { expect(url_sanitizer.credentials).to eq(credentials) }
+
+ context 'when user is given to #initialize' do
+ let(:url_sanitizer) do
+ described_class.new("https://github.com/me/project.git", credentials: described_class.http_credentials_for_user(user))
+ end
+
+ it { expect(url_sanitizer.credentials).to eq({ user: 'john.doe' }) }
+ end
end
describe '#full_url' do
@@ -69,13 +91,13 @@ describe Gitlab::UrlSanitizer, lib: true do
expect(sanitizer.full_url).to eq('user@server:project.git')
end
- end
- describe '.valid?' do
- it 'validates url strings' do
- expect(described_class.valid?(nil)).to be(false)
- expect(described_class.valid?('valid@project:url.git')).to be(true)
- expect(described_class.valid?('123://invalid:url')).to be(false)
+ context 'when user is given to #initialize' do
+ let(:url_sanitizer) do
+ described_class.new("https://github.com/me/project.git", credentials: described_class.http_credentials_for_user(user))
+ end
+
+ it { expect(url_sanitizer.full_url).to eq("https://john.doe@github.com/me/project.git") }
end
end
end
diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb
new file mode 100644
index 00000000000..3255c6f1ef7
--- /dev/null
+++ b/spec/lib/gitlab/visibility_level_spec.rb
@@ -0,0 +1,21 @@
+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
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb
index f134da441c2..82abad0e2f6 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/has_status_spec.rb
@@ -110,6 +110,24 @@ describe HasStatus do
it { is_expected.to eq 'running' }
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
let!(:statuses) do
[create(type, status: :failed),
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 618ce2b6d53..5e5f690acd4 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -218,6 +218,20 @@ describe Project, models: true do
expect(project2.import_data).to be_nil
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
let!(:project_pending_deletion) do
create(:empty_project,
@@ -1900,10 +1914,8 @@ describe Project, models: true do
context 'when no user is given' do
it 'returns the url to the repo without a username' do
- url = project.http_url_to_repo
-
- expect(url).to eq(project.http_url_to_repo)
- expect(url).not_to include('@')
+ expect(project.http_url_to_repo).to eq("#{project.web_url}.git")
+ expect(project.http_url_to_repo).not_to include('@')
end
end
@@ -1911,7 +1923,7 @@ describe Project, models: true do
it 'returns the url to the repo with the username' do
user = build_stubbed(:user)
- expect(project.http_url_to_repo(user)).to match(%r{https?:\/\/#{user.username}@})
+ expect(project.http_url_to_repo(user)).to start_with("http://#{user.username}@")
end
end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 58b57bd4fef..b5b9cd024b0 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -35,10 +35,23 @@ describe ProjectWiki, models: true do
end
describe "#http_url_to_repo" do
- it "provides the full http url to the repo" do
- gitlab_url = Gitlab.config.gitlab.url
- repo_http_url = "#{gitlab_url}/#{subject.path_with_namespace}.git"
- expect(subject.http_url_to_repo).to eq(repo_http_url)
+ let(:project) { create :empty_project }
+
+ context 'when no user is given' do
+ it 'returns the url to the repo without a username' do
+ expected_url = "#{Gitlab.config.gitlab.url}/#{subject.path_with_namespace}.git"
+
+ expect(project_wiki.http_url_to_repo).to eq(expected_url)
+ expect(project_wiki.http_url_to_repo).not_to include('@')
+ end
+ end
+
+ context 'when user is given' do
+ it 'returns the url to the repo with the username' do
+ user = build_stubbed(:user)
+
+ expect(project_wiki.http_url_to_repo(user)).to start_with("http://#{user.username}@")
+ end
end
end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index 0b222022e62..bc8ae4ae5a8 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -43,14 +43,22 @@ describe Route, models: true do
end
context 'name update' do
- before { route.update_attributes(name: 'bar') }
-
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 / test')).to be_truthy
expect(described_class.exists?(name: 'bar / test / foo')).to be_truthy
expect(described_class.exists?(name: 'gitlab-org')).to be_truthy
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
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index e7738ca3034..52f68fed2cc 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -534,6 +534,12 @@ describe API::Issues, api: true do
describe "GET /projects/:id/issues" do
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
private_project = create(:empty_project, :private)
create(:issue, project: private_project)
diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb
index 1941ca0d7d8..51021eec63c 100644
--- a/spec/requests/api/v3/issues_spec.rb
+++ b/spec/requests/api/v3/issues_spec.rb
@@ -439,6 +439,12 @@ describe API::V3::Issues, api: true do
describe "GET /projects/:id/issues" do
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
private_project = create(:empty_project, :private)
create(:issue, project: private_project)
diff --git a/spec/services/create_branch_service_spec.rb b/spec/services/create_branch_service_spec.rb
new file mode 100644
index 00000000000..3f548688c20
--- /dev/null
+++ b/spec/services/create_branch_service_spec.rb
@@ -0,0 +1,24 @@
+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
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index ab6e8f537ba..e5917bb0b7a 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -120,6 +120,26 @@ describe Projects::ImportService, services: true do
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
provider = OpenStruct.new(
'name' => 'github',
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index db9f1231682..11037a4917b 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -5,6 +5,7 @@ describe SystemHooksService, services: true do
let(:project) { create :project }
let(:project_member) { create :project_member }
let(:key) { create(:key, user: user) }
+ let(:deploy_key) { create(:key) }
let(:group) { create(:group) }
let(:group_member) { create(:group_member) }
@@ -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(key, :create)).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
project.old_path_with_namespace = 'renamed_from_path'
diff --git a/spec/support/target_branch_helpers.rb b/spec/support/target_branch_helpers.rb
new file mode 100644
index 00000000000..3ee8f0f657e
--- /dev/null
+++ b/spec/support/target_branch_helpers.rb
@@ -0,0 +1,16 @@
+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
diff --git a/spec/tasks/tokens_spec.rb b/spec/tasks/tokens_spec.rb
new file mode 100644
index 00000000000..19036c7677c
--- /dev/null
+++ b/spec/tasks/tokens_spec.rb
@@ -0,0 +1,21 @@
+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
diff --git a/yarn.lock b/yarn.lock
index 391b1c7eccf..2500ddc6f6b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -473,6 +473,13 @@ babel-plugin-transform-decorators@^6.22.0:
babel-template "^6.22.0"
babel-types "^6.22.0"
+babel-plugin-transform-define@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.2.0.tgz#f036bda05162f29a542e434f585da1ccf1e7ec6a"
+ dependencies:
+ lodash.get "4.4.2"
+ traverse "0.6.6"
+
babel-plugin-transform-es2015-arrow-functions@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
@@ -549,17 +556,17 @@ babel-plugin-transform-es2015-literals@^6.22.0:
dependencies:
babel-runtime "^6.22.0"
-babel-plugin-transform-es2015-modules-amd@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.22.0.tgz#bf69cd34889a41c33d90dfb740e0091ccff52f21"
+babel-plugin-transform-es2015-modules-amd@^6.24.0:
+ version "6.24.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.0.tgz#a1911fb9b7ec7e05a43a63c5995007557bcf6a2e"
dependencies:
- babel-plugin-transform-es2015-modules-commonjs "^6.22.0"
+ babel-plugin-transform-es2015-modules-commonjs "^6.24.0"
babel-runtime "^6.22.0"
babel-template "^6.22.0"
-babel-plugin-transform-es2015-modules-commonjs@^6.22.0:
- version "6.23.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.23.0.tgz#cba7aa6379fb7ec99250e6d46de2973aaffa7b92"
+babel-plugin-transform-es2015-modules-commonjs@^6.24.0:
+ version "6.24.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.0.tgz#e921aefb72c2cc26cb03d107626156413222134f"
dependencies:
babel-plugin-transform-strict-mode "^6.22.0"
babel-runtime "^6.22.0"
@@ -574,11 +581,11 @@ babel-plugin-transform-es2015-modules-systemjs@^6.22.0:
babel-runtime "^6.22.0"
babel-template "^6.23.0"
-babel-plugin-transform-es2015-modules-umd@^6.22.0:
- version "6.23.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.23.0.tgz#8d284ae2e19ed8fe21d2b1b26d6e7e0fcd94f0f1"
+babel-plugin-transform-es2015-modules-umd@^6.24.0:
+ version "6.24.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.0.tgz#fd5fa63521cae8d273927c3958afd7c067733450"
dependencies:
- babel-plugin-transform-es2015-modules-amd "^6.22.0"
+ babel-plugin-transform-es2015-modules-amd "^6.24.0"
babel-runtime "^6.22.0"
babel-template "^6.23.0"
@@ -669,9 +676,9 @@ babel-plugin-transform-strict-mode@^6.22.0:
babel-runtime "^6.22.0"
babel-types "^6.22.0"
-babel-preset-es2015@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.22.0.tgz#af5a98ecb35eb8af764ad8a5a05eb36dc4386835"
+babel-preset-es2015@^6.24.0:
+ version "6.24.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.0.tgz#c162d68b1932696e036cd3110dc1ccd303d2673a"
dependencies:
babel-plugin-check-es2015-constants "^6.22.0"
babel-plugin-transform-es2015-arrow-functions "^6.22.0"
@@ -684,10 +691,10 @@ babel-preset-es2015@^6.22.0:
babel-plugin-transform-es2015-for-of "^6.22.0"
babel-plugin-transform-es2015-function-name "^6.22.0"
babel-plugin-transform-es2015-literals "^6.22.0"
- babel-plugin-transform-es2015-modules-amd "^6.22.0"
- babel-plugin-transform-es2015-modules-commonjs "^6.22.0"
+ babel-plugin-transform-es2015-modules-amd "^6.24.0"
+ babel-plugin-transform-es2015-modules-commonjs "^6.24.0"
babel-plugin-transform-es2015-modules-systemjs "^6.22.0"
- babel-plugin-transform-es2015-modules-umd "^6.22.0"
+ babel-plugin-transform-es2015-modules-umd "^6.24.0"
babel-plugin-transform-es2015-object-super "^6.22.0"
babel-plugin-transform-es2015-parameters "^6.22.0"
babel-plugin-transform-es2015-shorthand-properties "^6.22.0"
@@ -698,6 +705,27 @@ babel-preset-es2015@^6.22.0:
babel-plugin-transform-es2015-unicode-regex "^6.22.0"
babel-plugin-transform-regenerator "^6.22.0"
+babel-preset-es2016@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-es2016/-/babel-preset-es2016-6.22.0.tgz#b061aaa3983d40c9fbacfa3743b5df37f336156c"
+ dependencies:
+ babel-plugin-transform-exponentiation-operator "^6.22.0"
+
+babel-preset-es2017@^6.22.0:
+ version "6.22.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-es2017/-/babel-preset-es2017-6.22.0.tgz#de2f9da5a30c50d293fb54a0ba15d6ddc573f0f2"
+ dependencies:
+ babel-plugin-syntax-trailing-function-commas "^6.22.0"
+ babel-plugin-transform-async-to-generator "^6.22.0"
+
+babel-preset-latest@^6.24.0:
+ version "6.24.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-latest/-/babel-preset-latest-6.24.0.tgz#a68d20f509edcc5d7433a48dfaebf7e4f2cd4cb7"
+ dependencies:
+ babel-preset-es2015 "^6.24.0"
+ babel-preset-es2016 "^6.22.0"
+ babel-preset-es2017 "^6.22.0"
+
babel-preset-stage-2@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.22.0.tgz#ccd565f19c245cade394b21216df704a73b27c07"
@@ -2900,6 +2928,10 @@ lodash.deburr@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-4.1.0.tgz#ddb1bbb3ef07458c0177ba07de14422cb033ff9b"
+lodash.get@4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+
lodash.get@^3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-3.7.0.tgz#3ce68ae2c91683b281cc5394128303cbf75e691f"
@@ -4271,6 +4303,10 @@ tough-cookie@~2.3.0:
dependencies:
punycode "^1.4.1"
+traverse@0.6.6:
+ version "0.6.6"
+ resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
+
trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"