summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Kumar Mohan <arunmohandm@gmail.com>2019-06-20 05:36:08 -0500
committerArun Kumar Mohan <arunmohandm@gmail.com>2019-06-20 05:36:08 -0500
commit0cb6b787a0508ed891f5e69dad5c914e064db491 (patch)
tree9ae4e25ca3da02017d152b46040627b40affc7d4
parenta10d80eaddf81eb4996df586cc1e871f1e8b0779 (diff)
parent4298a28a993363f4ab6b63c14820492393a3ae94 (diff)
downloadgitlab-ce-0cb6b787a0508ed891f5e69dad5c914e064db491.tar.gz
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into db/update-geo-nodes-primary
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/memory.gitlab-ci.yml19
-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/pages/sessions/new/index.js2
-rw-r--r--app/assets/javascripts/pages/sessions/new/username_validator.js148
-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/controllers/projects/tags_controller.rb3
-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/clusters/applications/knative.rb2
-rw-r--r--app/models/project.rb2
-rw-r--r--app/policies/project_policy.rb1
-rw-r--r--app/services/git/wiki_push_service.rb9
-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/devise/shared/_signup_box.html.haml8
-rw-r--r--app/views/projects/tags/_tag.html.haml8
-rw-r--r--app/views/projects/tags/index.html.haml2
-rw-r--r--app/views/projects/tags/show.html.haml4
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--app/workers/cleanup_container_repository_worker.rb29
-rw-r--r--app/workers/post_receive.rb23
-rw-r--r--changelogs/unreleased/11448-fix-cs-with-k8s-runners.yml5
-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/52954-allow-developers-to-delete-tags.yml5
-rw-r--r--changelogs/unreleased/57918-encrypt-feature-flags-tokens-changelog.yml5
-rw-r--r--changelogs/unreleased/59702-fix-notification-flags-for-ms-teams.yml5
-rw-r--r--changelogs/unreleased/61201-pass-identities-to-external-authorization.yml5
-rw-r--r--changelogs/unreleased/62183-update-response-code-for-bulk-delete-api-for-container-registry.yml5
-rw-r--r--changelogs/unreleased/62980-username-availability-checker-breaks-inline-validation.yml5
-rw-r--r--changelogs/unreleased/63227-fix-double-border.yml5
-rw-r--r--changelogs/unreleased/63417-add-missing-class.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/knative-0-6.yml5
-rw-r--r--changelogs/unreleased/sh-cache-feature-flag-names.yml5
-rw-r--r--changelogs/unreleased/sh-remove-import-columns-from-projects.yml5
-rw-r--r--config/database.yml.example0
-rw-r--r--config/initializers/1_postgresql_only.rb4
-rw-r--r--danger/commit_messages/Dangerfile12
-rw-r--r--db/migrate/20190607205656_add_wiki_columns_to_index_status.rb12
-rw-r--r--db/post_migrate/20190619175843_remove_import_columns_from_projects.rb14
-rw-r--r--db/schema.rb7
-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/README.md3
-rw-r--r--doc/api/container_registry.md5
-rw-r--r--doc/api/services.md2
-rw-r--r--doc/api/users.md15
-rw-r--r--doc/ci/img/collapsible_log.pngbin0 -> 174697 bytes
-rw-r--r--doc/ci/pipelines.md14
-rw-r--r--doc/development/architecture.md4
-rw-r--r--doc/development/i18n/externalization.md3
-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.md56
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md6
-rw-r--r--doc/user/admin_area/settings/external_authorization.md8
-rw-r--r--doc/user/admin_area/settings/img/group_pipelines_quota.pngbin7088 -> 21010 bytes
-rw-r--r--doc/user/group/img/group_storage_usage_quota.pngbin0 -> 28896 bytes
-rw-r--r--doc/user/group/index.md8
-rw-r--r--doc/user/group/subgroups/index.md13
-rw-r--r--doc/user/permissions.md2
-rw-r--r--doc/user/project/clusters/serverless/index.md18
-rw-r--r--doc/user/project/deploy_boards.md3
-rw-r--r--doc/user/project/file_lock.md83
-rw-r--r--doc/user/project/img/file_lock.pngbin26973 -> 46281 bytes
-rw-r--r--doc/user/project/img/file_lock_folders.pngbin22900 -> 0 bytes
-rw-r--r--doc/user/project/img/file_lock_list.pngbin18243 -> 0 bytes
-rw-r--r--doc/user/project/img/file_lock_merge_request_error_message.pngbin24573 -> 16254 bytes
-rw-r--r--doc/user/project/img/file_lock_repository_view.pngbin22947 -> 17644 bytes
-rw-r--r--doc/user/project/integrations/mattermost.md4
-rw-r--r--doc/user/project/integrations/mattermost_slash_commands.md3
-rw-r--r--doc/user/project/issues/img/link_zoom_call_in_issue.pngbin0 -> 69554 bytes
-rw-r--r--doc/user/project/issues/issue_data_and_actions.md13
-rw-r--r--doc/user/project/pipelines/settings.md3
-rw-r--r--lib/api/container_registry.rb10
-rw-r--r--lib/api/discussions.rb18
-rw-r--r--lib/api/helpers.rb9
-rw-r--r--lib/api/helpers/notes_helpers.rb21
-rw-r--r--lib/api/helpers/services_helpers.rb5
-rw-r--r--lib/api/notes.rb12
-rw-r--r--lib/api/resource_label_events.rb4
-rw-r--r--lib/api/tags.rb4
-rw-r--r--lib/api/users.rb15
-rw-r--r--lib/feature.rb7
-rw-r--r--lib/gitlab/checks/tag_check.rb2
-rw-r--r--lib/gitlab/ci/ansi2html.rb2
-rw-r--r--lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml2
-rw-r--r--lib/gitlab/database/migration_helpers.rb5
-rw-r--r--lib/gitlab/external_authorization/client.rb3
-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
-rw-r--r--lib/gitlab/kubernetes.rb7
-rw-r--r--lib/gitlab/user_access.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb3
-rwxr-xr-xscripts/frontend/test.js23
-rwxr-xr-xscripts/memory-static20
-rwxr-xr-xscripts/memory-static-objects27
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb14
-rw-r--r--spec/features/tags/developer_creates_tag_spec.rb (renamed from spec/features/tags/master_creates_tag_spec.rb)7
-rw-r--r--spec/features/tags/developer_deletes_tag_spec.rb (renamed from spec/features/tags/master_deletes_tag_spec.rb)7
-rw-r--r--spec/features/tags/developer_updates_tag_spec.rb (renamed from spec/features/tags/master_updates_tag_spec.rb)7
-rw-r--r--spec/features/tags/developer_views_tags_spec.rb (renamed from spec/features/tags/master_views_tags_spec.rb)7
-rw-r--r--spec/features/users/signup_spec.rb44
-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/feature_spec.rb9
-rw-r--r--spec/lib/gitlab/checks/tag_check_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/ansi2html_spec.rb4
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb4
-rw-r--r--spec/lib/gitlab/external_authorization/client_spec.rb29
-rw-r--r--spec/lib/gitlab/git_access_spec.rb2
-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/lib/gitlab/kubernetes_spec.rb10
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb2
-rw-r--r--spec/models/clusters/applications/knative_spec.rb2
-rw-r--r--spec/models/project_services/microsoft_teams_service_spec.rb12
-rw-r--r--spec/policies/project_policy_spec.rb2
-rw-r--r--spec/requests/api/container_registry_spec.rb21
-rw-r--r--spec/requests/api/tags_spec.rb2
-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
-rw-r--r--spec/support/shared_examples/policies/project_policy_shared_examples.rb1
-rw-r--r--spec/workers/cleanup_container_repository_worker_spec.rb8
175 files changed, 1252 insertions, 1010 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 059b181bb1c..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"
@@ -37,6 +36,7 @@ include:
- local: .gitlab/ci/cng.gitlab-ci.yml
- local: .gitlab/ci/docs.gitlab-ci.yml
- local: .gitlab/ci/frontend.gitlab-ci.yml
+ - local: .gitlab/ci/memory.gitlab-ci.yml
- local: .gitlab/ci/pages.gitlab-ci.yml
- local: .gitlab/ci/qa.gitlab-ci.yml
- local: .gitlab/ci/reports.gitlab-ci.yml
diff --git a/.gitlab/ci/memory.gitlab-ci.yml b/.gitlab/ci/memory.gitlab-ci.yml
new file mode 100644
index 00000000000..50b843df585
--- /dev/null
+++ b/.gitlab/ci/memory.gitlab-ci.yml
@@ -0,0 +1,19 @@
+memory-static:
+ extends: .dedicated-no-docs-no-db-pull-cache-job
+ script:
+ # Uses two different reports from the 'derailed_benchmars' gem.
+
+ # Loads each of gems in the Gemfile and checks how much memory they consume when they are required.
+ # 'derailed_benchmarks' internally uses 'get_process_mem'
+ - scripts/memory-static 'tmp/memory_static_full_report.txt' 'tmp/memory_static_metrics.txt'
+
+ # Outputs detailed information about objects created while gems are loaded.
+ # 'derailed_benchmarks' internally uses 'memory_profiler'
+ - scripts/memory-static-objects 'tmp/memory_static_objects_full_report.txt' 'tmp/memory_static_metrics.txt'
+ artifacts:
+ paths:
+ - tmp/memory_static_full_report.txt
+ - tmp/memory_static_objects_full_report.txt
+ - tmp/memory_static_metrics.txt
+ reports:
+ metrics: tmp/memory_static_metrics.txt
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/pages/sessions/new/index.js b/app/assets/javascripts/pages/sessions/new/index.js
index 3f5a3e15c2c..55bc93a2b13 100644
--- a/app/assets/javascripts/pages/sessions/new/index.js
+++ b/app/assets/javascripts/pages/sessions/new/index.js
@@ -7,8 +7,8 @@ import OAuthRememberMe from './oauth_remember_me';
import preserveUrlFragment from './preserve_url_fragment';
document.addEventListener('DOMContentLoaded', () => {
- new LengthValidator(); // eslint-disable-line no-new
new UsernameValidator(); // eslint-disable-line no-new
+ new LengthValidator(); // eslint-disable-line no-new
new SigninTabsMemoizer(); // eslint-disable-line no-new
new NoEmojiValidator(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/sessions/new/username_validator.js b/app/assets/javascripts/pages/sessions/new/username_validator.js
index 7a41805bada..36d1e773134 100644
--- a/app/assets/javascripts/pages/sessions/new/username_validator.js
+++ b/app/assets/javascripts/pages/sessions/new/username_validator.js
@@ -1,133 +1,79 @@
-/* eslint-disable consistent-return, class-methods-use-this */
+import InputValidator from '~/validators/input_validator';
-import $ from 'jquery';
import _ from 'underscore';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
import { __ } from '~/locale';
const debounceTimeoutDuration = 1000;
+const rootUrl = gon.relative_url_root;
const invalidInputClass = 'gl-field-error-outline';
const successInputClass = 'gl-field-success-outline';
-const unavailableMessageSelector = '.username .validation-error';
-const successMessageSelector = '.username .validation-success';
-const pendingMessageSelector = '.username .validation-pending';
-const invalidMessageSelector = '.username .gl-field-error';
+const successMessageSelector = '.validation-success';
+const pendingMessageSelector = '.validation-pending';
+const unavailableMessageSelector = '.validation-error';
-export default class UsernameValidator {
- constructor() {
- this.inputElement = $('#new_user_username');
- this.inputDomElement = this.inputElement.get(0);
- this.state = {
- available: false,
- valid: false,
- pending: false,
- empty: true,
- };
+export default class UsernameValidator extends InputValidator {
+ constructor(opts = {}) {
+ super();
- const debounceTimeout = _.debounce(username => {
- this.validateUsername(username);
- }, debounceTimeoutDuration);
-
- this.inputElement.on('keyup.username_check', () => {
- const username = this.inputElement.val();
-
- this.state.valid = this.inputDomElement.validity.valid;
- this.state.empty = !username.length;
+ const container = opts.container || '';
+ const validateLengthElements = document.querySelectorAll(`${container} .js-validate-username`);
- if (this.state.valid) {
- return debounceTimeout(username);
- }
-
- this.renderState();
- });
+ this.debounceValidateInput = _.debounce(inputDomElement => {
+ UsernameValidator.validateUsernameInput(inputDomElement);
+ }, debounceTimeoutDuration);
- // Override generic field validation
- this.inputElement.on('invalid', this.interceptInvalid.bind(this));
+ validateLengthElements.forEach(element =>
+ element.addEventListener('input', this.eventHandler.bind(this)),
+ );
}
- renderState() {
- // Clear all state
- this.clearFieldValidationState();
-
- if (this.state.valid && this.state.available) {
- return this.setSuccessState();
- }
-
- if (this.state.empty) {
- return this.clearFieldValidationState();
- }
-
- if (this.state.pending) {
- return this.setPendingState();
- }
+ eventHandler(event) {
+ const inputDomElement = event.target;
- if (!this.state.valid) {
- return this.setInvalidState();
- }
-
- if (!this.state.available) {
- return this.setUnavailableState();
- }
- }
-
- interceptInvalid(event) {
- event.preventDefault();
- event.stopPropagation();
+ UsernameValidator.resetInputState(inputDomElement);
+ this.debounceValidateInput(inputDomElement);
}
- validateUsername(username) {
- if (this.state.valid) {
- this.state.pending = true;
- this.state.available = false;
- this.renderState();
- axios
- .get(`${gon.relative_url_root}/users/${username}/exists`)
- .then(({ data }) => this.setAvailabilityState(data.exists))
+ static validateUsernameInput(inputDomElement) {
+ const username = inputDomElement.value;
+
+ if (inputDomElement.checkValidity() && username.length > 0) {
+ UsernameValidator.setMessageVisibility(inputDomElement, pendingMessageSelector);
+ UsernameValidator.fetchUsernameAvailability(username)
+ .then(usernameTaken => {
+ UsernameValidator.setInputState(inputDomElement, !usernameTaken);
+ UsernameValidator.setMessageVisibility(inputDomElement, pendingMessageSelector, false);
+ UsernameValidator.setMessageVisibility(
+ inputDomElement,
+ usernameTaken ? unavailableMessageSelector : successMessageSelector,
+ );
+ })
.catch(() => flash(__('An error occurred while validating username')));
}
}
- setAvailabilityState(usernameTaken) {
- if (usernameTaken) {
- this.state.available = false;
- } else {
- this.state.available = true;
- }
- this.state.pending = false;
- this.renderState();
+ static fetchUsernameAvailability(username) {
+ return axios.get(`${rootUrl}/users/${username}/exists`).then(({ data }) => data.exists);
}
- clearFieldValidationState() {
- this.inputElement.siblings('p').hide();
-
- this.inputElement.removeClass(invalidInputClass).removeClass(successInputClass);
+ static setMessageVisibility(inputDomElement, messageSelector, isVisible = true) {
+ const messageElement = inputDomElement.parentElement.querySelector(messageSelector);
+ messageElement.classList.toggle('hide', !isVisible);
}
- setUnavailableState() {
- const $usernameUnavailableMessage = this.inputElement.siblings(unavailableMessageSelector);
- this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
- $usernameUnavailableMessage.show();
+ static setInputState(inputDomElement, success = true) {
+ inputDomElement.classList.toggle(successInputClass, success);
+ inputDomElement.classList.toggle(invalidInputClass, !success);
}
- setSuccessState() {
- const $usernameSuccessMessage = this.inputElement.siblings(successMessageSelector);
- this.inputElement.addClass(successInputClass).removeClass(invalidInputClass);
- $usernameSuccessMessage.show();
- }
+ static resetInputState(inputDomElement) {
+ UsernameValidator.setMessageVisibility(inputDomElement, successMessageSelector, false);
+ UsernameValidator.setMessageVisibility(inputDomElement, unavailableMessageSelector, false);
- setPendingState() {
- const $usernamePendingMessage = $(pendingMessageSelector);
- if (this.state.pending) {
- $usernamePendingMessage.show();
- } else {
- $usernamePendingMessage.hide();
+ if (inputDomElement.checkValidity()) {
+ inputDomElement.classList.remove(successInputClass, invalidInputClass);
}
}
-
- setInvalidState() {
- const $inputErrorMessage = $(invalidMessageSelector);
- this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
- $inputErrorMessage.show();
- }
}
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/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index a17c050b696..7d9387b1d94 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -8,8 +8,7 @@ class Projects::TagsController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
- before_action :authorize_push_code!, only: [:new, :create]
- before_action :authorize_admin_project!, only: [:destroy]
+ before_action :authorize_admin_tag!, only: [:new, :create, :destroy]
# rubocop: disable CodeReuse/ActiveRecord
def index
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/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb
index d5a3bd62e3d..5df4812bd25 100644
--- a/app/models/clusters/applications/knative.rb
+++ b/app/models/clusters/applications/knative.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Knative < ApplicationRecord
- VERSION = '0.5.0'.freeze
+ VERSION = '0.6.0'.freeze
REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'.freeze
METRICS_CONFIG = 'https://storage.googleapis.com/triggermesh-charts/istio-metrics.yaml'.freeze
FETCH_IP_ADDRESS_DELAY = 30.seconds
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/policies/project_policy.rb b/app/policies/project_policy.rb
index b3e29e775fc..08bfe5d14ee 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -297,6 +297,7 @@ class ProjectPolicy < BasePolicy
end
rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror
+ rule { can?(:push_code) }.enable :admin_tag
rule { archived }.policy do
prevent :push_code
diff --git a/app/services/git/wiki_push_service.rb b/app/services/git/wiki_push_service.rb
new file mode 100644
index 00000000000..a053f133016
--- /dev/null
+++ b/app/services/git/wiki_push_service.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Git
+ class WikiPushService < ::BaseService
+ def execute
+ # This is used in EE
+ end
+ end
+end
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/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 5eba819172b..eae3ee6339f 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -10,10 +10,10 @@
= f.text_field :name, class: "form-control top qa-new-user-name js-block-emoji js-validate-length", :data => { :max_length => max_name_length, :max_length_message => s_("SignUp|Name is too long (maximum is %{max_length} characters).") % { max_length: max_name_length } }, required: true, title: _("This field is required.")
.username.form-group
= f.label :username, class: 'label-bold'
- = f.text_field :username, class: "form-control middle qa-new-user-username js-block-emoji js-validate-length", :data => { :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length } }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
- %p.validation-error.field-validation.hide= _('Username is already taken.')
- %p.validation-success.field-validation.hide= _('Username is available.')
- %p.validation-pending.field-validation.hide= _('Checking username availability...')
+ = f.text_field :username, class: "form-control middle qa-new-user-username js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length } }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
+ %p.validation-error.gl-field-error-ignore.field-validation.hide= _('Username is already taken.')
+ %p.validation-success.gl-field-error-ignore.field-validation.hide= _('Username is available.')
+ %p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking username availability...')
.form-group
= f.label :email, class: 'label-bold'
= f.email_field :email, class: "form-control middle qa-new-user-email", required: true, title: _("Please provide a valid email address.")
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 8bfface3f5a..b1432917f1d 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -26,10 +26,8 @@
.row-fixed-content.controls.flex-row
= render 'projects/buttons/download', project: @project, ref: tag.name, pipeline: @tags_pipelines[tag.name]
- - if can?(current_user, :push_code, @project)
+ - if can?(current_user, :admin_tag, @project)
= link_to edit_project_tag_release_path(@project, tag.name), class: 'btn btn-edit has-tooltip', title: s_('TagsPage|Edit release notes'), data: { container: "body" } do
= icon("pencil")
-
- - if can?(current_user, :admin_project, @project)
- = link_to project_tag_path(@project, tag.name), class: "btn btn-remove remove-row has-tooltip prepend-left-10 #{protected_tag?(@project, tag) ? 'disabled' : ''}", title: s_('TagsPage|Delete tag'), method: :delete, data: { confirm: s_('TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?') % { tag_name: tag.name }, container: 'body' }, remote: true do
- = icon("trash-o")
+ = link_to project_tag_path(@project, tag.name), class: "btn btn-remove remove-row has-tooltip prepend-left-10 #{protected_tag?(@project, tag) ? 'disabled' : ''}", title: s_('TagsPage|Delete tag'), method: :delete, data: { confirm: s_('TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?') % { tag_name: tag.name }, container: 'body' }, remote: true do
+ = icon("trash-o")
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index 2e78b0bff3e..1f0de1e2603 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -24,7 +24,7 @@
- tags_sort_options_hash.each do |value, title|
%li
= link_to title, filter_tags_path(sort: value), class: ("is-active" if @sort == value)
- - if can?(current_user, :push_code, @project)
+ - if can?(current_user, :admin_tag, @project)
= link_to new_project_tag_path(@project), class: 'btn btn-success new-tag-btn' do
= s_('TagsPage|New tag')
= link_to project_tags_path(@project, rss_url_options), title: _("Tags feed"), class: 'btn d-none d-sm-inline-block has-tooltip' do
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index 59232372150..02f6ef02843 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -19,7 +19,7 @@
= s_("TagsPage|Can't find HEAD commit for this tag")
.nav-controls
- - if can?(current_user, :push_code, @project)
+ - if can?(current_user, :admin_tag, @project)
= link_to edit_project_tag_release_path(@project, @tag.name), class: 'btn btn-edit controls-item has-tooltip', title: s_('TagsPage|Edit release notes') do
= icon("pencil")
= link_to project_tree_path(@project, @tag.name), class: 'btn controls-item has-tooltip', title: s_('TagsPage|Browse files') do
@@ -28,7 +28,7 @@
= icon('history')
.btn-container.controls-item
= render 'projects/buttons/download', project: @project, ref: @tag.name
- - if can?(current_user, :push_code, @project) && can?(current_user, :admin_project, @project)
+ - if can?(current_user, :admin_tag, @project)
.btn-container.controls-item-full
= link_to project_tag_path(@project, @tag.name), class: "btn btn-remove remove-row has-tooltip #{protected_tag?(@project, @tag) ? 'disabled' : ''}", title: s_('TagsPage|Delete tag'), method: :delete, data: { confirm: s_('TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?') % { tag_name: @tag.name } } do
%i.fa.fa-trash-o
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/app/workers/cleanup_container_repository_worker.rb b/app/workers/cleanup_container_repository_worker.rb
index 974ee8c8146..0331fc7b01c 100644
--- a/app/workers/cleanup_container_repository_worker.rb
+++ b/app/workers/cleanup_container_repository_worker.rb
@@ -2,12 +2,9 @@
class CleanupContainerRepositoryWorker
include ApplicationWorker
- include ExclusiveLeaseGuard
queue_namespace :container_repository
- LEASE_TIMEOUT = 1.hour
-
attr_reader :container_repository, :current_user
def perform(current_user_id, container_repository_id, params)
@@ -16,11 +13,9 @@ class CleanupContainerRepositoryWorker
return unless valid?
- try_obtain_lease do
- Projects::ContainerRepository::CleanupTagsService
- .new(project, current_user, params)
- .execute(container_repository)
- end
+ Projects::ContainerRepository::CleanupTagsService
+ .new(project, current_user, params)
+ .execute(container_repository)
end
private
@@ -32,22 +27,4 @@ class CleanupContainerRepositoryWorker
def project
container_repository&.project
end
-
- # For ExclusiveLeaseGuard concern
- def lease_key
- @lease_key ||= "container_repository:cleanup_tags:#{container_repository.id}"
- end
-
- # For ExclusiveLeaseGuard concern
- def lease_timeout
- LEASE_TIMEOUT
- end
-
- # For ExclusiveLeaseGuard concern
- def lease_release?
- # we don't allow to execute this worker
- # more often than LEASE_TIMEOUT
- # for given container repository
- false
- end
end
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 3f1639ec2ed..dba7837bd12 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -30,15 +30,17 @@ class PostReceive
private
+ def identify_user(post_received)
+ post_received.identify.tap do |user|
+ log("Triggered hook for non-existing user \"#{post_received.identifier}\"") unless user
+ end
+ end
+
def process_project_changes(post_received)
changes = []
refs = Set.new
- @user = post_received.identify
-
- unless @user
- log("Triggered hook for non-existing user \"#{post_received.identifier}\"")
- return false
- end
+ user = identify_user(post_received)
+ return false unless user
post_received.enum_for(:changes_refs).with_index do |(oldrev, newrev, ref), index|
service_klass =
@@ -51,7 +53,7 @@ class PostReceive
if service_klass
service_klass.new(
post_received.project,
- @user,
+ user,
oldrev: oldrev,
newrev: newrev,
ref: ref,
@@ -64,7 +66,7 @@ class PostReceive
refs << ref
end
- after_project_changes_hooks(post_received, @user, refs.to_a, changes)
+ after_project_changes_hooks(post_received, user, refs.to_a, changes)
end
def after_project_changes_hooks(post_received, user, refs, changes)
@@ -76,6 +78,11 @@ class PostReceive
post_received.project.touch(:last_activity_at, :last_repository_updated_at)
post_received.project.wiki.repository.expire_statistics_caches
ProjectCacheWorker.perform_async(post_received.project.id, [], [:wiki_size])
+
+ user = identify_user(post_received)
+ return false unless user
+
+ ::Git::WikiPushService.new(post_received.project, user, changes: post_received.enum_for(:changes_refs)).execute
end
def log(message)
diff --git a/changelogs/unreleased/11448-fix-cs-with-k8s-runners.yml b/changelogs/unreleased/11448-fix-cs-with-k8s-runners.yml
new file mode 100644
index 00000000000..191e64df4f1
--- /dev/null
+++ b/changelogs/unreleased/11448-fix-cs-with-k8s-runners.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Container Scanning job timeout when using the kubernetes executor
+merge_request: 29706
+author:
+type: fixed
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/52954-allow-developers-to-delete-tags.yml b/changelogs/unreleased/52954-allow-developers-to-delete-tags.yml
new file mode 100644
index 00000000000..38c65a67f2a
--- /dev/null
+++ b/changelogs/unreleased/52954-allow-developers-to-delete-tags.yml
@@ -0,0 +1,5 @@
+---
+title: Allow developers to delete tags
+merge_request: 29668
+author:
+type: changed
diff --git a/changelogs/unreleased/57918-encrypt-feature-flags-tokens-changelog.yml b/changelogs/unreleased/57918-encrypt-feature-flags-tokens-changelog.yml
new file mode 100644
index 00000000000..9701c8bc4a5
--- /dev/null
+++ b/changelogs/unreleased/57918-encrypt-feature-flags-tokens-changelog.yml
@@ -0,0 +1,5 @@
+---
+title: Add token_encrypted column to operations_feature_flags_clients table
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/59702-fix-notification-flags-for-ms-teams.yml b/changelogs/unreleased/59702-fix-notification-flags-for-ms-teams.yml
new file mode 100644
index 00000000000..14a8da95ed9
--- /dev/null
+++ b/changelogs/unreleased/59702-fix-notification-flags-for-ms-teams.yml
@@ -0,0 +1,5 @@
+---
+title: Fix missing API notification flags for Microsoft Teams
+merge_request: 29824
+author: Seiji Suenaga
+type: fixed
diff --git a/changelogs/unreleased/61201-pass-identities-to-external-authorization.yml b/changelogs/unreleased/61201-pass-identities-to-external-authorization.yml
new file mode 100644
index 00000000000..82eea653de6
--- /dev/null
+++ b/changelogs/unreleased/61201-pass-identities-to-external-authorization.yml
@@ -0,0 +1,5 @@
+---
+title: Add identity information to external authorization requests
+merge_request: 29461
+author:
+type: changed
diff --git a/changelogs/unreleased/62183-update-response-code-for-bulk-delete-api-for-container-registry.yml b/changelogs/unreleased/62183-update-response-code-for-bulk-delete-api-for-container-registry.yml
new file mode 100644
index 00000000000..ce75a55efdc
--- /dev/null
+++ b/changelogs/unreleased/62183-update-response-code-for-bulk-delete-api-for-container-registry.yml
@@ -0,0 +1,5 @@
+---
+title: Return 400 when deleting tags more often than once per hour.
+merge_request: 29448
+author:
+type: changed
diff --git a/changelogs/unreleased/62980-username-availability-checker-breaks-inline-validation.yml b/changelogs/unreleased/62980-username-availability-checker-breaks-inline-validation.yml
new file mode 100644
index 00000000000..7436f5d278e
--- /dev/null
+++ b/changelogs/unreleased/62980-username-availability-checker-breaks-inline-validation.yml
@@ -0,0 +1,5 @@
+---
+title: Fix the signup form's username validation messages not displaying
+merge_request: 29678
+author: Jiaan Louw
+type: fixed
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/63417-add-missing-class.yml b/changelogs/unreleased/63417-add-missing-class.yml
new file mode 100644
index 00000000000..3030f5c57e4
--- /dev/null
+++ b/changelogs/unreleased/63417-add-missing-class.yml
@@ -0,0 +1,5 @@
+---
+title: Indent collapsible sections
+merge_request: 29804
+author:
+type: other
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/knative-0-6.yml b/changelogs/unreleased/knative-0-6.yml
new file mode 100644
index 00000000000..66c5c7f343d
--- /dev/null
+++ b/changelogs/unreleased/knative-0-6.yml
@@ -0,0 +1,5 @@
+---
+title: Knative version bump 0.5 -> 0.6
+merge_request: 28798
+author: Chris Baumbauer
+type: changed
diff --git a/changelogs/unreleased/sh-cache-feature-flag-names.yml b/changelogs/unreleased/sh-cache-feature-flag-names.yml
new file mode 100644
index 00000000000..6120c4870f8
--- /dev/null
+++ b/changelogs/unreleased/sh-cache-feature-flag-names.yml
@@ -0,0 +1,5 @@
+---
+title: Cache feature flag names in Redis for a minute
+merge_request: 29816
+author:
+type: performance
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/database.yml.example b/config/database.yml.example
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/config/database.yml.example
+++ /dev/null
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/migrate/20190607205656_add_wiki_columns_to_index_status.rb b/db/migrate/20190607205656_add_wiki_columns_to_index_status.rb
new file mode 100644
index 00000000000..0910d425212
--- /dev/null
+++ b/db/migrate/20190607205656_add_wiki_columns_to_index_status.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class AddWikiColumnsToIndexStatus < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :index_statuses, :last_wiki_commit, :binary
+ add_column :index_statuses, :wiki_indexed_at, :datetime_with_timezone
+ end
+end
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 5d48aa5087d..02d8ab10935 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: 20190618171120) do
+ActiveRecord::Schema.define(version: 20190619175843) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1572,6 +1572,8 @@ ActiveRecord::Schema.define(version: 20190618171120) do
t.string "last_commit"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.binary "last_wiki_commit"
+ t.datetime_with_timezone "wiki_indexed_at"
t.index ["project_id"], name: "index_index_statuses_on_project_id", unique: true, using: :btree
end
@@ -2593,11 +2595,9 @@ ActiveRecord::Schema.define(version: 20190618171120) 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"
@@ -2619,7 +2619,6 @@ ActiveRecord::Schema.define(version: 20190618171120) 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/README.md b/doc/api/README.md
index 3a1064b787e..23c69deef23 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -35,6 +35,7 @@ The following API resources are available in the project context:
| [Environments](environments.md) | `/projects/:id/environments` |
| [Events](events.md) | `/projects/:id/events` (also available for users and standalone) |
| [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) |
+| [Issues Statistics](issues_statistics.md) | `/projects/:id/issues_statistics` (also available for groups and standalone) |
| [Issue boards](boards.md) | `/projects/:id/boards` |
| [Issue links](issue_links.md) **[STARTER]** | `/projects/:id/issues/.../links` |
| [Jobs](jobs.md) | `/projects/:id/jobs`, `/projects/:id/pipelines/.../jobs` |
@@ -92,6 +93,7 @@ The following API resources are available in the group context:
| [Group-level variables](group_level_variables.md) | `/groups/:id/variables` |
| [Group milestones](group_milestones.md) | `/groups/:id/milestones` |
| [Issues](issues.md) | `/groups/:id/issues` (also available for projects and standalone) |
+| [Issues Statistics](issues_statistics.md) | `/groups/:id/issues_statistics` (also available for projects and standalone) |
| [Members](members.md) | `/groups/:id/members` (also available for projects) |
| [Merge requests](merge_requests.md) | `/groups/:id/merge_requests` (also available for projects and standalone) |
| [Notes](notes.md) (comments) | `/groups/:id/epics/.../notes` (also available for projects) |
@@ -116,6 +118,7 @@ The following API resources are available outside of project and group contexts
| [Geo Nodes](geo_nodes.md) **[PREMIUM ONLY]** | `/geo_nodes` |
| [Import repository from GitHub](import.md) | `/import/github` |
| [Issues](issues.md) | `/issues` (also available for groups and projects) |
+| [Issues Statistics](issues_statistics.md) | `/issues_statistics` (also available for groups and projects) |
| [Keys](keys.md) | `/keys` |
| [License](license.md) **[CORE ONLY]** | `/license` |
| [Markdown](markdown.md) | `/markdown` |
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/services.md b/doc/api/services.md
index f38f96f64ad..042fee4a21a 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -1023,6 +1023,8 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhook` | string | true | The Microsoft Teams webhook. For example, `https://outlook.office.com/webhook/...` |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | Send notifications only for the default branch |
| `push_events` | boolean | false | Enable notifications for push events |
| `issues_events` | boolean | false | Enable notifications for issue events |
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
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/ci/img/collapsible_log.png b/doc/ci/img/collapsible_log.png
new file mode 100644
index 00000000000..2785033b349
--- /dev/null
+++ b/doc/ci/img/collapsible_log.png
Binary files differ
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index 1ad3d516df9..4a07aa31f8a 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -139,6 +139,20 @@ The union of A, B, and C is (1, 4) and (6, 7). Therefore, the total running time
(4 - 1) + (7 - 6) => 4
```
+### Expanding and collapsing job log sections
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/14664) in GitLab
+> 12.0.
+
+Job logs are divided into sections that can be collapsed or expanded.
+
+In the following example:
+
+- Two sections are expanded and can be collapsed.
+- One section is collapsed and can be expanded.
+
+![Collapsible sections](img/collapsible_log.png)
+
## Configuring pipelines
Pipelines, and their component jobs and stages, are defined in the [`.gitlab-ci.yml`](yaml/README.md) file for each project.
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 5f32cd7eba2..a43a40152db 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -149,7 +149,7 @@ Component statuses are linked to configuration documentation for each component.
| [PgBouncer Exporter](#pgbouncer-exporter) | Prometheus endpoint with PgBouncer metrics | [⚙][pgbouncer-exporter-omnibus] | [❌][pgbouncer-exporter-charts] | [❌][pgbouncer-exporter-charts] | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
| [GitLab Monitor](#gitlab-monitor) | Generates a variety of GitLab metrics | [✅][gitlab-monitor-omnibus] | [❌][gitab-monitor-charts] | [❌][gitab-monitor-charts] | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
| [Node Exporter](#node-exporter) | Prometheus endpoint with system metrics | [✅][node-exporter-omnibus] | [❌][node-exporter-charts] | [❌][node-exporter-charts] | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
-| [Mattermost](#mattermost) | Open-source Slack alternative | [⚙][mattermost-omnibus] | [⤓][mattermost-charts] | [⤓][mattermost-charts] | [⤓](../user/project/integrations/mattermost_slash_commands.md#manual-configuration), [⤓](../user/project/integrations/mattermost.html) | ❌ | ❌ | CE & EE |
+| [Mattermost](#mattermost) | Open-source Slack alternative | [⚙][mattermost-omnibus] | [⤓][mattermost-charts] | [⤓][mattermost-charts] | [⤓](../user/project/integrations/mattermost.md) | ❌ | ❌ | CE & EE |
| [MinIO](#minio) | Object storage service | [⤓][minio-omnibus] | [✅][minio-charts] | [✅][minio-charts] | [✅](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#storage-architecture) | ❌ | [⚙][minio-gdk] | CE & EE |
| [Runner](#gitlab-runner) | Executes GitLab CI jobs | [⤓][runner-omnibus] | [✅][runner-charts] | [⚙][runner-charts] | [✅](../user/gitlab_com/index.md#shared-runners) | [⚙][runner-source] | [⚙][runner-gdk] | CE & EE |
| [Database Migrations](#database-migrations) | Database migrations | [✅][database-migrations-omnibus] | [✅][database-migrations-charts] | [✅][database-migrations-charts] | ✅ | [⚙][database-migrations-source] | ✅ | CE & EE |
@@ -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/i18n/externalization.md b/doc/development/i18n/externalization.md
index 9fb8ea542d9..ce310672dad 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -77,6 +77,9 @@ Or:
hello = _("Hello world!")
```
+NOTE: **Note:** Messages in the API (`lib/api/` or `app/graphql`) do
+not need to be externalised.
+
### HAML files
Given the following content in HAML:
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 bc8e5fed774..023dc7d6de3 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -4,6 +4,10 @@ comments: false
# Upgrading Community Edition and Enterprise Edition from source
+NOTE: **Note:**
+Users wishing to upgrade to 12.0.0 will have to take some extra steps. See the
+version specific upgrade instructions for 12.0.0 for more details.
+
Make sure you view this update guide from the branch (version) of GitLab you
would like to install (e.g., `11.8`. You can select the version in the version
dropdown at the top left corner of GitLab (below the menu bar).
@@ -235,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`
@@ -331,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
@@ -360,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:
@@ -404,6 +378,20 @@ Example:
Additional instructions here.
-->
+### 12.0.0
+
+In 12.0.0 we made various database related changes. These changes require that
+users first upgrade to the latest 11.11 patch release. Once upgraded to 11.11.x,
+users can upgrade to 12.x. Failure to do so may result in database migrations
+not being applied, which could lead to application errors.
+
+Example 1: you are currently using GitLab 11.11.3, which is the latest patch
+release for 11.11.x. You can upgrade as usual to 12.0.0, 12.1.0, etc.
+
+Example 2: you are currently using a version of GitLab 10.x. To upgrade, first
+upgrade to 11.11.3. Once upgraded to 11.11.3 you can safely upgrade to 12.0.0
+or future versions.
+
## Things went south? Revert to previous version
### 1. Revert the code to the previous version
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index d2605cbfb5e..fde7d1aeaf7 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -89,10 +89,8 @@ are enabled.
![Project admin info](img/admin_project_quota_view.png)
-When the pipeline minutes quota for a group is set to a value different than 0,
-the **Pipelines quota** page is available to the group page settings list.
-You can see there an overview of the pipeline minutes quota of all projects of
-the group.
+You can see an overview of the pipeline minutes quota of all projects of
+a group in the **Usage Quotas** page available to the group page settings list.
![Group pipelines quota](img/group_pipelines_quota.png)
diff --git a/doc/user/admin_area/settings/external_authorization.md b/doc/user/admin_area/settings/external_authorization.md
index 11c0867da17..c1aa04f7bc2 100644
--- a/doc/user/admin_area/settings/external_authorization.md
+++ b/doc/user/admin_area/settings/external_authorization.md
@@ -76,13 +76,19 @@ service with this body:
{
"user_identifier": "jane@acme.org",
"project_classification_label": "project-label",
- "user_ldap_dn": "CN=Jane Doe,CN=admin,DC=acme"
+ "user_ldap_dn": "CN=Jane Doe,CN=admin,DC=acme",
+ "identities": [
+ { "provider": "ldap", "extern_uid": "CN=Jane Doe,CN=admin,DC=acme" },
+ { "provider": "bitbucket", "extern_uid": "2435223452345" }
+ ]
}
```
The `user_ldap_dn` is optional and is only sent when the user is logged in
through LDAP.
+`identities` will contain the details of all the identities associated with the user. This will be an empty array if there are no identities associated with the user.
+
When the external authorization service responds with a status code 200, the
user is granted access. When the external service responds with a status code
401 or 403, the user is denied access. In any case, the request is cached for 6 hours.
diff --git a/doc/user/admin_area/settings/img/group_pipelines_quota.png b/doc/user/admin_area/settings/img/group_pipelines_quota.png
index d94b609ad6f..318527426bd 100644
--- a/doc/user/admin_area/settings/img/group_pipelines_quota.png
+++ b/doc/user/admin_area/settings/img/group_pipelines_quota.png
Binary files differ
diff --git a/doc/user/group/img/group_storage_usage_quota.png b/doc/user/group/img/group_storage_usage_quota.png
new file mode 100644
index 00000000000..c5d81ad7a8b
--- /dev/null
+++ b/doc/user/group/img/group_storage_usage_quota.png
Binary files differ
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 95e10be9974..abd95eddf63 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -381,6 +381,14 @@ Define project templates at a group level by setting a group as the template sou
for the group. **[STARTER ONLY]**
- **Pipelines quota**: Keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group.
+#### Storage usage quota **[STARTER]**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/13294) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.0.
+
+A group owner can check the aggregated storage usage for all the project in a group, sub-groups included, in the **Storage** tab of the **Usage Quotas** page available to the group page settings list.
+
+![Group storage usage quota](img/group_storage_usage_quota.png)
+
## User contribution analysis **[STARTER]**
With [GitLab Contribution Analytics](contribution_analytics/index.md),
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/permissions.md b/doc/user/permissions.md
index 4b2dfdfc32e..7af3d4a0ac3 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -95,6 +95,7 @@ The following table depicts the various user permission levels in a project.
| Dismiss vulnerability **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
| Apply code change suggestions | | | ✓ | ✓ | ✓ |
| Create and edit wiki pages | | | ✓ | ✓ | ✓ |
+| Rewrite/remove Git tags | | | ✓ | ✓ | ✓ |
| Use environment terminals | | | | ✓ | ✓ |
| Run Web IDE's Interactive Web Terminals **[ULTIMATE ONLY]** | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ |
@@ -102,7 +103,6 @@ The following table depicts the various user permission levels in a project.
| Push to protected branches | | | | ✓ | ✓ |
| Turn on/off protected branch push for devs | | | | ✓ | ✓ |
| Enable/disable tag protections | | | | ✓ | ✓ |
-| Rewrite/remove Git tags | | | | ✓ | ✓ |
| Edit project | | | | ✓ | ✓ |
| Add deploy keys to project | | | | ✓ | ✓ |
| Configure project hooks | | | | ✓ | ✓ |
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/deploy_boards.md b/doc/user/project/deploy_boards.md
index 0994c51abb2..b7f6a855cb6 100644
--- a/doc/user/project/deploy_boards.md
+++ b/doc/user/project/deploy_boards.md
@@ -86,8 +86,7 @@ To display the Deploy Boards for a specific [environment] you should:
Kubernetes.
NOTE: **Note:**
- The Kubernetes label of `app` is deprecated and may be removed in next major
- release, GitLab 12.0.
+ Matching based on the Kubernetes `app` label was removed in [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/14020)
![Deploy Boards Kubernetes Label](img/deploy_boards_kubernetes_label.png)
diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md
index 3386eb9d0d4..40603790c12 100644
--- a/doc/user/project/file_lock.md
+++ b/doc/user/project/file_lock.md
@@ -1,8 +1,6 @@
# File Locking **[PREMIUM]**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/440) in [GitLab Premium](https://about.gitlab.com/pricing/) 8.9.
-> - This feature needs to have a license with the "File Lock" option enabled.
-> - If you are using Premium but you don't see the "Lock" button, ask your GitLab administrator.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/440) in [GitLab Premium](https://about.gitlab.com/pricing/) 8.9.
File Locking helps you avoid merge conflicts and better manage your binary files.
Lock any file or directory, make your changes, and then unlock it so another
@@ -30,12 +28,51 @@ The file locking feature is useful in situations when:
Locked directories are locked recursively, which means that everything that
lies under them is also locked.
+## Locking a file or a directory
+
+NOTE: **Note:**
+Locking only works for the default branch you have set in the project's settings
+(usually `master`).
+
+To lock a file:
+
+1. Navigate to your project's **Repository > Files**.
+1. Pick the file you want to lock.
+1. Click the "Lock" button.
+
+ ![Locking file](img/file_lock.png)
+
+To lock an entire directory, look for the "Lock" link next to "History".
+
+After you lock a file or directory, it will appear as locked in the repository
+view.
+
+![Repository view](img/file_lock_repository_view.png)
+
+Once locked, any merge request to the default branch will fail
+to merge until the file becomes unlocked.
+
+## Unlocking a file or a directory
+
+To unlock a file or a directory, follow the same procedure as when you locked
+them. For a detailed view of every existing lock, see the next section on
+"Viewing and managing existing locks".
+
+You can unlock a file that yourself or someone else previously locked as long
+as you have Maintainer or above [permissions](../permissions.md) to the project.
+
+## Viewing and managing existing locks
+
+To view or manage every existing lock, navigate to the
+**Project > Repository > Locked Files** area. There, you can view all existing
+locks and [remove the ones you have permission for](#permissions-on-file-locking).
+
## Permissions on file locking
The user that locks a file or directory **is the only one** that can edit and
push their changes back to the repository where the locked objects are located.
-Locks can be created by any person who has [push access](../../user/permissions.md) to the repository; i.e.,
+Locks can be created by any person who has [push access](../permissions.md) to the repository; i.e.,
Developer and higher level, and can be removed solely by their author and any
user with Maintainer permissions and above.
@@ -61,41 +98,3 @@ accepts a merge request, an error message will appear stating that the file
is locked.
![Merge request error message](img/file_lock_merge_request_error_message.png)
-
-## Locking a file or a directory
-
->**Note:**
-Locking only works for the default branch you have set in the project's settings
-(usually `master`).
-
-To lock a file, navigate to the repository tree under the **Repository > Files** tab,
-pick the file you want to lock and hit the "Lock" button.
-
-![Locking file](img/file_lock.png)
-
----
-
-To lock an entire directory, look for the "Lock" link next to "History".
-
-![Locking directory](img/file_lock_folders.png)
-
----
-
-After you lock a file or directory, it will appear as locked in the repository
-view.
-
-![Repository view](img/file_lock_repository_view.png)
-
-## Unlocking a file or a directory
-
-To unlock a file or a directory, follow the same procedure as when you locked
-them. For a detailed view of every existing lock, see the next section on
-"Viewing and managing existing locks".
-
-## Viewing and managing existing locks
-
-To view or manage every existing lock, navigate to the
-**Project > Repository > Locked Files** area. There, you can view all existing
-locks and [remove the ones you have permission for](#permissions-on-file-locking).
-
-![Locked Files](img/file_lock_list.png)
diff --git a/doc/user/project/img/file_lock.png b/doc/user/project/img/file_lock.png
index 33f96f20e91..82699a12ffd 100644
--- a/doc/user/project/img/file_lock.png
+++ b/doc/user/project/img/file_lock.png
Binary files differ
diff --git a/doc/user/project/img/file_lock_folders.png b/doc/user/project/img/file_lock_folders.png
deleted file mode 100644
index 5ff3d4d9dbd..00000000000
--- a/doc/user/project/img/file_lock_folders.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/file_lock_list.png b/doc/user/project/img/file_lock_list.png
deleted file mode 100644
index 2c276335c83..00000000000
--- a/doc/user/project/img/file_lock_list.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/file_lock_merge_request_error_message.png b/doc/user/project/img/file_lock_merge_request_error_message.png
index 65bc3692da0..4ef04b15bef 100644
--- a/doc/user/project/img/file_lock_merge_request_error_message.png
+++ b/doc/user/project/img/file_lock_merge_request_error_message.png
Binary files differ
diff --git a/doc/user/project/img/file_lock_repository_view.png b/doc/user/project/img/file_lock_repository_view.png
index 8f4ef52aacd..a2cab0decab 100644
--- a/doc/user/project/img/file_lock_repository_view.png
+++ b/doc/user/project/img/file_lock_repository_view.png
Binary files differ
diff --git a/doc/user/project/integrations/mattermost.md b/doc/user/project/integrations/mattermost.md
index d7fd75fd728..ea58a08e127 100644
--- a/doc/user/project/integrations/mattermost.md
+++ b/doc/user/project/integrations/mattermost.md
@@ -1,5 +1,9 @@
# Mattermost Notifications Service
+The Mattermost Notifications Service allows your GitLab project to send events (e.g., `issue created`) to your existing Mattermost team as notifications. This requires configurations in both Mattermost and GitLab.
+
+You can also use Mattermost slash commands to control GitLab inside Mattermost. This is the separately configured [Mattermost slash commands](mattermost_slash_commands.md).
+
## On Mattermost
To enable Mattermost integration you must create an incoming webhook integration:
diff --git a/doc/user/project/integrations/mattermost_slash_commands.md b/doc/user/project/integrations/mattermost_slash_commands.md
index 9c69437537a..41be26c1d30 100644
--- a/doc/user/project/integrations/mattermost_slash_commands.md
+++ b/doc/user/project/integrations/mattermost_slash_commands.md
@@ -6,6 +6,9 @@ Mattermost commands give users an extra interface to perform common operations
from the chat environment. This allows one to, for example, create an issue as
soon as the idea was discussed in Mattermost.
+GitLab can also send events (e.g., `issue created`) to Mattermost as notifications.
+This is the separately configured [Mattermost Notifications Service](mattermost.md).
+
## Prerequisites
Mattermost 3.4 and up is required.
diff --git a/doc/user/project/issues/img/link_zoom_call_in_issue.png b/doc/user/project/issues/img/link_zoom_call_in_issue.png
new file mode 100644
index 00000000000..3153a0a9b07
--- /dev/null
+++ b/doc/user/project/issues/img/link_zoom_call_in_issue.png
Binary files differ
diff --git a/doc/user/project/issues/issue_data_and_actions.md b/doc/user/project/issues/issue_data_and_actions.md
index da585022263..2103f331aa2 100644
--- a/doc/user/project/issues/issue_data_and_actions.md
+++ b/doc/user/project/issues/issue_data_and_actions.md
@@ -149,6 +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.
+##### Zoom call links
+
+> [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/container_registry.rb b/lib/api/container_registry.rb
index 7d9b5e1a598..7dad20a822a 100644
--- a/lib/api/container_registry.rb
+++ b/lib/api/container_registry.rb
@@ -68,6 +68,9 @@ module API
delete ':id/registry/repositories/:repository_id/tags', requirements: REGISTRY_ENDPOINT_REQUIREMENTS do
authorize_admin_container_image!
+ message = 'This request has already been made. You can run this at most once an hour for a given container repository'
+ render_api_error!(message, 400) unless obtain_new_cleanup_container_lease
+
CleanupContainerRepositoryWorker.perform_async(current_user.id, repository.id,
declared_params.except(:repository_id)) # rubocop: disable CodeReuse/ActiveRecord
@@ -123,6 +126,13 @@ module API
authorize! :admin_container_image, repository
end
+ def obtain_new_cleanup_container_lease
+ Gitlab::ExclusiveLease
+ .new("container_repository:cleanup_tags:#{repository.id}",
+ timeout: 1.hour)
+ .try_obtain
+ end
+
def repository
@repository ||= user_project.container_repositories.find(params[:repository_id])
end
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 00bcf6b055b..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)
@@ -235,6 +230,10 @@ module API
authorize! :push_code, user_project
end
+ def authorize_admin_tag
+ authorize! :admin_tag, user_project
+ end
+
def authorize_admin_project
authorize! :admin_project, user_project
end
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/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
index 44c577204b8..cf2e9d01356 100644
--- a/lib/api/helpers/services_helpers.rb
+++ b/lib/api/helpers/services_helpers.rb
@@ -683,8 +683,9 @@ module API
name: :webhook,
type: String,
desc: 'The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/…'
- }
- ],
+ },
+ chat_notification_flags
+ ].flatten,
'mattermost' => [
chat_notification_settings,
chat_notification_flags,
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/tags.rb b/lib/api/tags.rb
index f5359fd316c..796b1450602 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -55,7 +55,7 @@ module API
optional :release_description, type: String, desc: 'Specifying release notes stored in the GitLab database (deprecated in GitLab 11.7)'
end
post ':id/repository/tags' do
- authorize_push_project
+ authorize_admin_tag
result = ::Tags::CreateService.new(user_project, current_user)
.execute(params[:tag_name], params[:ref], params[:message])
@@ -87,7 +87,7 @@ module API
requires :tag_name, type: String, desc: 'The name of the tag'
end
delete ':id/repository/tags/:tag_name', requirements: TAG_ENDPOINT_REQUIREMENTS do
- authorize_push_project
+ authorize_admin_tag
tag = user_project.repository.find_tag(params[:tag_name])
not_found!('Tag') unless tag
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/feature.rb b/lib/feature.rb
index 749c861d740..cc9c9d44005 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -30,7 +30,12 @@ class Feature
end
def persisted_names
- Gitlab::SafeRequestStore[:flipper_persisted_names] ||= FlipperFeature.feature_names
+ Gitlab::SafeRequestStore[:flipper_persisted_names] ||=
+ begin
+ # We saw on GitLab.com, this database request was called 2300
+ # times/s. Let's cache it for a minute to avoid that load.
+ Rails.cache.fetch('flipper:persisted_names', expires_in: 1.minute) { FlipperFeature.feature_names }
+ end
end
def persisted?(feature)
diff --git a/lib/gitlab/checks/tag_check.rb b/lib/gitlab/checks/tag_check.rb
index 2a75c8059bd..ced0612a7a3 100644
--- a/lib/gitlab/checks/tag_check.rb
+++ b/lib/gitlab/checks/tag_check.rb
@@ -19,7 +19,7 @@ module Gitlab
return unless tag_name
logger.log_timed(LOG_MESSAGES[:tag_checks]) do
- if tag_exists? && user_access.cannot_do_action?(:admin_project)
+ if tag_exists? && user_access.cannot_do_action?(:admin_tag)
raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:change_existing_tags]
end
end
diff --git a/lib/gitlab/ci/ansi2html.rb b/lib/gitlab/ci/ansi2html.rb
index 6109b45ffd2..fc3223e7442 100644
--- a/lib/gitlab/ci/ansi2html.rb
+++ b/lib/gitlab/ci/ansi2html.rb
@@ -310,7 +310,7 @@ module Gitlab
if @sections.any?
css_classes << "section"
- css_classes << "js-section-header" if @lineno_in_section == 0
+ css_classes << "js-section-header section-header" if @lineno_in_section == 0
css_classes += sections.map { |section| "js-s-#{section}" }
end
diff --git a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
index 5372ec6cceb..d1a34c515fa 100644
--- a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
@@ -30,7 +30,7 @@ container_scanning:
services:
- docker:stable-dind
script:
- - if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then { export DOCKER_SERVICE="localhost" ; export DOCKER_HOST="tcp://${DOCKER_SERVICE}:2375" ; } fi
+ - if [[ -n "$KUBERNETES_PORT" ]]; then { export DOCKER_SERVICE="localhost" ; export DOCKER_HOST="tcp://${DOCKER_SERVICE}:2375" ; } fi
- |
if [[ -n "$CI_REGISTRY_USER" ]]; then
echo "Logging to GitLab Container Registry with CI credentials..."
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/external_authorization/client.rb b/lib/gitlab/external_authorization/client.rb
index 60aab2e7044..7985e6dcf7b 100644
--- a/lib/gitlab/external_authorization/client.rb
+++ b/lib/gitlab/external_authorization/client.rb
@@ -48,7 +48,8 @@ module Gitlab
@body ||= begin
body = {
user_identifier: @user.email,
- project_classification_label: @label
+ project_classification_label: @label,
+ identities: @user.identities.map { |identity| { provider: identity.provider, extern_uid: identity.extern_uid } }
}
if @user.ldap_identity
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/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb
index d46b5e3aee3..1103a4eed1d 100644
--- a/lib/gitlab/kubernetes.rb
+++ b/lib/gitlab/kubernetes.rb
@@ -37,15 +37,10 @@ module Gitlab
# Filters an array of pods (as returned by the kubernetes API) by their project and environment
def filter_by_project_environment(items, app, env)
- pods = filter_by_annotation(items, {
+ filter_by_annotation(items, {
'app.gitlab.com/app' => app,
'app.gitlab.com/env' => env
})
- return pods unless pods.empty?
-
- filter_by_label(items, {
- 'app' => env, # deprecated: replaced by app.gitlab.com/env
- })
end
# Converts a pod (as returned by the kubernetes API) into a terminal
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index 9ef23cf849f..097b502316e 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -45,7 +45,7 @@ module Gitlab
if protected?(ProtectedTag, project, ref)
protected_tag_accessible_to?(ref, action: :create)
else
- user.can?(:push_code, project)
+ user.can?(:admin_tag, project)
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb
index 15cd59f041b..a118176eb8a 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/login_via_oauth_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # https://gitlab.com/gitlab-org/quality/nightly/issues/100
- context 'Manage', :orchestrated, :oauth, :quarantine do
+ context 'Manage', :orchestrated, :oauth do
describe 'OAuth login' do
it 'User logs in to GitLab with GitHub OAuth' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
index c7db595284e..6ca7af8a3af 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/94
- context 'Create', :quarantine do
+ context 'Create' do
describe 'Merge request creation from fork' do
it 'user forks a project, submits a merge request and maintainer merges it' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
index 86692623790..a93f2695ec2 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/93
- context 'Create', :quarantine do
+ context 'Create' do
describe 'Merge request squashing' do
it 'user squashes commits while merging' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
index 37a784248d4..2b3d9b1711d 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/62
- context 'Create', :quarantine do
+ context 'Create' do
describe 'Create, list, and delete branches via web' do
master_branch = 'master'
second_branch = 'second-branch'
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/scripts/memory-static b/scripts/memory-static
new file mode 100755
index 00000000000..54f147a7a91
--- /dev/null
+++ b/scripts/memory-static
@@ -0,0 +1,20 @@
+#!/usr/bin/env ruby
+
+require_relative '../lib/gitlab/popen'
+
+full_report_filename, metrics_filename = ARGV
+abort 'usage: memory-static <full_report_filename> <metrics_filename>' unless full_report_filename && metrics_filename
+
+full_report, status = Gitlab::Popen.popen(%w(bundle exec derailed bundle:mem))
+abort 'failed to execute the benchmark' unless status.zero?
+
+File.open(full_report_filename, 'w') do |f|
+ f.write(full_report)
+end
+
+stats = /TOP: (?<total_mibs_str>.*) MiB/.match(full_report.lines.first)
+abort 'failed to process the benchmark output' unless stats
+
+File.open(metrics_filename, 'a') do |f|
+ f.puts "memory_static_total_mb #{stats[:total_mibs_str].to_f.round(1)}"
+end
diff --git a/scripts/memory-static-objects b/scripts/memory-static-objects
new file mode 100755
index 00000000000..2ad38d9717c
--- /dev/null
+++ b/scripts/memory-static-objects
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+
+require_relative '../lib/gitlab/popen'
+
+full_report_filename, metrics_filename = ARGV
+abort 'usage: memory-static-objects <full_report_filename> <metrics_filename>' unless full_report_filename && metrics_filename
+
+full_report, status = Gitlab::Popen.popen(%w(bundle exec derailed bundle:objects))
+abort 'failed to execute the benchmark' unless status.zero?
+
+File.open(full_report_filename, 'w') do |f|
+ f.write(full_report)
+end
+
+allocated_str = full_report.lines[1]
+retained_str = full_report.lines[2]
+allocated_stats = /Total allocated: (?<bytes>.*) bytes \((?<objects>.*) objects\)/.match(allocated_str)
+retained_stats = /Total retained: (?<bytes>.*) bytes \((?<objects>.*) objects\)/.match(retained_str)
+
+abort 'failed to process the benchmark output' unless allocated_stats && retained_stats
+
+File.open(metrics_filename, 'a') do |f|
+ f.puts "memory_static_objects_allocated_mb #{(allocated_stats[:bytes].to_f / (1024 * 1024)).round(1)}"
+ f.puts "memory_static_objects_retained_mb #{(retained_stats[:bytes].to_f / (104 * 1024)).round(1)}"
+ f.puts "memory_static_objects_allocated_items #{allocated_stats[:objects]}"
+ f.puts "memory_static_objects_retained_items #{retained_stats[:objects]}"
+end
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/features/tags/master_creates_tag_spec.rb b/spec/features/tags/developer_creates_tag_spec.rb
index f80ddd050d7..b2ad7ed8f3f 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/developer_creates_tag_spec.rb
@@ -1,11 +1,12 @@
require 'spec_helper'
-describe 'Maintainer creates tag' do
+describe 'Developer creates tag' do
let(:user) { create(:user) }
- let(:project) { create(:project, :repository, namespace: user.namespace) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :repository, namespace: group) }
before do
- project.add_maintainer(user)
+ project.add_developer(user)
sign_in(user)
end
diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/developer_deletes_tag_spec.rb
index bdbbe645779..dc4c7a4fb0a 100644
--- a/spec/features/tags/master_deletes_tag_spec.rb
+++ b/spec/features/tags/developer_deletes_tag_spec.rb
@@ -1,11 +1,12 @@
require 'spec_helper'
-describe 'Maintainer deletes tag' do
+describe 'Developer deletes tag' do
let(:user) { create(:user) }
- let(:project) { create(:project, :repository, namespace: user.namespace) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :repository, namespace: group) }
before do
- project.add_maintainer(user)
+ project.add_developer(user)
sign_in(user)
visit project_tags_path(project)
end
diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/developer_updates_tag_spec.rb
index d8b5b3c4cc4..1e11fc9e5d5 100644
--- a/spec/features/tags/master_updates_tag_spec.rb
+++ b/spec/features/tags/developer_updates_tag_spec.rb
@@ -1,11 +1,12 @@
require 'spec_helper'
-describe 'Maintainer updates tag' do
+describe 'Developer updates tag' do
let(:user) { create(:user) }
- let(:project) { create(:project, :repository, namespace: user.namespace) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :repository, namespace: group) }
before do
- project.add_maintainer(user)
+ project.add_developer(user)
sign_in(user)
visit project_tags_path(project)
end
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/developer_views_tags_spec.rb
index 36cfeb5ed84..09e644c6b97 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/developer_views_tags_spec.rb
@@ -1,7 +1,8 @@
require 'spec_helper'
-describe 'Maintainer views tags' do
+describe 'Developer views tags' do
let(:user) { create(:user) }
+ let(:group) { create(:group) }
before do
project.add_maintainer(user)
@@ -9,7 +10,7 @@ describe 'Maintainer views tags' do
end
context 'when project has no tags' do
- let(:project) { create(:project_empty_repo) }
+ let(:project) { create(:project_empty_repo, namespace: group) }
before do
visit project_path(project)
@@ -25,7 +26,7 @@ describe 'Maintainer views tags' do
end
context 'when project has tags' do
- let(:project) { create(:project, :repository, namespace: user.namespace) }
+ let(:project) { create(:project, :repository, namespace: group) }
let(:repository) { project.repository }
before do
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 1a9caf0ffbb..8a6901ea4e9 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -19,7 +19,7 @@ describe 'Signup' do
end
it 'does not show an error border if the username contains dots (.)' do
- fill_in 'new_user_username', with: 'new.user.username'
+ simulate_input('#new_user_username', 'new.user.username')
wait_for_requests
expect(find('.username')).not_to have_css '.gl-field-error-outline'
@@ -41,7 +41,14 @@ describe 'Signup' do
expect(find('.username')).to have_css '.gl-field-error-outline'
end
- it 'shows an error border if the username contains special characters' do
+ it 'shows a success border if the username is available' do
+ fill_in 'new_user_username', with: 'new-user'
+ wait_for_requests
+
+ expect(find('.username')).to have_css '.gl-field-success-outline'
+ end
+
+ it 'shows an error border if the username contains special characters' do
fill_in 'new_user_username', with: 'new$user!username'
wait_for_requests
@@ -71,7 +78,7 @@ describe 'Signup' do
expect(page).to have_content("Please create a username with only alphanumeric characters.")
end
- it 'shows an error border if the username contains emojis' do
+ it 'shows an error border if the username contains emojis' do
simulate_input('#new_user_username', 'ehsan😀')
expect(find('.username')).to have_css '.gl-field-error-outline'
@@ -82,6 +89,37 @@ describe 'Signup' do
expect(page).to have_content("Invalid input, please avoid emojis")
end
+
+ it 'shows a pending message if the username availability is being fetched' do
+ fill_in 'new_user_username', with: 'new-user'
+
+ expect(find('.username > .validation-pending')).not_to have_css '.hide'
+ end
+
+ it 'shows a success message if the username is available' do
+ fill_in 'new_user_username', with: 'new-user'
+ wait_for_requests
+
+ expect(find('.username > .validation-success')).not_to have_css '.hide'
+ end
+
+ it 'shows an error message if the username is unavailable' do
+ existing_user = create(:user)
+
+ fill_in 'new_user_username', with: existing_user.username
+ wait_for_requests
+
+ expect(find('.username > .validation-error')).not_to have_css '.hide'
+ end
+
+ it 'shows a success message if the username is corrected and then available' do
+ fill_in 'new_user_username', with: 'new-user$'
+ wait_for_requests
+ fill_in 'new_user_username', with: 'new-user'
+ wait_for_requests
+
+ expect(page).to have_content("Username is available.")
+ end
end
describe 'user\'s full name validation', :js do
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/feature_spec.rb b/spec/lib/feature_spec.rb
index a7163048370..6f05914f915 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -31,7 +31,8 @@ describe Feature do
expect(described_class.persisted_names).to be_empty
end
- it 'caches the feature names when request store is active', :request_store do
+ it 'caches the feature names when request store is active',
+ :request_store, :use_clean_rails_memory_store_caching do
Feature::FlipperFeature.create!(key: 'foo')
expect(Feature::FlipperFeature)
@@ -39,6 +40,12 @@ describe Feature do
.once
.and_call_original
+ expect(Rails.cache)
+ .to receive(:fetch)
+ .once
+ .with('flipper:persisted_names', expires_in: 1.minute)
+ .and_call_original
+
2.times do
expect(described_class.persisted_names).to eq(%w[foo])
end
diff --git a/spec/lib/gitlab/checks/tag_check_spec.rb b/spec/lib/gitlab/checks/tag_check_spec.rb
index b1258270611..80e9eb504ad 100644
--- a/spec/lib/gitlab/checks/tag_check_spec.rb
+++ b/spec/lib/gitlab/checks/tag_check_spec.rb
@@ -8,9 +8,8 @@ describe Gitlab::Checks::TagCheck do
describe '#validate!' do
let(:ref) { 'refs/tags/v1.0.0' }
- it 'raises an error' do
- allow(user_access).to receive(:can_do_action?).with(:push_code).and_return(true)
- expect(user_access).to receive(:can_do_action?).with(:admin_project).and_return(false)
+ it 'raises an error when user does not have access' do
+ allow(user_access).to receive(:can_do_action?).with(:admin_tag).and_return(false)
expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to change existing tags on this project.')
end
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb
index ac4612dda92..3d57ce431ab 100644
--- a/spec/lib/gitlab/ci/ansi2html_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2html_spec.rb
@@ -231,8 +231,8 @@ describe Gitlab::Ci::Ansi2html do
it 'prints light red' do
text = "#{section_start}\e[91mHello\e[0m\n#{section_end}"
- header = %{<span class="term-fg-l-red section js-section-header js-s-#{class_name(section_name)}">Hello</span>}
- line_break = %{<span class="section js-section-header js-s-#{class_name(section_name)}"><br/></span>}
+ header = %{<span class="term-fg-l-red section js-section-header section-header js-s-#{class_name(section_name)}">Hello</span>}
+ line_break = %{<span class="section js-section-header section-header js-s-#{class_name(section_name)}"><br/></span>}
line = %{<span class="section line s_#{class_name(section_name)}"></span>}
empty_line = %{<span class="section js-s-#{class_name(section_name)}"></span>}
html = "#{section_start_html}#{header}#{line_break}#{line}#{empty_line}#{section_end_html}"
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/external_authorization/client_spec.rb b/spec/lib/gitlab/external_authorization/client_spec.rb
index fa18c1e56e8..a87f50b4586 100644
--- a/spec/lib/gitlab/external_authorization/client_spec.rb
+++ b/spec/lib/gitlab/external_authorization/client_spec.rb
@@ -19,7 +19,8 @@ describe Gitlab::ExternalAuthorization::Client do
it 'adds the correct params for the user to the body of the request' do
expected_body = {
user_identifier: 'dummy_user@example.com',
- project_classification_label: 'dummy_label'
+ project_classification_label: 'dummy_label',
+ identities: []
}.to_json
expect(Excon).to receive(:post)
.with(dummy_url, hash_including(body: expected_body))
@@ -81,10 +82,11 @@ describe Gitlab::ExternalAuthorization::Client do
provider: 'ldapprovider')
end
- it 'includes the ldap dn for ldap users' do
+ it 'includes the ldap dn and identities for ldap users' do
expected_body = {
user_identifier: 'dummy_user@example.com',
project_classification_label: 'dummy_label',
+ identities: [{ provider: 'ldapprovider', extern_uid: 'external id' }],
user_ldap_dn: 'external id'
}.to_json
expect(Excon).to receive(:post)
@@ -93,5 +95,28 @@ describe Gitlab::ExternalAuthorization::Client do
client.request_access
end
end
+
+ describe 'for non-ldap users with identities' do
+ before do
+ %w(twitter facebook).each do |provider|
+ create(:identity, provider: provider, extern_uid: "#{provider}_external_id", user: user)
+ end
+ end
+
+ it 'includes all the identities' do
+ expected_body = {
+ user_identifier: 'dummy_user@example.com',
+ project_classification_label: 'dummy_label',
+ identities: [
+ { provider: 'twitter', extern_uid: 'twitter_external_id' },
+ { provider: 'facebook', extern_uid: 'facebook_external_id' }
+ ]
+ }.to_json
+ expect(Excon).to receive(:post)
+ .with(dummy_url, hash_including(body: expected_body))
+
+ client.request_access
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 634c370d211..b9c21b3a7bd 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -831,7 +831,7 @@ describe Gitlab::GitAccess do
push_master: true,
push_protected_branch: false,
push_remove_protected_branch: false,
- push_tag: false,
+ push_tag: true,
push_new_tag: true,
push_all: false,
merge_into_protected_branch: false
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/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb
index 57b570a9166..45369b91ed6 100644
--- a/spec/lib/gitlab/kubernetes_spec.rb
+++ b/spec/lib/gitlab/kubernetes_spec.rb
@@ -59,16 +59,6 @@ describe Gitlab::Kubernetes do
describe '#filter_by_project_environment' do
let(:matching_pod) { kube_pod(environment_slug: 'production', project_slug: 'my-cool-app') }
- it 'returns matching legacy env label' do
- matching_pod['metadata']['annotations'].delete('app.gitlab.com/app')
- matching_pod['metadata']['annotations'].delete('app.gitlab.com/env')
- matching_pod['metadata']['labels']['app'] = 'production'
- matching_items = [matching_pod]
- items = matching_items + [kube_pod]
-
- expect(filter_by_project_environment(items, 'my-cool-app', 'production')).to eq(matching_items)
- end
-
it 'returns matching env label' do
matching_items = [matching_pod]
items = matching_items + [kube_pod]
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index c4d8ad5317a..d7b81caddf5 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -129,7 +129,7 @@ describe Ci::PipelineSchedule do
let(:pipeline_schedule) { create(:ci_pipeline_schedule, :every_minute) }
it "updates next_run_at to the sidekiq worker's execution time" do
- Timecop.freeze do
+ Timecop.freeze(2019, 06, 19, 12, 00) do
expect(pipeline_schedule.next_run_at).to eq(cron_worker_next_run_at)
end
end
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index b38cf96de7e..7f4819cbb9a 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -112,7 +112,7 @@ describe Clusters::Applications::Knative do
subject { knative.install_command }
it 'is initialized with latest version' do
- expect(subject.version).to eq('0.5.0')
+ expect(subject.version).to eq('0.6.0')
end
it_behaves_like 'a command'
diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb
index c025d7c882e..3ffe633868f 100644
--- a/spec/models/project_services/microsoft_teams_service_spec.rb
+++ b/spec/models/project_services/microsoft_teams_service_spec.rb
@@ -289,6 +289,18 @@ describe MicrosoftTeamsService do
expect(result).to be_falsy
end
end
+
+ context 'when disabled' do
+ let(:pipeline) do
+ create(:ci_pipeline, :failed, project: project, ref: 'not-the-default-branch')
+ end
+
+ before do
+ chat_service.notify_only_default_branch = false
+ end
+
+ it_behaves_like 'call Microsoft Teams API'
+ end
end
end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 4b723a52b51..fd82150c12a 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -36,7 +36,7 @@ describe ProjectPolicy do
let(:developer_permissions) do
%i[
- admin_milestone admin_merge_request update_merge_request create_commit_status
+ admin_tag admin_milestone admin_merge_request update_merge_request create_commit_status
update_commit_status create_build update_build create_pipeline
update_pipeline create_merge_request_from create_wiki push_code
resolve_note create_container_image update_container_image destroy_container_image
diff --git a/spec/requests/api/container_registry_spec.rb b/spec/requests/api/container_registry_spec.rb
index 4ad15ed6bea..b64f3ea1081 100644
--- a/spec/requests/api/container_registry_spec.rb
+++ b/spec/requests/api/container_registry_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe API::ContainerRegistry do
+ include ExclusiveLeaseHelpers
+
set(:project) { create(:project, :private) }
set(:maintainer) { create(:user) }
set(:developer) { create(:user) }
@@ -155,7 +157,10 @@ describe API::ContainerRegistry do
older_than: '1 day' }
end
+ let(:lease_key) { "container_repository:cleanup_tags:#{root_repository.id}" }
+
it 'schedules cleanup of tags repository' do
+ stub_exclusive_lease(lease_key, timeout: 1.hour)
expect(CleanupContainerRepositoryWorker).to receive(:perform_async)
.with(maintainer.id, root_repository.id, worker_params)
@@ -163,6 +168,22 @@ describe API::ContainerRegistry do
expect(response).to have_gitlab_http_status(:accepted)
end
+
+ context 'called multiple times in one hour' do
+ it 'returns 400 with an error message' do
+ stub_exclusive_lease_taken(lease_key, timeout: 1.hour)
+ subject
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(response.body).to include('This request has already been made.')
+ end
+
+ it 'executes service only for the first time' do
+ expect(CleanupContainerRepositoryWorker).to receive(:perform_async).once
+
+ 2.times { subject }
+ end
+ end
end
end
end
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index d898319e709..c4f4a2cb889 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -10,7 +10,7 @@ describe API::Tags do
let(:current_user) { nil }
before do
- project.add_maintainer(user)
+ project.add_developer(user)
end
describe 'GET /projects/:id/repository/tags' do
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)
diff --git a/spec/support/shared_examples/policies/project_policy_shared_examples.rb b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
index 7a71e2ee370..13b7ade658b 100644
--- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb
+++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
@@ -17,6 +17,7 @@ RSpec.shared_examples 'archived project policies' do
upload_file
resolve_note
award_emoji
+ admin_tag
]
end
diff --git a/spec/workers/cleanup_container_repository_worker_spec.rb b/spec/workers/cleanup_container_repository_worker_spec.rb
index 5bee7294010..9be8f882785 100644
--- a/spec/workers/cleanup_container_repository_worker_spec.rb
+++ b/spec/workers/cleanup_container_repository_worker_spec.rb
@@ -35,13 +35,5 @@ describe CleanupContainerRepositoryWorker, :clean_gitlab_redis_shared_state do
subject.perform(user.id, -1, params)
end.not_to raise_error
end
-
- context 'when executed twice in short period' do
- it 'executes service only for the first time' do
- expect(service).to receive(:execute).once
-
- 2.times { subject.perform(user.id, repository.id, params) }
- end
- end
end
end