summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml6
-rw-r--r--app/assets/javascripts/application.js2
-rw-r--r--app/assets/javascripts/dispatcher.js.es612
-rw-r--r--app/assets/javascripts/environments/components/environment_item.js.es68
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js101
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js.es6126
-rw-r--r--app/assets/javascripts/shortcuts_blob.js29
-rw-r--r--app/assets/javascripts/shortcuts_blob.js.es629
-rw-r--r--app/assets/stylesheets/framework/pagination.scss14
-rw-r--r--app/assets/stylesheets/pages/issues.scss4
-rw-r--r--app/assets/stylesheets/pages/labels.scss5
-rw-r--r--app/controllers/concerns/spammable_actions.rb18
-rw-r--r--app/controllers/projects/environments_controller.rb11
-rw-r--r--app/controllers/projects/issues_controller.rb22
-rw-r--r--app/controllers/projects/merge_requests_controller.rb2
-rw-r--r--app/controllers/projects/pipelines_settings_controller.rb11
-rw-r--r--app/controllers/projects/runners_controller.rb8
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb44
-rw-r--r--app/controllers/projects/triggers_controller.rb10
-rw-r--r--app/controllers/projects/variables_controller.rb9
-rw-r--r--app/controllers/registrations_controller.rb4
-rw-r--r--app/helpers/gitlab_routing_helper.rb6
-rw-r--r--app/helpers/merge_requests_helper.rb4
-rw-r--r--app/models/concerns/spammable.rb12
-rw-r--r--app/models/deployment.rb2
-rw-r--r--app/models/environment.rb10
-rw-r--r--app/models/group.rb7
-rw-r--r--app/models/namespace.rb4
-rw-r--r--app/serializers/environment_entity.rb2
-rw-r--r--app/services/ci/stop_environments_service.rb3
-rw-r--r--app/services/issues/create_service.rb12
-rw-r--r--app/services/projects/import_export/export_service.rb2
-rw-r--r--app/services/projects/transfer_service.rb17
-rw-r--r--app/services/spam_service.rb3
-rw-r--r--app/services/system_note_service.rb1
-rw-r--r--app/views/admin/spam_logs/_spam_log.html.haml2
-rw-r--r--app/views/admin/spam_logs/index.html.haml1
-rw-r--r--app/views/devise/shared/_signup_box.html.haml2
-rw-r--r--app/views/help/_shortcuts.html.haml8
-rw-r--r--app/views/layouts/nav/_project_settings.html.haml16
-rw-r--r--app/views/projects/blob/_actions.html.haml2
-rw-r--r--app/views/projects/environments/_stop.html.haml2
-rw-r--r--app/views/projects/environments/show.html.haml2
-rw-r--r--app/views/projects/issues/_issue.html.haml2
-rw-r--r--app/views/projects/issues/verify.html.haml20
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--app/views/projects/pipelines_settings/_show.html.haml (renamed from app/views/projects/pipelines_settings/show.html.haml)6
-rw-r--r--app/views/projects/runners/_index.html.haml (renamed from app/views/projects/runners/index.html.haml)6
-rw-r--r--app/views/projects/runners/_shared_runners.html.haml2
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml4
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml6
-rw-r--r--app/views/projects/triggers/_index.html.haml (renamed from app/views/projects/triggers/index.html.haml)8
-rw-r--r--app/views/projects/variables/_index.html.haml (renamed from app/views/projects/variables/index.html.haml)8
-rw-r--r--changelogs/unreleased/21518_recaptcha_spam_issues.yml4
-rw-r--r--changelogs/unreleased/24147-delete-env-button.yml4
-rw-r--r--changelogs/unreleased/26059-segoe-ui-vertical.yml4
-rw-r--r--changelogs/unreleased/26920-hover-cursor-on-pagination-element.yml4
-rw-r--r--changelogs/unreleased/27632_fix_mr_widget_url.yml4
-rw-r--r--changelogs/unreleased/8082-permalink-to-file.yml4
-rw-r--r--changelogs/unreleased/api-remove-snippets-expires-at.yml4
-rw-r--r--changelogs/unreleased/fix-import-group-members.yml4
-rw-r--r--changelogs/unreleased/issue_19262.yml4
-rw-r--r--changelogs/unreleased/refresh-permissions-when-moving-projects.yml4
-rw-r--r--changelogs/unreleased/remove-deploy-key-endpoint.yml4
-rw-r--r--config/routes/project.rb1
-rw-r--r--db/migrate/20170206071414_add_recaptcha_verified_to_spam_logs.rb15
-rw-r--r--db/schema.rb3
-rw-r--r--doc/administration/integration/terminal.md2
-rw-r--r--doc/administration/reply_by_email_postfix_setup.md2
-rw-r--r--doc/api/project_snippets.md1
-rw-r--r--doc/api/v3_to_v4.md3
-rw-r--r--doc/ci/pipelines.md2
-rw-r--r--doc/development/ui_guide.md6
-rw-r--r--doc/install/installation.md4
-rw-r--r--doc/raketasks/features.md2
-rwxr-xr-xdoc/university/training/topics/additional_resources.md2
-rwxr-xr-xdoc/university/training/user_training.md2
-rw-r--r--doc/update/2.6-to-3.0.md2
-rw-r--r--doc/update/2.9-to-3.0.md2
-rw-r--r--doc/update/3.0-to-3.1.md2
-rw-r--r--doc/update/3.1-to-4.0.md2
-rw-r--r--doc/update/4.0-to-4.1.md2
-rw-r--r--doc/update/4.1-to-4.2.md2
-rw-r--r--doc/update/4.2-to-5.0.md2
-rw-r--r--doc/update/5.0-to-5.1.md2
-rw-r--r--doc/update/5.1-to-5.2.md2
-rw-r--r--doc/update/5.1-to-5.4.md2
-rw-r--r--doc/update/5.1-to-6.0.md2
-rw-r--r--doc/update/5.2-to-5.3.md2
-rw-r--r--doc/update/5.3-to-5.4.md2
-rw-r--r--doc/update/5.4-to-6.0.md2
-rw-r--r--doc/update/6.0-to-6.1.md2
-rw-r--r--doc/update/6.1-to-6.2.md2
-rw-r--r--doc/update/6.2-to-6.3.md2
-rw-r--r--doc/update/6.3-to-6.4.md2
-rw-r--r--doc/update/6.4-to-6.5.md2
-rw-r--r--doc/update/6.5-to-6.6.md2
-rw-r--r--doc/update/6.6-to-6.7.md2
-rw-r--r--doc/update/6.7-to-6.8.md2
-rw-r--r--doc/update/6.8-to-6.9.md2
-rw-r--r--doc/update/6.9-to-7.0.md2
-rw-r--r--doc/update/6.x-or-7.x-to-7.14.md4
-rw-r--r--doc/update/7.0-to-7.1.md2
-rw-r--r--doc/update/7.1-to-7.2.md2
-rw-r--r--doc/update/7.2-to-7.3.md2
-rw-r--r--doc/update/7.3-to-7.4.md2
-rw-r--r--doc/update/8.16-to-8.17.md239
-rw-r--r--doc/user/account/security.md2
-rw-r--r--doc/user/profile/account/two_factor_authentication.md2
-rw-r--r--doc/user/project/container_registry.md11
-rw-r--r--doc/user/project/integrations/kubernetes.md4
-rw-r--r--doc/user/project/integrations/mattermost_slash_commands.md15
-rw-r--r--doc/user/project/integrations/slack_slash_commands.md2
-rw-r--r--doc/user/project/repository/web_editor.md3
-rw-r--r--doc/user/project/settings/import_export.md7
-rw-r--r--doc/workflow/importing/import_projects_from_bitbucket.md2
-rw-r--r--doc/workflow/importing/import_projects_from_github.md2
-rw-r--r--doc/workflow/lfs/lfs_administration.md4
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/deploy_keys.rb172
-rw-r--r--lib/api/entities.rb3
-rw-r--r--lib/api/v3/deploy_keys.rb122
-rw-r--r--lib/api/v3/entities.rb16
-rw-r--r--lib/api/v3/issues.rb28
-rw-r--r--lib/api/v3/merge_requests.rb40
-rw-r--r--lib/api/v3/project_snippets.rb135
-rw-r--r--lib/api/v3/projects.rb74
-rw-r--r--lib/gitlab/import_export/members_mapper.rb4
-rw-r--r--lib/gitlab/import_export/project_tree_saver.rb27
-rw-r--r--lib/gitlab/import_export/reader.rb4
-rw-r--r--lib/gitlab/import_export/relation_factory.rb2
-rw-r--r--lib/gitlab/recaptcha.rb4
-rwxr-xr-xlib/support/init.d/gitlab2
-rw-r--r--lib/tasks/gitlab/backup.rake12
-rw-r--r--lib/tasks/grape.rake6
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb89
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb20
-rw-r--r--spec/controllers/projects/variables_controller_spec.rb59
-rw-r--r--spec/controllers/registrations_controller_spec.rb2
-rw-r--r--spec/features/environment_spec.rb48
-rw-r--r--spec/features/environments_spec.rb16
-rw-r--r--spec/features/issues/filtered_search/dropdown_label_spec.rb218
-rw-r--r--spec/features/issues/spam_issues_spec.rb66
-rw-r--r--spec/features/projects/blobs/shortcuts_blob_spec.rb37
-rw-r--r--spec/features/security/project/internal_access_spec.rb14
-rw-r--r--spec/features/security/project/private_access_spec.rb14
-rw-r--r--spec/features/security/project/public_access_spec.rb14
-rw-r--r--spec/features/triggers_spec.rb2
-rw-r--r--spec/features/variables_spec.rb2
-rw-r--r--spec/helpers/merge_requests_helper_spec.rb10
-rw-r--r--spec/javascripts/environments/environment_item_spec.js.es62
-rw-r--r--spec/javascripts/environments/mock_data.js.es610
-rw-r--r--spec/lib/gitlab/import_export/members_mapper_spec.rb22
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb61
-rw-r--r--spec/lib/gitlab/import_export/reader_spec.rb4
-rw-r--r--spec/models/deployment_spec.rb4
-rw-r--r--spec/models/environment_spec.rb34
-rw-r--r--spec/models/group_spec.rb13
-rw-r--r--spec/models/namespace_spec.rb7
-rw-r--r--spec/requests/api/project_snippets_spec.rb12
-rw-r--r--spec/requests/api/v3/deploy_keys_spec.rb172
-rw-r--r--spec/requests/api/v3/project_snippets_spec.rb188
-rw-r--r--spec/services/ci/stop_environments_service_spec.rb4
-rw-r--r--spec/services/issues/create_service_spec.rb102
-rw-r--r--spec/services/projects/transfer_service_spec.rb26
-rw-r--r--spec/services/spam_service_spec.rb47
-rw-r--r--spec/services/system_note_service_spec.rb39
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--vendor/gitignore/Android.gitignore8
-rw-r--r--vendor/gitignore/CMake.gitignore1
-rw-r--r--vendor/gitignore/CodeIgniter.gitignore6
-rw-r--r--vendor/gitignore/Global/JetBrains.gitignore24
-rw-r--r--vendor/gitignore/Global/Matlab.gitignore3
-rw-r--r--vendor/gitignore/Global/Stata.gitignore24
-rw-r--r--vendor/gitignore/Go.gitignore30
-rw-r--r--vendor/gitignore/Java.gitignore6
-rw-r--r--vendor/gitignore/Joomla.gitignore27
-rw-r--r--vendor/gitignore/KiCad.gitignore3
-rw-r--r--vendor/gitignore/Laravel.gitignore4
-rw-r--r--vendor/gitignore/Magento.gitignore120
-rw-r--r--vendor/gitignore/Node.gitignore13
-rw-r--r--vendor/gitignore/Objective-C.gitignore3
-rw-r--r--vendor/gitignore/Perl.gitignore1
-rw-r--r--vendor/gitignore/PureScript.gitignore8
-rw-r--r--vendor/gitignore/Python.gitignore2
-rw-r--r--vendor/gitignore/Scala.gitignore2
-rw-r--r--vendor/gitignore/Swift.gitignore4
-rw-r--r--vendor/gitignore/Unity.gitignore4
-rw-r--r--vendor/gitignore/UnrealEngine.gitignore4
-rw-r--r--vendor/gitignore/VisualStudio.gitignore8
-rw-r--r--vendor/gitignore/Waf.gitignore13
-rw-r--r--vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml2
192 files changed, 2594 insertions, 839 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e2141716311..65149ad2444 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -450,9 +450,9 @@ pages:
script:
- mv public/ .public/
- mkdir public/
- - mv coverage public/coverage-ruby
- - mv coverage-javascript/default/ public/coverage-javascript/
- - mv eslint-report.html public/
+ - mv coverage/ public/coverage-ruby/ || true
+ - mv coverage-javascript/default/ public/coverage-javascript/ || true
+ - mv eslint-report.html public/ || true
artifacts:
paths:
- public
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 637fca4d4da..ea3f13bd00f 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -247,5 +247,7 @@ window.ES6Promise.polyfill();
new Aside();
// bind sidebar events
new gl.Sidebar();
+
+ gl.utils.initTimeagoTimeout();
});
}).call(this);
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index 70f467d608f..1e764a950ca 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -19,7 +19,6 @@
/* global UsersSelect */
/* global GroupAvatar */
/* global LineHighlighter */
-/* global ShortcutsBlob */
/* global ProjectFork */
/* global BuildArtifacts */
/* global GroupsSelect */
@@ -36,6 +35,8 @@
/* global Labels */
/* global Shortcuts */
+const ShortcutsBlob = require('./shortcuts_blob');
+
(function() {
var Dispatcher;
@@ -220,7 +221,12 @@
case 'projects:blame:show':
new LineHighlighter();
shortcut_handler = new ShortcutsNavigation();
- new ShortcutsBlob(true);
+ const fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
+ const fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
+ new ShortcutsBlob({
+ skipResetBindings: true,
+ fileBlobPermalinkUrl,
+ });
break;
case 'groups:labels:new':
case 'groups:labels:edit':
@@ -254,7 +260,7 @@
new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList();
break;
- case 'projects:variables:index':
+ case 'projects:ci_cd:show':
new gl.ProjectVariables();
break;
case 'ci:lints:create':
diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6
index 6a3d996f69c..33a99231315 100644
--- a/app/assets/javascripts/environments/components/environment_item.js.es6
+++ b/app/assets/javascripts/environments/components/environment_item.js.es6
@@ -147,12 +147,12 @@ require('./environment_terminal_button');
},
/**
- * Returns the value of the `stoppable?` key provided in the response.
+ * Returns the value of the `stop_action?` key provided in the response.
*
* @returns {Boolean}
*/
- isStoppable() {
- return this.model['stoppable?'];
+ hasStopAction() {
+ return this.model['stop_action?'];
},
/**
@@ -508,7 +508,7 @@ require('./environment_terminal_button');
</external-url-component>
</div>
- <div v-if="isStoppable && canCreateDeployment"
+ <div v-if="hasStopAction && canCreateDeployment"
class="inline js-stop-component-container">
<stop-component
:stop-url="model.stop_path">
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
deleted file mode 100644
index 5128ffd8c6f..00000000000
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len */
-/* global timeago */
-/* global dateFormat */
-
-window.timeago = require('vendor/timeago');
-window.dateFormat = require('vendor/date.format');
-
-(function() {
- (function(w) {
- var base;
- if (w.gl == null) {
- w.gl = {};
- }
- if ((base = w.gl).utils == null) {
- base.utils = {};
- }
- w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
-
- w.gl.utils.formatDate = function(datetime) {
- return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
- };
-
- w.gl.utils.getDayName = function(date) {
- return this.days[date.getDay()];
- };
-
- w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago) {
- if (setTimeago == null) {
- setTimeago = true;
- }
-
- $timeagoEls.filter(':not([data-timeago-rendered])').each(function() {
- var $el = $(this);
- $el.attr('title', gl.utils.formatDate($el.attr('datetime')));
-
- if (setTimeago) {
- // Recreate with custom template
- $el.tooltip({
- template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
- });
- }
-
- $el.attr('data-timeago-rendered', true);
- gl.utils.renderTimeago($el);
- });
- };
-
- w.gl.utils.getTimeago = function() {
- var locale = function(number, index) {
- return [
- ['less than a minute ago', 'a while'],
- ['less than a minute ago', 'in %s seconds'],
- ['about a minute ago', 'in 1 minute'],
- ['%s minutes ago', 'in %s minutes'],
- ['about an hour ago', 'in 1 hour'],
- ['about %s hours ago', 'in %s hours'],
- ['a day ago', 'in 1 day'],
- ['%s days ago', 'in %s days'],
- ['a week ago', 'in 1 week'],
- ['%s weeks ago', 'in %s weeks'],
- ['a month ago', 'in 1 month'],
- ['%s months ago', 'in %s months'],
- ['a year ago', 'in 1 year'],
- ['%s years ago', 'in %s years']
- ][index];
- };
-
- timeago.register('gl_en', locale);
- return timeago();
- };
-
- w.gl.utils.timeFor = function(time, suffix, expiredLabel) {
- var timefor;
- if (!time) {
- return '';
- }
- suffix || (suffix = 'remaining');
- expiredLabel || (expiredLabel = 'Past due');
- timefor = gl.utils.getTimeago().format(time).replace('in', '');
- if (timefor.indexOf('ago') > -1) {
- timefor = expiredLabel;
- } else {
- timefor = timefor.trim() + ' ' + suffix;
- }
- return timefor;
- };
-
- w.gl.utils.renderTimeago = function($element) {
- var timeagoInstance = gl.utils.getTimeago();
- timeagoInstance.render($element, 'gl_en');
- };
-
- w.gl.utils.getDayDifference = function(a, b) {
- var millisecondsPerDay = 1000 * 60 * 60 * 24;
- var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
- var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
-
- return Math.floor((date2 - date1) / millisecondsPerDay);
- };
- })(window);
-}).call(this);
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js.es6 b/app/assets/javascripts/lib/utils/datetime_utility.js.es6
new file mode 100644
index 00000000000..56300926188
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js.es6
@@ -0,0 +1,126 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len */
+/* global timeago */
+/* global dateFormat */
+
+window.timeago = require('vendor/timeago');
+window.dateFormat = require('vendor/date.format');
+
+(function() {
+ (function(w) {
+ var base;
+ var timeagoInstance;
+
+ if (w.gl == null) {
+ w.gl = {};
+ }
+ if ((base = w.gl).utils == null) {
+ base.utils = {};
+ }
+ w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+
+ w.gl.utils.formatDate = function(datetime) {
+ return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
+ };
+
+ w.gl.utils.getDayName = function(date) {
+ return this.days[date.getDay()];
+ };
+
+ w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) {
+ $timeagoEls.each((i, el) => {
+ el.setAttribute('title', gl.utils.formatDate(el.getAttribute('datetime')));
+
+ if (setTimeago) {
+ // Recreate with custom template
+ $(el).tooltip({
+ template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
+ });
+ }
+
+ el.classList.add('js-timeago-render');
+ });
+
+ gl.utils.renderTimeago($timeagoEls);
+ };
+
+ w.gl.utils.getTimeago = function() {
+ var locale;
+
+ if (!timeagoInstance) {
+ locale = function(number, index) {
+ return [
+ ['less than a minute ago', 'a while'],
+ ['less than a minute ago', 'in %s seconds'],
+ ['about a minute ago', 'in 1 minute'],
+ ['%s minutes ago', 'in %s minutes'],
+ ['about an hour ago', 'in 1 hour'],
+ ['about %s hours ago', 'in %s hours'],
+ ['a day ago', 'in 1 day'],
+ ['%s days ago', 'in %s days'],
+ ['a week ago', 'in 1 week'],
+ ['%s weeks ago', 'in %s weeks'],
+ ['a month ago', 'in 1 month'],
+ ['%s months ago', 'in %s months'],
+ ['a year ago', 'in 1 year'],
+ ['%s years ago', 'in %s years']
+ ][index];
+ };
+
+ timeago.register('gl_en', locale);
+ timeagoInstance = timeago();
+ }
+
+ return timeagoInstance;
+ };
+
+ w.gl.utils.timeFor = function(time, suffix, expiredLabel) {
+ var timefor;
+ if (!time) {
+ return '';
+ }
+ suffix || (suffix = 'remaining');
+ expiredLabel || (expiredLabel = 'Past due');
+ timefor = gl.utils.getTimeago().format(time).replace('in', '');
+ if (timefor.indexOf('ago') > -1) {
+ timefor = expiredLabel;
+ } else {
+ timefor = timefor.trim() + ' ' + suffix;
+ }
+ return timefor;
+ };
+
+ w.gl.utils.cachedTimeagoElements = [];
+ w.gl.utils.renderTimeago = function($els) {
+ if (!$els && !w.gl.utils.cachedTimeagoElements.length) {
+ w.gl.utils.cachedTimeagoElements = [].slice.call(document.querySelectorAll('.js-timeago-render'));
+ } else if ($els) {
+ w.gl.utils.cachedTimeagoElements = w.gl.utils.cachedTimeagoElements.concat($els.toArray());
+ }
+
+ w.gl.utils.cachedTimeagoElements.forEach(gl.utils.updateTimeagoText);
+ };
+
+ w.gl.utils.updateTimeagoText = function(el) {
+ const timeago = gl.utils.getTimeago();
+ const formattedDate = timeago.format(el.getAttribute('datetime'), 'gl_en');
+
+ if (el.textContent !== formattedDate) {
+ el.textContent = formattedDate;
+ }
+ };
+
+ w.gl.utils.initTimeagoTimeout = function() {
+ gl.utils.renderTimeago();
+
+ gl.utils.timeagoTimeout = setTimeout(gl.utils.initTimeagoTimeout, 1000);
+ };
+
+ w.gl.utils.getDayDifference = function(a, b) {
+ var millisecondsPerDay = 1000 * 60 * 60 * 24;
+ var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
+ var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
+
+ return Math.floor((date2 - date1) / millisecondsPerDay);
+ };
+ })(window);
+}).call(this);
diff --git a/app/assets/javascripts/shortcuts_blob.js b/app/assets/javascripts/shortcuts_blob.js
deleted file mode 100644
index a3e549a2735..00000000000
--- a/app/assets/javascripts/shortcuts_blob.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, consistent-return */
-/* global Shortcuts */
-/* global Mousetrap */
-
-require('./shortcuts');
-
-(function() {
- var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- hasProp = {}.hasOwnProperty;
-
- this.ShortcutsBlob = (function(superClass) {
- extend(ShortcutsBlob, superClass);
-
- function ShortcutsBlob(skipResetBindings) {
- ShortcutsBlob.__super__.constructor.call(this, skipResetBindings);
- Mousetrap.bind('y', ShortcutsBlob.copyToClipboard);
- }
-
- ShortcutsBlob.copyToClipboard = function() {
- var clipboardButton;
- clipboardButton = $('.btn-clipboard');
- if (clipboardButton) {
- return clipboardButton.click();
- }
- };
-
- return ShortcutsBlob;
- })(Shortcuts);
-}).call(this);
diff --git a/app/assets/javascripts/shortcuts_blob.js.es6 b/app/assets/javascripts/shortcuts_blob.js.es6
new file mode 100644
index 00000000000..bfe90aef71e
--- /dev/null
+++ b/app/assets/javascripts/shortcuts_blob.js.es6
@@ -0,0 +1,29 @@
+/* global Mousetrap */
+/* global Shortcuts */
+
+require('./shortcuts');
+
+const defaults = {
+ skipResetBindings: false,
+ fileBlobPermalinkUrl: null,
+};
+
+class ShortcutsBlob extends Shortcuts {
+ constructor(opts) {
+ const options = Object.assign({}, defaults, opts);
+ super(options.skipResetBindings);
+ this.options = options;
+
+ Mousetrap.bind('y', this.moveToFilePermalink.bind(this));
+ }
+
+ moveToFilePermalink() {
+ if (this.options.fileBlobPermalinkUrl) {
+ const hash = gl.utils.getLocationHash();
+ const hashUrlString = hash ? `#${hash}` : '';
+ gl.utils.visitUrl(`${this.options.fileBlobPermalinkUrl}${hashUrlString}`);
+ }
+ }
+}
+
+module.exports = ShortcutsBlob;
diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
index b37c1d0d670..c3ec9db0f07 100644
--- a/app/assets/stylesheets/framework/pagination.scss
+++ b/app/assets/stylesheets/framework/pagination.scss
@@ -6,8 +6,22 @@
.pagination {
padding: 0;
+
+ a {
+ cursor: pointer;
+ }
+
+ .separator,
+ .separator:hover {
+ a {
+ cursor: default;
+ background-color: $gray-light;
+ padding: $gl-vert-padding;
+ }
+ }
}
+
.gap,
.gap:hover {
background-color: $gray-light;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 8734a3b1598..1e605337f09 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -148,3 +148,7 @@ ul.related-merge-requests > li {
border: 1px solid $border-gray-normal;
}
}
+
+.recaptcha {
+ margin-bottom: 30px;
+}
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 21d9b4c54ea..762b95a657c 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -259,3 +259,8 @@
}
}
}
+
+.label-link {
+ display: inline-block;
+ vertical-align: text-top;
+}
diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb
index 562f92bd83c..a6891149bfa 100644
--- a/app/controllers/concerns/spammable_actions.rb
+++ b/app/controllers/concerns/spammable_actions.rb
@@ -1,6 +1,8 @@
module SpammableActions
extend ActiveSupport::Concern
+ include Recaptcha::Verify
+
included do
before_action :authorize_submit_spammable!, only: :mark_as_spam
end
@@ -15,6 +17,15 @@ module SpammableActions
private
+ def recaptcha_params
+ return {} unless params[:recaptcha_verification] && Gitlab::Recaptcha.load_configurations! && verify_recaptcha
+
+ {
+ recaptcha_verified: true,
+ spam_log_id: params[:spam_log_id]
+ }
+ end
+
def spammable
raise NotImplementedError, "#{self.class} does not implement #{__method__}"
end
@@ -22,4 +33,11 @@ module SpammableActions
def authorize_submit_spammable!
access_denied! unless current_user.admin?
end
+
+ def render_recaptcha?
+ return false if spammable.errors.count > 1 # re-render "new" template in case there are other errors
+ return false unless Gitlab::Recaptcha.enabled?
+
+ spammable.spam
+ end
end
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 87cc36253f1..77877cd262d 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -52,10 +52,15 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end
def stop
- return render_404 unless @environment.stoppable?
+ return render_404 unless @environment.available?
- new_action = @environment.stop!(current_user)
- redirect_to polymorphic_path([project.namespace.becomes(Namespace), project, new_action])
+ stop_action = @environment.stop_with_action!(current_user)
+
+ if stop_action
+ redirect_to polymorphic_path([project.namespace.becomes(Namespace), project, stop_action])
+ else
+ redirect_to namespace_project_environment_path(project.namespace, project, @environment)
+ end
end
def terminal
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 8472ceca329..c75b8987a4b 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -93,15 +93,13 @@ class Projects::IssuesController < Projects::ApplicationController
def create
extra_params = { request: request,
merge_request_for_resolving_discussions: merge_request_for_resolving_discussions }
+ extra_params.merge!(recaptcha_params)
+
@issue = Issues::CreateService.new(project, current_user, issue_params.merge(extra_params)).execute
respond_to do |format|
format.html do
- if @issue.valid?
- redirect_to issue_path(@issue)
- else
- render :new
- end
+ html_response_create
end
format.js do
@link = @issue.attachment.url.to_js
@@ -178,6 +176,20 @@ class Projects::IssuesController < Projects::ApplicationController
protected
+ def html_response_create
+ if @issue.valid?
+ redirect_to issue_path(@issue)
+ elsif render_recaptcha?
+ if params[:recaptcha_verification]
+ flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
+ end
+
+ render :verify
+ else
+ render :new
+ end
+ end
+
def issue
# The Sortable default scope causes performance issues when used with find_by
@noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take || redirect_old
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 38a1946a71e..368bd27c91a 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -454,7 +454,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
deployment = environment.first_deployment_for(@merge_request.diff_head_commit)
stop_url =
- if environment.stoppable? && can?(current_user, :create_deployment, environment)
+ if environment.stop_action? && can?(current_user, :create_deployment, environment)
stop_namespace_project_environment_path(project.namespace, project, environment)
end
diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb
index 53ce23221ed..c8c80551ac9 100644
--- a/app/controllers/projects/pipelines_settings_controller.rb
+++ b/app/controllers/projects/pipelines_settings_controller.rb
@@ -2,20 +2,13 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
before_action :authorize_admin_pipeline!
def show
- @ref = params[:ref] || @project.default_branch || 'master'
-
- @badges = [Gitlab::Badge::Build::Status,
- Gitlab::Badge::Coverage::Report]
-
- @badges.map! do |badge|
- badge.new(@project, @ref).metadata
- end
+ redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project, params: params)
end
def update
if @project.update_attributes(update_params)
flash[:notice] = "CI/CD Pipelines settings for '#{@project.name}' were successfully updated."
- redirect_to namespace_project_pipelines_settings_path(@project.namespace, @project)
+ redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project)
else
render 'show'
end
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index 53c36635efe..74c54037ba9 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -5,11 +5,7 @@ class Projects::RunnersController < Projects::ApplicationController
layout 'project_settings'
def index
- @project_runners = project.runners.ordered
- @assignable_runners = current_user.ci_authorized_runners.
- assignable_for(project).ordered.page(params[:page]).per(20)
- @shared_runners = Ci::Runner.shared.active
- @shared_runners_count = @shared_runners.count(:all)
+ redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project)
end
def edit
@@ -53,7 +49,7 @@ class Projects::RunnersController < Projects::ApplicationController
def toggle_shared_runners
project.toggle!(:shared_runners_enabled)
- redirect_to namespace_project_runners_path(project.namespace, project)
+ redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project)
end
protected
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
new file mode 100644
index 00000000000..6f009d61950
--- /dev/null
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -0,0 +1,44 @@
+module Projects
+ module Settings
+ class CiCdController < Projects::ApplicationController
+ before_action :authorize_admin_pipeline!
+
+ def show
+ define_runners_variables
+ define_secret_variables
+ define_triggers_variables
+ define_badges_variables
+ end
+
+ private
+
+ def define_runners_variables
+ @project_runners = @project.runners.ordered
+ @assignable_runners = current_user.ci_authorized_runners.
+ assignable_for(project).ordered.page(params[:page]).per(20)
+ @shared_runners = Ci::Runner.shared.active
+ @shared_runners_count = @shared_runners.count(:all)
+ end
+
+ def define_secret_variables
+ @variable = Ci::Variable.new
+ end
+
+ def define_triggers_variables
+ @triggers = @project.triggers
+ @trigger = Ci::Trigger.new
+ end
+
+ def define_badges_variables
+ @ref = params[:ref] || @project.default_branch || 'master'
+
+ @badges = [Gitlab::Badge::Build::Status,
+ Gitlab::Badge::Coverage::Report]
+
+ @badges.map! do |badge|
+ badge.new(@project, @ref).metadata
+ end
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb
index 92359745cec..b2c11ea4156 100644
--- a/app/controllers/projects/triggers_controller.rb
+++ b/app/controllers/projects/triggers_controller.rb
@@ -4,8 +4,7 @@ class Projects::TriggersController < Projects::ApplicationController
layout 'project_settings'
def index
- @triggers = project.triggers
- @trigger = Ci::Trigger.new
+ redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project)
end
def create
@@ -13,17 +12,18 @@ class Projects::TriggersController < Projects::ApplicationController
@trigger.save
if @trigger.valid?
- redirect_to namespace_project_triggers_path(@project.namespace, @project)
+ redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Trigger was created successfully.'
else
@triggers = project.triggers.select(&:persisted?)
- render :index
+ render action: "show"
end
end
def destroy
trigger.destroy
+ flash[:alert] = "Trigger removed"
- redirect_to namespace_project_triggers_path(@project.namespace, @project)
+ redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project)
end
private
diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb
index 6f068729390..a4d1b1ee69b 100644
--- a/app/controllers/projects/variables_controller.rb
+++ b/app/controllers/projects/variables_controller.rb
@@ -4,7 +4,7 @@ class Projects::VariablesController < Projects::ApplicationController
layout 'project_settings'
def index
- @variable = Ci::Variable.new
+ redirect_to namespace_project_settings_ci_cd_path(@project.namespace, @project)
end
def show
@@ -25,9 +25,10 @@ class Projects::VariablesController < Projects::ApplicationController
@variable = Ci::Variable.new(project_params)
if @variable.valid? && @project.variables << @variable
- redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variables were successfully updated.'
+ flash[:notice] = 'Variables were successfully updated.'
+ redirect_to namespace_project_settings_ci_cd_path(project.namespace, project)
else
- render action: "index"
+ render "show"
end
end
@@ -35,7 +36,7 @@ class Projects::VariablesController < Projects::ApplicationController
@key = @project.variables.find(params[:id])
@key.destroy
- redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variable was successfully removed.'
+ redirect_to namespace_project_settings_ci_cd_path(project.namespace, project), notice: 'Variable was successfully removed.'
end
private
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index bf27f3d4d51..68bf01ba08d 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -17,7 +17,7 @@ class RegistrationsController < Devise::RegistrationsController
if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
super
else
- flash[:alert] = 'There was an error with the reCAPTCHA. Please re-solve the reCAPTCHA.'
+ flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
flash.delete :recaptcha_error
render action: 'new'
end
@@ -30,7 +30,7 @@ class RegistrationsController < Devise::RegistrationsController
format.html do
session.try(:destroy)
redirect_to new_user_session_path, notice: "Account successfully removed."
- end
+ end
end
end
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index 2159e4ce21a..f16a63e2178 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -211,8 +211,12 @@ module GitlabRoutingHelper
def project_settings_integrations_path(project, *args)
namespace_project_settings_integrations_path(project.namespace, project, *args)
end
-
+
def project_settings_members_path(project, *args)
namespace_project_settings_members_path(project.namespace, project, *args)
end
+
+ def project_settings_ci_cd_path(project, *args)
+ namespace_project_settings_ci_cd_path(project.namespace, project, *args)
+ end
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 83ff898e68a..91b24b8bc29 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -20,8 +20,8 @@ module MergeRequestsHelper
end
def mr_widget_refresh_url(mr)
- if mr && mr.source_project
- merge_widget_refresh_namespace_project_merge_request_url(mr.source_project.namespace, mr.source_project, mr)
+ if mr && mr.target_project
+ merge_widget_refresh_namespace_project_merge_request_url(mr.target_project.namespace, mr.target_project, mr)
else
''
end
diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb
index 1acff093aa1..423ae98a60e 100644
--- a/app/models/concerns/spammable.rb
+++ b/app/models/concerns/spammable.rb
@@ -11,6 +11,7 @@ module Spammable
has_one :user_agent_detail, as: :subject, dependent: :destroy
attr_accessor :spam
+ attr_accessor :spam_log
after_validation :check_for_spam, on: :create
@@ -34,9 +35,14 @@ module Spammable
end
def check_for_spam
- if spam?
- self.errors.add(:base, "Your #{spammable_entity_type} has been recognized as spam and has been discarded.")
- end
+ error_msg = if Gitlab::Recaptcha.enabled?
+ "Your #{spammable_entity_type} has been recognized as spam. "\
+ "You can still submit it by solving Captcha."
+ else
+ "Your #{spammable_entity_type} has been recognized as spam and has been discarded."
+ end
+
+ self.errors.add(:base, error_msg) if spam?
end
def spammable_entity_type
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 91d85c2279b..afad001d50f 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -91,7 +91,7 @@ class Deployment < ActiveRecord::Base
@stop_action ||= manual_actions.find_by(name: on_stop)
end
- def stoppable?
+ def stop_action?
stop_action.present?
end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 577367f1eed..13c4630c565 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -110,15 +110,15 @@ class Environment < ActiveRecord::Base
external_url.gsub(/\A.*?:\/\//, '')
end
- def stoppable?
+ def stop_action?
available? && stop_action.present?
end
- def stop!(current_user)
- return unless stoppable?
+ def stop_with_action!(current_user)
+ return unless available?
- stop
- stop_action.play(current_user)
+ stop!
+ stop_action.play(current_user) if stop_action
end
def actions_for(environment)
diff --git a/app/models/group.rb b/app/models/group.rb
index 4cdfd022094..a5b92283daa 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -197,7 +197,12 @@ class Group < Namespace
end
def refresh_members_authorized_projects
- UserProjectAccessChangedService.new(users_with_parents.pluck(:id)).execute
+ UserProjectAccessChangedService.new(user_ids_for_project_authorizations).
+ execute
+ end
+
+ def user_ids_for_project_authorizations
+ users_with_parents.pluck(:id)
end
def members_with_parents
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 2fb2eb44aaa..c5713fb7818 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -213,6 +213,10 @@ class Namespace < ActiveRecord::Base
self.class.joins(:route).where('routes.path LIKE ?', "#{route.path}/%").reorder('routes.path ASC')
end
+ def user_ids_for_project_authorizations
+ [owner_id]
+ end
+
private
def repository_storage_paths
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
index 5d15eb8d3d3..4c017960628 100644
--- a/app/serializers/environment_entity.rb
+++ b/app/serializers/environment_entity.rb
@@ -7,7 +7,7 @@ class EnvironmentEntity < Grape::Entity
expose :external_url
expose :environment_type
expose :last_deployment, using: DeploymentEntity
- expose :stoppable?
+ expose :stop_action?
expose :environment_path do |environment|
namespace_project_environment_path(
diff --git a/app/services/ci/stop_environments_service.rb b/app/services/ci/stop_environments_service.rb
index cf590459cb2..a51310c3967 100644
--- a/app/services/ci/stop_environments_service.rb
+++ b/app/services/ci/stop_environments_service.rb
@@ -8,10 +8,9 @@ module Ci
return unless has_ref?
environments.each do |environment|
- next unless environment.stoppable?
next unless can?(current_user, :create_deployment, project)
- environment.stop!(current_user)
+ environment.stop_with_action!(current_user)
end
end
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index d2eb46ac41b..c9168f74249 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -3,6 +3,8 @@ module Issues
def execute
@request = params.delete(:request)
@api = params.delete(:api)
+ @recaptcha_verified = params.delete(:recaptcha_verified)
+ @spam_log_id = params.delete(:spam_log_id)
issue_attributes = params.merge(merge_request_for_resolving_discussions: merge_request_for_resolving_discussions)
@issue = BuildService.new(project, current_user, issue_attributes).execute
@@ -11,7 +13,13 @@ module Issues
end
def before_create(issuable)
- issuable.spam = spam_service.check(@api)
+ if @recaptcha_verified
+ spam_log = current_user.spam_logs.find_by(id: @spam_log_id, title: issuable.title)
+ spam_log.update!(recaptcha_verified: true) if spam_log
+ else
+ issuable.spam = spam_service.check(@api)
+ issuable.spam_log = spam_service.spam_log
+ end
end
def after_create(issuable)
@@ -35,7 +43,7 @@ module Issues
private
def spam_service
- SpamService.new(@issue, @request)
+ @spam_service ||= SpamService.new(@issue, @request)
end
def user_agent_detail_service
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 06252c7b625..535da706159 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -26,7 +26,7 @@ module Projects
end
def project_tree_saver
- Gitlab::ImportExport::ProjectTreeSaver.new(project: project, shared: @shared)
+ Gitlab::ImportExport::ProjectTreeSaver.new(project: project, current_user: @current_user, shared: @shared)
end
def uploads_saver
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 20b049b5973..484700c8c29 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -25,9 +25,10 @@ module Projects
end
def transfer(project, new_namespace)
+ old_namespace = project.namespace
+
Project.transaction do
old_path = project.path_with_namespace
- old_namespace = project.namespace
old_group = project.group
new_path = File.join(new_namespace.try(:path) || '', project.path)
@@ -70,8 +71,11 @@ module Projects
project.old_path_with_namespace = old_path
SystemHooksService.new.execute_hooks_for(project, :transfer)
- true
end
+
+ refresh_permissions(old_namespace, new_namespace)
+
+ true
end
def allowed_transfer?(current_user, project, namespace)
@@ -80,5 +84,14 @@ module Projects
namespace.id != project.namespace_id &&
current_user.can?(:create_projects, namespace)
end
+
+ def refresh_permissions(old_namespace, new_namespace)
+ # This ensures we only schedule 1 job for every user that has access to
+ # the namespaces.
+ user_ids = old_namespace.user_ids_for_project_authorizations |
+ new_namespace.user_ids_for_project_authorizations
+
+ UserProjectAccessChangedService.new(user_ids).execute
+ end
end
end
diff --git a/app/services/spam_service.rb b/app/services/spam_service.rb
index 48903291799..024a7c19d33 100644
--- a/app/services/spam_service.rb
+++ b/app/services/spam_service.rb
@@ -1,5 +1,6 @@
class SpamService
attr_accessor :spammable, :request, :options
+ attr_reader :spam_log
def initialize(spammable, request = nil)
@spammable = spammable
@@ -63,7 +64,7 @@ class SpamService
end
def create_spam_log(api)
- SpamLog.create(
+ @spam_log = SpamLog.create!(
{
user_id: spammable_owner_id,
title: spammable.spam_title,
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 110072e3a16..87ba72cf991 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -385,6 +385,7 @@ module SystemNoteService
# Returns Boolean
def cross_reference_disallowed?(noteable, mentioner)
return true if noteable.is_a?(ExternalIssue) && !noteable.project.jira_tracker_active?
+ return true if noteable.is_a?(Issuable) && (noteable.try(:closed?) || noteable.try(:merged?))
return false unless mentioner.is_a?(MergeRequest)
return false unless noteable.is_a?(Commit)
diff --git a/app/views/admin/spam_logs/_spam_log.html.haml b/app/views/admin/spam_logs/_spam_log.html.haml
index 4ce4eab8753..33f6d847782 100644
--- a/app/views/admin/spam_logs/_spam_log.html.haml
+++ b/app/views/admin/spam_logs/_spam_log.html.haml
@@ -14,6 +14,8 @@
%td
= spam_log.via_api? ? 'Y' : 'N'
%td
+ = spam_log.recaptcha_verified ? 'Y' : 'N'
+ %td
= spam_log.noteable_type
%td
= spam_log.title
diff --git a/app/views/admin/spam_logs/index.html.haml b/app/views/admin/spam_logs/index.html.haml
index 0fdd5bd9960..8aaa6379730 100644
--- a/app/views/admin/spam_logs/index.html.haml
+++ b/app/views/admin/spam_logs/index.html.haml
@@ -10,6 +10,7 @@
%th User
%th Source IP
%th API?
+ %th Recaptcha verified?
%th Type
%th Title
%th Description
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 01ecf237925..5a44ec45b7b 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -23,7 +23,7 @@
= f.password_field :password, class: "form-control bottom", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters."
%p.gl-field-hint Minimum length is #{@minimum_password_length} characters
%div
- - if current_application_settings.recaptcha_enabled
+ - if Gitlab::Recaptcha.enabled?
= recaptcha_tags
%div
= f.submit "Register", class: "btn-register btn"
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index da2df0d8080..705e20112fa 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -79,6 +79,14 @@
%td.shortcut
.key esc
%td Go back
+ %tbody
+ %tr
+ %th
+ %th Project File
+ %tr
+ %td.shortcut
+ .key y
+ %td Go to file permalink
.col-lg-4
%table.shortcut-mappings
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index d6c158b6de3..665725f6862 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -18,20 +18,8 @@
Protected Branches
- if @project.feature_available?(:builds, current_user)
- = nav_link(controller: :runners) do
- = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners' do
- %span
- Runners
- = nav_link(controller: :variables) do
- = link_to namespace_project_variables_path(@project.namespace, @project), title: 'Variables' do
- %span
- Variables
- = nav_link(controller: :triggers) do
- = link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
- %span
- Triggers
- = nav_link(controller: :pipelines_settings) do
- = link_to namespace_project_pipelines_settings_path(@project.namespace, @project), title: 'CI/CD Pipelines' do
+ = nav_link(controller: :ci_cd) do
+ = link_to namespace_project_settings_ci_cd_path(@project.namespace, @project), title: 'CI/CD Pipelines' do
%span
CI/CD Pipelines
= nav_link(controller: :pages) do
diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml
index ff893ea74e1..26531f0b1a6 100644
--- a/app/views/projects/blob/_actions.html.haml
+++ b/app/views/projects/blob/_actions.html.haml
@@ -12,7 +12,7 @@
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id),
class: 'btn btn-sm'
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
- tree_join(@commit.sha, @path)), class: 'btn btn-sm'
+ tree_join(@commit.sha, @path)), class: 'btn btn-sm js-data-file-blob-permalink-url'
- if current_user
.btn-group{ role: "group" }
diff --git a/app/views/projects/environments/_stop.html.haml b/app/views/projects/environments/_stop.html.haml
index 69848123c17..14a2d627203 100644
--- a/app/views/projects/environments/_stop.html.haml
+++ b/app/views/projects/environments/_stop.html.haml
@@ -1,4 +1,4 @@
-- if can?(current_user, :create_deployment, environment) && environment.stoppable?
+- if can?(current_user, :create_deployment, environment) && environment.stop_action?
.inline
= link_to stop_namespace_project_environment_path(@project.namespace, @project, environment), method: :post,
class: 'btn stop-env-link', rel: 'nofollow', data: { confirm: 'Are you sure you want to stop this environment?' } do
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index 7800d6ac382..7036325fff8 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -12,7 +12,7 @@
= render 'projects/environments/external_url', environment: @environment
- if can?(current_user, :update_environment, @environment)
= link_to 'Edit', edit_namespace_project_environment_path(@project.namespace, @project, @environment), class: 'btn'
- - if can?(current_user, :create_deployment, @environment) && @environment.stoppable?
+ - if can?(current_user, :create_deployment, @environment) && @environment.can_stop?
= link_to 'Stop', stop_namespace_project_environment_path(@project.namespace, @project, @environment), data: { confirm: 'Are you sure you want to stop this environment?' }, class: 'btn btn-danger', method: :post
.deployments-container
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index f3be343daae..085b2fc2814 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -50,7 +50,7 @@
- if issue.labels.any?
&nbsp;
- issue.labels.each do |label|
- = link_to_label(label, subject: issue.project)
+ = link_to_label(label, subject: issue.project, css_class: 'label-link')
- if issue.tasks?
&nbsp;
%span.task-status
diff --git a/app/views/projects/issues/verify.html.haml b/app/views/projects/issues/verify.html.haml
new file mode 100644
index 00000000000..1934b18c086
--- /dev/null
+++ b/app/views/projects/issues/verify.html.haml
@@ -0,0 +1,20 @@
+- page_title "Anti-spam verification"
+
+%h3.page-title
+ Anti-spam verification
+%hr
+
+%p
+ We detected potential spam in the issue description. Please verify that you are not a robot to submit the issue.
+
+= form_for [@project.namespace.becomes(Namespace), @project, @issue] do |f|
+ .recaptcha
+ - params[:issue].each do |field, value|
+ = hidden_field(:issue, field, value: value)
+ = hidden_field_tag(:merge_request_for_resolving_discussions, params[:merge_request_for_resolving_discussions])
+ = hidden_field_tag(:spam_log_id, @issue.spam_log.id)
+ = hidden_field_tag(:recaptcha_verification, true)
+ = recaptcha_tags
+
+ .row-content-block.footer-block
+ = f.submit "Submit #{@issue.class.model_name.human.downcase}", class: 'btn btn-create'
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 513f0818169..4dbb97b3228 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -64,7 +64,7 @@
- if merge_request.labels.any?
&nbsp;
- merge_request.labels.each do |label|
- = link_to_label(label, subject: merge_request.project, type: :merge_request)
+ = link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link')
- if merge_request.tasks?
&nbsp;
diff --git a/app/views/projects/pipelines_settings/show.html.haml b/app/views/projects/pipelines_settings/_show.html.haml
index 18328c67f02..8024fb8979d 100644
--- a/app/views/projects/pipelines_settings/show.html.haml
+++ b/app/views/projects/pipelines_settings/_show.html.haml
@@ -1,9 +1,7 @@
-- page_title "CI/CD Pipelines"
-
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
- = page_title
+ CI/CD Pipelines
.col-lg-9
= form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project) do |f|
%fieldset.builds-feature
@@ -95,4 +93,4 @@
%hr
.row.prepend-top-default
- = render partial: 'badge', collection: @badges
+ = render partial: 'projects/pipelines_settings/badge', collection: @badges
diff --git a/app/views/projects/runners/index.html.haml b/app/views/projects/runners/_index.html.haml
index d6f691d9c24..f9808f7c990 100644
--- a/app/views/projects/runners/index.html.haml
+++ b/app/views/projects/runners/_index.html.haml
@@ -1,5 +1,3 @@
-- page_title "Runners"
-
.light.prepend-top-default
%p
A 'Runner' is a process which runs a job.
@@ -22,6 +20,6 @@
%p.lead To start serving your jobs you can either add specific Runners to your project or use shared Runners
.row
.col-sm-6
- = render 'specific_runners'
+ = render 'projects/runners/specific_runners'
.col-sm-6
- = render 'shared_runners'
+ = render 'projects/runners/shared_runners'
diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml
index 5afa193357e..0671dd66e78 100644
--- a/app/views/projects/runners/_shared_runners.html.haml
+++ b/app/views/projects/runners/_shared_runners.html.haml
@@ -22,7 +22,7 @@
- else
%h4.underlined-title Available shared Runners : #{@shared_runners_count}
%ul.bordered-list.available-shared-runners
- = render partial: 'runner', collection: @shared_runners, as: :runner
+ = render partial: 'projects/runners/runner', collection: @shared_runners, as: :runner
- if @shared_runners_count > 10
.light
and #{@shared_runners_count - 10} more...
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index dcff675eafc..6b8e6bd4fee 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -20,10 +20,10 @@
- if @project_runners.any?
%h4.underlined-title Runners activated for this project
%ul.bordered-list.activated-specific-runners
- = render partial: 'runner', collection: @project_runners, as: :runner
+ = render partial: 'projects/runners/runner', collection: @project_runners, as: :runner
- if @assignable_runners.any?
%h4.underlined-title Available specific runners
%ul.bordered-list.available-specific-runners
- = render partial: 'runner', collection: @assignable_runners, as: :runner
+ = render partial: 'projects/runners/runner', collection: @assignable_runners, as: :runner
= paginate @assignable_runners, theme: "gitlab"
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
new file mode 100644
index 00000000000..52f5f7b81e2
--- /dev/null
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -0,0 +1,6 @@
+- page_title "CI/CD Pipelines"
+
+= render 'projects/runners/index'
+= render 'projects/variables/index'
+= render 'projects/triggers/index'
+= render 'projects/pipelines_settings/show'
diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/_index.html.haml
index b9c4e323430..5cb1818ae54 100644
--- a/app/views/projects/triggers/index.html.haml
+++ b/app/views/projects/triggers/_index.html.haml
@@ -1,9 +1,7 @@
-- page_title "Triggers"
-
.row.prepend-top-default.append-bottom-default
.col-lg-3
%h4.prepend-top-0
- = page_title
+ Triggers
%p.prepend-top-20
Triggers can force a specific branch or tag to get rebuilt with an API call.
%p.append-bottom-0
@@ -25,12 +23,12 @@
%th
%strong Last used
%th
- = render partial: 'trigger', collection: @triggers, as: :trigger
+ = render partial: 'projects/triggers/trigger', collection: @triggers, as: :trigger
- else
%p.settings-message.text-center.append-bottom-default
No triggers have been created yet. Add one using the button below.
- = form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create') do |f|
+ = form_for @trigger, url: url_for(controller: '/projects/triggers', action: 'create') do |f|
= f.submit "Add trigger", class: 'btn btn-success'
.panel-footer
diff --git a/app/views/projects/variables/index.html.haml b/app/views/projects/variables/_index.html.haml
index cf7ae0b489f..1b852a9c5b3 100644
--- a/app/views/projects/variables/index.html.haml
+++ b/app/views/projects/variables/_index.html.haml
@@ -1,12 +1,10 @@
-- page_title "Variables"
-
.row.prepend-top-default.append-bottom-default
.col-lg-3
- = render "content"
+ = render "projects/variables/content"
.col-lg-9
%h5.prepend-top-0
Add a variable
- = render "form", btn_text: "Add new variable"
+ = render "projects/variables/form", btn_text: "Add new variable"
%hr
%h5.prepend-top-0
Your variables (#{@project.variables.size})
@@ -14,5 +12,5 @@
%p.settings-message.text-center.append-bottom-0
No variables found, add one with the form above.
- else
- = render "table"
+ = render "projects/variables/table"
%button.btn.btn-info.js-btn-toggle-reveal-values{ "data-status" => 'hidden' } Reveal Values
diff --git a/changelogs/unreleased/21518_recaptcha_spam_issues.yml b/changelogs/unreleased/21518_recaptcha_spam_issues.yml
new file mode 100644
index 00000000000..bd6c9d7521e
--- /dev/null
+++ b/changelogs/unreleased/21518_recaptcha_spam_issues.yml
@@ -0,0 +1,4 @@
+---
+title: Use reCaptcha when an issue is identified as a spam
+merge_request: 8846
+author:
diff --git a/changelogs/unreleased/24147-delete-env-button.yml b/changelogs/unreleased/24147-delete-env-button.yml
new file mode 100644
index 00000000000..14e80cacbfb
--- /dev/null
+++ b/changelogs/unreleased/24147-delete-env-button.yml
@@ -0,0 +1,4 @@
+---
+title: Adds back ability to stop all environments
+merge_request: 7379
+author:
diff --git a/changelogs/unreleased/26059-segoe-ui-vertical.yml b/changelogs/unreleased/26059-segoe-ui-vertical.yml
new file mode 100644
index 00000000000..fc3f1af5b61
--- /dev/null
+++ b/changelogs/unreleased/26059-segoe-ui-vertical.yml
@@ -0,0 +1,4 @@
+---
+title: Align Segoe UI label text
+merge_request:
+author:
diff --git a/changelogs/unreleased/26920-hover-cursor-on-pagination-element.yml b/changelogs/unreleased/26920-hover-cursor-on-pagination-element.yml
new file mode 100644
index 00000000000..ea567437ac2
--- /dev/null
+++ b/changelogs/unreleased/26920-hover-cursor-on-pagination-element.yml
@@ -0,0 +1,4 @@
+---
+title: Fixes hover cursor on pipeline pagenation
+merge_request: 9003
+author:
diff --git a/changelogs/unreleased/27632_fix_mr_widget_url.yml b/changelogs/unreleased/27632_fix_mr_widget_url.yml
new file mode 100644
index 00000000000..958621a43a1
--- /dev/null
+++ b/changelogs/unreleased/27632_fix_mr_widget_url.yml
@@ -0,0 +1,4 @@
+---
+title: Fix MR widget url
+merge_request: 8989
+author:
diff --git a/changelogs/unreleased/8082-permalink-to-file.yml b/changelogs/unreleased/8082-permalink-to-file.yml
new file mode 100644
index 00000000000..136d2108c63
--- /dev/null
+++ b/changelogs/unreleased/8082-permalink-to-file.yml
@@ -0,0 +1,4 @@
+---
+title: Add `y` keyboard shortcut to move to file permalink
+merge_request:
+author:
diff --git a/changelogs/unreleased/api-remove-snippets-expires-at.yml b/changelogs/unreleased/api-remove-snippets-expires-at.yml
new file mode 100644
index 00000000000..67603bfab3b
--- /dev/null
+++ b/changelogs/unreleased/api-remove-snippets-expires-at.yml
@@ -0,0 +1,4 @@
+---
+title: 'API: Remove deprecated ''expires_at'' from project snippets'
+merge_request: 8723
+author: Robert Schilling
diff --git a/changelogs/unreleased/fix-import-group-members.yml b/changelogs/unreleased/fix-import-group-members.yml
new file mode 100644
index 00000000000..fe580af31b3
--- /dev/null
+++ b/changelogs/unreleased/fix-import-group-members.yml
@@ -0,0 +1,4 @@
+---
+title: Add ability to export project inherited group members to Import/Export
+merge_request: 8923
+author:
diff --git a/changelogs/unreleased/issue_19262.yml b/changelogs/unreleased/issue_19262.yml
new file mode 100644
index 00000000000..5dea1493f23
--- /dev/null
+++ b/changelogs/unreleased/issue_19262.yml
@@ -0,0 +1,4 @@
+---
+title: Disallow system notes for closed issuables
+merge_request:
+author:
diff --git a/changelogs/unreleased/refresh-permissions-when-moving-projects.yml b/changelogs/unreleased/refresh-permissions-when-moving-projects.yml
new file mode 100644
index 00000000000..a94bcdaa9a3
--- /dev/null
+++ b/changelogs/unreleased/refresh-permissions-when-moving-projects.yml
@@ -0,0 +1,4 @@
+---
+title: Refresh authorizations when transferring projects
+merge_request:
+author:
diff --git a/changelogs/unreleased/remove-deploy-key-endpoint.yml b/changelogs/unreleased/remove-deploy-key-endpoint.yml
new file mode 100644
index 00000000000..3ff69adb4d3
--- /dev/null
+++ b/changelogs/unreleased/remove-deploy-key-endpoint.yml
@@ -0,0 +1,4 @@
+---
+title: 'API: Remove /projects/:id/keys/.. endpoints'
+merge_request: 8716
+author: Robert Schilling
diff --git a/config/routes/project.rb b/config/routes/project.rb
index c24893f4cd5..2ac98cf3842 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -315,6 +315,7 @@ constraints(ProjectUrlConstrainer.new) do
end
namespace :settings do
resource :members, only: [:show]
+ resource :ci_cd, only: [:show], controller: 'ci_cd'
resource :integrations, only: [:show]
end
diff --git a/db/migrate/20170206071414_add_recaptcha_verified_to_spam_logs.rb b/db/migrate/20170206071414_add_recaptcha_verified_to_spam_logs.rb
new file mode 100644
index 00000000000..44372334d21
--- /dev/null
+++ b/db/migrate/20170206071414_add_recaptcha_verified_to_spam_logs.rb
@@ -0,0 +1,15 @@
+class AddRecaptchaVerifiedToSpamLogs < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_column_with_default(:spam_logs, :recaptcha_verified, :boolean, default: false)
+ end
+
+ def down
+ remove_column(:spam_logs, :recaptcha_verified)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1099397824d..aeb0d8210f0 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: 20170204181513) do
+ActiveRecord::Schema.define(version: 20170206071414) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1114,6 +1114,7 @@ ActiveRecord::Schema.define(version: 20170204181513) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "submitted_as_ham", default: false, null: false
+ t.boolean "recaptcha_verified", default: false, null: false
end
create_table "subscriptions", force: :cascade do |t|
diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md
index 11444464537..3b5ee86b68b 100644
--- a/doc/administration/integration/terminal.md
+++ b/doc/administration/integration/terminal.md
@@ -82,4 +82,4 @@ GitLab instance if you find this undesirable from a scalability or security
point of view.
[ce-7690]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7690
-[kubservice]: ../../user/project/integrations/kubernetes.md)
+[kubservice]: ../../user/project/integrations/kubernetes.md
diff --git a/doc/administration/reply_by_email_postfix_setup.md b/doc/administration/reply_by_email_postfix_setup.md
index 22f10489a6c..3b8c716eff5 100644
--- a/doc/administration/reply_by_email_postfix_setup.md
+++ b/doc/administration/reply_by_email_postfix_setup.md
@@ -315,7 +315,7 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
## Done!
-If all the tests were successful, Postfix is all set up and ready to receive email! Continue with the [Reply by email](./README.md) guide to configure GitLab.
+If all the tests were successful, Postfix is all set up and ready to receive email! Continue with the [Reply by email](./reply_by_email.md) guide to configure GitLab.
---
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index c6685f54a9d..404876f6237 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -51,7 +51,6 @@ Parameters:
"state": "active",
"created_at": "2012-05-23T08:00:58Z"
},
- "expires_at": null,
"updated_at": "2012-06-28T10:52:04Z",
"created_at": "2012-06-28T10:52:04Z",
"web_url": "http://example.com/example/example/snippets/1"
diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md
index 9748aec17ad..707f0437b7e 100644
--- a/doc/api/v3_to_v4.md
+++ b/doc/api/v3_to_v4.md
@@ -10,4 +10,5 @@ changes are in V4:
- `iid` filter has been removed from `projects/:id/issues`
- `projects/:id/merge_requests?iid[]=x&iid[]=y` array filter has been renamed to `iids`
- Endpoints under `projects/merge_request/:id` have been removed (use: `projects/merge_requests/:id`)
-
+- Project snippets do not return deprecated field `expires_at`
+- Endpoints under `projects/:id/keys` have been removed (use `projects/:id/deploy_keys`)
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index f91b9d350f7..2c7c3ef3c18 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -75,5 +75,5 @@ respective link in the [Pipelines settings] page.
[builds]: #builds
[jobs]: yaml/README.md#jobs
[stages]: yaml/README.md#stages
-[runners]: runners/READM
+[runners]: runners/README.html
[pipelines settings]: ../user/project/pipelines/settings.md
diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md
index 2d1d504202c..df6ac452300 100644
--- a/doc/development/ui_guide.md
+++ b/doc/development/ui_guide.md
@@ -20,8 +20,8 @@ The content section contains a header and the content itself. The header describ
available to the user in this area. Depending on the area (project, group, profile setting) the header name and navigation may change. For example, when the user visits one of the
project pages the header will contain the project's name and navigation for that project. When the user visits a group page it will contain the group's name and navigation related to this group.
-You can see a visual representation of the navigation in GitLab in the GitLab Product Map, which is located in the [Design Repository](gitlab-map-graffle)
-along with [PDF](gitlab-map-pdf) and [PNG](gitlab-map-png) exports.
+You can see a visual representation of the navigation in GitLab in the GitLab Product Map, which is located in the [Design Repository][gitlab-map-graffle]
+along with [PDF][gitlab-map-pdf] and [PNG][gitlab-map-png] exports.
### Adding new tab to header navigation
@@ -104,4 +104,4 @@ Do not use both green and blue button in one form.
[number_with_delimiter]: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_with_delimiter
[gitlab-map-graffle]: https://gitlab.com/gitlab-org/gitlab-design/blob/master/production/resources/gitlab-map.graffle
[gitlab-map-pdf]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.pdf
-[gitlab-map-png]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png \ No newline at end of file
+[gitlab-map-png]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 2b5f8c6d02d..355179960b3 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -271,9 +271,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-16-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-17-stable gitlab
-**Note:** You can change `8-16-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `8-17-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
diff --git a/doc/raketasks/features.md b/doc/raketasks/features.md
index f9a46193547..fee49cc27cc 100644
--- a/doc/raketasks/features.md
+++ b/doc/raketasks/features.md
@@ -7,7 +7,7 @@ This command will enable the namespaces feature introduced in v4.0. It will move
Note:
- Because the **repository location will change**, you will need to **update all your git URLs** to point to the new location.
-- Username can be changed at [Profile / Account](/profile/account)
+- Username can be changed at **Profile âž” Account**.
**Example:**
diff --git a/doc/university/training/topics/additional_resources.md b/doc/university/training/topics/additional_resources.md
index 1ee615432aa..3ed601625cf 100755
--- a/doc/university/training/topics/additional_resources.md
+++ b/doc/university/training/topics/additional_resources.md
@@ -5,4 +5,4 @@
3. Pro git book [http://git-scm.com/book](http://git-scm.com/book)
4. Platzi Course [https://courses.platzi.com/courses/git-gitlab/](https://courses.platzi.com/courses/git-gitlab/)
5. Code School tutorial [http://try.github.io/](http://try.github.io/)
-6. Contact Us - [subscribers@gitlab.com](subscribers@gitlab.com)
+6. Contact Us at `subscribers@gitlab.com`
diff --git a/doc/university/training/user_training.md b/doc/university/training/user_training.md
index 35afe73708f..9e38df26b6a 100755
--- a/doc/university/training/user_training.md
+++ b/doc/university/training/user_training.md
@@ -389,4 +389,4 @@ GUI Clients [http://git-scm.com/downloads/guis](http://git-scm.com/downloads/gui
Pro git book [http://git-scm.com/book](http://git-scm.com/book)
Platzi Course [https://courses.platzi.com/courses/git-gitlab/](https://courses.platzi.com/courses/git-gitlab/)
Code School tutorial [http://try.github.io/](http://try.github.io/)
-Contact Us - [subscribers@gitlab.com](subscribers@gitlab.com)
+Contact Us at `subscribers@gitlab.com`
diff --git a/doc/update/2.6-to-3.0.md b/doc/update/2.6-to-3.0.md
index fb70eaacbc9..97cd277b424 100644
--- a/doc/update/2.6-to-3.0.md
+++ b/doc/update/2.6-to-3.0.md
@@ -1,5 +1,5 @@
# From 2.6 to 3.0
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/2.6-to-3.0.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/2.6-to-3.0.md) for the most up to date instructions.*
## 1. Stop server & resque
diff --git a/doc/update/2.9-to-3.0.md b/doc/update/2.9-to-3.0.md
index ce46b57c09a..a890aa885d5 100644
--- a/doc/update/2.9-to-3.0.md
+++ b/doc/update/2.9-to-3.0.md
@@ -1,5 +1,5 @@
# From 2.9 to 3.0
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/2.9-to-3.0.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/2.9-to-3.0.md) for the most up to date instructions.*
## 1. Stop server & resque
diff --git a/doc/update/3.0-to-3.1.md b/doc/update/3.0-to-3.1.md
index 6ac83f3b60d..e32508745a2 100644
--- a/doc/update/3.0-to-3.1.md
+++ b/doc/update/3.0-to-3.1.md
@@ -1,5 +1,5 @@
# From 3.0 to 3.1
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/3.0-to-3.1.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/3.0-to-3.1.md) for the most up to date instructions.*
**IMPORTANT!**
diff --git a/doc/update/3.1-to-4.0.md b/doc/update/3.1-to-4.0.md
index df53ed6de83..b370464390e 100644
--- a/doc/update/3.1-to-4.0.md
+++ b/doc/update/3.1-to-4.0.md
@@ -1,5 +1,5 @@
# From 3.1 to 4.0
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/3.1-to-4.0.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/3.1-to-4.0.md) for the most up to date instructions.*
## Important changes
diff --git a/doc/update/4.0-to-4.1.md b/doc/update/4.0-to-4.1.md
index c66c6dd0fd8..7124424bb60 100644
--- a/doc/update/4.0-to-4.1.md
+++ b/doc/update/4.0-to-4.1.md
@@ -1,5 +1,5 @@
# From 4.0 to 4.1
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/4.0-to-4.1.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/4.0-to-4.1.md) for the most up to date instructions.*
## Important changes
diff --git a/doc/update/4.1-to-4.2.md b/doc/update/4.1-to-4.2.md
index 97367c5f347..8ed5b333a2e 100644
--- a/doc/update/4.1-to-4.2.md
+++ b/doc/update/4.1-to-4.2.md
@@ -1,5 +1,5 @@
# From 4.1 to 4.2
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/4.1-to-4.2.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/4.1-to-4.2.md) for the most up to date instructions.*
## 1. Stop server & Resque
diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md
index 7654f4a0131..1ec39218ba8 100644
--- a/doc/update/4.2-to-5.0.md
+++ b/doc/update/4.2-to-5.0.md
@@ -1,5 +1,5 @@
# From 4.2 to 5.0
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/4.2-to-5.0.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/4.2-to-5.0.md) for the most up to date instructions.*
## Warning
diff --git a/doc/update/5.0-to-5.1.md b/doc/update/5.0-to-5.1.md
index c19a819ab5a..9c9950fb2c6 100644
--- a/doc/update/5.0-to-5.1.md
+++ b/doc/update/5.0-to-5.1.md
@@ -1,5 +1,5 @@
# From 5.0 to 5.1
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.0-to-5.1.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/5.0-to-5.1.md) for the most up to date instructions.*
## Warning
diff --git a/doc/update/5.1-to-5.2.md b/doc/update/5.1-to-5.2.md
index 625fcc33852..2aab47d2d7c 100644
--- a/doc/update/5.1-to-5.2.md
+++ b/doc/update/5.1-to-5.2.md
@@ -1,5 +1,5 @@
# From 5.1 to 5.2
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.1-to-5.2.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/5.1-to-5.2.md) for the most up to date instructions.*
## Warning
diff --git a/doc/update/5.1-to-5.4.md b/doc/update/5.1-to-5.4.md
index 547d453914c..e80f1b89c63 100644
--- a/doc/update/5.1-to-5.4.md
+++ b/doc/update/5.1-to-5.4.md
@@ -1,5 +1,5 @@
# From 5.1 to 5.4
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.1-to-5.4.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/5.1-to-5.4.md) for the most up to date instructions.*
Also works starting from 5.2.
diff --git a/doc/update/5.1-to-6.0.md b/doc/update/5.1-to-6.0.md
index c992c69678e..1ee175383da 100644
--- a/doc/update/5.1-to-6.0.md
+++ b/doc/update/5.1-to-6.0.md
@@ -1,5 +1,5 @@
# From 5.1 to 6.0
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.1-to-6.0.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/5.1-to-6.0.md) for the most up to date instructions.*
## Warning
diff --git a/doc/update/5.2-to-5.3.md b/doc/update/5.2-to-5.3.md
index fe8990b6843..2ae50510f63 100644
--- a/doc/update/5.2-to-5.3.md
+++ b/doc/update/5.2-to-5.3.md
@@ -1,5 +1,5 @@
# From 5.2 to 5.3
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.2-to-5.3.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/5.2-to-5.3.md) for the most up to date instructions.*
## Warning
diff --git a/doc/update/5.3-to-5.4.md b/doc/update/5.3-to-5.4.md
index 5f82ad7d444..842e3bb6791 100644
--- a/doc/update/5.3-to-5.4.md
+++ b/doc/update/5.3-to-5.4.md
@@ -1,5 +1,5 @@
# From 5.3 to 5.4
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.3-to-5.4.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/5.3-to-5.4.md) for the most up to date instructions.*
## 0. Backup
diff --git a/doc/update/5.4-to-6.0.md b/doc/update/5.4-to-6.0.md
index f0fee634322..44715984f0c 100644
--- a/doc/update/5.4-to-6.0.md
+++ b/doc/update/5.4-to-6.0.md
@@ -1,5 +1,5 @@
# From 5.4 to 6.0
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/5.4-to-6.0.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/5.4-to-6.0.md) for the most up to date instructions.*
## Warning
diff --git a/doc/update/6.0-to-6.1.md b/doc/update/6.0-to-6.1.md
index 409faf30902..0c672abeb05 100644
--- a/doc/update/6.0-to-6.1.md
+++ b/doc/update/6.0-to-6.1.md
@@ -1,5 +1,5 @@
# From 6.0 to 6.1
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.0-to-6.1.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.0-to-6.1.md) for the most up to date instructions.*
## Warning
diff --git a/doc/update/6.1-to-6.2.md b/doc/update/6.1-to-6.2.md
index 150c7ae1c83..d3760cf0619 100644
--- a/doc/update/6.1-to-6.2.md
+++ b/doc/update/6.1-to-6.2.md
@@ -1,5 +1,5 @@
# From 6.1 to 6.2
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.1-to-6.2.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.1-to-6.2.md) for the most up to date instructions.*
**You should update to 6.1 before installing 6.2 so all the necessary conversions are run.**
diff --git a/doc/update/6.2-to-6.3.md b/doc/update/6.2-to-6.3.md
index b96dfb8add7..91105de2e29 100644
--- a/doc/update/6.2-to-6.3.md
+++ b/doc/update/6.2-to-6.3.md
@@ -1,5 +1,5 @@
# From 6.2 to 6.3
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.2-to-6.3.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.2-to-6.3.md) for the most up to date instructions.*
**Requires version: 6.1 or 6.2.**
diff --git a/doc/update/6.3-to-6.4.md b/doc/update/6.3-to-6.4.md
index 37028be055f..20b58ed8b25 100644
--- a/doc/update/6.3-to-6.4.md
+++ b/doc/update/6.3-to-6.4.md
@@ -1,5 +1,5 @@
# From 6.3 to 6.4
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.3-to-6.4.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.3-to-6.4.md) for the most up to date instructions.*
## 0. Backup
diff --git a/doc/update/6.4-to-6.5.md b/doc/update/6.4-to-6.5.md
index 982381a4db0..5ee0f040b5d 100644
--- a/doc/update/6.4-to-6.5.md
+++ b/doc/update/6.4-to-6.5.md
@@ -1,5 +1,5 @@
# From 6.4 to 6.5
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.4-to-6.5.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.4-to-6.5.md) for the most up to date instructions.*
## 0. Backup
diff --git a/doc/update/6.5-to-6.6.md b/doc/update/6.5-to-6.6.md
index bbed2b30215..fa3712f83ad 100644
--- a/doc/update/6.5-to-6.6.md
+++ b/doc/update/6.5-to-6.6.md
@@ -1,5 +1,5 @@
# From 6.5 to 6.6
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.5-to-6.6.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.5-to-6.6.md) for the most up to date instructions.*
## 0. Backup
diff --git a/doc/update/6.6-to-6.7.md b/doc/update/6.6-to-6.7.md
index 8e82942a1a0..9c85ed091c5 100644
--- a/doc/update/6.6-to-6.7.md
+++ b/doc/update/6.6-to-6.7.md
@@ -1,5 +1,5 @@
# From 6.6 to 6.7
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.6-to-6.7.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.6-to-6.7.md) for the most up to date instructions.*
## 0. Backup
diff --git a/doc/update/6.7-to-6.8.md b/doc/update/6.7-to-6.8.md
index 4fb90639f16..687c1265d9b 100644
--- a/doc/update/6.7-to-6.8.md
+++ b/doc/update/6.7-to-6.8.md
@@ -1,5 +1,5 @@
# From 6.7 to 6.8
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.7-to-6.8.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.7-to-6.8.md) for the most up to date instructions.*
## 0. Backup
diff --git a/doc/update/6.8-to-6.9.md b/doc/update/6.8-to-6.9.md
index b9b8b63f652..0205b0c896a 100644
--- a/doc/update/6.8-to-6.9.md
+++ b/doc/update/6.8-to-6.9.md
@@ -1,5 +1,5 @@
# From 6.8 to 6.9
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.8-to-6.9.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.8-to-6.9.md) for the most up to date instructions.*
### 0. Backup
diff --git a/doc/update/6.9-to-7.0.md b/doc/update/6.9-to-7.0.md
index 5352fd52f93..4b6e3989893 100644
--- a/doc/update/6.9-to-7.0.md
+++ b/doc/update/6.9-to-7.0.md
@@ -1,5 +1,5 @@
# From 6.9 to 7.0
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.9-to-7.0.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.9-to-7.0.md) for the most up to date instructions.*
### 0. Backup
diff --git a/doc/update/6.x-or-7.x-to-7.14.md b/doc/update/6.x-or-7.x-to-7.14.md
index f170a0021b7..1e39fe47ef9 100644
--- a/doc/update/6.x-or-7.x-to-7.14.md
+++ b/doc/update/6.x-or-7.x-to-7.14.md
@@ -1,5 +1,5 @@
# From 6.x or 7.x to 7.14
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.x-or-7.x-to-7.14.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/6.x-or-7.x-to-7.14.md) for the most up to date instructions.*
This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.14.
@@ -222,7 +222,7 @@ If all items are green, then congratulations upgrade complete!
When using Google omniauth login, changes of the Google account required.
Ensure that `Contacts API` and the `Google+ API` are enabled in the [Google Developers Console](https://console.developers.google.com/).
-More details can be found at the [integration documentation](../../../master/doc/integration/google.md).
+More details can be found at the [integration documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/integration/google.md).
## 12. Optional optimizations for GitLab setups with MySQL databases
diff --git a/doc/update/7.0-to-7.1.md b/doc/update/7.0-to-7.1.md
index 71f39c44077..c717affebd3 100644
--- a/doc/update/7.0-to-7.1.md
+++ b/doc/update/7.0-to-7.1.md
@@ -1,5 +1,5 @@
# From 7.0 to 7.1
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/7.0-to-7.1.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/7.0-to-7.1.md) for the most up to date instructions.*
### 0. Backup
diff --git a/doc/update/7.1-to-7.2.md b/doc/update/7.1-to-7.2.md
index 88cb63d7d41..d01f8528e14 100644
--- a/doc/update/7.1-to-7.2.md
+++ b/doc/update/7.1-to-7.2.md
@@ -1,5 +1,5 @@
# From 7.1 to 7.2
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/7.1-to-7.2.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/7.1-to-7.2.md) for the most up to date instructions.*
## Editable labels
diff --git a/doc/update/7.2-to-7.3.md b/doc/update/7.2-to-7.3.md
index 18f77d6396e..0e91e682175 100644
--- a/doc/update/7.2-to-7.3.md
+++ b/doc/update/7.2-to-7.3.md
@@ -1,5 +1,5 @@
# From 7.2 to 7.3
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/7.2-to-7.3.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/7.2-to-7.3.md) for the most up to date instructions.*
### 0. Backup
diff --git a/doc/update/7.3-to-7.4.md b/doc/update/7.3-to-7.4.md
index 53e739c06fb..4df9127dd5f 100644
--- a/doc/update/7.3-to-7.4.md
+++ b/doc/update/7.3-to-7.4.md
@@ -1,5 +1,5 @@
# From 7.3 to 7.4
-*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/7.3-to-7.4.md) for the most up to date instructions.*
+*Make sure you view this [upgrade guide from the `master` branch](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update/7.3-to-7.4.md) for the most up to date instructions.*
### 0. Stop server
diff --git a/doc/update/8.16-to-8.17.md b/doc/update/8.16-to-8.17.md
new file mode 100644
index 00000000000..1808232c59a
--- /dev/null
+++ b/doc/update/8.16-to-8.17.md
@@ -0,0 +1,239 @@
+# From 8.16 to 8.17
+
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+We will continue supporting Ruby < 2.3 for the time being but we recommend you
+upgrade to Ruby 2.3 if you're running a source installation, as this is the same
+version that ships with our Omnibus package.
+
+You can check which version you are running with `ruby -v`.
+
+Download and compile Ruby:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.gz
+echo '1014ee699071aa2ddd501907d18cbe15399c997d ruby-2.3.3.tar.gz' | shasum -c - && tar xzf ruby-2.3.3.tar.gz
+cd ruby-2.3.3
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 8-17-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 8-17-stable-ee
+```
+
+### 5. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile 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).
+
+### 6. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires
+[Go 1.5](https://golang.org/dl) which should already be on your system from
+GitLab 8.1.
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v4.1.1
+```
+
+### 8. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/8-16-stable:config/gitlab.yml.example origin/8-17-stable:config/gitlab.yml.example
+```
+
+#### Git configuration
+
+Configure Git to generate packfile bitmaps (introduced in Git 2.0) on
+the GitLab server during `git gc`.
+
+```sh
+cd /home/git/gitlab
+
+sudo -u git -H git config --global repack.writeBitmaps true
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/8-16-stable:lib/support/nginx/gitlab-ssl origin/8-17-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/8-16-stable:lib/support/nginx/gitlab origin/8-17-stable:lib/support/nginx/gitlab
+```
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 9. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 10. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.16)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.15 to 8.16](8.15-to-8.16.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
diff --git a/doc/user/account/security.md b/doc/user/account/security.md
index 9336dee7451..2459f913583 100644
--- a/doc/user/account/security.md
+++ b/doc/user/account/security.md
@@ -1 +1 @@
-This document was moved to [profile](../profile/index.md#security).
+This document was moved to [profile](../profile/index.md).
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index cc688a7f99c..a23ad79ae1d 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -206,7 +206,7 @@ Sign in and re-enable two-factor authentication as soon as possible.
## Note to GitLab administrators
- You need to take special care to that 2FA keeps working after
-[restoring a GitLab backup](../raketasks/backup_restore.md).
+[restoring a GitLab backup](../../../raketasks/backup_restore.md).
- To ensure 2FA authorizes correctly with TOTP server, you may want to ensure
your GitLab server's time is synchronized via a service like NTP. Otherwise,
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index 47a4a3f85d0..91b35c73b34 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -1,10 +1,7 @@
# GitLab Container Registry
-> [Introduced][ce-4040] in GitLab 8.8.
-
----
-
>**Notes:**
+> [Introduced][ce-4040] in GitLab 8.8.
- Docker Registry manifest `v1` support was added in GitLab 8.9 to support Docker
versions earlier than 1.10.
- This document is about the user guide. To learn how to enable GitLab Container
@@ -98,8 +95,8 @@ delete them.
This feature requires GitLab 8.8 and GitLab Runner 1.2.
Make sure that your GitLab Runner is configured to allow building Docker images by
-following the [Using Docker Build](../ci/docker/using_docker_build.md)
-and [Using the GitLab Container Registry documentation](../ci/docker/using_docker_build.md#using-the-gitlab-container-registry).
+following the [Using Docker Build](../../ci/docker/using_docker_build.md)
+and [Using the GitLab Container Registry documentation](../../ci/docker/using_docker_build.md#using-the-gitlab-container-registry).
## Limitations
@@ -252,4 +249,4 @@ Once the right permissions were set, the error will go away.
[ce-4040]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4040
[docker-docs]: https://docs.docker.com/engine/userguide/intro/
-[private-docker]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/configuration/advanced-configuration.md#using-a-private-docker-registry
+[private-docker]: https://docs.gitlab.com/runner/configuration/advanced-configuration.html#using-a-private-docker-registry
diff --git a/doc/user/project/integrations/kubernetes.md b/doc/user/project/integrations/kubernetes.md
index 99aa9e44bdb..7b33327f6cf 100644
--- a/doc/user/project/integrations/kubernetes.md
+++ b/doc/user/project/integrations/kubernetes.md
@@ -40,7 +40,7 @@ the `ca.crt` contents as the `Custom CA bundle`.
## Deployment variables
The Kubernetes service exposes following
-[deployment variables](../ci/variables/README.md#deployment-variables) in the
+[deployment variables](../../../ci/variables/README.md#deployment-variables) in the
GitLab CI build environment:
- `KUBE_URL` - equal to the API URL
@@ -55,7 +55,7 @@ Added in GitLab 8.15. You must be the project owner or have `master` permissions
to use terminals. Support is currently limited to the first container in the
first pod of your environment.
-When enabled, the Kubernetes service adds [web terminal](../ci/environments.md#web-terminals)
+When enabled, the Kubernetes service adds [web terminal](../../../ci/environments.md#web-terminals)
support to your environments. This is based on the `exec` functionality found in
Docker and Kubernetes, so you get a new shell session within your existing
containers. To use this integration, you should deploy to Kubernetes using
diff --git a/doc/user/project/integrations/mattermost_slash_commands.md b/doc/user/project/integrations/mattermost_slash_commands.md
index 67cb88104c1..9a09e6d37ea 100644
--- a/doc/user/project/integrations/mattermost_slash_commands.md
+++ b/doc/user/project/integrations/mattermost_slash_commands.md
@@ -17,7 +17,7 @@ in it. All you have to do is configure it. Read more in the
## Automated Configuration
If Mattermost is installed on the same server as GitLab, the configuration process can be
-done for you by GitLab.
+done for you by GitLab.
Go to the Mattermost Slash Command service on your project and click the 'Add to Mattermost' button.
@@ -29,7 +29,7 @@ commands in Mattermost and then enable the service in GitLab.
### Step 1. Enable custom slash commands in Mattermost
This step is only required when using a source install, omnibus installs will be
-preconfigured with the right settings.
+preconfigured with the right settings.
The first thing to do in Mattermost is to enable custom slash commands from
the administrator console.
@@ -41,8 +41,8 @@ the administrator console.
---
-1. Click **Custom integrations** and set **Enable Custom Slash Commands**,
- **Enable custom integrations to override usernames**, and **Override
+1. Click **Custom integrations** and set **Enable Custom Slash Commands**,
+ **Enable custom integrations to override usernames**, and **Override
custom integrations to override profile picture icons** to true
![Mattermost console](img/mattermost_console_integrations.png)
@@ -149,15 +149,14 @@ trigger word followed by <kbd>help</kbd>. Example: <samp>/gitlab help</samp>
## Permissions
-The permissions to run the [available commands](#available-commands) derive from
-the [permissions you have on the project](../user/permissions.md#project).
+The permissions to run the [available commands](#available-slash-commands) derive from
+the [permissions you have on the project](../../permissions.md#project).
## Further reading
- [Mattermost slash commands documentation][mmslashdocs]
- [Omnibus GitLab Mattermost][omnimmdocs]
-
[omnimmdocs]: https://docs.gitlab.com/omnibus/gitlab-mattermost/
[mmslashdocs]: https://docs.mattermost.com/developer/slash-commands.html
-[ciyaml]: ../ci/yaml/README.md
+[ciyaml]: ../../../ci/yaml/README.md
diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md
index d9ff573d185..6efc2a168fa 100644
--- a/doc/user/project/integrations/slack_slash_commands.md
+++ b/doc/user/project/integrations/slack_slash_commands.md
@@ -6,7 +6,7 @@ Slack commands give users an extra interface to perform common operations
from the chat environment. This allows one to, for example, create an issue as
soon as the idea was discussed in chat.
For all available commands try the help subcommand, for example: `/gitlab help`,
-all review the [full list of commands](../integration/chat_commands.md).
+all review the [full list of commands](../../../integration/chat_commands.md).
## Prerequisites
diff --git a/doc/user/project/repository/web_editor.md b/doc/user/project/repository/web_editor.md
index 675e89e4247..c415d566a7c 100644
--- a/doc/user/project/repository/web_editor.md
+++ b/doc/user/project/repository/web_editor.md
@@ -170,6 +170,5 @@ you commit the changes you will be taken to a new merge request form.
![Start a new merge request with these changes](img/web_editor_start_new_merge_request.png)
-![New file button](basicsimages/file_button.png)
[ce-2808]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2808
-[issue closing pattern]: ../user/project/issues/automatic_issue_closing.md
+[issue closing pattern]: ../issues/automatic_issue_closing.md
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index cb1c1a84f8c..be042ddf623 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -14,6 +14,11 @@
> raketask.
> - The exports are stored in a temporary [shared directory][tmp] and are deleted
> every 24 hours by a specific worker.
+> - Group members will get exported as project members, as long as the user has
+> master or admin access to the group where the exported project lives. An admin
+> in the import side is required to map the users, based on email or username.
+> Otherwise, a supplementary comment is left to mention the original author and
+> the MRs, notes or issues will be owned by the importer.
Existing projects running on any GitLab instance or GitLab.com can be exported
with all their related data and be moved into a new GitLab instance.
@@ -22,7 +27,7 @@ with all their related data and be moved into a new GitLab instance.
| GitLab version | Import/Export version |
| -------- | -------- |
-| 8.16.2 to current | 0.1.6 |
+| 8.17.0 to current | 0.1.6 |
| 8.13.0 | 0.1.5 |
| 8.12.0 | 0.1.4 |
| 8.10.3 | 0.1.3 |
diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md
index 97380bce172..f3c636ed1d5 100644
--- a/doc/workflow/importing/import_projects_from_bitbucket.md
+++ b/doc/workflow/importing/import_projects_from_bitbucket.md
@@ -28,7 +28,7 @@ to enable this if not already.
When issues/pull requests are being imported, the Bitbucket importer tries to find
the Bitbucket author/assignee in GitLab's database using the Bitbucket ID. For this
to work, the Bitbucket author/assignee should have signed in beforehand in GitLab
-and [**associated their Bitbucket account**][social sign-in]. If the user is not
+and **associated their Bitbucket account**. If the user is not
found in GitLab's database, the project creator (most of the times the current
user that started the import process) is set as the author, but a reference on
the issue about the original Bitbucket author is kept.
diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
index 86a016fc6d6..cdacef9832f 100644
--- a/doc/workflow/importing/import_projects_from_github.md
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -28,7 +28,7 @@ still be able to import their GitHub repositories with a
When issues/pull requests are being imported, the GitHub importer tries to find
the GitHub author/assignee in GitLab's database using the GitHub ID. For this
to work, the GitHub author/assignee should have signed in beforehand in GitLab
-and [**associated their GitHub account**][social sign-in]. If the user is not
+and **associated their GitHub account**. If the user is not
found in GitLab's database, the project creator (most of the times the current
user that started the import process) is set as the author, but a reference on
the issue about the original GitHub author is kept.
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index 5f6a718135d..3a6773909d6 100644
--- a/doc/workflow/lfs/lfs_administration.md
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -43,8 +43,8 @@ In `config/gitlab.yml`:
## Storage statistics
You can see the total storage used for LFS objects on groups and projects
-in the administration area, as well as through the [groups](../api/groups.md)
-and [projects APIs](../api/projects.md).
+in the administration area, as well as through the [groups](../../api/groups.md)
+and [projects APIs](../../api/projects.md).
## Known limitations
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 1950d2791ab..eb9792680ff 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -5,9 +5,11 @@ module API
version %w(v3 v4), using: :path
version 'v3', using: :path do
+ mount ::API::V3::DeployKeys
mount ::API::V3::Issues
mount ::API::V3::MergeRequests
mount ::API::V3::Projects
+ mount ::API::V3::ProjectSnippets
end
before { allow_access_with_scope :api }
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 64da7d6b86f..3f5183d46a2 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -1,5 +1,4 @@
module API
- # Projects API
class DeployKeys < Grape::API
before { authenticate! }
@@ -16,107 +15,102 @@ module API
resource :projects do
before { authorize_admin_project }
- # Routing "projects/:id/keys/..." is DEPRECATED and WILL BE REMOVED in version 9.0
- # Use "projects/:id/deploy_keys/..." instead.
- #
- %w(keys deploy_keys).each do |path|
- desc "Get a specific project's deploy keys" do
- success Entities::SSHKey
- end
- get ":id/#{path}" do
- present user_project.deploy_keys, with: Entities::SSHKey
- end
+ desc "Get a specific project's deploy keys" do
+ success Entities::SSHKey
+ end
+ get ":id/deploy_keys" do
+ present user_project.deploy_keys, with: Entities::SSHKey
+ end
- desc 'Get single deploy key' do
- success Entities::SSHKey
- end
- params do
- requires :key_id, type: Integer, desc: 'The ID of the deploy key'
- end
- get ":id/#{path}/:key_id" do
- key = user_project.deploy_keys.find params[:key_id]
+ desc 'Get single deploy key' do
+ success Entities::SSHKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the deploy key'
+ end
+ get ":id/deploy_keys/:key_id" do
+ key = user_project.deploy_keys.find params[:key_id]
+ present key, with: Entities::SSHKey
+ end
+
+ desc 'Add new deploy key to currently authenticated user' do
+ success Entities::SSHKey
+ end
+ params do
+ requires :key, type: String, desc: 'The new deploy key'
+ requires :title, type: String, desc: 'The name of the deploy key'
+ end
+ post ":id/deploy_keys" do
+ params[:key].strip!
+
+ # Check for an existing key joined to this project
+ key = user_project.deploy_keys.find_by(key: params[:key])
+ if key
present key, with: Entities::SSHKey
+ break
end
- desc 'Add new deploy key to currently authenticated user' do
- success Entities::SSHKey
- end
- params do
- requires :key, type: String, desc: 'The new deploy key'
- requires :title, type: String, desc: 'The name of the deploy key'
+ # Check for available deploy keys in other projects
+ key = current_user.accessible_deploy_keys.find_by(key: params[:key])
+ if key
+ user_project.deploy_keys << key
+ present key, with: Entities::SSHKey
+ break
end
- post ":id/#{path}" do
- params[:key].strip!
- # Check for an existing key joined to this project
- key = user_project.deploy_keys.find_by(key: params[:key])
- if key
- present key, with: Entities::SSHKey
- break
- end
-
- # Check for available deploy keys in other projects
- key = current_user.accessible_deploy_keys.find_by(key: params[:key])
- if key
- user_project.deploy_keys << key
- present key, with: Entities::SSHKey
- break
- end
-
- # Create a new deploy key
- key = DeployKey.new(declared_params(include_missing: false))
- if key.valid? && user_project.deploy_keys << key
- present key, with: Entities::SSHKey
- else
- render_validation_error!(key)
- end
+ # Create a new deploy key
+ key = DeployKey.new(declared_params(include_missing: false))
+ if key.valid? && user_project.deploy_keys << key
+ present key, with: Entities::SSHKey
+ else
+ render_validation_error!(key)
end
+ end
- desc 'Enable a deploy key for a project' do
- detail 'This feature was added in GitLab 8.11'
- success Entities::SSHKey
- end
- params do
- requires :key_id, type: Integer, desc: 'The ID of the deploy key'
- end
- post ":id/#{path}/:key_id/enable" do
- key = ::Projects::EnableDeployKeyService.new(user_project,
- current_user, declared_params).execute
+ desc 'Enable a deploy key for a project' do
+ detail 'This feature was added in GitLab 8.11'
+ success Entities::SSHKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the deploy key'
+ end
+ post ":id/deploy_keys/:key_id/enable" do
+ key = ::Projects::EnableDeployKeyService.new(user_project,
+ current_user, declared_params).execute
- if key
- present key, with: Entities::SSHKey
- else
- not_found!('Deploy Key')
- end
+ if key
+ present key, with: Entities::SSHKey
+ else
+ not_found!('Deploy Key')
end
+ end
- desc 'Disable a deploy key for a project' do
- detail 'This feature was added in GitLab 8.11'
- success Entities::SSHKey
- end
- params do
- requires :key_id, type: Integer, desc: 'The ID of the deploy key'
- end
- delete ":id/#{path}/:key_id/disable" do
- key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
- key.destroy
+ desc 'Disable a deploy key for a project' do
+ detail 'This feature was added in GitLab 8.11'
+ success Entities::SSHKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the deploy key'
+ end
+ delete ":id/deploy_keys/:key_id/disable" do
+ key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
+ key.destroy
- present key.deploy_key, with: Entities::SSHKey
- end
+ present key.deploy_key, with: Entities::SSHKey
+ end
- desc 'Delete deploy key for a project' do
- success Key
- end
- params do
- requires :key_id, type: Integer, desc: 'The ID of the deploy key'
- end
- delete ":id/#{path}/:key_id" do
- key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
- if key
- key.destroy
- else
- not_found!('Deploy Key')
- end
+ desc 'Delete deploy key for a project' do
+ success Key
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the deploy key'
+ end
+ delete ":id/deploy_keys/:key_id" do
+ key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
+ if key
+ key.destroy
+ else
+ not_found!('Deploy Key')
end
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index b1ead48caf7..5d7b8e021bb 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -213,9 +213,6 @@ module API
expose :author, using: Entities::UserBasic
expose :updated_at, :created_at
- # TODO (rspeicher): Deprecated; remove in 9.0
- expose(:expires_at) { |snippet| nil }
-
expose :web_url do |snippet, options|
Gitlab::UrlBuilder.build(snippet)
end
diff --git a/lib/api/v3/deploy_keys.rb b/lib/api/v3/deploy_keys.rb
new file mode 100644
index 00000000000..5bbb167755c
--- /dev/null
+++ b/lib/api/v3/deploy_keys.rb
@@ -0,0 +1,122 @@
+module API
+ module V3
+ class DeployKeys < Grape::API
+ before { authenticate! }
+
+ get "deploy_keys" do
+ authenticated_as_admin!
+
+ keys = DeployKey.all
+ present keys, with: ::API::Entities::SSHKey
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of the project'
+ end
+ resource :projects do
+ before { authorize_admin_project }
+
+ %w(keys deploy_keys).each do |path|
+ desc "Get a specific project's deploy keys" do
+ success ::API::Entities::SSHKey
+ end
+ get ":id/#{path}" do
+ present user_project.deploy_keys, with: ::API::Entities::SSHKey
+ end
+
+ desc 'Get single deploy key' do
+ success ::API::Entities::SSHKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the deploy key'
+ end
+ get ":id/#{path}/:key_id" do
+ key = user_project.deploy_keys.find params[:key_id]
+ present key, with: ::API::Entities::SSHKey
+ end
+
+ desc 'Add new deploy key to currently authenticated user' do
+ success ::API::Entities::SSHKey
+ end
+ params do
+ requires :key, type: String, desc: 'The new deploy key'
+ requires :title, type: String, desc: 'The name of the deploy key'
+ end
+ post ":id/#{path}" do
+ params[:key].strip!
+
+ # Check for an existing key joined to this project
+ key = user_project.deploy_keys.find_by(key: params[:key])
+ if key
+ present key, with: ::API::Entities::SSHKey
+ break
+ end
+
+ # Check for available deploy keys in other projects
+ key = current_user.accessible_deploy_keys.find_by(key: params[:key])
+ if key
+ user_project.deploy_keys << key
+ present key, with: ::API::Entities::SSHKey
+ break
+ end
+
+ # Create a new deploy key
+ key = DeployKey.new(declared_params(include_missing: false))
+ if key.valid? && user_project.deploy_keys << key
+ present key, with: ::API::Entities::SSHKey
+ else
+ render_validation_error!(key)
+ end
+ end
+
+ desc 'Enable a deploy key for a project' do
+ detail 'This feature was added in GitLab 8.11'
+ success ::API::Entities::SSHKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the deploy key'
+ end
+ post ":id/#{path}/:key_id/enable" do
+ key = ::Projects::EnableDeployKeyService.new(user_project,
+ current_user, declared_params).execute
+
+ if key
+ present key, with: ::API::Entities::SSHKey
+ else
+ not_found!('Deploy Key')
+ end
+ end
+
+ desc 'Disable a deploy key for a project' do
+ detail 'This feature was added in GitLab 8.11'
+ success ::API::Entities::SSHKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the deploy key'
+ end
+ delete ":id/#{path}/:key_id/disable" do
+ key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
+ key.destroy
+
+ present key.deploy_key, with: ::API::Entities::SSHKey
+ end
+
+ desc 'Delete deploy key for a project' do
+ success Key
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the deploy key'
+ end
+ delete ":id/#{path}/:key_id" do
+ key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
+ if key
+ key.destroy
+ else
+ not_found!('Deploy Key')
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb
new file mode 100644
index 00000000000..3cc0dc968a8
--- /dev/null
+++ b/lib/api/v3/entities.rb
@@ -0,0 +1,16 @@
+module API
+ module V3
+ module Entities
+ class ProjectSnippet < Grape::Entity
+ expose :id, :title, :file_name
+ expose :author, using: ::API::Entities::UserBasic
+ expose :updated_at, :created_at
+ expose(:expires_at) { |snippet| nil }
+
+ expose :web_url do |snippet, options|
+ Gitlab::UrlBuilder.build(snippet)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/v3/issues.rb b/lib/api/v3/issues.rb
index be3ecc29449..081d45165e8 100644
--- a/lib/api/v3/issues.rb
+++ b/lib/api/v3/issues.rb
@@ -50,7 +50,7 @@ module API
resource :issues do
desc "Get currently authenticated user's issues" do
- success Entities::Issue
+ success ::API::Entities::Issue
end
params do
optional :state, type: String, values: %w[opened closed all], default: 'all',
@@ -60,7 +60,7 @@ module API
get do
issues = find_issues(scope: 'authored')
- present paginate(issues), with: Entities::Issue, current_user: current_user
+ present paginate(issues), with: ::API::Entities::Issue, current_user: current_user
end
end
@@ -69,7 +69,7 @@ module API
end
resource :groups do
desc 'Get a list of group issues' do
- success Entities::Issue
+ success ::API::Entities::Issue
end
params do
optional :state, type: String, values: %w[opened closed all], default: 'opened',
@@ -81,7 +81,7 @@ module API
issues = find_issues(group_id: group.id, state: params[:state] || 'opened', match_all_labels: true)
- present paginate(issues), with: Entities::Issue, current_user: current_user
+ present paginate(issues), with: ::API::Entities::Issue, current_user: current_user
end
end
@@ -93,7 +93,7 @@ module API
desc 'Get a list of project issues' do
detail 'iid filter is deprecated have been removed on V4'
- success Entities::Issue
+ success ::API::Entities::Issue
end
params do
optional :state, type: String, values: %w[opened closed all], default: 'all',
@@ -106,22 +106,22 @@ module API
issues = find_issues(project_id: project.id)
- present paginate(issues), with: Entities::Issue, current_user: current_user, project: user_project
+ present paginate(issues), with: ::API::Entities::Issue, current_user: current_user, project: user_project
end
desc 'Get a single project issue' do
- success Entities::Issue
+ success ::API::Entities::Issue
end
params do
requires :issue_id, type: Integer, desc: 'The ID of a project issue'
end
get ":id/issues/:issue_id" do
issue = find_project_issue(params[:issue_id])
- present issue, with: Entities::Issue, current_user: current_user, project: user_project
+ present issue, with: ::API::Entities::Issue, current_user: current_user, project: user_project
end
desc 'Create a new project issue' do
- success Entities::Issue
+ success ::API::Entities::Issue
end
params do
requires :title, type: String, desc: 'The title of an issue'
@@ -153,14 +153,14 @@ module API
end
if issue.valid?
- present issue, with: Entities::Issue, current_user: current_user, project: user_project
+ present issue, with: ::API::Entities::Issue, current_user: current_user, project: user_project
else
render_validation_error!(issue)
end
end
desc 'Update an existing issue' do
- success Entities::Issue
+ success ::API::Entities::Issue
end
params do
requires :issue_id, type: Integer, desc: 'The ID of a project issue'
@@ -186,14 +186,14 @@ module API
declared_params(include_missing: false)).execute(issue)
if issue.valid?
- present issue, with: Entities::Issue, current_user: current_user, project: user_project
+ present issue, with: ::API::Entities::Issue, current_user: current_user, project: user_project
else
render_validation_error!(issue)
end
end
desc 'Move an existing issue' do
- success Entities::Issue
+ success ::API::Entities::Issue
end
params do
requires :issue_id, type: Integer, desc: 'The ID of a project issue'
@@ -208,7 +208,7 @@ module API
begin
issue = ::Issues::MoveService.new(user_project, current_user).execute(issue, new_project)
- present issue, with: Entities::Issue, current_user: current_user, project: user_project
+ present issue, with: ::API::Entities::Issue, current_user: current_user, project: user_project
rescue ::Issues::MoveService::MoveError => error
render_api_error!(error.message, 400)
end
diff --git a/lib/api/v3/merge_requests.rb b/lib/api/v3/merge_requests.rb
index 1af70cf58cc..129f9d850e9 100644
--- a/lib/api/v3/merge_requests.rb
+++ b/lib/api/v3/merge_requests.rb
@@ -39,7 +39,7 @@ module API
desc 'List merge requests' do
detail 'iid filter is deprecated have been removed on V4'
- success Entities::MergeRequest
+ success ::API::Entities::MergeRequest
end
params do
optional :state, type: String, values: %w[opened closed merged all], default: 'all',
@@ -66,11 +66,11 @@ module API
end
merge_requests = merge_requests.reorder(params[:order_by] => params[:sort])
- present paginate(merge_requests), with: Entities::MergeRequest, current_user: current_user, project: user_project
+ present paginate(merge_requests), with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
end
desc 'Create a merge request' do
- success Entities::MergeRequest
+ success ::API::Entities::MergeRequest
end
params do
requires :title, type: String, desc: 'The title of the merge request'
@@ -89,7 +89,7 @@ module API
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute
if merge_request.valid?
- present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
else
handle_merge_request_errors! merge_request.errors
end
@@ -114,34 +114,34 @@ module API
if status == :deprecated
detail DEPRECATION_MESSAGE
end
- success Entities::MergeRequest
+ success ::API::Entities::MergeRequest
end
get path do
merge_request = find_merge_request_with_access(params[:merge_request_id])
- present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
end
desc 'Get the commits of a merge request' do
- success Entities::RepoCommit
+ success ::API::Entities::RepoCommit
end
get "#{path}/commits" do
merge_request = find_merge_request_with_access(params[:merge_request_id])
- present merge_request.commits, with: Entities::RepoCommit
+ present merge_request.commits, with: ::API::Entities::RepoCommit
end
desc 'Show the merge request changes' do
- success Entities::MergeRequestChanges
+ success ::API::Entities::MergeRequestChanges
end
get "#{path}/changes" do
merge_request = find_merge_request_with_access(params[:merge_request_id])
- present merge_request, with: Entities::MergeRequestChanges, current_user: current_user
+ present merge_request, with: ::API::Entities::MergeRequestChanges, current_user: current_user
end
desc 'Update a merge request' do
- success Entities::MergeRequest
+ success ::API::Entities::MergeRequest
end
params do
optional :title, type: String, allow_blank: false, desc: 'The title of the merge request'
@@ -162,14 +162,14 @@ module API
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request)
if merge_request.valid?
- present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
else
handle_merge_request_errors! merge_request.errors
end
end
desc 'Merge a merge request' do
- success Entities::MergeRequest
+ success ::API::Entities::MergeRequest
end
params do
optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
@@ -209,11 +209,11 @@ module API
.execute(merge_request)
end
- present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
+ present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
end
desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
- success Entities::MergeRequest
+ success ::API::Entities::MergeRequest
end
post "#{path}/cancel_merge_when_build_succeeds" do
merge_request = find_project_merge_request(params[:merge_request_id])
@@ -227,19 +227,19 @@ module API
desc 'Get the comments of a merge request' do
detail 'Duplicate. DEPRECATED and HAS BEEN REMOVED in V4'
- success Entities::MRNote
+ success ::API::Entities::MRNote
end
params do
use :pagination
end
get "#{path}/comments" do
merge_request = find_merge_request_with_access(params[:merge_request_id])
- present paginate(merge_request.notes.fresh), with: Entities::MRNote
+ present paginate(merge_request.notes.fresh), with: ::API::Entities::MRNote
end
desc 'Post a comment to a merge request' do
detail 'Duplicate. DEPRECATED and HAS BEEN REMOVED in V4'
- success Entities::MRNote
+ success ::API::Entities::MRNote
end
params do
requires :note, type: String, desc: 'The text of the comment'
@@ -256,14 +256,14 @@ module API
note = ::Notes::CreateService.new(user_project, current_user, opts).execute
if note.save
- present note, with: Entities::MRNote
+ present note, with: ::API::Entities::MRNote
else
render_api_error!("Failed to save note #{note.errors.messages}", 400)
end
end
desc 'List issues that will be closed on merge' do
- success Entities::MRNote
+ success ::API::Entities::MRNote
end
params do
use :pagination
diff --git a/lib/api/v3/project_snippets.rb b/lib/api/v3/project_snippets.rb
new file mode 100644
index 00000000000..9f95d4395fa
--- /dev/null
+++ b/lib/api/v3/project_snippets.rb
@@ -0,0 +1,135 @@
+module API
+ module V3
+ class ProjectSnippets < Grape::API
+ include PaginationParams
+
+ before { authenticate! }
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects do
+ helpers do
+ def handle_project_member_errors(errors)
+ if errors[:project_access].any?
+ error!(errors[:project_access], 422)
+ end
+ not_found!
+ end
+
+ def snippets_for_current_user
+ finder_params = { filter: :by_project, project: user_project }
+ SnippetsFinder.new.execute(current_user, finder_params)
+ end
+ end
+
+ desc 'Get all project snippets' do
+ success ::API::V3::Entities::ProjectSnippet
+ end
+ params do
+ use :pagination
+ end
+ get ":id/snippets" do
+ present paginate(snippets_for_current_user), with: ::API::V3::Entities::ProjectSnippet
+ end
+
+ desc 'Get a single project snippet' do
+ success ::API::V3::Entities::ProjectSnippet
+ end
+ params do
+ requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
+ end
+ get ":id/snippets/:snippet_id" do
+ snippet = snippets_for_current_user.find(params[:snippet_id])
+ present snippet, with: ::API::V3::Entities::ProjectSnippet
+ end
+
+ desc 'Create a new project snippet' do
+ success ::API::V3::Entities::ProjectSnippet
+ end
+ params do
+ requires :title, type: String, desc: 'The title of the snippet'
+ requires :file_name, type: String, desc: 'The file name of the snippet'
+ requires :code, type: String, desc: 'The content of the snippet'
+ requires :visibility_level, type: Integer,
+ values: [Gitlab::VisibilityLevel::PRIVATE,
+ Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PUBLIC],
+ desc: 'The visibility level of the snippet'
+ end
+ post ":id/snippets" do
+ authorize! :create_project_snippet, user_project
+ snippet_params = declared_params.merge(request: request, api: true)
+ snippet_params[:content] = snippet_params.delete(:code)
+
+ snippet = CreateSnippetService.new(user_project, current_user, snippet_params).execute
+
+ if snippet.persisted?
+ present snippet, with: ::API::V3::Entities::ProjectSnippet
+ else
+ render_validation_error!(snippet)
+ end
+ end
+
+ desc 'Update an existing project snippet' do
+ success ::API::V3::Entities::ProjectSnippet
+ end
+ params do
+ requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
+ optional :title, type: String, desc: 'The title of the snippet'
+ optional :file_name, type: String, desc: 'The file name of the snippet'
+ optional :code, type: String, desc: 'The content of the snippet'
+ optional :visibility_level, type: Integer,
+ values: [Gitlab::VisibilityLevel::PRIVATE,
+ Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PUBLIC],
+ desc: 'The visibility level of the snippet'
+ at_least_one_of :title, :file_name, :code, :visibility_level
+ end
+ put ":id/snippets/:snippet_id" do
+ snippet = snippets_for_current_user.find_by(id: params.delete(:snippet_id))
+ not_found!('Snippet') unless snippet
+
+ authorize! :update_project_snippet, snippet
+
+ snippet_params = declared_params(include_missing: false)
+ snippet_params[:content] = snippet_params.delete(:code) if snippet_params[:code].present?
+
+ UpdateSnippetService.new(user_project, current_user, snippet,
+ snippet_params).execute
+
+ if snippet.persisted?
+ present snippet, with: ::API::V3::Entities::ProjectSnippet
+ else
+ render_validation_error!(snippet)
+ end
+ end
+
+ desc 'Delete a project snippet'
+ params do
+ requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
+ end
+ delete ":id/snippets/:snippet_id" do
+ snippet = snippets_for_current_user.find_by(id: params[:snippet_id])
+ not_found!('Snippet') unless snippet
+
+ authorize! :admin_project_snippet, snippet
+ snippet.destroy
+ end
+
+ desc 'Get a raw project snippet'
+ params do
+ requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
+ end
+ get ":id/snippets/:snippet_id/raw" do
+ snippet = snippets_for_current_user.find_by(id: params[:snippet_id])
+ not_found!('Snippet') unless snippet
+
+ env['api.format'] = :txt
+ content_type 'text/plain'
+ present snippet.content
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/v3/projects.rb b/lib/api/v3/projects.rb
index bac7d485a22..6796da83f07 100644
--- a/lib/api/v3/projects.rb
+++ b/lib/api/v3/projects.rb
@@ -74,32 +74,32 @@ module API
def present_projects(projects, options = {})
options = options.reverse_merge(
- with: Entities::Project,
+ with: ::API::Entities::Project,
current_user: current_user,
simple: params[:simple],
)
projects = filter_projects(projects)
projects = projects.with_statistics if options[:statistics]
- options[:with] = Entities::BasicProjectDetails if options[:simple]
+ options[:with] = ::API::Entities::BasicProjectDetails if options[:simple]
present paginate(projects), options
end
end
desc 'Get a list of visible projects for authenticated user' do
- success Entities::BasicProjectDetails
+ success ::API::Entities::BasicProjectDetails
end
params do
use :collection_params
end
get '/visible' do
- entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails
+ entity = current_user ? ::API::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
present_projects ProjectsFinder.new.execute(current_user), with: entity
end
desc 'Get a projects list for authenticated user' do
- success Entities::BasicProjectDetails
+ success ::API::Entities::BasicProjectDetails
end
params do
use :collection_params
@@ -108,11 +108,11 @@ module API
authenticate!
present_projects current_user.authorized_projects,
- with: Entities::ProjectWithAccess
+ with: ::API::Entities::ProjectWithAccess
end
desc 'Get an owned projects list for authenticated user' do
- success Entities::BasicProjectDetails
+ success ::API::Entities::BasicProjectDetails
end
params do
use :collection_params
@@ -122,12 +122,12 @@ module API
authenticate!
present_projects current_user.owned_projects,
- with: Entities::ProjectWithAccess,
+ with: ::API::Entities::ProjectWithAccess,
statistics: params[:statistics]
end
desc 'Gets starred project for the authenticated user' do
- success Entities::BasicProjectDetails
+ success ::API::Entities::BasicProjectDetails
end
params do
use :collection_params
@@ -139,7 +139,7 @@ module API
end
desc 'Get all projects for admin user' do
- success Entities::BasicProjectDetails
+ success ::API::Entities::BasicProjectDetails
end
params do
use :collection_params
@@ -148,11 +148,11 @@ module API
get '/all' do
authenticated_as_admin!
- present_projects Project.all, with: Entities::ProjectWithAccess, statistics: params[:statistics]
+ present_projects Project.all, with: ::API::Entities::ProjectWithAccess, statistics: params[:statistics]
end
desc 'Search for projects the current user has access to' do
- success Entities::Project
+ success ::API::Entities::Project
end
params do
requires :query, type: String, desc: 'The project name to be searched'
@@ -164,11 +164,11 @@ module API
projects = search_service.objects('projects', params[:page])
projects = projects.reorder(params[:order_by] => params[:sort])
- present paginate(projects), with: Entities::Project
+ present paginate(projects), with: ::API::Entities::Project
end
desc 'Create new project' do
- success Entities::Project
+ success ::API::Entities::Project
end
params do
requires :name, type: String, desc: 'The name of the project'
@@ -181,7 +181,7 @@ module API
project = ::Projects::CreateService.new(current_user, attrs).execute
if project.saved?
- present project, with: Entities::Project,
+ present project, with: ::API::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, project)
else
if project.errors[:limit_reached].present?
@@ -192,7 +192,7 @@ module API
end
desc 'Create new project for a specified user. Only available to admin users.' do
- success Entities::Project
+ success ::API::Entities::Project
end
params do
requires :name, type: String, desc: 'The name of the project'
@@ -210,7 +210,7 @@ module API
project = ::Projects::CreateService.new(user, attrs).execute
if project.saved?
- present project, with: Entities::Project,
+ present project, with: ::API::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, project)
else
render_validation_error!(project)
@@ -223,26 +223,26 @@ module API
end
resource :projects, requirements: { id: /[^\/]+/ } do
desc 'Get a single project' do
- success Entities::ProjectWithAccess
+ success ::API::Entities::ProjectWithAccess
end
get ":id" do
- entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails
+ entity = current_user ? ::API::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
present user_project, with: entity, current_user: current_user,
user_can_admin_project: can?(current_user, :admin_project, user_project)
end
desc 'Get events for a single project' do
- success Entities::Event
+ success ::API::Entities::Event
end
params do
use :pagination
end
get ":id/events" do
- present paginate(user_project.events.recent), with: Entities::Event
+ present paginate(user_project.events.recent), with: ::API::Entities::Event
end
desc 'Fork new project for the current user or provided namespace.' do
- success Entities::Project
+ success ::API::Entities::Project
end
params do
optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
@@ -268,13 +268,13 @@ module API
if forked_project.errors.any?
conflict!(forked_project.errors.messages)
else
- present forked_project, with: Entities::Project,
+ present forked_project, with: ::API::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, forked_project)
end
end
desc 'Update an existing project' do
- success Entities::Project
+ success ::API::Entities::Project
end
params do
optional :name, type: String, desc: 'The name of the project'
@@ -298,7 +298,7 @@ module API
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
if result[:status] == :success
- present user_project, with: Entities::Project,
+ present user_project, with: ::API::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, user_project)
else
render_validation_error!(user_project)
@@ -306,29 +306,29 @@ module API
end
desc 'Archive a project' do
- success Entities::Project
+ success ::API::Entities::Project
end
post ':id/archive' do
authorize!(:archive_project, user_project)
user_project.archive!
- present user_project, with: Entities::Project
+ present user_project, with: ::API::Entities::Project
end
desc 'Unarchive a project' do
- success Entities::Project
+ success ::API::Entities::Project
end
post ':id/unarchive' do
authorize!(:archive_project, user_project)
user_project.unarchive!
- present user_project, with: Entities::Project
+ present user_project, with: ::API::Entities::Project
end
desc 'Star a project' do
- success Entities::Project
+ success ::API::Entities::Project
end
post ':id/star' do
if current_user.starred?(user_project)
@@ -337,19 +337,19 @@ module API
current_user.toggle_star(user_project)
user_project.reload
- present user_project, with: Entities::Project
+ present user_project, with: ::API::Entities::Project
end
end
desc 'Unstar a project' do
- success Entities::Project
+ success ::API::Entities::Project
end
delete ':id/star' do
if current_user.starred?(user_project)
current_user.toggle_star(user_project)
user_project.reload
- present user_project, with: Entities::Project
+ present user_project, with: ::API::Entities::Project
else
not_modified!
end
@@ -390,7 +390,7 @@ module API
end
desc 'Share the project with a group' do
- success Entities::ProjectGroupLink
+ success ::API::Entities::ProjectGroupLink
end
params do
requires :group_id, type: Integer, desc: 'The ID of a group'
@@ -412,7 +412,7 @@ module API
link = user_project.project_group_links.new(declared_params(include_missing: false))
if link.save
- present link, with: Entities::ProjectGroupLink
+ present link, with: ::API::Entities::ProjectGroupLink
else
render_api_error!(link.errors.full_messages.first, 409)
end
@@ -440,7 +440,7 @@ module API
end
desc 'Get the users list of a project' do
- success Entities::UserBasic
+ success ::API::Entities::UserBasic
end
params do
optional :search, type: String, desc: 'Return list of users matching the search criteria'
@@ -450,7 +450,7 @@ module API
users = user_project.team.users
users = users.search(params[:search]) if params[:search].present?
- present paginate(users), with: Entities::UserBasic
+ present paginate(users), with: ::API::Entities::UserBasic
end
end
end
diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb
index a09577ae48d..8b8e48aac76 100644
--- a/lib/gitlab/import_export/members_mapper.rb
+++ b/lib/gitlab/import_export/members_mapper.rb
@@ -32,6 +32,10 @@ module Gitlab
@user.id
end
+ def include?(old_author_id)
+ map.keys.include?(old_author_id) && map[old_author_id] != default_user_id
+ end
+
private
def missing_keys_tracking_hash
diff --git a/lib/gitlab/import_export/project_tree_saver.rb b/lib/gitlab/import_export/project_tree_saver.rb
index 2fbf437ec26..b79be62245b 100644
--- a/lib/gitlab/import_export/project_tree_saver.rb
+++ b/lib/gitlab/import_export/project_tree_saver.rb
@@ -5,8 +5,9 @@ module Gitlab
attr_reader :full_path
- def initialize(project:, shared:)
+ def initialize(project:, current_user:, shared:)
@project = project
+ @current_user = current_user
@shared = shared
@full_path = File.join(@shared.export_path, ImportExport.project_filename)
end
@@ -24,7 +25,29 @@ module Gitlab
private
def project_json_tree
- @project.to_json(Gitlab::ImportExport::Reader.new(shared: @shared).project_tree)
+ project_json['project_members'] += group_members_json
+
+ project_json.to_json
+ end
+
+ def project_json
+ @project_json ||= @project.as_json(reader.project_tree)
+ end
+
+ def reader
+ @reader ||= Gitlab::ImportExport::Reader.new(shared: @shared)
+ end
+
+ def group_members_json
+ group_members.as_json(reader.group_members_tree).each do |group_member|
+ group_member['source_type'] = 'Project' # Make group members project members of the future import
+ end
+ end
+
+ def group_members
+ return [] unless @current_user.can?(:admin_group, @project.group)
+
+ MembersFinder.new(@project.project_members, @project.group).execute(@current_user)
end
end
end
diff --git a/lib/gitlab/import_export/reader.rb b/lib/gitlab/import_export/reader.rb
index 5021a1a14ce..a1e7159fe42 100644
--- a/lib/gitlab/import_export/reader.rb
+++ b/lib/gitlab/import_export/reader.rb
@@ -21,6 +21,10 @@ module Gitlab
false
end
+ def group_members_tree
+ @attributes_finder.find_included(:project_members).merge(include: @attributes_finder.find(:user))
+ end
+
private
# Builds a hash in the format described here: http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 0319d7707a8..fae792237d9 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -89,7 +89,7 @@ module Gitlab
end
def has_author?(old_author_id)
- admin_user? && @members_mapper.map.keys.include?(old_author_id)
+ admin_user? && @members_mapper.include?(old_author_id)
end
def missing_author_note(updated_at, author_name)
diff --git a/lib/gitlab/recaptcha.rb b/lib/gitlab/recaptcha.rb
index 70e7f25d518..4bc76ea033f 100644
--- a/lib/gitlab/recaptcha.rb
+++ b/lib/gitlab/recaptcha.rb
@@ -10,5 +10,9 @@ module Gitlab
true
end
end
+
+ def self.enabled?
+ current_application_settings.recaptcha_enabled
+ end
end
end
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index 97414ead3dd..5fd7f0f98bd 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -107,7 +107,7 @@ check_pids(){
wait_for_pids(){
# We are sleeping a bit here mostly because sidekiq is slow at writing its pid
i=0;
- while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ] || { [ "$gitlab_pages_enabled" = true ] && [ ! -f $gitlab_pages_pid_path ]; }; do
+ while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; } || { [ "$gitlab_pages_enabled" = true ] && [ ! -f $gitlab_pages_pid_path ]; }; do
sleep 0.1;
i=$((i+1))
if [ $((i%10)) = 0 ]; then
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index ffab6f492fb..1650263b98d 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -57,7 +57,7 @@ namespace :gitlab do
Rake::Task['gitlab:backup:uploads:restore'].invoke unless backup.skipped?('uploads')
Rake::Task['gitlab:backup:builds:restore'].invoke unless backup.skipped?('builds')
Rake::Task['gitlab:backup:artifacts:restore'].invoke unless backup.skipped?('artifacts')
- Rake::Task["gitlab:backup:pages:restore"].invoke unless backup.skipped?("pages")
+ Rake::Task["gitlab:backup:pages:restore"].invoke unless backup.skipped?('pages')
Rake::Task['gitlab:backup:lfs:restore'].invoke unless backup.skipped?('lfs')
Rake::Task['gitlab:backup:registry:restore'].invoke unless backup.skipped?('registry')
Rake::Task['gitlab:shell:setup'].invoke
@@ -163,20 +163,20 @@ namespace :gitlab do
namespace :pages do
task create: :environment do
- $progress.puts "Dumping pages ... ".blue
+ $progress.puts "Dumping pages ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("pages")
- $progress.puts "[SKIPPED]".cyan
+ $progress.puts "[SKIPPED]".color(:cyan)
else
Backup::Pages.new.dump
- $progress.puts "done".green
+ $progress.puts "done".color(:green)
end
end
task restore: :environment do
- $progress.puts "Restoring pages ... ".blue
+ $progress.puts "Restoring pages ... ".color(:blue)
Backup::Pages.new.restore
- $progress.puts "done".green
+ $progress.puts "done".color(:green)
end
end
diff --git a/lib/tasks/grape.rake b/lib/tasks/grape.rake
index 9980e0b7984..ea2698da606 100644
--- a/lib/tasks/grape.rake
+++ b/lib/tasks/grape.rake
@@ -2,7 +2,11 @@ namespace :grape do
desc 'Print compiled grape routes'
task routes: :environment do
API::API.routes.each do |route|
- puts route
+ puts "#{route.options[:method]} #{route.path} - #{route_description(route.options)}"
end
end
+
+ def route_description(options)
+ options[:settings][:description][:description] if options[:settings][:description]
+ end
end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 5f27f336f72..4b89381eb96 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -326,7 +326,7 @@ describe Projects::IssuesController do
end
describe 'POST #create' do
- def post_new_issue(attrs = {})
+ def post_new_issue(issue_attrs = {}, additional_params = {})
sign_in(user)
project = create(:empty_project, :public)
project.team << [user, :developer]
@@ -334,8 +334,8 @@ describe Projects::IssuesController do
post :create, {
namespace_id: project.namespace.to_param,
project_id: project.to_param,
- issue: { title: 'Title', description: 'Description' }.merge(attrs)
- }
+ issue: { title: 'Title', description: 'Description' }.merge(issue_attrs)
+ }.merge(additional_params)
project.issues.first
end
@@ -378,24 +378,81 @@ describe Projects::IssuesController do
context 'Akismet is enabled' do
before do
+ stub_application_setting(recaptcha_enabled: true)
allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true)
- allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
end
- def post_spam_issue
- post_new_issue(title: 'Spam Title', description: 'Spam lives here')
- end
+ context 'when an issue is not identified as a spam' do
+ before do
+ allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false)
+ allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(false)
+ end
- it 'rejects an issue recognized as spam' do
- expect{ post_spam_issue }.not_to change(Issue, :count)
- expect(response).to render_template(:new)
+ it 'does not create an issue' do
+ expect { post_new_issue(title: '') }.not_to change(Issue, :count)
+ end
end
- it 'creates a spam log' do
- post_spam_issue
- spam_logs = SpamLog.all
- expect(spam_logs.count).to eq(1)
- expect(spam_logs[0].title).to eq('Spam Title')
+ context 'when an issue is identified as a spam' do
+ before { allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) }
+
+ context 'when captcha is not verified' do
+ def post_spam_issue
+ post_new_issue(title: 'Spam Title', description: 'Spam lives here')
+ end
+
+ before { allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) }
+
+ it 'rejects an issue recognized as a spam' do
+ expect { post_spam_issue }.not_to change(Issue, :count)
+ end
+
+ it 'creates a spam log' do
+ post_spam_issue
+ spam_logs = SpamLog.all
+
+ expect(spam_logs.count).to eq(1)
+ expect(spam_logs.first.title).to eq('Spam Title')
+ expect(spam_logs.first.recaptcha_verified).to be_falsey
+ end
+
+ it 'does not create an issue when it is not valid' do
+ expect { post_new_issue(title: '') }.not_to change(Issue, :count)
+ end
+
+ it 'does not create an issue when recaptcha is not enabled' do
+ stub_application_setting(recaptcha_enabled: false)
+
+ expect { post_spam_issue }.not_to change(Issue, :count)
+ end
+ end
+
+ context 'when captcha is verified' do
+ let!(:spam_logs) { create_list(:spam_log, 2, user: user, title: 'Title') }
+
+ def post_verified_issue
+ post_new_issue({}, { spam_log_id: spam_logs.last.id, recaptcha_verification: true } )
+ end
+
+ before do
+ allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(true)
+ end
+
+ it 'accepts an issue after recaptcha is verified' do
+ expect { post_verified_issue }.to change(Issue, :count)
+ end
+
+ it 'marks spam log as recaptcha_verified' do
+ expect { post_verified_issue }.to change { SpamLog.last.recaptcha_verified }.from(false).to(true)
+ end
+
+ it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do
+ spam_log = create(:spam_log)
+
+ expect { post_new_issue({}, { spam_log_id: spam_log.id, recaptcha_verification: true } ) }.
+ not_to change { SpamLog.last.recaptcha_verified }
+ end
+ end
end
end
@@ -405,7 +462,7 @@ describe Projects::IssuesController do
end
it 'creates a user agent detail' do
- expect{ post_new_issue }.to change(UserAgentDetail, :count).by(1)
+ expect { post_new_issue }.to change(UserAgentDetail, :count).by(1)
end
end
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
new file mode 100644
index 00000000000..e9a91cff1b3
--- /dev/null
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -0,0 +1,20 @@
+require('spec_helper')
+
+describe Projects::Settings::CiCdController do
+ let(:project) { create(:empty_project, :public, :access_requestable) }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ sign_in(user)
+ end
+
+ describe 'GET show' do
+ it 'renders show with 200 status code' do
+ get :show, namespace_id: project.namespace, project_id: project
+
+ expect(response).to have_http_status(200)
+ expect(response).to render_template(:show)
+ end
+ end
+end
diff --git a/spec/controllers/projects/variables_controller_spec.rb b/spec/controllers/projects/variables_controller_spec.rb
new file mode 100644
index 00000000000..9fa358f7d62
--- /dev/null
+++ b/spec/controllers/projects/variables_controller_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe Projects::VariablesController do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ project.team << [user, :master]
+ end
+
+ describe 'POST #create' do
+ context 'variable is valid' do
+ it 'shows a success flash message' do
+ post :create, namespace_id: project.namespace.to_param, project_id: project.to_param,
+ variable: { key: "one", value: "two" }
+
+ expect(flash[:notice]).to include 'Variables were successfully updated.'
+ expect(response).to redirect_to(namespace_project_settings_ci_cd_path(project.namespace, project))
+ end
+ end
+
+ context 'variable is invalid' do
+ it 'shows an alert flash message' do
+ post :create, namespace_id: project.namespace.to_param, project_id: project.to_param,
+ variable: { key: "..one", value: "two" }
+
+ expect(response).to render_template("projects/variables/show")
+ end
+ end
+ end
+
+ describe 'POST #update' do
+ let(:variable) { create(:ci_variable) }
+
+ context 'updating a variable with valid characters' do
+ before do
+ variable.gl_project_id = project.id
+ project.variables << variable
+ end
+
+ it 'shows a success flash message' do
+ post :update, namespace_id: project.namespace.to_param, project_id: project.to_param,
+ id: variable.id, variable: { key: variable.key, value: 'two' }
+
+ expect(flash[:notice]).to include 'Variable was successfully updated.'
+ expect(response).to redirect_to(namespace_project_variables_path(project.namespace, project))
+ end
+
+ it 'renders the action #show if the variable key is invalid' do
+ post :update, namespace_id: project.namespace.to_param, project_id: project.to_param,
+ id: variable.id, variable: { key: '?', value: variable.value }
+
+ expect(response).to have_http_status(200)
+ expect(response).to render_template :show
+ end
+ end
+ end
+end
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 42fbfe89368..8cc216445eb 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -44,7 +44,7 @@ describe RegistrationsController do
post(:create, user_params)
expect(response).to render_template(:new)
- expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please re-solve the reCAPTCHA.'
+ expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
end
it 'redirects to the dashboard when the recaptcha is solved' do
diff --git a/spec/features/environment_spec.rb b/spec/features/environment_spec.rb
index 511c95b758f..2f49e89b4e4 100644
--- a/spec/features/environment_spec.rb
+++ b/spec/features/environment_spec.rb
@@ -64,10 +64,6 @@ feature 'Environment', :feature do
expect(page).to have_link('Re-deploy')
end
- scenario 'does not show stop button' do
- expect(page).not_to have_link('Stop')
- end
-
scenario 'does not show terminal button' do
expect(page).not_to have_terminal_button
end
@@ -116,27 +112,43 @@ feature 'Environment', :feature do
end
end
- context 'with stop action' do
- given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
- given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
+ context 'when environment is available' do
+ context 'with stop action' do
+ given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
+ given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
- scenario 'does show stop button' do
- expect(page).to have_link('Stop')
- end
+ scenario 'does show stop button' do
+ expect(page).to have_link('Stop')
+ end
- scenario 'does allow to stop environment' do
- click_link('Stop')
+ scenario 'does allow to stop environment' do
+ click_link('Stop')
- expect(page).to have_content('close_app')
- end
+ expect(page).to have_content('close_app')
+ end
- context 'for reporter' do
- let(:role) { :reporter }
+ context 'for reporter' do
+ let(:role) { :reporter }
- scenario 'does not show stop button' do
- expect(page).not_to have_link('Stop')
+ scenario 'does not show stop button' do
+ expect(page).not_to have_link('Stop')
+ end
end
end
+
+ context 'without stop action' do
+ scenario 'does allow to stop environment' do
+ click_link('Stop')
+ end
+ end
+ end
+
+ context 'when environment is stopped' do
+ given(:environment) { create(:environment, project: project, state: :stopped) }
+
+ scenario 'does not show stop button' do
+ expect(page).not_to have_link('Stop')
+ end
end
end
end
diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb
index c033b693213..78be7d36f47 100644
--- a/spec/features/environments_spec.rb
+++ b/spec/features/environments_spec.rb
@@ -52,6 +52,22 @@ feature 'Environments page', :feature, :js do
scenario 'does show no deployments' do
expect(page).to have_content('No deployments yet')
end
+
+ context 'for available environment' do
+ given(:environment) { create(:environment, project: project, state: :available) }
+
+ scenario 'does not shows stop button' do
+ expect(page).not_to have_selector('.stop-env-link')
+ end
+ end
+
+ context 'for stopped environment' do
+ given(:environment) { create(:environment, project: project, state: :stopped) }
+
+ scenario 'does not shows stop button' do
+ expect(page).not_to have_selector('.stop-env-link')
+ end
+ end
end
context 'with deployments' do
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index 5079eb8dd00..c6a88e1b7b0 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -1,39 +1,47 @@
-require 'rails_helper'
+require 'spec_helper'
describe 'Dropdown label', js: true, feature: true do
- include WaitForAjax
-
- let!(:project) { create(:empty_project) }
- let!(:user) { create(:user) }
- let!(:bug_label) { create(:label, project: project, title: 'bug') }
- let!(:uppercase_label) { create(:label, project: project, title: 'BUG') }
- let!(:two_words_label) { create(:label, project: project, title: 'High Priority') }
- let!(:wont_fix_label) { create(:label, project: project, title: 'Won"t Fix') }
- let!(:wont_fix_single_label) { create(:label, project: project, title: 'Won\'t Fix') }
- let!(:special_label) { create(:label, project: project, title: '!@#$%^+&*()')}
- let!(:long_label) { create(:label, project: project, title: 'this is a very long title this is a very long title this is a very long title this is a very long title this is a very long title')}
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
let(:filtered_search) { find('.filtered-search') }
let(:js_dropdown_label) { '#js-dropdown-label' }
+ let(:filter_dropdown) { find("#{js_dropdown_label} .filter-dropdown") }
+
+ shared_context 'with labels' do
+ let!(:bug_label) { create(:label, project: project, title: 'bug-label') }
+ let!(:uppercase_label) { create(:label, project: project, title: 'BUG-LABEL') }
+ let!(:two_words_label) { create(:label, project: project, title: 'High Priority') }
+ let!(:wont_fix_label) { create(:label, project: project, title: 'Won"t Fix') }
+ let!(:wont_fix_single_label) { create(:label, project: project, title: 'Won\'t Fix') }
+ let!(:special_label) { create(:label, project: project, title: '!@#$%^+&*()') }
+ let!(:long_label) { create(:label, project: project, title: 'this is a very long title this is a very long title this is a very long title this is a very long title this is a very long title') }
+ end
- def send_keys_to_filtered_search(input)
- input.split("").each do |i|
- filtered_search.send_keys(i)
- sleep 3
- wait_for_ajax
- sleep 3
- end
+ def init_label_search
+ filtered_search.set('label:')
+ # This ensures the dropdown is shown
+ expect(find(js_dropdown_label)).not_to have_css('.filter-dropdown-loading')
end
- def dropdown_label_size
- page.all('#js-dropdown-label .filter-dropdown .filter-dropdown-item').size
+ def search_for_label(label)
+ init_label_search
+ filtered_search.send_keys(label)
end
def click_label(text)
- find('#js-dropdown-label .filter-dropdown .filter-dropdown-item', text: text).click
+ filter_dropdown.find('.filter-dropdown-item', text: text).click
+ end
+
+ def dropdown_label_size
+ filter_dropdown.all('.filter-dropdown-item').size
+ end
+
+ def clear_search_field
+ find('.filtered-search-input-container .clear-search').click
end
before do
- project.team << [user, :master]
+ project.add_master(user)
login_as(user)
create(:issue, project: project)
@@ -42,11 +50,12 @@ describe 'Dropdown label', js: true, feature: true do
describe 'keyboard navigation' do
it 'selects label' do
- send_keys_to_filtered_search('label:')
+ bug_label = create(:label, project: project, title: 'bug-label')
+ init_label_search
filtered_search.native.send_keys(:down, :down, :enter)
- expect(filtered_search.value).to eq("label:~#{special_label.name} ")
+ expect(filtered_search.value).to eq("label:~#{bug_label.title} ")
end
end
@@ -54,171 +63,177 @@ describe 'Dropdown label', js: true, feature: true do
it 'opens when the search bar has label:' do
filtered_search.set('label:')
- expect(page).to have_css(js_dropdown_label, visible: true)
+ expect(page).to have_css(js_dropdown_label)
end
it 'closes when the search bar is unfocused' do
- find('body').click()
+ find('body').click
- expect(page).to have_css(js_dropdown_label, visible: false)
+ expect(page).not_to have_css(js_dropdown_label)
end
- it 'should show loading indicator when opened' do
+ it 'shows loading indicator when opened and hides it when loaded' do
filtered_search.set('label:')
- expect(page).to have_css('#js-dropdown-label .filter-dropdown-loading', visible: true)
- end
-
- it 'should hide loading indicator when loaded' do
- send_keys_to_filtered_search('label:')
-
- expect(page).not_to have_css('#js-dropdown-label .filter-dropdown-loading')
+ expect(find(js_dropdown_label)).to have_css('.filter-dropdown-loading')
+ expect(find(js_dropdown_label)).not_to have_css('.filter-dropdown-loading')
end
- it 'should load all the labels when opened' do
- send_keys_to_filtered_search('label:')
+ it 'loads all the labels when opened' do
+ bug_label = create(:label, project: project, title: 'bug-label')
+ filtered_search.set('label:')
- expect(dropdown_label_size).to be > 0
+ expect(filter_dropdown).to have_content(bug_label.title)
+ expect(dropdown_label_size).to eq(1)
end
end
describe 'filtering' do
- before do
- filtered_search.set('label')
- end
-
- it 'filters by name' do
- send_keys_to_filtered_search(':b')
+ include_context 'with labels'
- expect(dropdown_label_size).to eq(2)
+ before do
+ init_label_search
end
- it 'filters by case insensitive name' do
- send_keys_to_filtered_search(':B')
+ it 'filters by case-insensitive name with or without symbol' do
+ search_for_label('b')
+ expect(filter_dropdown.find('.filter-dropdown-item', text: bug_label.title)).to be_visible
+ expect(filter_dropdown.find('.filter-dropdown-item', text: uppercase_label.title)).to be_visible
expect(dropdown_label_size).to eq(2)
- end
- it 'filters by name with symbol' do
- send_keys_to_filtered_search(':~bu')
+ clear_search_field
+ init_label_search
- expect(dropdown_label_size).to eq(2)
- end
-
- it 'filters by case insensitive name with symbol' do
- send_keys_to_filtered_search(':~BU')
+ search_for_label('~bu')
+ expect(filter_dropdown.find('.filter-dropdown-item', text: bug_label.title)).to be_visible
+ expect(filter_dropdown.find('.filter-dropdown-item', text: uppercase_label.title)).to be_visible
expect(dropdown_label_size).to eq(2)
end
- it 'filters by multiple words' do
- send_keys_to_filtered_search(':Hig')
+ it 'filters by multiple words with or without symbol' do
+ filtered_search.send_keys('Hig')
+ expect(filter_dropdown.find('.filter-dropdown-item', text: two_words_label.title)).to be_visible
expect(dropdown_label_size).to eq(1)
- end
- it 'filters by multiple words with symbol' do
- send_keys_to_filtered_search(':~Hig')
+ clear_search_field
+ init_label_search
+
+ filtered_search.send_keys('~Hig')
+ expect(filter_dropdown.find('.filter-dropdown-item', text: two_words_label.title)).to be_visible
expect(dropdown_label_size).to eq(1)
end
- it 'filters by multiple words containing single quotes' do
- send_keys_to_filtered_search(':won\'t')
+ it 'filters by multiple words containing single quotes with or without symbol' do
+ filtered_search.send_keys('won\'t')
+ expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_single_label.title)).to be_visible
expect(dropdown_label_size).to eq(1)
- end
- it 'filters by multiple words containing single quotes with symbol' do
- send_keys_to_filtered_search(':~won\'t')
+ clear_search_field
+ init_label_search
+
+ filtered_search.send_keys('~won\'t')
+ expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_single_label.title)).to be_visible
expect(dropdown_label_size).to eq(1)
end
- it 'filters by multiple words containing double quotes' do
- send_keys_to_filtered_search(':won"t')
+ it 'filters by multiple words containing double quotes with or without symbol' do
+ filtered_search.send_keys('won"t')
+ expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_label.title)).to be_visible
expect(dropdown_label_size).to eq(1)
- end
- it 'filters by multiple words containing double quotes with symbol' do
- send_keys_to_filtered_search(':~won"t')
+ clear_search_field
+ init_label_search
+ filtered_search.send_keys('~won"t')
+
+ expect(filter_dropdown.find('.filter-dropdown-item', text: wont_fix_label.title)).to be_visible
expect(dropdown_label_size).to eq(1)
end
- it 'filters by special characters' do
- send_keys_to_filtered_search(':^+')
+ it 'filters by special characters with or without symbol' do
+ filtered_search.send_keys('^+')
+ expect(filter_dropdown.find('.filter-dropdown-item', text: special_label.title)).to be_visible
expect(dropdown_label_size).to eq(1)
- end
- it 'filters by special characters with symbol' do
- send_keys_to_filtered_search(':~^+')
+ clear_search_field
+ init_label_search
+ filtered_search.send_keys('~^+')
+
+ expect(filter_dropdown.find('.filter-dropdown-item', text: special_label.title)).to be_visible
expect(dropdown_label_size).to eq(1)
end
end
describe 'selecting from dropdown' do
+ include_context 'with labels'
+
before do
- filtered_search.set('label:')
+ init_label_search
end
it 'fills in the label name when the label has not been filled' do
click_label(bug_label.title)
- expect(page).to have_css(js_dropdown_label, visible: false)
+ expect(page).not_to have_css(js_dropdown_label)
expect(filtered_search.value).to eq("label:~#{bug_label.title} ")
end
it 'fills in the label name when the label is partially filled' do
- send_keys_to_filtered_search('bu')
+ filtered_search.send_keys('bu')
click_label(bug_label.title)
- expect(page).to have_css(js_dropdown_label, visible: false)
+ expect(page).not_to have_css(js_dropdown_label)
expect(filtered_search.value).to eq("label:~#{bug_label.title} ")
end
it 'fills in the label name that contains multiple words' do
click_label(two_words_label.title)
- expect(page).to have_css(js_dropdown_label, visible: false)
+ expect(page).not_to have_css(js_dropdown_label)
expect(filtered_search.value).to eq("label:~\"#{two_words_label.title}\" ")
end
it 'fills in the label name that contains multiple words and is very long' do
click_label(long_label.title)
- expect(page).to have_css(js_dropdown_label, visible: false)
+ expect(page).not_to have_css(js_dropdown_label)
expect(filtered_search.value).to eq("label:~\"#{long_label.title}\" ")
end
it 'fills in the label name that contains double quotes' do
click_label(wont_fix_label.title)
- expect(page).to have_css(js_dropdown_label, visible: false)
+ expect(page).not_to have_css(js_dropdown_label)
expect(filtered_search.value).to eq("label:~'#{wont_fix_label.title}' ")
end
it 'fills in the label name with the correct capitalization' do
click_label(uppercase_label.title)
- expect(page).to have_css(js_dropdown_label, visible: false)
+ expect(page).not_to have_css(js_dropdown_label)
expect(filtered_search.value).to eq("label:~#{uppercase_label.title} ")
end
it 'fills in the label name with special characters' do
click_label(special_label.title)
- expect(page).to have_css(js_dropdown_label, visible: false)
+ expect(page).not_to have_css(js_dropdown_label)
expect(filtered_search.value).to eq("label:~#{special_label.title} ")
end
it 'selects `no label`' do
- find('#js-dropdown-label .filter-dropdown-item', text: 'No Label').click
+ find("#{js_dropdown_label} .filter-dropdown-item", text: 'No Label').click
- expect(page).to have_css(js_dropdown_label, visible: false)
+ expect(page).not_to have_css(js_dropdown_label)
expect(filtered_search.value).to eq("label:none ")
end
end
@@ -226,44 +241,47 @@ describe 'Dropdown label', js: true, feature: true do
describe 'input has existing content' do
it 'opens label dropdown with existing search term' do
filtered_search.set('searchTerm label:')
- expect(page).to have_css(js_dropdown_label, visible: true)
+
+ expect(page).to have_css(js_dropdown_label)
end
it 'opens label dropdown with existing author' do
filtered_search.set('author:@person label:')
- expect(page).to have_css(js_dropdown_label, visible: true)
+
+ expect(page).to have_css(js_dropdown_label)
end
it 'opens label dropdown with existing assignee' do
filtered_search.set('assignee:@person label:')
- expect(page).to have_css(js_dropdown_label, visible: true)
+
+ expect(page).to have_css(js_dropdown_label)
end
it 'opens label dropdown with existing label' do
filtered_search.set('label:~urgent label:')
- expect(page).to have_css(js_dropdown_label, visible: true)
+
+ expect(page).to have_css(js_dropdown_label)
end
it 'opens label dropdown with existing milestone' do
filtered_search.set('milestone:%v2.0 label:')
- expect(page).to have_css(js_dropdown_label, visible: true)
+
+ expect(page).to have_css(js_dropdown_label)
end
end
describe 'caching requests' do
it 'caches requests after the first load' do
- filtered_search.set('label')
- send_keys_to_filtered_search(':')
- initial_size = dropdown_label_size
+ create(:label, project: project, title: 'bug-label')
+ init_label_search
- expect(initial_size).to be > 0
+ expect(dropdown_label_size).to eq(1)
create(:label, project: project)
- find('.filtered-search-input-container .clear-search').click
- filtered_search.set('label')
- send_keys_to_filtered_search(':')
+ clear_search_field
+ init_label_search
- expect(dropdown_label_size).to eq(initial_size)
+ expect(dropdown_label_size).to eq(1)
end
end
end
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
new file mode 100644
index 00000000000..4bc9b49f889
--- /dev/null
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -0,0 +1,66 @@
+require 'rails_helper'
+
+describe 'New issue', feature: true do
+ include StubENV
+
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user)}
+
+ before do
+ stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
+
+ current_application_settings.update!(
+ akismet_enabled: true,
+ akismet_api_key: 'testkey',
+ recaptcha_enabled: true,
+ recaptcha_site_key: 'test site key',
+ recaptcha_private_key: 'test private key'
+ )
+
+ project.team << [user, :master]
+ login_as(user)
+ end
+
+ context 'when identified as a spam' do
+ before do
+ WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: "true", status: 200)
+
+ visit new_namespace_project_issue_path(project.namespace, project)
+ end
+
+ it 'creates an issue after solving reCaptcha' do
+ fill_in 'issue_title', with: 'issue title'
+ fill_in 'issue_description', with: 'issue description'
+
+ click_button 'Submit issue'
+
+ # it is impossible to test recaptcha automatically and there is no possibility to fill in recaptcha
+ # recaptcha verification is skipped in test environment and it always returns true
+ expect(page).not_to have_content('issue title')
+ expect(page).to have_css('.recaptcha')
+
+ click_button 'Submit issue'
+
+ expect(page.find('.issue-details h2.title')).to have_content('issue title')
+ expect(page.find('.issue-details .description')).to have_content('issue description')
+ end
+ end
+
+ context 'when not identified as a spam' do
+ before do
+ WebMock.stub_request(:any, /.*akismet.com.*/).to_return(body: 'false', status: 200)
+
+ visit new_namespace_project_issue_path(project.namespace, project)
+ end
+
+ it 'creates an issue' do
+ fill_in 'issue_title', with: 'issue title'
+ fill_in 'issue_description', with: 'issue description'
+
+ click_button 'Submit issue'
+
+ expect(page.find('.issue-details h2.title')).to have_content('issue title')
+ expect(page.find('.issue-details .description')).to have_content('issue description')
+ end
+ end
+end
diff --git a/spec/features/projects/blobs/shortcuts_blob_spec.rb b/spec/features/projects/blobs/shortcuts_blob_spec.rb
new file mode 100644
index 00000000000..30e2d587267
--- /dev/null
+++ b/spec/features/projects/blobs/shortcuts_blob_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+feature 'Blob shortcuts', feature: true do
+ include TreeHelper
+ let(:project) { create(:project, :public, :repository) }
+ let(:path) { project.repository.ls_files(project.repository.root_ref)[0] }
+ let(:sha) { project.repository.commit.sha }
+
+ describe 'On a file(blob)', js: true do
+ def get_absolute_url(path = "")
+ "http://#{page.server.host}:#{page.server.port}#{path}"
+ end
+
+ def visit_blob(fragment = nil)
+ visit namespace_project_blob_path(project.namespace, project, tree_join('master', path), anchor: fragment)
+ end
+
+ describe 'pressing "y"' do
+ it 'redirects to permalink with commit sha' do
+ visit_blob
+
+ find('body').native.send_key('y')
+
+ expect(page).to have_current_path(get_absolute_url(namespace_project_blob_path(project.namespace, project, tree_join(sha, path))), url: true)
+ end
+
+ it 'maintains fragment hash when redirecting' do
+ fragment = "L1"
+ visit_blob(fragment)
+
+ find('body').native.send_key('y')
+
+ expect(page).to have_current_path(get_absolute_url(namespace_project_blob_path(project.namespace, project, tree_join(sha, path), anchor: fragment)), url: true)
+ end
+ end
+ end
+end
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 92d5a2fbc48..24af062d763 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -96,6 +96,20 @@ describe "Internal Project Access", feature: true do
it { is_expected.to be_denied_for(:external) }
end
+ describe "GET /:project_path/settings/ci_cd" do
+ subject { namespace_project_settings_ci_cd_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:visitor) }
+ it { is_expected.to be_denied_for(:external) }
+ end
+
describe "GET /:project_path/blob" do
let(:commit) { project.repository.commit }
subject { namespace_project_blob_path(project.namespace, project, File.join(commit.id, '.gitignore')) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index b616e488487..c511dcfa18e 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -92,8 +92,22 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_allowed_for(:reporter).of(project) }
it { is_expected.to be_allowed_for(:guest).of(project) }
it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:visitor) }
it { is_expected.to be_denied_for(:external) }
+ end
+
+ describe "GET /:project_path/settings/ci_cd" do
+ subject { namespace_project_settings_ci_cd_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
it { is_expected.to be_denied_for(:visitor) }
+ it { is_expected.to be_denied_for(:external) }
end
describe "GET /:project_path/blob" do
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index ded85e837f4..d8cc012c27e 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -96,6 +96,20 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_allowed_for(:external) }
end
+ describe "GET /:project_path/settings/ci_cd" do
+ subject { namespace_project_settings_ci_cd_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_denied_for(:developer).of(project) }
+ it { is_expected.to be_denied_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:visitor) }
+ it { is_expected.to be_denied_for(:external) }
+ end
+
describe "GET /:project_path/pipelines" do
subject { namespace_project_pipelines_path(project.namespace, project) }
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index 72354834c5a..4a7511589d6 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -7,7 +7,7 @@ describe 'Triggers' do
before do
@project = FactoryGirl.create :empty_project
@project.team << [user, :master]
- visit namespace_project_triggers_path(@project.namespace, @project)
+ visit namespace_project_settings_ci_cd_path(@project.namespace, @project)
end
context 'create a trigger' do
diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb
index ff30ffd7820..9a4bc027004 100644
--- a/spec/features/variables_spec.rb
+++ b/spec/features/variables_spec.rb
@@ -10,7 +10,7 @@ describe 'Project variables', js: true do
project.team << [user, :master]
project.variables << variable
- visit namespace_project_variables_path(project.namespace, project)
+ visit namespace_project_settings_ci_cd_path(project.namespace, project)
end
it 'shows list of variables' do
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index 550b4a66a6a..8d268dbcf36 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -63,9 +63,11 @@ describe MergeRequestsHelper do
end
end
- describe 'mr_widget_refresh_url' do
- let(:project) { create(:empty_project) }
- let(:merge_request) { create(:merge_request, source_project: project) }
+ describe '#mr_widget_refresh_url' do
+ let(:guest) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:project_fork) { Projects::ForkService.new(project, guest).execute }
+ let(:merge_request) { create(:merge_request, source_project: project_fork, target_project: project) }
it 'returns correct url for MR' do
expected_url = "#{project.path_with_namespace}/merge_requests/#{merge_request.iid}/merge_widget_refresh"
@@ -74,7 +76,7 @@ describe MergeRequestsHelper do
end
it 'returns empty string for nil' do
- expect(mr_widget_refresh_url(nil)).to end_with('')
+ expect(mr_widget_refresh_url(nil)).to eq('')
end
end
end
diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6
index 9858f346c83..d87cc0996c9 100644
--- a/spec/javascripts/environments/environment_item_spec.js.es6
+++ b/spec/javascripts/environments/environment_item_spec.js.es6
@@ -119,7 +119,7 @@ describe('Environment item', () => {
},
],
},
- 'stoppable?': true,
+ 'stop_action?': true,
environment_path: 'root/ci-folders/environments/31',
created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-10T15:55:58.778Z',
diff --git a/spec/javascripts/environments/mock_data.js.es6 b/spec/javascripts/environments/mock_data.js.es6
index 58f6fb96afb..80e1cbc6f4d 100644
--- a/spec/javascripts/environments/mock_data.js.es6
+++ b/spec/javascripts/environments/mock_data.js.es6
@@ -50,7 +50,7 @@ const environmentsList = [
},
manual_actions: [],
},
- 'stoppable?': true,
+ 'stop_action?': true,
environment_path: '/root/ci-folders/environments/31',
created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-07T11:11:16.525Z',
@@ -105,7 +105,7 @@ const environmentsList = [
},
manual_actions: [],
},
- 'stoppable?': false,
+ 'stop_action?': false,
environment_path: '/root/ci-folders/environments/31',
created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-07T11:11:16.525Z',
@@ -116,7 +116,7 @@ const environmentsList = [
state: 'available',
environment_type: 'review',
last_deployment: null,
- 'stoppable?': true,
+ 'stop_action?': true,
environment_path: '/root/ci-folders/environments/31',
created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-07T11:11:16.525Z',
@@ -127,7 +127,7 @@ const environmentsList = [
state: 'available',
environment_type: 'review',
last_deployment: null,
- 'stoppable?': true,
+ 'stop_action?': true,
environment_path: '/root/ci-folders/environments/31',
created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-07T11:11:16.525Z',
@@ -143,7 +143,7 @@ const environment = {
external_url: 'http://production.',
environment_type: null,
last_deployment: {},
- 'stoppable?': false,
+ 'stop_action?': false,
environment_path: '/root/review-app/environments/4',
stop_path: '/root/review-app/environments/4/stop',
created_at: '2016-12-16T11:51:04.690Z',
diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb
index f2cb028206f..b9d4e59e770 100644
--- a/spec/lib/gitlab/import_export/members_mapper_spec.rb
+++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb
@@ -116,5 +116,27 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
end
+
+ context 'importing group members' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, namespace: group) }
+ let(:members_mapper) do
+ described_class.new(
+ exported_members: exported_members, user: user, project: project)
+ end
+
+ before do
+ group.add_users([user, user2], GroupMember::DEVELOPER)
+ user.update(email: 'invite@test.com')
+ end
+
+ it 'maps the importer' do
+ expect(members_mapper.map[-1]).to eq(user.id)
+ end
+
+ it 'maps the group member' do
+ expect(members_mapper.map[exported_user_id]).to eq(user2.id)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 550daa44010..3628adefc0c 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
describe 'saves the project tree into a json object' do
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
- let(:project_tree_saver) { described_class.new(project: project, shared: shared) }
+ let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared) }
let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
let(:user) { create(:user) }
let(:project) { setup_project }
@@ -92,7 +92,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
end
it 'has pipeline builds' do
- expect(saved_project_json['pipelines'].first['statuses'].count { |hash| hash['type'] == 'Ci::Build'}).to eq(1)
+ expect(saved_project_json['pipelines'].first['statuses'].count { |hash| hash['type'] == 'Ci::Build' }).to eq(1)
end
it 'has pipeline commits' do
@@ -112,13 +112,13 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
end
it 'has project and group labels' do
- label_types = saved_project_json['issues'].first['label_links'].map { |link| link['label']['type']}
+ label_types = saved_project_json['issues'].first['label_links'].map { |link| link['label']['type'] }
expect(label_types).to match_array(['ProjectLabel', 'GroupLabel'])
end
it 'has priorities associated to labels' do
- priorities = saved_project_json['issues'].first['label_links'].map { |link| link['label']['priorities']}
+ priorities = saved_project_json['issues'].first['label_links'].map { |link| link['label']['priorities'] }
expect(priorities.flatten).not_to be_empty
end
@@ -140,6 +140,51 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
expect(project_tree_saver.save).to be true
end
+
+ context 'group members' do
+ let(:user2) { create(:user, email: 'group@member.com') }
+ let(:member_emails) do
+ saved_project_json['project_members'].map do |pm|
+ pm['user']['email']
+ end
+ end
+
+ before do
+ Group.first.add_developer(user2)
+ end
+
+ it 'does not export group members if it has no permission' do
+ Group.first.add_developer(user)
+
+ expect(member_emails).not_to include('group@member.com')
+ end
+
+ it 'does not export group members as master' do
+ Group.first.add_master(user)
+
+ expect(member_emails).not_to include('group@member.com')
+ end
+
+ it 'exports group members as group owner' do
+ Group.first.add_owner(user)
+
+ expect(member_emails).to include('group@member.com')
+ end
+
+ context 'as admin' do
+ let(:user) { create(:admin) }
+
+ it 'exports group members as admin' do
+ expect(member_emails).to include('group@member.com')
+ end
+
+ it 'exports group members as project members' do
+ member_types = saved_project_json['project_members'].map { |pm| pm['source_type'] }
+
+ expect(member_types).to all(eq('Project'))
+ end
+ end
+ end
end
end
@@ -170,10 +215,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
commit_status = create(:commit_status, project: project)
ci_pipeline = create(:ci_pipeline,
- project: project,
- sha: merge_request.diff_head_sha,
- ref: merge_request.source_branch,
- statuses: [commit_status])
+ project: project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch,
+ statuses: [commit_status])
create(:ci_build, pipeline: ci_pipeline, project: project)
create(:milestone, project: project)
diff --git a/spec/lib/gitlab/import_export/reader_spec.rb b/spec/lib/gitlab/import_export/reader_spec.rb
index 3ceb1e7e803..48d74b07e27 100644
--- a/spec/lib/gitlab/import_export/reader_spec.rb
+++ b/spec/lib/gitlab/import_export/reader_spec.rb
@@ -86,6 +86,10 @@ describe Gitlab::ImportExport::Reader, lib: true do
expect(described_class.new(shared: shared).project_tree).to match(include: [{ issues: { methods: [:name] } }])
end
+ it 'generates the correct hash for group members' do
+ expect(described_class.new(shared: shared).group_members_tree).to match({ include: { user: { only: [:email] } } })
+ end
+
def setup_yaml(hash)
allow(YAML).to receive(:load_file).with(test_config).and_return(hash)
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index fc4435a2f64..080ff2f3f43 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -77,8 +77,8 @@ describe Deployment, models: true do
end
end
- describe '#stoppable?' do
- subject { deployment.stoppable? }
+ describe '#stop_action?' do
+ subject { deployment.stop_action? }
context 'when no other actions' do
let(:deployment) { build(:deployment) }
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index eba392044bf..8b57d8600fe 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -112,8 +112,8 @@ describe Environment, models: true do
end
end
- describe '#stoppable?' do
- subject { environment.stoppable? }
+ describe '#stop_action?' do
+ subject { environment.stop_action? }
context 'when no other actions' do
it { is_expected.to be_falsey }
@@ -142,17 +142,39 @@ describe Environment, models: true do
end
end
- describe '#stop!' do
+ describe '#stop_with_action!' do
let(:user) { create(:user) }
- subject { environment.stop!(user) }
+ subject { environment.stop_with_action!(user) }
before do
- expect(environment).to receive(:stoppable?).and_call_original
+ expect(environment).to receive(:available?).and_call_original
end
context 'when no other actions' do
- it { is_expected.to be_nil }
+ context 'environment is available' do
+ before do
+ environment.update(state: :available)
+ end
+
+ it do
+ subject
+
+ expect(environment).to be_stopped
+ end
+ end
+
+ context 'environment is already stopped' do
+ before do
+ environment.update(state: :stopped)
+ end
+
+ it do
+ subject
+
+ expect(environment).to be_stopped
+ end
+ end
end
context 'when matching action is defined' do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 9ca50555191..a4e6eb4e3a6 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -300,4 +300,17 @@ describe Group, models: true do
expect(group.members_with_parents).to include(master)
end
end
+
+ describe '#user_ids_for_project_authorizations' do
+ it 'returns the user IDs for which to refresh authorizations' do
+ master = create(:user)
+ developer = create(:user)
+
+ group.add_user(master, GroupMember::MASTER)
+ group.add_user(developer, GroupMember::DEVELOPER)
+
+ expect(group.user_ids_for_project_authorizations).
+ to include(master.id, developer.id)
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 4e96f19eb6f..7bb1657bc3a 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -218,4 +218,11 @@ describe Namespace, models: true do
expect(group.descendants.to_a).to eq([nested_group, deep_nested_group, very_deep_nested_group])
end
end
+
+ describe '#user_ids_for_project_authorizations' do
+ it 'returns the user IDs for which to refresh authorizations' do
+ expect(namespace.user_ids_for_project_authorizations).
+ to eq([namespace.owner_id])
+ end
+ end
end
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 45d5ae267c5..eea76c7bb94 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -7,18 +7,6 @@ describe API::ProjectSnippets, api: true do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
- describe 'GET /projects/:project_id/snippets/:id' do
- # TODO (rspeicher): Deprecated; remove in 9.0
- it 'always exposes expires_at as nil' do
- snippet = create(:project_snippet, author: admin)
-
- get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}", admin)
-
- expect(json_response).to have_key('expires_at')
- expect(json_response['expires_at']).to be_nil
- end
- end
-
describe 'GET /projects/:project_id/snippets/' do
let(:user) { create(:user) }
diff --git a/spec/requests/api/v3/deploy_keys_spec.rb b/spec/requests/api/v3/deploy_keys_spec.rb
new file mode 100644
index 00000000000..f5bdf408c5e
--- /dev/null
+++ b/spec/requests/api/v3/deploy_keys_spec.rb
@@ -0,0 +1,172 @@
+require 'spec_helper'
+
+describe API::V3::DeployKeys, api: true do
+ include ApiHelpers
+
+ let(:user) { create(:user) }
+ let(:admin) { create(:admin) }
+ let(:project) { create(:empty_project, creator_id: user.id) }
+ let(:project2) { create(:empty_project, creator_id: user.id) }
+ let(:deploy_key) { create(:deploy_key, public: true) }
+
+ let!(:deploy_keys_project) do
+ create(:deploy_keys_project, project: project, deploy_key: deploy_key)
+ end
+
+ describe 'GET /deploy_keys' do
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ get v3_api('/deploy_keys')
+
+ expect(response.status).to eq(401)
+ end
+ end
+
+ context 'when authenticated as non-admin user' do
+ it 'should return a 403 error' do
+ get v3_api('/deploy_keys', user)
+
+ expect(response.status).to eq(403)
+ end
+ end
+
+ context 'when authenticated as admin' do
+ it 'should return all deploy keys' do
+ get v3_api('/deploy_keys', admin)
+
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['id']).to eq(deploy_keys_project.deploy_key.id)
+ end
+ end
+ end
+
+ %w(deploy_keys keys).each do |path|
+ describe "GET /projects/:id/#{path}" do
+ before { deploy_key }
+
+ it 'should return array of ssh keys' do
+ get v3_api("/projects/#{project.id}/#{path}", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(deploy_key.title)
+ end
+ end
+
+ describe "GET /projects/:id/#{path}/:key_id" do
+ it 'should return a single key' do
+ get v3_api("/projects/#{project.id}/#{path}/#{deploy_key.id}", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['title']).to eq(deploy_key.title)
+ end
+
+ it 'should return 404 Not Found with invalid ID' do
+ get v3_api("/projects/#{project.id}/#{path}/404", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ describe "POST /projects/:id/deploy_keys" do
+ it 'should not create an invalid ssh key' do
+ post v3_api("/projects/#{project.id}/#{path}", admin), { title: 'invalid key' }
+
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('key is missing')
+ end
+
+ it 'should not create a key without title' do
+ post v3_api("/projects/#{project.id}/#{path}", admin), key: 'some key'
+
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('title is missing')
+ end
+
+ it 'should create new ssh key' do
+ key_attrs = attributes_for :another_key
+
+ expect do
+ post v3_api("/projects/#{project.id}/#{path}", admin), key_attrs
+ end.to change{ project.deploy_keys.count }.by(1)
+ end
+
+ it 'returns an existing ssh key when attempting to add a duplicate' do
+ expect do
+ post v3_api("/projects/#{project.id}/#{path}", admin), { key: deploy_key.key, title: deploy_key.title }
+ end.not_to change { project.deploy_keys.count }
+
+ expect(response).to have_http_status(201)
+ end
+
+ it 'joins an existing ssh key to a new project' do
+ expect do
+ post v3_api("/projects/#{project2.id}/#{path}", admin), { key: deploy_key.key, title: deploy_key.title }
+ end.to change { project2.deploy_keys.count }.by(1)
+
+ expect(response).to have_http_status(201)
+ end
+ end
+
+ describe "DELETE /projects/:id/#{path}/:key_id" do
+ before { deploy_key }
+
+ it 'should delete existing key' do
+ expect do
+ delete v3_api("/projects/#{project.id}/#{path}/#{deploy_key.id}", admin)
+ end.to change{ project.deploy_keys.count }.by(-1)
+ end
+
+ it 'should return 404 Not Found with invalid ID' do
+ delete v3_api("/projects/#{project.id}/#{path}/404", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ describe "POST /projects/:id/#{path}/:key_id/enable" do
+ let(:project2) { create(:empty_project) }
+
+ context 'when the user can admin the project' do
+ it 'enables the key' do
+ expect do
+ post v3_api("/projects/#{project2.id}/#{path}/#{deploy_key.id}/enable", admin)
+ end.to change { project2.deploy_keys.count }.from(0).to(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['id']).to eq(deploy_key.id)
+ end
+ end
+
+ context 'when authenticated as non-admin user' do
+ it 'should return a 404 error' do
+ post v3_api("/projects/#{project2.id}/#{path}/#{deploy_key.id}/enable", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe "DELETE /projects/:id/deploy_keys/:key_id/disable" do
+ context 'when the user can admin the project' do
+ it 'disables the key' do
+ expect do
+ delete v3_api("/projects/#{project.id}/#{path}/#{deploy_key.id}/disable", admin)
+ end.to change { project.deploy_keys.count }.from(1).to(0)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['id']).to eq(deploy_key.id)
+ end
+ end
+
+ context 'when authenticated as non-admin user' do
+ it 'should return a 404 error' do
+ delete v3_api("/projects/#{project.id}/#{path}/#{deploy_key.id}/disable", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/v3/project_snippets_spec.rb b/spec/requests/api/v3/project_snippets_spec.rb
new file mode 100644
index 00000000000..3700477f0db
--- /dev/null
+++ b/spec/requests/api/v3/project_snippets_spec.rb
@@ -0,0 +1,188 @@
+require 'rails_helper'
+
+describe API::ProjectSnippets, api: true do
+ include ApiHelpers
+
+ let(:project) { create(:empty_project, :public) }
+ let(:user) { create(:user) }
+ let(:admin) { create(:admin) }
+
+ describe 'GET /projects/:project_id/snippets/:id' do
+ # TODO (rspeicher): Deprecated; remove in 9.0
+ it 'always exposes expires_at as nil' do
+ snippet = create(:project_snippet, author: admin)
+
+ get v3_api("/projects/#{snippet.project.id}/snippets/#{snippet.id}", admin)
+
+ expect(json_response).to have_key('expires_at')
+ expect(json_response['expires_at']).to be_nil
+ end
+ end
+
+ describe 'GET /projects/:project_id/snippets/' do
+ let(:user) { create(:user) }
+
+ it 'returns all snippets available to team member' do
+ project.add_developer(user)
+ public_snippet = create(:project_snippet, :public, project: project)
+ internal_snippet = create(:project_snippet, :internal, project: project)
+ private_snippet = create(:project_snippet, :private, project: project)
+
+ get v3_api("/projects/#{project.id}/snippets/", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.size).to eq(3)
+ expect(json_response.map{ |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id)
+ expect(json_response.last).to have_key('web_url')
+ end
+
+ it 'hides private snippets from regular user' do
+ create(:project_snippet, :private, project: project)
+
+ get v3_api("/projects/#{project.id}/snippets/", user)
+ expect(response).to have_http_status(200)
+ expect(json_response.size).to eq(0)
+ end
+ end
+
+ describe 'POST /projects/:project_id/snippets/' do
+ let(:params) do
+ {
+ title: 'Test Title',
+ file_name: 'test.rb',
+ code: 'puts "hello world"',
+ visibility_level: Snippet::PUBLIC
+ }
+ end
+
+ it 'creates a new snippet' do
+ post v3_api("/projects/#{project.id}/snippets/", admin), params
+
+ expect(response).to have_http_status(201)
+ snippet = ProjectSnippet.find(json_response['id'])
+ expect(snippet.content).to eq(params[:code])
+ expect(snippet.title).to eq(params[:title])
+ expect(snippet.file_name).to eq(params[:file_name])
+ expect(snippet.visibility_level).to eq(params[:visibility_level])
+ end
+
+ it 'returns 400 for missing parameters' do
+ params.delete(:title)
+
+ post v3_api("/projects/#{project.id}/snippets/", admin), params
+
+ expect(response).to have_http_status(400)
+ end
+
+ context 'when the snippet is spam' do
+ def create_snippet(project, snippet_params = {})
+ project.add_developer(user)
+
+ post v3_api("/projects/#{project.id}/snippets", user), params.merge(snippet_params)
+ end
+
+ before do
+ allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
+ end
+
+ context 'when the project is private' do
+ let(:private_project) { create(:project_empty_repo, :private) }
+
+ context 'when the snippet is public' do
+ it 'creates the snippet' do
+ expect { create_snippet(private_project, visibility_level: Snippet::PUBLIC) }.
+ to change { Snippet.count }.by(1)
+ end
+ end
+ end
+
+ context 'when the project is public' do
+ context 'when the snippet is private' do
+ it 'creates the snippet' do
+ expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }.
+ to change { Snippet.count }.by(1)
+ end
+ end
+
+ context 'when the snippet is public' do
+ it 'rejects the shippet' do
+ expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
+ not_to change { Snippet.count }
+ expect(response).to have_http_status(400)
+ end
+
+ it 'creates a spam log' do
+ expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
+ to change { SpamLog.count }.by(1)
+ end
+ end
+ end
+ end
+ end
+
+ describe 'PUT /projects/:project_id/snippets/:id/' do
+ let(:snippet) { create(:project_snippet, author: admin) }
+
+ it 'updates snippet' do
+ new_content = 'New content'
+
+ put v3_api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content
+
+ expect(response).to have_http_status(200)
+ snippet.reload
+ expect(snippet.content).to eq(new_content)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ put v3_api("/projects/#{snippet.project.id}/snippets/1234", admin), title: 'foo'
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+
+ it 'returns 400 for missing parameters' do
+ put v3_api("/projects/#{project.id}/snippets/1234", admin)
+
+ expect(response).to have_http_status(400)
+ end
+ end
+
+ describe 'DELETE /projects/:project_id/snippets/:id/' do
+ let(:snippet) { create(:project_snippet, author: admin) }
+
+ it 'deletes snippet' do
+ admin = create(:admin)
+ snippet = create(:project_snippet, author: admin)
+
+ delete v3_api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin)
+
+ expect(response).to have_http_status(200)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ delete v3_api("/projects/#{snippet.project.id}/snippets/1234", admin)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+ end
+
+ describe 'GET /projects/:project_id/snippets/:id/raw' do
+ let(:snippet) { create(:project_snippet, author: admin) }
+
+ it 'returns raw text' do
+ get v3_api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin)
+
+ expect(response).to have_http_status(200)
+ expect(response.content_type).to eq 'text/plain'
+ expect(response.body).to eq(snippet.content)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ delete v3_api("/projects/#{snippet.project.id}/snippets/1234", admin)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+ end
+end
diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb
index 6f7d1a5d28d..560f83d94f7 100644
--- a/spec/services/ci/stop_environments_service_spec.rb
+++ b/spec/services/ci/stop_environments_service_spec.rb
@@ -42,10 +42,10 @@ describe Ci::StopEnvironmentsService, services: true do
end
end
- context 'when environment is not stoppable' do
+ context 'when environment is not stopped' do
before do
allow_any_instance_of(Environment)
- .to receive(:stoppable?).and_return(false)
+ .to receive(:state).and_return(:stopped)
end
it 'does not stop environment' do
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index ac3834c32ff..30578ee4c7d 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -181,5 +181,107 @@ describe Issues::CreateService, services: true do
expect(issue.title).to be_nil
end
end
+
+ context 'checking spam' do
+ let(:opts) do
+ {
+ title: 'Awesome issue',
+ description: 'please fix',
+ request: double(:request, env: {})
+ }
+ end
+
+ before do
+ allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true)
+ end
+
+ context 'when recaptcha was verified' do
+ let(:log_user) { user }
+ let(:spam_logs) { create_list(:spam_log, 2, user: log_user, title: 'Awesome issue') }
+
+ before do
+ opts[:recaptcha_verified] = true
+ opts[:spam_log_id] = spam_logs.last.id
+
+ expect(AkismetService).not_to receive(:new)
+ end
+
+ it 'does no mark an issue as a spam ' do
+ expect(issue).not_to be_spam
+ end
+
+ it 'an issue is valid ' do
+ expect(issue.valid?).to be_truthy
+ end
+
+ it 'does not assign a spam_log to an issue' do
+ expect(issue.spam_log).to be_nil
+ end
+
+ it 'marks related spam_log as recaptcha_verified' do
+ expect { issue }.to change{SpamLog.last.recaptcha_verified}.from(false).to(true)
+ end
+
+ context 'when spam log does not belong to a user' do
+ let(:log_user) { create(:user) }
+
+ it 'does not mark spam_log as recaptcha_verified' do
+ expect { issue }.not_to change{SpamLog.last.recaptcha_verified}
+ end
+ end
+
+ context 'when spam log title does not match the issue title' do
+ before do
+ opts[:title] = 'Another issue'
+ end
+
+ it 'does not mark spam_log as recaptcha_verified' do
+ expect { issue }.not_to change{SpamLog.last.recaptcha_verified}
+ end
+ end
+ end
+
+ context 'when recaptcha was not verified' do
+ context 'when akismet detects spam' do
+ before do
+ allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
+ end
+
+ it 'marks an issue as a spam ' do
+ expect(issue).to be_spam
+ end
+
+ it 'an issue is not valid ' do
+ expect(issue.valid?).to be_falsey
+ end
+
+ it 'creates a new spam_log' do
+ expect{issue}.to change{SpamLog.count}.from(0).to(1)
+ end
+
+ it 'assigns a spam_log to an issue' do
+ expect(issue.spam_log).to eq(SpamLog.last)
+ end
+ end
+
+ context 'when akismet does not detect spam' do
+ before do
+ allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(false)
+ end
+
+ it 'does not mark an issue as a spam ' do
+ expect(issue).not_to be_spam
+ end
+
+ it 'an issue is valid ' do
+ expect(issue.valid?).to be_truthy
+ end
+
+ it 'does not assign a spam_log to an issue' do
+ expect(issue.spam_log).to be_nil
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 5d5812c2c15..5c6fbea8d0e 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -83,4 +83,30 @@ describe Projects::TransferService, services: true do
transfer_project(project, user, group)
end
end
+
+ describe 'refreshing project authorizations' do
+ let(:group) { create(:group) }
+ let(:owner) { project.namespace.owner }
+ let(:group_member) { create(:user) }
+
+ before do
+ group.add_user(owner, GroupMember::MASTER)
+ group.add_user(group_member, GroupMember::DEVELOPER)
+ end
+
+ it 'refreshes the permissions of the old and new namespace' do
+ transfer_project(project, owner, group)
+
+ expect(group_member.authorized_projects).to include(project)
+ expect(owner.authorized_projects).to include(project)
+ end
+
+ it 'only schedules a single job for every user' do
+ expect(UserProjectAccessChangedService).to receive(:new).
+ with([owner.id, group_member.id]).
+ and_call_original
+
+ transfer_project(project, owner, group)
+ end
+ end
end
diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb
new file mode 100644
index 00000000000..271c17dd8c0
--- /dev/null
+++ b/spec/services/spam_service_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe SpamService, services: true do
+ describe '#check' do
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project) }
+ let(:request) { double(:request, env: {}) }
+
+ def check_spam(issue, request)
+ described_class.new(issue, request).check
+ end
+
+ context 'when indicated as spam by akismet' do
+ before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: true)) }
+
+ it 'returns false when request is missing' do
+ expect(check_spam(issue, nil)).to be_falsey
+ end
+
+ it 'returns false when issue is not public' do
+ issue = create(:issue, project: create(:project, :private))
+
+ expect(check_spam(issue, request)).to be_falsey
+ end
+
+ it 'returns true' do
+ expect(check_spam(issue, request)).to be_truthy
+ end
+
+ it 'creates a spam log' do
+ expect { check_spam(issue, request) }.to change { SpamLog.count }.from(0).to(1)
+ end
+ end
+
+ context 'when not indicated as spam by akismet' do
+ before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
+
+ it 'returns false' do
+ expect(check_spam(issue, request)).to be_falsey
+ end
+
+ it 'does not create a spam log' do
+ expect { check_spam(issue, request) }.not_to change { SpamLog.count }
+ end
+ end
+ end
+end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 7913a180f9b..7f027ae02a2 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -418,6 +418,45 @@ describe SystemNoteService, services: true do
to be_truthy
end
end
+
+ context 'when noteable is an Issue' do
+ let(:issue) { create(:issue, project: project) }
+
+ it 'is truthy when issue is closed' do
+ issue.close
+
+ expect(described_class.cross_reference_disallowed?(issue, project.commit)).
+ to be_truthy
+ end
+
+ it 'is falsey when issue is open' do
+ expect(described_class.cross_reference_disallowed?(issue, project.commit)).
+ to be_falsy
+ end
+ end
+
+ context 'when noteable is a Merge Request' do
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+
+ it 'is truthy when merge request is closed' do
+ allow(merge_request).to receive(:closed?).and_return(:true)
+
+ expect(described_class.cross_reference_disallowed?(merge_request, project.commit)).
+ to be_truthy
+ end
+
+ it 'is truthy when merge request is merged' do
+ allow(merge_request).to receive(:closed?).and_return(:true)
+
+ expect(described_class.cross_reference_disallowed?(merge_request, project.commit)).
+ to be_truthy
+ end
+
+ it 'is falsey when merge request is open' do
+ expect(described_class.cross_reference_disallowed?(merge_request, project.commit)).
+ to be_falsy
+ end
+ end
end
describe '.cross_reference_exists?' do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index ab38dac65c5..5fda7c63cdb 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -35,6 +35,7 @@ RSpec.configure do |config|
config.include Warden::Test::Helpers, type: :request
config.include LoginHelpers, type: :feature
config.include SearchHelpers, type: :feature
+ config.include WaitForAjax, type: :feature
config.include StubConfiguration
config.include EmailHelpers, type: :mailer
config.include TestEnv
diff --git a/vendor/gitignore/Android.gitignore b/vendor/gitignore/Android.gitignore
index d028d1251ad..a1a65c2d72e 100644
--- a/vendor/gitignore/Android.gitignore
+++ b/vendor/gitignore/Android.gitignore
@@ -44,3 +44,11 @@ captures/
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
+
+# Google Services (e.g. APIs or Firebase)
+google-services.json
+
+#Freeline
+freeline.py
+freeline/
+freeline_project_description.json
diff --git a/vendor/gitignore/CMake.gitignore b/vendor/gitignore/CMake.gitignore
index 27ada0591ec..9ea395f15ee 100644
--- a/vendor/gitignore/CMake.gitignore
+++ b/vendor/gitignore/CMake.gitignore
@@ -1,6 +1,7 @@
CMakeCache.txt
CMakeFiles
CMakeScripts
+Testing
Makefile
cmake_install.cmake
install_manifest.txt
diff --git a/vendor/gitignore/CodeIgniter.gitignore b/vendor/gitignore/CodeIgniter.gitignore
index 60571a0c383..bfea17cdc5b 100644
--- a/vendor/gitignore/CodeIgniter.gitignore
+++ b/vendor/gitignore/CodeIgniter.gitignore
@@ -9,3 +9,9 @@ user_guide_src/build/*
user_guide_src/cilexer/build/*
user_guide_src/cilexer/dist/*
user_guide_src/cilexer/pycilexer.egg-info/*
+
+#codeigniter 3
+application/logs/*
+!application/logs/index.html
+!application/logs/.htaccess
+/vendor/
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
index e375c744b6d..401fee15748 100644
--- a/vendor/gitignore/Global/JetBrains.gitignore
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -2,24 +2,24 @@
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
-.idea/workspace.xml
-.idea/tasks.xml
+.idea/**/workspace.xml
+.idea/**/tasks.xml
# Sensitive or high-churn files:
-.idea/dataSources/
-.idea/dataSources.ids
-.idea/dataSources.xml
-.idea/dataSources.local.xml
-.idea/sqlDataSources.xml
-.idea/dynamic.xml
-.idea/uiDesigner.xml
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.xml
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
# Gradle:
-.idea/gradle.xml
-.idea/libraries
+.idea/**/gradle.xml
+.idea/**/libraries
# Mongo Explorer plugin:
-.idea/mongoSettings.xml
+.idea/**/mongoSettings.xml
## File-based project format:
*.iws
diff --git a/vendor/gitignore/Global/Matlab.gitignore b/vendor/gitignore/Global/Matlab.gitignore
index 32a5ad4c777..09dfde64b5f 100644
--- a/vendor/gitignore/Global/Matlab.gitignore
+++ b/vendor/gitignore/Global/Matlab.gitignore
@@ -17,3 +17,6 @@ slprj/
# Session info
octave-workspace
+
+# Simulink autosave extension
+.autosave
diff --git a/vendor/gitignore/Global/Stata.gitignore b/vendor/gitignore/Global/Stata.gitignore
new file mode 100644
index 00000000000..07997bb1201
--- /dev/null
+++ b/vendor/gitignore/Global/Stata.gitignore
@@ -0,0 +1,24 @@
+# .gitignore file for git projects containing Stata files
+# Commercial statistical software: http://www.stata.com
+
+# Stata dataset and output files
+*.dta
+*.gph
+*.log
+*.smcl
+*.stpr
+*.stsem
+
+# Graphic export files from Stata
+# Stata command graph export: http://www.stata.com/manuals14/g-2graphexport.pdf
+#
+# You may add graphic export files to your .gitignore. However you should be
+# aware that this will exclude all image files from this main directory
+# and subdirectories.
+# *.ps
+# *.eps
+# *.wmf
+# *.emf
+# *.pdf
+# *.png
+# *.tif
diff --git a/vendor/gitignore/Go.gitignore b/vendor/gitignore/Go.gitignore
index 5e1047c9d78..a1338d68517 100644
--- a/vendor/gitignore/Go.gitignore
+++ b/vendor/gitignore/Go.gitignore
@@ -1,30 +1,14 @@
-# Compiled Object files, Static and Dynamic libs (Shared Objects)
-*.o
-*.a
+# Binaries for programs and plugins
+*.exe
+*.dll
*.so
+*.dylib
-# Folders
-_obj
-_test
-
-# Architecture specific extensions/prefixes
-*.[568vq]
-[568vq].out
-
-*.cgo1.go
-*.cgo2.c
-_cgo_defun.c
-_cgo_gotypes.go
-_cgo_export.*
-
-_testmain.go
-
-*.exe
+# Test binary, build with `go test -c`
*.test
-*.prof
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
-# External packages folder
-vendor/
+# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
+.glide/
diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore
index e44e0860405..dbb4a2dfa1a 100644
--- a/vendor/gitignore/Java.gitignore
+++ b/vendor/gitignore/Java.gitignore
@@ -1,5 +1,8 @@
*.class
+# Log file
+*.log
+
# BlueJ files
*.ctxt
@@ -10,6 +13,9 @@
*.jar
*.war
*.ear
+*.zip
+*.tar.gz
+*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
diff --git a/vendor/gitignore/Joomla.gitignore b/vendor/gitignore/Joomla.gitignore
index 93103fdbe77..53a74e74657 100644
--- a/vendor/gitignore/Joomla.gitignore
+++ b/vendor/gitignore/Joomla.gitignore
@@ -29,8 +29,6 @@
/administrator/components/com_search/*
/administrator/components/com_templates/*
/administrator/components/com_users/*
-/administrator/components/com_weblinks/*
-/administrator/components/index.html
/administrator/help/*
/administrator/includes/*
/administrator/language/en-GB/en-GB.com_ajax.ini
@@ -41,7 +39,6 @@
/administrator/language/en-GB/en-GB.com_joomlaupdate.sys.ini
/administrator/language/en-GB/en-GB.com_postinstall.ini
/administrator/language/en-GB/en-GB.com_postinstall.sys.ini
-/administrator/language/en-GB/en-GB.com_sitemapjen.sys.ini
/administrator/language/en-GB/en-GB.com_tags.ini
/administrator/language/en-GB/en-GB.com_tags.sys.ini
/administrator/language/en-GB/en-GB.mod_stats_admin.ini
@@ -250,15 +247,10 @@
/administrator/language/en-GB/en-GB.plg_user_joomla.sys.ini
/administrator/language/en-GB/en-GB.plg_user_profile.ini
/administrator/language/en-GB/en-GB.plg_user_profile.sys.ini
-/administrator/language/en-GB/en-GB.tpl_bluestork.ini
-/administrator/language/en-GB/en-GB.tpl_bluestork.sys.ini
/administrator/language/en-GB/en-GB.tpl_hathor.ini
/administrator/language/en-GB/en-GB.tpl_hathor.sys.ini
/administrator/language/en-GB/en-GB.xml
-/administrator/language/en-GB/index.html
-/administrator/language/ru-RU/index.html
/administrator/language/overrides/*
-/administrator/language/index.html
/administrator/logs/index.html
/administrator/manifests/*
/administrator/modules/mod_custom/*
@@ -278,12 +270,9 @@
/administrator/modules/mod_unread/*
/administrator/modules/mod_version/*
/administrator/modules/mod_stats_admin/*
-/administrator/modules/index.html
-/administrator/templates/bluestork/*
/administrator/templates/isis/*
/administrator/templates/hathor/*
/administrator/templates/system/*
-/administrator/templates/index.html
/administrator/index.php
/cache/*
/bin/*
@@ -302,7 +291,6 @@
/components/com_newsfeeds/*
/components/com_search/*
/components/com_users/*
-/components/com_weblinks/*
/components/com_wrapper/*
/components/index.html
/images/banners/*
@@ -403,7 +391,6 @@
/language/en-GB/en-GB.tpl_beez5.ini
/language/en-GB/en-GB.tpl_beez5.sys.ini
/language/en-GB/en-GB.xml
-/language/en-GB/index.html
/language/en-GB/install.xml
/language/overrides/*
/language/index.html
@@ -428,8 +415,6 @@
/libraries/index.html
/libraries/import.php
/libraries/loader.php
-/libraries/platform.php
-/logs/*
/media/cms/*
/media/com_contenthistory/*
/media/com_finder/*
@@ -472,7 +457,6 @@
/modules/mod_tags_popular/*
/modules/mod_tags_similar/*
/modules/mod_users_latest/*
-/modules/mod_weblinks/*
/modules/mod_whosonline/*
/modules/mod_wrapper/*
/modules/index.html
@@ -481,9 +465,7 @@
/plugins/authentication/joomla/*
/plugins/authentication/ldap/*
/plugins/authentication/cookie/*
-/plugins/authentication/index.html
/plugins/captcha/recaptcha/*
-/plugins/captcha/index.html
/plugins/content/emailcloak/*
/plugins/content/example/*
/plugins/content/finder/*
@@ -494,27 +476,21 @@
/plugins/content/pagenavigation/*
/plugins/content/vote/*
/plugins/content/contact/*
-/plugins/content/index.html
/plugins/editors/codemirror/*
/plugins/editors/none/*
/plugins/editors/tinymce/*
-/plugins/editors/index.html
/plugins/editors-xtd/module/*
/plugins/editors-xtd/article/*
/plugins/editors-xtd/image/*
/plugins/editors-xtd/pagebreak/*
/plugins/editors-xtd/readmore/*
-/plugins/editors-xtd/index.html
/plugins/extension/example/*
/plugins/extension/joomla/*
-/plugins/extension/index.html
-/plugins/finder/index.html
/plugins/finder/categories/*
/plugins/finder/contacts/*
/plugins/finder/content/*
/plugins/finder/newsfeeds/*
/plugins/finder/tags/*
-/plugins/finder/weblinks/*
/plugins/installer/*
/plugins/quickicon/extensionupdate/*
/plugins/quickicon/joomlaupdate/*
@@ -547,10 +523,7 @@
/plugins/user/profile/*
/plugins/user/index.html
/plugins/index.html
-/templates/atomic/*
/templates/beez3/*
-/templates/beez_20/*
-/templates/beez5/*
/templates/protostar/*
/templates/system/*
/templates/index.html
diff --git a/vendor/gitignore/KiCad.gitignore b/vendor/gitignore/KiCad.gitignore
index 606ed1c7b4d..208bc4fc591 100644
--- a/vendor/gitignore/KiCad.gitignore
+++ b/vendor/gitignore/KiCad.gitignore
@@ -13,7 +13,8 @@ _autosave-*
*.net
# Autorouter files (exported from Pcbnew)
-.dsn
+*.dsn
+*.ses
# Exported BOM files
*.xml
diff --git a/vendor/gitignore/Laravel.gitignore b/vendor/gitignore/Laravel.gitignore
index a2d1564060b..a4854bef534 100644
--- a/vendor/gitignore/Laravel.gitignore
+++ b/vendor/gitignore/Laravel.gitignore
@@ -1,5 +1,6 @@
vendor/
node_modules/
+npm-debug.log
# Laravel 4 specific
bootstrap/compiled.php
@@ -7,10 +8,13 @@ app/storage/
# Laravel 5 & Lumen specific
public/storage
+public/hot
storage/*.key
.env.*.php
.env.php
.env
+Homestead.yaml
+Homestead.json
# Rocketeer PHP task runner and deployment package. https://github.com/rocketeers/rocketeer
.rocketeer/
diff --git a/vendor/gitignore/Magento.gitignore b/vendor/gitignore/Magento.gitignore
index 195c9b7a029..b282f5cf547 100644
--- a/vendor/gitignore/Magento.gitignore
+++ b/vendor/gitignore/Magento.gitignore
@@ -1,104 +1,16 @@
-.htaccess.sample
-.modgit/
-.modman/
-app/code/community/Phoenix/Moneybookers/
-app/code/community/Cm/RedisSession/
-app/code/core/
-app/design/adminhtml/default/default/
-app/design/frontend/base/
-app/design/frontend/rwd/
-app/design/frontend/default/blank/
-app/design/frontend/default/default/
-app/design/frontend/default/iphone/
-app/design/frontend/default/modern/
-app/design/frontend/enterprise/default
-app/design/install/
-app/etc/modules/Enterprise_*
-app/etc/modules/Mage_*.xml
-app/etc/modules/Phoenix_Moneybookers.xml
-app/etc/modules/Cm_RedisSession.xml
-app/etc/applied.patches.list
-app/etc/config.xml
-app/etc/enterprise.xml
-app/etc/local.xml.additional
-app/etc/local.xml.template
-app/etc/local.xml
-app/.htaccess
-app/bootstrap.php
-app/locale/en_US/
-app/Mage.php
-/cron.php
-cron.sh
-dev/.htaccess
-dev/tests/functional/
-downloader/
-errors/
-favicon.ico
-/get.php
-includes/
-/index.php
-index.php.sample
-/install.php
-js/blank.html
-js/calendar/
-js/enterprise/
-js/extjs/
-js/firebug/
-js/flash/
-js/index.php
-js/jscolor/
-js/lib/
-js/mage/
-js/prototype/
-js/scriptaculous/
-js/spacer.gif
-js/tiny_mce/
-js/varien/
-lib/3Dsecure/
-lib/Apache/
-lib/flex/
-lib/googlecheckout/
-lib/.htaccess
-lib/LinLibertineFont/
-lib/Mage/
-lib/PEAR/
-lib/Pelago/
-lib/phpseclib/
-lib/Varien/
-lib/Zend/
-lib/Cm/
-lib/Credis/
-lib/Magento/
-LICENSE_AFL.txt
-LICENSE.html
-LICENSE.txt
-LICENSE_EE*
-/mage
-media/
-/api.php
-nbproject/
-pear
-pear/
-php.ini.sample
-pkginfo/
-RELEASE_NOTES.txt
-shell/.htaccess
-shell/abstract.php
-shell/compiler.php
-shell/indexer.php
-shell/log.php
-sitemap.xml
-skin/adminhtml/default/default/
-skin/adminhtml/default/enterprise
-skin/frontend/base/
-skin/frontend/rwd/
-skin/frontend/default/blank/
-skin/frontend/default/blue/
-skin/frontend/default/default/
-skin/frontend/default/french/
-skin/frontend/default/german/
-skin/frontend/default/iphone/
-skin/frontend/default/modern/
-skin/frontend/enterprise
-skin/install/
-var/
+#--------------------------#
+# Magento Default Files #
+#--------------------------#
+
+/app/etc/local.xml
+/media/*
+!/media/.htaccess
+!/media/customer/.htaccess
+!/media/dhl/logo.jpg
+!/media/downloadable/.htaccess
+!/media/xmlconnect/custom/ok.gif
+!/media/xmlconnect/original/ok.gif
+!/media/xmlconnect/system/ok.gif
+/var/*
+!/var/.htaccess
+!/var/package/*.xml
diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore
index 9a439fcd988..38ac77e405e 100644
--- a/vendor/gitignore/Node.gitignore
+++ b/vendor/gitignore/Node.gitignore
@@ -21,6 +21,9 @@ coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
+# Bower dependency directory (https://bower.io/)
+bower_components
+
# node-waf configuration
.lock-wscript
@@ -28,8 +31,11 @@ coverage
build/Release
# Dependency directories
-node_modules
-jspm_packages
+node_modules/
+jspm_packages/
+
+# Typescript v1 declaration files
+typings/
# Optional npm cache directory
.npm
@@ -46,3 +52,6 @@ jspm_packages
# Yarn Integrity file
.yarn-integrity
+# dotenv environment variables file
+.env
+
diff --git a/vendor/gitignore/Objective-C.gitignore b/vendor/gitignore/Objective-C.gitignore
index 58c51ecaed4..af90c007a3f 100644
--- a/vendor/gitignore/Objective-C.gitignore
+++ b/vendor/gitignore/Objective-C.gitignore
@@ -19,7 +19,8 @@ xcuserdata/
## Other
*.moved-aside
-*.xcuserstate
+*.xccheckout
+*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
diff --git a/vendor/gitignore/Perl.gitignore b/vendor/gitignore/Perl.gitignore
index d41364ab18e..9bf1537f6ae 100644
--- a/vendor/gitignore/Perl.gitignore
+++ b/vendor/gitignore/Perl.gitignore
@@ -4,6 +4,7 @@
/META.json
/MYMETA.*
*.o
+*.pm.tdy
*.bs
# Devel::Cover
diff --git a/vendor/gitignore/PureScript.gitignore b/vendor/gitignore/PureScript.gitignore
new file mode 100644
index 00000000000..361cf5277ba
--- /dev/null
+++ b/vendor/gitignore/PureScript.gitignore
@@ -0,0 +1,8 @@
+# Dependencies
+.psci_modules
+bower_components
+node_modules
+
+# Generated files
+.psci
+output
diff --git a/vendor/gitignore/Python.gitignore b/vendor/gitignore/Python.gitignore
index 9a05e2debe5..cf3102d6b00 100644
--- a/vendor/gitignore/Python.gitignore
+++ b/vendor/gitignore/Python.gitignore
@@ -80,7 +80,7 @@ celerybeat-schedule
.env
# virtualenv
-.venv/
+.venv
venv/
ENV/
diff --git a/vendor/gitignore/Scala.gitignore b/vendor/gitignore/Scala.gitignore
index a02d882cb88..006a7b247fe 100644
--- a/vendor/gitignore/Scala.gitignore
+++ b/vendor/gitignore/Scala.gitignore
@@ -13,6 +13,8 @@ project/boot/
project/plugins/project/
# Scala-IDE specific
+.ensime
+.ensime_cache/
.scala_dependencies
.worksheet
diff --git a/vendor/gitignore/Swift.gitignore b/vendor/gitignore/Swift.gitignore
index 2c22487b5e3..099d22ae2f4 100644
--- a/vendor/gitignore/Swift.gitignore
+++ b/vendor/gitignore/Swift.gitignore
@@ -19,7 +19,8 @@ xcuserdata/
## Other
*.moved-aside
-*.xcuserstate
+*.xccheckout
+*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
@@ -35,6 +36,7 @@ playground.xcworkspace
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
+# Package.pins
.build/
# CocoaPods
diff --git a/vendor/gitignore/Unity.gitignore b/vendor/gitignore/Unity.gitignore
index 1c10388911b..b829399ae85 100644
--- a/vendor/gitignore/Unity.gitignore
+++ b/vendor/gitignore/Unity.gitignore
@@ -5,6 +5,9 @@
/[Bb]uilds/
/Assets/AssetStoreTools*
+# Visual Studio 2015 cache directory
+/.vs/
+
# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
@@ -18,6 +21,7 @@ ExportedObj/
*.pidb
*.booproj
*.svd
+*.pdb
# Unity3D generated meta files
diff --git a/vendor/gitignore/UnrealEngine.gitignore b/vendor/gitignore/UnrealEngine.gitignore
index beec7b91f15..2f096001fec 100644
--- a/vendor/gitignore/UnrealEngine.gitignore
+++ b/vendor/gitignore/UnrealEngine.gitignore
@@ -36,6 +36,7 @@
# These project files can be generated by the engine
*.xcodeproj
+*.xcworkspace
*.sln
*.suo
*.opensdf
@@ -56,6 +57,9 @@ Build/*
# Don't ignore icon files in Build
!Build/**/*.ico
+# Built data for maps
+*_BuiltData.uasset
+
# Configuration files generated by the Editor
Saved/*
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index d9e876cfcdd..8054980d742 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -199,7 +199,6 @@ ClientBin/
*.jfm
*.pfx
*.publishsettings
-node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
@@ -234,6 +233,10 @@ FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
# Visual Studio 6 build log
*.plg
@@ -271,4 +274,5 @@ __pycache__/
*.pyc
# Cake - Uncomment if you are using it
-# tools/
+# tools/**
+# !tools/packages.config
diff --git a/vendor/gitignore/Waf.gitignore b/vendor/gitignore/Waf.gitignore
index 48e8d8f7be4..dad2b56bdda 100644
--- a/vendor/gitignore/Waf.gitignore
+++ b/vendor/gitignore/Waf.gitignore
@@ -1,4 +1,9 @@
-# for projects that use Waf for building: http://code.google.com/p/waf/
-.waf-*
-.waf3-*
-.lock-*
+# For projects that use the Waf build system: https://waf.io/
+# Dot-hidden on Unix-like systems
+.waf-*-*/
+.waf3-*-*/
+# Hidden directory on Windows (no dot)
+waf-*-*/
+waf3-*-*/
+# Lockfile
+.lock-waf_*_build
diff --git a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml
index 36dfc539b3b..7298ea73bab 100644
--- a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml
@@ -1,4 +1,4 @@
-# Explaination on the scripts:
+# Explanation on the scripts:
# https://gitlab.com/gitlab-examples/kubernetes-deploy/blob/master/README.md
image: registry.gitlab.com/gitlab-examples/kubernetes-deploy