Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.dockerignore72
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml2
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_card_list_item.vue41
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue88
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js2
-rw-r--r--app/assets/javascripts/diffs/components/app.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_expansion_cell.vue6
-rw-r--r--app/assets/javascripts/ide/stores/utils.js2
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js8
-rw-r--r--app/assets/javascripts/members.js2
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_url.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/file_row.vue8
-rw-r--r--app/assets/javascripts/vue_shared/directives/autofocusonshow.js39
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss50
-rw-r--r--app/assets/stylesheets/pages/diff.scss3
-rw-r--r--app/assets/stylesheets/pages/issuable.scss1
-rw-r--r--app/assets/stylesheets/pages/reports.scss5
-rw-r--r--app/controllers/projects/blob_controller.rb6
-rw-r--r--app/controllers/projects/starrers_controller.rb4
-rw-r--r--app/models/members/group_member.rb2
-rw-r--r--app/models/repository.rb12
-rw-r--r--app/models/users_star_project.rb1
-rw-r--r--app/presenters/blobs/unfold_presenter.rb43
-rw-r--r--app/services/git/base_hooks_service.rb19
-rw-r--r--app/views/projects/commit/_ajax_signature.html.haml2
-rw-r--r--app/views/projects/commit/_signature_badge.html.haml2
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml29
-rw-r--r--app/views/projects/pages_domains/_certificate.html.haml18
-rw-r--r--app/views/projects/pages_domains/show.html.haml7
-rw-r--r--app/views/projects/pipelines/_info.html.haml2
-rw-r--r--app/views/shared/members/_group.html.haml2
-rw-r--r--app/views/shared/members/_member.html.haml2
-rw-r--r--changelogs/unreleased/63905-discussion-expand-collapse-button-is-only-clickable-on-one-side.yml5
-rw-r--r--changelogs/unreleased/64251-branch-name-set-cache.yml5
-rw-r--r--changelogs/unreleased/64630-add-warning-to-pages-domains-that-obtaining-deploying-ssl-certifica.yml6
-rw-r--r--changelogs/unreleased/64677-delete-directory-webide.yml5
-rw-r--r--changelogs/unreleased/66061-update-tooltip-of-detached-label-state.yml5
-rw-r--r--changelogs/unreleased/66161-replace-expand-icons.yml5
-rw-r--r--changelogs/unreleased/clean-obsolete-css.yml5
-rw-r--r--changelogs/unreleased/fe-fix-merge-url-params-with-plus.yml5
-rw-r--r--changelogs/unreleased/fix-file-row-styling.yml5
-rw-r--r--changelogs/unreleased/sh-fix-pipelines-not-being-created.yml5
-rw-r--r--config.ru18
-rw-r--r--config/initializers/0_inject_enterprise_edition_module.rb2
-rw-r--r--config/initializers/7_prometheus_metrics.rb3
-rw-r--r--config/routes.rb1
-rw-r--r--config/sidekiq_queues.yml1
-rw-r--r--doc/administration/logs.md8
-rw-r--r--doc/api/README.md11
-rw-r--r--doc/api/merge_request_approvals.md294
-rw-r--r--doc/ci/docker/using_docker_images.md19
-rw-r--r--doc/ci/quick_start/img/build_log.pngbin35256 -> 138388 bytes
-rw-r--r--doc/ci/quick_start/img/builds_status.pngbin19107 -> 47887 bytes
-rw-r--r--doc/ci/quick_start/img/pipelines_status.pngbin22872 -> 64605 bytes
-rw-r--r--doc/ci/quick_start/img/runners_activated.pngbin18215 -> 104545 bytes
-rw-r--r--doc/ci/runners/README.md8
-rw-r--r--doc/ci/yaml/README.md2
-rw-r--r--doc/development/architecture.md22
-rw-r--r--doc/development/automatic_ce_ee_merge.md2
-rw-r--r--doc/development/contributing/issue_workflow.md31
-rw-r--r--doc/development/fe_guide/axios.md2
-rw-r--r--doc/development/fe_guide/style_guide_js.md522
-rw-r--r--doc/development/fe_guide/vuex.md2
-rw-r--r--doc/development/hash_indexes.md2
-rw-r--r--doc/development/instrumentation.md2
-rw-r--r--doc/development/interacting_components.md4
-rw-r--r--doc/development/new_fe_guide/development/testing.md363
-rw-r--r--doc/development/new_fe_guide/modules/dirty_submit.md9
-rw-r--r--doc/development/new_fe_guide/style/javascript.md2
-rw-r--r--doc/development/rake_tasks.md2
-rw-r--r--doc/development/sha1_as_binary.md2
-rw-r--r--doc/development/shell_scripting_guide/index.md9
-rw-r--r--doc/development/sql.md30
-rw-r--r--doc/development/testing_guide/best_practices.md23
-rw-r--r--doc/development/testing_guide/ci.md1
-rw-r--r--doc/development/testing_guide/flaky_tests.md4
-rw-r--r--doc/development/testing_guide/frontend_testing.md359
-rw-r--r--doc/development/testing_guide/review_apps.md4
-rw-r--r--doc/development/testing_guide/testing_levels.md1
-rw-r--r--doc/development/verifying_database_capabilities.md8
-rw-r--r--doc/development/what_requires_downtime.md25
-rw-r--r--doc/topics/autodevops/index.md46
-rw-r--r--doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md4
-rw-r--r--doc/user/analytics/cycle_analytics.md182
-rw-r--r--doc/user/analytics/index.md22
-rw-r--r--doc/user/discussions/img/make_suggestion.pngbin28447 -> 115084 bytes
-rw-r--r--doc/user/discussions/img/suggestion.pngbin39775 -> 149758 bytes
-rw-r--r--doc/user/gitlab_com/index.md83
-rw-r--r--doc/user/group/saml_sso/scim_setup.md15
-rw-r--r--doc/user/permissions.md19
-rw-r--r--doc/user/project/cycle_analytics.md182
-rw-r--r--doc/user/project/img/cycle_analytics_landing_page.pngbin64872 -> 0 bytes
-rw-r--r--doc/user/project/integrations/img/jira_service_page.pngbin22464 -> 0 bytes
-rw-r--r--doc/user/project/integrations/img/jira_service_page_v12_2.pngbin0 -> 57327 bytes
-rw-r--r--doc/user/project/integrations/jira.md2
-rw-r--r--doc/user/project/issues/design_management.md14
-rw-r--r--doc/user/project/merge_requests/img/cross_project_dependencies_edit_inaccessible_v12_2.png (renamed from doc/user/project/merge_requests/img/cross-project-dependencies-edit-inaccessible.png)bin19461 -> 19461 bytes
-rw-r--r--doc/user/project/merge_requests/img/cross_project_dependencies_edit_v12_2.png (renamed from doc/user/project/merge_requests/img/cross-project-dependencies-edit.png)bin19302 -> 19302 bytes
-rw-r--r--doc/user/project/merge_requests/img/cross_project_dependencies_view_v12_2.png (renamed from doc/user/project/merge_requests/img/cross-project-dependencies-view.png)bin37528 -> 37528 bytes
-rw-r--r--doc/user/project/merge_requests/img/incrementally_expand_merge_request_diffs_v12_2.pngbin0 -> 96982 bytes
-rw-r--r--doc/user/project/merge_requests/index.md9
-rw-r--r--doc/user/project/merge_requests/merge_request_dependencies.md30
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md29
-rw-r--r--doc/user/project/operations/feature_flags.md15
-rw-r--r--doc/user/project/operations/img/target_users_v12_2.pngbin0 -> 42768 bytes
-rw-r--r--doc/user/project/quick_actions.md18
-rw-r--r--doc/workflow/lfs/lfs_administration.md41
-rw-r--r--doc/workflow/lfs/manage_large_binaries_with_git_lfs.md27
-rw-r--r--doc/workflow/lfs/migrate_from_git_annex_to_git_lfs.md2
-rw-r--r--doc/workflow/repository_mirroring.md31
-rw-r--r--doc/workflow/todos.md4
-rw-r--r--lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml3
-rw-r--r--lib/gitlab/data_builder/push.rb2
-rw-r--r--lib/gitlab/repository_cache_adapter.rb53
-rw-r--r--lib/gitlab/repository_set_cache.rb67
-rw-r--r--lib/gitlab/sentry.rb17
-rw-r--r--lib/gitlab/usage_data.rb4
-rw-r--r--lib/gt_one_coercion.rb7
-rw-r--r--lib/prometheus/cleanup_multiproc_dir_service.rb23
-rw-r--r--locale/gitlab.pot21
-rw-r--r--qa/Dockerfile12
-rw-r--r--qa/Gemfile1
-rw-r--r--qa/Gemfile.lock13
-rw-r--r--qa/README.md5
-rw-r--r--qa/qa.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb3
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb18
-rw-r--r--spec/controllers/projects/starrers_controller_spec.rb14
-rw-r--r--spec/factories/group_members.rb4
-rw-r--r--spec/features/projects/pages_lets_encrypt_spec.rb8
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb30
-rw-r--r--spec/features/signed_commits_spec.rb18
-rw-r--r--spec/frontend/cycle_analytics/stage_nav_item_spec.js177
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js6
-rw-r--r--spec/javascripts/ide/stores/utils_spec.js35
-rw-r--r--spec/javascripts/vue_shared/directives/autofocusonshow_spec.js38
-rw-r--r--spec/lib/gitlab/repository_cache_adapter_spec.rb7
-rw-r--r--spec/lib/gitlab/repository_set_cache_spec.rb73
-rw-r--r--spec/lib/gitlab/sentry_spec.rb29
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb6
-rw-r--r--spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb51
-rw-r--r--spec/models/members/group_member_spec.rb36
-rw-r--r--spec/models/repository_spec.rb52
-rw-r--r--spec/presenters/blobs/unfold_presenter_spec.rb25
-rw-r--r--spec/services/git/branch_push_service_spec.rb16
-rw-r--r--spec/views/projects/pages_domains/show.html.haml_spec.rb66
148 files changed, 2664 insertions, 1394 deletions
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000000..b8e239e4049
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,72 @@
+# `build_from_dir` can't find Dockerfile when `.dockerignore` is "*"
+# See https://github.com/swipely/docker-api/issues/484
+# Ignore all folders except qa/, config/initializers and the root of lib/ since
+# the files we need to build the QA image are in these folders.
+# Following are the files we need:
+# - ./config/initializers/0_inject_enterprise_edition_module.rb
+# - ./lib/gitlab.rb
+# - ./qa/
+# - ./INSTALLATION_TYPE
+# - ./VERSION
+
+/app/
+/bin/
+/builds/
+/changelogs/
+/config/environments/
+/config/helpers/
+/config/knative/
+/config/locales/
+/config/prometheus/
+/config/routes/
+/danger/
+/db/
+/doc/
+/docker/
+/ee/
+/fixtures/
+/templates/
+/lint/
+/lib/api/
+/lib/assets/
+/lib/backup/
+/lib/banzai/
+/lib/bitbucket/
+/lib/server/
+/lib/constraints/
+/lib/registry/
+/lib/policy/
+/lib/feature/
+/lib/flowdock/
+/lib/generators/
+/lib/gitaly/
+/lib/gitlab/
+/lib/api/
+/lib/token/
+/lib/mattermost/
+/lib/teams/
+/lib/storage/
+/lib/auth/
+/lib/peek/
+/lib/prometheus/
+/lib/quality/
+/lib/rouge/
+/lib/flaky/
+/lib/zip/
+/lib/sentry/
+/lib/serializers/
+/lib/support/
+/lib/check/
+/lib/tasks/
+/locale/
+/log/
+/modules/
+/plugins/
+/public/
+/rubocop/
+/scripts/
+/shared/
+/spec/
+/symbol/
+/tmp/
+/vendor/
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index 4a9269ffd82..3cbfa32d9a5 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -50,7 +50,7 @@ build-qa-image:
<<: *review-docker
stage: test
script:
- - time docker build --cache-from ${LATEST_QA_IMAGE} --tag ${QA_IMAGE} ./qa/
+ - time docker build --cache-from ${LATEST_QA_IMAGE} --tag ${QA_IMAGE} --file ./qa/Dockerfile ./
- echo "${CI_JOB_TOKEN}" | docker login --username gitlab-ci-token --password-stdin ${CI_REGISTRY}
- time docker push ${QA_IMAGE}
diff --git a/Gemfile b/Gemfile
index a91399ab3ad..116c94bdaf8 100644
--- a/Gemfile
+++ b/Gemfile
@@ -275,7 +275,6 @@ gem 'font-awesome-rails', '~> 4.7'
gem 'gemojione', '~> 3.3'
gem 'gon', '~> 6.2'
gem 'request_store', '~> 1.3'
-gem 'virtus', '~> 1.0.1'
gem 'base32', '~> 0.3.0'
# Sentry integration
@@ -375,8 +374,6 @@ group :development, :test do
gem 'license_finder', '~> 5.4', require: false
gem 'knapsack', '~> 1.17'
- gem 'activerecord_sane_schema_dumper', '1.0'
-
gem 'stackprof', '~> 0.2.10', require: false
gem 'simple_po_parser', '~> 1.1.2', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index fcc0fb64897..0ecf3aa2840 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -41,8 +41,6 @@ GEM
activerecord-explain-analyze (0.1.0)
activerecord (>= 4)
pg
- activerecord_sane_schema_dumper (1.0)
- rails (>= 5, < 6)
activestorage (5.2.3)
actionpack (= 5.2.3)
activerecord (= 5.2.3)
@@ -1028,7 +1026,6 @@ DEPENDENCIES
ace-rails-ap (~> 4.1.0)
acme-client (~> 2.0.2)
activerecord-explain-analyze (~> 0.1)
- activerecord_sane_schema_dumper (= 1.0)
acts-as-taggable-on (~> 6.0)
addressable (~> 2.5.2)
akismet (~> 2.0)
@@ -1255,7 +1252,6 @@ DEPENDENCIES
unicorn-worker-killer (~> 0.4.4)
validates_hostname (~> 1.0.6)
version_sorter (~> 2.2.4)
- virtus (~> 1.0.1)
vmstat (~> 2.3.0)
webmock (~> 3.5.1)
webpack-rails (~> 0.9.10)
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_card_list_item.vue b/app/assets/javascripts/cycle_analytics/components/stage_card_list_item.vue
new file mode 100644
index 00000000000..d946594a069
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_card_list_item.vue
@@ -0,0 +1,41 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import { GlButton } from '@gitlab/ui';
+
+export default {
+ name: 'StageCardListItem',
+ components: {
+ Icon,
+ GlButton,
+ },
+ props: {
+ isActive: {
+ type: Boolean,
+ required: true,
+ },
+ canEdit: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+};
+</script>
+
+<template>
+ <div :class="{ active: isActive }" class="stage-nav-item d-flex pl-4 pr-4 m-0 mb-1 ml-2 rounded">
+ <slot></slot>
+ <div v-if="canEdit" class="dropdown">
+ <gl-button
+ :title="__('More actions')"
+ class="more-actions-toggle btn btn-transparent p-0"
+ data-toggle="dropdown"
+ >
+ <icon css-classes="icon" name="ellipsis_v" />
+ </gl-button>
+ <ul class="more-actions-dropdown dropdown-menu dropdown-open-left">
+ <slot name="dropdown-options"></slot>
+ </ul>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue b/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue
new file mode 100644
index 00000000000..004d335f572
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue
@@ -0,0 +1,88 @@
+<script>
+import StageCardListItem from './stage_card_list_item.vue';
+
+export default {
+ name: 'StageNavItem',
+ components: {
+ StageCardListItem,
+ },
+ props: {
+ isDefaultStage: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ isActive: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ isUserAllowed: {
+ type: Boolean,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ value: {
+ type: String,
+ default: '',
+ required: false,
+ },
+ canEdit: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+ computed: {
+ hasValue() {
+ return this.value && this.value.length > 0;
+ },
+ editable() {
+ return this.isUserAllowed && this.canEdit;
+ },
+ },
+};
+</script>
+
+<template>
+ <li @click="$emit('select')">
+ <stage-card-list-item :is-active="isActive" :can-edit="editable">
+ <div class="stage-nav-item-cell stage-name p-0" :class="{ 'font-weight-bold': isActive }">
+ {{ title }}
+ </div>
+ <div class="stage-nav-item-cell stage-median mr-4">
+ <template v-if="isUserAllowed">
+ <span v-if="hasValue">{{ value }}</span>
+ <span v-else class="stage-empty">{{ __('Not enough data') }}</span>
+ </template>
+ <template v-else>
+ <span class="not-available">{{ __('Not available') }}</span>
+ </template>
+ </div>
+ <template v-slot:dropdown-options>
+ <template v-if="isDefaultStage">
+ <li>
+ <button type="button" class="btn-default btn-transparent">
+ {{ __('Hide stage') }}
+ </button>
+ </li>
+ </template>
+ <template v-else>
+ <li>
+ <button type="button" class="btn-default btn-transparent">
+ {{ __('Edit stage') }}
+ </button>
+ </li>
+ <li>
+ <button type="button" class="btn-danger danger">
+ {{ __('Remove stage') }}
+ </button>
+ </li>
+ </template>
+ </template>
+ </stage-card-list-item>
+ </li>
+</template>
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index 671405602cc..b3ae47af750 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -12,6 +12,7 @@ import stageComponent from './components/stage_component.vue';
import stageReviewComponent from './components/stage_review_component.vue';
import stageStagingComponent from './components/stage_staging_component.vue';
import stageTestComponent from './components/stage_test_component.vue';
+import stageNavItem from './components/stage_nav_item.vue';
import CycleAnalyticsService from './cycle_analytics_service';
import CycleAnalyticsStore from './cycle_analytics_store';
@@ -41,6 +42,7 @@ export default () => {
import('ee_component/analytics/shared/components/projects_dropdown_filter.vue'),
DateRangeDropdown: () =>
import('ee_component/analytics/shared/components/date_range_dropdown.vue'),
+ 'stage-nav-item': stageNavItem,
},
mixins: [filterMixins],
data() {
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 81da0754752..19b85710710 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -305,7 +305,7 @@ export default {
<div
v-show="showTreeList"
:style="{ width: `${treeWidth}px` }"
- class="diff-tree-list js-diff-tree-list"
+ class="diff-tree-list js-diff-tree-list mr-3"
>
<panel-resizer
:size.sync="treeWidth"
diff --git a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
index 925385fa98a..43edc116ed1 100644
--- a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
@@ -223,8 +223,7 @@ export default {
:title="__('Expand up')"
@click="handleExpandLines(EXPAND_UP)"
>
- <!-- TODO: remove style & replace with correct icon, waiting for MR https://gitlab.com/gitlab-org/gitlab-design/issues/499 -->
- <icon :size="12" name="expand-left" aria-hidden="true" style="transform: rotate(270deg);" />
+ <icon :size="12" name="expand-up" aria-hidden="true" />
</a>
<a class="mx-2 cursor-pointer js-unfold-all" @click="handleExpandLines()">
<span>{{ s__('Diffs|Show all lines') }}</span>
@@ -238,8 +237,7 @@ export default {
:title="__('Expand down')"
@click="handleExpandLines(EXPAND_DOWN)"
>
- <!-- TODO: remove style & replace with correct icon, waiting for MR https://gitlab.com/gitlab-org/gitlab-design/issues/499 -->
- <icon :size="12" name="expand-left" aria-hidden="true" style="transform: rotate(90deg);" />
+ <icon :size="12" name="expand-down" aria-hidden="true" />
</a>
</div>
</td>
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index 04e86afb268..52200ce7847 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -129,7 +129,7 @@ export const commitActionForFile = file => {
export const getCommitFiles = stagedFiles =>
stagedFiles.reduce((acc, file) => {
- if (file.moved) return acc;
+ if (file.moved || file.type === 'tree') return acc;
return acc.concat({
...file,
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 1336b6a5461..7ead9d46fbb 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -1,5 +1,11 @@
import { join as joinPaths } from 'path';
+// Returns a decoded url parameter value
+// - Treats '+' as '%20'
+function decodeUrlParameter(val) {
+ return decodeURIComponent(val.replace(/\+/g, '%20'));
+}
+
// Returns an array containing the value(s) of the
// of the key passed as an argument
export function getParameterValues(sParam, url = window.location) {
@@ -30,7 +36,7 @@ export function mergeUrlParams(params, url) {
.forEach(part => {
if (part.length) {
const kv = part.split('=');
- merged[decodeURIComponent(kv[0])] = decodeURIComponent(kv.slice(1).join('='));
+ merged[decodeUrlParameter(kv[0])] = decodeUrlParameter(kv.slice(1).join('='));
}
});
}
diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js
index af2697444f2..d719fd8748d 100644
--- a/app/assets/javascripts/members.js
+++ b/app/assets/javascripts/members.js
@@ -17,6 +17,8 @@ export default class Members {
}
dropdownClicked(options) {
+ options.e.preventDefault();
+
this.formSubmit(null, options.$el);
}
diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue
index e01080b04d6..4efc1d2408a 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue
@@ -104,7 +104,7 @@ export default {
v-gl-tooltip
:title="
__(
- 'The code of a detached pipeline is tested against the source branch instead of merged results',
+ 'Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more on the documentation for Pipelines for Merged Results.',
)
"
class="js-pipeline-url-detached badge badge-info"
diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue
index fe5289ff371..f49e69c473b 100644
--- a/app/assets/javascripts/vue_shared/components/file_row.vue
+++ b/app/assets/javascripts/vue_shared/components/file_row.vue
@@ -146,6 +146,7 @@ export default {
<span ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated">
<file-icon
v-if="!showChangedIcon || file.type === 'tree'"
+ class="file-row-icon"
:file-name="file.name"
:loading="file.loading"
:folder="isTree"
@@ -223,13 +224,8 @@ export default {
white-space: nowrap;
}
-.file-row-name svg {
+.file-row-name .file-row-icon {
margin-right: 2px;
vertical-align: middle;
}
-
-.file-row-name .loading-container {
- display: inline-block;
- margin-right: 4px;
-}
</style>
diff --git a/app/assets/javascripts/vue_shared/directives/autofocusonshow.js b/app/assets/javascripts/vue_shared/directives/autofocusonshow.js
new file mode 100644
index 00000000000..4659ec20ceb
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/directives/autofocusonshow.js
@@ -0,0 +1,39 @@
+/**
+ * Input/Textarea Autofocus Directive for Vue
+ */
+export default {
+ /**
+ * Set focus when element is rendered, but
+ * is not visible, using IntersectionObserver
+ *
+ * @param {Element} el Target element
+ */
+ inserted(el) {
+ if ('IntersectionObserver' in window) {
+ // Element visibility is dynamic, so we attach observer
+ el.visibilityObserver = new IntersectionObserver(entries => {
+ entries.forEach(entry => {
+ // Combining `intersectionRatio > 0` and
+ // element's `offsetParent` presence will
+ // deteremine if element is truely visible
+ if (entry.intersectionRatio > 0 && entry.target.offsetParent) {
+ entry.target.focus();
+ }
+ });
+ });
+
+ // Bind the observer.
+ el.visibilityObserver.observe(el, { root: document.documentElement });
+ }
+ },
+ /**
+ * Detach observer on unbind hook.
+ *
+ * @param {Element} el Target element
+ */
+ unbind(el) {
+ if (el.visibilityObserver) {
+ el.visibilityObserver.disconnect();
+ }
+ },
+};
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index 2b932d164a5..d80155a416d 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -51,27 +51,19 @@
}
.stage-header {
- width: 26%;
- padding-left: $gl-padding;
+ width: 18.5%;
}
.median-header {
- width: 14%;
+ width: 21.5%;
}
.event-header {
width: 45%;
- padding-left: $gl-padding;
}
.total-time-header {
width: 15%;
- text-align: right;
- padding-right: $gl-padding;
- }
-
- .stage-name {
- font-weight: $gl-font-weight-bold;
}
}
@@ -153,23 +145,13 @@
}
.stage-nav-item {
- display: flex;
line-height: 65px;
- border-top: 1px solid transparent;
- border-bottom: 1px solid transparent;
- border-right: 1px solid $border-color;
- background-color: $gray-light;
+ border: 1px solid $border-color;
&.active {
- background-color: transparent;
- border-right-color: transparent;
- border-top-color: $border-color;
- border-bottom-color: $border-color;
- box-shadow: inset 2px 0 0 0 $blue-500;
-
- .stage-name {
- font-weight: $gl-font-weight-bold;
- }
+ background: $blue-50;
+ border-color: $blue-300;
+ box-shadow: inset 4px 0 0 0 $blue-500;
}
&:hover:not(.active) {
@@ -178,24 +160,12 @@
cursor: pointer;
}
- &:first-child {
- border-top: 0;
- }
-
- &:last-child {
- border-bottom: 0;
- }
-
- .stage-nav-item-cell {
- &.stage-median {
- margin-left: auto;
- margin-right: $gl-padding;
- min-width: calc(35% - #{$gl-padding});
- }
+ .stage-nav-item-cell.stage-name {
+ width: 44.5%;
}
- .stage-name {
- padding-left: 16px;
+ .stage-nav-item-cell.stage-median {
+ min-width: 43%;
}
.stage-empty,
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index ffb27e54f34..77a2fd6b876 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -1032,7 +1032,6 @@ table.code {
$top-pos: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
top: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
max-height: calc(100vh - #{$top-pos});
- padding-right: $gl-padding;
z-index: 202;
.with-performance-bar & {
@@ -1043,7 +1042,7 @@ table.code {
.drag-handle {
bottom: 16px;
- transform: translateX(-6px);
+ transform: translateX(10px);
}
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 66b4f3bad2b..fa52ce6402d 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -522,6 +522,7 @@
}
.multiple-users {
+ position: relative;
height: 24px;
margin-bottom: 17px;
margin-top: 4px;
diff --git a/app/assets/stylesheets/pages/reports.scss b/app/assets/stylesheets/pages/reports.scss
index 85e9f303dde..0fbf7033aa5 100644
--- a/app/assets/stylesheets/pages/reports.scss
+++ b/app/assets/stylesheets/pages/reports.scss
@@ -48,11 +48,6 @@
padding: $gl-padding-top $gl-padding;
border-top: 1px solid $border-color;
}
-
- .report-block-list-icon .loading-container {
- position: relative;
- left: -2px;
- }
}
.report-block-container {
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index b04ffe80db4..4125f44d00a 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -92,7 +92,7 @@ class Projects::BlobController < Projects::ApplicationController
def diff
apply_diff_view_cookie!
- @form = Blobs::UnfoldPresenter.new(blob, params.to_unsafe_h)
+ @form = Blobs::UnfoldPresenter.new(blob, diff_params)
# keep only json rendering when
# https://gitlab.com/gitlab-org/gitlab-ce/issues/44988 is done
@@ -239,4 +239,8 @@ class Projects::BlobController < Projects::ApplicationController
def tree_path
@path.rpartition('/').first
end
+
+ def diff_params
+ params.permit(:full, :since, :to, :bottom, :unfold, :offset, :indent)
+ end
end
diff --git a/app/controllers/projects/starrers_controller.rb b/app/controllers/projects/starrers_controller.rb
index e4093bed0ef..4efe956e973 100644
--- a/app/controllers/projects/starrers_controller.rb
+++ b/app/controllers/projects/starrers_controller.rb
@@ -5,11 +5,11 @@ class Projects::StarrersController < Projects::ApplicationController
def index
@starrers = UsersStarProjectsFinder.new(@project, params, current_user: @current_user).execute
+ @sort = params[:sort].presence || sort_value_name
+ @starrers = @starrers.preload_users.sort_by_attribute(@sort).page(params[:page])
@public_count = @project.starrers.with_public_profile.size
@total_count = @project.starrers.size
@private_count = @total_count - @public_count
- @sort = params[:sort].presence || sort_value_name
- @starrers = @starrers.sort_by_attribute(@sort).page(params[:page])
end
private
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index f6b19317c50..3d6f397e599 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -15,8 +15,8 @@ class GroupMember < Member
default_scope { where(source_type: SOURCE_TYPE) }
scope :of_groups, ->(groups) { where(source_id: groups.select(:id)) }
-
scope :count_users_by_group_id, -> { joins(:user).group(:source_id).count }
+ scope :of_ldap_type, -> { where(ldap: true) }
after_create :update_two_factor_requirement, unless: :invite?
after_destroy :update_two_factor_requirement, unless: :invite?
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 6f63cd32da4..b957b9b0bdd 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -239,13 +239,13 @@ class Repository
def branch_exists?(branch_name)
return false unless raw_repository
- branch_names.include?(branch_name)
+ branch_names_include?(branch_name)
end
def tag_exists?(tag_name)
return false unless raw_repository
- tag_names.include?(tag_name)
+ tag_names_include?(tag_name)
end
def ref_exists?(ref)
@@ -565,10 +565,10 @@ class Repository
end
delegate :branch_names, to: :raw_repository
- cache_method :branch_names, fallback: []
+ cache_method_as_redis_set :branch_names, fallback: []
delegate :tag_names, to: :raw_repository
- cache_method :tag_names, fallback: []
+ cache_method_as_redis_set :tag_names, fallback: []
delegate :branch_count, :tag_count, :has_visible_content?, to: :raw_repository
cache_method :branch_count, fallback: 0
@@ -1130,6 +1130,10 @@ class Repository
@cache ||= Gitlab::RepositoryCache.new(self)
end
+ def redis_set_cache
+ @redis_set_cache ||= Gitlab::RepositorySetCache.new(self)
+ end
+
def request_store_cache
@request_store_cache ||= Gitlab::RepositoryCache.new(self, backend: Gitlab::SafeRequestStore)
end
diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb
index 3c7a805cc5c..c633e2d8b3d 100644
--- a/app/models/users_star_project.rb
+++ b/app/models/users_star_project.rb
@@ -17,6 +17,7 @@ class UsersStarProject < ApplicationRecord
scope :by_project, -> (project) { where(project_id: project.id) }
scope :with_visible_profile, -> (user) { joins(:user).merge(User.with_visible_profile(user)) }
scope :with_public_profile, -> { joins(:user).merge(User.with_public_profile) }
+ scope :preload_users, -> { preload(:user) }
class << self
def sort_by_attribute(method)
diff --git a/app/presenters/blobs/unfold_presenter.rb b/app/presenters/blobs/unfold_presenter.rb
index 4c6dd6895cf..a256dd05a4d 100644
--- a/app/presenters/blobs/unfold_presenter.rb
+++ b/app/presenters/blobs/unfold_presenter.rb
@@ -1,30 +1,34 @@
# frozen_string_literal: true
-require 'gt_one_coercion'
-
module Blobs
class UnfoldPresenter < BlobPresenter
- include Virtus.model
+ include ActiveModel::Attributes
+ include ActiveModel::AttributeAssignment
include Gitlab::Utils::StrongMemoize
- attribute :full, Boolean, default: false
- attribute :since, GtOneCoercion
- attribute :to, Integer
- attribute :bottom, Boolean
- attribute :unfold, Boolean, default: true
- attribute :offset, Integer
- attribute :indent, Integer, default: 0
+ attribute :full, :boolean, default: false
+ attribute :since, :integer, default: 1
+ attribute :to, :integer, default: 1
+ attribute :bottom, :boolean, default: false
+ attribute :unfold, :boolean, default: true
+ attribute :offset, :integer, default: 0
+ attribute :indent, :integer, default: 0
+
+ alias_method :full?, :full
+ alias_method :bottom?, :bottom
+ alias_method :unfold?, :unfold
def initialize(blob, params)
+ super(blob)
+ self.attributes = params
+
# Load all blob data first as we need to ensure they're all loaded first
# so we can accurately show the rest of the diff when unfolding.
load_all_blob_data
- @subject = blob
@all_lines = blob.data.lines
- super(params)
- self.attributes = prepare_attributes
+ handle_full_or_end!
end
# Returns an array of Gitlab::Diff::Line with match line added
@@ -56,21 +60,18 @@ module Blobs
private
- def prepare_attributes
- return attributes unless full? || to == -1
+ def handle_full_or_end!
+ return unless full? || to == -1
- full_opts = {
- since: 1,
+ self.since = 1 if full?
+
+ self.attributes = {
to: all_lines_size,
bottom: false,
unfold: false,
offset: 0,
indent: 0
}
-
- return full_opts if full?
-
- full_opts.merge(attributes.slice(:since))
end
def all_lines_size
diff --git a/app/services/git/base_hooks_service.rb b/app/services/git/base_hooks_service.rb
index 3fd38444196..47c308c8280 100644
--- a/app/services/git/base_hooks_service.rb
+++ b/app/services/git/base_hooks_service.rb
@@ -56,7 +56,7 @@ module Git
return unless params.fetch(:create_pipelines, true)
Ci::CreatePipelineService
- .new(project, current_user, base_params)
+ .new(project, current_user, pipeline_params)
.execute(:push, pipeline_options)
end
@@ -75,24 +75,29 @@ module Git
ProjectCacheWorker.perform_async(project.id, file_types, [], false)
end
- def base_params
+ def pipeline_params
{
- oldrev: params[:oldrev],
- newrev: params[:newrev],
+ before: params[:oldrev],
+ after: params[:newrev],
ref: params[:ref],
- push_options: params[:push_options] || {}
+ push_options: params[:push_options] || {},
+ checkout_sha: Gitlab::DataBuilder::Push.checkout_sha(
+ project.repository, params[:newrev], params[:ref])
}
end
def push_data_params(commits:, with_changed_files: true)
- base_params.merge(
+ {
+ oldrev: params[:oldrev],
+ newrev: params[:newrev],
+ ref: params[:ref],
project: project,
user: current_user,
commits: commits,
message: event_message,
commits_count: commits_count,
with_changed_files: with_changed_files
- )
+ }
end
def event_push_data
diff --git a/app/views/projects/commit/_ajax_signature.html.haml b/app/views/projects/commit/_ajax_signature.html.haml
index ae9aef5a9b0..e1bf0940f59 100644
--- a/app/views/projects/commit/_ajax_signature.html.haml
+++ b/app/views/projects/commit/_ajax_signature.html.haml
@@ -1,2 +1,2 @@
- if commit.has_signature?
- %a{ href: 'javascript:void(0)', tabindex: 0, class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'top', title: _('GPG signature (loading...)'), 'commit-sha' => commit.sha } }
+ %button{ tabindex: 0, class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'top', title: _('GPG signature (loading...)'), 'commit-sha' => commit.sha } }
diff --git a/app/views/projects/commit/_signature_badge.html.haml b/app/views/projects/commit/_signature_badge.html.haml
index 1331fa179fc..cbd998c60ef 100644
--- a/app/views/projects/commit/_signature_badge.html.haml
+++ b/app/views/projects/commit/_signature_badge.html.haml
@@ -24,5 +24,5 @@
= link_to(_('Learn more about signing commits'), help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
-%a{ href: 'javascript:void(0)', tabindex: 0, class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'top', title: title, content: content } }
+%button{ tabindex: 0, class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'top', title: title, content: content } }
= label
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index 59f0afd59e6..2b594c125f4 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -34,40 +34,29 @@
{{ n__('Last %d day', 'Last %d days', 90) }}
.stage-panel-container
.card.stage-panel
- .card-header
+ .card-header.border-bottom-0
%nav.col-headers
%ul
- %li.stage-header
- %span.stage-name
+ %li.stage-header.pl-5
+ %span.stage-name.font-weight-bold
{{ s__('ProjectLifecycle|Stage') }}
%i.has-tooltip.fa.fa-question-circle{ "data-placement" => "top", title: _("The phase of the development lifecycle."), "aria-hidden" => "true" }
%li.median-header
- %span.stage-name
+ %span.stage-name.font-weight-bold
{{ __('Median') }}
%i.has-tooltip.fa.fa-question-circle{ "data-placement" => "top", title: _("The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."), "aria-hidden" => "true" }
- %li.event-header
- %span.stage-name
+ %li.event-header.pl-3
+ %span.stage-name.font-weight-bold
{{ currentStage ? __(currentStage.legend) : __('Related Issues') }}
%i.has-tooltip.fa.fa-question-circle{ "data-placement" => "top", title: _("The collection of events added to the data gathered for that stage."), "aria-hidden" => "true" }
- %li.total-time-header
- %span.stage-name
+ %li.total-time-header.pr-5.text-right
+ %span.stage-name.font-weight-bold
{{ __('Total Time') }}
%i.has-tooltip.fa.fa-question-circle{ "data-placement" => "top", title: _("The time taken by each data entry gathered by that stage."), "aria-hidden" => "true" }
.stage-panel-body
%nav.stage-nav
%ul
- %li.stage-nav-item{ ':class' => '{ active: stage.active }', '@click' => 'selectStage(stage)', "v-for" => "stage in state.stages" }
- .stage-nav-item-cell.stage-name
- {{ stage.title }}
- .stage-nav-item-cell.stage-median
- %template{ "v-if" => "stage.isUserAllowed" }
- %span{ "v-if" => "stage.value" }
- {{ stage.value }}
- %span.stage-empty{ "v-else" => true }
- {{ __('Not enough data') }}
- %template{ "v-else" => true }
- %span.not-available
- {{ __('Not available') }}
+ %stage-nav-item{ "v-for" => "stage in state.stages", ":key" => '`ca-stage-title-${stage.title}`', '@select' => 'selectStage(stage)', ":title" => "stage.title", ":is-user-allowed" => "stage.isUserAllowed", ":value" => "stage.value", ":is-active" => "stage.active" }
.section.stage-events
%template{ "v-if" => "isLoadingStage" }
= icon("spinner spin")
diff --git a/app/views/projects/pages_domains/_certificate.html.haml b/app/views/projects/pages_domains/_certificate.html.haml
new file mode 100644
index 00000000000..42631fca5e8
--- /dev/null
+++ b/app/views/projects/pages_domains/_certificate.html.haml
@@ -0,0 +1,18 @@
+- if @domain.auto_ssl_enabled?
+ - if @domain.enabled?
+ - if @domain.certificate_text
+ %pre
+ = @domain.certificate_text
+ - else
+ .bs-callout.bs-callout-info
+ = _("GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later.")
+ - else
+ .bs-callout.bs-callout-warning
+ = _("A Let's Encrypt SSL certificate can not be obtained until your domain is verified.")
+- else
+ - if @domain.certificate_text
+ %pre
+ = @domain.certificate_text
+ - else
+ .light
+ = _("missing")
diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml
index e9019175219..d0b54946f7e 100644
--- a/app/views/projects/pages_domains/show.html.haml
+++ b/app/views/projects/pages_domains/show.html.haml
@@ -60,9 +60,4 @@
%td
= _("Certificate")
%td
- - if @domain.certificate_text
- %pre
- = @domain.certificate_text
- - else
- .light
- = _("missing")
+ = render 'certificate'
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 2b56ada8b73..9614f33fe2f 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -43,7 +43,7 @@
} }
Auto DevOps
- if @pipeline.detached_merge_request_pipeline?
- %span.js-pipeline-url-mergerequest.badge.badge-info.has-tooltip{ title: "The code of a detached pipeline is tested against the source branch instead of merged results" }
+ %span.js-pipeline-url-mergerequest.badge.badge-info.has-tooltip{ title: _('Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more on the documentation for Pipelines for Merged Results.') }
detached
- if @pipeline.stuck?
%span.js-pipeline-url-stuck.badge.badge-warning
diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml
index e83ca5eaab8..42a823e3a8d 100644
--- a/app/views/shared/members/_group.html.haml
+++ b/app/views/shared/members/_group.html.haml
@@ -32,7 +32,7 @@
%ul
- Gitlab::Access.options.each do |role, role_id|
%li
- = link_to role, "javascript:void(0)",
+ = link_to role, '#',
class: ("is-active" if group_link.group_access == role_id),
data: { id: role_id, el_id: dom_id }
.clearable-input.member-form-control.d-sm-inline-block
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index 331283f7eec..6762f211a80 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -82,7 +82,7 @@
%ul
- member.valid_level_roles.each do |role, role_id|
%li
- = link_to role, "javascript:void(0)",
+ = link_to role, '#',
class: ("is-active" if member.access_level == role_id),
data: { id: role_id, el_id: dom_id(member) }
= render_if_exists 'shared/members/ee/revert_ldap_group_sync_option',
diff --git a/changelogs/unreleased/63905-discussion-expand-collapse-button-is-only-clickable-on-one-side.yml b/changelogs/unreleased/63905-discussion-expand-collapse-button-is-only-clickable-on-one-side.yml
new file mode 100644
index 00000000000..61cd69e88bf
--- /dev/null
+++ b/changelogs/unreleased/63905-discussion-expand-collapse-button-is-only-clickable-on-one-side.yml
@@ -0,0 +1,5 @@
+---
+title: All of discussion expand/collapse button is clickable
+merge_request: 31730
+author:
+type: fixed
diff --git a/changelogs/unreleased/64251-branch-name-set-cache.yml b/changelogs/unreleased/64251-branch-name-set-cache.yml
new file mode 100644
index 00000000000..6ce4bdf5e43
--- /dev/null
+++ b/changelogs/unreleased/64251-branch-name-set-cache.yml
@@ -0,0 +1,5 @@
+---
+title: Cache branch and tag names as Redis sets
+merge_request: 30476
+author:
+type: performance
diff --git a/changelogs/unreleased/64630-add-warning-to-pages-domains-that-obtaining-deploying-ssl-certifica.yml b/changelogs/unreleased/64630-add-warning-to-pages-domains-that-obtaining-deploying-ssl-certifica.yml
new file mode 100644
index 00000000000..bd2c9a3e2dc
--- /dev/null
+++ b/changelogs/unreleased/64630-add-warning-to-pages-domains-that-obtaining-deploying-ssl-certifica.yml
@@ -0,0 +1,6 @@
+---
+title: Add warning to pages domains that obtaining/deploying SSL certificates through
+ Let's Encrypt can take some time
+merge_request: 31765
+author:
+type: other
diff --git a/changelogs/unreleased/64677-delete-directory-webide.yml b/changelogs/unreleased/64677-delete-directory-webide.yml
new file mode 100644
index 00000000000..27d596b6b19
--- /dev/null
+++ b/changelogs/unreleased/64677-delete-directory-webide.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed removing directories in Web IDE
+merge_request: 31727
+author:
+type: fixed
diff --git a/changelogs/unreleased/66061-update-tooltip-of-detached-label-state.yml b/changelogs/unreleased/66061-update-tooltip-of-detached-label-state.yml
new file mode 100644
index 00000000000..826d5548999
--- /dev/null
+++ b/changelogs/unreleased/66061-update-tooltip-of-detached-label-state.yml
@@ -0,0 +1,5 @@
+---
+title: Updates tooltip of 'detached' label/state
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/66161-replace-expand-icons.yml b/changelogs/unreleased/66161-replace-expand-icons.yml
new file mode 100644
index 00000000000..e28e01108a8
--- /dev/null
+++ b/changelogs/unreleased/66161-replace-expand-icons.yml
@@ -0,0 +1,5 @@
+---
+title: Replaced expand diff icons
+merge_request: 31907
+author:
+type: changed
diff --git a/changelogs/unreleased/clean-obsolete-css.yml b/changelogs/unreleased/clean-obsolete-css.yml
new file mode 100644
index 00000000000..d9716d16762
--- /dev/null
+++ b/changelogs/unreleased/clean-obsolete-css.yml
@@ -0,0 +1,5 @@
+---
+title: Fix MR reports section loading icon alignment
+merge_request: 31897
+author:
+type: fixed
diff --git a/changelogs/unreleased/fe-fix-merge-url-params-with-plus.yml b/changelogs/unreleased/fe-fix-merge-url-params-with-plus.yml
new file mode 100644
index 00000000000..7ca8ae0a8f6
--- /dev/null
+++ b/changelogs/unreleased/fe-fix-merge-url-params-with-plus.yml
@@ -0,0 +1,5 @@
+---
+title: Fix search preserving space when change branch
+merge_request: 31973
+author: minghuan lei
+type: fixed
diff --git a/changelogs/unreleased/fix-file-row-styling.yml b/changelogs/unreleased/fix-file-row-styling.yml
new file mode 100644
index 00000000000..a4427fdd48f
--- /dev/null
+++ b/changelogs/unreleased/fix-file-row-styling.yml
@@ -0,0 +1,5 @@
+---
+title: Fix loading icon causing text to jump in file row of Web IDE
+merge_request: 31884
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-pipelines-not-being-created.yml b/changelogs/unreleased/sh-fix-pipelines-not-being-created.yml
new file mode 100644
index 00000000000..a6937eae588
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-pipelines-not-being-created.yml
@@ -0,0 +1,5 @@
+---
+title: Fix pipelines not always being created after a push
+merge_request: 31927
+author:
+type: fixed
diff --git a/config.ru b/config.ru
index f6a7dca0542..750c84f7642 100644
--- a/config.ru
+++ b/config.ru
@@ -17,24 +17,16 @@ end
require ::File.expand_path('../config/environment', __FILE__)
-# The following is necessary to ensure stale Prometheus metrics don't accumulate over time.
-# It needs to be done as early as here to ensure metrics files aren't deleted.
-# After we hit our app in `warmup`, first metrics and corresponding files already being created,
-# for example in `lib/gitlab/metrics/requests_rack_middleware.rb`.
-def cleanup_prometheus_multiproc_dir
- if dir = ::Prometheus::Client.configuration.multiprocess_files_dir
- old_metrics = Dir[File.join(dir, '*.db')]
-
- FileUtils.rm_rf(old_metrics)
- end
-end
-
def master_process?
Prometheus::PidProvider.worker_id.in? %w(unicorn_master puma_master)
end
warmup do |app|
- cleanup_prometheus_multiproc_dir if master_process?
+ # The following is necessary to ensure stale Prometheus metrics don't accumulate over time.
+ # It needs to be done as early as here to ensure metrics files aren't deleted.
+ # After we hit our app in `warmup`, first metrics and corresponding files already being created,
+ # for example in `lib/gitlab/metrics/requests_rack_middleware.rb`.
+ Prometheus::CleanupMultiprocDirService.new.execute if master_process?
client = Rack::MockRequest.new(app)
client.get('/')
diff --git a/config/initializers/0_inject_enterprise_edition_module.rb b/config/initializers/0_inject_enterprise_edition_module.rb
index 4b21732e179..39595e23abe 100644
--- a/config/initializers/0_inject_enterprise_edition_module.rb
+++ b/config/initializers/0_inject_enterprise_edition_module.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'active_support/inflector'
+
module InjectEnterpriseEditionModule
def prepend_if_ee(constant)
prepend(constant.constantize) if Gitlab.ee?
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index 70e5dcd042e..143b34b5368 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -32,6 +32,9 @@ end
Sidekiq.configure_server do |config|
config.on(:startup) do
+ # webserver metrics are cleaned up in config.ru: `warmup` block
+ Prometheus::CleanupMultiprocDirService.new.execute
+
Gitlab::Metrics::SidekiqMetricsExporter.instance.start
end
end
diff --git a/config/routes.rb b/config/routes.rb
index fdef31429f3..d633228a495 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -109,6 +109,7 @@ Rails.application.routes.draw do
Gitlab.ee do
draw :smartcard
draw :jira_connect
+ draw :username
end
Gitlab.ee do
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index c7586aa1e38..ea165508d29 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -113,3 +113,4 @@
- [elastic_namespace_indexer, 1]
- [export_csv, 1]
- [incident_management, 2]
+ - [jira_connect, 1]
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index 306d611f6bf..a57ef8ddc7d 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -284,13 +284,16 @@ Introduced in GitLab 11.3. This file lives in `/var/log/gitlab/gitlab-rails/impo
Omnibus GitLab packages or in `/home/git/gitlab/log/importer.log` for
installations from source.
-## `auth.log`
+## `auth.log`
Introduced in GitLab 12.0. This file lives in `/var/log/gitlab/gitlab-rails/auth.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/auth.log` for
installations from source.
-It logs information whenever [Rack Attack] registers an abusive request.
+This log records:
+
+- Information whenever [Rack Attack] registers an abusive request.
+- Requests over the [Rate Limit] on raw endpoints.
NOTE: **Note:**
From [%12.1](https://gitlab.com/gitlab-org/gitlab-ce/issues/62756), user id and username are available on this log.
@@ -334,3 +337,4 @@ installations from source.
[repocheck]: repository_checks.md
[Rack Attack]: ../security/rack_attack.md
+[Rate Limit]: ../user/admin_area/settings/rate_limits_on_raw_endpoints.md
diff --git a/doc/api/README.md b/doc/api/README.md
index b7ee710b87a..9156d719e11 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -77,11 +77,12 @@ authentication is not provided. For
those cases where it is not required, this will be mentioned in the documentation
for each individual endpoint. For example, the [`/projects/:id` endpoint](projects.md).
-There are three ways to authenticate with the GitLab API:
+There are four ways to authenticate with the GitLab API:
1. [OAuth2 tokens](#oauth2-tokens)
1. [Personal access tokens](#personal-access-tokens)
1. [Session cookie](#session-cookie)
+1. [GitLab CI job token](#gitlab-ci-job-token-premium) **(PREMIUM)**
For admins who want to authenticate with the API as a specific user, or who want to build applications or scripts that do so, two options are available:
@@ -151,6 +152,14 @@ The primary user of this authentication method is the web frontend of GitLab its
which can use the API as the authenticated user to get a list of their projects,
for example, without needing to explicitly pass an access token.
+### GitLab CI job token **(PREMIUM)**
+
+With a few API endpoints you can use a [GitLab CI job token](../user/project/new_ci_build_permissions_model.md#job-token)
+to authenticate with the API:
+
+* [Get job artifacts](jobs.md#get-job-artifacts)
+* [Pipeline triggers](pipeline_triggers.md)
+
### Impersonation tokens
> [Introduced][ce-9099] in GitLab 9.0. Needs admin permissions.
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index cc95689a65f..bf83bfd7e6a 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -23,36 +23,6 @@ GET /projects/:id/approvals
```json
{
- "approvers": [
- {
- "user": {
- "id": 5,
- "name": "John Doe6",
- "username": "user5",
- "state":"active","avatar_url":"https://www.gravatar.com/avatar/4aea8cf834ed91844a2da4ff7ae6b491?s=80\u0026d=identicon","web_url":"http://localhost/user5"
- }
- }
- ],
- "approver_groups": [
- {
- "group": {
- "id": 1,
- "name": "group1",
- "path": "group1",
- "description": "",
- "visibility": "public",
- "lfs_enabled": false,
- "avatar_url": null,
- "web_url": "http://localhost/groups/group1",
- "request_access_enabled": false,
- "full_name": "group1",
- "full_path": "group1",
- "parent_id": null,
- "ldap_cn": null,
- "ldap_access": null
- }
- }
- ],
"approvals_before_merge": 2,
"reset_approvals_on_push": true,
"disable_overriding_approvers_per_merge_request": false
@@ -75,7 +45,7 @@ POST /projects/:id/approvals
| Attribute | Type | Required | Description |
| ------------------------------------------------ | ------- | -------- | --------------------------------------------------------------------------------------------------- |
| `id` | integer | yes | The ID of a project |
-| `approvals_before_merge` | integer | no | How many approvals are required before an MR can be merged |
+| `approvals_before_merge` | integer | no | How many approvals are required before an MR can be merged. Deprecated in 12.0 in favor of Approval Rules API. |
| `reset_approvals_on_push` | boolean | no | Reset approvals on a new push |
| `disable_overriding_approvers_per_merge_request` | boolean | no | Allow/Disallow overriding approvers per MR |
| `merge_requests_author_approval` | boolean | no | Allow/Disallow authors from self approving merge requests; `true` means authors cannot self approve |
@@ -83,20 +53,68 @@ POST /projects/:id/approvals
```json
{
- "approvers": [
- {
- "user": {
+ "approvals_before_merge": 2,
+ "reset_approvals_on_push": true,
+ "disable_overriding_approvers_per_merge_request": false,
+ "merge_requests_author_approval": false,
+ "merge_requests_disable_committers_approval": false
+}
+```
+
+### Get project-level rules
+
+>**Note:** This API endpoint is only available on 12.3 Starter and above.
+
+You can request information about a project's approval rules using the following endpoint:
+
+```
+GET /projects/:id/approval_rules
+```
+
+**Parameters:**
+
+| Attribute | Type | Required | Description |
+|----------------------|---------|----------|-----------------------------------------------------------|
+| `id` | integer | yes | The ID of a project |
+
+```json
+[
+ {
+ "id": 1,
+ "name": "security",
+ "rule_type": "regular",
+ "eligible_approvers": [
+ {
"id": 5,
- "name": "John Doe6",
- "username": "user5",
- "state":"active","avatar_url":"https://www.gravatar.com/avatar/4aea8cf834ed91844a2da4ff7ae6b491?s=80\u0026d=identicon","web_url":"http://localhost/user5"
+ "name": "John Doe",
+ "username": "jdoe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/jdoe"
+ },
+ {
+ "id": 50,
+ "name": "Group Member 1",
+ "username": "group_member_1",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/group_member_1"
}
- }
- ],
- "approver_groups": [
- {
- "group": {
- "id": 1,
+ ],
+ "approvals_required": 3,
+ "users": [
+ {
+ "id": 5,
+ "name": "John Doe",
+ "username": "jdoe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/jdoe"
+ }
+ ],
+ "groups": [
+ {
+ "id": 5,
"name": "group1",
"path": "group1",
"description": "",
@@ -111,18 +129,187 @@ POST /projects/:id/approvals
"ldap_cn": null,
"ldap_access": null
}
+ ],
+ "contains_hidden_groups": false
+ }
+]
+```
+
+### Create project-level rule
+
+>**Note:** This API endpoint is only available on 12.3 Starter and above.
+
+You can create project approval rules using the following endpoint:
+
+```
+POST /projects/:id/approval_rules
+```
+
+**Parameters:**
+
+| Attribute | Type | Required | Description |
+|----------------------|---------|----------|-----------------------------------------------------------|
+| `id` | integer | yes | The ID of a project |
+| `name` | string | yes | The name of the approval rule |
+| `approvals_required` | integer | yes | The number of required approvals for this rule |
+| `users` | integer | no | The ids of users as approvers |
+| `groups` | integer | no | The ids of groups as approvers |
+
+```json
+{
+ "id": 1,
+ "name": "security",
+ "rule_type": "regular",
+ "eligible_approvers": [
+ {
+ "id": 2,
+ "name": "John Doe",
+ "username": "jdoe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/jdoe"
+ },
+ {
+ "id": 50,
+ "name": "Group Member 1",
+ "username": "group_member_1",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/group_member_1"
}
],
- "approvals_before_merge": 2,
- "reset_approvals_on_push": true,
- "disable_overriding_approvers_per_merge_request": false,
- "merge_requests_author_approval": false,
- "merge_requests_disable_committers_approval": false
+ "approvals_required": 1,
+ "users": [
+ {
+ "id": 2,
+ "name": "John Doe",
+ "username": "jdoe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/jdoe"
+ }
+ ],
+ "groups": [
+ {
+ "id": 5,
+ "name": "group1",
+ "path": "group1",
+ "description": "",
+ "visibility": "public",
+ "lfs_enabled": false,
+ "avatar_url": null,
+ "web_url": "http://localhost/groups/group1",
+ "request_access_enabled": false,
+ "full_name": "group1",
+ "full_path": "group1",
+ "parent_id": null,
+ "ldap_cn": null,
+ "ldap_access": null
+ }
+ ],
+ "contains_hidden_groups": false
+}
+```
+
+### Update project-level rule
+
+>**Note:** This API endpoint is only available on 12.3 Starter and above.
+
+You can update project approval rules using the following endpoint:
+
+```
+PUT /projects/:id/approval_rules/:approval_rule_id
+```
+
+**Important:** Approvers and groups not in the `users`/`groups` param will be **removed**
+
+**Parameters:**
+
+| Attribute | Type | Required | Description |
+|----------------------|---------|----------|-----------------------------------------------------------|
+| `id` | integer | yes | The ID of a project |
+| `approval_rule_id` | integer | yes | The ID of a approval rule |
+| `name` | string | yes | The name of the approval rule |
+| `approvals_required` | integer | yes | The number of required approvals for this rule |
+| `users` | integer | no | The ids of users as approvers |
+| `groups` | integer | no | The ids of groups as approvers |
+
+```json
+{
+ "id": 1,
+ "name": "security",
+ "rule_type": "regular",
+ "eligible_approvers": [
+ {
+ "id": 2,
+ "name": "John Doe",
+ "username": "jdoe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/jdoe"
+ },
+ {
+ "id": 50,
+ "name": "Group Member 1",
+ "username": "group_member_1",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/group_member_1"
+ }
+ ],
+ "approvals_required": 1,
+ "users": [
+ {
+ "id": 2,
+ "name": "John Doe",
+ "username": "jdoe",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
+ "web_url": "http://localhost/jdoe"
+ }
+ ],
+ "groups": [
+ {
+ "id": 5,
+ "name": "group1",
+ "path": "group1",
+ "description": "",
+ "visibility": "public",
+ "lfs_enabled": false,
+ "avatar_url": null,
+ "web_url": "http://localhost/groups/group1",
+ "request_access_enabled": false,
+ "full_name": "group1",
+ "full_path": "group1",
+ "parent_id": null,
+ "ldap_cn": null,
+ "ldap_access": null
+ }
+ ],
+ "contains_hidden_groups": false
}
```
+### Delete project-level rule
+
+>**Note:** This API endpoint is only available on 12.3 Starter and above.
+
+You can delete project approval rules using the following endpoint:
+
+```
+DELETE /projects/:id/approval_rules/:approval_rule_id
+```
+
+**Parameters:**
+
+| Attribute | Type | Required | Description |
+|----------------------|---------|----------|-----------------------------------------------------------|
+| `id` | integer | yes | The ID of a project |
+| `approval_rule_id` | integer | yes | The ID of a approval rule
+
### Change allowed approvers
+>**Note:** This API endpoint has been deprecated. Please use Approval Rule API instead.
>**Note:** This API endpoint is only available on 10.6 Starter and above.
If you are allowed to, you can change approvers and approver groups using
@@ -227,8 +414,6 @@ GET /projects/:id/merge_requests/:merge_request_iid/approvals
}
}
],
- "approvers": [],
- "approver_groups": []
}
```
@@ -249,7 +434,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/approvals
|----------------------|---------|----------|--------------------------------------------|
| `id` | integer | yes | The ID of a project |
| `merge_request_iid` | integer | yes | The IID of MR |
-| `approvals_required` | integer | yes | Approvals required before MR can be merged |
+| `approvals_required` | integer | yes | Approvals required before MR can be merged. Deprecated in 12.0 in favor of Approval Rules API. |
```json
{
@@ -264,14 +449,13 @@ POST /projects/:id/merge_requests/:merge_request_iid/approvals
"merge_status": "cannot_be_merged",
"approvals_required": 2,
"approvals_left": 2,
- "approved_by": [],
- "approvers": [],
- "approver_groups": []
+ "approved_by": []
}
```
### Change allowed approvers for Merge Request
+>**Note:** This API endpoint has been deprecated. Please use Approval Rule API instead.
>**Note:** This API endpoint is only available on 10.6 Starter and above.
If you are allowed to, you can change approvers and approver groups using
@@ -401,8 +585,6 @@ does not match, the response code will be `409`.
}
}
],
- "approvers": [],
- "approver_groups": []
}
```
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index d5056568dff..489791141ed 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -495,14 +495,6 @@ that runner.
## Define an image from a private Container Registry
-> **Notes:**
->
-> - This feature requires GitLab Runner **1.8** or higher
-> - For GitLab Runner versions **>= 0.6, <1.8** there was a partial
-> support for using private registries, which required manual configuration
-> of credentials on runner's host. We recommend to upgrade your Runner to
-> at least version **1.8** if you want to use private registries.
-
To access private container registries, the GitLab Runner process can use:
- [Statically defined credentials](#using-statically-defined-credentials). That is, a username and password for a specific registry.
@@ -525,6 +517,17 @@ it's provided as an environment variable. This is because GitLab Runnner uses **
`config.toml` configuration and doesn't interpolate **ANY** environment variables at
runtime.
+### Requirements and limitations
+
+- This feature requires GitLab Runner **1.8** or higher.
+- For GitLab Runner versions **>= 0.6, <1.8** there was a partial
+ support for using private registries, which required manual configuration
+ of credentials on runner's host. We recommend to upgrade your Runner to
+ at least version **1.8** if you want to use private registries.
+- Not available for [Kubernetes executor](https://docs.gitlab.com/runner/executors/kubernetes.html),
+ follow <https://gitlab.com/gitlab-org/gitlab-runner/issues/2673> for
+ details.
+
### Using statically-defined credentials
There are two approaches that you can take in order to access a
diff --git a/doc/ci/quick_start/img/build_log.png b/doc/ci/quick_start/img/build_log.png
index 2bf0992c50e..16698629edc 100644
--- a/doc/ci/quick_start/img/build_log.png
+++ b/doc/ci/quick_start/img/build_log.png
Binary files differ
diff --git a/doc/ci/quick_start/img/builds_status.png b/doc/ci/quick_start/img/builds_status.png
index 58978e23978..b4aeeb988d2 100644
--- a/doc/ci/quick_start/img/builds_status.png
+++ b/doc/ci/quick_start/img/builds_status.png
Binary files differ
diff --git a/doc/ci/quick_start/img/pipelines_status.png b/doc/ci/quick_start/img/pipelines_status.png
index 06d1559f5d2..39a77a26b25 100644
--- a/doc/ci/quick_start/img/pipelines_status.png
+++ b/doc/ci/quick_start/img/pipelines_status.png
Binary files differ
diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png
index cd83c1a7e4c..ac09e1d0137 100644
--- a/doc/ci/quick_start/img/runners_activated.png
+++ b/doc/ci/quick_start/img/runners_activated.png
Binary files differ
diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md
index 8474d4ef66e..269bd5c3428 100644
--- a/doc/ci/runners/README.md
+++ b/doc/ci/runners/README.md
@@ -88,7 +88,7 @@ visit the project you want to make the Runner work for in GitLab:
## Registering a group Runner
-Creating a group Runner requires Maintainer permissions for the group. To create a
+Creating a group Runner requires Owner permissions for the group. To create a
group Runner visit the group you want to make the Runner work for in GitLab:
1. Go to **Settings > CI/CD** to obtain the token
@@ -124,9 +124,9 @@ To lock/unlock a Runner:
## Assigning a Runner to another project
-If you are Maintainer on a project where a specific Runner is assigned to, and the
+If you are an Owner on a project where a specific Runner is assigned to, and the
Runner is not [locked only to that project](#locking-a-specific-runner-from-being-enabled-for-other-projects),
-you can enable the Runner also on any other project where you have Maintainer permissions.
+you can enable the Runner also on any other project where you have Owner permissions.
To enable/disable a Runner in your project:
@@ -250,7 +250,7 @@ When you [register a Runner][register], its default behavior is to **only pick**
[tagged jobs](../yaml/README.md#tags).
NOTE: **Note:**
-Maintainer [permissions](../../user/permissions.md) are required to change the
+Owner [permissions](../../user/permissions.md) are required to change the
Runner settings.
To make a Runner pick untagged jobs:
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 2be93433b36..76b73729d0b 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1721,7 +1721,7 @@ This example creates three paths of execution:
1. If `needs:` is set to point to a job that is not instantiated
because of `only/except` rules or otherwise does not exist, the
job will fail.
-1. Note that one day one of the launch, we are temporarily limiting the
+1. Note that on day one of the launch, we are temporarily limiting the
maximum number of jobs that a single job can need in the `needs:` array. Track
our [infrastructure issue](https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/7541)
for details on the current limit.
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 87405bc2fec..5cb2ddf6e52 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -20,7 +20,7 @@ A typical install of GitLab will be on GNU/Linux. It uses Nginx or Apache as a w
We also support deploying GitLab on Kubernetes using our [gitlab Helm chart](https://docs.gitlab.com/charts/).
-The GitLab web app uses MySQL or PostgreSQL for persistent database information (e.g. users, permissions, issues, other meta data). GitLab stores the bare git repositories it serves in `/home/git/repositories` by default. It also keeps default branch and hook information with the bare repository.
+The GitLab web app uses PostgreSQL for persistent database information (e.g. users, permissions, issues, other meta data). GitLab stores the bare git repositories it serves in `/home/git/repositories` by default. It also keeps default branch and hook information with the bare repository.
When serving repositories over HTTP/HTTPS GitLab utilizes the GitLab API to resolve authorization and access as well as serving git objects.
@@ -511,7 +511,15 @@ To summarize here's the [directory structure of the `git` user home directory](.
ps aux | grep '^git'
```
-GitLab has several components to operate. As a system user (i.e. any user that is not the `git` user) it requires a persistent database (MySQL/PostreSQL) and redis database. It also uses Apache httpd or Nginx to proxypass Unicorn. As the `git` user it starts Sidekiq and Unicorn (a simple ruby HTTP server running on port `8080` by default). Under the GitLab user there are normally 4 processes: `unicorn_rails master` (1 process), `unicorn_rails worker` (2 processes), `sidekiq` (1 process).
+GitLab has several components to operate. It requires a persistent database
+(PostgreSQL) and redis database, and uses Apache httpd or Nginx to proxypass
+Unicorn. All these components should run as different system users to GitLab
+(e.g., `postgres`, `redis` and `www-data`, instead of `git`).
+
+As the `git` user it starts Sidekiq and Unicorn (a simple ruby HTTP server
+running on port `8080` by default). Under the GitLab user there are normally 4
+processes: `unicorn_rails master` (1 process), `unicorn_rails worker`
+(2 processes), `sidekiq` (1 process).
### Repository access
@@ -554,12 +562,9 @@ $ /etc/init.d/nginx
Usage: nginx {start|stop|restart|reload|force-reload|status|configtest}
```
-Persistent database (one of the following)
+Persistent database
```
-/etc/init.d/mysqld
-Usage: /etc/init.d/mysqld {start|stop|status|restart|condrestart|try-restart|reload|force-reload}
-
$ /etc/init.d/postgresql
Usage: /etc/init.d/postgresql {start|stop|restart|reload|force-reload|status} [version ..]
```
@@ -597,11 +602,6 @@ PostgreSQL
- `/var/log/postgresql/*`
-MySQL
-
-- `/var/log/mysql/*`
-- `/var/log/mysql.*`
-
### GitLab specific config files
GitLab has configuration files located in `/home/git/gitlab/config/*`. Commonly referenced config files include:
diff --git a/doc/development/automatic_ce_ee_merge.md b/doc/development/automatic_ce_ee_merge.md
index 98b8a48abf4..001a92790e1 100644
--- a/doc/development/automatic_ce_ee_merge.md
+++ b/doc/development/automatic_ce_ee_merge.md
@@ -200,7 +200,7 @@ code.
### Why merge automatically?
As we work towards continuous deployments and a single repository for both CE
-and EE, we need to first make sure that all CE changes make their way into CE as
+and EE, we need to first make sure that all CE changes make their way into EE as
fast as possible. Past experiences and data have shown that periodic CE to EE
merge requests do not scale, and often take a very long time to complete. For
example, [in this
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index a38794c49af..b2e3ef7bf63 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -60,37 +60,18 @@ Subject labels are always all-lowercase.
## Team labels
-**Important**: Most of the team labels will be soon deprecated in favor of [Group labels](#group-labels).
+**Important**: Most of the historical team labels (e.g. Manage, Plan etc.) are
+now deprecated in favor of [Group labels](#group-labels) and [Stage labels](#stage-labels).
Team labels specify what team is responsible for this issue.
Assigning a team label makes sure issues get the attention of the appropriate
people.
-The team labels planned for deprecation are:
-
-- ~Configure
-- ~Create
-- ~Defend
-- ~Distribution
-- ~Ecosystem
-- ~Geo
-- ~Gitaly
-- ~Growth
-- ~Manage
-- ~Memory
-- ~Monitor
-- ~Plan
-- ~Release
-- ~Secure
-- ~Verify
-
-The following team labels are **true** teams per our [organization structure](https://about.gitlab.com/company/team/structure/#organizational-structure) which will remain post deprecation.
+The current team labels are:
- ~Delivery
- ~Documentation
-
-The descriptions on the [labels page](https://gitlab.com/gitlab-org/gitlab-ce/-/labels) explain what falls under the
-responsibility of each team.
+- ~Quality
Team labels are always capitalized so that they show up as the first label for
any issue.
@@ -120,13 +101,13 @@ and thus are mutually exclusive.
You can find the groups listed in the [Product Stages, Groups, and Categories][product-categories] page.
-We use the term group to map down product requirements from our product stages.
+We use the term group to map down product requirements from our product stages.
As a team needs some way to collect the work their members are planning to be assigned to, we use the `~group::` labels to do so.
Normally there is a 1:1 relationship between Stage labels and Group labels. In the spirit of "Everyone can contribute",
any issue can be picked up by any group, depending on current priorities. For example, an issue labeled ~"devops::create" may be picked up by the ~"group::access" group.
-We also use stage and group labels to help quantify our [throughput](https://about.gitlab.com/handbook/engineering/management/throughput).
+We also use stage and group labels to help quantify our [throughput](https://about.gitlab.com/handbook/engineering/management/throughput).
Please read [Stage and Group labels in Throughtput](https://about.gitlab.com/handbook/engineering/management/throughput/#stage-and-group-labels-in-throughput) for more information on how the labels are used in this context.
[structure-groups]: https://about.gitlab.com/company/team/structure/#groups
diff --git a/doc/development/fe_guide/axios.md b/doc/development/fe_guide/axios.md
index 09b4a3c3d96..6e7cf523f36 100644
--- a/doc/development/fe_guide/axios.md
+++ b/doc/development/fe_guide/axios.md
@@ -38,7 +38,7 @@ Advantages over [`spyOn()`]:
- no need to create response objects
- does not allow call through (which we want to avoid)
-- simple API to test error cases
+- simple API to test error cases
- provides `replyOnce()` to allow for different responses
We have also decided against using [axios interceptors] because they are not suitable for mocking.
diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md
index d3fa350b847..125b11afcd0 100644
--- a/doc/development/fe_guide/style_guide_js.md
+++ b/doc/development/fe_guide/style_guide_js.md
@@ -21,31 +21,31 @@ See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/
1. **Never Ever EVER** disable eslint globally for a file
```javascript
- // bad
- /* eslint-disable */
+ // bad
+ /* eslint-disable */
- // better
- /* eslint-disable some-rule, some-other-rule */
+ // better
+ /* eslint-disable some-rule, some-other-rule */
- // best
- // nothing :)
+ // best
+ // nothing :)
```
1. If you do need to disable a rule for a single violation, try to do it as locally as possible
```javascript
- // bad
- /* eslint-disable no-new */
+ // bad
+ /* eslint-disable no-new */
- import Foo from 'foo';
+ import Foo from 'foo';
- new Foo();
+ new Foo();
- // better
- import Foo from 'foo';
+ // better
+ import Foo from 'foo';
- // eslint-disable-next-line no-new
- new Foo();
+ // eslint-disable-next-line no-new
+ new Foo();
```
1. There are few rules that we need to disable due to technical debt. Which are:
@@ -56,16 +56,16 @@ See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/
followed by any global declarations, then a blank newline prior to any imports or code.
```javascript
- // bad
- /* global Foo */
- /* eslint-disable no-new */
- import Bar from './bar';
+ // bad
+ /* global Foo */
+ /* eslint-disable no-new */
+ import Bar from './bar';
- // good
- /* eslint-disable no-new */
- /* global Foo */
+ // good
+ /* eslint-disable no-new */
+ /* global Foo */
- import Bar from './bar';
+ import Bar from './bar';
```
1. **Never** disable the `no-undef` rule. Declare globals with `/* global Foo */` instead.
@@ -73,23 +73,23 @@ See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/
1. When declaring multiple globals, always use one `/* global [name] */` line per variable.
```javascript
- // bad
- /* globals Flash, Cookies, jQuery */
+ // bad
+ /* globals Flash, Cookies, jQuery */
- // good
- /* global Flash */
- /* global Cookies */
- /* global jQuery */
+ // good
+ /* global Flash */
+ /* global Cookies */
+ /* global jQuery */
```
1. Use up to 3 parameters for a function or class. If you need more accept an Object instead.
```javascript
- // bad
- fn(p1, p2, p3, p4) {}
+ // bad
+ fn(p1, p2, p3, p4) {}
- // good
- fn(options) {}
+ // good
+ fn(options) {}
```
#### Modules, Imports, and Exports
@@ -97,32 +97,32 @@ See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/
1. Use ES module syntax to import modules
```javascript
- // bad
- const SomeClass = require('some_class');
+ // bad
+ const SomeClass = require('some_class');
- // good
- import SomeClass from 'some_class';
+ // good
+ import SomeClass from 'some_class';
- // bad
- module.exports = SomeClass;
+ // bad
+ module.exports = SomeClass;
- // good
- export default SomeClass;
+ // good
+ export default SomeClass;
```
Import statements are following usual naming guidelines, for example object literals use camel case:
```javascript
- // some_object file
- export default {
- key: 'value',
- };
+ // some_object file
+ export default {
+ key: 'value',
+ };
- // bad
- import ObjectLiteral from 'some_object';
+ // bad
+ import ObjectLiteral from 'some_object';
- // good
- import objectLiteral from 'some_object';
+ // good
+ import objectLiteral from 'some_object';
```
1. Relative paths: when importing a module in the same directory, a child
@@ -171,22 +171,22 @@ See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/
1. Avoid adding to the global namespace.
```javascript
- // bad
- window.MyClass = class { /* ... */ };
+ // bad
+ window.MyClass = class { /* ... */ };
- // good
- export default class MyClass { /* ... */ }
+ // good
+ export default class MyClass { /* ... */ }
```
1. Side effects are forbidden in any script which contains export
```javascript
- // bad
- export default class MyClass { /* ... */ }
+ // bad
+ export default class MyClass { /* ... */ }
- document.addEventListener("DOMContentLoaded", function(event) {
- new MyClass();
- }
+ document.addEventListener("DOMContentLoaded", function(event) {
+ new MyClass();
+ }
```
#### Data Mutation and Pure functions
@@ -257,17 +257,17 @@ See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/
`.reduce` or `.filter`
```javascript
- const users = [ { name: 'Foo' }, { name: 'Bar' } ];
+ const users = [ { name: 'Foo' }, { name: 'Bar' } ];
- // bad
- users.forEach((user, index) => {
- user.id = index;
- });
+ // bad
+ users.forEach((user, index) => {
+ user.id = index;
+ });
- // good
- const usersWithId = users.map((user, index) => {
- return Object.assign({}, user, { id: index });
- });
+ // good
+ const usersWithId = users.map((user, index) => {
+ return Object.assign({}, user, { id: index });
+ });
```
#### Parse Strings into Numbers
@@ -275,14 +275,14 @@ See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/
1. `parseInt()` is preferable over `Number()` or `+`
```javascript
- // bad
- +'10' // 10
+ // bad
+ +'10' // 10
- // good
- Number('10') // 10
+ // good
+ Number('10') // 10
- // better
- parseInt('10', 10);
+ // better
+ parseInt('10', 10);
```
#### CSS classes used for JavaScript
@@ -290,15 +290,15 @@ See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/
1. If the class is being used in Javascript it needs to be prepend with `js-`
```html
- // bad
- <button class="add-user">
- Add User
- </button>
+ // bad
+ <button class="add-user">
+ Add User
+ </button>
- // good
- <button class="js-add-user">
- Add User
- </button>
+ // good
+ <button class="js-add-user">
+ Add User
+ </button>
```
### Vue.js
@@ -315,41 +315,41 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
1. Use a function in the bundle file to instantiate the Vue component:
```javascript
- // bad
- class {
- init() {
- new Component({})
- }
+ // bad
+ class {
+ init() {
+ new Component({})
}
+ }
- // good
- document.addEventListener('DOMContentLoaded', () => new Vue({
- el: '#element',
- components: {
- componentName
- },
- render: createElement => createElement('component-name'),
- }));
+ // good
+ document.addEventListener('DOMContentLoaded', () => new Vue({
+ el: '#element',
+ components: {
+ componentName
+ },
+ render: createElement => createElement('component-name'),
+ }));
```
1. Do not use a singleton for the service or the store
```javascript
- // bad
- class Store {
- constructor() {
- if (!this.prototype.singleton) {
- // do something
- }
+ // bad
+ class Store {
+ constructor() {
+ if (!this.prototype.singleton) {
+ // do something
}
}
+ }
- // good
- class Store {
- constructor() {
- // do something
- }
+ // good
+ class Store {
+ constructor() {
+ // do something
}
+ }
```
1. Use `.vue` for Vue templates. Do not use `%template` in HAML.
@@ -360,36 +360,36 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
1. **Reference Naming**: Use PascalCase for their instances:
```javascript
- // bad
- import cardBoard from 'cardBoard.vue'
+ // bad
+ import cardBoard from 'cardBoard.vue'
- components: {
- cardBoard,
- };
+ components: {
+ cardBoard,
+ };
- // good
- import CardBoard from 'cardBoard.vue'
+ // good
+ import CardBoard from 'cardBoard.vue'
- components: {
- CardBoard,
- };
+ components: {
+ CardBoard,
+ };
```
1. **Props Naming:** Avoid using DOM component prop names.
1. **Props Naming:** Use kebab-case instead of camelCase to provide props in templates.
```javascript
- // bad
- <component class="btn">
+ // bad
+ <component class="btn">
- // good
- <component css-class="btn">
+ // good
+ <component css-class="btn">
- // bad
- <component myProp="prop" />
+ // bad
+ <component myProp="prop" />
- // good
- <component my-prop="prop" />
+ // good
+ <component my-prop="prop" />
```
[#34371]: https://gitlab.com/gitlab-org/gitlab-ce/issues/34371
@@ -401,37 +401,37 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
1. With more than one attribute, all attributes should be on a new line:
```javascript
- // bad
- <component v-if="bar"
- param="baz" />
+ // bad
+ <component v-if="bar"
+ param="baz" />
- <button class="btn">Click me</button>
+ <button class="btn">Click me</button>
- // good
- <component
- v-if="bar"
- param="baz"
- />
+ // good
+ <component
+ v-if="bar"
+ param="baz"
+ />
- <button class="btn">
- Click me
- </button>
+ <button class="btn">
+ Click me
+ </button>
```
1. The tag can be inline if there is only one attribute:
```javascript
- // good
- <component bar="bar" />
+ // good
+ <component bar="bar" />
- // good
- <component
- bar="bar"
- />
+ // good
+ <component
+ bar="bar"
+ />
- // bad
- <component
- bar="bar" />
+ // bad
+ <component
+ bar="bar" />
```
#### Quotes
@@ -439,15 +439,15 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
1. Always use double quotes `"` inside templates and single quotes `'` for all other JS.
```javascript
- // bad
- template: `
- <button :class='style'>Button</button>
- `
+ // bad
+ template: `
+ <button :class='style'>Button</button>
+ `
- // good
- template: `
- <button :class="style">Button</button>
- `
+ // good
+ template: `
+ <button :class="style">Button</button>
+ `
```
#### Props
@@ -455,37 +455,37 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
1. Props should be declared as an object
```javascript
- // bad
- props: ['foo']
-
- // good
- props: {
- foo: {
- type: String,
- required: false,
- default: 'bar'
- }
+ // bad
+ props: ['foo']
+
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ default: 'bar'
}
+ }
```
1. Required key should always be provided when declaring a prop
```javascript
- // bad
- props: {
- foo: {
- type: String,
- }
+ // bad
+ props: {
+ foo: {
+ type: String,
}
+ }
- // good
- props: {
- foo: {
- type: String,
- required: false,
- default: 'bar'
- }
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ default: 'bar'
}
+ }
```
1. Default key should be provided if the prop is not required.
@@ -493,30 +493,30 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
On those a default key should not be provided.
```javascript
- // good
- props: {
- foo: {
- type: String,
- required: false,
- }
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
}
+ }
- // good
- props: {
- foo: {
- type: String,
- required: false,
- default: 'bar'
- }
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ default: 'bar'
}
+ }
- // good
- props: {
- foo: {
- type: String,
- required: true
- }
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: true
}
+ }
```
#### Data
@@ -524,17 +524,17 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
1. `data` method should always be a function
```javascript
- // bad
- data: {
- foo: 'foo'
- }
+ // bad
+ data: {
+ foo: 'foo'
+ }
- // good
- data() {
- return {
- foo: 'foo'
- };
- }
+ // good
+ data() {
+ return {
+ foo: 'foo'
+ };
+ }
```
#### Directives
@@ -542,31 +542,31 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
1. Shorthand `@` is preferable over `v-on`
```javascript
- // bad
- <component v-on:click="eventHandler"/>
+ // bad
+ <component v-on:click="eventHandler"/>
- // good
- <component @click="eventHandler"/>
+ // good
+ <component @click="eventHandler"/>
```
1. Shorthand `:` is preferable over `v-bind`
```javascript
- // bad
- <component v-bind:class="btn"/>
+ // bad
+ <component v-bind:class="btn"/>
- // good
- <component :class="btn"/>
+ // good
+ <component :class="btn"/>
```
1. Shorthand `#` is preferable over `v-slot`
```javascript
- // bad
- <template v-slot:header></template>
+ // bad
+ <template v-slot:header></template>
- // good
- <template #header></template>
+ // good
+ <template #header></template>
```
#### Closing tags
@@ -574,11 +574,11 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
1. Prefer self closing component tags
```javascript
- // bad
- <component></component>
+ // bad
+ <component></component>
- // good
- <component />
+ // good
+ <component />
```
#### Ordering
@@ -610,48 +610,48 @@ When using `v-for` you need to provide a *unique* `:key` attribute for each item
1. If the elements of the array being iterated have an unique `id` it is advised to use it:
```html
- <div
- v-for="item in items"
- :key="item.id"
- >
- <!-- content -->
- </div>
+ <div
+ v-for="item in items"
+ :key="item.id"
+ >
+ <!-- content -->
+ </div>
```
1. When the elements being iterated don't have a unique id, you can use the array index as the `:key` attribute
```html
- <div
- v-for="(item, index) in items"
- :key="index"
- >
- <!-- content -->
- </div>
+ <div
+ v-for="(item, index) in items"
+ :key="index"
+ >
+ <!-- content -->
+ </div>
```
1. When using `v-for` with `template` and there is more than one child element, the `:key` values must be unique. It's advised to use `kebab-case` namespaces.
```html
- <template v-for="(item, index) in items">
- <span :key="`span-${index}`"></span>
- <button :key="`button-${index}`"></button>
- </template>
+ <template v-for="(item, index) in items">
+ <span :key="`span-${index}`"></span>
+ <button :key="`button-${index}`"></button>
+ </template>
```
1. When dealing with nested `v-for` use the same guidelines as above.
```html
- <div
- v-for="item in items"
- :key="item.id"
+ <div
+ v-for="item in items"
+ :key="item.id"
+ >
+ <span
+ v-for="element in array"
+ :key="element.id"
>
- <span
- v-for="element in array"
- :key="element.id"
- >
- <!-- content -->
- </span>
- </div>
+ <!-- content -->
+ </span>
+ </div>
```
Useful links:
@@ -664,19 +664,19 @@ Useful links:
1. Tooltips: Do not rely on `has-tooltip` class name for Vue components
```javascript
- // bad
- <span
- class="has-tooltip"
- title="Some tooltip text">
- Text
- </span>
+ // bad
+ <span
+ class="has-tooltip"
+ title="Some tooltip text">
+ Text
+ </span>
- // good
- <span
- v-tooltip
- title="Some tooltip text">
- Text
- </span>
+ // good
+ <span
+ v-tooltip
+ title="Some tooltip text">
+ Text
+ </span>
```
1. Tooltips: When using a tooltip, include the tooltip directive, `./app/assets/javascripts/vue_shared/directives/tooltip.js`
@@ -684,13 +684,13 @@ Useful links:
1. Don't change `data-original-title`.
```javascript
- // bad
- <span data-original-title="tooltip text">Foo</span>
+ // bad
+ <span data-original-title="tooltip text">Foo</span>
- // good
- <span title="tooltip text">Foo</span>
+ // good
+ <span title="tooltip text">Foo</span>
- $('span').tooltip('_fixTitle');
+ $('span').tooltip('_fixTitle');
```
### The Javascript/Vue Accord
diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md
index 9eeaee4482f..557d3132d71 100644
--- a/doc/development/fe_guide/vuex.md
+++ b/doc/development/fe_guide/vuex.md
@@ -313,7 +313,7 @@ export default {
1. Do not call a mutation directly. Always use an action to commit a mutation. Doing so will keep consistency throughout the application. From Vuex docs:
- > why don't we just call store.commit('action') directly? Well, remember that mutations must be synchronous? Actions aren't. We can perform asynchronous operations inside an action.
+ > Why don't we just call store.commit('action') directly? Well, remember that mutations must be synchronous? Actions aren't. We can perform asynchronous operations inside an action.
```javascript
// component.vue
diff --git a/doc/development/hash_indexes.md b/doc/development/hash_indexes.md
index e6c1b3590b1..417ea18e22f 100644
--- a/doc/development/hash_indexes.md
+++ b/doc/development/hash_indexes.md
@@ -1,6 +1,6 @@
# Hash Indexes
-Both PostgreSQL and MySQL support hash indexes besides the regular btree
+PostgreSQL supports hash indexes besides the regular btree
indexes. Hash indexes however are to be avoided at all costs. While they may
_sometimes_ provide better performance the cost of rehashing can be very high.
More importantly: at least until PostgreSQL 10.0 hash indexes are not
diff --git a/doc/development/instrumentation.md b/doc/development/instrumentation.md
index 5f95cf3707c..777d372ec60 100644
--- a/doc/development/instrumentation.md
+++ b/doc/development/instrumentation.md
@@ -1,6 +1,6 @@
# Instrumenting Ruby Code
-GitLab Performance Monitoring allows instrumenting of both methods and custom
+[GitLab Performance Monitoring](../administration/monitoring/performance/index.md) allows instrumenting of both methods and custom
blocks of Ruby code. Method instrumentation is the primary form of
instrumentation with block-based instrumentation only being used when we want to
drill down to specific regions of code within a method.
diff --git a/doc/development/interacting_components.md b/doc/development/interacting_components.md
index 74d52d808e2..5e6dc8d460a 100644
--- a/doc/development/interacting_components.md
+++ b/doc/development/interacting_components.md
@@ -9,8 +9,8 @@ when making _backend_ changes that might involve multiple features or [component
## Uploads
-GitLab supports uploads to [object storage]. That means every feature and
-change that affects uploads should also be tested against [object storage],
+GitLab supports uploads to [object storage]. That means every feature and
+change that affects uploads should also be tested against [object storage],
which is _not_ enabled by default in [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit).
When working on a related feature, make sure to enable and test it
diff --git a/doc/development/new_fe_guide/development/testing.md b/doc/development/new_fe_guide/development/testing.md
index f7ea496d935..e0d413b748b 100644
--- a/doc/development/new_fe_guide/development/testing.md
+++ b/doc/development/new_fe_guide/development/testing.md
@@ -1,361 +1,6 @@
-# Overview of Frontend Testing
+---
+redirect_to: '../../testing_guide/frontend_testing.md'
+---
-Tests relevant for frontend development can be found at the following places:
+This document was moved to [another location](../../testing_guide/frontend_testing.md).
-- `spec/javascripts/` which are run by Karma (command: `yarn karma`) and contain
- - [frontend unit tests](#frontend-unit-tests)
- - [frontend component tests](#frontend-component-tests)
- - [frontend integration tests](#frontend-integration-tests)
-- `spec/frontend/` which are run by Jest (command: `yarn jest`) and contain
- - [frontend unit tests](#frontend-unit-tests)
- - [frontend component tests](#frontend-component-tests)
- - [frontend integration tests](#frontend-integration-tests)
-- `spec/features/` which are run by RSpec and contain
- - [feature tests](#feature-tests)
-
-All tests in `spec/javascripts/` will eventually be migrated to `spec/frontend/` (see also [#52483](https://gitlab.com/gitlab-org/gitlab-ce/issues/52483)).
-
-In addition there were feature tests in `features/` run by Spinach in the past.
-These have been removed from our codebase in May 2018 ([#23036](https://gitlab.com/gitlab-org/gitlab-ce/issues/23036)).
-
-See also:
-
-- [Old testing guide](../../testing_guide/frontend_testing.html).
-- [Notes on testing Vue components](../../fe_guide/vue.html#testing-vue-components).
-
-## Frontend unit tests
-
-Unit tests are on the lowest abstraction level and typically test functionality that is not directly perceivable by a user.
-
-### When to use unit tests
-
-<details>
- <summary>exported functions and classes</summary>
- Anything that is exported can be reused at various places in a way you have no control over.
- Therefore it is necessary to document the expected behavior of the public interface with tests.
-</details>
-
-<details>
- <summary>Vuex actions</summary>
- Any Vuex action needs to work in a consistent way independent of the component it is triggered from.
-</details>
-
-<details>
- <summary>Vuex mutations</summary>
- For complex Vuex mutations it helps to identify the source of a problem by separating the tests from other parts of the Vuex store.
-</details>
-
-### When *not* to use unit tests
-
-<details>
- <summary>non-exported functions or classes</summary>
- Anything that is not exported from a module can be considered private or an implementation detail and doesn't need to be tested.
-</details>
-
-<details>
- <summary>constants</summary>
- Testing the value of a constant would mean to copy it.
- This results in extra effort without additional confidence that the value is correct.
-</details>
-
-<details>
- <summary>Vue components</summary>
- Computed properties, methods, and lifecycle hooks can be considered an implementation detail of components and don't need to be tested.
- They are implicitly covered by component tests.
- The <a href="https://vue-test-utils.vuejs.org/guides/#getting-started">official Vue guidelines</a> suggest the same.
-</details>
-
-### What to mock in unit tests
-
-<details>
- <summary>state of the class under test</summary>
- Modifying the state of the class under test directly rather than using methods of the class avoids side-effects in test setup.
-</details>
-
-<details>
- <summary>other exported classes</summary>
- Every class needs to be tested in isolation to prevent test scenarios from growing exponentially.
-</details>
-
-<details>
- <summary>single DOM elements if passed as parameters</summary>
- For tests that only operate on single DOM elements rather than a whole page, creating these elements is cheaper than loading a whole HTML fixture.
-</details>
-
-<details>
- <summary>all server requests</summary>
- When running frontend unit tests, the backend may not be reachable.
- Therefore all outgoing requests need to be mocked.
-</details>
-
-<details>
- <summary>asynchronous background operations</summary>
- Background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
-</details>
-
-### What *not* to mock in unit tests
-
-<details>
- <summary>non-exported functions or classes</summary>
- Everything that is not exported can be considered private to the module and will be implicitly tested via the exported classes / functions.
-</details>
-
-<details>
- <summary>methods of the class under test</summary>
- By mocking methods of the class under test, the mocks will be tested and not the real methods.
-</details>
-
-<details>
- <summary>utility functions (pure functions, or those that only modify parameters)</summary>
- If a function has no side effects because it has no state, it is safe to not mock it in tests.
-</details>
-
-<details>
- <summary>full HTML pages</summary>
- Loading the HTML of a full page slows down tests, so it should be avoided in unit tests.
-</details>
-
-## Frontend component tests
-
-Component tests cover the state of a single component that is perceivable by a user depending on external signals such as user input, events fired from other components, or application state.
-
-### When to use component tests
-
-- Vue components
-
-### When *not* to use component tests
-
-<details>
- <summary>Vue applications</summary>
- Vue applications may contain many components.
- Testing them on a component level requires too much effort.
- Therefore they are tested on frontend integration level.
-</details>
-
-<details>
- <summary>HAML templates</summary>
- HAML templates contain only Markup and no frontend-side logic.
- Therefore they are not complete components.
-</details>
-
-### What to mock in component tests
-
-<details>
- <summary>DOM</summary>
- Operating on the real DOM is significantly slower than on the virtual DOM.
-</details>
-
-<details>
- <summary>properties and state of the component under test</summary>
- Similarly to testing classes, modifying the properties directly (rather than relying on methods of the component) avoids side-effects.
-</details>
-
-<details>
- <summary>Vuex store</summary>
- To avoid side effects and keep component tests simple, Vuex stores are replaced with mocks.
-</details>
-
-<details>
- <summary>all server requests</summary>
- Similar to unit tests, when running component tests, the backend may not be reachable.
- Therefore all outgoing requests need to be mocked.
-</details>
-
-<details>
- <summary>asynchronous background operations</summary>
- Similar to unit tests, background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
-</details>
-
-<details>
- <summary>child components</summary>
- Every component is tested individually, so child components are mocked.
- See also <a href="https://vue-test-utils.vuejs.org/api/#shallowmount">shallowMount()</a>
-</details>
-
-### What *not* to mock in component tests
-
-<details>
- <summary>methods or computed properties of the component under test</summary>
- By mocking part of the component under test, the mocks will be tested and not the real component.
-</details>
-
-<details>
- <summary>functions and classes independent from Vue</summary>
- All plain JavaScript code is already covered by unit tests and needs not to be mocked in component tests.
-</details>
-
-## Frontend integration tests
-
-Integration tests cover the interaction between all components on a single page.
-Their abstraction level is comparable to how a user would interact with the UI.
-
-### When to use integration tests
-
-<details>
- <summary>page bundles (<code>index.js</code> files in <code>app/assets/javascripts/pages/</code>)</summary>
- Testing the page bundles ensures the corresponding frontend components integrate well.
-</details>
-
-<details>
- <summary>Vue applications outside of page bundles</summary>
- Testing Vue applications as a whole ensures the corresponding frontend components integrate well.
-</details>
-
-### What to mock in integration tests
-
-<details>
- <summary>HAML views (use fixtures instead)</summary>
- Rendering HAML views requires a Rails environment including a running database which we cannot rely on in frontend tests.
-</details>
-
-<details>
- <summary>all server requests</summary>
- Similar to unit and component tests, when running component tests, the backend may not be reachable.
- Therefore all outgoing requests need to be mocked.
-</details>
-
-<details>
- <summary>asynchronous background operations that are not perceivable on the page</summary>
- Background operations that affect the page need to be tested on this level.
- All other background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
-</details>
-
-### What *not* to mock in integration tests
-
-<details>
- <summary>DOM</summary>
- Testing on the real DOM ensures our components work in the environment they are meant for.
- Part of this will be delegated to <a href="https://gitlab.com/gitlab-org/quality/team-tasks/issues/45">cross-browser testing</a>.
-</details>
-
-<details>
- <summary>properties or state of components</summary>
- On this level, all tests can only perform actions a user would do.
- For example to change the state of a component, a click event would be fired.
-</details>
-
-<details>
- <summary>Vuex stores</summary>
- When testing the frontend code of a page as a whole, the interaction between Vue components and Vuex stores is covered as well.
-</details>
-
-## Feature tests
-
-In contrast to [frontend integration tests](#frontend-integration-tests), feature tests make requests against the real backend instead of using fixtures.
-This also implies that database queries are executed which makes this category significantly slower.
-
-See also the [RSpec testing guidelines](../../testing_guide/best_practices.md#rspec).
-
-### When to use feature tests
-
-- use cases that require a backend and cannot be tested using fixtures
-- behavior that is not part of a page bundle but defined globally
-
-### Relevant notes
-
-A `:js` flag is added to the test to make sure the full environment is loaded.
-
-```
-scenario 'successfully', :js do
- sign_in(create(:admin))
-end
-```
-
-The steps of each test are written using capybara methods ([documentation](https://www.rubydoc.info/gems/capybara)).
-
-Bear in mind <abbr title="XMLHttpRequest">XHR</abbr> calls might require you to use `wait_for_requests` in between steps, like so:
-
-```rspec
-find('.form-control').native.send_keys(:enter)
-
-wait_for_requests
-
-expect(page).not_to have_selector('.card')
-```
-
-## Test helpers
-
-### Vuex Helper: `testAction`
-
-We have a helper available to make testing actions easier, as per [official documentation](https://vuex.vuejs.org/guide/testing.html):
-
-```
-testAction(
- actions.actionName, // action
- { }, // params to be passed to action
- state, // state
- [
- { type: types.MUTATION},
- { type: types.MUTATION_1, payload: {}},
- ], // mutations committed
- [
- { type: 'actionName', payload: {}},
- { type: 'actionName1', payload: {}},
- ] // actions dispatched
- done,
-);
-```
-
-Check an example in [spec/javascripts/ide/stores/actions_spec.jsspec/javascripts/ide/stores/actions_spec.js](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/javascripts/ide/stores/actions_spec.js).
-
-### Vue Helper: `mountComponent`
-
-To make mounting a Vue component easier and more readable, we have a few helpers available in `spec/helpers/vue_mount_component_helper`.
-
-- `createComponentWithStore`
-- `mountComponentWithStore`
-
-Examples of usage:
-
-```
-beforeEach(() => {
- vm = createComponentWithStore(Component, store);
-
- vm.$store.state.currentBranchId = 'master';
-
- vm.$mount();
-},
-```
-
-```
-beforeEach(() => {
- vm = mountComponentWithStore(Component, {
- el: '#dummy-element',
- store,
- props: { badge },
- });
-},
-```
-
-Don't forget to clean up:
-
-```
-afterEach(() => {
- vm.$destroy();
-});
-```
-
-## Testing with older browsers
-
-Some regressions only affect a specific browser version. We can install and test in particular browsers with either Firefox or Browserstack using the following steps:
-
-### Browserstack
-
-[Browserstack](https://www.browserstack.com/) allows you to test more than 1200 mobile devices and browsers.
-You can use it directly through the [live app](https://www.browserstack.com/live) or you can install the [chrome extension](https://chrome.google.com/webstore/detail/browserstack/nkihdmlheodkdfojglpcjjmioefjahjb) for easy access.
-You can find the credentials on 1Password, under `frontendteam@gitlab.com`.
-
-### Firefox
-
-#### macOS
-
-You can download any older version of Firefox from the releases FTP server, <https://ftp.mozilla.org/pub/firefox/releases/>
-
-1. From the website, select a version, in this case `50.0.1`.
-1. Go to the mac folder.
-1. Select your preferred language, you will find the dmg package inside, download it.
-1. Drag and drop the application to any other folder but the `Applications` folder.
-1. Rename the application to something like `Firefox_Old`.
-1. Move the application to the `Applications` folder.
-1. Open up a terminal and run `/Applications/Firefox_Old.app/Contents/MacOS/firefox-bin -profilemanager` to create a new profile specific to that Firefox version.
-1. Once the profile has been created, quit the app, and run it again like normal. You now have a working older Firefox version.
diff --git a/doc/development/new_fe_guide/modules/dirty_submit.md b/doc/development/new_fe_guide/modules/dirty_submit.md
index 6c03958b463..217743ea395 100644
--- a/doc/development/new_fe_guide/modules/dirty_submit.md
+++ b/doc/development/new_fe_guide/modules/dirty_submit.md
@@ -1,7 +1,6 @@
# Dirty Submit
-> [Introduced][ce-21115] in GitLab 11.3.
-> [dirty_submit][dirty-submit]
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21115) in GitLab 11.3.
## Summary
@@ -9,6 +8,9 @@ Prevent submitting forms with no changes.
Currently handles `input`, `textarea` and `select` elements.
+Also, see [the code](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/dirty_submit/)
+within the GitLab project.
+
## Usage
```js
@@ -18,6 +20,3 @@ new DirtySubmitForm(document.querySelector('form'));
// or
new DirtySubmitForm(document.querySelectorAll('form'));
```
-
-[ce-21115]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21115
-[dirty-submit]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/dirty_submit/ \ No newline at end of file
diff --git a/doc/development/new_fe_guide/style/javascript.md b/doc/development/new_fe_guide/style/javascript.md
index 802ebd12d92..b742d567f41 100644
--- a/doc/development/new_fe_guide/style/javascript.md
+++ b/doc/development/new_fe_guide/style/javascript.md
@@ -192,4 +192,4 @@ rules only if you are invoking/instantiating existing code modules.
- [class-method-use-this](http://eslint.org/docs/rules/class-methods-use-this)
> Note: Disable these rules on a per line basis. This makes it easier to refactor
- in the future. E.g. use `eslint-disable-next-line` or `eslint-disable-line`.
+> in the future. E.g. use `eslint-disable-next-line` or `eslint-disable-line`.
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index 67f36eb1ab4..fc96820c555 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -9,7 +9,7 @@ bundle exec rake setup
```
The `setup` task is an alias for `gitlab:setup`.
-This tasks calls `db:reset` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and finally it calls `db:seed_fu` to seed the database.
+This tasks calls `db:reset` to create the database, and calls `db:seed_fu` to seed the database.
Note: `db:setup` calls `db:seed` but this does nothing.
### Seeding issues for all or a given project
diff --git a/doc/development/sha1_as_binary.md b/doc/development/sha1_as_binary.md
index 3151cc29bbc..6c4252ec634 100644
--- a/doc/development/sha1_as_binary.md
+++ b/doc/development/sha1_as_binary.md
@@ -2,7 +2,7 @@
Storing SHA1 hashes as strings is not very space efficient. A SHA1 as a string
requires at least 40 bytes, an additional byte to store the encoding, and
-perhaps more space depending on the internals of PostgreSQL and MySQL.
+perhaps more space depending on the internals of PostgreSQL.
On the other hand, if one were to store a SHA1 as binary one would only need 20
bytes for the actual SHA1, and 1 or 4 bytes of additional space (again depending
diff --git a/doc/development/shell_scripting_guide/index.md b/doc/development/shell_scripting_guide/index.md
index ae7f2154682..0809f8b1a0a 100644
--- a/doc/development/shell_scripting_guide/index.md
+++ b/doc/development/shell_scripting_guide/index.md
@@ -24,7 +24,8 @@ Having said all of the above, we recommend staying away from shell scripts
as much as possible. A language like Ruby or Python (if required for
consistency with codebases that we leverage) is almost always a better choice.
The high-level interpreted languages have more readable syntax, offer much more
-mature capabilities for unit-testing, linting, and error reporting.
+mature capabilities for unit-testing, linting, and error reporting.
+
Use shell scripts only if there's a strong restriction on project's
dependencies size or any other requirements that are more important
in a particular case.
@@ -48,12 +49,12 @@ that is:
This section describes the tools that should be made a mandatory part of
a project's CI pipeline if it contains shell scripts. These tools
-automate shell code formatting, checking for errors or vulnerabilities, etc.
+automate shell code formatting, checking for errors or vulnerabilities, etc.
### Linting
We're using the [ShellCheck](https://www.shellcheck.net/) utility in its default configuration to lint our
-shell scripts.
+shell scripts.
All projects with shell scripts should use this GitLab CI/CD job:
@@ -98,7 +99,7 @@ NOTE: **Note:**
This is a work in progress.
It is an [ongoing effort](https://gitlab.com/gitlab-org/gitlab-ce/issues/64016) to evaluate different tools for the
-automated testing of shell scripts (like [BATS](https://github.com/sstephenson/bats)).
+automated testing of shell scripts (like [BATS](https://github.com/sstephenson/bats)).
## Code Review
diff --git a/doc/development/sql.md b/doc/development/sql.md
index a256fd46c09..2584dcfb4ca 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -15,14 +15,11 @@ FROM issues
WHERE title LIKE 'WIP:%';
```
-On PostgreSQL the `LIKE` statement is case-sensitive. On MySQL this depends on
-the case-sensitivity of the collation, which is usually case-insensitive. To
-perform a case-insensitive `LIKE` on PostgreSQL you have to use `ILIKE` instead.
-This statement in turn isn't supported on MySQL.
+On PostgreSQL the `LIKE` statement is case-sensitive. To perform a case-insensitive
+`LIKE` you have to use `ILIKE` instead.
-To work around this problem you should write `LIKE` queries using Arel instead
-of raw SQL fragments as Arel automatically uses `ILIKE` on PostgreSQL and `LIKE`
-on MySQL. This means that instead of this:
+To handle this automatically you should use `LIKE` queries using Arel instead
+of raw SQL fragments, as Arel automatically uses `ILIKE` on PostgreSQL.
```ruby
Issue.where('title LIKE ?', 'WIP:%')
@@ -45,7 +42,7 @@ table = Issue.arel_table
Issue.where(table[:title].matches('WIP:%').or(table[:foo].matches('WIP:%')))
```
-For PostgreSQL this produces:
+On PostgreSQL, this produces:
```sql
SELECT *
@@ -53,18 +50,10 @@ FROM issues
WHERE (title ILIKE 'WIP:%' OR foo ILIKE 'WIP:%')
```
-In turn for MySQL this produces:
-
-```sql
-SELECT *
-FROM issues
-WHERE (title LIKE 'WIP:%' OR foo LIKE 'WIP:%')
-```
-
## LIKE & Indexes
-Neither PostgreSQL nor MySQL use any indexes when using `LIKE` / `ILIKE` with a
-wildcard at the start. For example, this will not use any indexes:
+PostgreSQL won't use any indexes when using `LIKE` / `ILIKE` with a wildcard at
+the start. For example, this will not use any indexes:
```sql
SELECT *
@@ -75,9 +64,8 @@ WHERE title ILIKE '%WIP:%';
Because the value for `ILIKE` starts with a wildcard the database is not able to
use an index as it doesn't know where to start scanning the indexes.
-MySQL provides no known solution to this problem. Luckily PostgreSQL _does_
-provide a solution: trigram GIN indexes. These indexes can be created as
-follows:
+Luckily, PostgreSQL _does_ provide a solution: trigram GIN indexes. These
+indexes can be created as follows:
```sql
CREATE INDEX [CONCURRENTLY] index_name_here
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index 9d6792e9139..f30a83a4c71 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -15,16 +15,6 @@ manifest themselves within our code. When designing our tests, take time to revi
our test design. We can find some helpful heuristics documented in the Handbook in the
[Test Design](https://about.gitlab.com/handbook/engineering/quality/guidelines/test-engineering/test-design/) section.
-## Run tests against MySQL
-
-By default, tests are only run against PostgreSQL, but you can run them on
-demand against MySQL by following one of the following conventions:
-
-| Convention | Valid example |
-|:----------------------|:-----------------------------|
-| Include `mysql` in your branch name | `enhance-mysql-support` |
-| Include `[run mysql]` in your commit message | `Fix MySQL support<br><br>[run mysql]` |
-
## Test speed
GitLab has a massive test suite that, without [parallelization], can take hours
@@ -455,6 +445,19 @@ complexity of RSpec expectations.They should be placed under
a certain type of specs only (e.g. features, requests etc.) but shouldn't be if
they apply to multiple type of specs.
+#### `be_like_time`
+
+Time returned from a database can differ in precision from time objects
+in Ruby, so we need flexible tolerances when comparing in specs. We can
+use `be_like_time` to compare that times are within one second of each
+other.
+
+Example:
+
+```ruby
+expect(metrics.merged_at).to be_like_time(time)
+```
+
#### `have_gitlab_http_status`
Prefer `have_gitlab_http_status` over `have_http_status` because the former
diff --git a/doc/development/testing_guide/ci.md b/doc/development/testing_guide/ci.md
index 87d48726268..d9f66a827de 100644
--- a/doc/development/testing_guide/ci.md
+++ b/doc/development/testing_guide/ci.md
@@ -39,7 +39,6 @@ slowest test files and try to improve them.
## CI setup
-- On CE and EE, the test suite runs both PostgreSQL and MySQL.
- Rails logging to `log/test.log` is disabled by default in CI [for
performance reasons][logging]. To override this setting, provide the
`RAILS_ENABLE_TEST_LOG` environment variable.
diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md
index 931cbc51cae..eb0bf6fc563 100644
--- a/doc/development/testing_guide/flaky_tests.md
+++ b/doc/development/testing_guide/flaky_tests.md
@@ -35,8 +35,8 @@ Once a test is in quarantine, there are 3 choices:
Quarantined tests are run on the CI in dedicated jobs that are allowed to fail:
-- `rspec-pg-quarantine` and `rspec-mysql-quarantine` (CE & EE)
-- `rspec-pg-quarantine-ee` and `rspec-mysql-quarantine-ee` (EE only)
+- `rspec-pg-quarantine` (CE & EE)
+- `rspec-pg-quarantine-ee` (EE only)
## Automatic retries and flaky tests detection
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 2985278cc92..52ac746716c 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -593,6 +593,365 @@ end
[capybara]: https://github.com/teamcapybara/capybara
[jasmine]: https://jasmine.github.io/
+## Overview of Frontend Testing Levels
+
+Tests relevant for frontend development can be found at the following places:
+
+- `spec/javascripts/` which are run by Karma (command: `yarn karma`) and contain
+ - [frontend unit tests](#frontend-unit-tests)
+ - [frontend component tests](#frontend-component-tests)
+ - [frontend integration tests](#frontend-integration-tests)
+- `spec/frontend/` which are run by Jest (command: `yarn jest`) and contain
+ - [frontend unit tests](#frontend-unit-tests)
+ - [frontend component tests](#frontend-component-tests)
+ - [frontend integration tests](#frontend-integration-tests)
+- `spec/features/` which are run by RSpec and contain
+ - [feature tests](#feature-tests)
+
+All tests in `spec/javascripts/` will eventually be migrated to `spec/frontend/` (see also [#52483](https://gitlab.com/gitlab-org/gitlab-ce/issues/52483)).
+
+In addition, there used to be feature tests in `features/`, run by Spinach.
+These were removed from the codebase in May 2018 ([#23036](https://gitlab.com/gitlab-org/gitlab-ce/issues/23036)).
+
+See also [Notes on testing Vue components](../fe_guide/vue.html#testing-vue-components).
+
+### Frontend unit tests
+
+Unit tests are on the lowest abstraction level and typically test functionality that is not directly perceivable by a user.
+
+#### When to use unit tests
+
+<details>
+ <summary>exported functions and classes</summary>
+ Anything that is exported can be reused at various places in a way you have no control over.
+ Therefore it is necessary to document the expected behavior of the public interface with tests.
+</details>
+
+<details>
+ <summary>Vuex actions</summary>
+ Any Vuex action needs to work in a consistent way independent of the component it is triggered from.
+</details>
+
+<details>
+ <summary>Vuex mutations</summary>
+ For complex Vuex mutations it helps to identify the source of a problem by separating the tests from other parts of the Vuex store.
+</details>
+
+#### When *not* to use unit tests
+
+<details>
+ <summary>non-exported functions or classes</summary>
+ Anything that is not exported from a module can be considered private or an implementation detail and doesn't need to be tested.
+</details>
+
+<details>
+ <summary>constants</summary>
+ Testing the value of a constant would mean to copy it.
+ This results in extra effort without additional confidence that the value is correct.
+</details>
+
+<details>
+ <summary>Vue components</summary>
+ Computed properties, methods, and lifecycle hooks can be considered an implementation detail of components and don't need to be tested.
+ They are implicitly covered by component tests.
+ The <a href="https://vue-test-utils.vuejs.org/guides/#getting-started">official Vue guidelines</a> suggest the same.
+</details>
+
+#### What to mock in unit tests
+
+<details>
+ <summary>state of the class under test</summary>
+ Modifying the state of the class under test directly rather than using methods of the class avoids side-effects in test setup.
+</details>
+
+<details>
+ <summary>other exported classes</summary>
+ Every class needs to be tested in isolation to prevent test scenarios from growing exponentially.
+</details>
+
+<details>
+ <summary>single DOM elements if passed as parameters</summary>
+ For tests that only operate on single DOM elements rather than a whole page, creating these elements is cheaper than loading a whole HTML fixture.
+</details>
+
+<details>
+ <summary>all server requests</summary>
+ When running frontend unit tests, the backend may not be reachable.
+ Therefore all outgoing requests need to be mocked.
+</details>
+
+<details>
+ <summary>asynchronous background operations</summary>
+ Background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
+</details>
+
+#### What *not* to mock in unit tests
+
+<details>
+ <summary>non-exported functions or classes</summary>
+ Everything that is not exported can be considered private to the module and will be implicitly tested via the exported classes / functions.
+</details>
+
+<details>
+ <summary>methods of the class under test</summary>
+ By mocking methods of the class under test, the mocks will be tested and not the real methods.
+</details>
+
+<details>
+ <summary>utility functions (pure functions, or those that only modify parameters)</summary>
+ If a function has no side effects because it has no state, it is safe to not mock it in tests.
+</details>
+
+<details>
+ <summary>full HTML pages</summary>
+ Loading the HTML of a full page slows down tests, so it should be avoided in unit tests.
+</details>
+
+### Frontend component tests
+
+Component tests cover the state of a single component that is perceivable by a user depending on external signals such as user input, events fired from other components, or application state.
+
+#### When to use component tests
+
+- Vue components
+
+#### When *not* to use component tests
+
+<details>
+ <summary>Vue applications</summary>
+ Vue applications may contain many components.
+ Testing them on a component level requires too much effort.
+ Therefore they are tested on frontend integration level.
+</details>
+
+<details>
+ <summary>HAML templates</summary>
+ HAML templates contain only Markup and no frontend-side logic.
+ Therefore they are not complete components.
+</details>
+
+#### What to mock in component tests
+
+<details>
+ <summary>DOM</summary>
+ Operating on the real DOM is significantly slower than on the virtual DOM.
+</details>
+
+<details>
+ <summary>properties and state of the component under test</summary>
+ Similarly to testing classes, modifying the properties directly (rather than relying on methods of the component) avoids side-effects.
+</details>
+
+<details>
+ <summary>Vuex store</summary>
+ To avoid side effects and keep component tests simple, Vuex stores are replaced with mocks.
+</details>
+
+<details>
+ <summary>all server requests</summary>
+ Similar to unit tests, when running component tests, the backend may not be reachable.
+ Therefore all outgoing requests need to be mocked.
+</details>
+
+<details>
+ <summary>asynchronous background operations</summary>
+ Similar to unit tests, background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
+</details>
+
+<details>
+ <summary>child components</summary>
+ Every component is tested individually, so child components are mocked.
+ See also <a href="https://vue-test-utils.vuejs.org/api/#shallowmount">shallowMount()</a>
+</details>
+
+#### What *not* to mock in component tests
+
+<details>
+ <summary>methods or computed properties of the component under test</summary>
+ By mocking part of the component under test, the mocks will be tested and not the real component.
+</details>
+
+<details>
+ <summary>functions and classes independent from Vue</summary>
+ All plain JavaScript code is already covered by unit tests and needs not to be mocked in component tests.
+</details>
+
+### Frontend integration tests
+
+Integration tests cover the interaction between all components on a single page.
+Their abstraction level is comparable to how a user would interact with the UI.
+
+#### When to use integration tests
+
+<details>
+ <summary>page bundles (<code>index.js</code> files in <code>app/assets/javascripts/pages/</code>)</summary>
+ Testing the page bundles ensures the corresponding frontend components integrate well.
+</details>
+
+<details>
+ <summary>Vue applications outside of page bundles</summary>
+ Testing Vue applications as a whole ensures the corresponding frontend components integrate well.
+</details>
+
+#### What to mock in integration tests
+
+<details>
+ <summary>HAML views (use fixtures instead)</summary>
+ Rendering HAML views requires a Rails environment including a running database which we cannot rely on in frontend tests.
+</details>
+
+<details>
+ <summary>all server requests</summary>
+ Similar to unit and component tests, when running component tests, the backend may not be reachable.
+ Therefore all outgoing requests need to be mocked.
+</details>
+
+<details>
+ <summary>asynchronous background operations that are not perceivable on the page</summary>
+ Background operations that affect the page need to be tested on this level.
+ All other background operations cannot be stopped or waited on, so they will continue running in the following tests and cause side effects.
+</details>
+
+#### What *not* to mock in integration tests
+
+<details>
+ <summary>DOM</summary>
+ Testing on the real DOM ensures our components work in the environment they are meant for.
+ Part of this will be delegated to <a href="https://gitlab.com/gitlab-org/quality/team-tasks/issues/45">cross-browser testing</a>.
+</details>
+
+<details>
+ <summary>properties or state of components</summary>
+ On this level, all tests can only perform actions a user would do.
+ For example to change the state of a component, a click event would be fired.
+</details>
+
+<details>
+ <summary>Vuex stores</summary>
+ When testing the frontend code of a page as a whole, the interaction between Vue components and Vuex stores is covered as well.
+</details>
+
+### Feature tests
+
+In contrast to [frontend integration tests](#frontend-integration-tests), feature tests make requests against the real backend instead of using fixtures.
+This also implies that database queries are executed which makes this category significantly slower.
+
+See also the [RSpec testing guidelines](../testing_guide/best_practices.md#rspec).
+
+#### When to use feature tests
+
+- Use cases that require a backend and cannot be tested using fixtures.
+- Behavior that is not part of a page bundle but defined globally.
+
+#### Relevant notes
+
+A `:js` flag is added to the test to make sure the full environment is loaded.
+
+```js
+scenario 'successfully', :js do
+ sign_in(create(:admin))
+end
+```
+
+The steps of each test are written using capybara methods ([documentation](https://www.rubydoc.info/gems/capybara)).
+
+Bear in mind <abbr title="XMLHttpRequest">XHR</abbr> calls might require you to use `wait_for_requests` in between steps, like so:
+
+```rspec
+find('.form-control').native.send_keys(:enter)
+
+wait_for_requests
+
+expect(page).not_to have_selector('.card')
+```
+
+## Test helpers
+
+### Vuex Helper: `testAction`
+
+We have a helper available to make testing actions easier, as per [official documentation](https://vuex.vuejs.org/guide/testing.html):
+
+```js
+testAction(
+ actions.actionName, // action
+ { }, // params to be passed to action
+ state, // state
+ [
+ { type: types.MUTATION},
+ { type: types.MUTATION_1, payload: {}},
+ ], // mutations committed
+ [
+ { type: 'actionName', payload: {}},
+ { type: 'actionName1', payload: {}},
+ ] // actions dispatched
+ done,
+);
+```
+
+Check an example in [spec/javascripts/ide/stores/actions_spec.jsspec/javascripts/ide/stores/actions_spec.js](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/javascripts/ide/stores/actions_spec.js).
+
+### Vue Helper: `mountComponent`
+
+To make mounting a Vue component easier and more readable, we have a few helpers available in `spec/helpers/vue_mount_component_helper`:
+
+- `createComponentWithStore`
+- `mountComponentWithStore`
+
+Examples of usage:
+
+```js
+beforeEach(() => {
+ vm = createComponentWithStore(Component, store);
+
+ vm.$store.state.currentBranchId = 'master';
+
+ vm.$mount();
+});
+```
+
+```js
+beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ el: '#dummy-element',
+ store,
+ props: { badge },
+ });
+});
+```
+
+Don't forget to clean up:
+
+```js
+afterEach(() => {
+ vm.$destroy();
+});
+```
+
+## Testing with older browsers
+
+Some regressions only affect a specific browser version. We can install and test in particular browsers with either Firefox or Browserstack using the following steps:
+
+### Browserstack
+
+[Browserstack](https://www.browserstack.com/) allows you to test more than 1200 mobile devices and browsers.
+You can use it directly through the [live app](https://www.browserstack.com/live) or you can install the [chrome extension](https://chrome.google.com/webstore/detail/browserstack/nkihdmlheodkdfojglpcjjmioefjahjb) for easy access.
+You can find the credentials on 1Password, under `frontendteam@gitlab.com`.
+
+### Firefox
+
+#### macOS
+
+You can download any older version of Firefox from the releases FTP server, <https://ftp.mozilla.org/pub/firefox/releases/>:
+
+1. From the website, select a version, in this case `50.0.1`.
+1. Go to the mac folder.
+1. Select your preferred language, you will find the dmg package inside, download it.
+1. Drag and drop the application to any other folder but the `Applications` folder.
+1. Rename the application to something like `Firefox_Old`.
+1. Move the application to the `Applications` folder.
+1. Open up a terminal and run `/Applications/Firefox_Old.app/Contents/MacOS/firefox-bin -profilemanager` to create a new profile specific to that Firefox version.
+1. Once the profile has been created, quit the app, and run it again like normal. You now have a working older Firefox version.
+
---
[Return to Testing documentation](index.md)
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
index 7843fc4c874..11449712a04 100644
--- a/doc/development/testing_guide/review_apps.md
+++ b/doc/development/testing_guide/review_apps.md
@@ -255,8 +255,8 @@ that a machine will hit the "too many mount points" problem in the future.
thousands of unused Docker images.**
> We have to start somewhere and improve later. Also, we're using the
- CNG-mirror project to store these Docker images so that we can just wipe out
- the registry at some point, and use a new fresh, empty one.
+ > CNG-mirror project to store these Docker images so that we can just wipe out
+ > the registry at some point, and use a new fresh, empty one.
**How do we secure this from abuse? Apps are open to the world so we need to
find a way to limit it to only us.**
diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md
index 6e1fdc765a1..0090c84cbf0 100644
--- a/doc/development/testing_guide/testing_levels.md
+++ b/doc/development/testing_guide/testing_levels.md
@@ -66,7 +66,6 @@ They're useful to test permissions, redirections, what view is rendered etc.
| `app/controllers/` | `spec/controllers/` | RSpec | For N+1 tests, use [request specs](../query_recorder.md#use-request-specs-instead-of-controller-specs) |
| `app/mailers/` | `spec/mailers/` | RSpec | |
| `lib/api/` | `spec/requests/api/` | RSpec | |
-| `lib/ci/api/` | `spec/requests/ci/api/` | RSpec | |
| `app/assets/javascripts/` | `spec/javascripts/`, `spec/frontend/` | Karma & Jest | More details in the [Frontend Testing guide](frontend_testing.md) section. |
### About controller tests
diff --git a/doc/development/verifying_database_capabilities.md b/doc/development/verifying_database_capabilities.md
index ccec6f7d719..6b4995aebe2 100644
--- a/doc/development/verifying_database_capabilities.md
+++ b/doc/development/verifying_database_capabilities.md
@@ -1,15 +1,15 @@
# Verifying Database Capabilities
-Sometimes certain bits of code may only work on a certain database and/or
+Sometimes certain bits of code may only work on a certain database
version. While we try to avoid such code as much as possible sometimes it is
necessary to add database (version) specific behaviour.
To facilitate this we have the following methods that you can use:
-- `Gitlab::Database.postgresql?`: returns `true` if PostgreSQL is being used
-- `Gitlab::Database.mysql?`: returns `true` if MySQL is being used
+- `Gitlab::Database.postgresql?`: returns `true` if PostgreSQL is being used.
+ You can normally just assume this is the case.
- `Gitlab::Database.version`: returns the PostgreSQL version number as a string
- in the format `X.Y.Z`. This method does not work for MySQL
+ in the format `X.Y.Z`.
This allows you to write code such as:
diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md
index f0da1cc2ddc..944bf5900c5 100644
--- a/doc/development/what_requires_downtime.md
+++ b/doc/development/what_requires_downtime.md
@@ -7,9 +7,8 @@ downtime.
## Adding Columns
-On PostgreSQL you can safely add a new column to an existing table as long as it
-does **not** have a default value. For example, this query would not require
-downtime:
+You can safely add a new column to an existing table as long as it does **not**
+have a default value. For example, this query would not require downtime:
```sql
ALTER TABLE projects ADD COLUMN random_value int;
@@ -27,11 +26,6 @@ This requires updating every single row in the `projects` table so that
indexes in a table. This in turn acquires enough locks on the table for it to
effectively block any other queries.
-As of MySQL 5.6 adding a column to a table is still quite an expensive
-operation, even when using `ALGORITHM=INPLACE` and `LOCK=NONE`. This means
-downtime _may_ be required when modifying large tables as otherwise the
-operation could potentially take hours to complete.
-
Adding a column with a default value _can_ be done without requiring downtime
when using the migration helper method
`Gitlab::Database::MigrationHelpers#add_column_with_default`. This method works
@@ -311,8 +305,7 @@ migrations](background_migrations.md#cleaning-up).
## Adding Indexes
Adding indexes is an expensive process that blocks INSERT and UPDATE queries for
-the duration. When using PostgreSQL one can work around this by using the
-`CONCURRENTLY` option:
+the duration. You can work around this by using the `CONCURRENTLY` option:
```sql
CREATE INDEX CONCURRENTLY index_name ON projects (column_name);
@@ -336,17 +329,9 @@ end
Note that `add_concurrent_index` can not be reversed automatically, thus you
need to manually define `up` and `down`.
-When running this on PostgreSQL the `CONCURRENTLY` option mentioned above is
-used. On MySQL this method produces a regular `CREATE INDEX` query.
-
-MySQL doesn't really have a workaround for this. Supposedly it _can_ create
-indexes without the need for downtime but only for variable width columns. The
-details on this are a bit sketchy. Since it's better to be safe than sorry one
-should assume that adding indexes requires downtime on MySQL.
-
## Dropping Indexes
-Dropping an index does not require downtime on both PostgreSQL and MySQL.
+Dropping an index does not require downtime.
## Adding Tables
@@ -370,7 +355,7 @@ transaction this means this approach would require downtime.
GitLab allows you to work around this by using
`Gitlab::Database::MigrationHelpers#add_concurrent_foreign_key`. This method
-ensures that when PostgreSQL is used no downtime is needed.
+ensures that no downtime is needed.
## Removing Foreign Keys
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 9c1258fa1aa..4bfcd4aad96 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -584,6 +584,52 @@ Unless you have a `Dockerfile` in your repo, your image is built with
Herokuish, and you must prefix commands run in these images with `/bin/herokuish
procfile exec` to replicate the environment where your application will run.
+#### Workers
+
+Some web applications need to run extra deployments for "worker processes". For
+example it is common in a Rails application to have a separate worker process
+to run background tasks like sending emails.
+
+The [default Helm chart](https://gitlab.com/gitlab-org/charts/auto-deploy-app)
+used in Auto Deploy [has support for running worker
+processes](https://gitlab.com/gitlab-org/charts/auto-deploy-app/merge_requests/9).
+
+In order to run a worker you'll need to ensure that it is able to respond to
+the standard health checks which expect a successful HTTP response on port
+`5000`. For sidekiq you could make use of the
+[sidekiq_alive gem](https://rubygems.org/gems/sidekiq_alive) to do this.
+
+In order to work with sidekiq you'll also need to ensure your deployments have
+access to a redis instance. Auto DevOps won't deploy this for you so you'll
+need to manage this separately and then set a CI variable
+`K8S_SECRET_REDIS_URL` which the URL of this instance to ensure it's passed
+into your deployments.
+
+Once you have configured your worker to respond to health checks you you will
+need to configure a CI variable `HELM_UPGRADE_EXTRA_ARGS` with the value
+`--values helm-values.yaml`. Then you can, for example, run a
+[sidekiq](https://github.com/mperham/sidekiq) worker for your rails application
+by adding a file named `helm-values.yaml` to your repo with the following
+content:
+
+```yml
+workers:
+ sidekiq:
+ replicaCount: 1
+ command:
+ - /bin/herokuish
+ - procfile
+ - exec
+ - sidekiq
+ preStopCommand:
+ - /bin/herokuish
+ - procfile
+ - exec
+ - sidekiqctl
+ - quiet
+ terminationGracePeriodSeconds: 60
+```
+
### Auto Monitoring
See the [requirements](#requirements) for Auto Monitoring to enable this stage.
diff --git a/doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md b/doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md
index b2d56be154b..8e53a6995fb 100644
--- a/doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md
+++ b/doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md
@@ -9,7 +9,7 @@ type: reference
This setting allows you to rate limit the requests to raw endpoints, defaults to `300` requests per minute.
It can be modified in **Admin Area > Network > Performance Optimization**.
-For example, requests over `300` per minute to `https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/controllers/application_controller.rb` will be blocked.
+For example, requests over `300` per minute to `https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/controllers/application_controller.rb` will be blocked. Access to the raw file will be released after 1 minute.
![Rate limits on raw endpoints](img/rate_limits_on_raw_endpoints.png)
@@ -18,3 +18,5 @@ This limit is:
- Applied independently per project, per commit and per file path.
- Not applied per IP address.
- Active by default. To disable, set the option to `0`.
+
+Requests over the rate limit are logged into `auth.log`.
diff --git a/doc/user/analytics/cycle_analytics.md b/doc/user/analytics/cycle_analytics.md
new file mode 100644
index 00000000000..b7389c8689d
--- /dev/null
+++ b/doc/user/analytics/cycle_analytics.md
@@ -0,0 +1,182 @@
+# Cycle Analytics
+
+> - Introduced prior to GitLab 12.2 at the project level.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/12077) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.2 at the group level (enabled by feature flag `analytics`).
+
+Cycle Analytics measures the time spent to go from an [idea to production] - also known
+as cycle time - for each of your projects. Cycle Analytics displays the median time for an idea to
+reach production, along with the time typically spent in each DevOps stage along the way.
+
+Cycle Analytics is useful in order to quickly determine the velocity of a given
+project. It points to bottlenecks in the development process, enabling management
+to uncover, triage, and identify the root cause of slowdowns in the software development life cycle.
+
+Cycle Analytics is tightly coupled with the [GitLab flow] and
+calculates a separate median for each stage.
+
+## Overview
+
+Cycle Analytics is available:
+
+- From GitLab 12.2, at the group level in the analytics workspace at
+ **Analytics > Cycle Analytics**. **(PREMIUM)**
+
+ In the future, multiple groups will be selectable which will effectively make this an
+ instance-level feature.
+
+ NOTE: **Note:**
+ Requires the [analytics workspace](index.md) to be enabled.
+
+- At the project level via **Project > Cycle Analytics**.
+
+There are seven stages that are tracked as part of the Cycle Analytics calculations.
+
+- **Issue** (Tracker)
+ - Time to schedule an issue (by milestone or by adding it to an issue board)
+- **Plan** (Board)
+ - Time to first commit
+- **Code** (IDE)
+ - Time to create a merge request
+- **Test** (CI)
+ - Time it takes GitLab CI/CD to test your code
+- **Review** (Merge Request/MR)
+ - Time spent on code review
+- **Staging** (Continuous Deployment)
+ - Time between merging and deploying to production
+- **Production** (Total)
+ - Total lifecycle time; i.e. the velocity of the project or team
+
+## How the data is measured
+
+Cycle Analytics records cycle time and data based on the project issues with the
+exception of the staging and production stages, where only data deployed to
+production are measured.
+
+Specifically, if your CI is not set up and you have not defined a `production`
+or `production/*` [environment], then you will not have any data for those stages.
+
+Each stage of Cycle Analytics is further described in the table below.
+
+| **Stage** | **Description** |
+| --------- | --------------- |
+| Issue | Measures the median time between creating an issue and taking action to solve it, by either labeling it or adding it to a milestone, whatever comes first. The label will be tracked only if it already has an [Issue Board list](../project/issue_board.md#creating-a-new-list) created for it. |
+| Plan | Measures the median time between the action you took for the previous stage, and pushing the first commit to the branch. The very first commit of the branch is the one that triggers the separation between **Plan** and **Code**, and at least one of the commits in the branch needs to contain the related issue number (e.g., `#42`). If none of the commits in the branch mention the related issue number, it is not considered to the measurement time of the stage. |
+| Code | Measures the median time between pushing a first commit (previous stage) and creating a merge request (MR) related to that commit. The key to keep the process tracked is to include the [issue closing pattern](../project/issues/managing_issues.md#closing-issues-automatically) to the description of the merge request (for example, `Closes #xxx`, where `xxx` is the number of the issue related to this merge request). If the issue closing pattern is not present in the merge request description, the MR is not considered to the measurement time of the stage. |
+| Test | Measures the median time to run the entire pipeline for that project. It's related to the time GitLab CI takes to run every job for the commits pushed to that merge request defined in the previous stage. It is basically the start->finish time for all pipelines. |
+| Review | Measures the median time taken to review the merge request that has closing issue pattern, between its creation and until it's merged. |
+| Staging | Measures the median time between merging the merge request with closing issue pattern until the very first deployment to production. It's tracked by the [environment] set to `production` or matching `production/*` (case-sensitive, `Production` won't work) in your GitLab CI configuration. If there isn't a production environment, this is not tracked. |
+| Production| The sum of all time (medians) taken to run the entire process, from issue creation to deploying the code to production. |
+
+---
+
+How this works, behind the scenes:
+
+1. Issues and merge requests are grouped together in pairs, such that for each
+ `<issue, merge request>` pair, the merge request has the [issue closing pattern](../project/issues/managing_issues.md#closing-issues-automatically)
+ for the corresponding issue. All other issues and merge requests are **not**
+ considered.
+1. Then the `<issue, merge request>` pairs are filtered out by last XX days (specified
+ by the UI - default is 90 days). So it prohibits these pairs from being considered.
+1. For the remaining `<issue, merge request>` pairs, we check the information that
+ we need for the stages, like issue creation date, merge request merge time,
+ etc.
+
+To sum up, anything that doesn't follow [GitLab flow] will not be tracked and the
+Cycle Analytics dashboard will not present any data for:
+
+- merge requests that do not close an issue.
+- issues not labeled with a label present in the Issue Board or for issues not assigned a milestone.
+- staging and production stages, if the project has no `production` or `production/*`
+ environment.
+
+## Example workflow
+
+Below is a simple fictional workflow of a single cycle that happens in a
+single day passing through all seven stages. Note that if a stage does not have
+a start and a stop mark, it is not measured and hence not calculated in the median
+time. It is assumed that milestones are created and CI for testing and setting
+environments is configured.
+
+1. Issue is created at 09:00 (start of **Issue** stage).
+1. Issue is added to a milestone at 11:00 (stop of **Issue** stage / start of
+ **Plan** stage).
+1. Start working on the issue, create a branch locally and make one commit at
+ 12:00.
+1. Make a second commit to the branch which mentions the issue number at 12.30
+ (stop of **Plan** stage / start of **Code** stage).
+1. Push branch and create a merge request that contains the [issue closing pattern](../project/issues/managing_issues.md#closing-issues-automatically)
+ in its description at 14:00 (stop of **Code** stage / start of **Test** and
+ **Review** stages).
+1. The CI starts running your scripts defined in [`.gitlab-ci.yml`][yml] and
+ takes 5min (stop of **Test** stage).
+1. Review merge request, ensure that everything is OK and merge the merge
+ request at 19:00. (stop of **Review** stage / start of **Staging** stage).
+1. Now that the merge request is merged, a deployment to the `production`
+ environment starts and finishes at 19:30 (stop of **Staging** stage).
+1. The cycle completes and the sum of the median times of the previous stages
+ is recorded to the **Production** stage. That is the time between creating an
+ issue and deploying its relevant merge request to production.
+
+From the above example you can conclude the time it took each stage to complete
+as long as their total time:
+
+- **Issue**: 2h (11:00 - 09:00)
+- **Plan**: 1h (12:00 - 11:00)
+- **Code**: 2h (14:00 - 12:00)
+- **Test**: 5min
+- **Review**: 5h (19:00 - 14:00)
+- **Staging**: 30min (19:30 - 19:00)
+- **Production**: Since this stage measures the sum of median time off all
+ previous stages, we cannot calculate it if we don't know the status of the
+ stages before. In case this is the very first cycle that is run in the project,
+ then the **Production** time is 10h 30min (19:30 - 09:00)
+
+A few notes:
+
+- In the above example we demonstrated that it doesn't matter if your first
+ commit doesn't mention the issue number, you can do this later in any commit
+ of the branch you are working on.
+- You can see that the **Test** stage is not calculated to the overall time of
+ the cycle since it is included in the **Review** process (every MR should be
+ tested).
+- The example above was just **one cycle** of the seven stages. Add multiple
+ cycles, calculate their median time and the result is what the dashboard of
+ Cycle Analytics is showing.
+
+## Permissions
+
+The current permissions on the Project Cycle Analytics dashboard are:
+
+- Public projects - anyone can access
+- Internal projects - any authenticated user can access
+- Private projects - any member Guest and above can access
+
+You can [read more about permissions][permissions] in general.
+
+NOTE: **Note:**
+As of GitLab 12.2, the project-level page is deprecated. You should access
+project-level Cycle Analytics from **Analytics > Cycle Analytics** in the top
+navigation bar. We will ensure that the same project-level functionality is available
+to CE users in the new analytics space.
+
+For Cycle Analytics functionality introduced in GitLab 12.2 and later:
+
+- Users must have Reporter access or above.
+- Features are available only on
+ [Premium or Silver tiers](https://about.gitlab.com/pricing/) and above.
+
+## More resources
+
+Learn more about Cycle Analytics in the following resources:
+
+- [Cycle Analytics feature page](https://about.gitlab.com/features/cycle-analytics/)
+- [Cycle Analytics feature preview](https://about.gitlab.com/2016/09/16/feature-preview-introducing-cycle-analytics/)
+- [Cycle Analytics feature highlight](https://about.gitlab.com/2016/09/21/cycle-analytics-feature-highlight/)
+
+[ce-5986]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5986
+[ce-20975]: https://gitlab.com/gitlab-org/gitlab-ce/issues/20975
+[environment]: ../../ci/yaml/README.md#environment
+[GitLab flow]: ../../workflow/gitlab_flow.md
+[idea to production]: https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab
+[permissions]: ../permissions.md
+[yml]: ../../ci/yaml/README.md
diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md
new file mode 100644
index 00000000000..ec719c0b4a1
--- /dev/null
+++ b/doc/user/analytics/index.md
@@ -0,0 +1,22 @@
+# Analytics workspace
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/12077) in GitLab 12.2 (enabled using `analytics` feature flag).
+
+The Analytics workspace will make it possible to aggregate analytics across
+GitLab, so that users can view information across multiple projects and groups
+in one place.
+
+To access the centralized analytics workspace:
+
+1. Ensure it's enabled. Requires a GitLab administrator to enable it with the `analytics` feature
+ flag.
+1. Once enabled, click on **Analytics** from the top navigation bar.
+
+## Available analytics
+
+From the centralized analytics workspace, the following analytics are available:
+
+- [Cycle Analytics](cycle_analytics.md).
+
+NOTE: **Note:**
+Project-level Cycle Analytics are still available at a project's **Project > Cycle Analytics**.
diff --git a/doc/user/discussions/img/make_suggestion.png b/doc/user/discussions/img/make_suggestion.png
index 20acc1417da..a24e29770aa 100644
--- a/doc/user/discussions/img/make_suggestion.png
+++ b/doc/user/discussions/img/make_suggestion.png
Binary files differ
diff --git a/doc/user/discussions/img/suggestion.png b/doc/user/discussions/img/suggestion.png
index 68a67e6ae5e..f7962305a15 100644
--- a/doc/user/discussions/img/suggestion.png
+++ b/doc/user/discussions/img/suggestion.png
Binary files differ
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index c9fbd7effa0..d21a325d401 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -112,57 +112,6 @@ Below are the shared Runners settings.
The full contents of our `config.toml` are:
-**DigitalOcean**
-
-```toml
-concurrent = X
-check_interval = 1
-metrics_server = "X"
-sentry_dsn = "X"
-
-[[runners]]
- name = "docker-auto-scale"
- request_concurrency = X
- url = "https://gitlab.com/"
- token = "SHARED_RUNNER_TOKEN"
- executor = "docker+machine"
- environment = [
- "DOCKER_DRIVER=overlay2"
- ]
- limit = X
- [runners.docker]
- image = "ruby:2.5"
- privileged = true
- [runners.machine]
- IdleCount = 20
- IdleTime = 1800
- OffPeakPeriods = ["* * * * * sat,sun *"]
- OffPeakTimezone = "UTC"
- OffPeakIdleCount = 5
- OffPeakIdleTime = 1800
- MaxBuilds = 1
- MachineName = "srm-%s"
- MachineDriver = "digitalocean"
- MachineOptions = [
- "digitalocean-image=X",
- "digitalocean-ssh-user=core",
- "digitalocean-region=nyc1",
- "digitalocean-size=s-2vcpu-2gb",
- "digitalocean-private-networking",
- "digitalocean-tags=shared_runners,gitlab_com",
- "engine-registry-mirror=http://INTERNAL_IP_OF_OUR_REGISTRY_MIRROR",
- "digitalocean-access-token=DIGITAL_OCEAN_ACCESS_TOKEN",
- ]
- [runners.cache]
- Type = "s3"
- BucketName = "runner"
- Insecure = true
- Shared = true
- ServerAddress = "INTERNAL_IP_OF_OUR_CACHE_SERVER"
- AccessKey = "ACCESS_KEY"
- SecretKey = "ACCESS_SECRET_KEY"
-```
-
**Google Cloud Platform**
```toml
@@ -178,20 +127,25 @@ sentry_dsn = "X"
token = "SHARED_RUNNER_TOKEN"
executor = "docker+machine"
environment = [
- "DOCKER_DRIVER=overlay2"
+ "DOCKER_DRIVER=overlay2",
+ "DOCKER_TLS_CERTDIR="
]
limit = X
[runners.docker]
image = "ruby:2.5"
privileged = true
+ volumes = [
+ "/certs/client",
+ "/dummy-sys-class-dmi-id:/sys/class/dmi/id:ro" # Make kaniko builds work on GCP.
+ ]
[runners.machine]
- IdleCount = 20
- IdleTime = 1800
+ IdleCount = 50
+ IdleTime = 3600
OffPeakPeriods = ["* * * * * sat,sun *"]
OffPeakTimezone = "UTC"
- OffPeakIdleCount = 5
- OffPeakIdleTime = 1800
- MaxBuilds = 1
+ OffPeakIdleCount = 15
+ OffPeakIdleTime = 3600
+ MaxBuilds = 1 # For security reasons we delete the VM after job has finished so it's not reused.
MachineName = "srm-%s"
MachineDriver = "google"
MachineOptions = [
@@ -202,17 +156,18 @@ sentry_dsn = "X"
"google-tags=gitlab-com,srm",
"google-use-internal-ip",
"google-zone=us-east1-d",
+ "engine-opt=mtu=1460", # Set MTU for container interface, for more information check https://gitlab.com/gitlab-org/gitlab-runner/issues/3214#note_82892928
"google-machine-image=PROJECT/global/images/IMAGE",
- "engine-registry-mirror=http://INTERNAL_IP_OF_OUR_REGISTRY_MIRROR"
+ "engine-opt=ipv6", # This will create IPv6 interfaces in the containers.
+ "engine-opt=fixed-cidr-v6=fc00::/7",
+ "google-operation-backoff-initial-interval=2" # Custom flag from forked docker-machine, for more information check https://github.com/docker/machine/pull/4600
]
[runners.cache]
- Type = "s3"
- BucketName = "runner"
- Insecure = true
+ Type = "gcs"
Shared = true
- ServerAddress = "INTERNAL_IP_OF_OUR_CACHE_SERVER"
- AccessKey = "ACCESS_KEY"
- SecretKey = "ACCESS_SECRET_KEY"
+ [runners.cache.gcs]
+ CredentialsFile = "/path/to/file"
+ BucketName = "bucket-name"
```
## Sidekiq
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index f8bef8b8a6a..5d136ad62da 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -59,15 +59,14 @@ Once [Single sign-on](index.md) has been configured, we can:
### Azure
-First, double check the [Single sign-on](index.md) configuration for your group and ensure that **Name identifier value** (NameID) points to `user.objectid` or another unique identifier. This will match the `extern_uid` used on GitLab.
+The SAML application that was created during [Single sign-on](index.md) setup now needs to be set up for SCIM.
-![Name identifier value mapping](img/scim_name_identifier_mapping.png)
+1. Check the configuration for your GitLab SAML app and ensure that **Name identifier value** (NameID) points to `user.objectid` or another unique identifier. This will match the `extern_uid` used on GitLab.
-#### Set up admin credentials
+ ![Name identifier value mapping](img/scim_name_identifier_mapping.png)
-Next, configure your GitLab application in Azure by following the
-[Provisioning users and groups to applications that support SCIM](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/use-scim-to-provision-users-and-groups#provisioning-users-and-groups-to-applications-that-support-scim)
-section in Azure's SCIM setup documentation.
+1. Set up automatic provisioning and administrative credentials by following the
+ [Provisioning users and groups to applications that support SCIM](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/use-scim-to-provision-users-and-groups#provisioning-users-and-groups-to-applications-that-support-scim) section in Azure's SCIM setup documentation.
During this configuration, note the following:
@@ -97,6 +96,7 @@ You can then test the connection by clicking on **Test Connection**. If the conn
NOTE: **Note:** If you used a unique identifier **other than** `objectId`, be sure to map it instead to both `id` and `externalId`.
1. Below the mapping list click on **Show advanced options > Edit attribute list for AppName**.
+
1. Leave the `id` as the primary and only required field.
NOTE: **Note:**
@@ -129,8 +129,7 @@ When testing the connection, you may encounter an error: **You appear to have en
When checking the Audit Logs for the Provisioning, you can sometimes see the
error `Namespace can't be blank, Name can't be blank, and User can't be blank.`
-This is likely caused because not all required fields (such as first name and
-last name) are present for all users being mapped.
+This is likely caused because not all required fields (such as first name and last name) are present for all users being mapped.
As a workaround, try an alternate mapping:
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 4fd7c5abf78..9ecc8a80b3a 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -162,7 +162,7 @@ to learn more.
### Cycle Analytics permissions
Find the current permissions on the Cycle Analytics dashboard on
-the [documentation on Cycle Analytics permissions](project/cycle_analytics.md#permissions).
+the [documentation on Cycle Analytics permissions](analytics/cycle_analytics.md#permissions).
### Issue Board permissions
@@ -237,13 +237,16 @@ To learn more, read through the documentation on
## Guest User
-Create a user and assign to a project with a role as `Guest` user, this user
-will be considered as guest user by GitLab and will not take up the license.
-There is no specific `Guest` role for newly created users. If this user will
-be assigned a higher role to any of the projects and groups then this user will
-take a license seat. If a user creates a project this user becomes a maintainer,
-therefore, takes up a license seat as well, in order to prevent this you have
-to go and edit user profile and mark the user as External.
+When a user is given `Guest` permissions on a project and/or group, and holds no
+higher permission level on any other project or group on the instance, the user
+is considered a guest user by GitLab and will not consume a license seat.
+There is no other specific "guest" designation for newly created users.
+
+If the user is assigned a higher role on any projects or groups, the user will
+take a license seat. If a user creates a project, the user becomes a `Maintainer`
+on the project, resulting in the use of a license seat. To prevent a guest user
+from creating projects, you can edit the user profile to mark the user as
+[External](#external-users-permissions).
## External users permissions
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index 424bee6e9f1..87577c9ec88 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -1,181 +1,5 @@
-# Cycle Analytics
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/12077) at a group level in [GitLab Premium and Silver](https://about.gitlab.com/pricing/) 12.2 (enabled by feature flag `analytics`).
-
-Cycle Analytics measures the time spent to go from an [idea to production] - also known
-as cycle time - for each of your projects. Cycle Analytics displays the median time for an idea to
-reach production, along with the time typically spent in each DevOps stage along the way.
-
-Cycle Analytics is useful in order to quickly determine the velocity of a given
-project. It points to bottlenecks in the development process, enabling management
-to uncover, triage, and root-cause slowdowns in the software development life cycle.
-
-Cycle Analytics is tightly coupled with the [GitLab flow] and
-calculates a separate median for each stage.
-
-## Overview
-
-Cycle Analytics are available at a:
-
-- Group level from the top navigation bar **Analytics > Cycle Analytics**. **(PREMIUM)**
-
- In the future, multiple groups will be selectable which will effectively make this an
- instance-level feature.
-
-- Project level from a project's **Project > Cycle Analytics**.
-
- ![Cycle Analytics landing page](img/cycle_analytics_landing_page.png)
-
-There are seven stages that are tracked as part of the Cycle Analytics calculations.
-
-- **Issue** (Tracker)
- - Time to schedule an issue (by milestone or by adding it to an issue board)
-- **Plan** (Board)
- - Time to first commit
-- **Code** (IDE)
- - Time to create a merge request
-- **Test** (CI)
- - Time it takes GitLab CI/CD to test your code
-- **Review** (Merge Request/MR)
- - Time spent on code review
-- **Staging** (Continuous Deployment)
- - Time between merging and deploying to production
-- **Production** (Total)
- - Total lifecycle time; i.e. the velocity of the project or team
-
-## How the data is measured
-
-Cycle Analytics records cycle time and data based on the project issues with the
-exception of the staging and production stages, where only data deployed to
-production are measured.
-
-Specifically, if your CI is not set up and you have not defined a `production`
-or `production/*` [environment], then you will not have any data for those stages.
-
-Below you can see in more detail what the various stages of Cycle Analytics mean.
-
-| **Stage** | **Description** |
-| --------- | --------------- |
-| Issue | Measures the median time between creating an issue and taking action to solve it, by either labeling it or adding it to a milestone, whatever comes first. The label will be tracked only if it already has an [Issue Board list][board] created for it. |
-| Plan | Measures the median time between the action you took for the previous stage, and pushing the first commit to the branch. The very first commit of the branch is the one that triggers the separation between **Plan** and **Code**, and at least one of the commits in the branch needs to contain the related issue number (e.g., `#42`). If none of the commits in the branch mention the related issue number, it is not considered to the measurement time of the stage. |
-| Code | Measures the median time between pushing a first commit (previous stage) and creating a merge request (MR) related to that commit. The key to keep the process tracked is to include the [issue closing pattern] to the description of the merge request (for example, `Closes #xxx`, where `xxx` is the number of the issue related to this merge request). If the issue closing pattern is not present in the merge request description, the MR is not considered to the measurement time of the stage. |
-| Test | Measures the median time to run the entire pipeline for that project. It's related to the time GitLab CI takes to run every job for the commits pushed to that merge request defined in the previous stage. It is basically the start->finish time for all pipelines. |
-| Review | Measures the median time taken to review the merge request that has closing issue pattern, between its creation and until it's merged. |
-| Staging | Measures the median time between merging the merge request with closing issue pattern until the very first deployment to production. It's tracked by the [environment] set to `production` or matching `production/*` (case-sensitive, `Production` won't work) in your GitLab CI configuration. If there isn't a production environment, this is not tracked. |
-| Production| The sum of all time (medians) taken to run the entire process, from issue creation to deploying the code to production. |
-
+---
+redirect_to: '../analytics/cycle_analytics.md'
---
-Here's a little explanation of how this works behind the scenes:
-
-1. Issues and merge requests are grouped together in pairs, such that for each
- `<issue, merge request>` pair, the merge request has the [issue closing pattern]
- for the corresponding issue. All other issues and merge requests are **not**
- considered.
-1. Then the `<issue, merge request>` pairs are filtered out by last XX days (specified
- by the UI - default is 90 days). So it prohibits these pairs from being considered.
-1. For the remaining `<issue, merge request>` pairs, we check the information that
- we need for the stages, like issue creation date, merge request merge time,
- etc.
-
-To sum up, anything that doesn't follow the [GitLab flow] won't be tracked at all.
-So, the Cycle Analytics dashboard won't present any data:
-
-- For merge requests that do not close an issue.
-- For issues not labeled with a label present in the Issue Board or for issues not assigned a milestone.
-- For staging and production stages, if the project has no `production` or `production/*`
- environment.
-
-## Example workflow
-
-Below is a simple fictional workflow of a single cycle that happens in a
-single day passing through all seven stages. Note that if a stage does not have
-a start and a stop mark, it is not measured and hence not calculated in the median
-time. It is assumed that milestones are created and CI for testing and setting
-environments is configured.
-
-1. Issue is created at 09:00 (start of **Issue** stage).
-1. Issue is added to a milestone at 11:00 (stop of **Issue** stage / start of
- **Plan** stage).
-1. Start working on the issue, create a branch locally and make one commit at
- 12:00.
-1. Make a second commit to the branch which mentions the issue number at 12.30
- (stop of **Plan** stage / start of **Code** stage).
-1. Push branch and create a merge request that contains the [issue closing pattern]
- in its description at 14:00 (stop of **Code** stage / start of **Test** and
- **Review** stages).
-1. The CI starts running your scripts defined in [`.gitlab-ci.yml`][yml] and
- takes 5min (stop of **Test** stage).
-1. Review merge request, ensure that everything is OK and merge the merge
- request at 19:00. (stop of **Review** stage / start of **Staging** stage).
-1. Now that the merge request is merged, a deployment to the `production`
- environment starts and finishes at 19:30 (stop of **Staging** stage).
-1. The cycle completes and the sum of the median times of the previous stages
- is recorded to the **Production** stage. That is the time between creating an
- issue and deploying its relevant merge request to production.
-
-From the above example you can conclude the time it took each stage to complete
-as long as their total time:
-
-- **Issue**: 2h (11:00 - 09:00)
-- **Plan**: 1h (12:00 - 11:00)
-- **Code**: 2h (14:00 - 12:00)
-- **Test**: 5min
-- **Review**: 5h (19:00 - 14:00)
-- **Staging**: 30min (19:30 - 19:00)
-- **Production**: Since this stage measures the sum of median time off all
- previous stages, we cannot calculate it if we don't know the status of the
- stages before. In case this is the very first cycle that is run in the project,
- then the **Production** time is 10h 30min (19:30 - 09:00)
-
-A few notes:
-
-- In the above example we demonstrated that it doesn't matter if your first
- commit doesn't mention the issue number, you can do this later in any commit
- of the branch you are working on.
-- You can see that the **Test** stage is not calculated to the overall time of
- the cycle since it is included in the **Review** process (every MR should be
- tested).
-- The example above was just **one cycle** of the seven stages. Add multiple
- cycles, calculate their median time and the result is what the dashboard of
- Cycle Analytics is showing.
-
-## Permissions
-
-The current permissions on the Project Cycle Analytics dashboard are:
-
-- Public projects - anyone can access
-- Internal projects - any authenticated user can access
-- Private projects - any member Guest and above can access
-
-You can [read more about permissions][permissions] in general.
-
-NOTE: **Note:**
-As of GitLab 12.2, the project-level page is deprecated. You should access
-project-level Cycle Analytics from **Analytics > Cycle Analytics** in the top
-navigation bar. We will ensure that the same project-level functionality is available
-to CE users in the new analytics space.
-
-For Cycle Analytics functionality introduced in GitLab 12.2 and later:
-
-- Users must have Reporter access or above.
-- Features are available only on
- [Premium or Silver tiers](https://about.gitlab.com/pricing/) and above.
-
-## More resources
-
-Learn more about Cycle Analytics in the following resources:
-
-- [Cycle Analytics feature page](https://about.gitlab.com/features/cycle-analytics/)
-- [Cycle Analytics feature preview](https://about.gitlab.com/2016/09/16/feature-preview-introducing-cycle-analytics/)
-- [Cycle Analytics feature highlight](https://about.gitlab.com/2016/09/21/cycle-analytics-feature-highlight/)
-
-[board]: issue_board.md#creating-a-new-list
-[ce-5986]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5986
-[ce-20975]: https://gitlab.com/gitlab-org/gitlab-ce/issues/20975
-[environment]: ../../ci/yaml/README.md#environment
-[GitLab flow]: ../../workflow/gitlab_flow.md
-[idea to production]: https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab
-[issue closing pattern]: issues/managing_issues.md#closing-issues-automatically
-[permissions]: ../permissions.md
-[yml]: ../../ci/yaml/README.md
+This document was moved to [another location](../analytics/cycle_analytics.md)
diff --git a/doc/user/project/img/cycle_analytics_landing_page.png b/doc/user/project/img/cycle_analytics_landing_page.png
deleted file mode 100644
index c0c07e84a82..00000000000
--- a/doc/user/project/img/cycle_analytics_landing_page.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_service_page.png b/doc/user/project/integrations/img/jira_service_page.png
deleted file mode 100644
index 76fd5f4641c..00000000000
--- a/doc/user/project/integrations/img/jira_service_page.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_service_page_v12_2.png b/doc/user/project/integrations/img/jira_service_page_v12_2.png
new file mode 100644
index 00000000000..ba7dad9b438
--- /dev/null
+++ b/doc/user/project/integrations/img/jira_service_page_v12_2.png
Binary files differ
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index ca990ee6c32..61f6f6c9412 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -93,7 +93,7 @@ even if the status you are changing to is the same.
After saving the configuration, your GitLab project will be able to interact
with all Jira projects in your Jira instance and you'll see the Jira link on the GitLab project pages that takes you to the appropriate Jira project.
-![Jira service page](img/jira_service_page.png)
+![Jira service page](img/jira_service_page_v12_2.png)
## Jira issues
diff --git a/doc/user/project/issues/design_management.md b/doc/user/project/issues/design_management.md
index bffbcb544e3..1324a90e00b 100644
--- a/doc/user/project/issues/design_management.md
+++ b/doc/user/project/issues/design_management.md
@@ -35,9 +35,19 @@ to be enabled:
## Limitations
-- Files uploaded must have a file extension of either `png`, `jpg`, `jpeg`, `gif`, `bmp`, `tiff` or `ico`. The [`svg` extension is not yet supported](https://gitlab.com/gitlab-org/gitlab-ee/issues/12771).
+- Files uploaded must have a file extension of either `png`, `jpg`, `jpeg`, `gif`, `bmp`, `tiff` or `ico`.
+ The [`svg` extension is not yet supported](https://gitlab.com/gitlab-org/gitlab-ee/issues/12771).
+- Design uploads are limited to 10 files at a time.
- [Designs cannot yet be deleted](https://gitlab.com/gitlab-org/gitlab-ee/issues/11089).
-- Design Management is [not yet supported in the project export](https://gitlab.com/gitlab-org/gitlab-ee/issues/11090).
+- Design Management is
+ [not yet supported in the project export](https://gitlab.com/gitlab-org/gitlab-ee/issues/11090).
+- Design Management data
+ [isn't deleted when a project is destroyed](https://gitlab.com/gitlab-org/gitlab-ee/issues/13429) yet.
+- Design Management data [won't be moved](https://gitlab.com/gitlab-org/gitlab-ee/issues/13426)
+ when an issue is moved, nor [deleted](https://gitlab.com/gitlab-org/gitlab-ee/issues/13427)
+ when an issue is deleted.
+- Design Management
+ [isn't supported by Geo](https://gitlab.com/groups/gitlab-org/-/epics/1633) yet.
## The Design Management page
diff --git a/doc/user/project/merge_requests/img/cross-project-dependencies-edit-inaccessible.png b/doc/user/project/merge_requests/img/cross_project_dependencies_edit_inaccessible_v12_2.png
index 2dc02634fd8..2dc02634fd8 100644
--- a/doc/user/project/merge_requests/img/cross-project-dependencies-edit-inaccessible.png
+++ b/doc/user/project/merge_requests/img/cross_project_dependencies_edit_inaccessible_v12_2.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/cross-project-dependencies-edit.png b/doc/user/project/merge_requests/img/cross_project_dependencies_edit_v12_2.png
index 362e7e0ead2..362e7e0ead2 100644
--- a/doc/user/project/merge_requests/img/cross-project-dependencies-edit.png
+++ b/doc/user/project/merge_requests/img/cross_project_dependencies_edit_v12_2.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/cross-project-dependencies-view.png b/doc/user/project/merge_requests/img/cross_project_dependencies_view_v12_2.png
index e00231c839b..e00231c839b 100644
--- a/doc/user/project/merge_requests/img/cross-project-dependencies-view.png
+++ b/doc/user/project/merge_requests/img/cross_project_dependencies_view_v12_2.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/incrementally_expand_merge_request_diffs_v12_2.png b/doc/user/project/merge_requests/img/incrementally_expand_merge_request_diffs_v12_2.png
new file mode 100644
index 00000000000..ee94dbdea5c
--- /dev/null
+++ b/doc/user/project/merge_requests/img/incrementally_expand_merge_request_diffs_v12_2.png
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 8a82b163481..2794cbc0f39 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -497,6 +497,15 @@ list.
![Merge request diff file navigation](img/merge_request_diff_file_navigation.png)
+### Incrementally expand merge request diffs
+
+By default, the diff shows only the parts of a file which are changed.
+To view more unchanged lines above or below a change click on the
+**Expand up** or **Expand down** icons. You can also click on **Show all lines**
+to expand the entire file.
+
+![Incrementally expand merge request diffs](img/incrementally_expand_merge_request_diffs_v12_2.png)
+
## Ignore whitespace changes in Merge Request diff view
If you click the **Hide whitespace changes** button, you can see the diff
diff --git a/doc/user/project/merge_requests/merge_request_dependencies.md b/doc/user/project/merge_requests/merge_request_dependencies.md
index e046b3466c4..b30e24b2386 100644
--- a/doc/user/project/merge_requests/merge_request_dependencies.md
+++ b/doc/user/project/merge_requests/merge_request_dependencies.md
@@ -2,9 +2,9 @@
type: reference, concepts
---
-# Cross-project merge request dependencies **(PREMIUM)**
+# Cross-project Merge Request dependencies **(PREMIUM)**
-> Introduced in GitLab Premium 12.2
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9688) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.2.
Cross-project merge request dependencies allows a required order of merging
between merge requests in different projects to be expressed. If a
@@ -24,11 +24,11 @@ merge requests in the same project cannot depend on each other.
## Use cases
- Ensure changes to a library are merged before changes to a project that
- imports the library
+ imports the library.
- Prevent a documentation-only merge request from being merged before the merge request
- implementing the feature to be documented
+ implementing the feature to be documented.
- Require an merge request updating a permissions matrix to be merged before merging an
- merge request from someone who hasn't yet been granted permissions
+ merge request from someone who hasn't yet been granted permissions.
It is common for a single logical change to span several merge requests, spread
out across multiple projects, and the order in which they are merged can be
@@ -60,33 +60,33 @@ new merge request in `awesome-project` (or by editing it, if it already exists).
The dependency needs to be configured on the **dependent** merge
request. There is a "Cross-project dependencies" section in the form:
-![Cross-project dependencies form control](img/cross-project-dependencies-edit.png)
+![Cross-project dependencies form control](img/cross_project_dependencies_edit_v12_2.png)
Anyone who can edit a merge request can change the list of dependencies.
New dependencies can be added by reference, or by URL. To remove a dependency,
-press the "X" by its reference.
+press the **X** by its reference.
As dependencies are specified across projects, it's possible that someone else
has added a dependency for a merge request in a project you don't have access to.
These are shown as a simple count:
-![Cross-project dependencies form control with inaccessible merge requests](img/cross-project-dependencies-edit-inaccessible.png)
+![Cross-project dependencies form control with inaccessible merge requests](img/cross_project_dependencies_edit_inaccessible_v12_2.png)
-If necessary, you can remove all the dependencies like this by pressing the "X",
-just as you would for a single, visible dependency.
+If necessary, you can remove all the dependencies like this by pressing the
+**X**, just as you would for a single, visible dependency.
-Once you're finished, press the "Save changes" button to submit the request, or
-"Cancel" to return without making any changes.
+Once you're finished, press the **Save changes** button to submit the request,
+or **Cancel** to return without making any changes.
The list of configured dependencies, and the status of each one, is shown in the
merge request widget:
-![Cross-project dependencies in merge request widget](img/cross-project-dependencies-view.png)
+![Cross-project dependencies in merge request widget](img/cross_project_dependencies_view_v12_2.png)
-Until all dependencies have, themselves, been merged, the "Merge"
+Until all dependencies have, themselves, been merged, the **Merge**
button will be disabled for the dependent merge request. In
-particular, note that **closed** merge request still prevent their
+particular, note that **closed merge requests** still prevent their
dependents from being merged - it is impossible to automatically
determine whether the dependency expressed by a closed merge request
has been satisfied in some other way or not.
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index 03ae24242e3..8606d92f20c 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -28,13 +28,13 @@ The reasons to do it like that are:
With the new behavior, any job that is triggered by the user, is also marked
with their read permissions. When a user does a `git push` or changes files through
the web UI, a new pipeline will be usually created. This pipeline will be marked
-as created be the pusher (local push or via the UI) and any job created in this
+as created by the pusher (local push or via the UI) and any job created in this
pipeline will have the read permissions of the pusher but not write permissions.
This allows us to make it really easy to evaluate the access for all projects
that have [Git submodules][gitsub] or are using container images that the pusher
-would have access too. **The permission is granted only for time that job is
-running. The access is revoked after the job is finished.**
+would have access too. **The permission is granted only for the time that the job
+is running. The access is revoked after the job is finished.**
## Types of users
@@ -74,7 +74,7 @@ We try to make sure that this token doesn't leak by:
1. Securing all API endpoints to not expose the job token.
1. Masking the job token from job logs.
-1. Allowing to use the job token **only** when job is running.
+1. Granting permissions to the job token **only** when the job is running.
However, this brings a question about the Runners security. To make sure that
this token doesn't leak, you should also make sure that you configure
@@ -86,12 +86,6 @@ your Runners in the most possible secure way, by avoiding the following:
By using an insecure GitLab Runner configuration, you allow the rogue developers
to steal the tokens of other jobs.
-## Pipeline triggers
-
-Since 9.0 [pipeline triggers][triggers] do support the new permission model.
-The new triggers do impersonate their associated user including their access
-to projects and their project permissions.
-
## Before GitLab 8.12
In versions before GitLab 8.12, all CI jobs would use the CI Runner's token
@@ -203,7 +197,7 @@ Container Registries for private projects.
> **Notes:**
>
> - GitLab Runner versions prior to 1.8 don't incorporate the introduced changes
-> for permissions. This makes the `image:` directive to not work with private
+> for permissions. This makes the `image:` directive not work with private
> projects automatically and it needs to be configured manually on Runner's host
> with a predefined account (for example administrator's personal account with
> access token created explicitly for this purpose). This issue is resolved with
@@ -227,11 +221,22 @@ test:
- docker run $CI_REGISTRY/group/other-project:latest
```
+### Pipeline triggers
+
+Since 9.0 [pipeline triggers][triggers] do support the new permission model.
+The new triggers do impersonate their associated user including their access
+to projects and their project permissions.
+
+### API
+
+GitLab API cannot be used via `CI_JOB_TOKEN` but there is a [proposal](https://gitlab.com/gitlab-org/gitlab-ce/issues/29566)
+to support it.
+
[job permissions]: ../permissions.md#job-permissions
[comment]: https://gitlab.com/gitlab-org/gitlab-ce/issues/22484#note_16648302
[gitsub]: ../../ci/git_submodules.md
[https]: ../admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols
-[triggers]: ../../ci/triggers/README.md
+[triggers]: ../../ci/triggers/README.md#ci-job-token
[update-docs]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update
[workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse
[jobenv]: ../../ci/variables/README.md#predefined-environment-variables
diff --git a/doc/user/project/operations/feature_flags.md b/doc/user/project/operations/feature_flags.md
index 19ccde6e16a..75b0623e6b0 100644
--- a/doc/user/project/operations/feature_flags.md
+++ b/doc/user/project/operations/feature_flags.md
@@ -112,6 +112,19 @@ If this strategy is selected, then the Unleash client **must** be given a user i
**Percent rollout (logged in users)** is implemented using the Unleash [gradualRolloutUserId](https://unleash.github.io/docs/activation_strategy#gradualrolloutuserid) activation strategy.
+## Target Users
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/8240) in GitLab 12.2.
+
+A feature flag may be enabled for a list of target users.
+
+![Feature flag target users](img/target_users_v12_2.png)
+
+CAUTION: **Caution:**
+The Unleash client **must** be given a user id for the feature to be enabled for target users. See the [Ruby example](#ruby-application-example) below.
+
+**Target users** is implemented using the Unleash [userWithId](https://unleash.github.io/docs/activation_strategy#userwithid) activation strategy.
+
## Integrating with your application
In order to use Feature Flags, you need to first
@@ -207,7 +220,7 @@ func main() {
Here's an example of how to integrate the feature flags in a Ruby application.
-The Unleash client is given a user id for use with a **Percent rollout (logged in users)** rollout strategy.
+The Unleash client is given a user id for use with a **Percent rollout (logged in users)** rollout strategy or a list of **Target Users**.
```ruby
#!/usr/bin/env ruby
diff --git a/doc/user/project/operations/img/target_users_v12_2.png b/doc/user/project/operations/img/target_users_v12_2.png
new file mode 100644
index 00000000000..c88d2b7be97
--- /dev/null
+++ b/doc/user/project/operations/img/target_users_v12_2.png
Binary files differ
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 6758adf2b43..647250bd02a 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -40,18 +40,20 @@ discussions, and descriptions:
| `/label ~label1 ~label2` | Add label(s). Label names can also start without ~ but mixed syntax is not supported. | ✓ | ✓ |
| `/unlabel ~label1 ~label2` | Remove all or specific label(s)| ✓ | ✓ |
| `/relabel ~label1 ~label2` | Replace existing label(s) with those specified | ✓ | ✓ |
-| `/copy_metadata <#issue | !merge_request>` | Copy labels and milestone from other issue or merge request in the project | ✓ | ✓ |
+| `/copy_metadata <#issue>` | Copy labels and milestone from another issue in the project | ✓ | ✓ |
+| `/copy_metadata <!merge_request>` | Copy labels and milestone from another merge request in the project | ✓ | ✓ |
| `/estimate <1w 3d 2h 14m>` | Set time estimate | ✓ | ✓ |
| `/remove_estimate` | Remove time estimate | ✓ | ✓ |
-| `/spend <time(1h 30m | -1h 5m)> <date(YYYY-MM-DD)>` | Add or subtract spent time; optionally, specify the date that time was spent on | ✓ | ✓ |
+| `/spend <time(1h 30m)> <date(YYYY-MM-DD)>` | Add spent time; optionally, specify the date that time was spent on | ✓ | ✓ |
+| `/spend <time(-1h 5m)> <date(YYYY-MM-DD)>` | Subtract spent time; optionally, specify the date that time was spent on | ✓ | ✓ |
| `/remove_time_spent` | Remove time spent | ✓ | ✓ |
| `/lock` | Lock the thread | ✓ | ✓ |
| `/unlock` | Unlock the thread | ✓ | ✓ |
-| `/due <in 2 days | this Friday | December 31st>`| Set due date | ✓ | |
+| `/due <date>` | Set due date. Examples of valid `<date>` include `in 2 days`, `this Friday` and `December 31st`. | ✓ | |
| `/remove_due_date` | Remove due date | ✓ | |
-| `/weight <0 | 1 | 2 | ...>` | Set weight **(STARTER)** | ✓ | |
+| `/weight <value>` | Set weight. Valid options for `<value>` include `0`, `1`, `2`, etc. **(STARTER)** | ✓ | |
| `/clear_weight` | Clears weight **(STARTER)** | ✓ | |
-| `/epic <&epic | group&epic | Epic URL>` | Add to epic **(ULTIMATE)** | ✓ | |
+| `/epic <epic>` | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic` or `epic-URL`. **(ULTIMATE)** | ✓ | |
| `/remove_epic` | Removes from epic **(ULTIMATE)** | ✓ | |
| `/promote` | Promote issue to epic **(ULTIMATE)** | ✓ | |
| `/confidential` | Make confidential | ✓ | |
@@ -110,9 +112,9 @@ The following quick actions are applicable for epics threads and description:
| `/label ~label1 ~label2` | Add label(s) |
| `/unlabel ~label1 ~label2` | Remove all or specific label(s) |
| `/relabel ~label1 ~label2` | Replace existing label(s) with those specified |
-| `/child_epic <&epic | group&epic | Epic URL>` | Adds child epic to epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-ee/issues/7330)) |
-| `/remove_child_epic <&epic | group&epic | Epic URL>` | Removes child epic from epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-ee/issues/7330)) |
-| `/parent_epic <&epic | group&epic | Epic URL>` | Sets parent epic to epic ([introduced in GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ee/issues/10556)) |
+| `/child_epic <epic>` | Adds child epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic` or `epic-URL`. ([Introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-ee/issues/7330)) **(ULTIMATE)**|
+| `/remove_child_epic <epic>` | Removes child epic from `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic` or `epic-URL`. ([Introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-ee/issues/7330)) **(ULTIMATE)** |
+| `/parent_epic <epic>` | Sets parent epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic` or `epic-URL`. ([introduced in GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ee/issues/10556)) **(ULTIMATE)** |
| `/remove_parent_epic` | Removes parent epic from epic ([introduced in GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ee/issues/10556)) |
<!-- ## Troubleshooting
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index 3160b0c4deb..7e880b3009d 100644
--- a/doc/workflow/lfs/lfs_administration.md
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -46,8 +46,7 @@ In `config/gitlab.yml`:
## Storing LFS objects in remote object storage
-> [Introduced][ee-2760] in [GitLab Premium][eep] 10.0. Brought to GitLab Core
-in 10.7.
+> [Introduced][ee-2760] in [GitLab Premium][eep] 10.0. Brought to GitLab Core in 10.7.
It is possible to store LFS objects in remote object storage which allows you
to offload local hard disk R/W operations, and free up disk space significantly.
@@ -91,7 +90,7 @@ Here is a configuration example with S3.
| `aws_access_key_id` | AWS credentials, or compatible | `ABC123DEF456` |
| `aws_secret_access_key` | AWS credentials, or compatible | `ABC123DEF456ABC123DEF456ABC123DEF456` |
| `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 |
-| `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with AWS v4 signatures (https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true
+| `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with [AWS v4 signatures](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true |
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
@@ -107,7 +106,9 @@ Here is a configuration example with GCS.
| `google_client_email` | The email address of the service account | `foo@gcp-project-12345.iam.gserviceaccount.com` |
| `google_json_key_location` | The json key path | `/path/to/gcp-project-12345-abcde.json` |
-_NOTE: The service account must have permission to access the bucket. [See more](https://cloud.google.com/storage/docs/authentication)_
+NOTE: **Note:**
+The service account must have permission to access the bucket.
+[See more](https://cloud.google.com/storage/docs/authentication)
Here is a configuration example with Rackspace Cloud Files.
@@ -119,7 +120,13 @@ Here is a configuration example with Rackspace Cloud Files.
| `rackspace_region` | The Rackspace storage region to use, a three letter code from the [list of service access endpoints](https://developer.rackspace.com/docs/cloud-files/v1/general-api-info/service-access/) | `iad` |
| `rackspace_temp_url_key` | The private key you have set in the Rackspace API for temporary URLs. Read more [here](https://developer.rackspace.com/docs/cloud-files/v1/use-cases/public-access-to-your-cloud-files-account/#tempurl) | `ABC123DEF456ABC123DEF456ABC123DE` |
-_NOTES: Regardless of whether the container has public access enabled or disabled, Fog will use the TempURL method to grant access to LFS objects. If you see errors in logs referencing instantiating storage with a temp-url-key, ensure that you have set they key properly on the Rackspace API and in gitlab.rb. You can verify the value of the key Rackspace has set by sending a GET request with token header to the service access endpoint URL and comparing the output of the returned headers._
+NOTE: **Note:**
+Regardless of whether the container has public access enabled or disabled, Fog will
+use the TempURL method to grant access to LFS objects. If you see errors in logs referencing
+instantiating storage with a temp-url-key, ensure that you have set they key properly
+on the Rackspace API and in gitlab.rb. You can verify the value of the key Rackspace
+has set by sending a GET request with token header to the service access endpoint URL
+and comparing the output of the returned headers.
### Manual uploading to an object storage
@@ -167,13 +174,13 @@ On Omnibus installations, the settings are prefixed by `lfs_object_store_`:
1. Save the file and [reconfigure GitLab]s for the changes to take effect.
1. Migrate any existing local LFS objects to the object storage:
- ```bash
- gitlab-rake gitlab:lfs:migrate
- ```
+ ```bash
+ gitlab-rake gitlab:lfs:migrate
+ ```
- This will migrate existing LFS objects to object storage. New LFS objects
- will be forwarded to object storage unless
- `gitlab_rails['lfs_object_store_background_upload']` is set to false.
+ This will migrate existing LFS objects to object storage. New LFS objects
+ will be forwarded to object storage unless
+ `gitlab_rails['lfs_object_store_background_upload']` is set to false.
### S3 for installations from source
@@ -203,13 +210,13 @@ For source installations the settings are nested under `lfs:` and then
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Migrate any existing local LFS objects to the object storage:
- ```bash
- sudo -u git -H bundle exec rake gitlab:lfs:migrate RAILS_ENV=production
- ```
+ ```bash
+ sudo -u git -H bundle exec rake gitlab:lfs:migrate RAILS_ENV=production
+ ```
- This will migrate existing LFS objects to object storage. New LFS objects
- will be forwarded to object storage unless `background_upload` is set to
- false.
+ This will migrate existing LFS objects to object storage. New LFS objects
+ will be forwarded to object storage unless `background_upload` is set to
+ false.
## Storage statistics
diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
index 264372a512d..f5593927cc2 100644
--- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
+++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
@@ -35,9 +35,10 @@ Documentation for GitLab instance administrators is under [LFS administration do
- Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have
to add the URL to Git config manually (see [troubleshooting](#troubleshooting))
->**Note**: With 8.12 GitLab added LFS support to SSH. The Git LFS communication
- still goes over HTTP, but now the SSH client passes the correct credentials
- to the Git LFS client, so no action is required by the user.
+NOTE: **Note:**
+With 8.12 GitLab added LFS support to SSH. The Git LFS communication
+still goes over HTTP, but now the SSH client passes the correct credentials
+to the Git LFS client, so no action is required by the user.
## Using Git LFS
@@ -61,12 +62,13 @@ git commit -am "Added Debian iso" # commit the file meta data
git push origin master # sync the git repo and large file to the GitLab server
```
-> **Note**: Make sure that `.gitattributes` is tracked by git. Otherwise Git
-> LFS will not be working properly for people cloning the project.
->
-> ```bash
-> git add .gitattributes
-> ```
+NOTE: **Note:**
+**Make sure** that `.gitattributes` is tracked by Git. Otherwise Git
+LFS will not be working properly for people cloning the project.
+
+```bash
+git add .gitattributes
+```
Cloning the repository works the same as before. Git automatically detects the
LFS-tracked files and clones them via HTTP. If you performed the git clone
@@ -216,9 +218,10 @@ git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs"
### Credentials are always required when pushing an object
->**Note**: With 8.12 GitLab added LFS support to SSH. The Git LFS communication
- still goes over HTTP, but now the SSH client passes the correct credentials
- to the Git LFS client, so no action is required by the user.
+NOTE: **Note:**
+With 8.12 GitLab added LFS support to SSH. The Git LFS communication
+still goes over HTTP, but now the SSH client passes the correct credentials
+to the Git LFS client, so no action is required by the user.
Given that Git LFS uses HTTP Basic Authentication to authenticate the user pushing
the LFS object on every push for every object, user HTTPS credentials are required.
diff --git a/doc/workflow/lfs/migrate_from_git_annex_to_git_lfs.md b/doc/workflow/lfs/migrate_from_git_annex_to_git_lfs.md
index 75673d23799..5ceeb3253f1 100644
--- a/doc/workflow/lfs/migrate_from_git_annex_to_git_lfs.md
+++ b/doc/workflow/lfs/migrate_from_git_annex_to_git_lfs.md
@@ -76,7 +76,7 @@ Here you'll find a guide on
Since Annex files are stored as objects with symlinks and cannot be directly
modified, we need to first remove those symlinks.
->**Note:**
+NOTE: **Note:**
Make sure the you read about the [`direct` mode][annex-direct] as it contains
useful information that may fit in your use case. Note that `annex direct` is
deprecated in Git Annex version 6, so you may need to upgrade your repository
diff --git a/doc/workflow/repository_mirroring.md b/doc/workflow/repository_mirroring.md
index 0b8e7d851b0..753518d0424 100644
--- a/doc/workflow/repository_mirroring.md
+++ b/doc/workflow/repository_mirroring.md
@@ -37,8 +37,7 @@ The following are some possible use cases for repository mirroring:
## Pushing to a remote repository **(CORE)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/249) in GitLab Enterprise
-> Edition 8.7. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/249) in GitLab Enterprise Edition 8.7. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8.
For an existing project, you can set up push mirroring as follows:
@@ -67,8 +66,7 @@ section.
### Push only protected branches **(CORE)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3350) in
-> [GitLab Starter](https://about.gitlab.com/pricing/) 10.3. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3350) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.3. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8.
You can choose to only push your protected branches from GitLab to your remote repository.
@@ -98,8 +96,7 @@ The repository will push soon. To force a push, click the appropriate button.
## Pulling from a remote repository **(STARTER)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51) in GitLab Enterprise Edition 8.2.
-> [Added Git LFS support](https://gitlab.com/gitlab-org/gitlab-ee/issues/10871) in [GitLab Starter](https://about.gitlab.com/pricing/) 11.11.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51) in GitLab Enterprise Edition 8.2. [Added Git LFS support](https://gitlab.com/gitlab-org/gitlab-ee/issues/10871) in [GitLab Starter](https://about.gitlab.com/pricing/) 11.11.
NOTE: **Note:** This feature [is available for free](https://gitlab.com/gitlab-org/gitlab-ee/issues/10361) to
GitLab.com users until September 22nd, 2019.
@@ -157,8 +154,7 @@ Repository mirrors are updated as Sidekiq becomes available to process them. If
### SSH authentication
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2551) for Pull mirroring in [GitLab Starter](https://about.gitlab.com/pricing/) 9.5.
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22982) for Push mirroring in [GitLab Core](https://about.gitlab.com/pricing/) 11.6
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2551) for Pull mirroring in [GitLab Starter](https://about.gitlab.com/pricing/) 9.5. [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22982) for Push mirroring in [GitLab Core](https://about.gitlab.com/pricing/) 11.6
SSH authentication is mutual:
@@ -245,8 +241,7 @@ key to keep the mirror running.
### Overwrite diverged branches **(STARTER)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4559) in
-> [GitLab Starter](https://about.gitlab.com/pricing/) 10.6.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4559) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.6.
You can choose to always update your local branches with remote versions, even if they have
diverged from the remote.
@@ -258,8 +253,7 @@ To use this option, check the **Overwrite diverged branches** box when creating
### Only mirror protected branches **(STARTER)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3326) in
-> [GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3326) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
You can choose to pull mirror only the protected branches from your remote repository to GitLab.
Non-protected branches are not mirrored and can diverge.
@@ -268,8 +262,7 @@ To use this option, check the **Only mirror protected branches** box when creati
### Hard failure **(STARTER)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3117) in
-> [GitLab Starter](https://about.gitlab.com/pricing/) 10.2.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3117) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.2.
Once the mirroring process is unsuccessfully retried 14 times in a row, it will get marked as hard
failed. This will become visible in either the:
@@ -282,8 +275,7 @@ project mirroring again by [Forcing an update](#forcing-an-update-core).
### Trigger update using API **(STARTER)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3453) in
-[GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3453) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
Pull mirroring uses polling to detect new branches and commits added upstream, often minutes
afterwards. If you notify GitLab by [API](../api/projects.md#start-the-pull-mirroring-process-for-a-project-starter),
@@ -325,9 +317,10 @@ protected branches.
### Preventing conflicts using a `pre-receive` hook
-> **Warning:** The solution proposed will negatively impact the performance of
-> Git push operations because they will be proxied to the upstream Git
-> repository.
+CAUTION: **Warning:**
+The solution proposed will negatively impact the performance of
+Git push operations because they will be proxied to the upstream Git
+repository.
A server-side `pre-receive` hook can be used to prevent the race condition
described above by only accepting the push after first pushing the commit to
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index 0432c406f31..211d242e2d3 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -35,8 +35,8 @@ A To Do displays on your To-Do List when:
- You are `@mentioned` in a comment on a commit
- A job in the CI pipeline running for your merge request failed, but this
job is not allowed to fail
-- An open merge request becomes unmergeable due to conflict, and you are either:
- - The author
+- An open merge request becomes unmergeable due to conflict, and you are either:
+ - The author
- Have set it to automatically merge once the pipeline succeeds
To-do triggers are not affected by [GitLab Notification Email settings](notifications.md).
diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
index 4190de73e1f..90278122361 100644
--- a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
@@ -46,11 +46,14 @@ sast:
SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
SAST_PULL_ANALYZER_IMAGE_TIMEOUT \
SAST_RUN_ANALYZER_TIMEOUT \
+ SAST_JAVA_VERSION \
ANT_HOME \
ANT_PATH \
GRADLE_PATH \
JAVA_OPTS \
JAVA_PATH \
+ JAVA_8_VERSION \
+ JAVA_11_VERSION \
MAVEN_CLI_OPTS \
MAVEN_PATH \
MAVEN_REPO_PATH \
diff --git a/lib/gitlab/data_builder/push.rb b/lib/gitlab/data_builder/push.rb
index 37fadb47736..75d9a2d55b9 100644
--- a/lib/gitlab/data_builder/push.rb
+++ b/lib/gitlab/data_builder/push.rb
@@ -129,8 +129,6 @@ module Gitlab
SAMPLE_DATA
end
- private
-
def checkout_sha(repository, newrev, ref)
# Checkout sha is nil when we remove branch or tag
return if Gitlab::Git.blank_ref?(newrev)
diff --git a/lib/gitlab/repository_cache_adapter.rb b/lib/gitlab/repository_cache_adapter.rb
index e40c366ed02..75503ee1789 100644
--- a/lib/gitlab/repository_cache_adapter.rb
+++ b/lib/gitlab/repository_cache_adapter.rb
@@ -23,6 +23,37 @@ module Gitlab
end
end
+ # Caches and strongly memoizes the method as a Redis Set.
+ #
+ # This only works for methods that do not take any arguments. The method
+ # should return an Array of Strings to be cached.
+ #
+ # In addition to overriding the named method, a "name_include?" method is
+ # defined. This uses the "SISMEMBER" query to efficiently check membership
+ # without needing to load the entire set into memory.
+ #
+ # name - The name of the method to be cached.
+ # fallback - A value to fall back to if the repository does not exist, or
+ # in case of a Git error. Defaults to nil.
+ def cache_method_as_redis_set(name, fallback: nil)
+ uncached_name = alias_uncached_method(name)
+
+ define_method(name) do
+ cache_method_output_as_redis_set(name, fallback: fallback) do
+ __send__(uncached_name) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+
+ define_method("#{name}_include?") do |value|
+ # If the cache isn't populated, we can't rely on it
+ return redis_set_cache.include?(name, value) if redis_set_cache.exist?(name)
+
+ # Since we have to pull all branch names to populate the cache, use
+ # the data we already have to answer the query just this once
+ __send__(name).include?(value) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+
# Caches truthy values from the method. All values are strongly memoized,
# and cached in RequestStore.
#
@@ -84,6 +115,11 @@ module Gitlab
raise NotImplementedError
end
+ # RepositorySetCache to be used. Should be overridden by the including class
+ def redis_set_cache
+ raise NotImplementedError
+ end
+
# List of cached methods. Should be overridden by the including class
def cached_methods
raise NotImplementedError
@@ -100,6 +136,18 @@ module Gitlab
end
end
+ # Caches and strongly memoizes the supplied block as a Redis Set. The result
+ # will be provided as a sorted array.
+ #
+ # name - The name of the method to be cached.
+ # fallback - A value to fall back to if the repository does not exist, or
+ # in case of a Git error. Defaults to nil.
+ def cache_method_output_as_redis_set(name, fallback: nil, &block)
+ memoize_method_output(name, fallback: fallback) do
+ redis_set_cache.fetch(name, &block).sort
+ end
+ end
+
# Caches truthy values from the supplied block. All values are strongly
# memoized, and cached in RequestStore.
#
@@ -154,6 +202,7 @@ module Gitlab
clear_memoization(memoizable_name(name))
end
+ expire_redis_set_method_caches(methods)
expire_request_store_method_caches(methods)
end
@@ -169,6 +218,10 @@ module Gitlab
end
end
+ def expire_redis_set_method_caches(methods)
+ methods.each { |name| redis_set_cache.expire(name) }
+ end
+
# All cached repository methods depend on the existence of a Git repository,
# so if the repository doesn't exist, we already know not to call it.
def fallback_early?(method_name)
diff --git a/lib/gitlab/repository_set_cache.rb b/lib/gitlab/repository_set_cache.rb
new file mode 100644
index 00000000000..fb634328a95
--- /dev/null
+++ b/lib/gitlab/repository_set_cache.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+# Interface to the Redis-backed cache store for keys that use a Redis set
+module Gitlab
+ class RepositorySetCache
+ attr_reader :repository, :namespace, :expires_in
+
+ def initialize(repository, extra_namespace: nil, expires_in: 2.weeks)
+ @repository = repository
+ @namespace = "#{repository.full_path}:#{repository.project.id}"
+ @namespace = "#{@namespace}:#{extra_namespace}" if extra_namespace
+ @expires_in = expires_in
+ end
+
+ def cache_key(type)
+ [type, namespace, 'set'].join(':')
+ end
+
+ def expire(key)
+ with { |redis| redis.del(cache_key(key)) }
+ end
+
+ def exist?(key)
+ with { |redis| redis.exists(cache_key(key)) }
+ end
+
+ def read(key)
+ with { |redis| redis.smembers(cache_key(key)) }
+ end
+
+ def write(key, value)
+ full_key = cache_key(key)
+
+ with do |redis|
+ redis.multi do
+ redis.del(full_key)
+
+ # Splitting into groups of 1000 prevents us from creating a too-long
+ # Redis command
+ value.in_groups_of(1000, false) { |subset| redis.sadd(full_key, subset) }
+
+ redis.expire(full_key, expires_in)
+ end
+ end
+
+ value
+ end
+
+ def fetch(key, &block)
+ if exist?(key)
+ read(key)
+ else
+ write(key, yield)
+ end
+ end
+
+ def include?(key, value)
+ with { |redis| redis.sismember(cache_key(key), value) }
+ end
+
+ private
+
+ def with(&blk)
+ Gitlab::Redis::Cache.with(&blk) # rubocop:disable CodeReuse/ActiveRecord
+ end
+ end
+end
diff --git a/lib/gitlab/sentry.rb b/lib/gitlab/sentry.rb
index 764db14d720..005cb3112b8 100644
--- a/lib/gitlab/sentry.rb
+++ b/lib/gitlab/sentry.rb
@@ -39,9 +39,14 @@ module Gitlab
# development and test. If you need development and test to behave
# just the same as production you can use this instead of
# track_exception.
+ #
+ # If the exception implements the method `sentry_extra_data` and that method
+ # returns a Hash, then the return value of that method will be merged into
+ # `extra`. Exceptions can use this mechanism to provide structured data
+ # to sentry in addition to their message and back-trace.
def self.track_acceptable_exception(exception, issue_url: nil, extra: {})
if enabled?
- extra[:issue_url] = issue_url if issue_url
+ extra = build_extra_data(exception, issue_url, extra)
context # Make sure we've set everything we know in the context
Raven.capture_exception(exception, tags: default_tags, extra: extra)
@@ -58,5 +63,15 @@ module Gitlab
locale: I18n.locale
}
end
+
+ def self.build_extra_data(exception, issue_url, extra)
+ exception.try(:sentry_extra_data)&.tap do |data|
+ extra.merge!(data) if data.is_a?(Hash)
+ end
+
+ extra.merge({ issue_url: issue_url }.compact)
+ end
+
+ private_class_method :build_extra_data
end
end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 1542905d2ce..353298e67b3 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -188,8 +188,8 @@ module Gitlab
{} # augmented in EE
end
- def count(relation, fallback: -1)
- relation.count
+ def count(relation, count_by: nil, fallback: -1)
+ count_by ? relation.count(count_by) : relation.count
rescue ActiveRecord::StatementInvalid
fallback
end
diff --git a/lib/gt_one_coercion.rb b/lib/gt_one_coercion.rb
deleted file mode 100644
index 99be51bc8c6..00000000000
--- a/lib/gt_one_coercion.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-class GtOneCoercion < Virtus::Attribute
- def coerce(value)
- [1, value.to_i].max
- end
-end
diff --git a/lib/prometheus/cleanup_multiproc_dir_service.rb b/lib/prometheus/cleanup_multiproc_dir_service.rb
new file mode 100644
index 00000000000..6418b4de166
--- /dev/null
+++ b/lib/prometheus/cleanup_multiproc_dir_service.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Prometheus
+ class CleanupMultiprocDirService
+ include Gitlab::Utils::StrongMemoize
+
+ def execute
+ FileUtils.rm_rf(old_metrics) if old_metrics
+ end
+
+ private
+
+ def old_metrics
+ strong_memoize(:old_metrics) do
+ Dir[File.join(multiprocess_files_dir, '*.db')] if multiprocess_files_dir
+ end
+ end
+
+ def multiprocess_files_dir
+ ::Prometheus::Client.configuration.multiprocess_files_dir
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e8d27360395..26e6cb524bd 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -469,6 +469,9 @@ msgstr ""
msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
msgstr ""
+msgid "A Let's Encrypt SSL certificate can not be obtained until your domain is verified."
+msgstr ""
+
msgid "A Let's Encrypt account will be configured for this GitLab installation using your email address. You will receive emails to warn of expiring certificates."
msgstr ""
@@ -4144,6 +4147,9 @@ msgstr ""
msgid "Edit public deploy key"
msgstr ""
+msgid "Edit stage"
+msgstr ""
+
msgid "Edit wiki page"
msgstr ""
@@ -5244,6 +5250,9 @@ msgstr ""
msgid "GitLab User"
msgstr ""
+msgid "GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later."
+msgstr ""
+
msgid "GitLab member or Email address"
msgstr ""
@@ -5658,6 +5667,9 @@ msgstr ""
msgid "Hide shared projects"
msgstr ""
+msgid "Hide stage"
+msgstr ""
+
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@@ -8008,6 +8020,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more on the documentation for Pipelines for Merged Results."
+msgstr ""
+
msgid "Pipelines settings for '%{project_name}' were successfully updated."
msgstr ""
@@ -9357,6 +9372,9 @@ msgstr ""
msgid "Remove spent time"
msgstr ""
+msgid "Remove stage"
+msgstr ""
+
msgid "Remove time estimate"
msgstr ""
@@ -11145,9 +11163,6 @@ msgstr ""
msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
msgstr ""
-msgid "The code of a detached pipeline is tested against the source branch instead of merged results"
-msgstr ""
-
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr ""
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 74be373b8e8..3309f5b6ce3 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -47,9 +47,13 @@ RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
apt-get update -y && apt-get install google-cloud-sdk kubectl -y
-WORKDIR /home/qa
-COPY ./Gemfile* ./
-RUN bundle install
-COPY ./ ./
+WORKDIR /home/gitlab/qa
+COPY ./qa/Gemfile* /home/gitlab/qa/
+COPY ./config/initializers/0_inject_enterprise_edition_module.rb /home/gitlab/config/initializers/
+COPY ./lib/gitlab.rb /home/gitlab/lib/
+COPY ./INSTALLATION_TYPE /home/gitlab/
+COPY ./VERSION /home/gitlab/
+RUN cd /home/gitlab/qa/ && bundle install
+COPY ./qa /home/gitlab/qa
ENTRYPOINT ["bin/test"]
diff --git a/qa/Gemfile b/qa/Gemfile
index 6abc0d622ad..f04ecb13879 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -1,6 +1,7 @@
source 'https://rubygems.org'
gem 'gitlab-qa'
+gem 'activesupport', '5.2.3' # This should stay in sync with the root's Gemfile
gem 'pry-byebug', '~> 3.5.1', platform: :mri
gem 'capybara', '~> 2.16.1'
gem 'capybara-screenshot', '~> 1.0.18'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index bf051a115b5..d582d77c5cd 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -1,9 +1,9 @@
GEM
remote: https://rubygems.org/
specs:
- activesupport (5.1.4)
+ activesupport (5.2.3)
concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (~> 0.7)
+ i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.5.2)
@@ -28,7 +28,7 @@ GEM
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
coderay (1.1.2)
- concurrent-ruby (1.0.5)
+ concurrent-ruby (1.1.5)
diff-lcs (1.3)
domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0)
@@ -38,7 +38,7 @@ GEM
gitlab-qa (4.0.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
- i18n (0.9.1)
+ i18n (1.6.0)
concurrent-ruby (~> 1.0)
knapsack (1.17.1)
rake
@@ -50,7 +50,7 @@ GEM
mime-types-data (3.2016.0521)
mini_mime (1.0.0)
mini_portile2 (2.4.0)
- minitest (5.11.1)
+ minitest (5.11.3)
netrc (0.11.0)
nokogiri (1.10.4)
mini_portile2 (~> 2.4.0)
@@ -94,7 +94,7 @@ GEM
childprocess (~> 0.5)
rubyzip (~> 1.2, >= 1.2.2)
thread_safe (0.3.6)
- tzinfo (1.2.4)
+ tzinfo (1.2.5)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
@@ -106,6 +106,7 @@ PLATFORMS
ruby
DEPENDENCIES
+ activesupport (= 5.2.3)
airborne (~> 0.2.13)
capybara (~> 2.16.1)
capybara-screenshot (~> 1.0.18)
diff --git a/qa/README.md b/qa/README.md
index bab19665dac..97555f8d0c2 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -123,10 +123,11 @@ To set multiple cookies, separate them with the `;` character, for example: `QA_
Once you have made changes to the CE/EE repositories, you may want to build a
Docker image to test locally instead of waiting for the `gitlab-ce-qa` or
-`gitlab-ee-qa` nightly builds. To do that, you can run from this directory:
+`gitlab-ee-qa` nightly builds. To do that, you can run **from the top `gitlab`
+directory** (one level up from this directory):
```sh
-docker build -t gitlab/gitlab-ce-qa:nightly .
+docker build -t gitlab/gitlab-ce-qa:nightly --file ./qa/Dockerfile ./
```
[GDK]: https://gitlab.com/gitlab-org/gitlab-development-kit/
diff --git a/qa/qa.rb b/qa/qa.rb
index 18fb4509dce..8be2a289422 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -4,6 +4,9 @@ $: << File.expand_path(File.dirname(__FILE__))
Encoding.default_external = 'UTF-8'
+require_relative '../lib/gitlab'
+require_relative '../config/initializers/0_inject_enterprise_edition_module'
+
module QA
##
# GitLab QA runtime classes, mostly singletons.
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
index 567c6a83ddf..458072b1507 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
module QA
- context 'Create' do
+ # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/127
+ context 'Create', :quarantine do
describe 'File templates' do
include Runtime::Fixtures
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 9a50ea79f5e..089d06e11eb 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -177,18 +177,22 @@ describe Projects::PipelinesController do
end
it 'does not perform N + 1 queries' do
+ # Set up all required variables
+ get_pipeline_json
+
control_count = ActiveRecord::QueryRecorder.new { get_pipeline_json }.count
- create_build('test', 1, 'rspec 1')
- create_build('test', 1, 'spinach 0')
- create_build('test', 1, 'spinach 1')
- create_build('test', 1, 'audit')
- create_build('post deploy', 3, 'pages 1')
- create_build('post deploy', 3, 'pages 2')
+ first_build = pipeline.builds.first
+ first_build.tag_list << [:hello, :world]
+ create(:deployment, deployable: first_build)
+
+ second_build = pipeline.builds.second
+ second_build.tag_list << [:docker, :ruby]
+ create(:deployment, deployable: second_build)
new_count = ActiveRecord::QueryRecorder.new { get_pipeline_json }.count
- expect(new_count).to be_within(12).of(control_count)
+ expect(new_count).to be_within(1).of(control_count)
end
end
diff --git a/spec/controllers/projects/starrers_controller_spec.rb b/spec/controllers/projects/starrers_controller_spec.rb
index 7085cba08d5..5774ff7c576 100644
--- a/spec/controllers/projects/starrers_controller_spec.rb
+++ b/spec/controllers/projects/starrers_controller_spec.rb
@@ -32,6 +32,20 @@ describe Projects::StarrersController do
end
end
+ context 'N+1 queries' do
+ render_views
+
+ it 'avoids N+1s loading users', :request_store do
+ get_starrers
+
+ control_count = ActiveRecord::QueryRecorder.new { get_starrers }.count
+
+ create_list(:user, 5).each { |user| user.toggle_star(project) }
+
+ expect { get_starrers }.not_to exceed_query_limit(control_count)
+ end
+ end
+
context 'when project is public' do
before do
project.update_attribute(:visibility_level, Project::PUBLIC)
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 8dab6c71b06..4c875935d82 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -20,5 +20,9 @@ FactoryBot.define do
"email#{n}@email.com"
end
end
+
+ trait(:ldap) do
+ ldap true
+ end
end
end
diff --git a/spec/features/projects/pages_lets_encrypt_spec.rb b/spec/features/projects/pages_lets_encrypt_spec.rb
index a5f8702302c..8b5964b2eee 100644
--- a/spec/features/projects/pages_lets_encrypt_spec.rb
+++ b/spec/features/projects/pages_lets_encrypt_spec.rb
@@ -75,12 +75,10 @@ describe "Pages with Let's Encrypt", :https_pages_enabled do
end
shared_examples 'user sees private keys only for user provided certificate' do
- before do
- visit edit_project_pages_domain_path(project, domain)
- end
-
shared_examples 'user do not see private key' do
it 'user do not see private key' do
+ visit edit_project_pages_domain_path(project, domain)
+
expect(find_field('Key (PEM)', visible: :all, disabled: :all).value).to be_blank
end
end
@@ -101,6 +99,8 @@ describe "Pages with Let's Encrypt", :https_pages_enabled do
let(:domain) { create(:pages_domain, project: project) }
it 'user sees private key' do
+ visit edit_project_pages_domain_path(project, domain)
+
expect(find_field('Key (PEM)').value).not_to be_blank
end
end
diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb
index 5a60991c1bf..4ca79ccaea8 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -39,17 +39,16 @@ describe 'User searches for code' do
context 'when on a project page', :js do
before do
visit(search_path)
- end
-
- include_examples 'top right search form'
-
- it 'finds code' do
find('.js-search-project-dropdown').click
page.within('.project-filter') do
click_link(project.full_name)
end
+ end
+ include_examples 'top right search form'
+
+ it 'finds code' do
fill_in('dashboard_search', with: 'rspec')
find('.btn-search').click
@@ -57,6 +56,27 @@ describe 'User searches for code' do
expect(find(:css, '.search-results')).to have_content('Update capybara, rspec-rails, poltergeist to recent versions')
end
end
+
+ it 'search mutiple words with refs switching' do
+ expected_result = 'Use `snake_case` for naming files'
+ search = 'for naming files'
+
+ fill_in('dashboard_search', with: search)
+ find('.btn-search').click
+
+ page.within('.results') do
+ expect(find('.search-results')).to have_content(expected_result)
+ end
+
+ find('.js-project-refs-dropdown').click
+ find('.dropdown-page-one .dropdown-content').click_link('v1.0.0')
+
+ page.within('.results') do
+ expect(find(:css, '.search-results')).to have_content(expected_result)
+ end
+
+ expect(find_field('dashboard_search').value).to eq(search)
+ end
end
context 'search code within refs', :js do
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index e2b3444272e..70e6978a7b6 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -15,8 +15,8 @@ describe 'GPG signed commits' do
visit project_commit_path(project, ref)
- expect(page).to have_link 'Unverified'
- expect(page).not_to have_link 'Verified'
+ expect(page).to have_button 'Unverified'
+ expect(page).not_to have_button 'Verified'
# user changes his email which makes the gpg key verified
perform_enqueued_jobs do
@@ -26,8 +26,8 @@ describe 'GPG signed commits' do
visit project_commit_path(project, ref)
- expect(page).not_to have_link 'Unverified'
- expect(page).to have_link 'Verified'
+ expect(page).not_to have_button 'Unverified'
+ expect(page).to have_button 'Verified'
end
it 'changes from unverified to verified when the user adds the missing gpg key' do
@@ -36,8 +36,8 @@ describe 'GPG signed commits' do
visit project_commit_path(project, ref)
- expect(page).to have_link 'Unverified'
- expect(page).not_to have_link 'Verified'
+ expect(page).to have_button 'Unverified'
+ expect(page).not_to have_button 'Verified'
# user adds the gpg key which makes the signature valid
perform_enqueued_jobs do
@@ -46,8 +46,8 @@ describe 'GPG signed commits' do
visit project_commit_path(project, ref)
- expect(page).not_to have_link 'Unverified'
- expect(page).to have_link 'Verified'
+ expect(page).not_to have_button 'Unverified'
+ expect(page).to have_button 'Verified'
end
context 'shows popover badges', :js do
@@ -136,7 +136,7 @@ describe 'GPG signed commits' do
visit project_commit_path(project, GpgHelpers::SIGNED_AND_AUTHORED_SHA)
# wait for the signature to get generated
- expect(page).to have_link 'Verified'
+ expect(page).to have_button 'Verified'
user_1.destroy!
diff --git a/spec/frontend/cycle_analytics/stage_nav_item_spec.js b/spec/frontend/cycle_analytics/stage_nav_item_spec.js
new file mode 100644
index 00000000000..ff079082ca7
--- /dev/null
+++ b/spec/frontend/cycle_analytics/stage_nav_item_spec.js
@@ -0,0 +1,177 @@
+import { mount, shallowMount } from '@vue/test-utils';
+import StageNavItem from '~/cycle_analytics/components/stage_nav_item.vue';
+
+describe('StageNavItem', () => {
+ let wrapper = null;
+ const title = 'Cool stage';
+ const value = '1 day';
+
+ function createComponent(props, shallow = true) {
+ const func = shallow ? shallowMount : mount;
+ return func(StageNavItem, {
+ propsData: {
+ canEdit: false,
+ isActive: false,
+ isUserAllowed: false,
+ isDefaultStage: true,
+ title,
+ value,
+ ...props,
+ },
+ });
+ }
+
+ function hasStageName() {
+ const stageName = wrapper.find('.stage-name');
+ expect(stageName.exists()).toBe(true);
+ expect(stageName.text()).toEqual(title);
+ }
+
+ it('renders stage name', () => {
+ wrapper = createComponent({ isUserAllowed: true });
+ hasStageName();
+ wrapper.destroy();
+ });
+
+ describe('User has access', () => {
+ describe('with a value', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ isUserAllowed: true });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+ it('renders the value for median value', () => {
+ expect(wrapper.find('.stage-empty').exists()).toBe(false);
+ expect(wrapper.find('.not-available').exists()).toBe(false);
+ expect(wrapper.find('.stage-median').text()).toEqual(value);
+ });
+ });
+
+ describe('without a value', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ isUserAllowed: true, value: null });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('has the stage-empty class', () => {
+ expect(wrapper.find('.stage-empty').exists()).toBe(true);
+ });
+
+ it('renders Not enough data for the median value', () => {
+ expect(wrapper.find('.stage-median').text()).toEqual('Not enough data');
+ });
+ });
+ });
+
+ describe('is active', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ isActive: true }, false);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+ it('has the active class', () => {
+ expect(wrapper.find('.stage-nav-item').classes('active')).toBe(true);
+ });
+ });
+
+ describe('is not active', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+ it('emits the `select` event when clicked', () => {
+ expect(wrapper.emitted().select).toBeUndefined();
+ wrapper.trigger('click');
+ expect(wrapper.emitted().select.length).toBe(1);
+ });
+ });
+
+ describe('User does not have access', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ isUserAllowed: false }, false);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+ it('renders stage name', () => {
+ hasStageName();
+ });
+
+ it('has class not-available', () => {
+ expect(wrapper.find('.stage-empty').exists()).toBe(false);
+ expect(wrapper.find('.not-available').exists()).toBe(true);
+ });
+
+ it('renders Not available for the median value', () => {
+ expect(wrapper.find('.stage-median').text()).toBe('Not available');
+ });
+ it('does not render options menu', () => {
+ expect(wrapper.find('.more-actions-toggle').exists()).toBe(false);
+ });
+ });
+
+ describe('User can edit stages', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ canEdit: true, isUserAllowed: true }, false);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+ it('renders stage name', () => {
+ hasStageName();
+ });
+
+ it('renders options menu', () => {
+ expect(wrapper.find('.more-actions-toggle').exists()).toBe(true);
+ });
+
+ describe('Default stages', () => {
+ beforeEach(() => {
+ wrapper = createComponent(
+ { canEdit: true, isUserAllowed: true, isDefaultStage: true },
+ false,
+ );
+ });
+ it('can hide the stage', () => {
+ expect(wrapper.text()).toContain('Hide stage');
+ });
+ it('can not edit the stage', () => {
+ expect(wrapper.text()).not.toContain('Edit stage');
+ });
+ it('can not remove the stage', () => {
+ expect(wrapper.text()).not.toContain('Remove stage');
+ });
+ });
+
+ describe('Custom stages', () => {
+ beforeEach(() => {
+ wrapper = createComponent(
+ { canEdit: true, isUserAllowed: true, isDefaultStage: false },
+ false,
+ );
+ });
+ it('can edit the stage', () => {
+ expect(wrapper.text()).toContain('Edit stage');
+ });
+ it('can remove the stage', () => {
+ expect(wrapper.text()).toContain('Remove stage');
+ });
+
+ it('can not hide the stage', () => {
+ expect(wrapper.text()).not.toContain('Hide stage');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index a986bc49f28..b0bdd924921 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -94,6 +94,12 @@ describe('URL utility', () => {
it('adds and updates encoded params', () => {
expect(urlUtils.mergeUrlParams({ a: '&', q: '?' }, '?a=%23#frag')).toBe('?a=%26&q=%3F#frag');
});
+
+ it('treats "+" as "%20"', () => {
+ expect(urlUtils.mergeUrlParams({ ref: 'bogus' }, '?a=lorem+ipsum&ref=charlie')).toBe(
+ '?a=lorem%20ipsum&ref=bogus',
+ );
+ });
});
describe('removeParams', () => {
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index bceb3a8db91..0fc9519a6bf 100644
--- a/spec/javascripts/ide/stores/utils_spec.js
+++ b/spec/javascripts/ide/stores/utils_spec.js
@@ -261,6 +261,41 @@ describe('Multi-file store utils', () => {
},
]);
});
+
+ it('filters out folders from the list', () => {
+ const files = [
+ {
+ path: 'a',
+ type: 'blob',
+ deleted: true,
+ },
+ {
+ path: 'c',
+ type: 'tree',
+ deleted: true,
+ },
+ {
+ path: 'c/d',
+ type: 'blob',
+ deleted: true,
+ },
+ ];
+
+ const flattendFiles = utils.getCommitFiles(files);
+
+ expect(flattendFiles).toEqual([
+ {
+ path: 'a',
+ type: 'blob',
+ deleted: true,
+ },
+ {
+ path: 'c/d',
+ type: 'blob',
+ deleted: true,
+ },
+ ]);
+ });
});
describe('mergeTrees', () => {
diff --git a/spec/javascripts/vue_shared/directives/autofocusonshow_spec.js b/spec/javascripts/vue_shared/directives/autofocusonshow_spec.js
new file mode 100644
index 00000000000..f1ca5f61496
--- /dev/null
+++ b/spec/javascripts/vue_shared/directives/autofocusonshow_spec.js
@@ -0,0 +1,38 @@
+import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
+
+/**
+ * We're testing this directive's hooks as pure functions
+ * since behaviour of this directive is highly-dependent
+ * on underlying DOM methods.
+ */
+describe('AutofocusOnShow directive', () => {
+ describe('with input invisible on component render', () => {
+ let el;
+
+ beforeAll(() => {
+ setFixtures('<div id="container" style="display: none;"><input id="inputel"/></div>');
+ el = document.querySelector('#inputel');
+ });
+
+ it('should bind IntersectionObserver on input element', () => {
+ spyOn(el, 'focus');
+
+ autofocusonshow.inserted(el);
+
+ expect(el.visibilityObserver).toBeDefined();
+ expect(el.focus).not.toHaveBeenCalled();
+ });
+
+ it('should stop IntersectionObserver on input element on unbind hook', () => {
+ el.visibilityObserver = {
+ disconnect: () => {},
+ };
+ spyOn(el.visibilityObserver, 'disconnect');
+
+ autofocusonshow.unbind(el);
+
+ expect(el.visibilityObserver).toBeDefined();
+ expect(el.visibilityObserver.disconnect).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb
index 0295138fc3a..04fc24b6205 100644
--- a/spec/lib/gitlab/repository_cache_adapter_spec.rb
+++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb
@@ -4,6 +4,7 @@ describe Gitlab::RepositoryCacheAdapter do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:cache) { repository.send(:cache) }
+ let(:redis_set_cache) { repository.send(:redis_set_cache) }
describe '#cache_method_output', :use_clean_rails_memory_store_caching do
let(:fallback) { 10 }
@@ -206,9 +207,11 @@ describe Gitlab::RepositoryCacheAdapter do
describe '#expire_method_caches' do
it 'expires the caches of the given methods' do
expect(cache).to receive(:expire).with(:rendered_readme)
- expect(cache).to receive(:expire).with(:gitignore)
+ expect(cache).to receive(:expire).with(:branch_names)
+ expect(redis_set_cache).to receive(:expire).with(:rendered_readme)
+ expect(redis_set_cache).to receive(:expire).with(:branch_names)
- repository.expire_method_caches(%i(rendered_readme gitignore))
+ repository.expire_method_caches(%i(rendered_readme branch_names))
end
it 'does not expire caches for non-existent methods' do
diff --git a/spec/lib/gitlab/repository_set_cache_spec.rb b/spec/lib/gitlab/repository_set_cache_spec.rb
new file mode 100644
index 00000000000..9695e13d842
--- /dev/null
+++ b/spec/lib/gitlab/repository_set_cache_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+
+describe Gitlab::RepositorySetCache, :clean_gitlab_redis_cache do
+ let(:project) { create(:project) }
+ let(:repository) { project.repository }
+ let(:namespace) { "#{repository.full_path}:#{project.id}" }
+ let(:cache) { described_class.new(repository) }
+
+ describe '#cache_key' do
+ subject { cache.cache_key(:foo) }
+
+ it 'includes the namespace' do
+ is_expected.to eq("foo:#{namespace}:set")
+ end
+
+ context 'with a given namespace' do
+ let(:extra_namespace) { 'my:data' }
+ let(:cache) { described_class.new(repository, extra_namespace: extra_namespace) }
+
+ it 'includes the full namespace' do
+ is_expected.to eq("foo:#{namespace}:#{extra_namespace}:set")
+ end
+ end
+ end
+
+ describe '#expire' do
+ it 'expires the given key from the cache' do
+ cache.write(:foo, ['value'])
+
+ expect(cache.read(:foo)).to contain_exactly('value')
+ expect(cache.expire(:foo)).to eq(1)
+ expect(cache.read(:foo)).to be_empty
+ end
+ end
+
+ describe '#exist?' do
+ it 'checks whether the key exists' do
+ expect(cache.exist?(:foo)).to be(false)
+
+ cache.write(:foo, ['value'])
+
+ expect(cache.exist?(:foo)).to be(true)
+ end
+ end
+
+ describe '#fetch' do
+ let(:blk) { -> { ['block value'] } }
+
+ subject { cache.fetch(:foo, &blk) }
+
+ it 'fetches the key from the cache when filled' do
+ cache.write(:foo, ['value'])
+
+ is_expected.to contain_exactly('value')
+ end
+
+ it 'writes the value of the provided block when empty' do
+ cache.expire(:foo)
+
+ is_expected.to contain_exactly('block value')
+ expect(cache.read(:foo)).to contain_exactly('block value')
+ end
+ end
+
+ describe '#include?' do
+ it 'checks inclusion in the Redis set' do
+ cache.write(:foo, ['value'])
+
+ expect(cache.include?(:foo, 'value')).to be(true)
+ expect(cache.include?(:foo, 'bar')).to be(false)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sentry_spec.rb b/spec/lib/gitlab/sentry_spec.rb
index af8b059b984..a48cc0d128a 100644
--- a/spec/lib/gitlab/sentry_spec.rb
+++ b/spec/lib/gitlab/sentry_spec.rb
@@ -65,6 +65,7 @@ describe Gitlab::Sentry do
context '.track_acceptable_exception' do
let(:exception) { RuntimeError.new('boom') }
+ let(:issue_url) { 'http://gitlab.com/gitlab-org/gitlab-ce/issues/1' }
before do
allow(described_class).to receive(:enabled?).and_return(true)
@@ -74,7 +75,7 @@ describe Gitlab::Sentry do
it 'calls Raven.capture_exception' do
expected_extras = {
some_other_info: 'info',
- issue_url: 'http://gitlab.com/gitlab-org/gitlab-ce/issues/1'
+ issue_url: issue_url
}
expected_tags = {
@@ -88,9 +89,33 @@ describe Gitlab::Sentry do
described_class.track_acceptable_exception(
exception,
- issue_url: 'http://gitlab.com/gitlab-org/gitlab-ce/issues/1',
+ issue_url: issue_url,
extra: { some_other_info: 'info' }
)
end
+
+ context 'the exception implements :sentry_extra_data' do
+ let(:extra_info) { { event: 'explosion', size: :massive } }
+ let(:exception) { double(message: 'bang!', sentry_extra_data: extra_info) }
+
+ it 'includes the extra data from the exception in the tracking information' do
+ expect(Raven).to receive(:capture_exception)
+ .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
+
+ described_class.track_acceptable_exception(exception)
+ end
+ end
+
+ context 'the exception implements :sentry_extra_data, which returns nil' do
+ let(:exception) { double(message: 'bang!', sentry_extra_data: nil) }
+
+ it 'just includes the other extra info' do
+ extra_info = { issue_url: issue_url }
+ expect(Raven).to receive(:capture_exception)
+ .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
+
+ described_class.track_acceptable_exception(exception, extra_info)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 9bbd9394d57..64254674157 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -266,6 +266,12 @@ describe Gitlab::UsageData do
expect(described_class.count(relation)).to eq(1)
end
+ it 'returns the count for count_by when provided' do
+ allow(relation).to receive(:count).with(:creator_id).and_return(2)
+
+ expect(described_class.count(relation, count_by: :creator_id)).to eq(2)
+ end
+
it 'returns the fallback value when counting fails' do
allow(relation).to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
diff --git a/spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb b/spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb
new file mode 100644
index 00000000000..c7302a1a656
--- /dev/null
+++ b/spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Prometheus::CleanupMultiprocDirService do
+ describe '.call' do
+ subject { described_class.new.execute }
+
+ let(:metrics_multiproc_dir) { Dir.mktmpdir }
+ let(:metrics_file_path) { File.join(metrics_multiproc_dir, 'counter_puma_master-0.db') }
+
+ before do
+ FileUtils.touch(metrics_file_path)
+ end
+
+ after do
+ FileUtils.rm_r(metrics_multiproc_dir)
+ end
+
+ context 'when `multiprocess_files_dir` is defined' do
+ before do
+ expect(Prometheus::Client.configuration)
+ .to receive(:multiprocess_files_dir)
+ .and_return(metrics_multiproc_dir)
+ .at_least(:once)
+ end
+
+ it 'removes old metrics' do
+ expect { subject }
+ .to change { File.exist?(metrics_file_path) }
+ .from(true)
+ .to(false)
+ end
+ end
+
+ context 'when `multiprocess_files_dir` is not defined' do
+ before do
+ expect(Prometheus::Client.configuration)
+ .to receive(:multiprocess_files_dir)
+ .and_return(nil)
+ .at_least(:once)
+ end
+
+ it 'does not remove any files' do
+ expect { subject }
+ .not_to change { File.exist?(metrics_file_path) }
+ .from(true)
+ end
+ end
+ end
+end
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index ebb0bfca369..ad7dfac87af 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -3,19 +3,29 @@
require 'spec_helper'
describe GroupMember do
- describe '.count_users_by_group_id' do
- it 'counts users by group ID' do
- user_1 = create(:user)
- user_2 = create(:user)
- group_1 = create(:group)
- group_2 = create(:group)
-
- group_1.add_owner(user_1)
- group_1.add_owner(user_2)
- group_2.add_owner(user_1)
-
- expect(described_class.count_users_by_group_id).to eq(group_1.id => 2,
- group_2.id => 1)
+ context 'scopes' do
+ describe '.count_users_by_group_id' do
+ it 'counts users by group ID' do
+ user_1 = create(:user)
+ user_2 = create(:user)
+ group_1 = create(:group)
+ group_2 = create(:group)
+
+ group_1.add_owner(user_1)
+ group_1.add_owner(user_2)
+ group_2.add_owner(user_1)
+
+ expect(described_class.count_users_by_group_id).to eq(group_1.id => 2,
+ group_2.id => 1)
+ end
+ end
+
+ describe '.of_ldap_type' do
+ it 'returns ldap type users' do
+ group_member = create(:group_member, :ldap)
+
+ expect(described_class.of_ldap_type).to eq([group_member])
+ end
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 419e1dc2459..79395fcc994 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1223,36 +1223,66 @@ describe Repository do
end
describe '#branch_exists?' do
- it 'uses branch_names' do
- allow(repository).to receive(:branch_names).and_return(['foobar'])
+ let(:branch) { repository.root_ref }
- expect(repository.branch_exists?('foobar')).to eq(true)
- expect(repository.branch_exists?('master')).to eq(false)
+ subject { repository.branch_exists?(branch) }
+
+ it 'delegates to branch_names when the cache is empty' do
+ repository.expire_branches_cache
+
+ expect(repository).to receive(:branch_names).and_call_original
+ is_expected.to eq(true)
+ end
+
+ it 'uses redis set caching when the cache is filled' do
+ repository.branch_names # ensure the branch name cache is filled
+
+ expect(repository)
+ .to receive(:branch_names_include?)
+ .with(branch)
+ .and_call_original
+
+ is_expected.to eq(true)
end
end
describe '#tag_exists?' do
- it 'uses tag_names' do
- allow(repository).to receive(:tag_names).and_return(['foobar'])
+ let(:tag) { repository.tags.first.name }
+
+ subject { repository.tag_exists?(tag) }
+
+ it 'delegates to tag_names when the cache is empty' do
+ repository.expire_tags_cache
+
+ expect(repository).to receive(:tag_names).and_call_original
+ is_expected.to eq(true)
+ end
+
+ it 'uses redis set caching when the cache is filled' do
+ repository.tag_names # ensure the tag name cache is filled
+
+ expect(repository)
+ .to receive(:tag_names_include?)
+ .with(tag)
+ .and_call_original
- expect(repository.tag_exists?('foobar')).to eq(true)
- expect(repository.tag_exists?('master')).to eq(false)
+ is_expected.to eq(true)
end
end
- describe '#branch_names', :use_clean_rails_memory_store_caching do
+ describe '#branch_names', :clean_gitlab_redis_cache do
let(:fake_branch_names) { ['foobar'] }
it 'gets cached across Repository instances' do
allow(repository.raw_repository).to receive(:branch_names).once.and_return(fake_branch_names)
- expect(repository.branch_names).to eq(fake_branch_names)
+ expect(repository.branch_names).to match_array(fake_branch_names)
fresh_repository = Project.find(project.id).repository
expect(fresh_repository.object_id).not_to eq(repository.object_id)
expect(fresh_repository.raw_repository).not_to receive(:branch_names)
- expect(fresh_repository.branch_names).to eq(fake_branch_names)
+ expect(fresh_repository.branch_names).to match_array(fake_branch_names)
end
end
diff --git a/spec/presenters/blobs/unfold_presenter_spec.rb b/spec/presenters/blobs/unfold_presenter_spec.rb
index ab3f8080257..83004809536 100644
--- a/spec/presenters/blobs/unfold_presenter_spec.rb
+++ b/spec/presenters/blobs/unfold_presenter_spec.rb
@@ -10,16 +10,31 @@ describe Blobs::UnfoldPresenter do
let(:subject) { described_class.new(blob, params) }
describe '#initialize' do
+ let(:result) { subject }
+
+ context 'with empty params' do
+ let(:params) { {} }
+
+ it 'sets default attributes' do
+ expect(result.full?).to eq(false)
+ expect(result.since).to eq(1)
+ expect(result.to).to eq(1)
+ expect(result.bottom).to eq(false)
+ expect(result.unfold).to eq(true)
+ expect(result.offset).to eq(0)
+ expect(result.indent).to eq(0)
+ end
+ end
+
context 'when full is false' do
let(:params) { { full: false, since: 2, to: 3, bottom: false, offset: 1, indent: 1 } }
it 'sets attributes' do
- result = subject
-
expect(result.full?).to eq(false)
expect(result.since).to eq(2)
expect(result.to).to eq(3)
expect(result.bottom).to eq(false)
+ expect(result.unfold).to eq(true)
expect(result.offset).to eq(1)
expect(result.indent).to eq(1)
end
@@ -29,12 +44,11 @@ describe Blobs::UnfoldPresenter do
let(:params) { { full: true, since: 2, to: 3, bottom: false, offset: 1, indent: 1 } }
it 'sets other attributes' do
- result = subject
-
expect(result.full?).to eq(true)
expect(result.since).to eq(1)
expect(result.to).to eq(blob.lines.size)
expect(result.bottom).to eq(false)
+ expect(result.unfold).to eq(false)
expect(result.offset).to eq(0)
expect(result.indent).to eq(0)
end
@@ -44,12 +58,11 @@ describe Blobs::UnfoldPresenter do
let(:params) { { full: false, since: 2, to: -1, bottom: true, offset: 1, indent: 1 } }
it 'sets other attributes' do
- result = subject
-
expect(result.full?).to eq(false)
expect(result.since).to eq(2)
expect(result.to).to eq(blob.lines.size)
expect(result.bottom).to eq(false)
+ expect(result.unfold).to eq(false)
expect(result.offset).to eq(0)
expect(result.indent).to eq(0)
end
diff --git a/spec/services/git/branch_push_service_spec.rb b/spec/services/git/branch_push_service_spec.rb
index ad5d296f5c1..d9e607cd251 100644
--- a/spec/services/git/branch_push_service_spec.rb
+++ b/spec/services/git/branch_push_service_spec.rb
@@ -76,6 +76,22 @@ describe Git::BranchPushService, services: true do
stub_ci_pipeline_to_return_yaml_file
end
+ it 'creates a pipeline with the right parameters' do
+ expect(Ci::CreatePipelineService)
+ .to receive(:new)
+ .with(project,
+ user,
+ {
+ before: oldrev,
+ after: newrev,
+ ref: ref,
+ checkout_sha: SeedRepo::Commit::ID,
+ push_options: {}
+ }).and_call_original
+
+ subject
+ end
+
it "creates a new pipeline" do
expect { subject }.to change { Ci::Pipeline.count }
diff --git a/spec/views/projects/pages_domains/show.html.haml_spec.rb b/spec/views/projects/pages_domains/show.html.haml_spec.rb
new file mode 100644
index 00000000000..da27a04bfe9
--- /dev/null
+++ b/spec/views/projects/pages_domains/show.html.haml_spec.rb
@@ -0,0 +1,66 @@
+require 'spec_helper'
+
+describe 'projects/pages_domains/show' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ assign(:project, project)
+ assign(:domain, domain)
+ end
+
+ context 'when auto_ssl is enabled' do
+ context 'when domain is disabled' do
+ let(:domain) { create(:pages_domain, :disabled, project: project, auto_ssl_enabled: true) }
+
+ it 'shows verification warning' do
+ render
+
+ expect(rendered).to have_content("A Let's Encrypt SSL certificate can not be obtained until your domain is verified.")
+ end
+ end
+
+ context 'when certificate is absent' do
+ let(:domain) { create(:pages_domain, :without_key, :without_certificate, project: project, auto_ssl_enabled: true) }
+
+ it 'shows alert about time of obtaining certificate' do
+ render
+
+ expect(rendered).to have_content("GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later.")
+ end
+ end
+
+ context 'when certificate is present' do
+ let(:domain) { create(:pages_domain, :letsencrypt, project: project) }
+
+ it 'shows certificate info' do
+ render
+
+ # test just a random part of cert represenations(X509v3 Subject Key Identifier:)
+ expect(rendered).to have_content("C6:5F:56:4B:10:69:AC:1D:33:D2:26:C9:B3:7A:D7:12:4D:3E:F7:90")
+ end
+ end
+ end
+
+ context 'when auto_ssl is disabled' do
+ context 'when certificate is present' do
+ let(:domain) { create(:pages_domain, project: project) }
+
+ it 'shows certificate info' do
+ render
+
+ # test just a random part of cert represenations(X509v3 Subject Key Identifier:)
+ expect(rendered).to have_content("C6:5F:56:4B:10:69:AC:1D:33:D2:26:C9:B3:7A:D7:12:4D:3E:F7:90")
+ end
+ end
+
+ context 'when certificate is absent' do
+ let(:domain) { create(:pages_domain, :without_certificate, :without_key, project: project) }
+
+ it 'shows missing certificate' do
+ render
+
+ expect(rendered).to have_content("missing")
+ end
+ end
+ end
+end