summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml69
-rw-r--r--app/assets/javascripts/api.js16
-rw-r--r--app/assets/javascripts/issuable_suggestions/components/app.vue2
-rw-r--r--app/assets/javascripts/issuable_suggestions/queries/issues.query.graphql (renamed from app/assets/javascripts/issuable_suggestions/queries/issues.graphql)0
-rw-r--r--app/assets/javascripts/mr_popover/components/mr_popover.vue2
-rw-r--r--app/assets/javascripts/mr_popover/queries/merge_request.query.graphql (renamed from app/assets/javascripts/mr_popover/queries/merge_request.graphql)0
-rw-r--r--app/assets/javascripts/repository/components/breadcrumbs.vue2
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue4
-rw-r--r--app/assets/javascripts/repository/mixins/get_ref.js2
-rw-r--r--app/assets/javascripts/repository/queries/getFiles.query.graphql (renamed from app/assets/javascripts/repository/queries/getFiles.graphql)0
-rw-r--r--app/assets/javascripts/repository/queries/getProjectPath.query.graphql (renamed from app/assets/javascripts/repository/queries/getProjectPath.graphql)0
-rw-r--r--app/assets/javascripts/repository/queries/getProjectShortPath.query.graphql (renamed from app/assets/javascripts/repository/queries/getProjectShortPath.graphql)0
-rw-r--r--app/assets/javascripts/repository/queries/getRef.query.graphql (renamed from app/assets/javascripts/repository/queries/getRef.graphql)0
-rw-r--r--app/assets/stylesheets/framework/blocks.scss1
-rw-r--r--app/assets/stylesheets/framework/typography.scss5
-rw-r--r--app/controllers/projects/jobs_controller.rb6
-rw-r--r--app/finders/notes_finder.rb16
-rw-r--r--app/graphql/types/base_object.rb1
-rw-r--r--app/graphql/types/issue_type.rb2
-rw-r--r--app/graphql/types/label_type.rb1
-rw-r--r--app/graphql/types/merge_request_type.rb2
-rw-r--r--app/graphql/types/namespace_type.rb1
-rw-r--r--app/graphql/types/notes/note_type.rb2
-rw-r--r--app/graphql/types/project_type.rb1
-rw-r--r--app/helpers/markup_helper.rb2
-rw-r--r--app/models/project.rb2
-rw-r--r--app/services/lfs/file_transformer.rb10
-rw-r--r--app/services/users/build_service.rb10
-rw-r--r--app/services/users/update_service.rb17
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--changelogs/unreleased/29775-fix-nested-lists-unnecessary-margin.yml5
-rw-r--r--changelogs/unreleased/52442-minimal-remove-mysql-support.yml5
-rw-r--r--changelogs/unreleased/63227-fix-double-border.yml5
-rw-r--r--changelogs/unreleased/bvl-markdown-graphql.yml5
-rw-r--r--changelogs/unreleased/expose-saml-provider-id-to-users-api.yml5
-rw-r--r--changelogs/unreleased/fix-facivon-url-if-uploads-object-store-enabled.yml5
-rw-r--r--changelogs/unreleased/sh-remove-import-columns-from-projects.yml5
-rw-r--r--config/initializers/1_postgresql_only.rb4
-rw-r--r--danger/commit_messages/Dangerfile12
-rw-r--r--db/post_migrate/20190619175843_remove_import_columns_from_projects.rb14
-rw-r--r--db/schema.rb5
-rw-r--r--doc/administration/environment_variables.md2
-rw-r--r--doc/administration/geo/replication/database.md4
-rw-r--r--doc/administration/gitaly/index.md18
-rw-r--r--doc/administration/index.md1
-rw-r--r--doc/api/container_registry.md5
-rw-r--r--doc/api/users.md15
-rw-r--r--doc/development/architecture.md2
-rw-r--r--doc/development/rake_tasks.md24
-rw-r--r--doc/development/swapping_tables.md8
-rw-r--r--doc/development/testing_guide/end_to_end/quick_start_guide.md4
-rw-r--r--doc/install/database_mysql.md319
-rw-r--r--doc/install/installation.md17
-rw-r--r--doc/install/requirements.md30
-rw-r--r--doc/integration/omniauth.md6
-rw-r--r--doc/raketasks/backup_restore.md31
-rw-r--r--doc/university/README.md1
-rw-r--r--doc/update/README.md5
-rw-r--r--doc/update/mysql_to_postgresql.md6
-rw-r--r--doc/update/patch_versions.md6
-rw-r--r--doc/update/upgrading_from_source.md38
-rw-r--r--doc/user/group/subgroups/index.md13
-rw-r--r--doc/user/project/clusters/serverless/index.md18
-rw-r--r--doc/user/project/issues/issue_data_and_actions.md11
-rw-r--r--doc/user/project/pipelines/settings.md3
-rw-r--r--lib/api/discussions.rb18
-rw-r--r--lib/api/helpers.rb5
-rw-r--r--lib/api/helpers/notes_helpers.rb21
-rw-r--r--lib/api/notes.rb12
-rw-r--r--lib/api/resource_label_events.rb4
-rw-r--r--lib/api/users.rb15
-rw-r--r--lib/gitlab/database/migration_helpers.rb5
-rw-r--r--lib/gitlab/favicon.rb8
-rw-r--r--lib/gitlab/git/repository.rb9
-rw-r--r--lib/gitlab/graphql/markdown_field.rb26
-rw-r--r--lib/gitlab/graphql/markdown_field/resolver.rb22
-rwxr-xr-xscripts/frontend/test.js23
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb14
-rw-r--r--spec/finders/notes_finder_spec.rb26
-rw-r--r--spec/frontend/api_spec.js23
-rw-r--r--spec/graphql/types/issue_type_spec.rb5
-rw-r--r--spec/graphql/types/label_type_spec.rb10
-rw-r--r--spec/graphql/types/merge_request_type_spec.rb17
-rw-r--r--spec/graphql/types/namespace_type_spec.rb9
-rw-r--r--spec/graphql/types/notes/note_type_spec.rb2
-rw-r--r--spec/graphql/types/project_type_spec.rb30
-rw-r--r--spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js180
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb4
-rw-r--r--spec/lib/gitlab/graphql/markdown_field/resolver_spec.rb33
-rw-r--r--spec/lib/gitlab/graphql/markdown_field_spec.rb55
-rw-r--r--spec/support/helpers/query_recorder.rb4
-rw-r--r--spec/support/matchers/be_n_plus_1_query.rb62
-rw-r--r--spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb12
94 files changed, 750 insertions, 740 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0df08597bc2..5b39304444c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,6 @@
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.21-chrome-73.0-node-12.x-yarn-1.16-postgresql-9.6-graphicsmagick-1.3.29"
variables:
- MYSQL_ALLOW_EMPTY_PASSWORD: "1"
RAILS_ENV: "test"
NODE_ENV: "test"
SIMPLECOV: "true"
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 8534b15e16b..529a0de696b 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -10,11 +10,6 @@
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
-.use-mysql: &use-mysql
- services:
- - mysql:5.7
- - redis:alpine
-
.only-schedules-master: &only-schedules-master
only:
- schedules@gitlab-org/gitlab-ce
@@ -94,10 +89,6 @@
<<: *use-pg-10
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.21-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.29"
-.rspec-metadata-mysql: &rspec-metadata-mysql
- <<: *rspec-metadata
- <<: *use-mysql
-
# DB migration, rollback, and seed jobs
.db-migrate-reset: &db-migrate-reset
extends: .dedicated-no-docs-and-no-qa-pull-cache-job
@@ -173,42 +164,6 @@ rspec system pg-10:
<<: *only-schedules-master
parallel: 24
-rspec unit mysql:
- <<: *rspec-metadata-mysql
- <<: *only-schedules-master
- parallel: 20
-
-rspec integration mysql:
- <<: *rspec-metadata-mysql
- <<: *only-schedules-master
- parallel: 6
-
-rspec system mysql:
- <<: *rspec-metadata-mysql
- <<: *only-schedules-master
- parallel: 24
-
-.rspec-mysql-on-demand: &rspec-mysql-on-demand
- only:
- variables:
- - $CI_COMMIT_MESSAGE =~ /\[run mysql\]/i
- - $CI_COMMIT_REF_NAME =~ /mysql/
-
-rspec unit mysql on-demand:
- <<: *rspec-metadata-mysql
- <<: *rspec-mysql-on-demand
- parallel: 20
-
-rspec integration mysql on-demand:
- <<: *rspec-metadata-mysql
- <<: *rspec-mysql-on-demand
- parallel: 6
-
-rspec system mysql on-demand:
- <<: *rspec-metadata-mysql
- <<: *rspec-mysql-on-demand
- parallel: 24
-
rspec-fast-spec-helper:
<<: *rspec-metadata-pg
script:
@@ -226,12 +181,6 @@ rspec quarantine pg:
<<: *rspec-quarantine
allow_failure: true
-rspec quarantine mysql:
- <<: *rspec-metadata-mysql
- <<: *rspec-quarantine
- <<: *only-schedules-master
- allow_failure: true
-
static-analysis:
extends: .dedicated-no-docs-no-db-pull-cache-job
dependencies:
@@ -281,10 +230,6 @@ db:migrate:reset-pg:
<<: *db-migrate-reset
<<: *use-pg
-db:migrate:reset-mysql:
- <<: *db-migrate-reset
- <<: *use-mysql
-
db:check-schema-pg:
<<: *db-migrate-reset
<<: *use-pg
@@ -295,10 +240,6 @@ migration:path-pg:
<<: *migration-paths
<<: *use-pg
-migration:path-mysql:
- <<: *migration-paths
- <<: *use-mysql
-
.db-rollback: &db-rollback
extends: .dedicated-no-docs-and-no-qa-pull-cache-job
script:
@@ -311,22 +252,12 @@ db:rollback-pg:
<<: *db-rollback
<<: *use-pg
-db:rollback-mysql:
- <<: *db-rollback
- <<: *use-mysql
-
gitlab:setup-pg:
<<: *gitlab-setup
<<: *use-pg
dependencies:
- setup-test-env
-gitlab:setup-mysql:
- <<: *gitlab-setup
- <<: *use-mysql
- dependencies:
- - setup-test-env
-
coverage:
# Don't include dedicated-no-docs-no-db-pull-cache-job here since we need to
# download artifacts from all the rspec jobs instead of from setup-test-env only
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 7cebb88f3a4..4f66a5d080c 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -12,6 +12,7 @@ const Api = {
groupProjectsPath: '/api/:version/groups/:id/projects.json',
projectsPath: '/api/:version/projects.json',
projectPath: '/api/:version/projects/:id',
+ forkedProjectsPath: '/api/:version/projects/:id/forks',
projectLabelsPath: '/:namespace_path/:project_path/-/labels',
projectMergeRequestsPath: '/api/:version/projects/:id/merge_requests',
projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
@@ -114,6 +115,21 @@ const Api = {
},
/**
+ * Get all projects for a forked relationship to a specified project
+ * @param {string} projectPath - Path or ID of a project
+ * @param {Object} params - Get request parameters
+ * @returns {Promise} - Request promise
+ */
+ projectForks(projectPath, params) {
+ const url = Api.buildUrl(Api.forkedProjectsPath).replace(
+ ':id',
+ encodeURIComponent(projectPath),
+ );
+
+ return axios.get(url, { params });
+ },
+
+ /**
* Get all Merge Requests for a project, eventually filtering based on
* supplied parameters
* @param projectPath
diff --git a/app/assets/javascripts/issuable_suggestions/components/app.vue b/app/assets/javascripts/issuable_suggestions/components/app.vue
index 575c860851c..d435460e38f 100644
--- a/app/assets/javascripts/issuable_suggestions/components/app.vue
+++ b/app/assets/javascripts/issuable_suggestions/components/app.vue
@@ -4,7 +4,7 @@ import { GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import Suggestion from './item.vue';
-import query from '../queries/issues.graphql';
+import query from '../queries/issues.query.graphql';
export default {
components: {
diff --git a/app/assets/javascripts/issuable_suggestions/queries/issues.graphql b/app/assets/javascripts/issuable_suggestions/queries/issues.query.graphql
index 2384b381344..2384b381344 100644
--- a/app/assets/javascripts/issuable_suggestions/queries/issues.graphql
+++ b/app/assets/javascripts/issuable_suggestions/queries/issues.query.graphql
diff --git a/app/assets/javascripts/mr_popover/components/mr_popover.vue b/app/assets/javascripts/mr_popover/components/mr_popover.vue
index 8e2d8fa816a..c203cb0667c 100644
--- a/app/assets/javascripts/mr_popover/components/mr_popover.vue
+++ b/app/assets/javascripts/mr_popover/components/mr_popover.vue
@@ -3,7 +3,7 @@ import { GlPopover, GlSkeletonLoading } from '@gitlab/ui';
import Icon from '../../vue_shared/components/icon.vue';
import CiIcon from '../../vue_shared/components/ci_icon.vue';
import timeagoMixin from '../../vue_shared/mixins/timeago';
-import query from '../queries/merge_request.graphql';
+import query from '../queries/merge_request.query.graphql';
import { mrStates, humanMRStates } from '../constants';
export default {
diff --git a/app/assets/javascripts/mr_popover/queries/merge_request.graphql b/app/assets/javascripts/mr_popover/queries/merge_request.query.graphql
index 37d4bc88a69..37d4bc88a69 100644
--- a/app/assets/javascripts/mr_popover/queries/merge_request.graphql
+++ b/app/assets/javascripts/mr_popover/queries/merge_request.query.graphql
diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue
index 6eca015036f..0d4d431855c 100644
--- a/app/assets/javascripts/repository/components/breadcrumbs.vue
+++ b/app/assets/javascripts/repository/components/breadcrumbs.vue
@@ -1,6 +1,6 @@
<script>
import getRefMixin from '../mixins/get_ref';
-import getProjectShortPath from '../queries/getProjectShortPath.graphql';
+import getProjectShortPath from '../queries/getProjectShortPath.query.graphql';
export default {
apollo: {
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index 0357a0e44c3..891e3fe9d16 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -3,8 +3,8 @@ import { GlLoadingIcon } from '@gitlab/ui';
import createFlash from '~/flash';
import { sprintf, __ } from '../../../locale';
import getRefMixin from '../../mixins/get_ref';
-import getFiles from '../../queries/getFiles.graphql';
-import getProjectPath from '../../queries/getProjectPath.graphql';
+import getFiles from '../../queries/getFiles.query.graphql';
+import getProjectPath from '../../queries/getProjectPath.query.graphql';
import TableHeader from './header.vue';
import TableRow from './row.vue';
import ParentRow from './parent_row.vue';
diff --git a/app/assets/javascripts/repository/mixins/get_ref.js b/app/assets/javascripts/repository/mixins/get_ref.js
index b06087d6f42..a43e0e91bcf 100644
--- a/app/assets/javascripts/repository/mixins/get_ref.js
+++ b/app/assets/javascripts/repository/mixins/get_ref.js
@@ -1,4 +1,4 @@
-import getRef from '../queries/getRef.graphql';
+import getRef from '../queries/getRef.query.graphql';
export default {
apollo: {
diff --git a/app/assets/javascripts/repository/queries/getFiles.graphql b/app/assets/javascripts/repository/queries/getFiles.query.graphql
index ef924fde556..ef924fde556 100644
--- a/app/assets/javascripts/repository/queries/getFiles.graphql
+++ b/app/assets/javascripts/repository/queries/getFiles.query.graphql
diff --git a/app/assets/javascripts/repository/queries/getProjectPath.graphql b/app/assets/javascripts/repository/queries/getProjectPath.query.graphql
index 74e73e07577..74e73e07577 100644
--- a/app/assets/javascripts/repository/queries/getProjectPath.graphql
+++ b/app/assets/javascripts/repository/queries/getProjectPath.query.graphql
diff --git a/app/assets/javascripts/repository/queries/getProjectShortPath.graphql b/app/assets/javascripts/repository/queries/getProjectShortPath.query.graphql
index 34eb26598c2..34eb26598c2 100644
--- a/app/assets/javascripts/repository/queries/getProjectShortPath.graphql
+++ b/app/assets/javascripts/repository/queries/getProjectShortPath.query.graphql
diff --git a/app/assets/javascripts/repository/queries/getRef.graphql b/app/assets/javascripts/repository/queries/getRef.query.graphql
index 58c09844c3f..58c09844c3f 100644
--- a/app/assets/javascripts/repository/queries/getRef.graphql
+++ b/app/assets/javascripts/repository/queries/getRef.query.graphql
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 65c0ee74c60..53a8f7c483a 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -199,7 +199,6 @@
&.user-cover-block {
padding: 24px 0 0;
- border-bottom: 1px solid $border-color;
.nav-links {
width: 100%;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 9e1431963d9..7baab478034 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -221,6 +221,11 @@
ol {
padding: 0;
margin: 0 0 16px;
+
+ ul,
+ ol {
+ margin-bottom: 0;
+ }
}
ul:dir(rtl),
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 2a4933e7bc2..d7c0039b234 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -31,8 +31,12 @@ class Projects::JobsController < Projects::ApplicationController
@builds
end
@builds = @builds.includes([
- { pipeline: :project },
+ { pipeline: [:project, :user] },
+ :job_artifacts_archive,
+ :metadata,
+ :trigger_request,
:project,
+ :user,
:tags
])
@builds = @builds.page(params[:page]).per(30).without_count
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 817aac8b5d5..8f610d7dddb 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -34,8 +34,10 @@ class NotesFinder
target_type = @params[:target_type]
target_id = @params[:target_id]
+ target_iid = @params[:target_iid]
- return @target = nil unless target_type && target_id
+ return @target = nil unless target_type
+ return @target = nil unless target_id || target_iid
@target =
if target_type == "commit"
@@ -43,12 +45,22 @@ class NotesFinder
@project.commit(target_id)
end
else
- noteables_for_type(target_type).find(target_id)
+ noteables_for_type_by_id(target_type, target_id, target_iid)
end
end
private
+ def noteables_for_type_by_id(type, id, iid)
+ query = if id
+ { id: id }
+ else
+ { iid: iid }
+ end
+
+ noteables_for_type(type).find_by!(query) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
def init_collection
if target
notes_on_target
diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb
index e40059c46bb..dad16898ba6 100644
--- a/app/graphql/types/base_object.rb
+++ b/app/graphql/types/base_object.rb
@@ -4,6 +4,7 @@ module Types
class BaseObject < GraphQL::Schema::Object
prepend Gitlab::Graphql::Present
prepend Gitlab::Graphql::ExposePermissions
+ prepend Gitlab::Graphql::MarkdownField
field_class Types::BaseField
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index f2365499eee..8b208cab1df 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -14,7 +14,9 @@ module Types
field :iid, GraphQL::ID_TYPE, null: false
field :title, GraphQL::STRING_TYPE, null: false
+ markdown_field :title_html, null: true
field :description, GraphQL::STRING_TYPE, null: true
+ markdown_field :description_html, null: true
field :state, IssueStateEnum, null: false
field :reference, GraphQL::STRING_TYPE, null: false, method: :to_reference do
diff --git a/app/graphql/types/label_type.rb b/app/graphql/types/label_type.rb
index ccd466edc1a..50eb1b89c61 100644
--- a/app/graphql/types/label_type.rb
+++ b/app/graphql/types/label_type.rb
@@ -5,6 +5,7 @@ module Types
graphql_name 'Label'
field :description, GraphQL::STRING_TYPE, null: true
+ markdown_field :description_html, null: true
field :title, GraphQL::STRING_TYPE, null: false
field :color, GraphQL::STRING_TYPE, null: false
field :text_color, GraphQL::STRING_TYPE, null: false
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index dac4c24cf10..577ccd48ef8 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -15,7 +15,9 @@ module Types
field :id, GraphQL::ID_TYPE, null: false
field :iid, GraphQL::STRING_TYPE, null: false
field :title, GraphQL::STRING_TYPE, null: false
+ markdown_field :title_html, null: true
field :description, GraphQL::STRING_TYPE, null: true
+ markdown_field :description_html, null: true
field :state, MergeRequestStateEnum, null: false
field :created_at, Types::TimeType, null: false
field :updated_at, Types::TimeType, null: false
diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb
index f6d91320e50..62feccaa660 100644
--- a/app/graphql/types/namespace_type.rb
+++ b/app/graphql/types/namespace_type.rb
@@ -12,6 +12,7 @@ module Types
field :full_path, GraphQL::ID_TYPE, null: false
field :description, GraphQL::STRING_TYPE, null: true
+ markdown_field :description_html, null: true
field :visibility, GraphQL::STRING_TYPE, null: true
field :lfs_enabled, GraphQL::BOOLEAN_TYPE, null: true, method: :lfs_enabled?
field :request_access_enabled, GraphQL::BOOLEAN_TYPE, null: true
diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb
index 85c55d16ac2..fe54a45c7dc 100644
--- a/app/graphql/types/notes/note_type.rb
+++ b/app/graphql/types/notes/note_type.rb
@@ -35,6 +35,8 @@ module Types
method: :note,
description: "The content note itself"
+ markdown_field :body_html, null: true, method: :note
+
field :created_at, Types::TimeType, null: false
field :updated_at, Types::TimeType, null: false
field :discussion, Types::Notes::DiscussionType, null: true, description: "The discussion this note is a part of"
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 81914b70c7f..ac957eafafc 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -17,6 +17,7 @@ module Types
field :name, GraphQL::STRING_TYPE, null: false
field :description, GraphQL::STRING_TYPE, null: true
+ markdown_field :description_html, null: true
field :tag_list, GraphQL::STRING_TYPE, null: true
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index bf894360a2e..8ccb39f8444 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -278,7 +278,7 @@ module MarkupHelper
def prepare_for_rendering(html, context = {})
return '' unless html.present?
- context.merge!(
+ context.reverse_merge!(
current_user: (current_user if defined?(current_user)),
# RelativeLinkFilter
diff --git a/app/models/project.rb b/app/models/project.rb
index 351d08eaf63..7851f37116c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -55,8 +55,6 @@ class Project < ApplicationRecord
VALID_MIRROR_PORTS = [22, 80, 443].freeze
VALID_MIRROR_PROTOCOLS = %w(http https ssh git).freeze
- ignore_column :import_status, :import_jid, :import_error
-
cache_markdown_field :description, pipeline: :description
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
diff --git a/app/services/lfs/file_transformer.rb b/app/services/lfs/file_transformer.rb
index d1746399908..88f59b820a4 100644
--- a/app/services/lfs/file_transformer.rb
+++ b/app/services/lfs/file_transformer.rb
@@ -4,6 +4,14 @@ module Lfs
# Usage: Calling `new_file` check to see if a file should be in LFS and
# return a transformed result with `content` and `encoding` to commit.
#
+ # The `repository` passed to the initializer can be a Repository or
+ # a DesignManagement::Repository (an EE-specific class that inherits
+ # from Repository).
+ #
+ # The `repository_type` property will be one of the types named in
+ # `Gitlab::GlRepository.types`, and is recorded on the `LfsObjectsProject`
+ # in order to identify the repository location of the blob.
+ #
# For LFS an LfsObject linked to the project is stored and an LFS
# pointer returned. If the file isn't in LFS the untransformed content
# is returned to save in the commit.
@@ -52,7 +60,7 @@ module Lfs
end
def cached_attributes
- @cached_attributes ||= Gitlab::Git::AttributesAtRefParser.new(repository, branch_name)
+ @cached_attributes ||= repository.attributes_at(branch_name)
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb
index 30f7743c56e..026bcfcdaf4 100644
--- a/app/services/users/build_service.rb
+++ b/app/services/users/build_service.rb
@@ -5,10 +5,12 @@ module Users
delegate :user_default_internal_regex_enabled?,
:user_default_internal_regex_instance,
to: :'Gitlab::CurrentSettings.current_application_settings'
+ attr_reader :identity_params
def initialize(current_user, params = {})
@current_user = current_user
@params = params.dup
+ @identity_params = params.slice(*identity_attributes)
end
def execute(skip_authorization: false)
@@ -26,10 +28,8 @@ module Users
end
end
- identity_attrs = params.slice(*identity_params)
-
- unless identity_attrs.empty?
- user.identities.build(identity_attrs)
+ unless identity_params.empty?
+ user.identities.build(identity_params)
end
user
@@ -37,7 +37,7 @@ module Users
private
- def identity_params
+ def identity_attributes
[:extern_uid, :provider]
end
diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb
index 0b00bd135eb..15c13a452ad 100644
--- a/app/services/users/update_service.rb
+++ b/app/services/users/update_service.rb
@@ -3,11 +3,13 @@
module Users
class UpdateService < BaseService
include NewUserNotifier
+ attr_reader :user, :identity_params
def initialize(current_user, params = {})
@current_user = current_user
@user = params.delete(:user)
@status_params = params.delete(:status)
+ @identity_params = params.slice(*identity_attributes)
@params = params.dup
end
@@ -15,8 +17,8 @@ module Users
yield(@user) if block_given?
user_exists = @user.persisted?
-
assign_attributes
+ assign_identity
if @user.save(validate: validate) && update_status
notify_success(user_exists)
@@ -55,7 +57,18 @@ module Users
params.reject! { |key, _| read_only.include?(key.to_sym) }
end
- @user.assign_attributes(params) unless params.empty?
+ @user.assign_attributes(params.except(*identity_attributes)) unless params.empty? # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ def assign_identity
+ return unless identity_params.present?
+
+ identity = user.identities.find_or_create_by(provider: identity_params[:provider]) # rubocop: disable CodeReuse/ActiveRecord
+ identity.update(identity_params)
+ end
+
+ def identity_attributes
+ [:provider, :extern_uid]
end
end
end
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index a71bfd624e4..b3a73030859 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -9,7 +9,7 @@
= auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
.user-profile
- .cover-block.user-cover-block
+ .cover-block.user-cover-block{ class: [('border-bottom' if profile_tabs.empty?)] }
.cover-controls
- if @user == current_user
= link_to profile_path, class: 'btn btn-default has-tooltip', title: s_('UserProfile|Edit profile'), 'aria-label': 'Edit profile' do
diff --git a/changelogs/unreleased/29775-fix-nested-lists-unnecessary-margin.yml b/changelogs/unreleased/29775-fix-nested-lists-unnecessary-margin.yml
new file mode 100644
index 00000000000..e7e43c54bab
--- /dev/null
+++ b/changelogs/unreleased/29775-fix-nested-lists-unnecessary-margin.yml
@@ -0,0 +1,5 @@
+---
+title: Fix nested lists unnecessary margin
+merge_request: 29775
+author: Kuba Kopeć
+type: fixed
diff --git a/changelogs/unreleased/52442-minimal-remove-mysql-support.yml b/changelogs/unreleased/52442-minimal-remove-mysql-support.yml
new file mode 100644
index 00000000000..f1a50657383
--- /dev/null
+++ b/changelogs/unreleased/52442-minimal-remove-mysql-support.yml
@@ -0,0 +1,5 @@
+---
+title: Remove MySQL support
+merge_request: 29790
+author:
+type: removed
diff --git a/changelogs/unreleased/63227-fix-double-border.yml b/changelogs/unreleased/63227-fix-double-border.yml
new file mode 100644
index 00000000000..6cc4040d333
--- /dev/null
+++ b/changelogs/unreleased/63227-fix-double-border.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Double Border in Profile Page
+merge_request: 29784
+author: Yoginth <@yo>
+type: fixed
diff --git a/changelogs/unreleased/bvl-markdown-graphql.yml b/changelogs/unreleased/bvl-markdown-graphql.yml
new file mode 100644
index 00000000000..c2432b3ae81
--- /dev/null
+++ b/changelogs/unreleased/bvl-markdown-graphql.yml
@@ -0,0 +1,5 @@
+---
+title: Render GFM in GraphQL
+merge_request: 29700
+author:
+type: added
diff --git a/changelogs/unreleased/expose-saml-provider-id-to-users-api.yml b/changelogs/unreleased/expose-saml-provider-id-to-users-api.yml
new file mode 100644
index 00000000000..6f0e4f4cf7f
--- /dev/null
+++ b/changelogs/unreleased/expose-saml-provider-id-to-users-api.yml
@@ -0,0 +1,5 @@
+---
+title: Expose saml_provider_id in the users API
+merge_request: 14045
+author:
+type: added
diff --git a/changelogs/unreleased/fix-facivon-url-if-uploads-object-store-enabled.yml b/changelogs/unreleased/fix-facivon-url-if-uploads-object-store-enabled.yml
new file mode 100644
index 00000000000..4e6d9c087ef
--- /dev/null
+++ b/changelogs/unreleased/fix-facivon-url-if-uploads-object-store-enabled.yml
@@ -0,0 +1,5 @@
+---
+title: 'Fix favicon path with uploads of object store'
+merge_request: 29482
+author: Roger Meier
+type: fixed
diff --git a/changelogs/unreleased/sh-remove-import-columns-from-projects.yml b/changelogs/unreleased/sh-remove-import-columns-from-projects.yml
new file mode 100644
index 00000000000..f4052b2bef5
--- /dev/null
+++ b/changelogs/unreleased/sh-remove-import-columns-from-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Remove import columns from projects table
+merge_request: 29863
+author:
+type: performance
diff --git a/config/initializers/1_postgresql_only.rb b/config/initializers/1_postgresql_only.rb
new file mode 100644
index 00000000000..be771bebf47
--- /dev/null
+++ b/config/initializers/1_postgresql_only.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+raise "PostgreSQL is the only supported database from GitLab 12.1" unless
+ Gitlab::Database.postgresql?
diff --git a/danger/commit_messages/Dangerfile b/danger/commit_messages/Dangerfile
index bdb4343b1d6..ec494635f02 100644
--- a/danger/commit_messages/Dangerfile
+++ b/danger/commit_messages/Dangerfile
@@ -80,7 +80,7 @@ def unicode_emoji_regex
))x
end
-def lint_commit(commit)
+def lint_commit(commit) # rubocop:disable Metrics/AbcSize
# For now we'll ignore merge commits, as getting rid of those is a problem
# separate from enforcing good commit messages.
return false if commit.message.start_with?('Merge branch')
@@ -114,6 +114,16 @@ def lint_commit(commit)
)
end
+ # Fail if a suggestion commit is used and squash is not enabled
+ if commit.message.start_with?('Apply suggestion to') && !gitlab.mr_json['squash']
+ fail_commit(
+ commit,
+ 'If you are applying suggestions, squash needs to be enabled in the merge request'
+ )
+
+ failures = true
+ end
+
unless subject_starts_with_capital?(subject)
fail_commit(commit, 'The commit subject must start with a capital letter')
failures = true
diff --git a/db/post_migrate/20190619175843_remove_import_columns_from_projects.rb b/db/post_migrate/20190619175843_remove_import_columns_from_projects.rb
new file mode 100644
index 00000000000..85f776ac99c
--- /dev/null
+++ b/db/post_migrate/20190619175843_remove_import_columns_from_projects.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class RemoveImportColumnsFromProjects < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def change
+ remove_column :projects, :import_status, :string
+ remove_column :projects, :import_jid, :string
+ remove_column :projects, :import_error, :text
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 7a7319c132e..4e333633a8b 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: 20190613030606) do
+ActiveRecord::Schema.define(version: 20190619175843) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -2595,11 +2595,9 @@ ActiveRecord::Schema.define(version: 20190613030606) do
t.integer "visibility_level", default: 0, null: false
t.boolean "archived", default: false, null: false
t.string "avatar"
- t.string "import_status"
t.integer "star_count", default: 0, null: false
t.string "import_type"
t.string "import_source"
- t.text "import_error"
t.boolean "shared_runners_enabled", default: true, null: false
t.string "runners_token"
t.string "build_coverage_regex"
@@ -2621,7 +2619,6 @@ ActiveRecord::Schema.define(version: 20190613030606) do
t.boolean "only_allow_merge_if_all_discussions_are_resolved"
t.boolean "printing_merge_request_link_enabled", default: true, null: false
t.integer "auto_cancel_pending_pipelines", default: 1, null: false
- t.string "import_jid"
t.integer "cached_markdown_version"
t.text "delete_error"
t.datetime "last_repository_updated_at"
diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md
index 864a84b33e0..874b1f3c80d 100644
--- a/doc/administration/environment_variables.md
+++ b/doc/administration/environment_variables.md
@@ -42,7 +42,7 @@ The list of `GITLAB_DATABASE_XXX` variables that you can set is:
Variable | Default value | Overridden by `DATABASE_URL`?
-------- | ------------- | -----------------------------
-`GITLAB_DATABASE_ADAPTER` | `postgresql` (for MySQL use `mysql2`) | Yes
+`GITLAB_DATABASE_ADAPTER` | `postgresql` | Yes
`GITLAB_DATABASE_DATABASE` | `gitlab_#{ENV['RAILS_ENV']` | Yes
`GITLAB_DATABASE_USERNAME` | `root` | Yes
`GITLAB_DATABASE_PASSWORD` | None | Yes
diff --git a/doc/administration/geo/replication/database.md b/doc/administration/geo/replication/database.md
index 1e5a56c3f4e..6658c37752a 100644
--- a/doc/administration/geo/replication/database.md
+++ b/doc/administration/geo/replication/database.md
@@ -489,10 +489,6 @@ work:
gitlab-ctl reconfigure
```
-## MySQL replication
-
-MySQL replication is not supported for Geo.
-
## Troubleshooting
Read the [troubleshooting document](troubleshooting.md).
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 53a85dfad6c..da8f1ee1529 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -432,6 +432,24 @@ gitaly_enabled=false
When you run `service gitlab restart` Gitaly will be disabled on this
particular machine.
+## Eliminating NFS altogether
+
+If you are planning to use Gitaly without NFS for your storage needs
+and want to eliminate NFS from your environment altogether, there are
+a few things that you need to do:
+
+ 1. Make sure the [`git` user home directory](https://docs.gitlab.com/omnibus/settings/configuration.html#moving-the-home-directory-for-a-user) is on local disk.
+ 1. Configure [database lookup of SSH keys](https://docs.gitlab.com/ce/administration/operations/fast_ssh_key_lookup.html)
+ to eliminate the need for a shared authorized_keys file.
+ 1. Configure [object storage for job artifacts](https://docs.gitlab.com/ce/administration/job_artifacts.html#using-object-storage)
+ including [live tracing](https://docs.gitlab.com/ce/administration/job_traces.html#new-live-trace-architecture).
+ 1. Configure [object storage for LFS objects](https://docs.gitlab.com/ce/workflow/lfs/lfs_administration.html#storing-lfs-objects-in-remote-object-storage).
+ 1. Configure [object storage for uploads](https://docs.gitlab.com/ce/administration/uploads.html#using-object-storage-core-only).
+
+NOTE: **Note:** One current feature of GitLab still requires a shared directory (NFS): [GitLab Pages](../../user/project/pages/index.md).
+There is [work in progress](https://gitlab.com/gitlab-org/gitlab-pages/issues/196)
+to eliminate the need for NFS to support GitLab Pages.
+
## Troubleshooting Gitaly in production
Since GitLab 11.6, Gitaly comes with a command-line tool called
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 06d900b152d..602eecb9746 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -33,7 +33,6 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Install](../install/README.md): Requirements, directory structures, and installation methods.
- [Database load balancing](database_load_balancing.md): Distribute database queries among multiple database servers. **[STARTER ONLY]**
- - [Omnibus support for external MySQL DB](https://docs.gitlab.com/omnibus/settings/database.html#using-a-mysql-database-management-server-enterprise-edition-only): Omnibus package supports configuring an external MySQL database. **[STARTER ONLY]**
- [Omnibus support for log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only) **[STARTER ONLY]**
- [High Availability](high_availability/README.md): Configure multiple servers for scaling or high availability.
- [Installing GitLab HA on Amazon Web Services (AWS)](../install/aws/index.md): Set up GitLab High Availability on Amazon AWS.
diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md
index 1f17af1f1e9..64ea15bca93 100644
--- a/doc/api/container_registry.md
+++ b/doc/api/container_registry.md
@@ -145,6 +145,9 @@ DELETE /projects/:id/registry/repositories/:repository_id/tags/:tag_name
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags/v10.0.0"
```
+This action does not delete blobs. In order to delete them and recycle disk space,
+[run the garbage collection](https://docs.gitlab.com/omnibus/maintenance/README.html#removing-unused-layers-not-referenced-by-manifests).
+
## Delete repository tags in bulk
Delete repository tags in bulk based on given criteria.
@@ -174,6 +177,8 @@ This API call performs the following operations:
These operations are executed asynchronously and it might
take time to get executed. You can run this at most
once an hour for a given container repository.
+This action does not delete blobs. In order to delete them and recycle disk space,
+[run the garbage collection](https://docs.gitlab.com/omnibus/maintenance/README.html#removing-unused-layers-not-referenced-by-manifests).
NOTE: **Note:**
Due to a [Docker Distribution deficiency](https://gitlab.com/gitlab-org/gitlab-ce/issues/21405),
diff --git a/doc/api/users.md b/doc/api/users.md
index 47028c679b8..4bc0335ae33 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -2,6 +2,8 @@
## List users
+Active users = Total accounts - Blocked users
+
Get a list of users.
This function takes pagination parameters `page` and `per_page` to restrict the list of users.
@@ -257,7 +259,8 @@ Parameters:
"two_factor_enabled": true,
"external": false,
"private_profile": false,
- "highest_role":10
+ "shared_runners_minutes_limit": 133
+ "extra_shared_runners_minutes_limit": 133
}
```
@@ -290,6 +293,7 @@ Parameters:
- `projects_limit` (optional) - Number of projects user can create
- `extern_uid` (optional) - External UID
- `provider` (optional) - External provider name
+- `group_id_for_saml` (optional) - ID of group where SAML has been configured
- `bio` (optional) - User's biography
- `location` (optional) - User's location
- `public_email` (optional) - The public email of the user
@@ -299,6 +303,8 @@ Parameters:
- `external` (optional) - Flags the user as external - true or false(default)
- `avatar` (optional) - Image file for user's avatar
- `private_profile` (optional) - User's profile is private - true or false
+- `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user
+- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user
## User modification
@@ -322,6 +328,7 @@ Parameters:
- `projects_limit` - Limit projects each user can create
- `extern_uid` - External UID
- `provider` - External provider name
+- `group_id_for_saml` (optional) - ID of group where SAML has been configured
- `bio` - User's biography
- `location` (optional) - User's location
- `public_email` (optional) - The public email of the user
@@ -329,6 +336,8 @@ Parameters:
- `can_create_group` (optional) - User can create groups - true or false
- `skip_reconfirmation` (optional) - Skip reconfirmation - true or false (default)
- `external` (optional) - Flags the user as external - true or false(default)
+- `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user
+- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user
- `avatar` (optional) - Image file for user's avatar
- `private_profile` (optional) - User's profile is private - true or false
@@ -1150,8 +1159,6 @@ settings page.
POST /users/:user_id/impersonation_tokens
```
-Parameters:
-
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user_id` | integer | yes | The ID of the user |
@@ -1255,4 +1262,4 @@ Example response:
Please note that `last_activity_at` is deprecated, please use `last_activity_on`.
-[gemojione-index]: https://github.com/jonathanwiesel/gemojione/blob/master/config/index.json
+[gemojione-index]: https://github.com/jonathanwiesel/gemojione/blob/master/config/index.json \ No newline at end of file
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index d817d513173..a43a40152db 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -695,7 +695,7 @@ We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/ha
[runner-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/runner.md
[database-migrations-omnibus]: https://docs.gitlab.com/omnibus/settings/database.html#disabling-automatic-database-migration
[database-migrations-charts]: https://docs.gitlab.com/charts/charts/gitlab/migrations/
-[database-migrations-source]: ../update/upgrading_from_source.md#14-install-libs-migrations-etc
+[database-migrations-source]: ../update/upgrading_from_source.md#13-install-libs-migrations-etc
[certificate-management-omnibus]: https://docs.gitlab.com/omnibus/settings/ssl.html
[certificate-management-charts]: https://docs.gitlab.com/charts/installation/tls.html
[certificate-management-source]: ../install/installation.md#using-https
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index 28a12572961..4fc10b6af5c 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -80,30 +80,6 @@ There are a few environment flags you can pass to change how projects are seeded
- `LARGE_PROJECTS`: defaults to false. If set will clone 6 large projects to help with testing.
- `FORK`: defaults to false. If set to `true` will fork `torvalds/linux` five times. Can also be set to an existing project full_path and it will fork that instead.
-### Notes for MySQL
-
-Since the seeds would contain various UTF-8 characters, such as emojis or so,
-we'll need to make sure that we're using `utf8mb4` for all the encoding
-settings and `utf8mb4_unicode_ci` for collation. Please check
-[MySQL utf8mb4 support](../install/database_mysql.md#mysql-utf8mb4-support)
-
-Make sure that `config/database.yml` has `encoding: utf8mb4`, too.
-
-Next, we'll need to update the schema to make the indices fit:
-
-``` shell
-sed -i 's/limit: 255/limit: 191/g' db/schema.rb
-```
-
-Then run the setup script:
-
-``` shell
-bundle exec rake setup
-```
-
-To make sure that indices still fit. You could find great details in:
-[How to support full Unicode in MySQL databases](https://mathiasbynens.be/notes/mysql-utf8mb4)
-
## Run tests
In order to run the test you can use the following commands:
diff --git a/doc/development/swapping_tables.md b/doc/development/swapping_tables.md
index 29cd6a43aff..5c62900dbff 100644
--- a/doc/development/swapping_tables.md
+++ b/doc/development/swapping_tables.md
@@ -39,14 +39,6 @@ PostgreSQL you can use the `reset_pk_sequence!` method like so:
reset_pk_sequence!('events')
```
-For MySQL however you need to do run the following:
-
-```ruby
-amount = Event.pluck('COALESCE(MAX(id), 1)').first
-
-execute "ALTER TABLE events AUTO_INCREMENT = #{amount}"
-```
-
Failure to reset the primary keys will result in newly created rows starting
with an ID value of 1. Depending on the existing data this can then lead to
duplicate key constraints from popping up, preventing users from creating new
diff --git a/doc/development/testing_guide/end_to_end/quick_start_guide.md b/doc/development/testing_guide/end_to_end/quick_start_guide.md
index d33ef0fc229..f96c85be1ba 100644
--- a/doc/development/testing_guide/end_to_end/quick_start_guide.md
+++ b/doc/development/testing_guide/end_to_end/quick_start_guide.md
@@ -247,7 +247,7 @@ module QA
[@new_label_same_scope, @new_label_different_scope].each do |label|
Resource::Label.fabricate_via_api! do |l|
- l.project = issue.project.id
+ l.project = issue.project
l.title = label
end
end
@@ -414,7 +414,7 @@ def api_get_path
end
def api_post_path
- "/projects/#{project}/labels"
+ "/projects/#{project.id}/labels"
end
def api_post_body
diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md
deleted file mode 100644
index cbb3b766b4e..00000000000
--- a/doc/install/database_mysql.md
+++ /dev/null
@@ -1,319 +0,0 @@
----
-type: reference
----
-
-# Database MySQL
-
-NOTE: **Note:**
-We do not recommend using MySQL due to various issues.
-For example, there have been bugs with case
-[(in)sensitivity](https://dev.mysql.com/doc/refman/5.7/en/case-sensitivity.html).
-
-Bugs relating to case sensitivity:
-
-- <https://bugs.mysql.com/bug.php?id=65830>
-- <https://bugs.mysql.com/bug.php?id=50909>
-- <https://bugs.mysql.com/bug.php?id=65830>
-- <https://bugs.mysql.com/bug.php?id=63164>
-
-## Initial database setup
-
-```sh
-# Install the database packages
-sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
-
-# Ensure you have MySQL version 5.6 or later
-mysql --version
-
-# Pick a MySQL root password (can be anything), type it and press enter
-# Retype the MySQL root password and press enter
-
-# Secure your installation
-sudo mysql_secure_installation
-
-# Login to MySQL
-mysql -u root -p
-
-# Type the MySQL root password
-
-# Create a user for GitLab
-# do not type the 'mysql>', this is part of the prompt
-# change $password in the command below to a real password you pick
-mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password';
-
-# Ensure you can use the InnoDB engine which is necessary to support long indexes
-# If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off"
-mysql> SET storage_engine=INNODB;
-
-# If you have MySQL < 5.7.7 and want to enable utf8mb4 character set support with your GitLab install, you must set the following NOW:
-mysql> SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda, innodb_large_prefix=1;
-
-# If you use MySQL with replication, or just have MySQL configured with binary logging, you need to run the following to allow the use of `TRIGGER`:
-mysql> SET GLOBAL log_bin_trust_function_creators = 1;
-
-# Create the GitLab production database
-mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_general_ci`;
-
-# Grant the GitLab user necessary permissions on the database
-mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES, REFERENCES, TRIGGER ON `gitlabhq_production`.* TO 'git'@'localhost';
-
-# Quit the database session
-mysql> \q
-
-# Try connecting to the new database with the new user
-sudo -u git -H mysql -u git -p -D gitlabhq_production
-
-# Type the password you replaced $password with earlier
-
-# You should now see a 'mysql>' prompt
-
-# Quit the database session
-mysql> \q
-```
-
-You are done installing the database for now and can go back to the rest of the installation.
-Please proceed to the rest of the installation **before** running through the steps below.
-
-### `log_bin_trust_function_creators`
-
-If you use MySQL with replication, or just have MySQL configured with binary logging, all of your MySQL servers will need to have `log_bin_trust_function_creators` enabled to allow the use of `TRIGGER` in migrations. You have already set this global variable in the steps above, but to make it persistent, add the following to your `my.cnf` file:
-
-```
-log_bin_trust_function_creators=1
-```
-
-### MySQL utf8mb4 support
-
-After installation or upgrade, remember to [convert any new tables](#tables-and-data-conversion-to-utf8mb4) to `utf8mb4`/`utf8mb4_general_ci`.
-
----
-
-GitLab 8.14 has introduced [a feature](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7420) requiring `utf8mb4` encoding to be supported in your GitLab MySQL Database, which is not the case if you have set up your database before GitLab 8.16.
-
-Follow the below instructions to ensure you use the most up to date requirements for your GitLab MySQL Database.
-
-**We are about to do the following:**
-
-- Ensure you can enable `utf8mb4` encoding and `utf8mb4_general_ci` collation for your GitLab DB, tables and data.
-- Convert your GitLab tables and data from `utf8`/`utf8_general_ci` to `utf8mb4`/`utf8mb4_general_ci`.
-
-### Check for utf8mb4 support
-
-#### Check for InnoDB File-Per-Table Tablespaces
-
-We need to check, enable and maybe convert your existing GitLab DB tables to the [InnoDB File-Per-Table Tablespaces](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) as a prerequisite for supporting **utfb8mb4 with long indexes** required by recent GitLab databases.
-
- # Login to MySQL
- mysql -u root -p
-
- # Type the MySQL root password
- mysql > use gitlabhq_production;
-
- # Check your MySQL version is >= 5.5.3 (GitLab requires 5.5.14+)
- mysql > SHOW VARIABLES LIKE 'version';
- +---------------+-----------------+
- | Variable_name | Value |
- +---------------+-----------------+
- | version | 5.5.53-0+deb8u1 |
- +---------------+-----------------+
-
- # Note where is your MySQL data dir for later:
- mysql > select @@datadir;
- +----------------+
- | @@datadir |
- +----------------+
- | /var/lib/mysql |
- +----------------+
-
- # Note whether your MySQL server runs with innodb_file_per_table ON or OFF:
- mysql> SELECT @@innodb_file_per_table;
- +-------------------------+
- | @@innodb_file_per_table |
- +-------------------------+
- | 1 |
- +-------------------------+
-
- # You can now quit the database session
- mysql> \q
-
-> You need **MySQL 5.5.3 or later** to perform this update.
-
-Whatever the results of your checks above, we now need to check if your GitLab database has been created using [InnoDB File-Per-Table Tablespaces](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) (i.e. `innodb_file_per_table` was set to **1** at initial setup time).
-
-NOTE: **Note:**
-This setting is [enabled by default](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_file_per_table) since MySQL 5.6.6.
-
- # Run this command with root privileges, replace the data dir if different:
- sudo ls -lh /var/lib/mysql/gitlabhq_production/*.ibd | wc -l
-
- # Run this command with root privileges, replace the data dir if different:
- sudo ls -lh /var/lib/mysql/gitlabhq_production/*.frm | wc -l
-
-- **Case 1: a result > 0 for both commands**
-
-Congratulations, your GitLab database uses the right InnoDB tablespace format.
-
-However, you must still ensure that any **future tables** created by GitLab will still use the right format:
-
-- If `SELECT @@innodb_file_per_table` returned **1** previously, your server is running correctly.
-
- > It's however a requirement to check *now* that this setting is indeed persisted in your [`my.cnf`](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) file!
-
-- If `SELECT @@innodb_file_per_table` returned **0** previously, your server is not running correctly.
-
- > [Enable innodb_file_per_table](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) by running in a MySQL session as root the command `SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda;` and persist the two settings in your [`my.cnf`](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) file.
-
-Now, if you have a **different result** returned by the 2 commands above, it means you have a **mix of tables format** uses in your GitLab database. This can happen if your MySQL server had different values for `innodb_file_per_table` in its life and you updated GitLab at different moments with those inconsistent values. So keep reading.
-
-- **Case 2: a result equals to "0" OR not the same result for both commands**
-
-Unfortunately, none or only some of your GitLab database tables use the GitLab requirement of [InnoDB File-Per-Table Tablespaces](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html).
-
-Let's enable what we need on the running server:
-
- # Login to MySQL
- mysql -u root -p
-
- # Type the MySQL root password
-
- # Enable innodb_file_per_table and set innodb_file_format on the running server:
- mysql > SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda;
-
- # You can now quit the database session
- mysql> \q
-
-> Now, **persist** [innodb_file_per_table](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) and [innodb_file_format](https://dev.mysql.com/doc/refman/5.7/en/innodb-file-format-enabling.html) in your `my.cnf` file.
-
-Ensure at this stage that your GitLab instance is indeed **stopped**.
-
-Now, let's convert all the GitLab database tables to the new tablespace format:
-
- # Login to MySQL
- mysql -u root -p
-
- # Type the MySQL root password
- mysql > use gitlabhq_production;
-
- # Safety check: you should still have those values set as follows:
- mysql> SELECT @@innodb_file_per_table, @@innodb_file_format;
- +-------------------------+----------------------+
- | @@innodb_file_per_table | @@innodb_file_format |
- +-------------------------+----------------------+
- | 1 | Barracuda |
- +-------------------------+----------------------+
-
- mysql > SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` ENGINE=InnoDB;') AS 'Copy & run these SQL statements:' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="gitlabhq_production" AND TABLE_TYPE="BASE TABLE";
-
- # If previous query returned results, copy & run all shown SQL statements
-
- # You can now quit the database session
- mysql> \q
-
----
-
-#### Check for proper InnoDB File Format, Row Format, Large Prefix and tables conversion
-
-We need to check, enable and probably convert your existing GitLab DB tables to use the [Barracuda InnoDB file format](https://dev.mysql.com/doc/refman/5.7/en/innodb-file-format.html), the [DYNAMIC row format](https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_dynamic_row_format) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) as a second prerequisite for supporting **utfb8mb4 with long indexes** used by recent GitLab databases.
-
- # Login to MySQL
- mysql -u root -p
-
- # Type the MySQL root password
- mysql > use gitlabhq_production;
-
- # Set innodb_file_format and innodb_large_prefix on the running server:
- # Note: These are the default settings only for MySQL 5.7.7 and later.
-
- mysql > SET GLOBAL innodb_file_format=Barracuda, innodb_large_prefix=1;
-
- # Your DB must be (still) using utf8/utf8_general_ci as default encoding and collation.
- # We will NOT change the default encoding and collation on the DB in order to support future GitLab migrations creating tables
- # that require "long indexes support" on installations using MySQL <= 5.7.9.
- # However, when such migrations occur, you will have to follow this guide again to convert the newly created tables to the proper encoding/collation.
-
- # This should return the following:
- mysql> SELECT @@character_set_database, @@collation_database;
- +--------------------------+----------------------+
- | @@character_set_database | @@collation_database |
- +--------------------------+----------------------+
- | utf8 | utf8_general_ci |
- +--------------------------+----------------------+
-
-> Now, ensure that [innodb_file_format](https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) and [innodb_large_prefix](http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix) are **persisted** in your `my.cnf` file.
-
-#### Tables and data conversion to utf8mb4
-
-Now that you have a persistent MySQL setup, you can safely upgrade tables after setup or upgrade time:
-
- # Convert tables not using ROW_FORMAT DYNAMIC:
-
- mysql> SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` ROW_FORMAT=DYNAMIC;') AS 'Copy & run these SQL statements:' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="gitlabhq_production" AND TABLE_TYPE="BASE TABLE" AND ROW_FORMAT!="Dynamic";
-
- # !! If previous query returned results, copy & run all shown SQL statements
-
- # Convert tables/columns not using utf8mb4/utf8mb4_general_ci as encoding/collation:
-
- mysql > SET foreign_key_checks = 0;
-
- mysql > SELECT CONCAT('ALTER TABLE `', TABLE_NAME,'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;') AS 'Copy & run these SQL statements:' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA="gitlabhq_production" AND TABLE_COLLATION != "utf8mb4_general_ci" AND TABLE_TYPE="BASE TABLE";
-
- # !! If previous query returned results, copy & run all shown SQL statements
-
- # Turn foreign key checks back on
- mysql > SET foreign_key_checks = 1;
-
- # You can now quit the database session
- mysql> \q
-
-Ensure your GitLab database configuration file uses a proper connection encoding and collation:
-
-`sudo -u git -H editor config/database.yml`
-
- production:
- adapter: mysql2
- encoding: utf8mb4
- collation: utf8mb4_general_ci
-
-[Restart your GitLab instance](../administration/restart_gitlab.md).
-
-## MySQL strings limits
-
-After installation or upgrade, remember to run the `add_limits_mysql` Rake task:
-
-**Omnibus GitLab installations**
-
-```sh
-sudo gitlab-rake add_limits_mysql
-```
-
-**Installations from source**
-
-```sh
-bundle exec rake add_limits_mysql RAILS_ENV=production
-```
-
-The `text` type in MySQL has a different size limit than the `text` type in
-PostgreSQL. In MySQL `text` columns are limited to ~65kB, whereas in PostgreSQL
-`text` columns are limited up to ~1GB!
-
-The `add_limits_mysql` Rake task converts some important `text` columns in the
-GitLab database to `longtext` columns, which can persist values of up to 4GB
-(sometimes less if the value contains multibyte characters).
-
-Details can be found in the [PostgreSQL][postgres-text-type] and
-[MySQL](https://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html) manuals.
-
-[postgres-text-type]: http://www.postgresql.org/docs/9.2/static/datatype-character.html
-[ce-38152]: https://gitlab.com/gitlab-org/gitlab-ce/issues/38152
-
-<!-- ## Troubleshooting
-
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support and to avoid doc comments with
-questions that you know someone might ask.
-
-Each scenario can be a third-level heading, e.g. `### Getting error message X`.
-If you have none to add when creating a doc, leave this section in place
-but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/install/installation.md b/doc/install/installation.md
index e73bf2c16ff..4c1021d097f 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -293,10 +293,9 @@ sudo adduser --disabled-login --gecos 'GitLab' git
## 6. Database
-We recommend using a PostgreSQL database. For MySQL, see the [MySQL setup guide](database_mysql.md).
-
NOTE: **Note:**
-Because we need to make use of extensions and concurrent index removal, you need at least PostgreSQL 9.2.
+Starting from GitLab 12.1, only PostgreSQL is supported. Because we need to make
+use of extensions and concurrent index removal, you need at least PostgreSQL 9.2.
1. Install the database packages:
@@ -502,13 +501,8 @@ If you want to use HTTPS, see [Using HTTPS](#using-https) for the additional ste
### Configure GitLab DB Settings
```sh
-# PostgreSQL only:
sudo -u git cp config/database.yml.postgresql config/database.yml
-# MySQL only:
-sudo -u git cp config/database.yml.mysql config/database.yml
-
-# PostgreSQL only:
# Remove host, username, and password lines from config/database.yml.
# Once modified, the `production` settings will be as follows:
#
@@ -520,7 +514,7 @@ sudo -u git cp config/database.yml.mysql config/database.yml
#
sudo -u git -H editor config/database.yml
-# MySQL and remote PostgreSQL only:
+# Remote PostgreSQL only:
# Update username/password in config/database.yml.
# You only need to adapt the production settings (first part).
# If you followed the database guide then please do as follows:
@@ -528,7 +522,6 @@ sudo -u git -H editor config/database.yml
# You can keep the double quotes around the password
sudo -u git -H editor config/database.yml
-# PostgreSQL and MySQL:
# Make config/database.yml readable to git only
sudo -u git -H chmod o-rwx config/database.yml
```
@@ -544,11 +537,7 @@ Make sure you have `bundle` (run `bundle -v`):
- `< 2.x`.
```sh
-# For PostgreSQL (note, the option says "without ... mysql")
sudo -u git -H bundle install --deployment --without development test mysql aws kerberos
-
-# Or if you use MySQL (note, the option says "without ... postgres")
-sudo -u git -H bundle install --deployment --without development test postgres aws kerberos
```
NOTE: **Note:**
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index ee3d17704a2..92122fca7dd 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -104,32 +104,10 @@ installation (e.g. the number of users, projects, etc).
We currently support the following databases:
-- PostgreSQL (highly recommended)
-- MySQL/MariaDB (strongly discouraged, not all GitLab features are supported, no support for [MySQL/MariaDB GTID](https://mariadb.com/kb/en/mariadb/gtid/))
-
-We highly recommend the use of PostgreSQL instead of MySQL/MariaDB as not all
-features of GitLab work with MySQL/MariaDB:
-
-1. MySQL support for subgroups was [dropped with GitLab 9.3][post].
- See [issue #30472][30472] for more information.
-1. Geo does [not support MySQL](../administration/geo/replication/database.md). This means no supported Disaster Recovery solution if using MySQL. **[PREMIUM ONLY]**
-1. [Zero downtime migrations](../update/README.md#upgrading-without-downtime) do not work with MySQL.
-1. [Database load balancing](../administration/database_load_balancing.md) is
- supported only for PostgreSQL. **[PREMIUM ONLY]**
-1. GitLab [optimizes the loading of dashboard events](https://gitlab.com/gitlab-org/gitlab-ce/issues/31806) using [PostgreSQL LATERAL JOINs](https://blog.heapanalytics.com/postgresqls-powerful-new-join-type-lateral/).
-1. In general, SQL optimized for PostgreSQL may run much slower in MySQL due to
- differences in query planners. For example, subqueries that work well in PostgreSQL
- may not be [performant in MySQL](https://dev.mysql.com/doc/refman/5.7/en/optimizing-subqueries.html).
-1. Binary column index length is limited to 20 bytes. This is accomplished with [a hack](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/mysql_set_length_for_binary_indexes.rb).
-1. MySQL requires a variety of hacks to increase limits on various columns, [for example](https://gitlab.com/gitlab-org/gitlab-ce/issues/49583).
-1. [The milestone filter runs slower queries on MySQL](https://gitlab.com/gitlab-org/gitlab-ce/issues/51173#note_99391731).
-1. We expect this list to grow over time.
-
-Existing users using GitLab with MySQL/MariaDB are advised to
-[migrate to PostgreSQL](../update/mysql_to_postgresql.md) instead.
-
-[30472]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472
-[post]: https://about.gitlab.com/2017/06/22/gitlab-9-3-released/#dropping-support-for-subgroups-in-mysql
+- PostgreSQL
+
+Support for MySQL was removed in GitLab 12.1. Existing users using GitLab with
+MySQL/MariaDB are advised to [migrate to PostgreSQL](../update/mysql_to_postgresql.md) before upgrading.
### PostgreSQL Requirements
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index a13e9f73f48..bf5debc7694 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -194,11 +194,7 @@ from the Omniauth provider's documentation.
gem "omniauth-your-auth-provider"
-- If you're using MySQL, install the new Omniauth provider gem by running the following command:
-
- sudo -u git -H bundle install --without development test postgres --path vendor/bundle --no-deployment
-
-- If you're using PostgreSQL, install the new Omniauth provider gem by running the following command:
+- Install the new Omniauth provider gem by running the following command:
sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 764916ca82d..c7aa22b11f8 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -936,5 +936,36 @@ A similar strategy can be employed for the remaining features - by removing the
data that cannot be decrypted, GitLab can be brought back into working order,
and the lost data can be manually replaced.
+### Container Registry push failures after restoring from a backup
+
+If you use the [Container Registry](../user/project/container_registry.md), you
+may see pushes to the registry fail after restoring your backup on an Omnibus
+GitLab instance after restoring the registry data.
+
+These failures will mention permission issues in the registry logs, like:
+
+```
+level=error
+msg="response completed with error"
+err.code=unknown
+err.detail="filesystem: mkdir /var/opt/gitlab/gitlab-rails/shared/registry/docker/registry/v2/repositories/...: permission denied"
+err.message="unknown error"
+```
+
+This is caused by the restore being run as the unprivileged user `git` which was
+unable to assign the correct ownership to the registry files during the restore
+([issue 62759](https://gitlab.com/gitlab-org/gitlab-ce/issues/62759 "Incorrect permissions on registry filesystem after restore")).
+
+To get your registry working again:
+
+```bash
+sudo chown -R registry:registry /var/opt/gitlab/gitlab-rails/shared/registry/docker
+```
+
+NOTE: **Note:**
+If you have changed the default filesystem location for the registry, you will
+want to run the chown against your custom location instead of
+`/var/opt/gitlab/gitlab-rails/shared/registry/docker`.
+
[reconfigure GitLab]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
[restart GitLab]: ../administration/restart_gitlab.md#installations-from-source
diff --git a/doc/university/README.md b/doc/university/README.md
index c116e54ad48..1d2c0f2068a 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -149,7 +149,6 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
1. [How to Install GitLab with Omnibus - Video](https://www.youtube.com/watch?v=Q69YaOjqNhg)
1. [Installing GitLab - Online Course](https://courses.platzi.com/classes/git-gitlab/concepto/part-1/part-3/material/)
1. [Using a Non-Packaged PostgreSQL Database](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#using-a-non-packaged-postgresql-database-management-server)
-1. [Using a MySQL Database](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#using-a-mysql-database-management-server-enterprise-edition-only)
1. [Installing GitLab on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/)
1. [Installing GitLab on Digital Ocean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/)
diff --git a/doc/update/README.md b/doc/update/README.md
index 58227b6fd3f..974982da5d0 100644
--- a/doc/update/README.md
+++ b/doc/update/README.md
@@ -49,8 +49,7 @@ However, for this to work there are the following requirements:
- You have to use [post-deployment
migrations](../development/post_deployment_migrations.md) (included in
zero downtime update steps below).
-- You are using PostgreSQL. If you are using MySQL please look at the release
- post to see if downtime is required.
+- You are using PostgreSQL. Starting from GitLab 12.1, MySQL is not supported.
Most of the time you can safely upgrade from a patch release to the next minor
release if the patch release is not the latest. For example, upgrading from
@@ -140,8 +139,6 @@ possible.
- [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating
your database from MySQL to PostgreSQL.
-- [MySQL installation guide](../install/database_mysql.md) contains additional
- information about configuring GitLab to work with a MySQL database.
- [Restoring from backup after a failed upgrade](restore_after_failure.md)
- [Upgrading PostgreSQL Using Slony](upgrading_postgresql_using_slony.md), for
upgrading a PostgreSQL database with minimal downtime.
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index b83abcd36f7..4b13e41ab53 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -1,5 +1,5 @@
---
-last_updated: 2019-03-27
+last_updated: 2019-06-18
---
# Migrating from MySQL to PostgreSQL
@@ -9,6 +9,10 @@ migrate it to a PostgreSQL database.
## Requirements
+NOTE: **Note:**
+Support for MySQL was removed in GitLab 12.1. This procedure should be performed
+**before** installing GitLab 12.1.
+
[pgloader](http://pgloader.io) 3.4.1+ is required.
You can install it directly from your distribution, for example in
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index f2df4277ca8..4300d6d56c7 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -13,8 +13,6 @@ You can select the tag in the version dropdown in the top left corner of GitLab
### 0. Backup
It's useful to make a backup just in case things go south:
-(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab
-user on the database version)
```bash
cd /home/git/gitlab
@@ -48,12 +46,8 @@ sudo -u git -H git checkout LATEST_TAG -b LATEST_TAG
```bash
cd /home/git/gitlab
-# PostgreSQL
sudo -u git -H bundle install --without development test mysql --deployment
-# MySQL
-sudo -u git -H bundle install --without development test postgres --deployment
-
# Optional: clean up old gems
sudo -u git -H bundle clean
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index a05af7350de..023dc7d6de3 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -239,29 +239,7 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
sudo -u git -H make
```
-### 12. Update MySQL permissions
-
-If you are using MySQL you need to grant the GitLab user the necessary
-permissions on the database:
-
-```bash
-mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
-```
-
-If you use MySQL with replication, or just have MySQL configured with binary logging,
-you will need to also run the following on all of your MySQL servers:
-
-```bash
-mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
-```
-
-You can make this setting permanent by adding it to your `my.cnf`:
-
-```
-log_bin_trust_function_creators=1
-```
-
-### 13. Update configuration files
+### 12. Update configuration files
#### New configuration options for `gitlab.yml`
@@ -335,18 +313,13 @@ For Ubuntu 16.04.1 LTS:
sudo systemctl daemon-reload
```
-### 14. Install libs, migrations, etc.
+### 13. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
-# PostgreSQL installations (note: the line below states '--without mysql')
sudo -u git -H bundle install --deployment --without development test mysql aws kerberos
-# MySQL installations (note: the line below states '--without postgres')
-sudo -u git -H bundle install --deployment --without development test postgres aws kerberos
-
-
# Optional: clean up old gems
sudo -u git -H bundle clean
@@ -364,17 +337,14 @@ sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:c
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
-**MySQL installations**: Run through the `MySQL strings limits` and `Tables and
-data conversion to utf8mb4` [tasks](../install/database_mysql.md).
-
-### 15. Start application
+### 14. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
-### 16. Check application status
+### 15. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 79ae94dd9ef..1c6cca049c5 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -5,8 +5,7 @@ type: reference, howto, concepts
# Subgroups
NOTE: **Note:**
-[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2772) in GitLab 9.0. Not available when using MySQL as external
-database (support removed in GitLab 9.3 [due to performance reasons](https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600)).
+[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2772) in GitLab 9.0.
Subgroups, also known as nested groups or hierarchical groups, allow you to have up to 20
levels of groups.
@@ -21,16 +20,6 @@ By using subgroups you can do the following:
- **Make it easier to manage people and control visibility.** Give people
different [permissions](../../permissions.md#group-members-permissions) depending on their group [membership](#membership).
-## Database Requirements
-
-Subgroups are only supported when you use PostgreSQL. Supporting subgroups on MySQL in an
-efficient way is not possible due to MySQL's limitations.
-
-See the following links for more information:
-
-- <https://gitlab.com/gitlab-org/gitlab-ce/issues/30472>
-- <https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10885>
-
## Overview
A group can have many subgroups inside it, and at the same time a group can have
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index 0ab6d8e112f..3be5bfeaddc 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -85,12 +85,22 @@ on a given project but not both. The current implementation makes use of a `serv
## Using an existing installation of Knative
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/58941) in GitLab 12.0.
+
NOTE: **Note:**
-The "invocations" monitoring feature of GitLab serverless will not work when adding an existing installation of Knative.
+The "invocations" monitoring feature of GitLab serverless will not work when
+adding an existing installation of Knative.
+
+It is also possible to use GitLab Serverless with an existing Kubernetes
+cluster which already has Knative installed.
+
+Simply:
-It is also possible to use GitLab Serverless with an existing Kubernetes cluster which already has Knative installed.
-Simply follow the steps to [add an existing Kubernetes cluster](../index.md#adding-an-existing-kubernetes-cluster)
-and then follow the steps to deploy [functions](#deploying-functions) or [serverless applications](#deploying-serverless-applications) onto your cluster.
+1. Follow the steps to
+ [add an existing Kubernetes cluster](../index.md#adding-an-existing-kubernetes-cluster).
+1. Follow the steps to deploy [functions](#deploying-functions)
+ or [serverless applications](#deploying-serverless-applications) onto your
+ cluster.
## Deploying functions
diff --git a/doc/user/project/issues/issue_data_and_actions.md b/doc/user/project/issues/issue_data_and_actions.md
index ac26b672d99..2103f331aa2 100644
--- a/doc/user/project/issues/issue_data_and_actions.md
+++ b/doc/user/project/issues/issue_data_and_actions.md
@@ -149,12 +149,19 @@ The plain text title and description of the issue fill the top center of the iss
The description fully supports [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm),
allowing many formatting options.
-##### 16.1 Zoom Call Links
+##### Zoom call links
-Including a link to a Zoom call in the description of an issue will result in a "Join Zoom meeting" button at the top of the issue, just under the header. To remove the button, edit the description and remove the Zoom call link.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/62966) in GitLab 12.0.
+
+Including a link to a [Zoom](https://zoom.us) call in the description of an issue
+results in a **Join Zoom meeting** button at the top of the issue, just under the header.
+
+For example:
![Link Zoom Call in Issue](img/link_zoom_call_in_issue.png)
+To remove the button, edit the description and remove the Zoom call link.
+
#### 17. Mentions
You can mention a user or a group present in your GitLab instance with `@username` or
diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md
index 16f48c462eb..24e15a37a40 100644
--- a/doc/user/project/pipelines/settings.md
+++ b/doc/user/project/pipelines/settings.md
@@ -24,7 +24,8 @@ in `.gitlab-ci.yml`.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/28919) in GitLab 12.0.
-NOTE: **Note**: As of GitLab 12.0, newly created projects will automaticallyl have a default
+NOTE: **Note**:
+As of GitLab 12.0, newly created projects will automatically have a default
`git depth` value of `50`.
It is possible to limit the number of changes that GitLab CI/CD will fetch when cloning
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index 693172b7d08..cc62ce22a1b 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -25,7 +25,7 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
get ":id/#{noteables_path}/:noteable_id/discussions" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
notes = noteable.notes
.inc_relations_for_view
@@ -47,7 +47,7 @@ module API
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
end
get ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
notes = readable_discussion_notes(noteable, params[:discussion_id])
if notes.empty?
@@ -82,7 +82,7 @@ module API
end
end
post ":id/#{noteables_path}/:noteable_id/discussions" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
type = params[:position] ? 'DiffNote' : 'DiscussionNote'
id_key = noteable.is_a?(Commit) ? :commit_id : :noteable_id
@@ -112,7 +112,7 @@ module API
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
end
get ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
notes = readable_discussion_notes(noteable, params[:discussion_id])
if notes.empty?
@@ -132,7 +132,7 @@ module API
optional :created_at, type: String, desc: 'The creation date of the note'
end
post ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
notes = readable_discussion_notes(noteable, params[:discussion_id])
first_note = notes.first
@@ -166,7 +166,7 @@ module API
requires :note_id, type: Integer, desc: 'The ID of a note'
end
get ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
get_note(noteable, params[:note_id])
end
@@ -183,7 +183,7 @@ module API
exactly_one_of :body, :resolved
end
put ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
if params[:resolved].nil?
update_note(noteable, params[:note_id])
@@ -201,7 +201,7 @@ module API
requires :note_id, type: Integer, desc: 'The ID of a note'
end
delete ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
delete_note(noteable, params[:note_id])
end
@@ -216,7 +216,7 @@ module API
requires :resolved, type: Boolean, desc: 'Mark discussion resolved/unresolved'
end
put ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
resolve_discussion(noteable, params[:discussion_id], params[:resolved])
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index fd258e3edbc..6382d295f79 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -183,11 +183,6 @@ module API
user_project.commit_by(oid: id)
end
- def find_project_snippet(id)
- finder_params = { project: user_project }
- SnippetsFinder.new(current_user, finder_params).find(id)
- end
-
# rubocop: disable CodeReuse/ActiveRecord
def find_merge_request_with_access(iid, access_level = :read_merge_request)
merge_request = user_project.merge_requests.find_by!(iid: iid)
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index a068de4361c..b03ac7deb71 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -73,14 +73,23 @@ module API
"read_#{noteable.class.to_s.underscore}".to_sym
end
- def find_noteable(parent, noteables_str, noteable_id)
- noteable = public_send("find_#{parent}_#{noteables_str.singularize}", noteable_id) # rubocop:disable GitlabSecurity/PublicSend
+ def find_noteable(parent_type, parent_id, noteable_type, noteable_id)
+ params = params_by_noteable_type_and_id(noteable_type, noteable_id)
- readable = can?(current_user, noteable_read_ability_name(noteable), noteable)
-
- return not_found!(noteables_str) unless readable
+ noteable = NotesFinder.new(user_project, current_user, params).target
+ noteable = nil unless can?(current_user, noteable_read_ability_name(noteable), noteable)
+ noteable || not_found!(noteable_type)
+ end
- noteable
+ def params_by_noteable_type_and_id(type, id)
+ target_type = type.name.underscore
+ { target_type: target_type }.tap do |h|
+ if %w(issue merge_request).include?(target_type)
+ h[:target_iid] = id
+ else
+ h[:target_id] = id
+ end
+ end
end
def noteable_parent(noteable)
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 416cf39d3ec..9381f045144 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -15,8 +15,6 @@ module API
requires :id, type: String, desc: "The ID of a #{parent_type}"
end
resource parent_type.pluralize.to_sym, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- noteables_str = noteable_type.to_s.underscore.pluralize
-
desc "Get a list of #{noteable_type.to_s.downcase} notes" do
success Entities::Note
end
@@ -30,7 +28,7 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
get ":id/#{noteables_str}/:noteable_id/notes" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
# We exclude notes that are cross-references and that cannot be viewed
# by the current user. By doing this exclusion at this level and not
@@ -56,7 +54,7 @@ module API
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
end
get ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
get_note(noteable, params[:note_id])
end
@@ -69,7 +67,7 @@ module API
optional :created_at, type: String, desc: 'The creation date of the note'
end
post ":id/#{noteables_str}/:noteable_id/notes" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
opts = {
note: params[:body],
@@ -96,7 +94,7 @@ module API
requires :body, type: String, desc: 'The content of a note'
end
put ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
update_note(noteable, params[:note_id])
end
@@ -109,7 +107,7 @@ module API
requires :note_id, type: Integer, desc: 'The ID of a note'
end
delete ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
- noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ noteable = find_noteable(parent_type, params[:id], noteable_type, params[:noteable_id])
delete_note(noteable, params[:note_id])
end
diff --git a/lib/api/resource_label_events.rb b/lib/api/resource_label_events.rb
index 448bef12cec..505a6c68c9c 100644
--- a/lib/api/resource_label_events.rb
+++ b/lib/api/resource_label_events.rb
@@ -26,7 +26,7 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
get ":id/#{eventables_str}/:eventable_id/resource_label_events" do
- eventable = find_noteable(parent_type, eventables_str, params[:eventable_id])
+ eventable = find_noteable(parent_type, params[:id], eventable_type, params[:eventable_id])
events = eventable.resource_label_events.includes(:label, :user)
present paginate(events), with: Entities::ResourceLabelEvent
@@ -42,7 +42,7 @@ module API
requires :eventable_id, types: [Integer, String], desc: 'The ID of the eventable'
end
get ":id/#{eventables_str}/:eventable_id/resource_label_events/:event_id" do
- eventable = find_noteable(parent_type, eventables_str, params[:eventable_id])
+ eventable = find_noteable(parent_type, params[:id], eventable_type, params[:eventable_id])
event = eventable.resource_label_events.find(params[:event_id])
present event, with: Entities::ResourceLabelEvent
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 6afeebb6890..9ab5fa8d0bd 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -209,22 +209,9 @@ module API
.where.not(id: user.id).count > 0
user_params = declared_params(include_missing: false)
- identity_attrs = user_params.slice(:provider, :extern_uid)
-
- if identity_attrs.any?
- identity = user.identities.find_by(provider: identity_attrs[:provider])
-
- if identity
- identity.update(identity_attrs)
- else
- identity = user.identities.build(identity_attrs)
- identity.save
- end
- end
user_params[:password_expires_at] = Time.now if user_params[:password].present?
-
- result = ::Users::UpdateService.new(current_user, user_params.except(:extern_uid, :provider).merge(user: user)).execute
+ result = ::Users::UpdateService.new(current_user, user_params.merge(user: user)).execute
if result[:status] == :success
present user, with: Entities::UserPublic, current_user: current_user
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 1b5cd0fbb07..0b12e862ded 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -225,7 +225,10 @@ module Gitlab
# here is based on Rails' foreign_key_name() method, which unfortunately
# is private so we can't rely on it directly.
def concurrent_foreign_key_name(table, column)
- "fk_#{Digest::SHA256.hexdigest("#{table}_#{column}_fk").first(10)}"
+ identifier = "#{table}_#{column}_fk"
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
+
+ "fk_#{hashed_identifier}"
end
# Long-running migrations may take more than the timeout allowed by
diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb
index 6e31064f737..519213e143c 100644
--- a/lib/gitlab/favicon.rb
+++ b/lib/gitlab/favicon.rb
@@ -5,8 +5,8 @@ module Gitlab
class << self
def main
image_name =
- if appearance_favicon.exists?
- appearance_favicon.url
+ if appearance.favicon.exists?
+ appearance.favicon_path
elsif Gitlab::Utils.to_boolean(ENV['CANARY'])
'favicon-yellow.png'
elsif Rails.env.development?
@@ -57,10 +57,6 @@ module Gitlab
def appearance
Gitlab::SafeRequestStore[:appearance] ||= (Appearance.current || Appearance.new)
end
-
- def appearance_favicon
- appearance.favicon
- end
end
end
end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 8a2e711ec4e..a6739f12280 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -683,17 +683,16 @@ module Gitlab
attributes(path)[name]
end
- # Check .gitattributes for a given ref
+ # Returns parsed .gitattributes for a given ref
#
- # This only checks the root .gitattributes file,
+ # This only parses the root .gitattributes file,
# it does not traverse subfolders to find additional .gitattributes files
#
# This method is around 30 times slower than `attributes`, which uses
# `$GIT_DIR/info/attributes`. Consider caching AttributesAtRefParser
# and reusing that for multiple calls instead of this method.
- def attributes_at(ref, file_path)
- parser = AttributesAtRefParser.new(self, ref)
- parser.attributes(file_path)
+ def attributes_at(ref)
+ AttributesAtRefParser.new(self, ref)
end
def languages(ref = nil)
diff --git a/lib/gitlab/graphql/markdown_field.rb b/lib/gitlab/graphql/markdown_field.rb
new file mode 100644
index 00000000000..7be6810f7ba
--- /dev/null
+++ b/lib/gitlab/graphql/markdown_field.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module MarkdownField
+ extend ActiveSupport::Concern
+
+ prepended do
+ def self.markdown_field(name, **kwargs)
+ if kwargs[:resolver].present? || kwargs[:resolve].present?
+ raise ArgumentError, 'Only `method` is allowed to specify the markdown field'
+ end
+
+ method_name = kwargs.delete(:method) || name.to_s.sub(/_html$/, '')
+ kwargs[:resolve] = Gitlab::Graphql::MarkdownField::Resolver.new(method_name.to_sym).proc
+
+ kwargs[:description] ||= "The GitLab Flavored Markdown rendering of `#{method_name}`"
+ # Adding complexity to rendered notes since that could cause queries.
+ kwargs[:complexity] ||= 5
+
+ field name, GraphQL::STRING_TYPE, **kwargs
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/markdown_field/resolver.rb b/lib/gitlab/graphql/markdown_field/resolver.rb
new file mode 100644
index 00000000000..11a01b95ad1
--- /dev/null
+++ b/lib/gitlab/graphql/markdown_field/resolver.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module MarkdownField
+ class Resolver
+ attr_reader :method_name
+
+ def initialize(method_name)
+ @method_name = method_name
+ end
+
+ def proc
+ -> (object, _args, ctx) do
+ # We need to `dup` the context so the MarkdownHelper doesn't modify it
+ ::MarkupHelper.markdown_field(object, method_name, ctx.to_h.dup)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/scripts/frontend/test.js b/scripts/frontend/test.js
index dab7176f8c1..71a8bebf0f2 100755
--- a/scripts/frontend/test.js
+++ b/scripts/frontend/test.js
@@ -5,19 +5,24 @@ const { EOL } = require('os');
const program = require('commander');
const chalk = require('chalk');
+const SUCCESS_CODE = 0;
const JEST_ROUTE = 'spec/frontend';
const KARMA_ROUTE = 'spec/javascripts';
const COMMON_ARGS = ['--colors'];
-const JEST_ARGS = ['--passWithNoTests'];
-const KARMA_ARGS = ['--no-fail-on-empty-test-suite'];
-const SUCCESS_CODE = 0;
+const jestArgs = [...COMMON_ARGS, '--passWithNoTests'];
+const karmaArgs = [...COMMON_ARGS, '--no-fail-on-empty-test-suite'];
program
- .version('0.1.0')
.usage('[options] <file ...>')
.option('-p, --parallel', 'Run tests suites in parallel')
+ .option(
+ '-w, --watch',
+ 'Rerun tests when files change (tests will be run in parallel if this enabled)',
+ )
.parse(process.argv);
+const shouldParallelize = program.parallel || program.watch;
+
const isSuccess = code => code === SUCCESS_CODE;
const combineExitCodes = codes => {
@@ -31,7 +36,7 @@ const skipIfFail = fn => code => (isSuccess(code) ? fn() : code);
const endWithEOL = str => (str[str.length - 1] === '\n' ? str : `${str}${EOL}`);
const runTests = paths => {
- if (program.parallel) {
+ if (shouldParallelize) {
return Promise.all([runJest(paths), runKarma(paths)]).then(combineExitCodes);
} else {
return runJest(paths).then(skipIfFail(() => runKarma(paths)));
@@ -73,11 +78,11 @@ const spawnYarnScript = (cmd, args) => {
};
const runJest = args => {
- return spawnYarnScript('jest', [...JEST_ARGS, ...COMMON_ARGS, ...toJestArgs(args)]);
+ return spawnYarnScript('jest', [...jestArgs, ...toJestArgs(args)]);
};
const runKarma = args => {
- return spawnYarnScript('karma', [...KARMA_ARGS, ...COMMON_ARGS, ...toKarmaArgs(args)]);
+ return spawnYarnScript('karma', [...karmaArgs, ...toKarmaArgs(args)]);
};
const replacePath = to => path =>
@@ -96,6 +101,10 @@ const toKarmaArgs = paths =>
paths.reduce((acc, path) => acc.concat('-f', replacePathForKarma(path)), []);
const main = paths => {
+ if (program.watch) {
+ jestArgs.push('--watch');
+ karmaArgs.push('--single-run', 'false', '--auto-watch');
+ }
runTests(paths).then(code => {
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
if (isSuccess(code)) {
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 0dabe27977a..901402aa5fd 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -73,21 +73,27 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'number of queries' do
+ render_views
+
before do
Ci::Build::AVAILABLE_STATUSES.each do |status|
create_job(status, status)
end
+
+ allow(Appearance).to receive(:current_without_cache)
+ .and_return(nil)
end
it 'verifies number of queries', :request_store do
- recorded = ActiveRecord::QueryRecorder.new { get_index }
- expect(recorded.count).to be_within(5).of(7)
+ expect { get_index }.not_to be_n_plus_1_query.with_threshold(3)
end
def create_job(name, status)
- pipeline = create(:ci_pipeline, project: project)
+ user = create(:user)
+ pipeline = create(:ci_pipeline, project: project, user: user)
create(:ci_build, :tags, :triggered, :artifacts,
- pipeline: pipeline, name: name, status: status)
+ pipeline: pipeline, name: name, status: status,
+ user: user)
end
end
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 0a685152cf9..87bde4ca2f6 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -292,5 +292,31 @@ describe NotesFinder do
expect(subject.target).to eq(commit)
end
end
+
+ context 'target_iid' do
+ let(:issue) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+
+ it 'finds issues by iid' do
+ iid_params = { target_type: 'issue', target_iid: issue.iid }
+ expect(described_class.new(project, user, iid_params).target).to eq(issue)
+ end
+
+ it 'finds merge requests by iid' do
+ iid_params = { target_type: 'merge_request', target_iid: merge_request.iid }
+ expect(described_class.new(project, user, iid_params).target).to eq(merge_request)
+ end
+
+ it 'returns nil if both target_id and target_iid are not given' do
+ params_without_any_id = { target_type: 'issue' }
+ expect(described_class.new(project, user, params_without_any_id).target).to be_nil
+ end
+
+ it 'prioritizes target_id over target_iid' do
+ issue2 = create(:issue, project: project)
+ iid_params = { target_type: 'issue', target_id: issue2.id, target_iid: issue.iid }
+ expect(described_class.new(project, user, iid_params).target).to eq(issue2)
+ end
+ end
end
end
diff --git a/spec/frontend/api_spec.js b/spec/frontend/api_spec.js
index 6010488d9e0..0188d12a57d 100644
--- a/spec/frontend/api_spec.js
+++ b/spec/frontend/api_spec.js
@@ -474,4 +474,27 @@ describe('Api', () => {
.catch(done.fail);
});
});
+
+ describe('projectForks', () => {
+ it('gets forked projects', done => {
+ const dummyProjectPath = 'gitlab-org/gitlab-ce';
+ const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${encodeURIComponent(
+ dummyProjectPath,
+ )}/forks`;
+
+ jest.spyOn(axios, 'get');
+
+ mock.onGet(expectedUrl).replyOnce(200, ['fork']);
+
+ Api.projectForks(dummyProjectPath, { visibility: 'private' })
+ .then(({ data }) => {
+ expect(data).toEqual(['fork']);
+ expect(axios.get).toHaveBeenCalledWith(expectedUrl, {
+ params: { visibility: 'private' },
+ });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/graphql/types/issue_type_spec.rb b/spec/graphql/types/issue_type_spec.rb
index 210932f8488..799a8662b94 100644
--- a/spec/graphql/types/issue_type_spec.rb
+++ b/spec/graphql/types/issue_type_spec.rb
@@ -10,7 +10,10 @@ describe GitlabSchema.types['Issue'] do
it { expect(described_class.interfaces).to include(Types::Notes::NoteableType.to_graphql) }
it 'has specific fields' do
- %i[relative_position web_path web_url reference].each do |field_name|
+ fields = %i[title_html description_html relative_position web_path web_url
+ reference]
+
+ fields.each do |field_name|
expect(described_class).to have_graphql_field(field_name)
end
end
diff --git a/spec/graphql/types/label_type_spec.rb b/spec/graphql/types/label_type_spec.rb
new file mode 100644
index 00000000000..f498b32f9ed
--- /dev/null
+++ b/spec/graphql/types/label_type_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe GitlabSchema.types['Label'] do
+ it 'has the correct fields' do
+ expected_fields = [:description, :description_html, :title, :color, :text_color]
+
+ is_expected.to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index fd1c782bcc5..f73bd062369 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -7,7 +7,20 @@ describe GitlabSchema.types['MergeRequest'] do
it { expect(described_class.interfaces).to include(Types::Notes::NoteableType.to_graphql) }
- describe 'nested head pipeline' do
- it { expect(described_class).to have_graphql_field(:head_pipeline) }
+ it 'has the expected fields' do
+ expected_fields = %w[
+ notes discussions user_permissions id iid title title_html description
+ description_html state created_at updated_at source_project target_project
+ project project_id source_project_id target_project_id source_branch
+ target_branch work_in_progress merge_when_pipeline_succeeds diff_head_sha
+ merge_commit_sha user_notes_count should_remove_source_branch
+ force_remove_source_branch merge_status in_progress_merge_commit_sha
+ merge_error allow_collaboration should_be_rebased rebase_commit_sha
+ rebase_in_progress merge_commit_message default_merge_commit_message
+ merge_ongoing source_branch_exists mergeable_discussions_state web_url
+ upvotes downvotes subscribed head_pipeline pipelines task_completion_status
+ ]
+
+ is_expected.to have_graphql_fields(*expected_fields)
end
end
diff --git a/spec/graphql/types/namespace_type_spec.rb b/spec/graphql/types/namespace_type_spec.rb
index b4144cc4121..77fd590586e 100644
--- a/spec/graphql/types/namespace_type_spec.rb
+++ b/spec/graphql/types/namespace_type_spec.rb
@@ -5,5 +5,12 @@ require 'spec_helper'
describe GitlabSchema.types['Namespace'] do
it { expect(described_class.graphql_name).to eq('Namespace') }
- it { expect(described_class).to have_graphql_field(:projects) }
+ it 'has the expected fields' do
+ expected_fields = %w[
+ id name path full_name full_path description description_html visibility
+ lfs_enabled request_access_enabled projects
+ ]
+
+ is_expected.to have_graphql_fields(*expected_fields)
+ end
end
diff --git a/spec/graphql/types/notes/note_type_spec.rb b/spec/graphql/types/notes/note_type_spec.rb
index 8022b20f9dd..e8a58da4b17 100644
--- a/spec/graphql/types/notes/note_type_spec.rb
+++ b/spec/graphql/types/notes/note_type_spec.rb
@@ -5,7 +5,7 @@ describe GitlabSchema.types['Note'] do
it 'exposes the expected fields' do
expected_fields = [:id, :project, :author, :body, :created_at,
:updated_at, :discussion, :resolvable, :position, :user_permissions,
- :resolved_by, :resolved_at, :system]
+ :resolved_by, :resolved_at, :system, :body_html]
is_expected.to have_graphql_fields(*expected_fields)
end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index cb5ac2e3cb1..69fbc72bdf5 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -7,18 +7,22 @@ describe GitlabSchema.types['Project'] do
it { expect(described_class).to require_graphql_authorizations(:read_project) }
- describe 'nested merge request' do
- it { expect(described_class).to have_graphql_field(:merge_requests) }
- it { expect(described_class).to have_graphql_field(:merge_request) }
+ it 'has the expected fields' do
+ expected_fields = %w[
+ user_permissions id full_path path name_with_namespace
+ name description description_html tag_list ssh_url_to_repo
+ http_url_to_repo web_url star_count forks_count
+ created_at last_activity_at archived visibility
+ container_registry_enabled shared_runners_enabled
+ lfs_enabled merge_requests_ff_only_enabled avatar_url
+ issues_enabled merge_requests_enabled wiki_enabled
+ snippets_enabled jobs_enabled public_jobs open_issues_count import_status
+ only_allow_merge_if_pipeline_succeeds request_access_enabled
+ only_allow_merge_if_all_discussions_are_resolved printing_merge_request_link_enabled
+ namespace group statistics repository merge_requests merge_request issues
+ issue pipelines
+ ]
+
+ is_expected.to have_graphql_fields(*expected_fields)
end
-
- describe 'nested issues' do
- it { expect(described_class).to have_graphql_field(:issues) }
- end
-
- it { is_expected.to have_graphql_field(:pipelines) }
-
- it { is_expected.to have_graphql_field(:repository) }
-
- it { is_expected.to have_graphql_field(:statistics) }
end
diff --git a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js
index 997d84dcc42..ad8d5a53291 100644
--- a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js
+++ b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js
@@ -1,68 +1,72 @@
-import { mountComponentWithRender } from 'spec/helpers/vue_mount_component_helper';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
const TEST_TITLE = 'lorem-ipsum-dolar-sit-amit-consectur-adipiscing-elit-sed-do';
-const CLASS_SHOW_TOOLTIP = 'js-show-tooltip';
-const STYLE_TRUNCATED = {
- display: 'inline-block',
- 'max-width': '20px',
-};
-const STYLE_NORMAL = {
- display: 'inline-block',
- 'max-width': '1000px',
-};
-
-function mountTooltipOnTruncate(options, createChildren) {
- return mountComponentWithRender(h => h(TooltipOnTruncate, options, createChildren(h)), '#app');
-}
+const STYLE_TRUNCATED = 'display: inline-block; max-width: 20px;';
+const STYLE_NORMAL = 'display: inline-block; max-width: 1000px;';
-describe('TooltipOnTruncate component', () => {
- let vm;
+const localVue = createLocalVue();
- beforeEach(() => {
- const el = document.createElement('div');
- el.id = 'app';
- document.body.appendChild(el);
- });
+const createElementWithStyle = (style, content) => `<a href="#" style="${style}">${content}</a>`;
+
+describe('TooltipOnTruncate component', () => {
+ let wrapper;
+
+ const createComponent = ({ propsData, ...options } = {}) => {
+ wrapper = shallowMount(localVue.extend(TooltipOnTruncate), {
+ localVue,
+ sync: false,
+ attachToDocument: true,
+ propsData: {
+ title: TEST_TITLE,
+ ...propsData,
+ },
+ ...options,
+ });
+ };
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
+ const hasTooltip = () => wrapper.classes('js-show-tooltip');
+
describe('with default target', () => {
it('renders tooltip if truncated', done => {
- const options = {
- style: STYLE_TRUNCATED,
- props: {
- title: TEST_TITLE,
+ createComponent({
+ attrs: {
+ style: STYLE_TRUNCATED,
},
- };
-
- vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]);
+ slots: {
+ default: [TEST_TITLE],
+ },
+ });
- vm.$nextTick()
+ wrapper.vm
+ .$nextTick()
.then(() => {
- expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
- expect(vm.$el).toHaveData('original-title', TEST_TITLE);
- expect(vm.$el).toHaveData('placement', 'top');
+ expect(hasTooltip()).toBe(true);
+ expect(wrapper.attributes('data-original-title')).toEqual(TEST_TITLE);
+ expect(wrapper.attributes('data-placement')).toEqual('top');
})
.then(done)
.catch(done.fail);
});
it('does not render tooltip if normal', done => {
- const options = {
- style: STYLE_NORMAL,
- props: {
- title: TEST_TITLE,
+ createComponent({
+ attrs: {
+ style: STYLE_NORMAL,
},
- };
-
- vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]);
+ slots: {
+ default: [TEST_TITLE],
+ },
+ });
- vm.$nextTick()
+ wrapper.vm
+ .$nextTick()
.then(() => {
- expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP);
+ expect(hasTooltip()).toBe(false);
})
.then(done)
.catch(done.fail);
@@ -71,37 +75,41 @@ describe('TooltipOnTruncate component', () => {
describe('with child target', () => {
it('renders tooltip if truncated', done => {
- const options = {
- style: STYLE_NORMAL,
- props: {
- title: TEST_TITLE,
+ createComponent({
+ attrs: {
+ style: STYLE_NORMAL,
+ },
+ propsData: {
truncateTarget: 'child',
},
- };
-
- vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]);
+ slots: {
+ default: createElementWithStyle(STYLE_TRUNCATED, TEST_TITLE),
+ },
+ });
- vm.$nextTick()
+ wrapper.vm
+ .$nextTick()
.then(() => {
- expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
+ expect(hasTooltip()).toBe(true);
})
.then(done)
.catch(done.fail);
});
it('does not render tooltip if normal', done => {
- const options = {
- props: {
- title: TEST_TITLE,
+ createComponent({
+ propsData: {
truncateTarget: 'child',
},
- };
-
- vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_NORMAL }, TEST_TITLE)]);
+ slots: {
+ default: createElementWithStyle(STYLE_NORMAL, TEST_TITLE),
+ },
+ });
- vm.$nextTick()
+ wrapper.vm
+ .$nextTick()
.then(() => {
- expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP);
+ expect(hasTooltip()).toBe(false);
})
.then(done)
.catch(done.fail);
@@ -110,22 +118,25 @@ describe('TooltipOnTruncate component', () => {
describe('with fn target', () => {
it('renders tooltip if truncated', done => {
- const options = {
- style: STYLE_NORMAL,
- props: {
- title: TEST_TITLE,
+ createComponent({
+ attrs: {
+ style: STYLE_NORMAL,
+ },
+ propsData: {
truncateTarget: el => el.childNodes[1],
},
- };
-
- vm = mountTooltipOnTruncate(options, h => [
- h('a', { style: STYLE_NORMAL }, TEST_TITLE),
- h('span', { style: STYLE_TRUNCATED }, TEST_TITLE),
- ]);
+ slots: {
+ default: [
+ createElementWithStyle('', TEST_TITLE),
+ createElementWithStyle(STYLE_TRUNCATED, TEST_TITLE),
+ ],
+ },
+ });
- vm.$nextTick()
+ wrapper.vm
+ .$nextTick()
.then(() => {
- expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
+ expect(hasTooltip()).toBe(true);
})
.then(done)
.catch(done.fail);
@@ -134,20 +145,25 @@ describe('TooltipOnTruncate component', () => {
describe('placement', () => {
it('sets data-placement when tooltip is rendered', done => {
- const options = {
- props: {
- title: TEST_TITLE,
- truncateTarget: 'child',
- placement: 'bottom',
- },
- };
+ const placement = 'bottom';
- vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]);
+ createComponent({
+ propsData: {
+ placement,
+ },
+ attrs: {
+ style: STYLE_TRUNCATED,
+ },
+ slots: {
+ default: TEST_TITLE,
+ },
+ });
- vm.$nextTick()
+ wrapper.vm
+ .$nextTick()
.then(() => {
- expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP);
- expect(vm.$el).toHaveData('placement', options.props.placement);
+ expect(hasTooltip()).toBe(true);
+ expect(wrapper.attributes('data-placement')).toEqual(placement);
})
.then(done)
.catch(done.fail);
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 1e4c4c38f74..3cf3d032bf4 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -461,9 +461,9 @@ describe Gitlab::Database::MigrationHelpers do
end
it 'updates all the rows in a table' do
- model.update_column_in_batches(:projects, :import_error, 'foo')
+ model.update_column_in_batches(:projects, :description_html, 'foo')
- expect(Project.where(import_error: 'foo').count).to eq(5)
+ expect(Project.where(description_html: 'foo').count).to eq(5)
end
it 'updates boolean values correctly' do
diff --git a/spec/lib/gitlab/graphql/markdown_field/resolver_spec.rb b/spec/lib/gitlab/graphql/markdown_field/resolver_spec.rb
new file mode 100644
index 00000000000..b95bcdef188
--- /dev/null
+++ b/spec/lib/gitlab/graphql/markdown_field/resolver_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Graphql::MarkdownField::Resolver do
+ include Gitlab::Routing
+ let(:resolver) { described_class.new(:note) }
+
+ describe '#proc' do
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project) }
+ let(:note) do
+ create(:note,
+ note: "Referencing #{issue.to_reference(full: true)}")
+ end
+
+ it 'renders markdown correctly' do
+ expect(resolver.proc.call(note, {}, {})).to include(issue_path(issue))
+ end
+
+ context 'when the issue is not publicly accessible' do
+ let(:project) { create(:project, :private) }
+
+ it 'hides the references from users that are not allowed to see the reference' do
+ expect(resolver.proc.call(note, {}, {})).not_to include(issue_path(issue))
+ end
+
+ it 'shows the reference to users that are allowed to see it' do
+ expect(resolver.proc.call(note, {}, { current_user: project.owner }))
+ .to include(issue_path(issue))
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/markdown_field_spec.rb b/spec/lib/gitlab/graphql/markdown_field_spec.rb
new file mode 100644
index 00000000000..a8566aa8e1c
--- /dev/null
+++ b/spec/lib/gitlab/graphql/markdown_field_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Graphql::MarkdownField do
+ describe '.markdown_field' do
+ it 'creates the field with some default attributes' do
+ field = class_with_markdown_field(:test_html, null: true, method: :hello).fields['testHtml']
+
+ expect(field.name).to eq('testHtml')
+ expect(field.description).to eq('The GitLab Flavored Markdown rendering of `hello`')
+ expect(field.type).to eq(GraphQL::STRING_TYPE)
+ expect(field.to_graphql.complexity).to eq(5)
+ end
+
+ context 'developer warnings' do
+ let(:expected_error) { /Only `method` is allowed to specify the markdown field/ }
+
+ it 'raises when passing a resolver' do
+ expect { class_with_markdown_field(:test_html, null: true, resolver: 'not really') }
+ .to raise_error(expected_error)
+ end
+
+ it 'raises when passing a resolve block' do
+ expect { class_with_markdown_field(:test_html, null: true, resolve: -> (_, _, _) { 'not really' } ) }
+ .to raise_error(expected_error)
+ end
+ end
+
+ context 'resolving markdown' do
+ let(:note) { build(:note, note: '# Markdown!') }
+ let(:thing_with_markdown) { double('markdown thing', object: note) }
+ let(:expected_markdown) { '<h1 data-sourcepos="1:1-1:11" dir="auto">Markdown!</h1>' }
+
+ it 'renders markdown from the same property as the field name without the `_html` suffix' do
+ field = class_with_markdown_field(:note_html, null: false).fields['noteHtml']
+
+ expect(field.to_graphql.resolve(thing_with_markdown, {}, {})).to eq(expected_markdown)
+ end
+
+ it 'renders markdown from a specific property when a `method` argument is passed' do
+ field = class_with_markdown_field(:test_html, null: false, method: :note).fields['testHtml']
+
+ expect(field.to_graphql.resolve(thing_with_markdown, {}, {})).to eq(expected_markdown)
+ end
+ end
+ end
+
+ def class_with_markdown_field(name, **args)
+ Class.new(GraphQL::Schema::Object) do
+ prepend Gitlab::Graphql::MarkdownField
+
+ markdown_field name, **args
+ end
+ end
+end
diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb
index d45377267f3..f77b43391dd 100644
--- a/spec/support/helpers/query_recorder.rb
+++ b/spec/support/helpers/query_recorder.rb
@@ -35,5 +35,9 @@ module ActiveRecord
def log_message
@log.join("\n\n")
end
+
+ def occurrences
+ @log.group_by(&:to_s).transform_values(&:count)
+ end
end
end
diff --git a/spec/support/matchers/be_n_plus_1_query.rb b/spec/support/matchers/be_n_plus_1_query.rb
new file mode 100644
index 00000000000..bbfd1897f04
--- /dev/null
+++ b/spec/support/matchers/be_n_plus_1_query.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+module Nplus1QueryHelpers
+ DEFAULT_THRESHOLD = 3
+
+ def with_threshold(threshold)
+ @threshold = threshold
+ self
+ end
+
+ def for_query(query)
+ @query = query
+ self
+ end
+
+ def threshold
+ @threshold || DEFAULT_THRESHOLD
+ end
+
+ def occurrences
+ @occurrences ||=
+ if @query
+ recorder.occurrences.select { |recorded, count| recorded =~ @query }
+ else
+ recorder.occurrences
+ end
+ end
+
+ def over_threshold
+ occurrences.select do |recorded, count|
+ count > threshold
+ end
+ end
+
+ def recorder
+ @recorder ||= ActiveRecord::QueryRecorder.new(&@subject_block)
+ end
+
+ def verify_count(&block)
+ @subject_block = block
+ over_threshold.present?
+ end
+
+ def failure_message
+ log_message = over_threshold.to_yaml
+ "The following queries are executed more than #{threshold} time(s):\n#{log_message}"
+ end
+end
+
+RSpec::Matchers.define :be_n_plus_1_query do
+ supports_block_expectations
+
+ include Nplus1QueryHelpers
+
+ match do |block|
+ verify_count(&block)
+ end
+
+ failure_message_when_negated do |actual|
+ failure_message
+ end
+end
diff --git a/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb b/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb
index b7080c68270..d3cadf2ba7c 100644
--- a/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb
+++ b/spec/support/shared_examples/controllers/repository_lfs_file_load_examples.rb
@@ -9,7 +9,15 @@
# - `filepath`: path of the file (contains filename)
# - `subject`: the request to be made to the controller. Example:
# subject { get :show, namespace_id: project.namespace, project_id: project }
-shared_examples 'a controller that can serve LFS files' do
+#
+# The LFS disabled scenario can be skipped by passing `skip_lfs_disabled_tests: true`
+# when including the examples (Note, at time of writing this is only used by
+# an EE-specific spec):
+#
+# it_behaves_like 'a controller that can serve LFS files', skip_lfs_disabled_tests: true do
+# ...
+# end
+shared_examples 'a controller that can serve LFS files' do |options = {}|
let(:lfs_oid) { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
let(:lfs_size) { '1575078' }
let!(:lfs_object) { create(:lfs_object, oid: lfs_oid, size: lfs_size) }
@@ -83,6 +91,8 @@ shared_examples 'a controller that can serve LFS files' do
end
it 'delivers ASCII file' do
+ skip 'Calling spec asked to skip testing LFS disabled scenario' if options[:skip_lfs_disabled_tests]
+
subject
expect(response).to have_gitlab_http_status(200)