summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-30 12:06:34 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-30 12:06:34 +0000
commit228d752ff09362002cc904d28edee7d63cc3cef2 (patch)
tree63e7ff466c0b0794f67c87c34e874f8682fb5de0
parentb539ac1d619c0aafe5988ab8b125a8b43b14d87f (diff)
downloadgitlab-ce-228d752ff09362002cc904d28edee7d63cc3cef2.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/gl_dropdown.js4
-rw-r--r--app/assets/javascripts/repository/components/directory_download_links.vue47
-rw-r--r--app/assets/javascripts/repository/components/tree_action_link.vue28
-rw-r--r--app/assets/javascripts/repository/index.js64
-rw-r--r--app/assets/javascripts/search_autocomplete.js45
-rw-r--r--app/graphql/types/todo_target_enum.rb8
-rw-r--r--app/graphql/types/todo_type.rb3
-rw-r--r--app/helpers/tree_helper.rb11
-rw-r--r--app/services/clusters/update_service.rb2
-rw-r--r--app/views/projects/buttons/_download.html.haml13
-rw-r--r--app/views/projects/tree/_tree_header.html.haml12
-rw-r--r--changelogs/unreleased/32562-dynamic-db-pool-size-in-puma.yml5
-rw-r--r--changelogs/unreleased/33897-make-arrow-buttons-work-within-search-box.yml5
-rw-r--r--changelogs/unreleased/34717-update-expired-trial-copy.yml5
-rw-r--r--changelogs/unreleased/34757-bugfix-graphql-missing-todo-types.yml5
-rw-r--r--changelogs/unreleased/most-affected-projects.yml5
-rw-r--r--changelogs/unreleased/remove-empty-github-service-templates.yml5
-rw-r--r--changelogs/unreleased/sh-set-admin-default-visibilities.yml5
-rw-r--r--config/initializers/database_config.rb18
-rw-r--r--db/migrate/20191026124116_set_application_settings_default_project_and_snippet_visibility.rb15
-rw-r--r--db/post_migrate/20191021101942_remove_empty_github_service_templates.rb28
-rw-r--r--db/schema.rb6
-rw-r--r--lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb2
-rw-r--r--locale/gitlab.pot10
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb22
-rw-r--r--spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap75
-rw-r--r--spec/frontend/repository/components/directory_download_links_spec.js29
-rw-r--r--spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js3
-rw-r--r--spec/initializers/database_config_spec.rb73
-rw-r--r--spec/migrations/remove_empty_github_service_templates_spec.rb55
-rw-r--r--spec/requests/api/graphql/current_user/todos_query_spec.rb12
-rw-r--r--spec/requests/api/graphql/current_user_query_spec.rb33
-rw-r--r--spec/services/clusters/update_service_spec.rb17
-rwxr-xr-x[-rw-r--r--]vendor/gitignore/C++.gitignore0
-rwxr-xr-x[-rw-r--r--]vendor/gitignore/Java.gitignore0
35 files changed, 603 insertions, 67 deletions
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 4e1b4f2652c..045f77af7ea 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -617,7 +617,7 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.hidden = function(e) {
var $input;
this.resetRows();
- this.removeArrayKeyEvent();
+ this.removeArrowKeyEvent();
$input = this.dropdown.find('.dropdown-input-field');
if (this.options.filterable) {
$input.blur();
@@ -900,7 +900,7 @@ GitLabDropdown = (function() {
);
};
- GitLabDropdown.prototype.removeArrayKeyEvent = function() {
+ GitLabDropdown.prototype.removeArrowKeyEvent = function() {
return $('body').off('keydown');
};
diff --git a/app/assets/javascripts/repository/components/directory_download_links.vue b/app/assets/javascripts/repository/components/directory_download_links.vue
new file mode 100644
index 00000000000..dffadade082
--- /dev/null
+++ b/app/assets/javascripts/repository/components/directory_download_links.vue
@@ -0,0 +1,47 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlLink,
+ },
+ props: {
+ currentPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ links: {
+ type: Array,
+ required: true,
+ },
+ },
+ computed: {
+ normalizedLinks() {
+ return this.links.map(link => ({
+ text: link.text,
+ path: `${link.path}?path=${this.currentPath}`,
+ }));
+ },
+ },
+};
+</script>
+
+<template>
+ <section class="border-top pt-1 mt-1">
+ <h5 class="m-0 dropdown-bold-header">{{ __('Download this directory') }}</h5>
+ <div class="dropdown-menu-content">
+ <div class="btn-group ml-0 w-100">
+ <gl-link
+ v-for="(link, index) in normalizedLinks"
+ :key="index"
+ :href="link.path"
+ :class="{ 'btn-primary': index === 0 }"
+ class="btn btn-xs"
+ >
+ {{ link.text }}
+ </gl-link>
+ </div>
+ </div>
+ </section>
+</template>
diff --git a/app/assets/javascripts/repository/components/tree_action_link.vue b/app/assets/javascripts/repository/components/tree_action_link.vue
new file mode 100644
index 00000000000..72764f3ccc9
--- /dev/null
+++ b/app/assets/javascripts/repository/components/tree_action_link.vue
@@ -0,0 +1,28 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlLink,
+ },
+ props: {
+ path: {
+ type: String,
+ required: true,
+ },
+ text: {
+ type: String,
+ required: true,
+ },
+ cssClass: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-link :href="path" :class="cssClass" class="btn">{{ text }}</gl-link>
+</template>
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index 6a6e7f73188..de7350f0d2f 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -3,9 +3,13 @@ import createRouter from './router';
import App from './components/app.vue';
import Breadcrumbs from './components/breadcrumbs.vue';
import LastCommit from './components/last_commit.vue';
+import TreeActionLink from './components/tree_action_link.vue';
+import DirectoryDownloadLinks from './components/directory_download_links.vue';
import apolloProvider from './graphql';
import { setTitle } from './utils/title';
import { parseBoolean } from '../lib/utils/common_utils';
+import { webIDEUrl } from '../lib/utils/url_utility';
+import { __ } from '../locale';
export default function setupVueRepositoryList() {
const el = document.getElementById('js-tree-list');
@@ -91,6 +95,66 @@ export default function setupVueRepositoryList() {
},
});
+ const treeHistoryLinkEl = document.getElementById('js-tree-history-link');
+ const { historyLink } = treeHistoryLinkEl.dataset;
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: treeHistoryLinkEl,
+ router,
+ render(h) {
+ return h(TreeActionLink, {
+ props: {
+ path: historyLink + (this.$route.params.pathMatch || '/'),
+ text: __('History'),
+ },
+ });
+ },
+ });
+
+ const webIdeLinkEl = document.getElementById('js-tree-web-ide-link');
+
+ if (webIdeLinkEl) {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: webIdeLinkEl,
+ router,
+ render(h) {
+ return h(TreeActionLink, {
+ props: {
+ path: webIDEUrl(`/${projectPath}/edit/${ref}/-${this.$route.params.pathMatch || '/'}`),
+ text: __('Web IDE'),
+ cssClass: 'qa-web-ide-button',
+ },
+ });
+ },
+ });
+ }
+
+ const directoryDownloadLinks = document.getElementById('js-directory-downloads');
+
+ if (directoryDownloadLinks) {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: directoryDownloadLinks,
+ router,
+ render(h) {
+ const currentPath = this.$route.params.pathMatch || '/';
+
+ if (currentPath !== '/') {
+ return h(DirectoryDownloadLinks, {
+ props: {
+ currentPath: currentPath.replace(/^\//, ''),
+ links: JSON.parse(directoryDownloadLinks.dataset.links),
+ },
+ });
+ }
+
+ return null;
+ },
+ });
+ }
+
// eslint-disable-next-line no-new
new Vue({
el,
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index e08a67ec604..57bf9a2d233 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-return-assign, one-var, no-var, consistent-return, class-methods-use-this, no-lonely-if, vars-on-top */
+/* eslint-disable no-return-assign, one-var, no-var, consistent-return, class-methods-use-this, vars-on-top */
import $ from 'jquery';
import { escape, throttle } from 'underscore';
@@ -95,7 +95,6 @@ export class SearchAutocomplete {
this.createAutocomplete();
}
- this.saveTextLength();
this.bindEvents();
this.dropdownToggle.dropdown();
this.searchInput.addClass('js-autocomplete-disabled');
@@ -107,7 +106,7 @@ export class SearchAutocomplete {
this.onClearInputClick = this.onClearInputClick.bind(this);
this.onSearchInputFocus = this.onSearchInputFocus.bind(this);
this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this);
- this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this);
+ this.onSearchInputChange = this.onSearchInputChange.bind(this);
this.setScrollFade = this.setScrollFade.bind(this);
}
getElement(selector) {
@@ -118,10 +117,6 @@ export class SearchAutocomplete {
return (this.originalState = this.serializeState());
}
- saveTextLength() {
- return (this.lastTextLength = this.searchInput.val().length);
- }
-
createAutocomplete() {
return this.searchInput.glDropdown({
filterInputBlur: false,
@@ -318,12 +313,16 @@ export class SearchAutocomplete {
}
bindEvents() {
- this.searchInput.on('keydown', this.onSearchInputKeyDown);
+ this.searchInput.on('input', this.onSearchInputChange);
this.searchInput.on('keyup', this.onSearchInputKeyUp);
this.searchInput.on('focus', this.onSearchInputFocus);
this.searchInput.on('blur', this.onSearchInputBlur);
this.clearInput.on('click', this.onClearInputClick);
this.dropdownContent.on('scroll', throttle(this.setScrollFade, 250));
+
+ this.searchInput.on('click', e => {
+ e.stopPropagation();
+ });
}
enableAutocomplete() {
@@ -342,43 +341,19 @@ export class SearchAutocomplete {
}
}
- // Saves last length of the entered text
- onSearchInputKeyDown() {
- return this.saveTextLength();
+ onSearchInputChange() {
+ this.enableAutocomplete();
}
onSearchInputKeyUp(e) {
switch (e.keyCode) {
- case KEYCODE.BACKSPACE:
- // When removing the last character and no badge is present
- if (this.lastTextLength === 1) {
- this.disableAutocomplete();
- }
- // When removing any character from existin value
- if (this.lastTextLength > 1) {
- this.enableAutocomplete();
- }
- break;
case KEYCODE.ESCAPE:
this.restoreOriginalState();
break;
case KEYCODE.ENTER:
this.disableAutocomplete();
break;
- case KEYCODE.UP:
- case KEYCODE.DOWN:
- return;
default:
- // Handle the case when deleting the input value other than backspace
- // e.g. Pressing ctrl + backspace or ctrl + x
- if (this.searchInput.val() === '') {
- this.disableAutocomplete();
- } else {
- // We should display the menu only when input is not empty
- if (e.keyCode !== KEYCODE.ENTER) {
- this.enableAutocomplete();
- }
- }
}
this.wrap.toggleClass('has-value', Boolean(e.target.value));
}
@@ -434,7 +409,7 @@ export class SearchAutocomplete {
disableAutocomplete() {
if (!this.searchInput.hasClass('js-autocomplete-disabled') && this.dropdown.hasClass('show')) {
this.searchInput.addClass('js-autocomplete-disabled');
- this.dropdown.removeClass('show').trigger('hidden.bs.dropdown');
+ this.dropdown.dropdown('toggle');
this.restoreMenu();
}
}
diff --git a/app/graphql/types/todo_target_enum.rb b/app/graphql/types/todo_target_enum.rb
index 9a7391dcd99..8358a86b35c 100644
--- a/app/graphql/types/todo_target_enum.rb
+++ b/app/graphql/types/todo_target_enum.rb
@@ -2,8 +2,10 @@
module Types
class TodoTargetEnum < BaseEnum
- value 'Issue'
- value 'MergeRequest'
- value 'Epic'
+ value 'COMMIT', value: 'Commit', description: 'A Commit'
+ value 'ISSUE', value: 'Issue', description: 'An Issue'
+ value 'MERGEREQUEST', value: 'MergeRequest', description: 'A MergeRequest'
end
end
+
+Types::TodoTargetEnum.prepend_if_ee('::EE::Types::TodoTargetEnum')
diff --git a/app/graphql/types/todo_type.rb b/app/graphql/types/todo_type.rb
index d36daaf7dec..5ce5093c55e 100644
--- a/app/graphql/types/todo_type.rb
+++ b/app/graphql/types/todo_type.rb
@@ -40,7 +40,8 @@ module Types
field :body, GraphQL::STRING_TYPE,
description: 'Body of the todo',
- null: false
+ null: false,
+ calls_gitaly: true # TODO This is only true when `target_type` is `Commit`. See https://gitlab.com/gitlab-org/gitlab/issues/34757#note_234752665
field :state, Types::TodoStateEnum,
description: 'State of the todo',
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 1020c91b245..28adc7ccbdb 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -195,6 +195,17 @@ module TreeHelper
full_name: project.name_with_namespace
}
end
+
+ def directory_download_links(project, ref, archive_prefix)
+ formats = ['zip', 'tar.gz', 'tar.bz2', 'tar']
+
+ formats.map do |fmt|
+ {
+ text: fmt,
+ path: project_archive_path(project, id: tree_join(ref, archive_prefix), format: fmt)
+ }
+ end
+ end
end
TreeHelper.prepend_if_ee('::EE::TreeHelper')
diff --git a/app/services/clusters/update_service.rb b/app/services/clusters/update_service.rb
index 98dd6b26a47..8cb77040b14 100644
--- a/app/services/clusters/update_service.rb
+++ b/app/services/clusters/update_service.rb
@@ -23,7 +23,7 @@ module Clusters
end
def validate_params(cluster)
- if params[:management_project_id]
+ if params[:management_project_id].present?
management_project = management_project_scope(cluster).find_by_id(params[:management_project_id])
unless management_project
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index 96df3cd18fe..e8aff58b505 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -12,11 +12,14 @@
%h5.m-0.dropdown-bold-header= _('Download source code')
.dropdown-menu-content
= render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: nil
- - if directory? && Feature.enabled?(:git_archive_path, default_enabled: true)
- %section.border-top.pt-1.mt-1
- %h5.m-0.dropdown-bold-header= _('Download this directory')
- .dropdown-menu-content
- = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: @path
+ - if Feature.enabled?(:git_archive_path, default_enabled: true)
+ - if vue_file_list_enabled?
+ #js-directory-downloads{ data: { links: directory_download_links(project, ref, archive_prefix).to_json } }
+ - elsif directory?
+ %section.border-top.pt-1.mt-1
+ %h5.m-0.dropdown-bold-header= _('Download this directory')
+ .dropdown-menu-content
+ = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: @path
- if pipeline && pipeline.latest_builds_with_artifacts.any?
%section.border-top.pt-1.mt-1
%h5.m-0.dropdown-bold-header= _('Download artifacts')
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 38422d4533d..127734ddfd7 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -77,15 +77,21 @@
.tree-controls
= render_if_exists 'projects/tree/lock_link'
- = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn'
+ - if vue_file_list_enabled?
+ #js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } }
+ - else
+ = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn'
= render 'projects/find_file_link'
- if can_create_mr_from_fork
= succeed " " do
- if can_collaborate || current_user&.already_forked?(@project)
- = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
- = _('Web IDE')
+ - if vue_file_list_enabled?
+ #js-tree-web-ide-link.d-inline-block
+ - else
+ = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
+ = _('Web IDE')
- else
= link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do
= _('Web IDE')
diff --git a/changelogs/unreleased/32562-dynamic-db-pool-size-in-puma.yml b/changelogs/unreleased/32562-dynamic-db-pool-size-in-puma.yml
new file mode 100644
index 00000000000..e90de9a4d6a
--- /dev/null
+++ b/changelogs/unreleased/32562-dynamic-db-pool-size-in-puma.yml
@@ -0,0 +1,5 @@
+---
+title: 'Puma only: database connection pool now always >= number of worker threads'
+merge_request: 19286
+author:
+type: performance
diff --git a/changelogs/unreleased/33897-make-arrow-buttons-work-within-search-box.yml b/changelogs/unreleased/33897-make-arrow-buttons-work-within-search-box.yml
new file mode 100644
index 00000000000..8132343322b
--- /dev/null
+++ b/changelogs/unreleased/33897-make-arrow-buttons-work-within-search-box.yml
@@ -0,0 +1,5 @@
+---
+title: Fix keyboard shortcuts in header search autocomplete
+merge_request: 18685
+author:
+type: fixed
diff --git a/changelogs/unreleased/34717-update-expired-trial-copy.yml b/changelogs/unreleased/34717-update-expired-trial-copy.yml
new file mode 100644
index 00000000000..d1f77d3e4cb
--- /dev/null
+++ b/changelogs/unreleased/34717-update-expired-trial-copy.yml
@@ -0,0 +1,5 @@
+---
+title: Update expired trial status copy
+merge_request: 18962
+author:
+type: changed
diff --git a/changelogs/unreleased/34757-bugfix-graphql-missing-todo-types.yml b/changelogs/unreleased/34757-bugfix-graphql-missing-todo-types.yml
new file mode 100644
index 00000000000..94ed1963f53
--- /dev/null
+++ b/changelogs/unreleased/34757-bugfix-graphql-missing-todo-types.yml
@@ -0,0 +1,5 @@
+---
+title: Fix errors in GraphQL Todos API due to missing TargetTypeEnum values
+merge_request: 19052
+author:
+type: fixed
diff --git a/changelogs/unreleased/most-affected-projects.yml b/changelogs/unreleased/most-affected-projects.yml
new file mode 100644
index 00000000000..1835f62e533
--- /dev/null
+++ b/changelogs/unreleased/most-affected-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Add endpoint for a group's vulnerable projects
+merge_request: 15317
+author:
+type: added
diff --git a/changelogs/unreleased/remove-empty-github-service-templates.yml b/changelogs/unreleased/remove-empty-github-service-templates.yml
new file mode 100644
index 00000000000..6f5bb3ddcf1
--- /dev/null
+++ b/changelogs/unreleased/remove-empty-github-service-templates.yml
@@ -0,0 +1,5 @@
+---
+title: Remove empty Github service templates from database
+merge_request: 18868
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-set-admin-default-visibilities.yml b/changelogs/unreleased/sh-set-admin-default-visibilities.yml
new file mode 100644
index 00000000000..46e0b5c0ca1
--- /dev/null
+++ b/changelogs/unreleased/sh-set-admin-default-visibilities.yml
@@ -0,0 +1,5 @@
+---
+title: Enforce default, global project and snippet visibilities
+merge_request: 19188
+author:
+type: fixed
diff --git a/config/initializers/database_config.rb b/config/initializers/database_config.rb
new file mode 100644
index 00000000000..d8c2821066b
--- /dev/null
+++ b/config/initializers/database_config.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+# when running on puma, scale connection pool size with the number
+# of threads per worker process
+if defined?(::Puma)
+ db_config = Gitlab::Database.config ||
+ Rails.application.config.database_configuration[Rails.env]
+ puma_options = Puma.cli_config.options
+
+ # We use either the maximum number of threads per worker process, or
+ # the user specified value, whichever is larger.
+ desired_pool_size = [db_config['pool'].to_i, puma_options[:max_threads]].max
+
+ db_config['pool'] = desired_pool_size
+
+ # recreate the connection pool from the new config
+ ActiveRecord::Base.establish_connection(db_config)
+end
diff --git a/db/migrate/20191026124116_set_application_settings_default_project_and_snippet_visibility.rb b/db/migrate/20191026124116_set_application_settings_default_project_and_snippet_visibility.rb
new file mode 100644
index 00000000000..9d19279510a
--- /dev/null
+++ b/db/migrate/20191026124116_set_application_settings_default_project_and_snippet_visibility.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class SetApplicationSettingsDefaultProjectAndSnippetVisibility < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ change_column_null :application_settings, :default_project_visibility, false, 0
+ change_column_default :application_settings, :default_project_visibility, from: nil, to: 0
+
+ change_column_null :application_settings, :default_snippet_visibility, false, 0
+ change_column_default :application_settings, :default_snippet_visibility, from: nil, to: 0
+ end
+end
diff --git a/db/post_migrate/20191021101942_remove_empty_github_service_templates.rb b/db/post_migrate/20191021101942_remove_empty_github_service_templates.rb
new file mode 100644
index 00000000000..64abe93b3e8
--- /dev/null
+++ b/db/post_migrate/20191021101942_remove_empty_github_service_templates.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+## It's expected to delete one record on GitLab.com
+#
+class RemoveEmptyGithubServiceTemplates < ActiveRecord::Migration[5.2]
+ DOWNTIME = false
+
+ class Service < ActiveRecord::Base
+ self.table_name = 'services'
+ self.inheritance_column = :_type_disabled
+
+ serialize :properties, JSON
+ end
+
+ def up
+ relationship.where(properties: {}).delete_all
+ end
+
+ def down
+ relationship.find_or_create_by!(properties: {})
+ end
+
+ private
+
+ def relationship
+ RemoveEmptyGithubServiceTemplates::Service.where(template: true, type: 'GithubService')
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 09149cfbcfe..c4a541824c8 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_10_26_041447) do
+ActiveRecord::Schema.define(version: 2019_10_26_124116) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
@@ -158,8 +158,8 @@ ActiveRecord::Schema.define(version: 2019_10_26_041447) do
t.text "restricted_visibility_levels"
t.boolean "version_check_enabled", default: true
t.integer "max_attachment_size", default: 10, null: false
- t.integer "default_project_visibility"
- t.integer "default_snippet_visibility"
+ t.integer "default_project_visibility", default: 0, null: false
+ t.integer "default_snippet_visibility", default: 0, null: false
t.text "domain_whitelist"
t.boolean "user_oauth_applications", default: true
t.string "after_sign_out_path"
diff --git a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
index 15cdd25e711..568104cb30b 100644
--- a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
+++ b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
@@ -5,7 +5,7 @@ require 'rails/generators'
module Rails
class PostDeploymentMigrationGenerator < Rails::Generators::NamedBase
def create_migration_file
- timestamp = Time.now.strftime('%Y%m%d%H%M%S')
+ timestamp = Time.now.utc.strftime('%Y%m%d%H%M%S')
template "migration.rb", "db/post_migrate/#{timestamp}_#{file_name}.rb"
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1097cc0238d..a0b661c023f 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2421,9 +2421,6 @@ msgstr ""
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
msgstr ""
-msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
-msgstr ""
-
msgid "BillingPlans|Learn more about each plan by visiting our %{pricing_page_link}."
msgstr ""
@@ -2442,18 +2439,15 @@ msgstr ""
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
msgstr ""
-msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
+msgid "BillingPlans|Your GitLab.com Gold trial expired on %{expiration_date}. You can restore access to the Gold features at any time by upgrading below."
msgstr ""
-msgid "BillingPlans|Your GitLab.com trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
+msgid "BillingPlans|Your GitLab.com Gold trial will <strong>expire after %{expiration_date}</strong>. You can retain access to the Gold features by upgrading below."
msgstr ""
msgid "BillingPlans|billed annually at %{price_per_year}"
msgstr ""
-msgid "BillingPlans|features"
-msgstr ""
-
msgid "BillingPlans|frequently asked questions"
msgstr ""
diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb
index d386e489739..0f60cd42748 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -26,6 +26,16 @@ describe 'User uses header search field', :js do
end
end
+ context 'when using the keyboard shortcut' do
+ before do
+ find('body').native.send_keys('s')
+ end
+
+ it 'shows the category search dropdown' do
+ expect(page).to have_selector('.dropdown-header', text: /#{scope_name}/i)
+ end
+ end
+
context 'when clicking the search field' do
before do
page.find('#search.js-autocomplete-disabled').click
@@ -77,15 +87,21 @@ describe 'User uses header search field', :js do
end
context 'when entering text into the search field' do
- before do
+ it 'does not display the category search dropdown' do
page.within('.search-input-wrap') do
fill_in('search', with: scope_name.first(4))
end
- end
- it 'does not display the category search dropdown' do
expect(page).not_to have_selector('.dropdown-header', text: /#{scope_name}/i)
end
+
+ it 'hides the dropdown when there are no results' do
+ page.within('.search-input-wrap') do
+ fill_in('search', with: 'a_search_term_with_no_results')
+ end
+
+ expect(page).not_to have_selector('.dropdown-menu')
+ end
end
end
diff --git a/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap b/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap
new file mode 100644
index 00000000000..31a1cd23060
--- /dev/null
+++ b/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap
@@ -0,0 +1,75 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Repository directory download links component renders downloads links for path app 1`] = `
+<section
+ class="border-top pt-1 mt-1"
+>
+ <h5
+ class="m-0 dropdown-bold-header"
+ >
+ Download this directory
+ </h5>
+
+ <div
+ class="dropdown-menu-content"
+ >
+ <div
+ class="btn-group ml-0 w-100"
+ >
+ <gllink-stub
+ class="btn btn-xs btn-primary"
+ href="http://test.com/?path=app"
+ >
+
+ zip
+
+ </gllink-stub>
+ <gllink-stub
+ class="btn btn-xs"
+ href="http://test.com/?path=app"
+ >
+
+ tar
+
+ </gllink-stub>
+ </div>
+ </div>
+</section>
+`;
+
+exports[`Repository directory download links component renders downloads links for path app/assets 1`] = `
+<section
+ class="border-top pt-1 mt-1"
+>
+ <h5
+ class="m-0 dropdown-bold-header"
+ >
+ Download this directory
+ </h5>
+
+ <div
+ class="dropdown-menu-content"
+ >
+ <div
+ class="btn-group ml-0 w-100"
+ >
+ <gllink-stub
+ class="btn btn-xs btn-primary"
+ href="http://test.com/?path=app/assets"
+ >
+
+ zip
+
+ </gllink-stub>
+ <gllink-stub
+ class="btn btn-xs"
+ href="http://test.com/?path=app/assets"
+ >
+
+ tar
+
+ </gllink-stub>
+ </div>
+ </div>
+</section>
+`;
diff --git a/spec/frontend/repository/components/directory_download_links_spec.js b/spec/frontend/repository/components/directory_download_links_spec.js
new file mode 100644
index 00000000000..4d70b44de08
--- /dev/null
+++ b/spec/frontend/repository/components/directory_download_links_spec.js
@@ -0,0 +1,29 @@
+import { shallowMount } from '@vue/test-utils';
+import DirectoryDownloadLinks from '~/repository/components/directory_download_links.vue';
+
+let vm;
+
+function factory(currentPath) {
+ vm = shallowMount(DirectoryDownloadLinks, {
+ propsData: {
+ currentPath,
+ links: [{ text: 'zip', path: 'http://test.com/' }, { text: 'tar', path: 'http://test.com/' }],
+ },
+ });
+}
+
+describe('Repository directory download links component', () => {
+ afterEach(() => {
+ vm.destroy();
+ });
+
+ it.each`
+ path
+ ${'app'}
+ ${'app/assets'}
+ `('renders downloads links for path $path', ({ path }) => {
+ factory(path);
+
+ expect(vm.element).toMatchSnapshot();
+ });
+});
diff --git a/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js b/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js
index b85e2673624..575d35c50a9 100644
--- a/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js
+++ b/spec/frontend/vue_shared/components/issue/related_issuable_item_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import { formatDate } from '~/lib/utils/datetime_utility';
import { mount, createLocalVue } from '@vue/test-utils';
+import { PathIdSeparator } from 'ee/related_issues/constants';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
import {
defaultAssignees,
@@ -12,7 +13,7 @@ describe('RelatedIssuableItem', () => {
const props = {
idKey: 1,
displayReference: 'gitlab-org/gitlab-test#1',
- pathIdSeparator: '#',
+ pathIdSeparator: PathIdSeparator.Issue,
path: `${gl.TEST_HOST}/path`,
title: 'title',
confidential: true,
diff --git a/spec/initializers/database_config_spec.rb b/spec/initializers/database_config_spec.rb
new file mode 100644
index 00000000000..a5a074f5884
--- /dev/null
+++ b/spec/initializers/database_config_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Database config initializer' do
+ subject do
+ load Rails.root.join('config/initializers/database_config.rb')
+ end
+
+ before do
+ allow(ActiveRecord::Base).to receive(:establish_connection)
+ end
+
+ context "when using Puma" do
+ let(:puma) { double('puma') }
+ let(:puma_options) { { max_threads: 8 } }
+
+ before do
+ stub_const("Puma", puma)
+ allow(puma).to receive_message_chain(:cli_config, :options).and_return(puma_options)
+ end
+
+ context "and no existing pool size is set" do
+ before do
+ stub_database_config(pool_size: nil)
+ end
+
+ it "sets it to the max number of worker threads" do
+ expect { subject }.to change { Gitlab::Database.config['pool'] }.from(nil).to(8)
+ end
+ end
+
+ context "and the existing pool size is smaller than the max number of worker threads" do
+ before do
+ stub_database_config(pool_size: 7)
+ end
+
+ it "sets it to the max number of worker threads" do
+ expect { subject }.to change { Gitlab::Database.config['pool'] }.from(7).to(8)
+ end
+ end
+
+ context "and the existing pool size is larger than the max number of worker threads" do
+ before do
+ stub_database_config(pool_size: 9)
+ end
+
+ it "keeps the configured pool size" do
+ expect { subject }.not_to change { Gitlab::Database.config['pool'] }
+ end
+ end
+ end
+
+ context "when not using Puma" do
+ before do
+ stub_database_config(pool_size: 7)
+ end
+
+ it "does nothing" do
+ expect { subject }.not_to change { Gitlab::Database.config['pool'] }
+ end
+ end
+
+ def stub_database_config(pool_size:)
+ config = {
+ 'adapter' => 'postgresql',
+ 'host' => 'db.host.com',
+ 'pool' => pool_size
+ }.compact
+
+ allow(Gitlab::Database).to receive(:config).and_return(config)
+ end
+end
diff --git a/spec/migrations/remove_empty_github_service_templates_spec.rb b/spec/migrations/remove_empty_github_service_templates_spec.rb
new file mode 100644
index 00000000000..c128c8538db
--- /dev/null
+++ b/spec/migrations/remove_empty_github_service_templates_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20191021101942_remove_empty_github_service_templates.rb')
+
+describe RemoveEmptyGithubServiceTemplates, :migration do
+ subject(:migration) { described_class.new }
+
+ let(:services) do
+ table(:services).tap do |klass|
+ klass.class_eval do
+ serialize :properties, JSON
+ end
+ end
+ end
+
+ before do
+ services.delete_all
+
+ create_service(properties: nil)
+ create_service(properties: {})
+ create_service(properties: { some: :value })
+ create_service(properties: {}, template: false)
+ create_service(properties: {}, type: 'SomeType')
+ end
+
+ def all_service_properties
+ services.where(template: true, type: 'GithubService').pluck(:properties)
+ end
+
+ it 'correctly migrates up and down service templates' do
+ reversible_migration do |migration|
+ migration.before -> do
+ expect(services.count).to eq(5)
+
+ expect(all_service_properties)
+ .to match(a_collection_containing_exactly(nil, {}, { 'some' => 'value' }))
+ end
+
+ migration.after -> do
+ expect(services.count).to eq(4)
+
+ expect(all_service_properties)
+ .to match(a_collection_containing_exactly(nil, { 'some' => 'value' }))
+ end
+ end
+ end
+
+ def create_service(params)
+ data = { template: true, type: 'GithubService' }
+ data.merge!(params)
+
+ services.create!(data)
+ end
+end
diff --git a/spec/requests/api/graphql/current_user/todos_query_spec.rb b/spec/requests/api/graphql/current_user/todos_query_spec.rb
index 6817e37e64b..82deba0d92c 100644
--- a/spec/requests/api/graphql/current_user/todos_query_spec.rb
+++ b/spec/requests/api/graphql/current_user/todos_query_spec.rb
@@ -13,7 +13,7 @@ describe 'Query current user todos' do
let(:fields) do
<<~QUERY
nodes {
- id
+ #{all_graphql_fields_for('todos'.classify)}
}
QUERY
end
@@ -28,6 +28,8 @@ describe 'Query current user todos' do
post_graphql(query, current_user: current_user)
end
+ it_behaves_like 'a working graphql query'
+
it 'contains the expected ids' do
is_expected.to include(
a_hash_including('id' => commit_todo.to_global_id.to_s),
@@ -35,4 +37,12 @@ describe 'Query current user todos' do
a_hash_including('id' => merge_request_todo.to_global_id.to_s)
)
end
+
+ it 'returns Todos for all target types' do
+ is_expected.to include(
+ a_hash_including('targetType' => 'COMMIT'),
+ a_hash_including('targetType' => 'ISSUE'),
+ a_hash_including('targetType' => 'MERGEREQUEST')
+ )
+ end
end
diff --git a/spec/requests/api/graphql/current_user_query_spec.rb b/spec/requests/api/graphql/current_user_query_spec.rb
new file mode 100644
index 00000000000..9db638ea59e
--- /dev/null
+++ b/spec/requests/api/graphql/current_user_query_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'getting project information' do
+ include GraphqlHelpers
+
+ let(:query) do
+ graphql_query_for('currentUser', {}, 'name')
+ end
+
+ subject { graphql_data['currentUser'] }
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ context 'when there is a current_user' do
+ set(:current_user) { create(:user) }
+
+ it_behaves_like 'a working graphql query'
+
+ it { is_expected.to include('name' => current_user.name) }
+ end
+
+ context 'when there is no current_user' do
+ let(:current_user) { nil }
+
+ it_behaves_like 'a working graphql query'
+
+ it { is_expected.to be_nil }
+ end
+end
diff --git a/spec/services/clusters/update_service_spec.rb b/spec/services/clusters/update_service_spec.rb
index 8c2d8c9246e..fdbed4fa5d8 100644
--- a/spec/services/clusters/update_service_spec.rb
+++ b/spec/services/clusters/update_service_spec.rb
@@ -138,6 +138,23 @@ describe Clusters::UpdateService do
expect(cluster.management_project_id).to be_nil
end
end
+
+ context 'cluster already has a management project set' do
+ before do
+ cluster.update!(management_project: create(:project))
+ end
+
+ let(:params) do
+ { management_project_id: '' }
+ end
+
+ it 'unsets management_project_id' do
+ is_expected.to eq(true)
+
+ cluster.reload
+ expect(cluster.management_project_id).to be_nil
+ end
+ end
end
context 'project cluster' do
diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore
index 259148fa18f..259148fa18f 100644..100755
--- a/vendor/gitignore/C++.gitignore
+++ b/vendor/gitignore/C++.gitignore
diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore
index a1c2a238a96..a1c2a238a96 100644..100755
--- a/vendor/gitignore/Java.gitignore
+++ b/vendor/gitignore/Java.gitignore