summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.yml10
-rw-r--r--.gitlab/CODEOWNERS2
-rw-r--r--CHANGELOG.md60
-rw-r--r--GITLAB_ELASTICSEARCH_INDEXER_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock29
-rw-r--r--PROCESS.md27
-rw-r--r--app/assets/javascripts/awards_handler.js2
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js3
-rw-r--r--app/assets/javascripts/boards/index.js4
-rw-r--r--app/assets/javascripts/boards/models/assignee.js (renamed from app/assets/javascripts/vue_shared/models/assignee.js)0
-rw-r--r--app/assets/javascripts/boards/models/issue.js2
-rw-r--r--app/assets/javascripts/boards/models/label.js11
-rw-r--r--app/assets/javascripts/boards/models/list.js4
-rw-r--r--app/assets/javascripts/boards/stores/actions.js1
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js1
-rw-r--r--app/assets/javascripts/ci_variable_list/ajax_variable_list.js11
-rw-r--r--app/assets/javascripts/ci_variable_list/ci_variable_list.js6
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js16
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue83
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue2
-rw-r--r--app/assets/javascripts/clusters/services/application_state_machine.js1
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js11
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue2
-rw-r--r--app/assets/javascripts/diffs/components/commit_item.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue15
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue21
-rw-r--r--app/assets/javascripts/environments/components/container.vue2
-rw-r--r--app/assets/javascripts/environments/mixins/environments_mixin.js2
-rw-r--r--app/assets/javascripts/gl_dropdown.js2
-rw-r--r--app/assets/javascripts/ide/components/ide.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue16
-rw-r--r--app/assets/javascripts/ide/components/ide_status_list.vue23
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue2
-rw-r--r--app/assets/javascripts/ide/lib/keymap.json8
-rw-r--r--app/assets/javascripts/ide/stores/actions.js5
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js5
-rw-r--r--app/assets/javascripts/lib/utils/autosave.js1
-rw-r--r--app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js9
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js2
-rw-r--r--app/assets/javascripts/lib/utils/invalid_url.js6
-rw-r--r--app/assets/javascripts/lib/utils/notify.js2
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js6
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js12
-rw-r--r--app/assets/javascripts/monitoring/components/charts/area.vue5
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue45
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle.js6
-rw-r--r--app/assets/javascripts/monitoring/stores/actions.js90
-rw-r--r--app/assets/javascripts/monitoring/stores/mutation_types.js3
-rw-r--r--app/assets/javascripts/monitoring/stores/mutations.js53
-rw-r--r--app/assets/javascripts/monitoring/stores/state.js5
-rw-r--r--app/assets/javascripts/monitoring/stores/utils.js16
-rw-r--r--app/assets/javascripts/new_branch_form.js2
-rw-r--r--app/assets/javascripts/notes.js25
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue2
-rw-r--r--app/assets/javascripts/notes/components/discussion_counter.vue12
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter.vue4
-rw-r--r--app/assets/javascripts/notes/components/discussion_locked_widget.vue20
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue2
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue45
-rw-r--r--app/assets/javascripts/notes/mixins/issuable_state.js11
-rw-r--r--app/assets/javascripts/operation_settings/index.js8
-rw-r--r--app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js1
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js15
-rw-r--r--app/assets/javascripts/pages/projects/project.js12
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js1
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_name_component.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue2
-rw-r--r--app/assets/javascripts/registry/components/table_registry.vue2
-rw-r--r--app/assets/javascripts/reports/components/issues_list.vue20
-rw-r--r--app/assets/javascripts/reports/components/report_section.vue42
-rw-r--r--app/assets/javascripts/reports/constants.js6
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue1
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue15
-rw-r--r--app/assets/javascripts/repository/graphql.js2
-rw-r--r--app/assets/javascripts/repository/queries/getFiles.graphql1
-rw-r--r--app/assets/javascripts/repository/utils/title.js1
-rw-r--r--app/assets/javascripts/search_autocomplete.js2
-rw-r--r--app/assets/javascripts/users_select.js1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue (renamed from app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue)16
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue47
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/constants.js10
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mixins/auto_merge.js15
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue24
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js22
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/issue_warning.vue45
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/modal_copy_button.vue121
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination/constants.js9
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination/graphql_pagination.vue47
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue (renamed from app/assets/javascripts/vue_shared/components/table_pagination.vue)18
-rw-r--r--app/assets/javascripts/vue_shared/models/label.js13
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss5
-rw-r--r--app/assets/stylesheets/components/avatar.scss9
-rw-r--r--app/assets/stylesheets/components/popover.scss114
-rw-r--r--app/assets/stylesheets/framework/animations.scss24
-rw-r--r--app/assets/stylesheets/framework/blocks.scss9
-rw-r--r--app/assets/stylesheets/framework/buttons.scss3
-rw-r--r--app/assets/stylesheets/framework/common.scss47
-rw-r--r--app/assets/stylesheets/framework/feature_highlight.scss7
-rw-r--r--app/assets/stylesheets/framework/files.scss22
-rw-r--r--app/assets/stylesheets/framework/highlight.scss6
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss4
-rw-r--r--app/assets/stylesheets/framework/lists.scss8
-rw-r--r--app/assets/stylesheets/framework/mixins.scss21
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss52
-rw-r--r--app/assets/stylesheets/framework/timeline.scss4
-rw-r--r--app/assets/stylesheets/framework/typography.scss1
-rw-r--r--app/assets/stylesheets/framework/variables.scss7
-rw-r--r--app/assets/stylesheets/page_bundles/_ide_mixins.scss14
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss15
-rw-r--r--app/assets/stylesheets/pages/boards.scss9
-rw-r--r--app/assets/stylesheets/pages/commits.scss8
-rw-r--r--app/assets/stylesheets/pages/diff.scss6
-rw-r--r--app/assets/stylesheets/pages/groups.scss3
-rw-r--r--app/assets/stylesheets/pages/issuable.scss13
-rw-r--r--app/assets/stylesheets/pages/issues.scss11
-rw-r--r--app/assets/stylesheets/pages/members.scss59
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss35
-rw-r--r--app/assets/stylesheets/pages/milestone.scss21
-rw-r--r--app/assets/stylesheets/pages/note_form.scss30
-rw-r--r--app/assets/stylesheets/pages/notes.scss68
-rw-r--r--app/assets/stylesheets/pages/prometheus.scss1
-rw-r--r--app/controllers/acme_challenges_controller.rb17
-rw-r--r--app/controllers/admin/application_settings_controller.rb6
-rw-r--r--app/controllers/clusters/clusters_controller.rb3
-rw-r--r--app/controllers/import/fogbugz_controller.rb8
-rw-r--r--app/controllers/profiles/emails_controller.rb4
-rw-r--r--app/controllers/projects/clusters_controller.rb4
-rw-r--r--app/controllers/projects/environments/prometheus_api_controller.rb2
-rw-r--r--app/controllers/projects/environments_controller.rb7
-rw-r--r--app/controllers/projects/merge_requests/application_controller.rb10
-rw-r--r--app/controllers/projects/merge_requests_controller.rb18
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb3
-rw-r--r--app/controllers/projects/settings/operations_controller.rb4
-rw-r--r--app/controllers/search_controller.rb1
-rw-r--r--app/finders/issuable_finder.rb8
-rw-r--r--app/graphql/types/base_field.rb15
-rw-r--r--app/graphql/types/tree/blob_type.rb3
-rw-r--r--app/helpers/ci_variables_helper.rb4
-rw-r--r--app/helpers/emails_helper.rb6
-rw-r--r--app/helpers/environments_helper.rb1
-rw-r--r--app/models/broadcast_message.rb11
-rw-r--r--app/models/ci/pipeline_schedule.rb3
-rw-r--r--app/models/clusters/cluster.rb1
-rw-r--r--app/models/clusters/platforms/kubernetes.rb1
-rw-r--r--app/models/concerns/maskable.rb4
-rw-r--r--app/models/concerns/prometheus_adapter.rb1
-rw-r--r--app/models/concerns/reactive_caching.rb4
-rw-r--r--app/models/concerns/taskable.rb7
-rw-r--r--app/models/diff_note.rb2
-rw-r--r--app/models/label.rb1
-rw-r--r--app/models/merge_request.rb53
-rw-r--r--app/models/notification_recipient.rb5
-rw-r--r--app/models/pages_domain.rb11
-rw-r--r--app/models/pages_domain_acme_order.rb24
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_ci_cd_setting.rb20
-rw-r--r--app/models/project_feature.rb2
-rw-r--r--app/models/project_services/youtrack_service.rb6
-rw-r--r--app/models/project_statistics.rb2
-rw-r--r--app/policies/project_policy.rb1
-rw-r--r--app/presenters/blob_presenter.rb2
-rw-r--r--app/presenters/ci/build_runner_presenter.rb18
-rw-r--r--app/serializers/issue_entity.rb8
-rw-r--r--app/services/auto_merge/base_service.rb63
-rw-r--r--app/services/auto_merge/merge_when_pipeline_succeeds_service.rb34
-rw-r--r--app/services/auto_merge_service.rb6
-rw-r--r--app/services/ci/pipeline_schedule_service.rb2
-rw-r--r--app/services/git/branch_hooks_service.rb6
-rw-r--r--app/services/merge_requests/base_service.rb4
-rw-r--r--app/services/merge_requests/close_service.rb4
-rw-r--r--app/services/merge_requests/merge_to_ref_service.rb20
-rw-r--r--app/services/merge_requests/mergeability_check_service.rb82
-rw-r--r--app/services/merge_requests/refresh_service.rb6
-rw-r--r--app/services/merge_requests/update_service.rb2
-rw-r--r--app/services/pages_domains/create_acme_order_service.rb31
-rw-r--r--app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb41
-rw-r--r--app/services/preview_markdown_service.rb4
-rw-r--r--app/services/projects/fork_service.rb20
-rw-r--r--app/services/search_service.rb4
-rw-r--r--app/services/service_response.rb15
-rw-r--r--app/views/abuse_reports/new.html.haml8
-rw-r--r--app/views/admin/application_settings/_performance_bar.html.haml2
-rw-r--r--app/views/admin/application_settings/_visibility_and_access.html.haml1
-rw-r--r--app/views/admin/application_settings/metrics_and_profiling.html.haml2
-rw-r--r--app/views/ci/variables/_index.html.haml2
-rw-r--r--app/views/dashboard/todos/index.html.haml2
-rw-r--r--app/views/devise/shared/_tabs_normal.html.haml2
-rw-r--r--app/views/discussions/_notes.html.haml20
-rw-r--r--app/views/groups/show.html.haml2
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml32
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml2
-rw-r--r--app/views/notify/new_user_email.html.haml3
-rw-r--r--app/views/notify/new_user_email.text.erb11
-rw-r--r--app/views/profiles/emails/index.html.haml46
-rw-r--r--app/views/profiles/keys/_form.html.haml4
-rw-r--r--app/views/profiles/preferences/show.html.haml21
-rw-r--r--app/views/projects/_home_panel.html.haml2
-rw-r--r--app/views/projects/branches/_branch.html.haml2
-rw-r--r--app/views/projects/commit/_commit_box.html.haml2
-rw-r--r--app/views/projects/diffs/_parallel_view.html.haml2
-rw-r--r--app/views/projects/diffs/_text_file.html.haml2
-rw-r--r--app/views/projects/issues/_new_branch.html.haml8
-rw-r--r--app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml2
-rw-r--r--app/views/projects/merge_requests/show.html.haml43
-rw-r--r--app/views/projects/notes/_more_actions_dropdown.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml8
-rw-r--r--app/views/projects/tags/index.html.haml2
-rw-r--r--app/views/projects/tags/show.html.haml2
-rw-r--r--app/views/search/_results.html.haml2
-rw-r--r--app/views/shared/_email_with_badge.html.haml2
-rw-r--r--app/views/shared/members/_group.html.haml19
-rw-r--r--app/views/shared/members/_member.html.haml30
-rw-r--r--app/views/shared/milestones/_deprecation_message.html.haml1
-rw-r--r--app/views/shared/notes/_note.html.haml2
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--changelogs/archive.md36
-rw-r--r--changelogs/unreleased/10088-move-code-differences-EE-to-CE.yml5
-rw-r--r--changelogs/unreleased/10842-add-missing-environments-variable-to-the-sast-analyzer-docker-container.yml5
-rw-r--r--changelogs/unreleased/12106-sp-ce.yml5
-rw-r--r--changelogs/unreleased/33064-add-labels-to-note-event-payload.yml5
-rw-r--r--changelogs/unreleased/37495.yml5
-rw-r--r--changelogs/unreleased/51636-task-list-api-pderichs.yml5
-rw-r--r--changelogs/unreleased/55033-discussion-system-note-alignment.yml5
-rw-r--r--changelogs/unreleased/55125-mr-tab-scrolling.yml5
-rw-r--r--changelogs/unreleased/5615-non-admins-only-archieve-ce.yml5
-rw-r--r--changelogs/unreleased/58297-remove-extraneous-gitaly-calls-from-md-rendering.yml5
-rw-r--r--changelogs/unreleased/58433-email-notifications-do-not-work-properly-issue-due-date.yml5
-rw-r--r--changelogs/unreleased/58984-doc-missing-milestones-and-labels-links.yml5
-rw-r--r--changelogs/unreleased/59376-Report-abuse-to-GitLab-should-be-Report-abuse-in-non-gitlab-com-instances.yml5
-rw-r--r--changelogs/unreleased/59651-remove-unnecessary-decimal-places-on-chart-axes.yml5
-rw-r--r--changelogs/unreleased/60303-replace-sidekiq-mtail-metrics.yml5
-rw-r--r--changelogs/unreleased/61072-link-to-user-profile-not-distinguishable-on-latest-commit-widget.yml5
-rw-r--r--changelogs/unreleased/61157-reviewer-roulette-shouldn-t-include-the-author-as-a-possibility.yml5
-rw-r--r--changelogs/unreleased/61246-fix-label-click-scroll-to-top.yml5
-rw-r--r--changelogs/unreleased/61323-snippet-copy-icon-button-is-misaligned.yml2
-rw-r--r--changelogs/unreleased/61324-non-project-snippet-new-snippet-button-should-be-green-outline.yml2
-rw-r--r--changelogs/unreleased/61339-Add-underline-to-attach-a-file.yml2
-rw-r--r--changelogs/unreleased/61565-merge-request-discussion-text-jumps-when-resolved.yml5
-rw-r--r--changelogs/unreleased/61988-collapse-icon-on-merge-request-diff-larger-than-profile-picture.yml4
-rw-r--r--changelogs/unreleased/62091-remove-time-windows-flag.yml5
-rw-r--r--changelogs/unreleased/62124-new-threaded-discussion-design.yml5
-rw-r--r--changelogs/unreleased/62134-fix-non-wraping-project-description.yml5
-rw-r--r--changelogs/unreleased/62144-fix-option-dropdown-button-size.yml5
-rw-r--r--changelogs/unreleased/62154-fe-create-fix-long-branch-name-in-dropdown.yml6
-rw-r--r--changelogs/unreleased/62300-target-area-for-dropdown-list-items-is-too-small-on-metrics-dashboard.yml5
-rw-r--r--changelogs/unreleased/62418-project-default-git-depth.yml5
-rw-r--r--changelogs/unreleased/62656-adjusted-dropdown-styles.yml5
-rw-r--r--changelogs/unreleased/62713-fix-uninstalling-cluster-apps.yml5
-rw-r--r--changelogs/unreleased/62788-clean-up-pagination.yml5
-rw-r--r--changelogs/unreleased/62788-graphql-pagination.yml5
-rw-r--r--changelogs/unreleased/62847-url-for-the-next-request-with-pagination-is-missing-port.yml5
-rw-r--r--changelogs/unreleased/62974-follow-up-from-wip-align-merge-request-icons-and-text.yml5
-rw-r--r--changelogs/unreleased/9186-implement-atmtwps-state-to-mr-widget.yml5
-rw-r--r--changelogs/unreleased/ac-63020-typeerror-nil-can-t-be-coerced-into-integer.yml5
-rw-r--r--changelogs/unreleased/add-lfs-blob-ids-to-tree-type.yml5
-rw-r--r--changelogs/unreleased/allow-emoji-in-references.yml5
-rw-r--r--changelogs/unreleased/cancel-auto-merge-when-branch-is-changed.yml5
-rw-r--r--changelogs/unreleased/copy-button-in-modals.yml5
-rw-r--r--changelogs/unreleased/docs-add-chatops-request-doc.yml5
-rw-r--r--changelogs/unreleased/expose-project-git-depth-via-api.yml5
-rw-r--r--changelogs/unreleased/fe-fix-gl-dropdown-scrolling-to-top.yml5
-rw-r--r--changelogs/unreleased/feature-gb-use-gitlabktl-to-build-serverless-applications.yml5
-rw-r--r--changelogs/unreleased/fix-allow-lower-case-issue-ids.yml5
-rw-r--r--changelogs/unreleased/fix-diverged-branch-locals.yml5
-rw-r--r--changelogs/unreleased/fix-gb-fix-serverless-apps-deployment-template.yml5
-rw-r--r--changelogs/unreleased/fix-gb-remove-serverless-app-build-policies-from-template.yml5
-rw-r--r--changelogs/unreleased/fix-issue-mr-badge.yml5
-rw-r--r--changelogs/unreleased/fix-pipeline-schedule-owner-is-nil.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-profiles-preferences.yml5
-rw-r--r--changelogs/unreleased/i18n-email-of-user-profile.yml5
-rw-r--r--changelogs/unreleased/ignore-artifact-attirbutes-in-project-import-export.yml5
-rw-r--r--changelogs/unreleased/improve-email-text-part.yml5
-rw-r--r--changelogs/unreleased/jc-migration-for-source-project-id.yml5
-rw-r--r--changelogs/unreleased/osw-avoid-encoding-errors-on-merge-to-ref-service.yml5
-rw-r--r--changelogs/unreleased/osw-fix-post-dep-migration-with-timeout.yml5
-rw-r--r--changelogs/unreleased/osw-sync-merge-ref-upon-mergeability-check.yml5
-rw-r--r--changelogs/unreleased/patch-65.yml5
-rw-r--r--changelogs/unreleased/patch-issue--56683.yml6
-rw-r--r--changelogs/unreleased/remove-grafana-dashboard-link-feature-flag.yml5
-rw-r--r--changelogs/unreleased/revert-git-depth-for-merge-request.yml5
-rw-r--r--changelogs/unreleased/sh-add-backtrace-to-sql-queries.yml5
-rw-r--r--changelogs/unreleased/sh-fix-fogbugz-import.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-55869.yml5
-rw-r--r--changelogs/unreleased/sh-fix-resolve-button-not-available.yml5
-rw-r--r--changelogs/unreleased/sh-fix-utf-8-encoding-resolve-conflicts.yml5
-rw-r--r--changelogs/unreleased/sh-project-import-visibility-error.yml5
-rw-r--r--changelogs/unreleased/thomas-nilsson-irfu-gitlab-ce-thomas-nilsson-irfu-master-patch-13137.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-shell-9-3-0.yml5
-rw-r--r--changelogs/unreleased/upgrade-pages-to-v1-6-1.yml5
-rw-r--r--config/initializers/7_prometheus_metrics.rb6
-rw-r--r--config/initializers/rack_timeout.rb2
-rw-r--r--config/puma.example.development.rb1
-rw-r--r--config/routes.rb2
-rw-r--r--config/routes/project.rb2
-rw-r--r--danger/commit_messages/Dangerfile4
-rw-r--r--danger/gitlab_ui_wg/Dangerfile29
-rw-r--r--danger/roulette/Dangerfile18
-rw-r--r--danger/single_codebase/Dangerfile14
-rw-r--r--db/migrate/20190429082448_create_pages_domain_acme_orders.rb28
-rw-r--r--db/migrate/20190524071727_add_ssl_valid_period_to_pages_domain.rb16
-rw-r--r--db/migrate/20190530042141_add_default_git_depth_to_ci_cd_settings.rb11
-rw-r--r--db/migrate/20190604184643_fix_pool_repository_source_project_id.rb19
-rw-r--r--db/migrate/20190605104727_add_default_project_deletion_protection_to_application_settings.rb20
-rw-r--r--db/migrate/20190606014128_add_last_ci_minutes_notification_at_to_namespaces.rb12
-rw-r--r--db/migrate/20190611161641_add_target_project_id_to_merge_trains.rb14
-rw-r--r--db/post_migrate/20190524073827_schedule_fill_valid_time_for_pages_domain_certificates.rb34
-rw-r--r--db/post_migrate/20190528180441_enqueue_reset_merge_status.rb15
-rw-r--r--db/schema.rb26
-rw-r--r--doc/administration/auth/oidc.md58
-rw-r--r--doc/administration/high_availability/README.md3
-rw-r--r--doc/administration/high_availability/nfs.md8
-rw-r--r--doc/administration/index.md2
-rw-r--r--doc/administration/logs.md2
-rw-r--r--doc/administration/monitoring/performance/index.md2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md1
-rw-r--r--doc/administration/repository_storage_types.md5
-rw-r--r--doc/api/issues.md50
-rw-r--r--doc/api/merge_requests.md94
-rw-r--r--doc/api/projects.md8
-rw-r--r--doc/ci/README.md60
-rw-r--r--doc/ci/caching/index.md24
-rw-r--r--doc/ci/chatops/README.md27
-rw-r--r--doc/ci/ci_cd_for_external_repos/bitbucket_integration.md49
-rw-r--r--doc/ci/ci_cd_for_external_repos/github_integration.md64
-rw-r--r--doc/ci/ci_cd_for_external_repos/index.md26
-rw-r--r--doc/ci/docker/README.md4
-rw-r--r--doc/ci/docker/using_docker_build.md44
-rw-r--r--doc/ci/docker/using_docker_images.md158
-rw-r--r--doc/ci/docker/using_kaniko.md42
-rw-r--r--doc/ci/enable_or_disable_ci.md59
-rw-r--r--doc/ci/environments.md10
-rw-r--r--doc/ci/environments/protected_environments.md16
-rw-r--r--doc/ci/examples/README.md73
-rw-r--r--doc/ci/examples/artifactory_and_gitlab/index.md7
-rw-r--r--doc/ci/examples/browser_performance.md28
-rw-r--r--doc/ci/examples/code_climate.md3
-rw-r--r--doc/ci/examples/code_quality.md7
-rw-r--r--doc/ci/examples/container_scanning.md4
-rw-r--r--doc/ci/examples/dast.md4
-rw-r--r--doc/ci/examples/dependency_scanning.md4
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md9
-rw-r--r--doc/ci/examples/deployment/README.md14
-rw-r--r--doc/ci/examples/deployment/composer-npm-deploy.md24
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md3
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md5
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/index.md1
-rw-r--r--doc/ci/examples/license_management.md4
-rw-r--r--doc/ci/examples/php.md12
-rw-r--r--doc/ci/examples/sast.md4
-rw-r--r--doc/ci/examples/sast_docker.md4
-rw-r--r--doc/ci/examples/test-and-deploy-python-application-to-heroku.md19
-rw-r--r--doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md17
-rw-r--r--doc/ci/examples/test-clojure-application.md20
-rw-r--r--doc/ci/examples/test-scala-application.md21
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md7
-rw-r--r--doc/ci/introduction/index.md21
-rw-r--r--doc/ci/metrics_reports.md9
-rw-r--r--doc/ci/multi_project_pipelines.md8
-rw-r--r--doc/ci/variables/README.md2
-rw-r--r--doc/ci/variables/where_variables_can_be_used.md8
-rw-r--r--doc/ci/yaml/README.md34
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/architecture.md23
-rw-r--r--doc/development/chatops_on_gitlabcom.md21
-rw-r--r--doc/development/fe_guide/frontend_faq.md14
-rw-r--r--doc/development/fe_guide/graphql.md6
-rw-r--r--doc/development/feature_flags.md2
-rw-r--r--doc/development/git_object_deduplication.md109
-rw-r--r--doc/development/gitaly.md2
-rw-r--r--doc/development/i18n/externalization.md4
-rw-r--r--doc/development/i18n/proofreader.md2
-rw-r--r--doc/development/img/architecture_simplified.pngbin0 -> 61590 bytes
-rw-r--r--doc/development/new_fe_guide/development/accessibility.md1
-rw-r--r--doc/development/testing_guide/best_practices.md2
-rw-r--r--doc/development/testing_guide/end_to_end/dynamic_element_validation.md113
-rw-r--r--doc/development/testing_guide/end_to_end/page_objects.md39
-rw-r--r--doc/development/testing_guide/end_to_end/quick_start_guide.md44
-rw-r--r--doc/development/testing_guide/review_apps.md76
-rw-r--r--doc/development/understanding_explain_plans.md7
-rw-r--r--doc/gitlab-basics/README.md1
-rw-r--r--doc/install/installation.md8
-rw-r--r--doc/install/requirements.md4
-rw-r--r--doc/integration/elasticsearch.md140
-rw-r--r--doc/integration/jenkins.md5
-rw-r--r--doc/migrate_ci_to_ce/README.md19
-rw-r--r--doc/policy/maintenance.md25
-rw-r--r--doc/public_access/public_access.md53
-rw-r--r--doc/push_rules/push_rules.md29
-rw-r--r--doc/security/README.md1
-rw-r--r--doc/security/crime_vulnerability.md36
-rw-r--r--doc/security/information_exclusivity.md41
-rw-r--r--doc/security/password_length_limits.md21
-rw-r--r--doc/security/rack_attack.md26
-rw-r--r--doc/security/reset_root_password.md19
-rw-r--r--doc/security/ssh_keys_restrictions.md15
-rw-r--r--doc/security/two_factor_authentication.md15
-rw-r--r--doc/security/unlock_user.md20
-rw-r--r--doc/security/user_email_confirmation.md22
-rw-r--r--doc/security/user_file_uploads.md35
-rw-r--r--doc/security/webhooks.md52
-rw-r--r--doc/ssh/README.md14
-rw-r--r--doc/subscriptions/index.md27
-rw-r--r--doc/system_hooks/system_hooks.md24
-rw-r--r--doc/tools/email.md34
-rw-r--r--doc/topics/autodevops/index.md133
-rw-r--r--doc/topics/autodevops/quick_start_guide.md2
-rw-r--r--doc/topics/git/index.md2
-rw-r--r--doc/university/README.md88
-rw-r--r--doc/university/bookclub/booklist.md107
-rw-r--r--doc/university/bookclub/index.md11
-rw-r--r--doc/university/glossary/README.md707
-rw-r--r--doc/university/high-availability/aws/README.md393
-rw-r--r--doc/university/high-availability/aws/img/auto-scaling-det.pngbin29967 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/db-subnet-group.pngbin29298 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/ec-subnet.pngbin28405 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/ig-rt.pngbin12547 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/ig.pngbin8140 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/instance_specs.pngbin11522 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/new_vpc.pngbin15691 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/policies.pngbin39723 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/rds-net-opt.pngbin16340 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/rds-sec-group.pngbin11579 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/redis-cluster-det.pngbin23761 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/redis-net.pngbin27261 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/reference-arch2.pngbin53510 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/route_table.pngbin12088 -> 0 bytes
-rw-r--r--doc/university/high-availability/aws/img/subnet.pngbin17070 -> 0 bytes
-rw-r--r--doc/university/support/README.md95
-rw-r--r--doc/university/training/gitlab_flow.md15
-rw-r--r--doc/university/training/index.md32
-rw-r--r--doc/university/training/topics/additional_resources.md12
-rw-r--r--doc/university/training/topics/agile_git.md9
-rw-r--r--doc/university/training/topics/bisect.md16
-rw-r--r--doc/university/training/topics/cherry_picking.md6
-rw-r--r--doc/university/training/topics/env_setup.md5
-rw-r--r--doc/university/training/topics/explore_gitlab.md2
-rw-r--r--doc/university/training/topics/feature_branching.md8
-rw-r--r--doc/university/training/topics/getting_started.md21
-rw-r--r--doc/university/training/topics/git_add.md4
-rw-r--r--doc/university/training/topics/git_intro.md4
-rw-r--r--doc/university/training/topics/git_log.md20
-rw-r--r--doc/university/training/topics/merge_conflicts.md11
-rw-r--r--doc/university/training/topics/merge_requests.md8
-rw-r--r--doc/university/training/topics/rollback_commits.md22
-rw-r--r--doc/university/training/topics/stash.md22
-rw-r--r--doc/university/training/topics/subtree.md20
-rw-r--r--doc/university/training/topics/tags.md21
-rw-r--r--doc/university/training/topics/unstage.md8
-rw-r--r--doc/university/training/user_training.md80
-rw-r--r--doc/update/upgrading_from_source.md68
-rw-r--r--doc/user/admin_area/index.md25
-rw-r--r--doc/user/admin_area/license.md12
-rw-r--r--doc/user/admin_area/monitoring/health_check.md2
-rw-r--r--doc/user/admin_area/settings/account_and_limit_settings.md8
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md10
-rw-r--r--doc/user/admin_area/settings/index.md12
-rw-r--r--doc/user/admin_area/settings/visibility_and_access_controls.md46
-rw-r--r--doc/user/application_security/container_scanning/index.md2
-rw-r--r--doc/user/application_security/dast/index.md2
-rw-r--r--doc/user/application_security/dependency_scanning/index.md167
-rw-r--r--doc/user/application_security/img/dismissed_info.pngbin0 -> 20244 bytes
-rw-r--r--doc/user/application_security/img/interactive_reports.pngbin23190 -> 93910 bytes
-rw-r--r--doc/user/application_security/index.md10
-rw-r--r--doc/user/application_security/license_management/index.md2
-rw-r--r--doc/user/application_security/sast/index.md116
-rw-r--r--doc/user/discussions/index.md2
-rw-r--r--doc/user/group/clusters/index.md14
-rw-r--r--doc/user/group/index.md14
-rw-r--r--doc/user/group/insights/index.md6
-rw-r--r--doc/user/group/saml_sso/scim_setup.md20
-rw-r--r--doc/user/group/subgroups/index.md57
-rw-r--r--doc/user/img/markdown_inline_diffs_tags_rendered.pngbin0 -> 1804 bytes
-rw-r--r--doc/user/instance_statistics/convdev.md2
-rw-r--r--doc/user/markdown.md6
-rw-r--r--doc/user/permissions.md31
-rw-r--r--doc/user/profile/account/delete_account.md2
-rw-r--r--doc/user/project/clusters/serverless/index.md2
-rw-r--r--doc/user/project/cycle_analytics.md18
-rw-r--r--doc/user/project/import/github.md2
-rw-r--r--doc/user/project/index.md1
-rw-r--r--doc/user/project/insights/index.md6
-rw-r--r--doc/user/project/integrations/img/jira_add_user_to_group.pngbin24838 -> 266180 bytes
-rw-r--r--doc/user/project/integrations/img/jira_added_user_to_group.pngbin0 -> 82473 bytes
-rw-r--r--doc/user/project/integrations/img/jira_create_new_group.pngbin19127 -> 262453 bytes
-rw-r--r--doc/user/project/integrations/img/jira_create_new_user.pngbin12625 -> 173516 bytes
-rw-r--r--doc/user/project/integrations/img/jira_group_access.pngbin19147 -> 112706 bytes
-rw-r--r--doc/user/project/integrations/img/jira_user_management_link.pngbin23906 -> 206155 bytes
-rw-r--r--doc/user/project/integrations/jira.md6
-rw-r--r--doc/user/project/integrations/jira_server_configuration.md45
-rw-r--r--doc/user/project/integrations/webhooks.md28
-rw-r--r--doc/user/project/integrations/youtrack.md4
-rw-r--r--doc/user/project/issues/img/comment-or-discussion.pngbin0 -> 8378 bytes
-rw-r--r--doc/user/project/issues/img/create_mr_from_issue.pngbin0 -> 21286 bytes
-rw-r--r--doc/user/project/issues/img/issues_main_view_numbered.jpgbin205803 -> 0 bytes
-rw-r--r--doc/user/project/issues/img/issues_main_view_numbered.pngbin0 -> 278285 bytes
-rw-r--r--doc/user/project/issues/img/reopen-issue.pngbin0 -> 2522 bytes
-rw-r--r--doc/user/project/issues/img/report-abuse.pngbin0 -> 1620 bytes
-rw-r--r--doc/user/project/issues/img/show-all-activity.pngbin0 -> 8119 bytes
-rw-r--r--doc/user/project/issues/index.md6
-rw-r--r--doc/user/project/issues/issue_data_and_actions.md260
-rw-r--r--doc/user/project/issues/moving_issues.md25
-rw-r--r--doc/user/project/labels.md14
-rw-r--r--doc/user/project/merge_requests/allow_collaboration.md16
-rw-r--r--doc/user/project/merge_requests/authorization_for_merge_requests.md18
-rw-r--r--doc/user/project/merge_requests/browser_performance_testing.md20
-rw-r--r--doc/user/project/merge_requests/cherry_pick_changes.md26
-rw-r--r--doc/user/project/merge_requests/code_quality.md24
-rw-r--r--doc/user/project/merge_requests/fast_forward_merge.md35
-rw-r--r--doc/user/project/merge_requests/index.md10
-rw-r--r--doc/user/project/merge_requests/merge_request_approvals.md42
-rw-r--r--doc/user/project/merge_requests/merge_when_pipeline_succeeds.md23
-rw-r--r--doc/user/project/merge_requests/resolve_conflicts.md16
-rw-r--r--doc/user/project/merge_requests/revert_changes.md29
-rw-r--r--doc/user/project/merge_requests/squash_and_merge.md45
-rw-r--r--doc/user/project/merge_requests/versions.md34
-rw-r--r--doc/user/project/merge_requests/work_in_progress_merge_requests.md24
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md9
-rw-r--r--doc/user/project/operations/img/external_dashboard_link.pngbin0 -> 63282 bytes
-rw-r--r--doc/user/project/operations/img/external_dashboard_settings.pngbin0 -> 103474 bytes
-rw-r--r--doc/user/project/operations/index.md1
-rw-r--r--doc/user/project/operations/linking_to_an_external_dashboard.md19
-rw-r--r--doc/user/project/pipelines/settings.md16
-rw-r--r--doc/user/project/settings/index.md6
-rw-r--r--doc/workflow/gitlab_flow.md2
-rw-r--r--lib/api/entities.rb8
-rw-r--r--lib/api/helpers/pagination.rb2
-rw-r--r--lib/api/helpers/projects_helpers.rb1
-rw-r--r--lib/api/issues.rb11
-rw-r--r--lib/api/merge_requests.rb24
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb2
-rw-r--r--lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb40
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/matches.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexer.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/parser.rb2
-rw-r--r--lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml16
-rw-r--r--lib/gitlab/ci/templates/Serverless.gitlab-ci.yml18
-rw-r--r--lib/gitlab/cluster/puma_worker_killer_initializer.rb3
-rw-r--r--lib/gitlab/cluster/puma_worker_killer_observer.rb24
-rw-r--r--lib/gitlab/cluster/rack_timeout_observer.rb51
-rw-r--r--lib/gitlab/danger/helper.rb8
-rw-r--r--lib/gitlab/danger/roulette.rb20
-rw-r--r--lib/gitlab/danger/teammate.rb27
-rw-r--r--lib/gitlab/data_builder/note.rb1
-rw-r--r--lib/gitlab/diff/suggestions_parser.rb6
-rw-r--r--lib/gitlab/gitaly_client/conflicts_service.rb6
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb2
-rw-r--r--lib/gitlab/graphql/loaders/batch_lfs_oid_loader.rb25
-rw-r--r--lib/gitlab/hook_data/issue_builder.rb3
-rw-r--r--lib/gitlab/import_export/import_export.yml3
-rw-r--r--lib/gitlab/import_export/relation_factory.rb3
-rw-r--r--lib/gitlab/lets_encrypt/challenge.rb2
-rw-r--r--lib/gitlab/lets_encrypt/order.rb11
-rw-r--r--lib/gitlab/metrics/dashboard/base_service.rb24
-rw-r--r--lib/gitlab/metrics/dashboard/cache.rb44
-rw-r--r--lib/gitlab/metrics/dashboard/finder.rb2
-rw-r--r--lib/gitlab/metrics/dashboard/processor.rb2
-rw-r--r--lib/gitlab/metrics/dashboard/project_dashboard_service.rb12
-rw-r--r--lib/gitlab/metrics/dashboard/stages/base_stage.rb9
-rw-r--r--lib/gitlab/metrics/dashboard/stages/endpoint_inserter.rb42
-rw-r--r--lib/gitlab/metrics/samplers/ruby_sampler.rb2
-rw-r--r--lib/gitlab/performance_bar/peek_query_tracker.rb7
-rw-r--r--lib/gitlab/rack_timeout_observer.rb46
-rw-r--r--lib/gitlab/search_results.rb4
-rw-r--r--lib/gitlab/setup_helper.rb8
-rw-r--r--lib/tasks/gettext.rake1
-rw-r--r--locale/gitlab.pot204
-rw-r--r--package.json1
-rw-r--r--qa/qa/ce/strategy.rb11
-rw-r--r--qa/qa/page/project/issue/show.rb3
-rw-r--r--qa/qa/page/project/operations/kubernetes/show.rb4
-rw-r--r--qa/qa/resource/repository/project_push.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb1
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb5
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb3
-rw-r--r--spec/controllers/acme_challenges_controller_spec.rb44
-rw-r--r--spec/controllers/import/fogbugz_controller_spec.rb38
-rw-r--r--spec/controllers/projects/environments/prometheus_api_controller_spec.rb4
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb14
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb26
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb15
-rw-r--r--spec/factories/pages_domain_acme_orders.rb17
-rw-r--r--spec/features/admin/admin_appearance_spec.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb8
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb2
-rw-r--r--spec/features/commits_spec.rb6
-rw-r--r--spec/features/groups/merge_requests_spec.rb2
-rw-r--r--spec/features/ics/dashboard_issues_spec.rb2
-rw-r--r--spec/features/ics/group_issues_spec.rb2
-rw-r--r--spec/features/ics/project_issues_spec.rb2
-rw-r--r--spec/features/instance_statistics/conversational_development_index_spec.rb2
-rw-r--r--spec/features/issues_spec.rb4
-rw-r--r--spec/features/merge_request/user_accepts_merge_request_spec.rb3
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_merges_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb3
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb4
-rw-r--r--spec/features/merge_request/user_suggests_changes_on_diff_spec.rb4
-rw-r--r--spec/features/merge_requests/user_lists_merge_requests_spec.rb2
-rw-r--r--spec/features/project_variables_spec.rb2
-rw-r--r--spec/features/projects/clusters_spec.rb2
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb2
-rw-r--r--spec/features/projects/features_visibility_spec.rb3
-rw-r--r--spec/features/projects/files/undo_template_spec.rb2
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb1
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb2
-rw-r--r--spec/features/projects/labels/user_removes_labels_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb4
-rw-r--r--spec/features/projects/settings/integration_settings_spec.rb2
-rw-r--r--spec/features/projects/tree/upload_file_spec.rb2
-rw-r--r--spec/features/projects_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_commits_spec.rb2
-rw-r--r--spec/features/security/profile_access_spec.rb2
-rw-r--r--spec/features/tags/master_creates_tag_spec.rb2
-rw-r--r--spec/finders/issues_finder_spec.rb8
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json6
-rw-r--r--spec/frontend/boards/modal_store_spec.js4
-rw-r--r--spec/frontend/clusters/clusters_bundle_spec.js33
-rw-r--r--spec/frontend/clusters/components/application_row_spec.js76
-rw-r--r--spec/frontend/clusters/services/application_state_machine_spec.js2
-rw-r--r--spec/frontend/clusters/stores/clusters_store_spec.js3
-rw-r--r--spec/frontend/helpers/timeout.js16
-rw-r--r--spec/frontend/ide/components/ide_status_list_spec.js91
-rw-r--r--spec/frontend/lib/utils/number_utility_spec.js11
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js44
-rw-r--r--spec/frontend/reports/components/report_section_spec.js40
-rw-r--r--spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap3
-rw-r--r--spec/frontend/repository/components/table/row_spec.js44
-rw-r--r--spec/frontend/test_setup.js2
-rw-r--r--spec/frontend/vue_shared/components/issue/issue_warning_spec.js18
-rw-r--r--spec/frontend/vue_shared/components/modal_copy_button_spec.js40
-rw-r--r--spec/graphql/resolvers/base_resolver_spec.rb22
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/namespace_projects_resolver_spec.rb2
-rw-r--r--spec/graphql/types/base_field_spec.rb29
-rw-r--r--spec/graphql/types/tree/blob_type_spec.rb2
-rw-r--r--spec/javascripts/boards/board_card_spec.js4
-rw-r--r--spec/javascripts/boards/boards_store_spec.js4
-rw-r--r--spec/javascripts/boards/issue_card_spec.js4
-rw-r--r--spec/javascripts/boards/issue_spec.js4
-rw-r--r--spec/javascripts/boards/list_spec.js4
-rw-r--r--spec/javascripts/ci_variable_list/ajax_variable_list_spec.js8
-rw-r--r--spec/javascripts/ci_variable_list/ci_variable_list_spec.js59
-rw-r--r--spec/javascripts/diffs/components/commit_item_spec.js2
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js16
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js5
-rw-r--r--spec/javascripts/monitoring/charts/area_spec.js14
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js105
-rw-r--r--spec/javascripts/monitoring/mock_data.js65
-rw-r--r--spec/javascripts/monitoring/store/actions_spec.js166
-rw-r--r--spec/javascripts/monitoring/store/mutations_spec.js77
-rw-r--r--spec/javascripts/notes/components/note_actions_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js (renamed from spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js)62
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js75
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js136
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js7
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js54
-rw-r--r--spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js42
-rw-r--r--spec/javascripts/vue_shared/components/pagination/graphql_pagination_spec.js70
-rw-r--r--spec/javascripts/vue_shared/components/table_pagination_spec.js2
-rw-r--r--spec/lib/api/helpers/pagination_spec.rb34
-rw-r--r--spec/lib/banzai/filter/external_issue_reference_filter_spec.rb7
-rw-r--r--spec/lib/banzai/pipeline/gfm_pipeline_spec.rb23
-rw-r--r--spec/lib/gitlab/background_migration/delete_diff_files_spec.rb6
-rw-r--r--spec/lib/gitlab/background_migration/populate_external_pipeline_source_spec.rb3
-rw-r--r--spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb27
-rw-r--r--spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb (renamed from spec/lib/gitlab/rack_timeout_observer_spec.rb)25
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb10
-rw-r--r--spec/lib/gitlab/danger/roulette_spec.rb43
-rw-r--r--spec/lib/gitlab/danger/teammate_spec.rb47
-rw-r--r--spec/lib/gitlab/data_builder/note_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb21
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb18
-rw-r--r--spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb23
-rw-r--r--spec/lib/gitlab/import_export/project.json5
-rw-r--r--spec/lib/gitlab/lets_encrypt/challenge_spec.rb18
-rw-r--r--spec/lib/gitlab/lets_encrypt/order_spec.rb38
-rw-r--r--spec/lib/gitlab/metrics/dashboard/finder_spec.rb9
-rw-r--r--spec/lib/gitlab/metrics/dashboard/processor_spec.rb28
-rw-r--r--spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb2
-rw-r--r--spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb7
-rw-r--r--spec/migrations/enqueue_reset_merge_status_spec.rb7
-rw-r--r--spec/migrations/enqueue_verify_pages_domain_workers_spec.rb6
-rw-r--r--spec/migrations/fix_pool_repository_source_project_id_spec.rb29
-rw-r--r--spec/migrations/remove_orphaned_label_links_spec.rb6
-rw-r--r--spec/migrations/schedule_fill_valid_time_for_pages_domain_certificates_spec.rb46
-rw-r--r--spec/models/broadcast_message_spec.rb8
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb13
-rw-r--r--spec/models/merge_request_spec.rb103
-rw-r--r--spec/models/notification_recipient_spec.rb233
-rw-r--r--spec/models/pages_domain_acme_order_spec.rb49
-rw-r--r--spec/models/pages_domain_spec.rb11
-rw-r--r--spec/models/project_ci_cd_setting_spec.rb40
-rw-r--r--spec/models/project_statistics_spec.rb12
-rw-r--r--spec/policies/project_policy_spec.rb2
-rw-r--r--spec/presenters/ci/build_runner_presenter_spec.rb64
-rw-r--r--spec/rack_servers/puma_spec.rb2
-rw-r--r--spec/requests/api/issues/issues_spec.rb20
-rw-r--r--spec/requests/api/merge_requests_spec.rb61
-rw-r--r--spec/requests/api/projects_spec.rb2
-rw-r--r--spec/requests/api/runner_spec.rb16
-rw-r--r--spec/requests/api/task_completion_status_spec.rb85
-rw-r--r--spec/services/auto_merge/base_service_spec.rb178
-rw-r--r--spec/services/auto_merge_service_spec.rb28
-rw-r--r--spec/services/ci/pipeline_schedule_service_spec.rb8
-rw-r--r--spec/services/git/branch_hooks_service_spec.rb4
-rw-r--r--spec/services/merge_requests/merge_to_ref_service_spec.rb41
-rw-r--r--spec/services/merge_requests/mergeability_check_service_spec.rb187
-rw-r--r--spec/services/merge_requests/update_service_spec.rb12
-rw-r--r--spec/services/pages_domains/create_acme_order_service_spec.rb63
-rw-r--r--spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb146
-rw-r--r--spec/services/preview_markdown_service_spec.rb4
-rw-r--r--spec/services/projects/fork_service_spec.rb24
-rw-r--r--spec/services/service_response_spec.rb16
-rw-r--r--spec/services/suggestions/create_service_spec.rb2
-rw-r--r--spec/support/capybara.rb5
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb2
-rw-r--r--spec/support/features/reportable_note_shared_examples.rb4
-rw-r--r--spec/support/helpers/lets_encrypt_helpers.rb40
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb (renamed from spec/support/shared_context/policies/project_policy_shared_context.rb)2
-rw-r--r--spec/support/shared_examples/finders/assignees_filter_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/shrug_quick_action_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/tableflip_quick_action_shared_examples.rb2
-rw-r--r--spec/views/notify/pipeline_failed_email.html.haml_spec.rb4
-rw-r--r--spec/views/notify/pipeline_failed_email.text.erb_spec.rb2
-rw-r--r--spec/views/notify/pipeline_success_email.html.haml_spec.rb4
-rw-r--r--spec/views/profiles/show.html.haml_spec.rb4
-rw-r--r--spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb8
-rw-r--r--vendor/assets/javascripts/snowplow/sp.js22
-rw-r--r--yarn.lock12
743 files changed, 9175 insertions, 4661 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 98a497aa12a..2b881d5f201 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -8,11 +8,13 @@ globals:
plugins:
- import
- html
+ - "@gitlab/i18n"
settings:
import/resolver:
webpack:
config: './config/webpack.config.js'
rules:
+ "@gitlab/i18n/no-non-i18n-strings": error
import/no-commonjs: error
no-underscore-dangle:
- error
@@ -31,3 +33,11 @@ rules:
vue/no-use-v-if-with-v-for: off
vue/no-v-html: off
vue/use-v-on-exact: off
+overrides:
+ files:
+ # Vue is temporarily being disabled until the autofix errors are resolved
+ # Follow up issue https://gitlab.com/gitlab-org/gitlab-ce/issues/57969
+ - '*.vue'
+ - '**/spec/**/*'
+ rules:
+ "@gitlab/i18n/no-non-i18n-strings": off
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index d49fa4a49a0..63ac5a408bd 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -3,7 +3,7 @@
*.rake @ashmckenzie @ayufan @dbalexandre @DouweM @dzaporozhets @godfat @grzesiek @mkozono @mayra-cabrera @nick.thomas @rspeicher @rymai @reprazent @smcgivern @tkuah
# Technical writing team are the default reviewers for everything in `doc/`
-/doc/ @axil @marcia
+/doc/ @axil @marcia @eread
# Frontend maintainers should see everything in `app/assets/`
app/assets/ @ClemMakesApps @fatihacet @filipa @iamphill @mikegreiling @timzallmann @kushalpandya
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd9c4df2f3f..623e5d0acb9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.11.3 (2019-06-10)
+
+### Fixed (5 changes)
+
+- Fix invalid visibility string comparison in project import. !28612
+- Remove a default git depth in Pipelines for merge requests. !28926
+- Fix connection to Tiller error while uninstalling. !29131
+- Fix label click scrolling to top. !29202
+- Make OpenID Connect work without requiring a name. !29312
+
+
## 11.11.2 (2019-06-04)
### Fixed (7 changes)
@@ -649,7 +660,7 @@ entry.
- Forbid creating discussions for users with restricted access.
- Fix leaking private repository information in API.
- Fixed ability to see private groups by users not belonging to given group.
-- Prevent releases links API to leak tag existance.
+- Prevent releases links API to leak tag existence.
- Display the correct number of MRs a user has access to.
- Block local URLs for Kubernetes integration.
- Fix arbitrary file read via diffs during import.
@@ -1008,7 +1019,7 @@ entry.
- Display SAML failure messages instead of expecting CSRF token. !24509
- Adjust vertical alignment for project visibility icons. !24511 (Martin Hobert)
- Load initUserInternalRegexPlaceholder only when required. !24522
-- Hashed Storage: `AfterRenameService` was receiving the wrong `old_path` under some circunstances. !24526
+- Hashed Storage: `AfterRenameService` was receiving the wrong `old_path` under some circumstances. !24526
- Resolve Runners IPv6 address overlaps other values. !24531
- Fix 404s with snippet uploads in object storage. !24550
- Fixed oversized custom project notification selector dropdown. !24557
@@ -1047,7 +1058,7 @@ entry.
- Update CI YAML param table with include.
- Return bottom border on MR Tabs.
- Fixes z-index and margins of archived alert in job page.
-- Fixes archived sticky top bar without perfomance bar.
+- Fixes archived sticky top bar without performance bar.
- Fixed rebase button not showing in merge request widget.
- Fixed double tooltips on note awards buttons.
- Allow suggestions to be copied and pasted as GFM.
@@ -1480,7 +1491,7 @@ entry.
- Fix deprecation: Using positional arguments in integration tests. !24009 (Jasper Maes)
- UI improvements for redesigned project lists. !24011
- Update cert-manager chart from v0.5.0 to v0.5.2. !24025 (Takuya Noguchi)
-- Hide spinner on empty activites list on user profile overview. !24063
+- Hide spinner on empty activities list on user profile overview. !24063
- Don't show Auto DevOps enabled banner for projects with CI file or CI disabled. !24067
- Update GitLab Runner Helm Chart to 0.1.43. !24083
- Fix navigation style in docs. !24090 (Takuya Noguchi)
@@ -2009,7 +2020,7 @@ entry.
- Hide all tables on Pipeline when no Jobs for the Pipeline. !18540 (Takuya Noguchi)
- Fixing count on Milestones. !21446
-- Use case insensitve username lookups. !21728 (William George)
+- Use case insensitive username lookups. !21728 (William George)
- Correctly process Bamboo API result array. !21970 (Alex Lossent)
- Fix 'merged with' UI being displayed when merge request has no merge commit. !22022
- Fix broken file name navigation on MRs. !22109
@@ -2792,7 +2803,7 @@ entry.
- Fixes SVGs for empty states in job page overflowing on mobile.
- Fix checkboxes on runner admin settings - The labels are now clickable.
- Fixed IDE file row scrolling into view when hovering.
-- Accept upload files in public/uplaods/tmp when using accelerated uploads.
+- Accept upload files in public/uploads/tmp when using accelerated uploads.
- Include correct CSS file for xterm in environments page.
- Increase padding in code blocks.
- Fix: Project deletion may not log audit events during user deletion.
@@ -3678,7 +3689,7 @@ entry.
### Fixed (69 changes, 23 of them are from the community)
-- Optimize the upload migration proces. !15947
+- Optimize the upload migration process. !15947
- Import bitbucket issues that are reported by an anonymous user. !18199 (bartl)
- Fix an issue where the notification email address would be set to an unconfirmed email address. !18474
- Stop logging email information when emails are disabled. !18521 (Marc Shaw)
@@ -3806,7 +3817,7 @@ entry.
- Add a cronworker to rescue stale live traces. !18680
- Move SquashBeforeMerge vue component. !18813 (George Tsiolis)
- Add index on runner_type for ci_runners. !18897
-- Fix CarrierWave reads local files into memoery when migrates to ObjectStorage. !19102
+- Fix CarrierWave reads local files into memory when migrates to ObjectStorage. !19102
- Remove double-checked internal id generation. !19181
- Throttle updates to Project#last_repository_updated_at. !19183
- Add background migrations for archiving legacy job traces. !19194
@@ -3897,7 +3908,7 @@ entry.
- Adjust SQL and transaction Prometheus buckets.
- Adding branches through the WebUI is handled by Gitaly.
- Remove shellout implementation for Repository checksums.
-- Refs containting sha checks are done by Gitaly.
+- Refs containing sha checks are done by Gitaly.
- Finding a wiki page is done by Gitaly by default.
- Workhorse will use Gitaly to create archives.
- Workhorse to send raw diff and patch for commits.
@@ -4101,7 +4112,7 @@ entry.
- Display active sessions and allow the user to revoke any of it. !17867 (Alexis Reigel)
- Add cron job to email users on issue due date. !17985 (Stuart Nelson)
- Rubocop rule to avoid returning from a block. !18000 (Jacopo Beschi @jacopo-beschi)
-- Add the signature verfication badge to the compare view. !18245 (Marc Shaw)
+- Add the signature verification badge to the compare view. !18245 (Marc Shaw)
- Expose Deploy Token data as environment varialbes on CI/CD jobs. !18414
- Show group id in group settings. !18482 (George Tsiolis)
- Allow admins to enforce accepting Terms of Service on an instance. !18570
@@ -4839,7 +4850,7 @@ entry.
- Override group sidebar links. !16942 (George Tsiolis)
- Avoid running `PopulateForkNetworksRange`-migration multiple times. !16988
- Resolve PrepareUntrackedUploads PostgreSQL syntax error. !17019
-- Fix monaco editor features which were incompatable with GitLab CDN settings. !17021
+- Fix monaco editor features which were incompatible with GitLab CDN settings. !17021
- Fixed error 500 when removing an identity with synced attributes and visiting the profile page. !17054
- Fix cnacel edit note button reverting changes. !42462
- For issues display time of last edit of title or description instead of time of any attribute change.
@@ -5056,7 +5067,7 @@ entry.
### Performance (2 changes)
- rework indexes on redirect_routes.
-- Remove unecessary query from labels filter.
+- Remove unnecessary query from labels filter.
## 10.4.0 (2018-01-22)
@@ -5187,7 +5198,7 @@ entry.
- Add a gitlab:tcp_check rake task. !15759
- add support for sorting in tags api. !15772 (haseebeqx)
- Add Prometheus to available Cluster applications. !15895
-- Validate file status when commiting multiple files. !15922
+- Validate file status when committing multiple files. !15922
- List of avatars should never show +1. !15972 (Jacopo Beschi @jacopo-beschi)
- Do not generate NPM links for private NPM modules in blob view. !16002 (Mario de la Ossa)
- Backport fast database lookup of SSH authorized_keys from EE. !16014
@@ -5218,7 +5229,7 @@ entry.
- Fix web ide user preferences copy and buttons. !41789
- Update redis-rack to 2.0.4.
- Import some code and functionality from gitlab-shell to improve subprocess handling.
-- Update Browse file to Choose file in all occurences.
+- Update Browse file to Choose file in all occurrences.
- Bump mysql2 gem version from 0.4.5 to 0.4.10. (asaparov)
- Use a background migration for issues.closed_at.
@@ -5353,7 +5364,7 @@ entry.
- Fix graph notes number duplication. !15696 (Vladislav Kaverin)
- Fix updateEndpoint undefined error for issue_show app root. !15698
- Change boards page boards_data absolute urls to paths. !15703
-- Using appropiate services in the API for managing forks. !15709
+- Using appropriate services in the API for managing forks. !15709
- Confirming email with invalid token should no longer generate an error. !15726
- fix #39233 - 500 in merge request. !15774 (Martin Nowak)
- Use Markdown styling for new project guidelines. !15785 (Markus Koller)
@@ -5463,7 +5474,7 @@ entry.
- Stop reloading the page when using pagination and tabs - use API calls - in Pipelines table.
- Clean up schema of the "issues" table.
- Clarify wording of protected branch settings for the default branch.
-- Update svg external depencency.
+- Update svg external dependency.
- Clean up schema of the "merge_requests" table.
@@ -5633,7 +5644,7 @@ entry.
- Fix gitlab:backup rake for hashed storage based repositories. !15400
- Fix issue where clicking a GPG verification badge would scroll to the top of the page. !15407
- Update container repository path reference and allow using double underscore. !15417
-- Fix crash when navigating to second page of the group dashbaord when there are projects and groups on the first page. !15456
+- Fix crash when navigating to second page of the group dashboard when there are projects and groups on the first page. !15456
- Fix flash errors showing up on a non configured prometheus integration. !35652
- Fix timezone bug in Pikaday and upgrade Pikaday version.
- Fix arguments Import/Export error importing project merge requests.
@@ -6183,7 +6194,7 @@ entry.
- [CHANGED] Fire hooks asynchronously when creating a new job to improve performance. !13734
- [CHANGED] Improve performance for AutocompleteController#users.json. !13754 (Hiroyuki Sato)
- [CHANGED] Update the GPG verification semantics: A GPG signature must additionally match the committer in order to be verified. !13771 (Alexis Reigel)
-- [CHANGED] Support a multi-word fuzzy seach issues/merge requests on search bar. !13780 (Hiroyuki Sato)
+- [CHANGED] Support a multi-word fuzzy search issues/merge requests on search bar. !13780 (Hiroyuki Sato)
- [CHANGED] Default LDAP config "verify_certificates" to true for security. !13915
- [CHANGED] "Share with group lock" now applies to subgroups, but owner can override setting on subgroups. !13944
- [CHANGED] Make Gitaly PostUploadPack mandatory. !13953
@@ -6882,7 +6893,7 @@ entry.
## 9.3.2 (2017-06-27)
-- API: Fix optional arugments for POST :id/variables. !12474
+- API: Fix optional arguments for POST :id/variables. !12474
- Bump premailer-rails gem to 1.9.7 and its dependencies to prevent network retrieval of assets.
## 9.3.1 (2017-06-26)
@@ -6899,7 +6910,7 @@ entry.
- Refactored gitlab:app:check into SystemCheck liberary and improve some checks. !9173
- Add an ability to cancel attaching file and redesign attaching files UI. !9431 (blackst0ne)
- Add Aliyun OSS as the backup storage provider. !9721 (Yuanfei Zhu)
-- Add suport for find_local_branches GRPC from Gitaly. !10059
+- Add support for find_local_branches GRPC from Gitaly. !10059
- Allow manual bypass of auto_sign_in_with_provider with a new param. !10187 (Maxime Besson)
- Redirect to user's keys index instead of user's index after a key is deleted in the admin. !10227 (Cyril Jouve)
- Changed Blame to Annotate in the UI to promote blameless culture. !10378 (Ilya Vassilevsky)
@@ -6978,7 +6989,7 @@ entry.
- Add tag_list param to project api. !11799 (Ivan Chernov)
- Add changelog for improved Registry description. !11816
- Automatically adjust project settings to match changes in project visibility. !11831
-- Add slugify project path to CI enviroment variables. !11838 (Ivan Chernov)
+- Add slugify project path to CI environment variables. !11838 (Ivan Chernov)
- Add all pipeline sources as special keywords to 'only' and 'except'. !11844 (Filip Krakowski)
- Allow pulling of container images using personal access tokens. !11845
- Expose import_status in Projects API. !11851 (Robin Bobbitt)
@@ -7893,7 +7904,7 @@ entry.
- Clean-up Project navigation order. !9272
- Add Runner's jobs v4 API. !9273
- Add pipeline trigger API with user permissions. !9277
-- Enhanced filter issues layout for better mobile experiance. !9280 (Pratik Borsadiya)
+- Enhanced filter issues layout for better mobile experience. !9280 (Pratik Borsadiya)
- Move babel config for instanbul to karma config. !9286 (winniehell)
- Document U2F limitations with multiple URLs. !9300
- Wrap long Project and Group titles. !9301
@@ -8074,7 +8085,7 @@ entry.
- Add badges to global dropdown.
- Changed coverage reg expression placeholder text to be more like a placeholder.
- Show members of parent groups on project members page.
-- Fix grammer issue in admin/runners.
+- Fix grammar issue in admin/runners.
- Allow slashes in slash command arguments.
- Adds paginationd and folders view to environments table.
- hide loading spinners for server-rendered sidebar fields.
@@ -8247,7 +8258,7 @@ entry.
- Allows to search within project by commit hash. (YarNayar)
- Show organisation membership and delete comment on smaller viewports, plus change comment author name to username.
- Remove turbolinks.
-- Convert pipeline action icons to svg to have them propperly positioned.
+- Convert pipeline action icons to svg to have them properly positioned.
- Remove rogue scrollbars for issue comments with inline elements.
- Align Segoe UI label text.
- Color + and - signs in diffs to increase code legibility.
@@ -8538,3 +8549,4 @@ entry.
## 8.15.8 through 0.8.0
- See [changelogs/archive.md](changelogs/archive.md)
+
diff --git a/GITLAB_ELASTICSEARCH_INDEXER_VERSION b/GITLAB_ELASTICSEARCH_INDEXER_VERSION
index 9084fa2f716..26aaba0e866 100644
--- a/GITLAB_ELASTICSEARCH_INDEXER_VERSION
+++ b/GITLAB_ELASTICSEARCH_INDEXER_VERSION
@@ -1 +1 @@
-1.1.0
+1.2.0
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index dc1e644a101..9c6d6293b1a 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.6.0
+1.6.1
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index deeb3d66ef0..b13d146a7b0 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-9.2.0
+9.3.0
diff --git a/Gemfile b/Gemfile
index 195d06502a4..375fcaf76ce 100644
--- a/Gemfile
+++ b/Gemfile
@@ -41,7 +41,7 @@ gem 'omniauth-shibboleth', '~> 1.3.0'
gem 'omniauth-twitter', '~> 1.4'
gem 'omniauth_crowd', '~> 2.2.0'
gem 'omniauth-authentiq', '~> 0.3.3'
-gem 'omniauth_openid_connect', '~> 0.3.0'
+gem 'omniauth_openid_connect', '~> 0.3.1'
gem "omniauth-ultraauth", '~> 0.0.2'
gem 'omniauth-salesforce', '~> 1.0.5'
gem 'rack-oauth2', '~> 1.9.3'
@@ -275,7 +275,7 @@ gem 'virtus', '~> 1.0.1'
gem 'base32', '~> 0.3.0'
# Sentry integration
-gem 'sentry-raven', '~> 2.7'
+gem 'sentry-raven', '~> 2.9'
gem 'premailer-rails', '~> 1.9.7'
@@ -336,7 +336,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.7.0'
gem 'factory_bot_rails', '~> 4.8.2'
gem 'rspec-rails', '~> 3.7.0'
- gem 'rspec-retry', '~> 0.4.5'
+ gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_profiling', '~> 0.0.5'
gem 'rspec-set', '~> 0.1.3'
gem 'rspec-parameterized', require: false
@@ -347,7 +347,7 @@ group :development, :test do
# Generate Fake data
gem 'ffaker', '~> 2.10'
- gem 'capybara', '~> 2.18.0'
+ gem 'capybara', '~> 3.22.0'
gem 'capybara-screenshot', '~> 1.0.22'
gem 'selenium-webdriver', '~> 3.141'
diff --git a/Gemfile.lock b/Gemfile.lock
index 11e71cfb80e..c403f45109c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -110,13 +110,14 @@ GEM
bundler (~> 1.2)
thor (~> 0.18)
byebug (9.1.0)
- capybara (2.18.0)
+ capybara (3.22.0)
addressable
mini_mime (>= 0.1.3)
- nokogiri (>= 1.3.3)
- rack (>= 1.0.0)
- rack-test (>= 0.5.4)
- xpath (>= 2.0, < 4.0)
+ nokogiri (~> 1.8)
+ rack (>= 1.6.0)
+ rack-test (>= 0.6.3)
+ regexp_parser (~> 1.5)
+ xpath (~> 3.2)
capybara-screenshot (1.0.22)
capybara (>= 1.0, < 4)
launchy
@@ -587,7 +588,7 @@ GEM
activesupport
nokogiri (>= 1.4.4)
omniauth (~> 1.0)
- omniauth_openid_connect (0.3.0)
+ omniauth_openid_connect (0.3.1)
addressable (~> 2.5)
omniauth (~> 1.3)
openid_connect (~> 1.1)
@@ -658,7 +659,7 @@ GEM
pry (~> 0.10)
pry-rails (0.3.6)
pry (>= 0.10.4)
- public_suffix (3.0.3)
+ public_suffix (3.1.0)
puma (3.12.0)
puma_worker_killer (0.1.0)
get_process_mem (~> 0.2)
@@ -750,7 +751,7 @@ GEM
redis-store (>= 1.2, < 2)
redis-store (1.6.0)
redis (>= 2.2, < 5)
- regexp_parser (1.4.0)
+ regexp_parser (1.5.1)
regexp_property_values (0.3.4)
representable (3.0.4)
declarative (< 0.1.0)
@@ -798,8 +799,8 @@ GEM
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-support (~> 3.7.0)
- rspec-retry (0.4.5)
- rspec-core
+ rspec-retry (0.6.1)
+ rspec-core (> 3.3)
rspec-set (0.1.3)
rspec-support (3.7.1)
rspec_junit_formatter (0.4.1)
@@ -1040,7 +1041,7 @@ DEPENDENCIES
browser (~> 2.5)
bullet (~> 5.5.0)
bundler-audit (~> 0.5.0)
- capybara (~> 2.18.0)
+ capybara (~> 3.22.0)
capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3)
charlock_holmes (~> 0.7.5)
@@ -1159,7 +1160,7 @@ DEPENDENCIES
omniauth-twitter (~> 1.4)
omniauth-ultraauth (~> 0.0.2)
omniauth_crowd (~> 2.2.0)
- omniauth_openid_connect (~> 0.3.0)
+ omniauth_openid_connect (~> 0.3.1)
org-ruby (~> 0.9.12)
peek (~> 1.0.1)
peek-gc (~> 0.0.2)
@@ -1199,7 +1200,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 3.7.0)
- rspec-retry (~> 0.4.5)
+ rspec-retry (~> 0.6.1)
rspec-set (~> 0.1.3)
rspec_junit_formatter
rspec_profiling (~> 0.0.5)
@@ -1217,7 +1218,7 @@ DEPENDENCIES
scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7)
selenium-webdriver (~> 3.141)
- sentry-raven (~> 2.7)
+ sentry-raven (~> 2.9)
settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6)
shoulda-matchers (~> 4.0.1)
diff --git a/PROCESS.md b/PROCESS.md
index 3c40f658070..9e971ef7531 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -64,7 +64,7 @@ The milestone of an issue that is currently being worked on by a community contr
should not be set to a named GitLab milestone (e.g. 11.7, 11.8), until the associated
merge request is very close to being merged, and we will likely know in which named
GitLab milestone the issue will land. There are many factors that influence when
-a community contributor finishes an issue, or even at all. So we should set this
+a community contributor finishes an issue, or even at all. So we should set this
milestone only when we have more certainty.
Note this only applies to issues currently assigned to community contributors. For
@@ -86,21 +86,12 @@ star, smile, etc.). Some good tips about code reviews can be found in our
## Feature freeze on the 7th for the release on the 22nd
-After 7th at 23:59 (Pacific Time Zone) of each month, stable branch and RC1
-of the upcoming release (to be shipped on the 22nd) is created and deployed to GitLab.com.
-The stable branch is frozen at the most recent "qualifying commit" on master.
-A "qualifying commit" is one that is pushed before the feature freeze cutoff time
-and that passes all CI jobs (green pipeline).
+The feature freeze on the 7th has been discontinued. [Transition period overview]
+describes the change to this process. During the transition period, the only guarantee that
+a change will be included in the release on the 22nd is if the change has been
+deployed to GitLab.com prior to this date.
-Merge requests may still be merged into master during this
-period, but they will go into the _next_ release, unless they are manually
-cherry-picked into the stable branch.
-
-By freezing the stable branches 2 weeks prior to a release, we reduce the risk
-of a last minute merge request potentially breaking things.
-
-Any release candidate that gets created after this date can become a final
-release, hence the name release candidate.
+[Transition period]: https://gitlab.com/gitlab-org/release/docs/blob/21cbd409dd5f157fe252f254f3e897f01908abe2/general/deploy/auto-deploy-transition.md#transition
### Feature flags
@@ -108,15 +99,15 @@ Merge requests that make changes hidden behind a feature flag, or remove an
existing feature flag because a feature is deemed stable, may be merged (and
picked into the stable branches) up to the 19th of the month. Such merge
requests should have the ~"feature flag" label assigned, and don't require a
-corresponding exception request to be created.
+corresponding exception request to be created.
-A level of common sense should be applied when deciding whether to have a feature
+A level of common sense should be applied when deciding whether to have a feature
behind a feature flag off or on by default.
The following guidelines can be applied to help make this decision:
* If the feature is not fully ready or functioning, the feature flag should be disabled by default.
-* If the feature is ready but there are concerns about performance or impact, the feature flag should be enabled by default, but
+* If the feature is ready but there are concerns about performance or impact, the feature flag should be enabled by default, but
disabled via chatops before deployment on GitLab.com environments. If the performance concern is confirmed, the final release should have the feature flag disabled by default.
* In most other cases, the feature flag can be enabled by default.
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 743f11625bc..aaab217964c 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,4 +1,4 @@
-/* eslint-disable class-methods-use-this */
+/* eslint-disable class-methods-use-this, @gitlab/i18n/no-non-i18n-strings */
import $ from 'jquery';
import _ from 'underscore';
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index a5ed695af35..c8a9cb1c296 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -2,7 +2,6 @@
import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
-import _ from 'underscore';
import CreateLabelDropdown from '../../create_label';
import boardsStore from '../stores/boards_store';
@@ -78,8 +77,6 @@ export default function initNewListDropdown() {
color: label.color,
},
});
-
- boardsStore.state.lists = _.sortBy(boardsStore.state.lists, 'position');
}
},
});
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index e9cab3e3bba..f2f37d22b97 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -3,8 +3,8 @@ import Vue from 'vue';
import Flash from '~/flash';
import { __ } from '~/locale';
-import '~/vue_shared/models/label';
-import '~/vue_shared/models/assignee';
+import './models/label';
+import './models/assignee';
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
diff --git a/app/assets/javascripts/vue_shared/models/assignee.js b/app/assets/javascripts/boards/models/assignee.js
index 4a29b0d0581..4a29b0d0581 100644
--- a/app/assets/javascripts/vue_shared/models/assignee.js
+++ b/app/assets/javascripts/boards/models/assignee.js
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index f8ff20cb0cd..f858b162c6b 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -4,7 +4,7 @@
/* global ListAssignee */
import Vue from 'vue';
-import '~/vue_shared/models/label';
+import './label';
import { isEE, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import IssueProject from './project';
import boardsStore from '../stores/boards_store';
diff --git a/app/assets/javascripts/boards/models/label.js b/app/assets/javascripts/boards/models/label.js
new file mode 100644
index 00000000000..cd2a2c0137f
--- /dev/null
+++ b/app/assets/javascripts/boards/models/label.js
@@ -0,0 +1,11 @@
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+
+export default class ListLabel {
+ constructor(obj) {
+ Object.assign(this, convertObjectPropsToCamelCase(obj, { dropKeys: ['priority'] }), {
+ priority: obj.priority !== null ? obj.priority : Infinity,
+ });
+ }
+}
+
+window.ListLabel = ListLabel;
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 08aecfab8a4..a9d88f19146 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -2,8 +2,8 @@
/* global ListIssue */
import { __ } from '~/locale';
-import ListLabel from '~/vue_shared/models/label';
-import ListAssignee from '~/vue_shared/models/assignee';
+import ListLabel from './label';
+import ListAssignee from './assignee';
import { isEE, urlParamsToObject } from '~/lib/utils/common_utils';
import boardsStore from '../stores/boards_store';
import ListMilestone from './milestone';
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index da82b52330a..d4f4df3ad75 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -1,4 +1,5 @@
const notImplemented = () => {
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
throw new Error('Not implemented!');
};
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 77ba68be07e..09eb8bb9b98 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -1,6 +1,7 @@
import * as mutationTypes from './mutation_types';
const notImplemented = () => {
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
throw new Error('Not implemented!');
};
diff --git a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js
index 592e1fd1c31..0bba2a2e160 100644
--- a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js
+++ b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js
@@ -27,15 +27,24 @@ function generateErrorBoxContent(errors) {
// Used for the variable list on CI/CD projects/groups settings page
export default class AjaxVariableList {
- constructor({ container, saveButton, errorBox, formField = 'variables', saveEndpoint }) {
+ constructor({
+ container,
+ saveButton,
+ errorBox,
+ formField = 'variables',
+ saveEndpoint,
+ maskableRegex,
+ }) {
this.container = container;
this.saveButton = saveButton;
this.errorBox = errorBox;
this.saveEndpoint = saveEndpoint;
+ this.maskableRegex = maskableRegex;
this.variableList = new VariableList({
container: this.container,
formField,
+ maskableRegex,
});
this.bindEvents();
diff --git a/app/assets/javascripts/ci_variable_list/ci_variable_list.js b/app/assets/javascripts/ci_variable_list/ci_variable_list.js
index 0390a3bf96a..0303e4e51dd 100644
--- a/app/assets/javascripts/ci_variable_list/ci_variable_list.js
+++ b/app/assets/javascripts/ci_variable_list/ci_variable_list.js
@@ -16,9 +16,10 @@ function createEnvironmentItem(value) {
}
export default class VariableList {
- constructor({ container, formField }) {
+ constructor({ container, formField, maskableRegex }) {
this.$container = $(container);
this.formField = formField;
+ this.maskableRegex = new RegExp(maskableRegex);
this.environmentDropdownMap = new WeakMap();
this.inputMap = {
@@ -196,9 +197,8 @@ export default class VariableList {
validateMaskability($row) {
const invalidInputClass = 'gl-field-error-outline';
- const maskableRegex = /^\w{8,}$/; // Eight or more alphanumeric characters plus underscores
const variableValue = $row.find(this.inputMap.secret_value.selector).val();
- const isValueMaskable = maskableRegex.test(variableValue) || variableValue === '';
+ const isValueMaskable = this.maskableRegex.test(variableValue) || variableValue === '';
const isMaskedChecked = $row.find(this.inputMap.masked.selector).val() === 'true';
// Show a validation error if the user wants to mask an unmaskable variable value
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index bc2e71b99f2..aacfa0d87e6 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -142,8 +142,7 @@ export default class Clusters {
addListeners() {
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication);
- eventHub.$on('upgradeApplication', data => this.upgradeApplication(data));
- eventHub.$on('dismissUpgradeSuccess', appId => this.dismissUpgradeSuccess(appId));
+ eventHub.$on('updateApplication', data => this.updateApplication(data));
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
@@ -155,8 +154,7 @@ export default class Clusters {
removeListeners() {
if (this.showTokenButton) this.showTokenButton.removeEventListener('click', this.showToken);
eventHub.$off('installApplication', this.installApplication);
- eventHub.$off('upgradeApplication', this.upgradeApplication);
- eventHub.$off('dismissUpgradeSuccess', this.dismissUpgradeSuccess);
+ eventHub.$off('updateApplication', this.updateApplication);
eventHub.$off('saveKnativeDomain');
eventHub.$off('setKnativeHostname');
eventHub.$off('uninstallApplication');
@@ -331,19 +329,13 @@ export default class Clusters {
});
}
- upgradeApplication(data) {
- const appId = data.id;
-
+ updateApplication({ id: appId, params }) {
this.store.updateApplication(appId);
- this.service.installApplication(appId, data.params).catch(() => {
+ this.service.installApplication(appId, params).catch(() => {
this.store.notifyUpdateFailure(appId);
});
}
- dismissUpgradeSuccess(appId) {
- this.store.acknowledgeSuccessfulUpdate(appId);
- }
-
toggleIngressDomainHelpText({ externalIp }, { externalIp: newExternalIp }) {
if (externalIp !== newExternalIp) {
this.ingressDomainHelpText.classList.toggle('hide', !newExternalIp);
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index 7b173be599a..4771090aa7e 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -2,7 +2,7 @@
/* eslint-disable vue/require-default-prop */
import { GlLink, GlModalDirective } from '@gitlab/ui';
import TimeagoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
-import { s__, sprintf } from '../../locale';
+import { s__, __, sprintf } from '~/locale';
import eventHub from '../event_hub';
import identicon from '../../vue_shared/components/identicon.vue';
import loadingButton from '../../vue_shared/components/loading_button.vue';
@@ -85,7 +85,7 @@ export default {
type: String,
required: false,
},
- upgradeAvailable: {
+ updateAvailable: {
type: Boolean,
required: false,
},
@@ -113,11 +113,6 @@ export default {
required: false,
default: false,
},
- updateAcknowledged: {
- type: Boolean,
- required: false,
- default: true,
- },
installApplicationRequestParams: {
type: Object,
required: false,
@@ -174,11 +169,11 @@ export default {
installButtonLabel() {
let label;
if (this.canInstall) {
- label = s__('ClusterIntegration|Install');
+ label = __('Install');
} else if (this.isInstalling) {
- label = s__('ClusterIntegration|Installing');
+ label = __('Installing');
} else if (this.installed) {
- label = s__('ClusterIntegration|Installed');
+ label = __('Installed');
}
return label;
@@ -187,7 +182,7 @@ export default {
return this.manageLink && this.status === APPLICATION_STATUS.INSTALLED;
},
manageButtonLabel() {
- return s__('ClusterIntegration|Manage');
+ return __('Manage');
},
hasError() {
return this.installFailed || this.uninstallFailed;
@@ -207,42 +202,42 @@ export default {
},
versionLabel() {
if (this.updateFailed) {
- return s__('ClusterIntegration|Upgrade failed');
- } else if (this.isUpgrading) {
- return s__('ClusterIntegration|Upgrading');
+ return __('Update failed');
+ } else if (this.isUpdating) {
+ return __('Updating');
}
- return s__('ClusterIntegration|Upgraded');
+ return __('Updated');
},
- upgradeFailureDescription() {
+ updateFailureDescription() {
return s__('ClusterIntegration|Update failed. Please check the logs and try again.');
},
- upgradeSuccessDescription() {
- return sprintf(s__('ClusterIntegration|%{title} upgraded successfully.'), {
+ updateSuccessDescription() {
+ return sprintf(s__('ClusterIntegration|%{title} updated successfully.'), {
title: this.title,
});
},
- upgradeButtonLabel() {
+ updateButtonLabel() {
let label;
- if (this.upgradeAvailable && !this.updateFailed && !this.isUpgrading) {
- label = s__('ClusterIntegration|Upgrade');
- } else if (this.isUpgrading) {
- label = s__('ClusterIntegration|Updating');
+ if (this.updateAvailable && !this.updateFailed && !this.isUpdating) {
+ label = __('Update');
+ } else if (this.isUpdating) {
+ label = __('Updating');
} else if (this.updateFailed) {
- label = s__('ClusterIntegration|Retry update');
+ label = __('Retry update');
}
return label;
},
- isUpgrading() {
+ isUpdating() {
// Since upgrading is handled asynchronously on the backend we need this check to prevent any delay on the frontend
return this.status === APPLICATION_STATUS.UPDATING;
},
- shouldShowUpgradeDetails() {
+ shouldShowUpdateDetails() {
// This method only returns true when;
- // Upgrade was successful OR Upgrade failed
- // AND new upgrade is unavailable AND version information is present.
- return (this.updateSuccessful || this.updateFailed) && !this.upgradeAvailable && this.version;
+ // Update was successful OR Update failed
+ // AND new update is unavailable AND version information is present.
+ return (this.updateSuccessful || this.updateFailed) && !this.updateAvailable && this.version;
},
uninstallSuccessDescription() {
return sprintf(s__('ClusterIntegration|%{title} uninstalled successfully.'), {
@@ -253,7 +248,7 @@ export default {
watch: {
updateSuccessful(updateSuccessful) {
if (updateSuccessful) {
- this.$toast.show(this.upgradeSuccessDescription);
+ this.$toast.show(this.updateSuccessDescription);
}
},
uninstallSuccessful(uninstallSuccessful) {
@@ -269,8 +264,8 @@ export default {
params: this.installApplicationRequestParams,
});
},
- upgradeClicked() {
- eventHub.$emit('upgradeApplication', {
+ updateClicked() {
+ eventHub.$emit('updateApplication', {
id: this.id,
params: this.installApplicationRequestParams,
});
@@ -332,8 +327,8 @@ export default {
<div v-if="updateable">
<div
- v-if="shouldShowUpgradeDetails"
- class="form-text text-muted label p-0 js-cluster-application-upgrade-details"
+ v-if="shouldShowUpdateDetails"
+ class="form-text text-muted label p-0 js-cluster-application-update-details"
>
{{ versionLabel }}
<span v-if="updateSuccessful">to</span>
@@ -342,24 +337,24 @@ export default {
v-if="updateSuccessful"
:href="chartRepo"
target="_blank"
- class="js-cluster-application-upgrade-version"
+ class="js-cluster-application-update-version"
>chart v{{ version }}</gl-link
>
</div>
<div
- v-if="updateFailed && !isUpgrading"
- class="bs-callout bs-callout-danger cluster-application-banner mt-2 mb-0 js-cluster-application-upgrade-failure-message"
+ v-if="updateFailed && !isUpdating"
+ class="bs-callout bs-callout-danger cluster-application-banner mt-2 mb-0 js-cluster-application-update-details"
>
- {{ upgradeFailureDescription }}
+ {{ updateFailureDescription }}
</div>
<loading-button
- v-if="upgradeAvailable || updateFailed || isUpgrading"
- class="btn btn-primary js-cluster-application-upgrade-button mt-2"
- :loading="isUpgrading"
- :disabled="isUpgrading"
- :label="upgradeButtonLabel"
- @click="upgradeClicked"
+ v-if="updateAvailable || updateFailed || isUpdating"
+ class="btn btn-primary js-cluster-application-update-button mt-2"
+ :loading="isUpdating"
+ :disabled="isUpdating"
+ :label="updateButtonLabel"
+ @click="updateClicked"
/>
</div>
</div>
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 2d129245d37..970f5a7b297 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -376,7 +376,7 @@ export default {
:request-reason="applications.runner.requestReason"
:version="applications.runner.version"
:chart-repo="applications.runner.chartRepo"
- :upgrade-available="applications.runner.upgradeAvailable"
+ :update-available="applications.runner.updateAvailable"
:installed="applications.runner.installed"
:install-failed="applications.runner.installFailed"
:update-successful="applications.runner.updateSuccessful"
diff --git a/app/assets/javascripts/clusters/services/application_state_machine.js b/app/assets/javascripts/clusters/services/application_state_machine.js
index 14b80a116a7..17ea4d77795 100644
--- a/app/assets/javascripts/clusters/services/application_state_machine.js
+++ b/app/assets/javascripts/clusters/services/application_state_machine.js
@@ -123,7 +123,6 @@ const applicationStateMachine = {
target: INSTALLED,
effects: {
updateSuccessful: true,
- updateAcknowledged: false,
},
},
[UPDATE_ERRORED]: {
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index 89e61c10a46..f64f0ca616f 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -56,8 +56,7 @@ export default class ClusterStore {
title: s__('ClusterIntegration|GitLab Runner'),
version: null,
chartRepo: 'https://gitlab.com/charts/gitlab-runner',
- upgradeAvailable: null,
- updateAcknowledged: true,
+ updateAvailable: null,
updateSuccessful: false,
updateFailed: false,
},
@@ -136,10 +135,6 @@ export default class ClusterStore {
this.state.applications[appId] = transitionApplicationState(currentAppState, event);
}
- acknowledgeSuccessfulUpdate(appId) {
- this.state.applications[appId].updateAcknowledged = true;
- }
-
updateAppProperty(appId, prop, value) {
this.state.applications[appId][prop] = value;
}
@@ -154,7 +149,7 @@ export default class ClusterStore {
status,
status_reason: statusReason,
version,
- update_available: upgradeAvailable,
+ update_available: updateAvailable,
can_uninstall: uninstallable,
} = serverAppEntry;
const currentApplicationState = this.state.applications[appId] || {};
@@ -191,7 +186,7 @@ export default class ClusterStore {
serverAppEntry.external_hostname || this.state.applications.knative.externalHostname;
} else if (appId === RUNNER) {
this.state.applications.runner.version = version;
- this.state.applications.runner.upgradeAvailable = upgradeAvailable;
+ this.state.applications.runner.updateAvailable = updateAvailable;
}
});
}
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 3e01841d563..4890f99e9d1 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -2,7 +2,7 @@
import PipelinesService from '../../pipelines/services/pipelines_service';
import PipelineStore from '../../pipelines/stores/pipelines_store';
import pipelinesMixin from '../../pipelines/mixins/pipelines';
-import TablePagination from '../../vue_shared/components/table_pagination.vue';
+import TablePagination from '../../vue_shared/components/pagination/table_pagination.vue';
import { getParameterByName } from '../../lib/utils/common_utils';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue
index bd7259ce3ee..aaa9f8b759a 100644
--- a/app/assets/javascripts/diffs/components/commit_item.vue
+++ b/app/assets/javascripts/diffs/components/commit_item.vue
@@ -91,7 +91,7 @@ export default {
<icon :size="12" name="ellipsis_h" />
</button>
- <div class="commiter">
+ <div class="committer">
<a
:href="authorUrl"
:class="authorClass"
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 2b3d6d1a3fa..d59b1136677 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -8,6 +8,7 @@ import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_d
import NoPreviewViewer from '~/vue_shared/components/diff_viewer/viewers/no_preview.vue';
import InlineDiffView from './inline_diff_view.vue';
import ParallelDiffView from './parallel_diff_view.vue';
+import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import NoteForm from '../../notes/components/note_form.vue';
import ImageDiffOverlay from './image_diff_overlay.vue';
import DiffDiscussions from './diff_discussions.vue';
@@ -26,6 +27,7 @@ export default {
ImageDiffOverlay,
NotDiffableViewer,
NoPreviewViewer,
+ userAvatarLink,
DiffFileDrafts: () => import('ee_component/batch_comments/components/diff_file_drafts.vue'),
},
mixins: [diffLineNoteFormMixin, draftCommentsMixin],
@@ -47,7 +49,7 @@ export default {
}),
...mapGetters('diffs', ['isInlineView', 'isParallelView']),
...mapGetters('diffs', ['getCommentFormForDiffFile']),
- ...mapGetters(['getNoteableData', 'noteableType']),
+ ...mapGetters(['getNoteableData', 'noteableType', 'getUserData']),
diffMode() {
return getDiffMode(this.diffFile);
},
@@ -72,6 +74,9 @@ export default {
diffFileHash() {
return this.diffFile.file_hash;
},
+ author() {
+ return this.getUserData;
+ },
},
methods: {
...mapActions('diffs', ['saveDiffDiscussion', 'closeDiffFileCommentForm']),
@@ -134,6 +139,14 @@ export default {
:can-comment="getNoteableData.current_user.can_create_note"
/>
<div v-if="showNotesContainer" class="note-container">
+ <user-avatar-link
+ v-if="diffFileCommentForm && author"
+ :link-href="author.path"
+ :img-src="author.avatar_url"
+ :img-alt="author.name"
+ :img-size="40"
+ class="d-none d-sm-block new-comment"
+ />
<diff-discussions
v-if="diffFile.discussions.length"
class="diff-file-discussions"
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index 41670b45798..c209b857652 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -4,11 +4,13 @@ import { s__ } from '~/locale';
import diffLineNoteFormMixin from 'ee_else_ce/notes/mixins/diff_line_note_form';
import noteForm from '../../notes/components/note_form.vue';
import autosave from '../../notes/mixins/autosave';
+import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import { DIFF_NOTE_TYPE } from '../constants';
export default {
components: {
noteForm,
+ userAvatarLink,
},
mixins: [autosave, diffLineNoteFormMixin],
props: {
@@ -41,7 +43,16 @@ export default {
diffViewType: state => state.diffs.diffViewType,
}),
...mapGetters('diffs', ['getDiffFileByHash']),
- ...mapGetters(['isLoggedIn', 'noteableType', 'getNoteableData', 'getNotesDataByProp']),
+ ...mapGetters([
+ 'isLoggedIn',
+ 'noteableType',
+ 'getNoteableData',
+ 'getNotesDataByProp',
+ 'getUserData',
+ ]),
+ author() {
+ return this.getUserData;
+ },
formData() {
return {
noteableData: this.noteableData,
@@ -99,6 +110,14 @@ export default {
<template>
<div class="content discussion-form discussion-form-container discussion-notes">
+ <user-avatar-link
+ v-if="author"
+ :link-href="author.path"
+ :img-src="author.avatar_url"
+ :img-alt="author.name"
+ :img-size="40"
+ class="d-none d-sm-block"
+ />
<note-form
ref="noteForm"
:is-editing="true"
diff --git a/app/assets/javascripts/environments/components/container.vue b/app/assets/javascripts/environments/components/container.vue
index be80661223c..f8a637138ad 100644
--- a/app/assets/javascripts/environments/components/container.vue
+++ b/app/assets/javascripts/environments/components/container.vue
@@ -1,6 +1,6 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
-import TablePagination from '~/vue_shared/components/table_pagination.vue';
+import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import containerMixin from 'ee_else_ce/environments/mixins/container_mixin';
import EnvironmentTable from '../components/environments_table.vue';
diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js
index a5812b173dc..31347d95a25 100644
--- a/app/assets/javascripts/environments/mixins/environments_mixin.js
+++ b/app/assets/javascripts/environments/mixins/environments_mixin.js
@@ -11,7 +11,7 @@ import Flash from '../../flash';
import eventHub from '../event_hub';
import EnvironmentsService from '../services/environments_service';
-import tablePagination from '../../vue_shared/components/table_pagination.vue';
+import tablePagination from '../../vue_shared/components/pagination/table_pagination.vue';
import environmentTable from '../components/environments_table.vue';
import tabs from '../../vue_shared/components/navigation_tabs.vue';
import container from '../components/container.vue';
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 05f34391323..bdb50606a53 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -567,7 +567,7 @@ GitLabDropdown = (function() {
e.stopPropagation();
// This prevents automatic scrolling to the top
- if ($target.is('a')) {
+ if ($target.closest('a').length) {
return false;
}
}
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index e41b1530226..363a8f43033 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -146,7 +146,7 @@ export default {
</div>
<component :is="rightPaneComponent" v-if="currentProjectId" />
</div>
- <ide-status-bar :file="activeFile" />
+ <ide-status-bar />
<new-modal />
</article>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index ce577ae85b0..206b8341aad 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -1,5 +1,6 @@
<script>
import { mapActions, mapState, mapGetters } from 'vuex';
+import IdeStatusList from 'ee_else_ce/ide/components/ide_status_list.vue';
import icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import timeAgoMixin from '~/vue_shared/mixins/timeago';
@@ -12,18 +13,12 @@ export default {
icon,
userAvatarImage,
CiIcon,
+ IdeStatusList,
},
directives: {
tooltip,
},
mixins: [timeAgoMixin],
- props: {
- file: {
- type: Object,
- required: false,
- default: null,
- },
- },
data() {
return {
lastCommitFormatedAge: null,
@@ -125,11 +120,6 @@ export default {
>{{ lastCommitFormatedAge }}</time
>
</div>
- <div v-if="file" class="ide-status-file">{{ file.name }}</div>
- <div v-if="file" class="ide-status-file">{{ file.eol }}</div>
- <div v-if="file && !file.binary" class="ide-status-file">
- {{ file.editorRow }}:{{ file.editorColumn }}
- </div>
- <div v-if="file" class="ide-status-file">{{ file.fileLanguage }}</div>
+ <ide-status-list class="ml-auto" />
</footer>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_status_list.vue b/app/assets/javascripts/ide/components/ide_status_list.vue
new file mode 100644
index 00000000000..364e3f081a1
--- /dev/null
+++ b/app/assets/javascripts/ide/components/ide_status_list.vue
@@ -0,0 +1,23 @@
+<script>
+import { mapGetters } from 'vuex';
+
+export default {
+ computed: {
+ ...mapGetters(['activeFile']),
+ },
+};
+</script>
+
+<template>
+ <div class="ide-status-list d-flex">
+ <template v-if="activeFile">
+ <div class="ide-status-file">{{ activeFile.name }}</div>
+ <div class="ide-status-file">{{ activeFile.eol }}</div>
+ <div v-if="!activeFile.binary" class="ide-status-file">
+ {{ activeFile.editorRow }}:{{ activeFile.editorColumn }}
+ </div>
+ <div class="ide-status-file">{{ activeFile.fileLanguage }}</div>
+ </template>
+ <slot></slot>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index e15b2a6f76b..b0c4969c5e4 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -125,6 +125,7 @@ export default {
'setFileEOL',
'updateViewer',
'removePendingTab',
+ 'triggerFilesChange',
]),
initEditor() {
if (this.shouldHideEditor) return;
@@ -256,6 +257,7 @@ export default {
'is-added': file.tempFile,
}"
class="multi-file-editor-holder"
+ @focusout="triggerFilesChange"
></div>
<content-viewer
v-if="showContentViewer"
diff --git a/app/assets/javascripts/ide/lib/keymap.json b/app/assets/javascripts/ide/lib/keymap.json
index 131abfebbed..2db87c07dde 100644
--- a/app/assets/javascripts/ide/lib/keymap.json
+++ b/app/assets/javascripts/ide/lib/keymap.json
@@ -7,5 +7,13 @@
"name": "toggleFileFinder",
"params": true
}
+ },
+ {
+ "id": "save-files",
+ "label": "Save files",
+ "bindings": ["CtrlCmd+KEY_S"],
+ "action": {
+ "name": "triggerFilesChange"
+ }
}
]
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index dc8ca732879..5429b834708 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -99,6 +99,7 @@ export const createTempEntry = (
commit(types.TOGGLE_FILE_OPEN, file.path);
commit(types.ADD_FILE_TO_CHANGED, file.path);
dispatch('setFileActive', file.path);
+ dispatch('triggerFilesChange');
}
if (parentPath && !state.entries[parentPath].opened) {
@@ -210,6 +211,8 @@ export const deleteEntry = ({ commit, dispatch, state }, path) => {
if (entry.parentPath && state.entries[entry.parentPath].tree.length === 0) {
dispatch('deleteEntry', entry.parentPath);
}
+
+ dispatch('triggerFilesChange');
};
export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
@@ -240,6 +243,8 @@ export const renameEntry = (
if (!entryPath && !entry.tempFile) {
dispatch('deleteEntry', path);
}
+
+ dispatch('triggerFilesChange');
};
export const getBranchData = ({ commit, state }, { projectId, branchId, force = false } = {}) =>
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index e7e8ac6d80b..dc40a1fa6a2 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -265,3 +265,8 @@ export const removePendingTab = ({ commit }, file) => {
eventHub.$emit(`editor.update.model.dispose.${file.key}`);
};
+
+export const triggerFilesChange = () => {
+ // Used in EE for file mirroring
+ eventHub.$emit('ide.files.change');
+};
diff --git a/app/assets/javascripts/lib/utils/autosave.js b/app/assets/javascripts/lib/utils/autosave.js
index 023c336db02..37896626053 100644
--- a/app/assets/javascripts/lib/utils/autosave.js
+++ b/app/assets/javascripts/lib/utils/autosave.js
@@ -29,4 +29,5 @@ export const updateDraft = (autosaveKey, text) => {
};
export const getDiscussionReplyKey = (noteableType, discussionId) =>
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
['Note', capitalizeFirstCharacter(noteableType), discussionId, 'Reply'].join('/');
diff --git a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
index a24c71aeab1..28a7ebfdc69 100644
--- a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
+++ b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
@@ -51,6 +51,7 @@ export default class LinkedTabs {
this.defaultAction = this.options.defaultAction;
this.action = this.options.action || this.defaultAction;
+ this.hashedTabs = this.options.hashedTabs || false;
if (this.action === 'show') {
this.action = this.defaultAction;
@@ -58,6 +59,10 @@ export default class LinkedTabs {
this.currentLocation = window.location;
+ if (this.hashedTabs) {
+ this.action = this.currentLocation.hash || this.action;
+ }
+
const tabSelector = `${this.options.parentEl} a[data-toggle="tab"]`;
// since this is a custom event we need jQuery :(
@@ -91,7 +96,9 @@ export default class LinkedTabs {
copySource.replace(/\/+$/, '');
- const newState = `${copySource}${this.currentLocation.search}${this.currentLocation.hash}`;
+ const newState = this.hashedTabs
+ ? copySource
+ : `${copySource}${this.currentLocation.search}${this.currentLocation.hash}`;
window.history.replaceState(
{
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index d3e6851496b..d521c462ad8 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -81,7 +81,7 @@ export const getDayName = date =>
*/
export const formatDate = datetime => {
if (_.isString(datetime) && datetime.match(/\d+-\d+\d+ /)) {
- throw new Error('Invalid date');
+ throw new Error(__('Invalid date'));
}
return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
};
diff --git a/app/assets/javascripts/lib/utils/invalid_url.js b/app/assets/javascripts/lib/utils/invalid_url.js
new file mode 100644
index 00000000000..481bd059fc9
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/invalid_url.js
@@ -0,0 +1,6 @@
+/**
+ * Invalid URL that ensures we don't make a network request
+ * Can be used as a default value for URLs. Using an empty
+ * string can still result in request being made to the current page
+ */
+export default 'https://invalid';
diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js
index d93873e0214..e7f6255e5f1 100644
--- a/app/assets/javascripts/lib/utils/notify.js
+++ b/app/assets/javascripts/lib/utils/notify.js
@@ -12,6 +12,7 @@ function notificationGranted(message, opts, onclick) {
}
function notifyPermissions() {
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
if ('Notification' in window) {
return Notification.requestPermission();
}
@@ -24,6 +25,7 @@ function notifyMe(message, body, icon, onclick) {
icon: icon,
};
// Let's check if the browser supports notifications
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
if (!('Notification' in window)) {
// do nothing
} else if (Notification.permission === 'granted') {
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index 9ddfb4bca11..61c8b8803d7 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -100,3 +100,9 @@ export function numberToHumanSize(size) {
* @returns {Float} The summed value
*/
export const sum = (a = 0, b = 0) => a + b;
+
+/**
+ * Checks if the provided number is odd
+ * @param {Int} number
+ */
+export const isOdd = (number = 0) => number % 2;
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index b5474fc5c71..32fd0990374 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -1,3 +1,5 @@
+import { join as joinPaths } from 'path';
+
// Returns an array containing the value(s) of the
// of the key passed as an argument
export function getParameterValues(sParam) {
@@ -157,4 +159,12 @@ export function isSafeURL(url) {
}
}
-export { join as joinPaths } from 'path';
+export function getWebSocketProtocol() {
+ return window.location.protocol.replace('http', 'ws');
+}
+
+export function getWebSocketUrl(path) {
+ return `${getWebSocketProtocol()}//${joinPaths(window.location.host, path)}`;
+}
+
+export { joinPaths };
diff --git a/app/assets/javascripts/monitoring/components/charts/area.vue b/app/assets/javascripts/monitoring/components/charts/area.vue
index c43791f2426..9de4e96e4da 100644
--- a/app/assets/javascripts/monitoring/components/charts/area.vue
+++ b/app/assets/javascripts/monitoring/components/charts/area.vue
@@ -1,7 +1,7 @@
<script>
import { GlAreaChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import dateFormat from 'dateformat';
-import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
+import { debounceByAnimationFrame, roundOffFloat } from '~/lib/utils/common_utils';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import Icon from '~/vue_shared/components/icon.vue';
import { chartHeight, graphTypes, lineTypes } from '../../constants';
@@ -111,7 +111,7 @@ export default {
yAxis: {
name: this.yAxisLabel,
axisLabel: {
- formatter: value => value.toFixed(3),
+ formatter: num => roundOffFloat(num, 3).toString(),
},
},
series: this.scatterSeries,
@@ -227,6 +227,7 @@ export default {
[this.primaryColor] = chart.getOption().color;
},
onResize() {
+ if (!this.$refs.areaChart) return;
const { width } = this.$refs.areaChart.$el.getBoundingClientRect();
this.width = width;
},
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 78744c0a0a9..d716fc211ca 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -1,18 +1,12 @@
<script>
-import {
- GlButton,
- GlDropdown,
- GlDropdownItem,
- GlModal,
- GlModalDirective,
- GlLink,
-} from '@gitlab/ui';
+import { GlButton, GlDropdown, GlDropdownItem, GlModal, GlModalDirective } from '@gitlab/ui';
import _ from 'underscore';
import { mapActions, mapState } from 'vuex';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import '~/vue_shared/mixins/is_ee';
import { getParameterValues } from '~/lib/utils/url_utility';
+import invalidUrl from '~/lib/utils/invalid_url';
import MonitorAreaChart from './charts/area.vue';
import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
@@ -31,7 +25,6 @@ export default {
GlButton,
GlDropdown,
GlDropdownItem,
- GlLink,
GlModal,
},
directives: {
@@ -106,10 +99,6 @@ export default {
type: String,
required: true,
},
- showTimeWindowDropdown: {
- type: Boolean,
- required: true,
- },
customMetricsAvailable: {
type: Boolean,
required: false,
@@ -123,6 +112,11 @@ export default {
type: String,
required: true,
},
+ dashboardEndpoint: {
+ type: String,
+ required: false,
+ default: invalidUrl,
+ },
},
data() {
return {
@@ -143,13 +137,19 @@ export default {
'showEmptyState',
'environments',
'deploymentData',
+ 'metricsWithData',
+ 'useDashboardEndpoint',
]),
+ groupsWithData() {
+ return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0);
+ },
},
created() {
this.setEndpoints({
metricsEndpoint: this.metricsEndpoint,
environmentsEndpoint: this.environmentsEndpoint,
deploymentsEndpoint: this.deploymentEndpoint,
+ dashboardEndpoint: this.dashboardEndpoint,
});
this.timeWindows = timeWindows;
@@ -187,7 +187,16 @@ export default {
'fetchData',
'setGettingStartedEmptyState',
'setEndpoints',
+ 'setDashboardEnabled',
]),
+ chartsWithData(charts) {
+ if (!this.useDashboardEndpoint) {
+ return charts;
+ }
+ return charts.filter(chart =>
+ chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)),
+ );
+ },
getGraphAlerts(queries) {
if (!this.allAlerts) return {};
const metricIdsForChart = queries.map(q => q.metricId);
@@ -248,7 +257,7 @@ export default {
>
</gl-dropdown>
</div>
- <div v-if="showTimeWindowDropdown" class="d-flex align-items-center prepend-left-8">
+ <div class="d-flex align-items-center prepend-left-8">
<strong>{{ s__('Metrics|Show last') }}</strong>
<gl-dropdown
class="prepend-left-10 js-time-window-dropdown"
@@ -259,7 +268,9 @@ export default {
v-for="(value, key) in timeWindows"
:key="key"
:active="activeTimeWindow(key)"
- ><gl-link :href="setTimeWindowParameter(key)">{{ value }}</gl-link></gl-dropdown-item
+ :href="setTimeWindowParameter(key)"
+ active-class="active"
+ >{{ value }}</gl-dropdown-item
>
</gl-dropdown>
</div>
@@ -311,13 +322,13 @@ export default {
</div>
</div>
<graph-group
- v-for="(groupData, index) in groups"
+ v-for="(groupData, index) in groupsWithData"
:key="index"
:name="groupData.group"
:show-panels="showPanels"
>
<monitor-area-chart
- v-for="(graphData, graphIndex) in groupData.metrics"
+ v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
:key="graphIndex"
:graph-data="graphData"
:deployment-data="deploymentData"
diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js
index 57771ccf4d9..1d33537b3b2 100644
--- a/app/assets/javascripts/monitoring/monitoring_bundle.js
+++ b/app/assets/javascripts/monitoring/monitoring_bundle.js
@@ -7,6 +7,11 @@ export default (props = {}) => {
const el = document.getElementById('prometheus-graphs');
if (el && el.dataset) {
+ store.dispatch(
+ 'monitoringDashboard/setDashboardEnabled',
+ gon.features.environmentMetricsUsePrometheusEndpoint,
+ );
+
// eslint-disable-next-line no-new
new Vue({
el,
@@ -16,7 +21,6 @@ export default (props = {}) => {
props: {
...el.dataset,
hasMetrics: parseBoolean(el.dataset.hasMetrics),
- showTimeWindowDropdown: gon.features.metricsTimeWindow,
...props,
},
});
diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js
index 63c23e8449d..49ce722b838 100644
--- a/app/assets/javascripts/monitoring/stores/actions.js
+++ b/app/assets/javascripts/monitoring/stores/actions.js
@@ -35,6 +35,21 @@ export const setEndpoints = ({ commit }, endpoints) => {
commit(types.SET_ENDPOINTS, endpoints);
};
+export const setDashboardEnabled = ({ commit }, enabled) => {
+ commit(types.SET_DASHBOARD_ENABLED, enabled);
+};
+
+export const requestMetricsDashboard = ({ commit }) => {
+ commit(types.REQUEST_METRICS_DATA);
+};
+export const receiveMetricsDashboardSuccess = ({ commit, dispatch }, { response, params }) => {
+ commit(types.RECEIVE_METRICS_DATA_SUCCESS, response.dashboard.panel_groups);
+ dispatch('fetchPrometheusMetrics', params);
+};
+export const receiveMetricsDashboardFailure = ({ commit }, error) => {
+ commit(types.RECEIVE_METRICS_DATA_FAILURE, error);
+};
+
export const requestMetricsData = ({ commit }) => commit(types.REQUEST_METRICS_DATA);
export const receiveMetricsDataSuccess = ({ commit }, data) =>
commit(types.RECEIVE_METRICS_DATA_SUCCESS, data);
@@ -56,6 +71,10 @@ export const fetchData = ({ dispatch }, params) => {
};
export const fetchMetricsData = ({ state, dispatch }, params) => {
+ if (state.useDashboardEndpoint) {
+ return dispatch('fetchDashboard', params);
+ }
+
dispatch('requestMetricsData');
return backOffRequest(() => axios.get(state.metricsEndpoint, { params }))
@@ -73,6 +92,77 @@ export const fetchMetricsData = ({ state, dispatch }, params) => {
});
};
+export const fetchDashboard = ({ state, dispatch }, params) => {
+ dispatch('requestMetricsDashboard');
+
+ return axios
+ .get(state.dashboardEndpoint, { params })
+ .then(resp => resp.data)
+ .then(response => {
+ dispatch('receiveMetricsDashboardSuccess', { response, params });
+ })
+ .catch(error => {
+ dispatch('receiveMetricsDashboardFailure', error);
+ createFlash(s__('Metrics|There was an error while retrieving metrics'));
+ });
+};
+
+function fetchPrometheusResult(prometheusEndpoint, params) {
+ return backOffRequest(() => axios.get(prometheusEndpoint, { params }))
+ .then(res => res.data)
+ .then(response => {
+ if (response.status === 'error') {
+ throw new Error(response.error);
+ }
+
+ return response.data.result;
+ });
+}
+
+/**
+ * Returns list of metrics in data.result
+ * {"status":"success", "data":{"resultType":"matrix","result":[]}}
+ *
+ * @param {metric} metric
+ */
+export const fetchPrometheusMetric = ({ commit }, { metric, params }) => {
+ const { start, end } = params;
+ const timeDiff = end - start;
+
+ const minStep = 60;
+ const queryDataPoints = 600;
+ const step = Math.max(minStep, Math.ceil(timeDiff / queryDataPoints));
+
+ const queryParams = {
+ start,
+ end,
+ step,
+ };
+
+ return fetchPrometheusResult(metric.prometheus_endpoint_path, queryParams).then(result => {
+ commit(types.SET_QUERY_RESULT, { metricId: metric.metric_id, result });
+ });
+};
+
+export const fetchPrometheusMetrics = ({ state, commit, dispatch }, params) => {
+ commit(types.REQUEST_METRICS_DATA);
+
+ const promises = [];
+ state.groups.forEach(group => {
+ group.panels.forEach(panel => {
+ panel.metrics.forEach(metric => {
+ promises.push(dispatch('fetchPrometheusMetric', { metric, params }));
+ });
+ });
+ });
+
+ return Promise.all(promises).then(() => {
+ if (state.metricsWithData.length === 0) {
+ commit(types.SET_NO_DATA_EMPTY_STATE);
+ }
+ });
+};
+
export const fetchDeploymentsData = ({ state, dispatch }) => {
if (!state.deploymentEndpoint) {
return Promise.resolve([]);
diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js
index 3fd9e07fa8b..63894e83362 100644
--- a/app/assets/javascripts/monitoring/stores/mutation_types.js
+++ b/app/assets/javascripts/monitoring/stores/mutation_types.js
@@ -7,6 +7,9 @@ export const RECEIVE_DEPLOYMENTS_DATA_FAILURE = 'RECEIVE_DEPLOYMENTS_DATA_FAILUR
export const REQUEST_ENVIRONMENTS_DATA = 'REQUEST_ENVIRONMENTS_DATA';
export const RECEIVE_ENVIRONMENTS_DATA_SUCCESS = 'RECEIVE_ENVIRONMENTS_DATA_SUCCESS';
export const RECEIVE_ENVIRONMENTS_DATA_FAILURE = 'RECEIVE_ENVIRONMENTS_DATA_FAILURE';
+export const SET_QUERY_RESULT = 'SET_QUERY_RESULT';
export const SET_TIME_WINDOW = 'SET_TIME_WINDOW';
+export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED';
export const SET_ENDPOINTS = 'SET_ENDPOINTS';
export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE';
+export const SET_NO_DATA_EMPTY_STATE = 'SET_NO_DATA_EMPTY_STATE';
diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js
index c1779333d75..d4b816e2717 100644
--- a/app/assets/javascripts/monitoring/stores/mutations.js
+++ b/app/assets/javascripts/monitoring/stores/mutations.js
@@ -1,5 +1,6 @@
+import Vue from 'vue';
import * as types from './mutation_types';
-import { normalizeMetrics, sortMetrics } from './utils';
+import { normalizeMetrics, sortMetrics, normalizeQueryResult } from './utils';
export default {
[types.REQUEST_METRICS_DATA](state) {
@@ -7,10 +8,24 @@ export default {
state.showEmptyState = true;
},
[types.RECEIVE_METRICS_DATA_SUCCESS](state, groupData) {
- state.groups = groupData.map(group => ({
- ...group,
- metrics: normalizeMetrics(sortMetrics(group.metrics)),
- }));
+ state.groups = groupData.map(group => {
+ let { metrics } = group;
+
+ // for backwards compatibility, and to limit Vue template changes:
+ // for each group alias panels to metrics
+ // for each panel alias metrics to queries
+ if (state.useDashboardEndpoint) {
+ metrics = group.panels.map(panel => ({
+ ...panel,
+ queries: panel.metrics,
+ }));
+ }
+
+ return {
+ ...group,
+ metrics: normalizeMetrics(sortMetrics(metrics)),
+ };
+ });
if (!state.groups.length) {
state.emptyState = 'noData';
@@ -34,12 +49,40 @@ export default {
[types.RECEIVE_ENVIRONMENTS_DATA_FAILURE](state) {
state.environments = [];
},
+ [types.SET_QUERY_RESULT](state, { metricId, result }) {
+ if (!metricId || !result || result.length === 0) {
+ return;
+ }
+
+ state.showEmptyState = false;
+
+ state.groups.forEach(group => {
+ group.metrics.forEach(metric => {
+ metric.queries.forEach(query => {
+ if (query.metric_id === metricId) {
+ state.metricsWithData.push(metricId);
+ // ensure dates/numbers are correctly formatted for charts
+ const normalizedResults = result.map(normalizeQueryResult);
+ Vue.set(query, 'result', Object.freeze(normalizedResults));
+ }
+ });
+ });
+ });
+ },
[types.SET_ENDPOINTS](state, endpoints) {
state.metricsEndpoint = endpoints.metricsEndpoint;
state.environmentsEndpoint = endpoints.environmentsEndpoint;
state.deploymentsEndpoint = endpoints.deploymentsEndpoint;
+ state.dashboardEndpoint = endpoints.dashboardEndpoint;
+ },
+ [types.SET_DASHBOARD_ENABLED](state, enabled) {
+ state.useDashboardEndpoint = enabled;
},
[types.SET_GETTING_STARTED_EMPTY_STATE](state) {
state.emptyState = 'gettingStarted';
},
+ [types.SET_NO_DATA_EMPTY_STATE](state) {
+ state.showEmptyState = true;
+ state.emptyState = 'noData';
+ },
};
diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js
index 5103122612a..c33529cd588 100644
--- a/app/assets/javascripts/monitoring/stores/state.js
+++ b/app/assets/javascripts/monitoring/stores/state.js
@@ -1,12 +1,17 @@
+import invalidUrl from '~/lib/utils/invalid_url';
+
export default () => ({
hasMetrics: false,
showPanels: true,
metricsEndpoint: null,
environmentsEndpoint: null,
deploymentsEndpoint: null,
+ dashboardEndpoint: invalidUrl,
+ useDashboardEndpoint: false,
emptyState: 'gettingStarted',
showEmptyState: true,
groups: [],
deploymentData: [],
environments: [],
+ metricsWithData: [],
});
diff --git a/app/assets/javascripts/monitoring/stores/utils.js b/app/assets/javascripts/monitoring/stores/utils.js
index 9216554ecbf..84e1f1c4c20 100644
--- a/app/assets/javascripts/monitoring/stores/utils.js
+++ b/app/assets/javascripts/monitoring/stores/utils.js
@@ -58,6 +58,14 @@ export const sortMetrics = metrics =>
.sortBy('weight')
.value();
+export const normalizeQueryResult = timeSeries => ({
+ ...timeSeries,
+ values: timeSeries.values.map(([timestamp, value]) => [
+ new Date(timestamp * 1000).toISOString(),
+ Number(value),
+ ]),
+});
+
export const normalizeMetrics = metrics => {
const groupedMetrics = groupQueriesByChartInfo(metrics);
@@ -66,13 +74,7 @@ export const normalizeMetrics = metrics => {
...query,
// custom metrics do not require a label, so we should ensure this attribute is defined
label: query.label || metric.y_label,
- result: query.result.map(result => ({
- ...result,
- values: result.values.map(([timestamp, value]) => [
- new Date(timestamp * 1000).toISOString(),
- Number(value),
- ]),
- })),
+ result: (query.result || []).map(normalizeQueryResult),
}));
return {
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index f338dbbb0a6..98522c67696 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, one-var, consistent-return, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return */
+/* eslint-disable func-names, no-var, one-var, consistent-return, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, @gitlab/i18n/no-non-i18n-strings */
import $ from 'jquery';
import RefSelectDropdown from './ref_select_dropdown';
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 1c9ca180100..a7156bd2406 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -990,6 +990,14 @@ export default class Notes {
form.find('#note_position').val(dataHolder.attr('data-position'));
form
+ .prepend(
+ `<div class="avatar-note-form-holder"><div class="content"><a href="${escape(
+ gon.current_username,
+ )}" class="user-avatar-link d-none d-sm-block"><img class="avatar s40" src="${encodeURI(
+ gon.current_user_avatar_url,
+ )}" alt="${escape(gon.current_user_fullname)}" /></a></div></div>`,
+ )
+ .append('</div>')
.find('.js-close-discussion-note-form')
.show()
.removeClass('hide');
@@ -1025,6 +1033,9 @@ export default class Notes {
target: $link,
lineType: link.dataset.lineType,
showReplyInput,
+ currentUsername: gon.current_username,
+ currentUserAvatar: gon.current_user_avatar_url,
+ currentUserFullname: gon.current_user_fullname,
});
}
@@ -1053,7 +1064,15 @@ export default class Notes {
this.setupDiscussionNoteForm($link, newForm);
}
- toggleDiffNote({ target, lineType, forceShow, showReplyInput = false }) {
+ toggleDiffNote({
+ target,
+ lineType,
+ forceShow,
+ showReplyInput = false,
+ currentUsername,
+ currentUserAvatar,
+ currentUserFullname,
+ }) {
var $link,
addForm,
hasNotes,
@@ -1546,7 +1565,9 @@ export default class Notes {
<div class="note-header">
<div class="note-header-info">
<a href="/${_.escape(currentUsername)}">
- <span class="d-none d-sm-inline-block">${_.escape(currentUsername)}</span>
+ <span class="d-none d-sm-inline-block bold">${_.escape(
+ currentUsername,
+ )}</span>
<span class="note-headline-light">${_.escape(currentUsername)}</span>
</a>
</div>
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 688c06878ac..075c28e8d07 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -337,6 +337,8 @@ Please check your network connection and try again.`;
v-if="hasWarning(getNoteableData)"
:is-locked="isLocked(getNoteableData)"
:is-confidential="isConfidential(getNoteableData)"
+ :locked-issue-docs-path="lockedIssueDocsPath"
+ :confidential-issue-docs-path="confidentialIssueDocsPath"
/>
<markdown-field
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index 307e56708e0..efd84f5722c 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -49,8 +49,8 @@ export default {
</script>
<template>
- <div v-if="resolvableDiscussionsCount > 0" class="line-resolve-all-container prepend-top-8">
- <div>
+ <div v-if="resolvableDiscussionsCount > 0" class="line-resolve-all-container full-width-mobile">
+ <div class="full-width-mobile d-flex d-sm-block">
<div :class="{ 'has-next-btn': hasNextButton }" class="line-resolve-all">
<span
:class="{ 'is-active': allResolved }"
@@ -64,7 +64,11 @@ export default {
{{ n__('discussion resolved', 'discussions resolved', resolvableDiscussionsCount) }}
</span>
</div>
- <div v-if="resolveAllDiscussionsIssuePath && !allResolved" class="btn-group" role="group">
+ <div
+ v-if="resolveAllDiscussionsIssuePath && !allResolved"
+ class="btn-group btn-group-sm"
+ role="group"
+ >
<a
v-gl-tooltip
:href="resolveAllDiscussionsIssuePath"
@@ -74,7 +78,7 @@ export default {
<icon name="issue-new" />
</a>
</div>
- <div v-if="isLoggedIn && !allResolved" class="btn-group" role="group">
+ <div v-if="isLoggedIn && !allResolved" class="btn-group btn-group-sm" role="group">
<button
v-gl-tooltip
title="Jump to first unresolved discussion"
diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue
index 47951591e82..eb3fbbe1385 100644
--- a/app/assets/javascripts/notes/components/discussion_filter.vue
+++ b/app/assets/javascripts/notes/components/discussion_filter.vue
@@ -105,12 +105,12 @@ export default {
<template>
<div
v-if="displayFilters"
- class="discussion-filter-container js-discussion-filter-container d-inline-block align-bottom"
+ class="discussion-filter-container js-discussion-filter-container d-inline-block align-bottom full-width-mobile"
>
<button
id="discussion-filter-dropdown"
ref="dropdownToggle"
- class="btn btn-default qa-discussion-filter"
+ class="btn btn-sm qa-discussion-filter"
data-toggle="dropdown"
aria-expanded="false"
>
diff --git a/app/assets/javascripts/notes/components/discussion_locked_widget.vue b/app/assets/javascripts/notes/components/discussion_locked_widget.vue
index c469a6b7bcd..53f509185a8 100644
--- a/app/assets/javascripts/notes/components/discussion_locked_widget.vue
+++ b/app/assets/javascripts/notes/components/discussion_locked_widget.vue
@@ -1,12 +1,24 @@
<script>
+import { GlLink } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
+import { __, sprintf } from '~/locale';
import Issuable from '~/vue_shared/mixins/issuable';
+import issuableStateMixin from '../mixins/issuable_state';
export default {
components: {
Icon,
+ GlLink,
+ },
+ mixins: [Issuable, issuableStateMixin],
+ computed: {
+ lockedIssueWarning() {
+ return sprintf(
+ __('This %{issuableDisplayName} is locked. Only project members can comment.'),
+ { issuableDisplayName: this.issuableDisplayName },
+ );
+ },
},
- mixins: [Issuable],
};
</script>
@@ -15,7 +27,11 @@ export default {
<span class="issuable-note-warning inline">
<icon :size="16" name="lock" class="icon" />
<span>
- This {{ issuableDisplayName }} is locked. Only <b>project members</b> can comment.
+ {{ lockedIssueWarning }}
+
+ <gl-link :href="lockedIssueDocsPath" target="_blank" class="learn-more">
+ {{ __('Learn more') }}
+ </gl-link>
</span>
</span>
</div>
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index c9c40cb6acf..844d0c3e376 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -195,7 +195,7 @@ export default {
</button>
<ul class="dropdown-menu more-actions-dropdown dropdown-open-left">
<li v-if="canReportAsAbuse">
- <a :href="reportAbusePath">{{ __('Report abuse to GitLab') }}</a>
+ <a :href="reportAbusePath">{{ __('Report abuse to admin') }}</a>
</li>
<li v-if="noteUrl">
<button
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index acbb91ce7be..09ecb695214 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -234,6 +234,8 @@ export default {
v-if="hasWarning(getNoteableData)"
:is-locked="isLocked(getNoteableData)"
:is-confidential="isConfidential(getNoteableData)"
+ :locked-issue-docs-path="lockedIssueDocsPath"
+ :confidential-issue-docs-path="confidentialIssueDocsPath"
/>
<markdown-field
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index 5c59c0c32dd..fbf82fab9e9 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -82,7 +82,7 @@ export default {
:data-username="author.username"
>
<slot name="note-header-info"></slot>
- <span class="note-header-author-name">{{ author.name }}</span>
+ <span class="note-header-author-name bold">{{ author.name }}</span>
<span v-if="author.status_tooltip_html" v-html="author.status_tooltip_html"></span>
<span class="note-headline-light">@{{ author.username }}</span>
</a>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 2c549e7abdd..eb6a4a67fff 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -87,7 +87,11 @@ export default {
'unresolvedDiscussionsCount',
'hasUnresolvedDiscussions',
'showJumpToNextDiscussion',
+ 'getUserData',
]),
+ currentUser() {
+ return this.getUserData;
+ },
author() {
return this.firstNote.author;
},
@@ -377,6 +381,14 @@ Please check your network connection and try again.`;
:class="{ 'is-replying': isReplying }"
class="discussion-reply-holder"
>
+ <user-avatar-link
+ v-if="!isReplying && currentUser"
+ :link-href="currentUser.path"
+ :img-src="currentUser.avatar_url"
+ :img-alt="currentUser.name"
+ :img-size="40"
+ class="d-none d-sm-block"
+ />
<discussion-actions
v-if="!isReplying && userCanReply"
:discussion="discussion"
@@ -388,18 +400,27 @@ Please check your network connection and try again.`;
@resolve="resolveHandler"
@jumpToNextDiscussion="jumpToNextDiscussion"
/>
- <note-form
- v-if="isReplying"
- ref="noteForm"
- :discussion="discussion"
- :is-editing="false"
- :line="diffLine"
- save-button-title="Comment"
- :autosave-key="autosaveKey"
- @handleFormUpdateAddToReview="addReplyToReview"
- @handleFormUpdate="saveReply"
- @cancelForm="cancelReplyForm"
- />
+ <div v-if="isReplying" class="avatar-note-form-holder">
+ <user-avatar-link
+ v-if="currentUser"
+ :link-href="currentUser.path"
+ :img-src="currentUser.avatar_url"
+ :img-alt="currentUser.name"
+ :img-size="40"
+ class="d-none d-sm-block"
+ />
+ <note-form
+ ref="noteForm"
+ :discussion="discussion"
+ :is-editing="false"
+ :line="diffLine"
+ save-button-title="Comment"
+ :autosave-key="autosaveKey"
+ @handleFormUpdateAddToReview="addReplyToReview"
+ @handleFormUpdate="saveReply"
+ @cancelForm="cancelReplyForm"
+ />
+ </div>
<note-signed-out-widget v-if="!userCanReply" />
</div>
</template>
diff --git a/app/assets/javascripts/notes/mixins/issuable_state.js b/app/assets/javascripts/notes/mixins/issuable_state.js
index ded0ac3cfa9..d97d9f6850a 100644
--- a/app/assets/javascripts/notes/mixins/issuable_state.js
+++ b/app/assets/javascripts/notes/mixins/issuable_state.js
@@ -1,4 +1,15 @@
+import { mapGetters } from 'vuex';
+
export default {
+ computed: {
+ ...mapGetters(['getNoteableDataByProp']),
+ lockedIssueDocsPath() {
+ return this.getNoteableDataByProp('locked_discussion_docs_path');
+ },
+ confidentialIssueDocsPath() {
+ return this.getNoteableDataByProp('confidential_issues_docs_path');
+ },
+ },
methods: {
isConfidential(issue) {
return Boolean(issue.confidential);
diff --git a/app/assets/javascripts/operation_settings/index.js b/app/assets/javascripts/operation_settings/index.js
index 6946578e6d2..f075291ce98 100644
--- a/app/assets/javascripts/operation_settings/index.js
+++ b/app/assets/javascripts/operation_settings/index.js
@@ -3,14 +3,6 @@ import store from './store';
import ExternalDashboardForm from './components/external_dashboard.vue';
export default () => {
- /**
- * This check can be removed when we remove
- * the :grafana_dashboard_link feature flag
- */
- if (!gon.features.grafanaDashboardLink) {
- return null;
- }
-
const el = document.querySelector('.js-operation-settings');
return new Vue({
diff --git a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
index ae0a8c74964..8a5300c9266 100644
--- a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
@@ -12,5 +12,6 @@ document.addEventListener('DOMContentLoaded', () => {
saveButton: variableListEl.querySelector('.js-ci-variables-save-button'),
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
+ maskableRegex: variableListEl.dataset.maskableRegex,
});
});
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
index 377dce6c746..506e6075d16 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
@@ -124,11 +124,14 @@ export const ContributorsGraph = (function() {
};
ContributorsGraph.prototype.draw_x_axis = function() {
- return this.svg
- .append('g')
- .attr('class', 'x axis')
- .attr('transform', 'translate(0, ' + this.height + ')')
- .call(this.x_axis);
+ return (
+ this.svg
+ .append('g')
+ .attr('class', 'x axis')
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
+ .attr('transform', 'translate(0, ' + this.height + ')')
+ .call(this.x_axis)
+ );
};
ContributorsGraph.prototype.draw_y_axis = function() {
@@ -205,6 +208,7 @@ export const ContributorsMasterGraph = (function(superClass) {
.attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom)
.attr('class', 'tint-box')
.append('g')
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
.attr('transform', 'translate(' + this.MARGIN.left + ',' + this.MARGIN.top + ')');
return this.svg;
};
@@ -354,6 +358,7 @@ export const ContributorsAuthorGraph = (function(superClass) {
.attr('height', this.height + this.MARGIN.top + this.MARGIN.bottom)
.attr('class', 'spark')
.append('g')
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
.attr('transform', 'translate(' + this.MARGIN.left + ',' + this.MARGIN.top + ')');
return this.svg;
};
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index b288989b252..f0d529758d5 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -39,6 +39,11 @@ export default class Project {
$label.text(activeText);
});
+ $('#modal-geo-info').data({
+ cloneUrlSecondary: $this.attr('href'),
+ cloneUrlPrimary: $this.data('primaryUrl') || '',
+ });
+
if (mobileCloneField) {
mobileCloneField.dataset.clipboardText = url;
} else {
@@ -67,6 +72,13 @@ export default class Project {
.remove();
return e.preventDefault();
});
+ $('.hide-shared-runner-limit-message').on('click', function(e) {
+ var $alert = $(this).parents('.shared-runner-quota-message');
+ var scope = $alert.data('scope');
+ Cookies.set('hide_shared_runner_quota_message', 'false', { path: scope });
+ $alert.remove();
+ e.preventDefault();
+ });
$('.hide-auto-devops-implicitly-enabled-banner').on('click', function(e) {
const projectId = $(this).data('project-id');
const cookieKey = `hide_auto_devops_implicitly_enabled_banner_${projectId}`;
diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
index 15c6fb550c1..885247335a4 100644
--- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
@@ -21,6 +21,7 @@ document.addEventListener('DOMContentLoaded', () => {
saveButton: variableListEl.querySelector('.js-ci-variables-save-button'),
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
+ maskableRegex: variableListEl.dataset.maskableRegex,
});
// hide extra auto devops settings based checkbox state
diff --git a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue
index 02451839330..7125790ac3d 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_name_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_name_component.vue
@@ -25,7 +25,7 @@ export default {
};
</script>
<template>
- <span class="ci-job-name-component">
+ <span class="ci-job-name-component mw-100">
<ci-icon :status="status" />
<span class="ci-status-text text-truncate mw-70p gl-pl-1 d-inline-block align-bottom">
{{ name }}
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
index 9dcea557b32..d730ef41b1a 100644
--- a/app/assets/javascripts/pipelines/components/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -4,7 +4,7 @@ import { __, sprintf, s__ } from '../../locale';
import createFlash from '../../flash';
import PipelinesService from '../services/pipelines_service';
import pipelinesMixin from '../mixins/pipelines';
-import TablePagination from '../../vue_shared/components/table_pagination.vue';
+import TablePagination from '../../vue_shared/components/pagination/table_pagination.vue';
import NavigationTabs from '../../vue_shared/components/navigation_tabs.vue';
import NavigationControls from './nav_controls.vue';
import { getParameterByName } from '../../lib/utils/common_utils';
diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue
index 81fe0a48c06..1e4dfe76b26 100644
--- a/app/assets/javascripts/registry/components/table_registry.vue
+++ b/app/assets/javascripts/registry/components/table_registry.vue
@@ -4,7 +4,7 @@ import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { n__ } from '../../locale';
import createFlash from '../../flash';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
-import TablePagination from '../../vue_shared/components/table_pagination.vue';
+import TablePagination from '../../vue_shared/components/pagination/table_pagination.vue';
import Icon from '../../vue_shared/components/icon.vue';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import { errorMessages, errorMessagesTypes } from '../constants';
diff --git a/app/assets/javascripts/reports/components/issues_list.vue b/app/assets/javascripts/reports/components/issues_list.vue
index 50f2910e02d..ee07efea3b0 100644
--- a/app/assets/javascripts/reports/components/issues_list.vue
+++ b/app/assets/javascripts/reports/components/issues_list.vue
@@ -52,11 +52,21 @@ export default {
required: false,
default: '',
},
- showReportSectionStatus: {
+ showReportSectionStatusIcon: {
type: Boolean,
required: false,
default: true,
},
+ issuesUlElementClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ issueItemClass: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
computed: {
issuesWithState() {
@@ -67,6 +77,9 @@ export default {
...this.resolvedIssues.map(wrapIssueWithState(STATUS_SUCCESS)),
];
},
+ wclass() {
+ return `report-block-list ${this.issuesUlElementClass}`;
+ },
},
};
</script>
@@ -77,7 +90,7 @@ export default {
:size="$options.typicalReportItemHeight"
class="report-block-container"
wtag="ul"
- wclass="report-block-list"
+ :wclass="wclass"
>
<report-item
v-for="(wrapped, index) in issuesWithState"
@@ -86,7 +99,8 @@ export default {
:status="wrapped.status"
:component="component"
:is-new="wrapped.isNew"
- :show-report-section-status="showReportSectionStatus"
+ :show-report-section-status-icon="showReportSectionStatusIcon"
+ :class="issueItemClass"
/>
</smart-virtual-list>
</template>
diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue
index 241185e3126..3d576caaf8f 100644
--- a/app/assets/javascripts/reports/components/report_section.vue
+++ b/app/assets/javascripts/reports/components/report_section.vue
@@ -3,10 +3,7 @@ import { __ } from '~/locale';
import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
import Popover from '~/vue_shared/components/help_popover.vue';
import IssuesList from './issues_list.vue';
-
-const LOADING = 'LOADING';
-const ERROR = 'ERROR';
-const SUCCESS = 'SUCCESS';
+import { status } from '../constants';
export default {
name: 'ReportSection',
@@ -42,7 +39,8 @@ export default {
},
successText: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
unresolvedIssues: {
type: Array,
@@ -78,6 +76,21 @@ export default {
required: false,
default: true,
},
+ issuesUlElementClass: {
+ type: String,
+ required: false,
+ default: undefined,
+ },
+ issuesListContainerClass: {
+ type: String,
+ required: false,
+ default: undefined,
+ },
+ issueItemClass: {
+ type: String,
+ required: false,
+ default: undefined,
+ },
},
data() {
@@ -91,13 +104,13 @@ export default {
return this.isCollapsed ? __('Expand') : __('Collapse');
},
isLoading() {
- return this.status === LOADING;
+ return this.status === status.LOADING;
},
loadingFailed() {
- return this.status === ERROR;
+ return this.status === status.ERROR;
},
isSuccess() {
- return this.status === SUCCESS;
+ return this.status === status.SUCCESS;
},
isCollapsible() {
return !this.alwaysOpen && this.hasIssues;
@@ -132,6 +145,15 @@ export default {
hasPopover() {
return Object.keys(this.popoverOptions).length > 0;
},
+ slotName() {
+ if (this.isSuccess) {
+ return 'success';
+ } else if (this.isLoading) {
+ return 'loading';
+ }
+
+ return 'error';
+ },
},
methods: {
toggleCollapsed() {
@@ -147,6 +169,7 @@ export default {
<div class="media-body d-flex flex-align-self-center">
<span class="js-code-text code-text">
{{ headerText }}
+ <slot :name="slotName"></slot>
<popover v-if="hasPopover" :options="popoverOptions" class="prepend-left-5" />
</span>
@@ -172,6 +195,9 @@ export default {
:neutral-issues="neutralIssues"
:component="component"
:show-report-section-status-icon="showReportSectionStatusIcon"
+ :issues-ul-element-class="issuesUlElementClass"
+ :class="issuesListContainerClass"
+ :issue-item-class="issueItemClass"
/>
</slot>
</div>
diff --git a/app/assets/javascripts/reports/constants.js b/app/assets/javascripts/reports/constants.js
index c323dc543f3..66ac1af062b 100644
--- a/app/assets/javascripts/reports/constants.js
+++ b/app/assets/javascripts/reports/constants.js
@@ -16,3 +16,9 @@ export const STATUS_NEUTRAL = 'neutral';
export const ICON_WARNING = 'warning';
export const ICON_SUCCESS = 'success';
export const ICON_NOTFOUND = 'notfound';
+
+export const status = {
+ LOADING: 'LOADING',
+ ERROR: 'ERROR',
+ SUCCESS: 'SUCCESS',
+};
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index d2198bcccfe..0357a0e44c3 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -135,6 +135,7 @@ export default {
:path="entry.flatPath"
:type="entry.type"
:url="entry.webUrl"
+ :lfs-oid="entry.lfsOid"
/>
</template>
</tbody>
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index 764882a7936..4519f82fc93 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -1,8 +1,13 @@
<script>
+import { GlBadge } from '@gitlab/ui';
+import { visitUrl } from '~/lib/utils/url_utility';
import { getIconName } from '../../utils/icon';
import getRefMixin from '../../mixins/get_ref';
export default {
+ components: {
+ GlBadge,
+ },
mixins: [getRefMixin],
props: {
id: {
@@ -26,6 +31,11 @@ export default {
required: false,
default: null,
},
+ lfsOid: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
computed: {
routerLinkTo() {
@@ -54,6 +64,8 @@ export default {
openRow() {
if (this.isFolder) {
this.$router.push(this.routerLinkTo);
+ } else {
+ visitUrl(this.url);
}
},
},
@@ -67,6 +79,9 @@ export default {
<component :is="linkComponent" :to="routerLinkTo" :href="url" class="str-truncated">
{{ fullPath }}
</component>
+ <gl-badge v-if="lfsOid" variant="default" class="label-lfs ml-1">
+ LFS
+ </gl-badge>
<template v-if="isSubmodule">
@ <a href="#" class="commit-sha">{{ shortSha }}</a>
</template>
diff --git a/app/assets/javascripts/repository/graphql.js b/app/assets/javascripts/repository/graphql.js
index c64d16ef02a..ef147ec15cb 100644
--- a/app/assets/javascripts/repository/graphql.js
+++ b/app/assets/javascripts/repository/graphql.js
@@ -18,6 +18,7 @@ const defaultClient = createDefaultClient(
cacheConfig: {
fragmentMatcher,
dataIdFromObject: obj => {
+ /* eslint-disable @gitlab/i18n/no-non-i18n-strings */
// eslint-disable-next-line no-underscore-dangle
switch (obj.__typename) {
// We need to create a dynamic ID for each entry
@@ -33,6 +34,7 @@ const defaultClient = createDefaultClient(
// eslint-disable-next-line no-underscore-dangle
return obj.id || obj._id;
}
+ /* eslint-enable @gitlab/i18n/no-non-i18n-strings */
},
},
},
diff --git a/app/assets/javascripts/repository/queries/getFiles.graphql b/app/assets/javascripts/repository/queries/getFiles.graphql
index 7d92bc46455..ef924fde556 100644
--- a/app/assets/javascripts/repository/queries/getFiles.graphql
+++ b/app/assets/javascripts/repository/queries/getFiles.graphql
@@ -45,6 +45,7 @@ query getFiles(
node {
...TreeEntry
webUrl
+ lfsOid
}
}
pageInfo {
diff --git a/app/assets/javascripts/repository/utils/title.js b/app/assets/javascripts/repository/utils/title.js
index 4e194640e92..87d54c01200 100644
--- a/app/assets/javascripts/repository/utils/title.js
+++ b/app/assets/javascripts/repository/utils/title.js
@@ -5,5 +5,6 @@ export const setTitle = (pathMatch, ref, project) => {
const path = pathMatch.replace(/^\//, '');
const isEmpty = path === '';
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
document.title = `${isEmpty ? 'Files' : path} · ${ref} · ${project}`;
};
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index 6aca4067ba7..842fb5e5b4f 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -447,9 +447,11 @@ export class SearchAutocomplete {
onClick(item, $el, e) {
if (window.location.pathname.indexOf(item.url) !== -1) {
if (!e.metaKey) e.preventDefault();
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
if (item.category === 'Projects') {
this.projectInputEl.val(item.id);
}
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
if (item.category === 'Groups') {
this.groupInputEl.val(item.id);
}
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 7e6f02b10af..33cedf78331 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -427,6 +427,7 @@ function UsersSelect(currentUser, els, options = {}) {
const isActive = $el.hasClass('is-active');
const previouslySelected = $dropdown
.closest('.selectbox')
+ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
.find("input[name='" + $dropdown.data('fieldName') + "'][value!=0]");
// Enables support for limiting the number of users selected
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
index 361441640e1..e20a16900d4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
@@ -128,7 +128,7 @@ export default {
:disabled="mr.sourceBranchRemoved"
data-target="#modal_merge_info"
data-toggle="modal"
- class="btn btn-default js-check-out-branch append-right-default"
+ class="btn btn-default js-check-out-branch append-right-8"
type="button"
>
{{ s__('mrWidget|Check out branch') }}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
index 88e1ccbaf35..5958c2cf87e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
@@ -1,15 +1,19 @@
<script>
+import _ from 'underscore';
+import autoMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/auto_merge';
import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue';
import MrWidgetAuthor from '../../components/mr_widget_author.vue';
import eventHub from '../../event_hub';
+import { AUTO_MERGE_STRATEGIES } from '../../constants';
export default {
- name: 'MRWidgetMergeWhenPipelineSucceeds',
+ name: 'MRWidgetAutoMergeEnabled',
components: {
MrWidgetAuthor,
statusIcon,
},
+ mixins: [autoMergeMixin],
props: {
mr: {
type: Object,
@@ -57,7 +61,7 @@ export default {
removeSourceBranch() {
const options = {
sha: this.mr.sha,
- auto_merge_strategy: 'merge_when_pipeline_succeeds',
+ auto_merge_strategy: this.mr.autoMergeStrategy,
should_remove_source_branch: true,
};
@@ -66,7 +70,7 @@ export default {
.merge(options)
.then(res => res.data)
.then(data => {
- if (data.status === 'merge_when_pipeline_succeeds') {
+ if (_.includes(AUTO_MERGE_STRATEGIES, data.status)) {
eventHub.$emit('MRWidgetUpdateRequested');
}
})
@@ -84,9 +88,9 @@ export default {
<div class="media-body">
<h4 class="d-flex align-items-start">
<span class="append-right-10">
- {{ s__('mrWidget|Set by') }}
+ <span class="js-status-text-before-author">{{ statusTextBeforeAuthor }}</span>
<mr-widget-author :author="mr.setToAutoMergeBy" />
- {{ s__('mrWidget|to be merged automatically when the pipeline succeeds') }}
+ <span class="js-status-text-after-author">{{ statusTextAfterAuthor }}</span>
</span>
<a
v-if="mr.canCancelAutomaticMerge"
@@ -97,7 +101,7 @@ export default {
@click.prevent="cancelAutomaticMerge"
>
<i v-if="isCancellingAutoMerge" class="fa fa-spinner fa-spin" aria-hidden="true"> </i>
- {{ s__('mrWidget|Cancel automatic merge') }}
+ {{ cancelButtonText }}
</a>
</h4>
<section class="mr-info-list">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
index f6f445c1cef..3df4a777aca 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
@@ -26,7 +26,7 @@ export default {
);
},
showResolveButton() {
- return this.mr.conflictResolutionPath && this.mr.canMerge;
+ return this.mr.conflictResolutionPath && this.mr.canPushToSourceBranch;
},
showPopover() {
return this.showResolveButton && this.mr.sourceBranchProtected;
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index 615d59a7b8e..ca1b4a57717 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll';
@@ -12,6 +13,7 @@ import SquashBeforeMerge from './squash_before_merge.vue';
import CommitsHeader from './commits_header.vue';
import CommitEdit from './commit_edit.vue';
import CommitMessageDropdown from './commit_message_dropdown.vue';
+import { AUTO_MERGE_STRATEGIES } from '../../constants';
export default {
name: 'ReadyToMerge',
@@ -30,8 +32,6 @@ export default {
data() {
return {
removeSourceBranch: this.mr.shouldRemoveSourceBranch,
- mergeWhenBuildSucceeds: false,
- autoMergeStrategy: undefined,
isMakingRequest: false,
isMergingImmediately: false,
commitMessage: this.mr.commitMessage,
@@ -42,18 +42,18 @@ export default {
};
},
computed: {
- shouldShowAutoMergeText() {
- return this.mr.isPipelineActive;
+ isAutoMergeAvailable() {
+ return !_.isEmpty(this.mr.availableAutoMergeStrategies);
},
status() {
- const { pipeline, isPipelineActive, isPipelineFailed, hasCI, ciStatus } = this.mr;
+ const { pipeline, isPipelineFailed, hasCI, ciStatus } = this.mr;
if (hasCI && !ciStatus) {
return 'failed';
+ } else if (this.isAutoMergeAvailable) {
+ return 'pending';
} else if (!pipeline) {
return 'success';
- } else if (isPipelineActive) {
- return 'pending';
} else if (isPipelineFailed) {
return 'failed';
}
@@ -87,14 +87,14 @@ export default {
mergeButtonText() {
if (this.isMergingImmediately) {
return __('Merge in progress');
- } else if (this.shouldShowAutoMergeText) {
- return __('Merge when pipeline succeeds');
+ } else if (this.isAutoMergeAvailable) {
+ return this.autoMergeText;
}
- return 'Merge';
+ return __('Merge');
},
shouldShowMergeOptionsDropdown() {
- return this.mr.isPipelineActive && !this.mr.onlyAllowMergeIfPipelineSucceeds;
+ return this.isAutoMergeAvailable && !this.mr.onlyAllowMergeIfPipelineSucceeds;
},
isRemoveSourceBranchButtonDisabled() {
return this.isMergeButtonDisabled;
@@ -104,7 +104,7 @@ export default {
return enableSquashBeforeMerge && commitsCount > 1;
},
shouldShowMergeControls() {
- return this.mr.isMergeAllowed || this.shouldShowAutoMergeText;
+ return this.mr.isMergeAllowed || this.isAutoMergeAvailable;
},
shouldShowSquashEdit() {
return this.squashBeforeMerge && this.shouldShowSquashBeforeMerge;
@@ -118,20 +118,15 @@ export default {
const { commitMessageWithDescription, commitMessage } = this.mr;
this.commitMessage = includeDescription ? commitMessageWithDescription : commitMessage;
},
- handleMergeButtonClick(mergeWhenBuildSucceeds, mergeImmediately) {
- // TODO: Remove no-param-reassign
- if (mergeWhenBuildSucceeds === undefined) {
- mergeWhenBuildSucceeds = this.mr.isPipelineActive; // eslint-disable-line no-param-reassign
- } else if (mergeImmediately) {
+ handleMergeButtonClick(useAutoMerge, mergeImmediately = false) {
+ if (mergeImmediately) {
this.isMergingImmediately = true;
}
- this.autoMergeStrategy = mergeWhenBuildSucceeds ? 'merge_when_pipeline_succeeds' : undefined;
-
const options = {
sha: this.mr.sha,
commit_message: this.commitMessage,
- auto_merge_strategy: this.autoMergeStrategy,
+ auto_merge_strategy: useAutoMerge ? this.mr.preferredAutoMergeStrategy : undefined,
should_remove_source_branch: this.removeSourceBranch === true,
squash: this.squashBeforeMerge,
squash_commit_message: this.squashCommitMessage,
@@ -144,7 +139,7 @@ export default {
.then(data => {
const hasError = data.status === 'failed' || data.status === 'hook_validation_error';
- if (data.status === 'merge_when_pipeline_succeeds') {
+ if (_.includes(AUTO_MERGE_STRATEGIES, data.status)) {
eventHub.$emit('MRWidgetUpdateRequested');
} else if (data.status === 'success') {
this.initiateMergePolling();
@@ -242,13 +237,13 @@ export default {
:class="mergeButtonClass"
type="button"
class="qa-merge-button"
- @click="handleMergeButtonClick()"
+ @click="handleMergeButtonClick(isAutoMergeAvailable)"
>
<i v-if="isMakingRequest" class="fa fa-spinner fa-spin" aria-hidden="true"></i>
{{ mergeButtonText }}
</button>
<button
- v-if="shouldShowMergeOptionsDropdown"
+ v-if="isAutoMergeAvailable"
:disabled="isMergeButtonDisabled"
type="button"
class="btn btn-sm btn-info dropdown-toggle js-merge-moment"
@@ -264,15 +259,13 @@ export default {
>
<li>
<a
- class="merge_when_pipeline_succeeds qa-merge-when-pipeline-succeeds-option"
+ class="auto_merge_enabled qa-merge-when-pipeline-succeeds-option"
href="#"
@click.prevent="handleMergeButtonClick(true)"
>
<span class="media">
<span class="merge-opt-icon" aria-hidden="true" v-html="successSvg"></span>
- <span class="media-body merge-opt-title">{{
- __('Merge when pipeline succeeds')
- }}</span>
+ <span class="media-body merge-opt-title">{{ autoMergeText }}</span>
</span>
</a>
</li>
diff --git a/app/assets/javascripts/vue_merge_request_widget/constants.js b/app/assets/javascripts/vue_merge_request_widget/constants.js
index 0a29d55fbd6..3e65bdf0cb0 100644
--- a/app/assets/javascripts/vue_merge_request_widget/constants.js
+++ b/app/assets/javascripts/vue_merge_request_widget/constants.js
@@ -3,3 +3,13 @@ export const DANGER = 'danger';
export const WARNING_MESSAGE_CLASS = 'warning_message';
export const DANGER_MESSAGE_CLASS = 'danger_message';
+
+export const MWPS_MERGE_STRATEGY = 'merge_when_pipeline_succeeds';
+export const ATMTWPS_MERGE_STRATEGY = 'add_to_merge_train_when_pipeline_succeeds';
+export const MT_MERGE_STRATEGY = 'merge_train';
+
+export const AUTO_MERGE_STRATEGIES = [
+ MWPS_MERGE_STRATEGY,
+ ATMTWPS_MERGE_STRATEGY,
+ MT_MERGE_STRATEGY,
+];
diff --git a/app/assets/javascripts/vue_merge_request_widget/mixins/auto_merge.js b/app/assets/javascripts/vue_merge_request_widget/mixins/auto_merge.js
new file mode 100644
index 00000000000..23e140623cc
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/mixins/auto_merge.js
@@ -0,0 +1,15 @@
+import { s__ } from '~/locale';
+
+export default {
+ computed: {
+ statusTextBeforeAuthor() {
+ return s__('mrWidget|Set by');
+ },
+ statusTextAfterAuthor() {
+ return s__('mrWidget|to be merged automatically when the pipeline succeeds');
+ },
+ cancelButtonText() {
+ return s__('mrWidget|Cancel automatic merge');
+ },
+ },
+};
diff --git a/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
index b2e64506472..116d537c463 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
@@ -1,3 +1,5 @@
+import { __ } from '~/locale';
+
export default {
computed: {
isMergeButtonDisabled() {
@@ -9,5 +11,9 @@ export default {
this.mr.preventMerge,
);
},
+ autoMergeText() {
+ // MWPS is currently the only auto merge strategy available in CE
+ return __('Merge when pipeline succeeds');
+ },
},
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index d02bb2f341d..41386178a1e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -29,7 +29,7 @@ import UnresolvedDiscussionsState from './components/states/unresolved_discussio
import PipelineBlockedState from './components/states/mr_widget_pipeline_blocked.vue';
import PipelineFailedState from './components/states/pipeline_failed.vue';
import FailedToMerge from './components/states/mr_widget_failed_to_merge.vue';
-import MergeWhenPipelineSucceedsState from './components/states/mr_widget_merge_when_pipeline_succeeds.vue';
+import MrWidgetAutoMergeEnabled from './components/states/mr_widget_auto_merge_enabled.vue';
import AutoMergeFailed from './components/states/mr_widget_auto_merge_failed.vue';
import CheckingState from './components/states/mr_widget_checking.vue';
import eventHub from './event_hub';
@@ -64,7 +64,7 @@ export default {
'mr-widget-unresolved-discussions': UnresolvedDiscussionsState,
'mr-widget-pipeline-blocked': PipelineBlockedState,
'mr-widget-pipeline-failed': PipelineFailedState,
- 'mr-widget-merge-when-pipeline-succeeds': MergeWhenPipelineSucceedsState,
+ MrWidgetAutoMergeEnabled,
'mr-widget-auto-merge-failed': AutoMergeFailed,
'mr-widget-rebase': RebaseState,
SourceBranchRemovalStatus,
@@ -117,14 +117,6 @@ export default {
this.mr.mergePipelinesEnabled && this.mr.sourceProjectId !== this.mr.targetProjectId,
);
},
- showTargetBranchAdvancedError() {
- return Boolean(
- this.mr.isOpen &&
- this.mr.pipeline &&
- this.mr.pipeline.target_sha &&
- this.mr.pipeline.target_sha !== this.mr.targetBranchSha,
- );
- },
mergeError() {
return sprintf(s__('mrWidget|Merge failed: %{mergeError}. Please try again.'), {
mergeError: this.mr.mergeError,
@@ -363,18 +355,6 @@ export default {
}}
</mr-widget-alert-message>
- <mr-widget-alert-message
- v-if="showTargetBranchAdvancedError"
- type="danger"
- :help-path="mr.mergeRequestPipelinesHelpPath"
- >
- {{
- s__(
- 'mrWidget|The target branch has advanced, which invalidates the merge request pipeline. Please update the source branch and retry merging',
- )
- }}
- </mr-widget-alert-message>
-
<mr-widget-alert-message v-if="mr.mergeError" type="danger">
{{ mergeError }}
</mr-widget-alert-message>
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index 32badb0fb08..bfa3e7f4a59 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -1,7 +1,9 @@
import Timeago from 'timeago.js';
+import _ from 'underscore';
import getStateKey from 'ee_else_ce/vue_merge_request_widget/stores/get_state_key';
import { stateKey } from './state_maps';
import { formatDate } from '../../lib/utils/datetime_utility';
+import { ATMTWPS_MERGE_STRATEGY, MT_MERGE_STRATEGY, MWPS_MERGE_STRATEGY } from '../constants';
export default class MergeRequestStore {
constructor(data) {
@@ -77,6 +79,10 @@ export default class MergeRequestStore {
this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false;
this.autoMergeEnabled = Boolean(data.auto_merge_enabled);
this.autoMergeStrategy = data.auto_merge_strategy;
+ this.availableAutoMergeStrategies = data.available_auto_merge_strategies;
+ this.preferredAutoMergeStrategy = MergeRequestStore.getPreferredAutoMergeStrategy(
+ this.availableAutoMergeStrategies,
+ );
this.mergePath = data.merge_path;
this.ffOnlyEnabled = data.ff_only_enabled;
this.shouldBeRebased = Boolean(data.should_be_rebased);
@@ -104,7 +110,9 @@ export default class MergeRequestStore {
this.sourceProjectFullPath = data.source_project_full_path;
this.sourceProjectId = data.source_project_id;
this.targetProjectId = data.target_project_id;
- this.mergePipelinesEnabled = data.merge_pipelines_enabled;
+ this.mergePipelinesEnabled = Boolean(data.merge_pipelines_enabled);
+ this.mergeTrainsCount = data.merge_trains_count || 0;
+ this.mergeTrainIndex = data.merge_train_index;
// Cherry-pick and Revert actions related
this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false;
@@ -204,4 +212,16 @@ export default class MergeRequestStore {
return timeagoInstance.format(date);
}
+
+ static getPreferredAutoMergeStrategy(availableAutoMergeStrategies) {
+ if (_.includes(availableAutoMergeStrategies, ATMTWPS_MERGE_STRATEGY)) {
+ return ATMTWPS_MERGE_STRATEGY;
+ } else if (_.includes(availableAutoMergeStrategies, MT_MERGE_STRATEGY)) {
+ return MT_MERGE_STRATEGY;
+ } else if (_.includes(availableAutoMergeStrategies, MWPS_MERGE_STRATEGY)) {
+ return MWPS_MERGE_STRATEGY;
+ }
+
+ return undefined;
+ }
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
index 48bc6a867f4..28507bba3e5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/state_maps.js
@@ -13,7 +13,7 @@ const stateToComponentMap = {
unresolvedDiscussions: 'mr-widget-unresolved-discussions',
pipelineBlocked: 'mr-widget-pipeline-blocked',
pipelineFailed: 'mr-widget-pipeline-failed',
- autoMergeEnabled: 'mr-widget-merge-when-pipeline-succeeds',
+ autoMergeEnabled: 'mr-widget-auto-merge-enabled',
failedToMerge: 'mr-widget-failed-to-merge',
autoMergeFailed: 'mr-widget-auto-merge-failed',
shaMismatch: 'sha-mismatch',
diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue
index 3ba946e6447..a1168fa0f1e 100644
--- a/app/assets/javascripts/vue_shared/components/commit.vue
+++ b/app/assets/javascripts/vue_shared/components/commit.vue
@@ -1,6 +1,7 @@
<script>
import _ from 'underscore';
import { GlTooltipDirective, GlLink } from '@gitlab/ui';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import UserAvatarLink from './user_avatar/user_avatar_link.vue';
import Icon from '../../vue_shared/components/icon.vue';
@@ -12,6 +13,7 @@ export default {
UserAvatarLink,
Icon,
GlLink,
+ TooltipOnTruncate,
},
props: {
/**
@@ -165,7 +167,7 @@ export default {
<gl-link :href="commitUrl" class="commit-sha mr-0"> {{ shortSha }} </gl-link>
<div class="commit-title flex-truncate-parent">
- <span v-if="title" class="flex-truncate-child">
+ <tooltip-on-truncate v-if="title" class="flex-truncate-child" :title="title">
<user-avatar-link
v-if="hasAuthor"
:link-href="author.path"
@@ -174,8 +176,10 @@ export default {
:tooltip-text="author.username"
class="avatar-image-container"
/>
- <gl-link :href="commitUrl" class="commit-row-message cgray"> {{ title }} </gl-link>
- </span>
+ <gl-link :href="commitUrl" class="commit-row-message cgray">
+ {{ title }}
+ </gl-link>
+ </tooltip-on-truncate>
<span v-else> Can't find HEAD commit for this branch </span>
</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
index e92babc499b..e438ff16a41 100644
--- a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
+++ b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
@@ -1,9 +1,17 @@
<script>
+import { GlLink } from '@gitlab/ui';
+import _ from 'underscore';
+import { sprintf } from '~/locale';
import icon from '../../../vue_shared/components/icon.vue';
+function buildDocsLinkStart(path) {
+ return `<a href="${_.escape(path)}" target="_blank" rel="noopener noreferrer">`;
+}
+
export default {
components: {
icon,
+ GlLink,
},
props: {
isLocked: {
@@ -16,6 +24,16 @@ export default {
default: false,
required: false,
},
+ lockedIssueDocsPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ confidentialIssueDocsPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
warningIcon() {
@@ -27,6 +45,17 @@ export default {
isLockedAndConfidential() {
return this.isConfidential && this.isLocked;
},
+ confidentialAndLockedDiscussionText() {
+ return sprintf(
+ 'This issue is %{confidentialLinkStart}confidential%{linkEnd} and %{lockedLinkStart}locked%{linkEnd}.',
+ {
+ confidentialLinkStart: buildDocsLinkStart(this.confidentialIssueDocsPath),
+ lockedLinkStart: buildDocsLinkStart(this.lockedIssueDocsPath),
+ linkEnd: '</a>',
+ },
+ false,
+ );
+ },
},
};
</script>
@@ -35,20 +64,26 @@ export default {
<icon v-if="!isLockedAndConfidential" :name="warningIcon" :size="16" class="icon inline" />
<span v-if="isLockedAndConfidential">
- {{ __('This issue is confidential and locked.') }}
+ <span v-html="confidentialAndLockedDiscussionText"></span>
{{
- __(`People without permission will never
-get a notification and won't be able to comment.`)
+ __(`People without permission will never get a notification and won't be able to comment.`)
}}
</span>
<span v-else-if="isConfidential">
{{ __('This is a confidential issue.') }}
- {{ __('Your comment will not be visible to the public.') }}
+ {{ __('People without permission will never get a notification.') }}
+ <gl-link :href="confidentialIssueDocsPath" target="_blank">
+ {{ __('Learn more') }}
+ </gl-link>
</span>
<span v-else-if="isLocked">
- {{ __('This issue is locked.') }} {{ __('Only project members can comment.') }}
+ {{ __('This issue is locked.') }}
+ {{ __('Only project members can comment.') }}
+ <gl-link :href="lockedIssueDocsPath" target="_blank">
+ {{ __('Learn more') }}
+ </gl-link>
</span>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
index b807a35b421..05ad7710a62 100644
--- a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
+++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
@@ -24,6 +24,11 @@ export default {
required: false,
default: false,
},
+ greyLinkWhenMerged: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
stateTitle() {
@@ -36,6 +41,11 @@ export default {
},
);
},
+ issueableLinkClass() {
+ return this.greyLinkWhenMerged
+ ? `sortable-link ${this.state === 'merged' ? ' text-secondary' : ''}`
+ : 'sortable-link';
+ },
},
};
</script>
@@ -69,7 +79,7 @@ export default {
class="confidential-icon append-right-4 align-self-baseline align-self-md-auto mt-xl-0"
:aria-label="__('Confidential')"
/>
- <a :href="computedPath" class="sortable-link">{{ title }}</a>
+ <a :href="computedPath" :class="issueableLinkClass">{{ title }}</a>
</div>
<div class="item-meta d-flex flex-wrap mt-xl-0 justify-content-xl-end flex-xl-nowrap">
<div
diff --git a/app/assets/javascripts/vue_shared/components/modal_copy_button.vue b/app/assets/javascripts/vue_shared/components/modal_copy_button.vue
new file mode 100644
index 00000000000..bf59a6abf3f
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/modal_copy_button.vue
@@ -0,0 +1,121 @@
+<script>
+import $ from 'jquery';
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { __ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
+import Clipboard from 'clipboard';
+
+export default {
+ components: {
+ GlButton,
+ Icon,
+ },
+
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+
+ props: {
+ text: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ container: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ modalId: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ target: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ tooltipPlacement: {
+ type: String,
+ required: false,
+ default: 'top',
+ },
+ tooltipContainer: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+
+ copySuccessText: __('Copied'),
+
+ computed: {
+ modalDomId() {
+ return this.modalId ? `#${this.modalId}` : '';
+ },
+ },
+
+ mounted() {
+ this.$nextTick(() => {
+ this.clipboard = new Clipboard(this.$el, {
+ container:
+ document.querySelector(`${this.modalDomId} div.modal-content`) ||
+ document.getElementById(this.container) ||
+ document.body,
+ });
+ this.clipboard
+ .on('success', e => {
+ this.updateTooltip(e.trigger);
+ this.$emit('success', e);
+ // Clear the selection and blur the trigger so it loses its border
+ e.clearSelection();
+ $(e.trigger).blur();
+ })
+ .on('error', e => this.$emit('error', e));
+ });
+ },
+
+ destroyed() {
+ if (this.clipboard) {
+ this.clipboard.destroy();
+ }
+ },
+
+ methods: {
+ updateTooltip(target) {
+ const $target = $(target);
+ const originalTitle = $target.data('originalTitle');
+
+ if ($target.tooltip) {
+ /**
+ * The original tooltip will continue staying there unless we remove it by hand.
+ * $target.tooltip('hide') isn't working.
+ */
+ $('.tooltip').remove();
+ $target.attr('title', this.$options.copySuccessText);
+ $target.tooltip('_fixTitle');
+ $target.tooltip('show');
+ $target.attr('title', originalTitle);
+ $target.tooltip('_fixTitle');
+ }
+ },
+ },
+};
+</script>
+<template>
+ <gl-button
+ v-gl-tooltip="{ placement: tooltipPlacement, container: tooltipContainer }"
+ :data-clipboard-target="target"
+ :data-clipboard-text="text"
+ :title="title"
+ >
+ <slot>
+ <icon name="duplicate" />
+ </slot>
+ </gl-button>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
index a50f49c1279..baed26a157c 100644
--- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
@@ -51,7 +51,7 @@ export default {
<div class="note-header">
<div class="note-header-info">
<a :href="getUserData.path">
- <span class="d-none d-sm-inline-block">{{ getUserData.name }}</span>
+ <span class="d-none d-sm-inline-block bold">{{ getUserData.name }}</span>
<span class="note-headline-light">@{{ getUserData.username }}</span>
</a>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/pagination/constants.js b/app/assets/javascripts/vue_shared/components/pagination/constants.js
new file mode 100644
index 00000000000..c24b142ac7e
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/pagination/constants.js
@@ -0,0 +1,9 @@
+import { s__ } from '~/locale';
+
+export const PAGINATION_UI_BUTTON_LIMIT = 4;
+export const UI_LIMIT = 6;
+export const SPREAD = '...';
+export const PREV = s__('Pagination|Prev');
+export const NEXT = s__('Pagination|Next');
+export const FIRST = s__('Pagination|« First');
+export const LAST = s__('Pagination|Last »');
diff --git a/app/assets/javascripts/vue_shared/components/pagination/graphql_pagination.vue b/app/assets/javascripts/vue_shared/components/pagination/graphql_pagination.vue
new file mode 100644
index 00000000000..53e473432db
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/pagination/graphql_pagination.vue
@@ -0,0 +1,47 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import { PREV, NEXT } from '~/vue_shared/components/pagination/constants';
+
+/**
+ * Pagination Component for graphql API
+ */
+export default {
+ name: 'GraphqlPaginationComponent',
+ components: {
+ GlButton,
+ },
+ labels: {
+ prev: PREV,
+ next: NEXT,
+ },
+ props: {
+ hasNextPage: {
+ required: true,
+ type: Boolean,
+ },
+ hasPreviousPage: {
+ required: true,
+ type: Boolean,
+ },
+ },
+};
+</script>
+<template>
+ <div class="justify-content-center d-flex prepend-top-default">
+ <div class="btn-group">
+ <gl-button
+ class="js-prev-btn page-link"
+ :disabled="!hasPreviousPage"
+ @click="$emit('previousClicked')"
+ >{{ $options.labels.prev }}</gl-button
+ >
+
+ <gl-button
+ class="js-next-btn page-link"
+ :disabled="!hasNextPage"
+ @click="$emit('nextClicked')"
+ >{{ $options.labels.next }}</gl-button
+ >
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
index 9cce9a4e542..1e2d4ffa7e3 100644
--- a/app/assets/javascripts/vue_shared/components/table_pagination.vue
+++ b/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
@@ -1,13 +1,13 @@
<script>
-import { s__ } from '../../locale';
-
-const PAGINATION_UI_BUTTON_LIMIT = 4;
-const UI_LIMIT = 6;
-const SPREAD = '...';
-const PREV = s__('Pagination|Prev');
-const NEXT = s__('Pagination|Next');
-const FIRST = s__('Pagination|« First');
-const LAST = s__('Pagination|Last »');
+import {
+ PAGINATION_UI_BUTTON_LIMIT,
+ UI_LIMIT,
+ SPREAD,
+ PREV,
+ NEXT,
+ FIRST,
+ LAST,
+} from '~/vue_shared/components/pagination/constants';
export default {
props: {
diff --git a/app/assets/javascripts/vue_shared/models/label.js b/app/assets/javascripts/vue_shared/models/label.js
deleted file mode 100644
index 2d2732d0661..00000000000
--- a/app/assets/javascripts/vue_shared/models/label.js
+++ /dev/null
@@ -1,13 +0,0 @@
-export default class ListLabel {
- constructor(obj) {
- this.id = obj.id;
- this.title = obj.title;
- this.type = obj.type;
- this.color = obj.color;
- this.textColor = obj.text_color;
- this.description = obj.description;
- this.priority = obj.priority !== null ? obj.priority : Infinity;
- }
-}
-
-window.ListLabel = ListLabel;
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index 7f6384f4eea..802d58779d0 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -147,11 +147,6 @@ table {
pointer-events: none;
}
-.popover,
-.popover-header {
- font-size: 14px;
-}
-
@each $breakpoint in map-keys($grid-breakpoints) {
@include media-breakpoint-up($breakpoint) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
diff --git a/app/assets/stylesheets/components/avatar.scss b/app/assets/stylesheets/components/avatar.scss
index 1afa5ed90f4..8e9650cdf34 100644
--- a/app/assets/stylesheets/components/avatar.scss
+++ b/app/assets/stylesheets/components/avatar.scss
@@ -191,12 +191,5 @@ $identicon-backgrounds: $identicon-red, $identicon-purple, $identicon-indigo, $i
}
.avatar-counter {
- background-color: $gray-darkest;
- color: $white-light;
- border: 1px solid $gray-normal;
- border-radius: 1em;
- font-family: $regular-font;
- font-size: 9px;
- line-height: 16px;
- text-align: center;
+ @include avatar-counter();
}
diff --git a/app/assets/stylesheets/components/popover.scss b/app/assets/stylesheets/components/popover.scss
index d0aa6ec78aa..58aaca93160 100644
--- a/app/assets/stylesheets/components/popover.scss
+++ b/app/assets/stylesheets/components/popover.scss
@@ -1,37 +1,98 @@
.popover {
- min-width: 300px;
-
- .popover-body .user-popover {
- padding: $gl-padding-8;
- font-size: $gl-font-size-small;
- line-height: $gl-line-height;
-
- .category-icon {
- color: $gray-600;
- }
- }
+ max-width: $popover-max-width;
+ border: 1px solid $gray-200;
+ box-shadow: 0 2px 3px 1px $gray-200;
+ font-size: $gl-font-size-small;
+ /**
+ * Blue popover variation
+ */
&.blue {
background-color: $blue-600;
+ border-color: $blue-600;
.popover-body {
color: $white-light;
}
&.bs-popover-bottom {
+ .arrow::before,
.arrow::after {
border-bottom-color: $blue-600;
}
}
&.bs-popover-top {
+ .arrow::before,
.arrow::after {
border-top-color: $blue-600;
}
}
+
+ &.bs-popover-right {
+ .arrow::after,
+ .arrow::before {
+ border-right-color: $blue-600;
+ }
+ }
+
+ &.bs-popover-left {
+ .arrow::before,
+ .arrow::after {
+ border-left-color: $blue-600;
+ }
+ }
}
}
+.bs-popover-top {
+ /* When popover position is top, the arrow is translated 1 pixel
+ * due to the box-shadow include in our custom styles.
+ */
+ > .arrow::before {
+ border-top-color: $gray-200;
+ bottom: 1px;
+ }
+
+ > .arrow::after {
+ bottom: 2px;
+ }
+}
+
+.bs-popover-bottom {
+ > .arrow::before {
+ border-bottom-color: $gray-200;
+ }
+
+ > .popover-header::before {
+ border-color: $white-light;
+ }
+}
+
+.bs-popover-right > .arrow::before {
+ border-right-color: $gray-200;
+}
+
+.bs-popover-left > .arrow::before {
+ border-left-color: $gray-200;
+}
+
+.popover-header {
+ background-color: $white-light;
+ font-size: $gl-font-size-small;
+}
+
+.popover-body {
+ padding: $gl-padding $gl-padding-12;
+
+ > .popover-hr {
+ margin: $gl-padding 0;
+ }
+}
+
+/**
+* mr_popover component
+*/
.mr-popover {
.text-secondary {
font-size: 12px;
@@ -39,6 +100,37 @@
}
}
+.onboarding-popover {
+ box-shadow: 0 2px 4px $dropdown-shadow-color;
+
+ .popover-body {
+ font-size: $gl-font-size;
+ line-height: $gl-line-height;
+ padding: $gl-padding;
+ }
+
+ .popover-header {
+ display: none;
+ }
+
+ .accept-mr-label {
+ background-color: $accepting-mr-label-color;
+ color: $white-light;
+ }
+}
+
+/**
+* user_popover component
+*/
+.user-popover {
+ padding: $gl-padding-8;
+ line-height: $gl-line-height;
+
+ .category-icon {
+ color: $gray-600;
+ }
+}
+
.onboarding-welcome-page {
.popover {
min-width: auto;
diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss
index 257d788873c..6f5a2e561af 100644
--- a/app/assets/stylesheets/framework/animations.scss
+++ b/app/assets/stylesheets/framework/animations.scss
@@ -268,3 +268,27 @@ $skeleton-line-widths: (
@include webkit-prefix(animation-duration, 1s);
transform-origin: 50% 50%;
}
+
+/* ----------------------------------------------
+ * Generated by Animista on 2019-4-26 17:40:41
+ * w: http://animista.net, t: @cssanimista
+ * ---------------------------------------------- */
+@keyframes slide-in-fwd-bottom {
+ 0% {
+ transform: translateZ(-1400px) translateY(800px);
+ opacity: 0;
+ }
+
+ 100% {
+ transform: translateZ(0) translateY(0);
+ opacity: 1;
+ }
+}
+
+.slide-in-fwd-bottom-enter-active {
+ animation: slide-in-fwd-bottom 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
+}
+
+.slide-in-fwd-bottom-leave-active {
+ animation: slide-in-fwd-bottom 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) both reverse;
+}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 3aabb66f7a6..65c0ee74c60 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -199,6 +199,7 @@
&.user-cover-block {
padding: 24px 0 0;
+ border-bottom: 1px solid $border-color;
.nav-links {
width: 100%;
@@ -232,14 +233,6 @@
margin-top: -1px;
}
-.nav-block {
- .controls {
- float: right;
- margin-top: 8px;
- padding-bottom: 8px;
- }
-}
-
.content-block {
padding: $gl-padding 0;
border-bottom: 1px solid $white-dark;
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 97a763671ba..767832e242c 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -249,7 +249,7 @@
padding: 6px 16px;
border-color: $border-color;
color: $gray-darkest;
- background-color: $gray-light;
+ background-color: $white-light;
&:hover,
&:active,
@@ -258,7 +258,6 @@
box-shadow: none;
border-color: lighten($blue-300, 20%);
color: $gray-darkest;
- background-color: $gray-light;
}
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index fc488b85138..db09118ba15 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -489,3 +489,50 @@ img.emoji {
.cursor-pointer {
cursor: pointer;
}
+
+// Make buttons/dropdowns full-width on mobile
+.full-width-mobile {
+ @include media-breakpoint-down(xs) {
+ width: 100%;
+
+ > .dropdown-menu,
+ > .btn {
+ width: 100%;
+ }
+ }
+}
+
+.onboarding-helper-container {
+ bottom: 40px;
+ right: 40px;
+ font-size: $gl-font-size-small;
+ background: $gray-100;
+ width: 200px;
+ border-radius: 24px;
+ box-shadow: 0 2px 4px $issue-boards-card-shadow;
+ z-index: 10000;
+
+ .collapsible {
+ max-height: 0;
+ transition: max-height 0.5s cubic-bezier(0, 1, 0, 1);
+ }
+
+ &.expanded {
+ border-bottom-right-radius: $border-radius-default;
+ border-bottom-left-radius: $border-radius-default;
+
+ .collapsible {
+ max-height: 1000px;
+ transition: max-height 1s ease-in-out;
+ }
+ }
+
+ .avatar {
+ border-color: darken($gray-normal, 10%);
+
+ img {
+ width: 32px;
+ height: 32px;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/feature_highlight.scss b/app/assets/stylesheets/framework/feature_highlight.scss
index 85cabf43e9e..f9b167669a6 100644
--- a/app/assets/stylesheets/framework/feature_highlight.scss
+++ b/app/assets/stylesheets/framework/feature_highlight.scss
@@ -39,7 +39,7 @@
display: none;
hr {
- margin: $gl-padding * 0.5 0;
+ margin: $gl-padding 0;
}
.btn-link {
@@ -71,9 +71,6 @@
.feature-highlight-popover {
width: 240px;
- padding: 0;
- border: 1px solid $border-color;
- box-shadow: 0 2px 4px $dropdown-shadow-color;
&.right > .arrow {
border-right-color: $border-color;
@@ -85,7 +82,7 @@
}
.feature-highlight-popover-sub-content {
- padding: 9px 14px;
+ padding: $gl-padding $gl-padding-12;
}
@include keyframes(pulse-highlight) {
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index ef6f0633150..536a26a6ffe 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -453,6 +453,28 @@ span.idiff {
}
}
+ .note-container {
+ .user-avatar-link.new-comment {
+ position: absolute;
+ margin: 40px $gl-padding 0 116px;
+
+ ~ .note-edit-form form.edit-note {
+ @include media-breakpoint-up(sm) {
+ margin-left: $note-icon-gutter-width;
+ }
+ }
+ }
+ }
+
+ .diff-discussions:not(:last-child) .discussion .discussion-body {
+ padding-bottom: $gl-padding;
+
+ .discussion-reply-holder {
+ border-bottom: 1px solid $gray-100;
+ border-radius: 0;
+ }
+ }
+
.md-previewer {
padding: $gl-padding;
}
diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss
index 741f92110c3..983bd032da4 100644
--- a/app/assets/stylesheets/framework/highlight.scss
+++ b/app/assets/stylesheets/framework/highlight.scss
@@ -11,7 +11,7 @@
border-radius: 0 0 $border-radius-default $border-radius-default;
font-family: $monospace-font;
font-size: $code-font-size;
- line-height: 19px;
+ line-height: 1.5;
margin: 0;
overflow: auto;
overflow-y: hidden;
@@ -30,7 +30,7 @@
.line {
display: block;
width: 100%;
- min-height: 19px;
+ min-height: 1.5em;
padding-left: 10px;
padding-right: 10px;
white-space: pre;
@@ -48,7 +48,7 @@
font-family: $monospace-font;
display: block;
font-size: $code-font-size !important;
- min-height: 19px;
+ min-height: 1.5em;
white-space: nowrap;
i {
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index e51f230a680..1a38f3ccce4 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -16,10 +16,10 @@
margin-top: 5px;
}
- border-radius: 3px;
+ border-radius: $border-radius-default;
display: block;
float: left;
- margin-right: 10px;
+ margin-right: $gl-padding-8;
color: $white-light;
font-size: $gl-font-size;
line-height: $gl-line-height-24;
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 298610a0631..555a3fe0dc7 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -177,14 +177,6 @@ ul.content-list {
}
}
- .member-controls {
- float: none;
-
- @include media-breakpoint-up(sm) {
- float: right;
- }
- }
-
// When dragging a list item
&.ui-sortable-helper {
border-bottom: 0;
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index df40149f0a6..ad5096761cd 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -218,16 +218,22 @@
}
}
-@mixin build-trace-top-bar($height) {
+// Used in EE for Web Terminal
+@mixin build-trace-bar($height) {
height: $height;
min-height: $height;
background: $gray-light;
border: 1px solid $border-color;
color: $gl-text-color;
+ padding: $grid-size;
+}
+
+@mixin build-trace-top-bar($height) {
+ @include build-trace-bar($height);
+
position: -webkit-sticky;
position: sticky;
top: $header-height;
- padding: $grid-size;
.with-performance-bar & {
top: $header-height + $performance-bar-height;
@@ -390,3 +396,14 @@
width: $gl-font-size * $code-line-height * 0.9;
height: $gl-font-size * $code-line-height * 0.9;
}
+
+@mixin avatar-counter($border-radius: 1em) {
+ background-color: $gray-darkest;
+ color: $white-light;
+ border: 1px solid $gray-normal;
+ border-radius: $border-radius;
+ font-family: $regular-font;
+ font-size: 9px;
+ line-height: 16px;
+ text-align: center;
+}
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 31297b9d20c..ada8f2fe1a6 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -13,8 +13,8 @@
a,
button {
- padding: $gl-btn-padding;
- padding-bottom: 11px;
+ padding: $gl-padding-8;
+ padding-bottom: $gl-padding-8 + 1;
font-size: 14px;
line-height: 28px;
color: $gl-text-color-secondary;
@@ -58,8 +58,12 @@
}
.top-area {
- @include clearfix;
border-bottom: 1px solid $border-color;
+ display: flex;
+
+ @include media-breakpoint-down(md) {
+ flex-flow: column-reverse wrap;
+ }
.nav-text {
padding-top: 16px;
@@ -75,9 +79,8 @@
}
.nav-links {
- margin-bottom: 0;
border-bottom: 0;
- float: left;
+ flex: 1;
&.wide {
width: 100%;
@@ -98,16 +101,23 @@
&.mobile-separator {
border-bottom: 1px solid $border-color;
+ margin-bottom: $gl-padding-8;
}
}
}
.nav-controls {
display: inline-block;
- float: right;
text-align: right;
- padding: $gl-padding-8 0;
- margin-bottom: 0;
+
+ @include media-breakpoint-down(sm) {
+ margin-top: $gl-padding-8;
+ }
+
+ @include media-breakpoint-up(md) {
+ display: flex;
+ align-items: center;
+ }
> .btn,
> .btn-container,
@@ -115,8 +125,6 @@
> input,
> form {
margin-right: $gl-padding-top;
- display: inline-block;
- vertical-align: top;
&:last-child {
margin-right: 0;
@@ -143,7 +151,7 @@
@include media-breakpoint-up(lg) { width: 250px; }
}
- @include media-breakpoint-down(xs) {
+ @include media-breakpoint-down(sm) {
padding-bottom: 0;
width: 100%;
@@ -153,7 +161,7 @@
.dropdown-toggle,
.dropdown-menu-toggle,
.form-control {
- margin: 0 0 10px;
+ margin: 0 0 $gl-padding-8;
display: block;
width: 100%;
}
@@ -165,7 +173,7 @@
form {
display: block;
height: auto;
- margin-bottom: 14px;
+ margin-bottom: $gl-padding-8;
input {
width: 100%;
@@ -236,20 +244,11 @@
width: 100%;
}
- @include media-breakpoint-down(xs) {
- flex-flow: row wrap;
-
+ @include media-breakpoint-down(md) {
.nav-controls {
$controls-margin: $btn-margin-5 - 2px;
flex: 0 0 100%;
-
- &.controls-flex {
- display: flex;
- flex-flow: row wrap;
- align-items: center;
- justify-content: center;
- padding: 0 0 $gl-padding-top;
- }
+ margin-top: $gl-padding-8;
.controls-item,
.controls-item-full,
@@ -326,8 +325,8 @@
.fade-right,
.fade-left {
- top: 16px;
- bottom: auto;
+ bottom: $gl-padding;
+ top: auto;
}
&.is-smaller {
@@ -367,6 +366,7 @@
display: flex;
border-bottom: 1px solid $border-color;
overflow: hidden;
+ align-items: center;
.nav-links {
border-bottom: 0;
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index e8176e59c19..42a739e88f7 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -42,8 +42,8 @@
}
}
- .avatar {
- margin-right: 15px;
+ img.avatar {
+ margin-right: $gl-padding;
}
.controls {
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 7c152efd9c7..9e1431963d9 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -403,6 +403,7 @@ code {
.git-revision-dropdown .dropdown-content ul li a {
@extend .ref-name;
+ word-break: break-all;
}
/**
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 0a3bd9bf4d1..07fc655307e 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -336,6 +336,7 @@ $tooltip-font-size: 12px;
*/
$gl-padding-4: 4px;
$gl-padding-8: 8px;
+$gl-padding-12: 12px;
$gl-padding: 16px;
$gl-padding-24: 24px;
$gl-padding-32: 32px;
@@ -641,6 +642,7 @@ $input-lg-width: 320px;
*/
$document-index-color: #888;
$help-shortcut-header-color: #333;
+$accepting-mr-label-color: #69d100;
/*
* Issues
@@ -809,6 +811,11 @@ $modal-border-color: #e9ecef;
$priority-label-empty-state-width: 114px;
/*
+Popovers
+*/
+$popover-max-width: 384px;
+
+/*
Issues Analytics
*/
$issues-analytics-popover-boarder-color: rgba(0, 0, 0, 0.15);
diff --git a/app/assets/stylesheets/page_bundles/_ide_mixins.scss b/app/assets/stylesheets/page_bundles/_ide_mixins.scss
index 896a3466cb4..9465dd5bed6 100644
--- a/app/assets/stylesheets/page_bundles/_ide_mixins.scss
+++ b/app/assets/stylesheets/page_bundles/_ide_mixins.scss
@@ -2,17 +2,17 @@
display: flex;
flex-direction: column;
height: 100%;
- margin-top: -$grid-size;
- margin-bottom: -$grid-size;
- &.build-page .top-bar {
+ .top-bar {
+ @include build-trace-bar(35px);
+
top: 0;
- height: auto;
font-size: 12px;
border-top-right-radius: $border-radius-default;
- }
-
- .top-bar {
margin-left: -$gl-padding;
+
+ .controllers {
+ @include build-controllers(15px, center, false, 0, inline, 0);
+ }
}
}
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index f08fa80495d..cbcd8a474f1 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -396,10 +396,6 @@ $ide-commit-header-height: 48px;
font-size: inherit;
}
- > div + div {
- padding-left: $gl-padding;
- }
-
svg {
vertical-align: sub;
}
@@ -410,13 +406,14 @@ $ide-commit-header-height: 48px;
}
}
+.ide-status-list {
+ > div + div {
+ padding-left: $gl-padding;
+ }
+}
+
.ide-status-file {
text-align: right;
-
- .ide-status-branch + &,
- &:first-child {
- margin-left: auto;
- }
}
// Not great, but this is to deal with our current output
.multi-file-preview-holder {
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 09ff518bbdf..5e3652db48f 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -31,14 +31,15 @@
width: 320px;
.dropdown-content {
- max-height: 162px;
+ max-height: 140px;
}
}
.issue-board-dropdown-content {
- margin: 0 8px 10px;
- padding-bottom: 10px;
- border-bottom: 1px solid $dropdown-divider-bg;
+ margin: 0;
+ padding: $gl-padding-4 $gl-padding $gl-padding;
+ border-bottom: 0;
+ color: $gl-text-color-secondary;
}
.issue-boards-page {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 77a36e59b03..e12ea6fcb99 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -180,6 +180,14 @@
display: flex;
align-items: center;
}
+
+ .committer {
+ color: $gl-text-color-tertiary;
+
+ .commit-author-link {
+ color: $gl-text-color;
+ }
+ }
}
.commit-actions {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 5e5d298f8f2..4ebf1019456 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -676,7 +676,7 @@ table.code {
.diff-comments-more-count,
.diff-notes-collapse {
- @extend .avatar-counter;
+ @include avatar-counter(50%);
}
.diff-notes-collapse {
@@ -1008,6 +1008,10 @@ table.code {
display: block;
}
}
+
+ .note-edit-form {
+ margin-left: $note-icon-gutter-width;
+ }
}
.discussion-body .image .frame {
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 0a07747e0d4..656202f4e58 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -35,9 +35,6 @@
}
.group-nav-container .nav-controls {
- align-items: flex-start;
- padding: $gl-padding-top 0 0;
-
.group-filter-form {
flex: 1 1 auto;
margin-right: $gl-padding-8;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 4ba74d34664..dcbb23684d1 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -69,7 +69,11 @@
}
.emoji-block {
- padding: 10px 0;
+ padding: $gl-padding-4 0;
+
+ @include media-breakpoint-down(md) {
+ padding: $gl-padding-8 0;
+ }
}
}
@@ -132,6 +136,10 @@
z-index: 200;
overflow: hidden;
+ @include media-breakpoint-down(sm) {
+ z-index: 251;
+ }
+
a:not(.btn) {
color: inherit;
@@ -674,8 +682,7 @@
justify-content: center;
align-items: center;
margin-top: 0;
- padding-left: 9px;
- padding-right: 9px;
+ padding: 0 $gl-padding-8;
@include media-breakpoint-up(sm) {
display: inline-block;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index c7d2369a6b8..48289c8f381 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -258,8 +258,15 @@ ul.related-merge-requests > li {
}
}
-.discussion-reply-holder .note-edit-form {
- display: block;
+.discussion-reply-holder {
+ .avatar-note-form-holder .note-edit-form {
+ display: block;
+ margin-left: $note-icon-gutter-width;
+
+ @include media-breakpoint-down(xs) {
+ margin-left: 0;
+ }
+ }
}
.issue-sort-dropdown {
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index f8e273a2735..68af01f9ccc 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -20,17 +20,6 @@
}
}
- .list-item-name {
- @include media-breakpoint-up(sm) {
- float: left;
- width: 50%;
- }
-
- strong {
- font-weight: $gl-font-weight-bold;
- }
- }
-
.controls {
@include media-breakpoint-up(sm) {
display: flex;
@@ -43,10 +32,11 @@
.form-group {
margin-bottom: 0;
+ }
- @include media-breakpoint-down(sm) {
- display: block;
- margin-left: 5px;
+ .member-controls {
+ .fa {
+ line-height: inherit;
}
}
@@ -66,23 +56,12 @@
}
.member-form-control {
- @include media-breakpoint-down(sm) {
- width: $dropdown-member-form-control-width;
- margin-left: 0;
- padding-bottom: 5px;
- }
-
@include media-breakpoint-down(xs) {
margin-right: 0;
width: auto;
}
}
-.member-access-text {
- margin-left: auto;
- line-height: 43px;
-}
-
.member-search-form {
position: relative;
@@ -221,9 +200,6 @@
}
.content-list.members-list li {
- display: flex;
- justify-content: space-between;
-
.list-item-name {
float: none;
display: flex;
@@ -252,33 +228,24 @@
align-self: flex-start;
}
+ @include media-breakpoint-down(sm) {
+ .member-access-text {
+ margin: 0 0 $gl-padding-4 ($grid-size * 6);
+ }
+ }
+
@include media-breakpoint-down(xs) {
display: block;
- .controls > .btn {
- margin-left: 0;
- margin-right: 0;
+ .controls > .btn,
+ .controls .member-form-control {
+ margin: 0 0 $gl-padding-8;
display: block;
}
- .controls > .btn:last-child {
- margin-left: 5px;
- margin-right: 5px;
- width: auto;
- }
-
.form-control {
width: 100%;
}
-
- .member-access-text {
- line-height: 0;
- margin-left: 50px;
- }
-
- .member-controls {
- margin-top: 5px;
- }
}
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 77b40fe2d30..8cb3fab74e0 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -846,15 +846,40 @@
display: flex;
justify-content: space-between;
- @include media-breakpoint-down(sm) {
- flex-direction: column-reverse;
+ @include media-breakpoint-down(xs) {
+ .discussion-filter-container,
+ .line-resolve-all-container {
+ margin-bottom: $gl-padding-4;
+ }
}
.discussion-filter-container {
- margin-top: $gl-padding-8;
-
&:not(:only-child) {
- padding-right: $gl-padding-8;
+ margin: $gl-padding-4;
+ }
+ }
+
+ .merge-request-tabs {
+ height: $grid-size * 6;
+ }
+}
+
+// Wrap MR tabs/buttons so you don't have to scroll on desktop
+@include media-breakpoint-down(md) {
+ .merge-request-tabs-container,
+ .epic-tabs-container {
+ flex-direction: column-reverse;
+ padding-top: $gl-padding-8;
+ }
+}
+
+@include media-breakpoint-down(lg) {
+ .right-sidebar-expanded {
+ .merge-request-tabs-container,
+ .epic-tabs-container {
+ flex-direction: column-reverse;
+ align-items: flex-start;
+ padding-top: $gl-padding-8;
}
}
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 49608a3964f..00d84df1650 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -230,27 +230,6 @@ $status-box-line-height: 26px;
background-color: $white-light;
}
-.milestone-deprecation-message {
- .popover {
- padding: 0;
- }
-
- .popover-body,
- .popover-content {
- padding: 0;
- }
-}
-
-.milestone-popover-body {
- padding: $gl-padding-8;
- background-color: $gray-light;
-}
-
-.milestone-popover-footer {
- padding: $gl-padding-8 $gl-padding;
- border-top: 1px solid $white-dark;
-}
-
.milestone-popover-instructions-list {
padding-left: 2em;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 8c7b124dd33..c6bac33e888 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -59,6 +59,7 @@
border-radius: $border-radius-base;
transition: border-color ease-in-out 0.15s,
box-shadow ease-in-out 0.15s;
+ background-color: $white-light;
&.is-focused {
@extend .form-control:focus;
@@ -103,6 +104,11 @@
margin: auto;
align-items: center;
+ a {
+ color: $orange-600;
+ text-decoration: underline;
+ }
+
.icon {
margin-right: $issuable-warning-icon-margin;
vertical-align: text-bottom;
@@ -168,6 +174,16 @@
.discussion-form {
background-color: $white-light;
+
+ @include media-breakpoint-down(xs) {
+ .user-avatar-link {
+ display: none;
+ }
+
+ .note-edit-form {
+ margin-left: 0;
+ }
+ }
}
table {
@@ -234,13 +250,25 @@ table {
.diff-file,
.commit-diff {
.discussion-reply-holder {
- background-color: $white-light;
+ background-color: $gray-light;
border-radius: 0 0 3px 3px;
padding: $gl-padding;
+ border-top: 1px solid $gray-100;
+
+ + .new-note {
+ background-color: $gray-light;
+ border-top: 1px solid $gray-100;
+ }
&.is-replying {
padding-bottom: $gl-padding;
}
+
+ .user-avatar-link {
+ img {
+ margin-top: -3px;
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 32477c20db6..5cacd42bf0d 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -80,21 +80,17 @@ $note-form-margin-left: 72px;
}
}
- li.note {
- border-bottom: 1px solid $border-color;
- }
-
.replies-toggle {
background-color: $gray-light;
padding: $gl-padding-8 $gl-padding;
+ border-top: 1px solid $gray-100;
+ border-bottom: 1px solid $gray-100;
.collapse-replies-btn:hover {
color: $blue-600;
}
&.expanded {
- border-bottom: 1px solid $border-color;
-
span {
cursor: pointer;
}
@@ -211,8 +207,13 @@ $note-form-margin-left: 72px;
display: none;
}
+ .user-avatar-link img {
+ margin-top: $gl-padding-8;
+ }
+
.note-edit-form {
display: block;
+ margin-left: 0;
&.current-note-edit-form + .note-awards {
display: none;
@@ -264,8 +265,8 @@ $note-form-margin-left: 72px;
}
.system-note {
- padding: 6px 21px;
- margin: $gl-padding-24 0;
+ padding: $gl-padding-4 20px;
+ margin: $gl-padding 0;
background-color: transparent;
.note-header-info {
@@ -364,7 +365,7 @@ $note-form-margin-left: 72px;
height: $system-note-icon-size;
border: 1px solid $border-color;
border-radius: $system-note-icon-size;
- margin: -6px $gl-padding 0 0;
+ margin: -6px 20px 0 0;
svg {
width: $system-note-svg-size;
@@ -430,7 +431,7 @@ $note-form-margin-left: 72px;
.notes > .note-discussion li.note.system-note {
border-bottom: 0;
- padding: 0 $gl-padding;
+ padding: 0;
}
}
@@ -519,12 +520,30 @@ $note-form-margin-left: 72px;
}
}
-.commit-diff {
- .notes-content {
- background-color: $white-light;
+.code-commit .notes-content,
+.diff-viewer > .image ~ .note-container {
+ background-color: $white-light;
+
+ .avatar-note-form-holder {
+ .user-avatar-link img {
+ margin: 13px $gl-padding $gl-padding;
+ }
+
+ form,
+ ~ .discussion-form-container {
+ padding: $gl-padding;
+
+ @include media-breakpoint-up(sm) {
+ margin-left: $note-icon-gutter-width;
+ }
+ }
}
}
+.diff-viewer > .image ~ .note-container form.new-note {
+ margin-left: 0;
+}
+
.discussion-header,
.note-header-info {
a {
@@ -550,7 +569,7 @@ $note-form-margin-left: 72px;
}
.discussion-header {
- min-height: 72px;
+ min-height: 74px;
.note-header-info {
padding-bottom: 0;
@@ -563,8 +582,10 @@ $note-form-margin-left: 72px;
}
.unresolved {
- .note-header-info {
- margin-top: $gl-padding-8;
+ .discussion-header {
+ .note-header-info {
+ margin-top: $gl-padding-8;
+ }
}
}
@@ -762,15 +783,13 @@ $note-form-margin-left: 72px;
background-color: $white-light;
}
- a {
+ a:not(.learn-more) {
color: $blue-600;
}
}
.line-resolve-all-container {
- @include notes-media('min', map-get($grid-breakpoints, sm)) {
- margin-right: 0;
- }
+ margin: $gl-padding-4;
> div {
white-space: nowrap;
@@ -786,6 +805,8 @@ $note-form-margin-left: 72px;
}
.btn {
+ line-height: $gl-line-height;
+
svg {
fill: $gray-darkest;
}
@@ -811,10 +832,11 @@ $note-form-margin-left: 72px;
.line-resolve-all {
vertical-align: middle;
display: inline-block;
- padding: 6px 10px;
+ padding: $gl-padding-4 10px;
background-color: $gray-light;
border: 1px solid $border-color;
border-radius: $border-radius-default;
+ font-size: $gl-btn-small-font-size;
&.has-next-btn {
border-top-right-radius: 0;
@@ -830,6 +852,10 @@ $note-form-margin-left: 72px;
vertical-align: middle;
}
}
+
+ @include media-breakpoint-down(xs) {
+ flex: 1;
+ }
}
.line-resolve-btn {
diff --git a/app/assets/stylesheets/pages/prometheus.scss b/app/assets/stylesheets/pages/prometheus.scss
index c03554b287f..2d600e3aef6 100644
--- a/app/assets/stylesheets/pages/prometheus.scss
+++ b/app/assets/stylesheets/pages/prometheus.scss
@@ -136,7 +136,6 @@
> .popover-header,
> .popover-body {
padding: 8px;
- font-size: 12px;
white-space: nowrap;
position: relative;
}
diff --git a/app/controllers/acme_challenges_controller.rb b/app/controllers/acme_challenges_controller.rb
new file mode 100644
index 00000000000..67a39d8870b
--- /dev/null
+++ b/app/controllers/acme_challenges_controller.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AcmeChallengesController < ActionController::Base
+ def show
+ if acme_order
+ render plain: acme_order.challenge_file_content, content_type: 'text/plain'
+ else
+ head :not_found
+ end
+ end
+
+ private
+
+ def acme_order
+ @acme_order ||= PagesDomainAcmeOrder.find_by_domain_and_token(params[:domain], params[:token])
+ end
+end
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index d5bc723aa8c..57b976b9121 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -2,7 +2,9 @@
class Admin::ApplicationSettingsController < Admin::ApplicationController
include InternalRedirect
+
before_action :set_application_setting
+ before_action :whitelist_query_limiting, only: [:usage_data]
def show
end
@@ -102,6 +104,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
@application_setting = Gitlab::CurrentSettings.current_application_settings
end
+ def whitelist_query_limiting
+ Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/63107')
+ end
+
def application_setting_params
params[:application_setting] ||= {}
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index 73ebd4e0e42..80ee7c35906 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -12,9 +12,6 @@ class Clusters::ClustersController < Clusters::BaseController
before_action :authorize_update_cluster!, only: [:update]
before_action :authorize_admin_cluster!, only: [:destroy]
before_action :update_applications_status, only: [:cluster_status]
- before_action only: [:show] do
- push_frontend_feature_flag(:metrics_time_window)
- end
helper_method :token_in_session
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index a37ba682b91..28ead8d44da 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -11,7 +11,7 @@ class Import::FogbugzController < Import::BaseController
def callback
begin
- res = Gitlab::FogbugzImport::Client.new(import_params.symbolize_keys)
+ res = Gitlab::FogbugzImport::Client.new(import_params.to_h.symbolize_keys)
rescue
# If the URI is invalid various errors can occur
return redirect_to new_import_fogbugz_path, alert: _('Could not connect to FogBugz, check your URL')
@@ -26,7 +26,7 @@ class Import::FogbugzController < Import::BaseController
end
def create_user_map
- user_map = params[:users]
+ user_map = user_map_params.to_h[:users]
unless user_map.is_a?(Hash) && user_map.all? { |k, v| !v[:name].blank? }
flash.now[:alert] = _('All users must have a name.')
@@ -99,6 +99,10 @@ class Import::FogbugzController < Import::BaseController
params.permit(:uri, :email, :password)
end
+ def user_map_params
+ params.permit(users: %w(name email gitlab_user))
+ end
+
def verify_fogbugz_import_enabled
render_404 unless fogbugz_import_enabled?
end
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index 503eda250b4..f666a1150a6 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -28,9 +28,9 @@ class Profiles::EmailsController < Profiles::ApplicationController
def resend_confirmation_instructions
if Emails::ConfirmService.new(current_user, user: current_user).execute(@email)
- flash[:notice] = "Confirmation email sent to #{@email.email}"
+ flash[:notice] = _("Confirmation email sent to %{email}") % { email: @email.email }
else
- flash[:alert] = "There was a problem sending the confirmation email"
+ flash[:alert] = _("There was a problem sending the confirmation email")
end
redirect_to profile_emails_url
diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb
index cb02581da37..98cd66cf6f9 100644
--- a/app/controllers/projects/clusters_controller.rb
+++ b/app/controllers/projects/clusters_controller.rb
@@ -4,6 +4,10 @@ class Projects::ClustersController < Clusters::ClustersController
prepend_before_action :project
before_action :repository
+ before_action do
+ push_frontend_feature_flag(:prometheus_computed_alerts)
+ end
+
layout 'project'
private
diff --git a/app/controllers/projects/environments/prometheus_api_controller.rb b/app/controllers/projects/environments/prometheus_api_controller.rb
index f8ef23cd83e..9c6c6513a78 100644
--- a/app/controllers/projects/environments/prometheus_api_controller.rb
+++ b/app/controllers/projects/environments/prometheus_api_controller.rb
@@ -13,7 +13,7 @@ class Projects::Environments::PrometheusApiController < Projects::ApplicationCon
).execute
if result.nil?
- return render status: :accepted, json: {
+ return render status: :no_content, json: {
status: _('processing'),
message: _('Not ready yet. Try again later.')
}
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index c342e1c80b0..ae46a234aa6 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -11,10 +11,9 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :verify_api_request!, only: :terminal_websocket_authorize
before_action :expire_etag_cache, only: [:index]
before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do
- push_frontend_feature_flag(:metrics_time_window)
push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint)
push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards)
- push_frontend_feature_flag(:grafana_dashboard_link)
+ push_frontend_feature_flag(:prometheus_computed_alerts)
end
def index
@@ -165,7 +164,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
if Feature.enabled?(:environment_metrics_show_multiple_dashboards, project)
result = dashboard_finder.find(project, current_user, environment, params[:dashboard])
- result[:all_dashboards] = project.repository.metrics_dashboard_paths
+ result[:all_dashboards] = dashboard_finder.find_all_paths(project)
else
result = dashboard_finder.find(project, current_user, environment)
end
@@ -220,8 +219,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end
def metrics_params
- return unless Feature.enabled?(:metrics_time_window, project)
-
params.require([:start, :end])
end
diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb
index eb469d2d714..f2a6268b3e9 100644
--- a/app/controllers/projects/merge_requests/application_controller.rb
+++ b/app/controllers/projects/merge_requests/application_controller.rb
@@ -7,11 +7,15 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
private
- # rubocop: disable CodeReuse/ActiveRecord
def merge_request
- @issuable = @merge_request ||= @project.merge_requests.includes(author: :status).find_by!(iid: params[:id])
+ @issuable =
+ @merge_request ||=
+ merge_request_includes(@project.merge_requests).find_by_iid!(params[:id])
+ end
+
+ def merge_request_includes(association)
+ association.includes(:metrics, :assignees, author: :status) # rubocop:disable CodeReuse/ActiveRecord
end
- # rubocop: enable CodeReuse/ActiveRecord
def merge_request_params
params.require(:merge_request).permit(merge_request_params_attributes)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 135117926be..9e7e3ed5afb 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -33,7 +33,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def show
close_merge_request_if_no_source_project
- @merge_request.check_mergeability
+ mark_merge_request_mergeable
respond_to do |format|
format.html do
@@ -251,6 +251,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@merge_request.has_no_commits? && !@merge_request.target_branch_exists?
end
+ def mark_merge_request_mergeable
+ @merge_request.check_if_can_be_merged
+ end
+
def merge!
# Disable the CI check if auto_merge_strategy is specified since we have
# to wait until CI completes to know
@@ -269,9 +273,15 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@merge_request.update(merge_error: nil, squash: merge_params.fetch(:squash, false))
if auto_merge_requested?
- AutoMergeService.new(project, current_user, merge_params)
- .execute(merge_request,
- params[:auto_merge_strategy] || AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
+ if merge_request.auto_merge_enabled?
+ # TODO: We should have a dedicated endpoint for updating merge params.
+ # See https://gitlab.com/gitlab-org/gitlab-ce/issues/63130.
+ AutoMergeService.new(project, current_user, merge_params).update(merge_request)
+ else
+ AutoMergeService.new(project, current_user, merge_params)
+ .execute(merge_request,
+ params[:auto_merge_strategy] || AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
+ end
else
@merge_request.merge_async(current_user.id, merge_params)
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index c4dff95a4b9..1b8d479209b 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -50,7 +50,8 @@ module Projects
:runners_token, :builds_enabled, :build_allow_git_fetch,
:build_timeout_human_readable, :build_coverage_regex, :public_builds,
:auto_cancel_pending_pipelines, :ci_config_path,
- auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy]
+ auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy],
+ ci_cd_settings_attributes: [:default_git_depth]
)
end
diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
index b5c77e5bbf4..5cfb0ac307d 100644
--- a/app/controllers/projects/settings/operations_controller.rb
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -5,10 +5,6 @@ module Projects
class OperationsController < Projects::ApplicationController
before_action :authorize_update_environment!
- before_action do
- push_frontend_feature_flag(:grafana_dashboard_link)
- end
-
helper_method :error_tracking_setting
def show
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index cb25548c83f..a80ab3bcd28 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -25,7 +25,6 @@ class SearchController < ApplicationController
@show_snippets = search_service.show_snippets?
@search_results = search_service.search_results
@search_objects = search_service.search_objects
- @display_options = search_service.display_options
render_commits if @scope == 'commits'
eager_load_user_status if @scope == 'users'
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 50e9418677c..3592505a977 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -43,7 +43,7 @@ class IssuableFinder
FILTER_NONE = 'none'.freeze
FILTER_ANY = 'any'.freeze
- # This is accepted as a deprecated filter and is also used in unassigning users
+ # This is used in unassigning users
NONE = '0'.freeze
attr_accessor :current_user, :params
@@ -248,8 +248,7 @@ class IssuableFinder
def filter_by_no_label?
downcased = label_names.map(&:downcase)
- # Label::NONE is deprecated and should be removed in 12.0
- downcased.include?(FILTER_NONE) || downcased.include?(Label::NONE)
+ downcased.include?(FILTER_NONE)
end
def filter_by_any_label?
@@ -449,8 +448,7 @@ class IssuableFinder
# rubocop: enable CodeReuse/ActiveRecord
def filter_by_no_assignee?
- # Assignee_id takes precedence over assignee_username
- [NONE, FILTER_NONE].include?(params[:assignee_id].to_s.downcase) || params[:assignee_username].to_s == NONE
+ params[:assignee_id].to_s.downcase == FILTER_NONE
end
def filter_by_any_assignee?
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index a374851e835..dd0d9105df6 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -29,15 +29,18 @@ module Types
# proc because we set complexity depending on arguments and number of
# items which can be loaded.
proc do |ctx, args, child_complexity|
- page_size = @max_page_size || ctx.schema.default_max_page_size
- limit_value = [args[:first], args[:last], page_size].compact.min
-
# Resolvers may add extra complexity depending on used arguments
complexity = child_complexity + self.resolver&.try(:resolver_complexity, args, child_complexity: child_complexity).to_i
- # Resolvers may add extra complexity depending on number of items being loaded.
- multiplier = self.resolver&.try(:complexity_multiplier, args).to_f
- complexity += complexity * limit_value * multiplier
+ field_defn = to_graphql
+
+ if field_defn.connection?
+ # Resolvers may add extra complexity depending on number of items being loaded.
+ page_size = field_defn.connection_max_page_size || ctx.schema.default_max_page_size
+ limit_value = [args[:first], args[:last], page_size].compact.min
+ multiplier = self.resolver&.try(:complexity_multiplier, args).to_f
+ complexity += complexity * limit_value * multiplier
+ end
complexity.to_i
end
diff --git a/app/graphql/types/tree/blob_type.rb b/app/graphql/types/tree/blob_type.rb
index f2b7d5df2b2..760781f3612 100644
--- a/app/graphql/types/tree/blob_type.rb
+++ b/app/graphql/types/tree/blob_type.rb
@@ -9,6 +9,9 @@ module Types
graphql_name 'Blob'
field :web_url, GraphQL::STRING_TYPE, null: true
+ field :lfs_oid, GraphQL::STRING_TYPE, null: true, resolve: -> (blob, args, ctx) do
+ Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(blob.repository, blob.id).find
+ end
end
end
end
diff --git a/app/helpers/ci_variables_helper.rb b/app/helpers/ci_variables_helper.rb
index e313015c937..fc51f00d052 100644
--- a/app/helpers/ci_variables_helper.rb
+++ b/app/helpers/ci_variables_helper.rb
@@ -27,4 +27,8 @@ module CiVariablesHelper
%w(File file)
]
end
+
+ def ci_variable_maskable_regex
+ Maskable::REGEX.inspect.sub('\\A', '^').sub('\\z', '$').sub(/^\//, '').sub(/\/[a-z]*$/, '').gsub('\/', '/')
+ end
end
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index 2beb081ab77..36122d3a22a 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -57,12 +57,6 @@ module EmailsHelper
pluralize(valid_length, unit)
end
- def reset_token_expire_message
- link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email))
- "This link is valid for #{password_reset_token_valid_time}. " \
- "After it expires, you can #{link_tag}."
- end
-
def header_logo
if current_appearance&.header_logo?
image_tag(
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index 8002eb08ada..855b243cc8a 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -26,6 +26,7 @@ module EnvironmentsHelper
"empty-no-data-svg-path" => image_path('illustrations/monitoring/no_data.svg'),
"empty-unable-to-connect-svg-path" => image_path('illustrations/monitoring/unable_to_connect.svg'),
"metrics-endpoint" => additional_metrics_project_environment_path(project, environment, format: :json),
+ "dashboard-endpoint" => metrics_dashboard_project_environment_path(project, environment, format: :json),
"deployment-endpoint" => project_environment_deployments_path(project, environment, format: :json),
"environments-endpoint": project_environments_path(project, format: :json),
"project-path" => project_path(project),
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index 18fe2a9624f..0fd8dca70b4 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -17,13 +17,11 @@ class BroadcastMessage < ApplicationRecord
default_value_for :font, '#FFFFFF'
CACHE_KEY = 'broadcast_message_current_json'.freeze
- LEGACY_CACHE_KEY = 'broadcast_message_current'.freeze
after_commit :flush_redis_cache
def self.current
messages = cache.fetch(CACHE_KEY, as: BroadcastMessage, expires_in: cache_expires_in) do
- remove_legacy_cache_key
current_and_future_messages
end
@@ -50,14 +48,6 @@ class BroadcastMessage < ApplicationRecord
nil
end
- # This can be removed in GitLab 12.0+
- # The old cache key had an indefinite lifetime, and in an HA
- # environment a one-shot migration would not work because the cache
- # would be repopulated by a node that has not been upgraded.
- def self.remove_legacy_cache_key
- cache.expire(LEGACY_CACHE_KEY)
- end
-
def active?
started? && !ended?
end
@@ -84,6 +74,5 @@ class BroadcastMessage < ApplicationRecord
def flush_redis_cache
self.class.cache.expire(CACHE_KEY)
- self.class.remove_legacy_cache_key
end
end
diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb
index c40ad39be61..6a4241c94bc 100644
--- a/app/models/ci/pipeline_schedule.rb
+++ b/app/models/ci/pipeline_schedule.rb
@@ -73,7 +73,8 @@ module Ci
private
def ideal_next_run_at
- Gitlab::Ci::CronParser.new(cron, cron_timezone).next_time_from(Time.now)
+ Gitlab::Ci::CronParser.new(cron, cron_timezone)
+ .next_time_from(Time.zone.now)
end
end
end
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index e1d6b2a802b..ccc877fb924 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -8,7 +8,6 @@ module Clusters
include ReactiveCaching
self.table_name = 'clusters'
- self.reactive_cache_key = -> (cluster) { [cluster.class.model_name.singular, cluster.id] }
PROJECT_ONLY_APPLICATIONS = {
Applications::Jupyter.application_name => Applications::Jupyter,
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index 9b951578aee..8e06156c73d 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -11,7 +11,6 @@ module Clusters
RESERVED_NAMESPACES = %w(gitlab-managed-apps).freeze
self.table_name = 'cluster_platforms_kubernetes'
- self.reactive_cache_key = ->(kubernetes) { [kubernetes.class.model_name.singular, kubernetes.id] }
belongs_to :cluster, inverse_of: :platform_kubernetes, class_name: 'Clusters::Cluster'
diff --git a/app/models/concerns/maskable.rb b/app/models/concerns/maskable.rb
index 2943872ffab..e0f2c41b836 100644
--- a/app/models/concerns/maskable.rb
+++ b/app/models/concerns/maskable.rb
@@ -7,9 +7,9 @@ module Maskable
# * No escape characters
# * No variables
# * No spaces
- # * Minimal length of 8 characters
+ # * Minimal length of 8 characters from the Base64 alphabets (RFC4648)
# * Absolutely no fun is allowed
- REGEX = /\A\w{8,}\z/.freeze
+ REGEX = /\A[a-zA-Z0-9_+=\/-]{8,}\z/.freeze
included do
validates :masked, inclusion: { in: [true, false] }
diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb
index 258c819f243..c2542dbe743 100644
--- a/app/models/concerns/prometheus_adapter.rb
+++ b/app/models/concerns/prometheus_adapter.rb
@@ -6,7 +6,6 @@ module PrometheusAdapter
included do
include ReactiveCaching
- self.reactive_cache_key = ->(adapter) { [adapter.class.model_name.singular, adapter.id] }
self.reactive_cache_lease_timeout = 30.seconds
self.reactive_cache_refresh_interval = 30.seconds
self.reactive_cache_lifetime = 1.minute
diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb
index 1e09cd89550..6c3962b4c4f 100644
--- a/app/models/concerns/reactive_caching.rb
+++ b/app/models/concerns/reactive_caching.rb
@@ -10,8 +10,6 @@
# class Foo < ApplicationRecord
# include ReactiveCaching
#
-# self.reactive_cache_key = ->(thing) { ["foo", thing.id] }
-#
# after_save :clear_reactive_cache!
#
# def calculate_reactive_cache
@@ -89,6 +87,8 @@ module ReactiveCaching
class_attribute :reactive_cache_worker_finder
# defaults
+ self.reactive_cache_key = -> (record) { [model_name.singular, record.id] }
+
self.reactive_cache_lease_timeout = 2.minutes
self.reactive_cache_refresh_interval = 1.minute
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index 2f0e078c807..b42adad94ba 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -75,4 +75,11 @@ module Taskable
def task_status_short
task_status(short: true)
end
+
+ def task_completion_status
+ @task_completion_status ||= {
+ count: tasks.summary.item_count,
+ completed_count: tasks.summary.complete_count
+ }
+ end
end
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index 8221b7de2b6..f75c32633b1 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -90,7 +90,7 @@ class DiffNote < Note
end
def banzai_render_context(field)
- super.merge(project: project, suggestions_filter_enabled: supports_suggestion?)
+ super.merge(suggestions_filter_enabled: true)
end
private
diff --git a/app/models/label.rb b/app/models/label.rb
index e9085e8bd25..b83e0862bab 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -13,7 +13,6 @@ class Label < ApplicationRecord
cache_markdown_field :description, pipeline: :single_line
DEFAULT_COLOR = '#428BCA'
- NONE = 'no label'
default_value_for :color, DEFAULT_COLOR
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 59416fb4b51..f07636e8f77 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -668,7 +668,7 @@ class MergeRequest < ApplicationRecord
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37435
Gitlab::GitalyClient.allow_n_plus_1_calls do
- merge_request_diffs.create
+ merge_request_diffs.create!
reload_merge_request_diff
end
end
@@ -725,16 +725,19 @@ class MergeRequest < ApplicationRecord
MergeRequests::ReloadDiffsService.new(self, current_user).execute
end
-
- def check_mergeability
- MergeRequests::MergeabilityCheckService.new(self).execute
- end
# rubocop: enable CodeReuse/ServiceClass
- # Returns boolean indicating the merge_status should be rechecked in order to
- # switch to either can_be_merged or cannot_be_merged.
- def recheck_merge_status?
- self.class.state_machines[:merge_status].check_state?(merge_status)
+ def check_if_can_be_merged
+ return unless self.class.state_machines[:merge_status].check_state?(merge_status) && Gitlab::Database.read_write?
+
+ can_be_merged =
+ !broken? && project.repository.can_be_merged?(diff_head_sha, target_branch)
+
+ if can_be_merged
+ mark_as_mergeable
+ else
+ mark_as_unmergeable
+ end
end
def merge_event
@@ -760,7 +763,7 @@ class MergeRequest < ApplicationRecord
def mergeable?(skip_ci_check: false)
return false unless mergeable_state?(skip_ci_check: skip_ci_check)
- check_mergeability
+ check_if_can_be_merged
can_be_merged? && !should_be_rebased?
end
@@ -775,6 +778,15 @@ class MergeRequest < ApplicationRecord
true
end
+ def mergeable_to_ref?
+ return false unless mergeable_state?(skip_ci_check: true, skip_discussions_check: true)
+
+ # Given the `merge_ref_path` will have the same
+ # state the `target_branch` would have. Ideally
+ # we need to check if it can be merged to it.
+ project.repository.can_be_merged?(diff_head_sha, target_branch)
+ end
+
def ff_merge_possible?
project.repository.ancestor?(target_branch_sha, diff_head_sha)
end
@@ -984,21 +996,6 @@ class MergeRequest < ApplicationRecord
end
end
- def reset_auto_merge
- return unless auto_merge_enabled?
-
- self.auto_merge_enabled = false
- self.merge_user = nil
- if merge_params
- merge_params.delete('should_remove_source_branch')
- merge_params.delete('commit_message')
- merge_params.delete('squash_commit_message')
- merge_params.delete('auto_merge_strategy')
- end
-
- self.save
- end
-
# Return array of possible target branches
# depends on target project of MR
def target_branches
@@ -1102,12 +1099,6 @@ class MergeRequest < ApplicationRecord
target_project.repository.fetch_source_branch!(source_project.repository, source_branch, ref_path)
end
- # Returns the current merge-ref HEAD commit.
- #
- def merge_ref_head
- project.repository.commit(merge_ref_path)
- end
-
def ref_path
"refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/head"
end
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index 9b2bbb7eba5..a7f73c0f29c 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -101,6 +101,7 @@ class NotificationRecipient
end
def excluded_watcher_action?
+ return false unless @type == :watch
return false unless @custom_action
NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(@custom_action)
@@ -140,7 +141,7 @@ class NotificationRecipient
return project_setting unless project_setting.nil? || project_setting.global?
- group_setting = closest_non_global_group_notification_settting
+ group_setting = closest_non_global_group_notification_setting
return group_setting unless group_setting.nil?
@@ -148,7 +149,7 @@ class NotificationRecipient
end
# Returns the notification_setting of the lowest group in hierarchy with non global level
- def closest_non_global_group_notification_settting
+ def closest_non_global_group_notification_setting
return unless @group
@group
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index 407d85b1520..524df30289e 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -5,6 +5,7 @@ class PagesDomain < ApplicationRecord
VERIFICATION_THRESHOLD = 3.days.freeze
belongs_to :project
+ has_many :acme_orders, class_name: "PagesDomainAcmeOrder"
validates :domain, hostname: { allow_numeric_hostname: true }
validates :domain, uniqueness: { case_sensitive: false }
@@ -134,6 +135,14 @@ class PagesDomain < ApplicationRecord
"#{VERIFICATION_KEY}=#{verification_code}"
end
+ def certificate=(certificate)
+ super(certificate)
+
+ # set nil, if certificate is nil
+ self.certificate_valid_not_before = x509&.not_before
+ self.certificate_valid_not_after = x509&.not_after
+ end
+
private
def set_verification_code
@@ -186,7 +195,7 @@ class PagesDomain < ApplicationRecord
end
def x509
- return unless certificate
+ return unless certificate.present?
@x509 ||= OpenSSL::X509::Certificate.new(certificate)
rescue OpenSSL::X509::CertificateError
diff --git a/app/models/pages_domain_acme_order.rb b/app/models/pages_domain_acme_order.rb
new file mode 100644
index 00000000000..63d7fbc8206
--- /dev/null
+++ b/app/models/pages_domain_acme_order.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class PagesDomainAcmeOrder < ApplicationRecord
+ belongs_to :pages_domain
+
+ scope :expired, -> { where("expires_at < ?", Time.now) }
+
+ validates :pages_domain, presence: true
+ validates :expires_at, presence: true
+ validates :url, presence: true
+ validates :challenge_token, presence: true
+ validates :challenge_file_content, presence: true
+ validates :private_key, presence: true
+
+ attr_encrypted :private_key,
+ mode: :per_attribute_iv,
+ key: Settings.attr_encrypted_db_key_base_truncated,
+ algorithm: 'aes-256-gcm',
+ encode: true
+
+ def self.find_by_domain_and_token(domain_name, challenge_token)
+ joins(:pages_domain).find_by(pages_domains: { domain: domain_name }, challenge_token: challenge_token)
+ end
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 78d54571d94..9d17d68eee2 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -292,6 +292,7 @@ class Project < ApplicationRecord
accepts_nested_attributes_for :project_feature, update_only: true
accepts_nested_attributes_for :import_data
accepts_nested_attributes_for :auto_devops, update_only: true
+ accepts_nested_attributes_for :ci_cd_settings, update_only: true
accepts_nested_attributes_for :remote_mirrors,
allow_destroy: true,
@@ -310,6 +311,7 @@ class Project < ApplicationRecord
delegate :root_ancestor, to: :namespace, allow_nil: true
delegate :last_pipeline, to: :commit, allow_nil: true
delegate :external_dashboard_url, to: :metrics_setting, allow_nil: true, prefix: true
+ delegate :default_git_depth, :default_git_depth=, to: :ci_cd_settings, prefix: :ci
# Validations
validates :creator, presence: true, on: :create
diff --git a/app/models/project_ci_cd_setting.rb b/app/models/project_ci_cd_setting.rb
index 1414164b703..821e022f51b 100644
--- a/app/models/project_ci_cd_setting.rb
+++ b/app/models/project_ci_cd_setting.rb
@@ -6,6 +6,18 @@ class ProjectCiCdSetting < ApplicationRecord
# The version of the schema that first introduced this model/table.
MINIMUM_SCHEMA_VERSION = 20180403035759
+ DEFAULT_GIT_DEPTH = 50
+
+ before_create :set_default_git_depth
+
+ validates :default_git_depth,
+ numericality: {
+ only_integer: true,
+ greater_than_or_equal_to: 0,
+ less_than_or_equal_to: 1000
+ },
+ allow_nil: true
+
def self.available?
@available ||=
ActiveRecord::Migrator.current_version >= MINIMUM_SCHEMA_VERSION
@@ -15,4 +27,12 @@ class ProjectCiCdSetting < ApplicationRecord
@available = nil
super
end
+
+ private
+
+ def set_default_git_depth
+ return unless Feature.enabled?(:ci_set_project_default_git_depth, default_enabled: true)
+
+ self.default_git_depth ||= DEFAULT_GIT_DEPTH
+ end
end
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index 6bcb051bff6..0542581c6e0 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -72,8 +72,6 @@ class ProjectFeature < ApplicationRecord
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
- scope :for_project_id, -> (project) { where(project: project) }
-
def feature_available?(feature, user)
# This feature might not be behind a feature flag at all, so default to true
return false unless ::Feature.enabled?(feature, user, default_enabled: true)
diff --git a/app/models/project_services/youtrack_service.rb b/app/models/project_services/youtrack_service.rb
index 957be685aea..175c2ebf197 100644
--- a/app/models/project_services/youtrack_service.rb
+++ b/app/models/project_services/youtrack_service.rb
@@ -5,12 +5,12 @@ class YoutrackService < IssueTrackerService
prop_accessor :description, :project_url, :issues_url
- # {PROJECT-KEY}-{NUMBER} Examples: YT-1, PRJ-1
+ # {PROJECT-KEY}-{NUMBER} Examples: YT-1, PRJ-1, gl-030
def self.reference_pattern(only_long: false)
if only_long
- /(?<issue>\b[A-Z][A-Za-z0-9_]*-\d+)/
+ /(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+)/
else
- /(?<issue>\b[A-Z][A-Za-z0-9_]*-\d+)|(#{Issue.reference_prefix}(?<issue>\d+))/
+ /(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+)|(#{Issue.reference_prefix}(?<issue>\d+))/
end
end
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 11e3737298c..8a179b4d56d 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -54,7 +54,7 @@ class ProjectStatistics < ApplicationRecord
end
def update_storage_size
- self.storage_size = repository_size + wiki_size + lfs_objects_size + build_artifacts_size + packages_size
+ self.storage_size = repository_size + wiki_size.to_i + lfs_objects_size + build_artifacts_size + packages_size
end
# Since this incremental update method does not call update_storage_size above,
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 3218c04b219..728a3040227 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -164,6 +164,7 @@ class ProjectPolicy < BasePolicy
enable :set_issue_iid
enable :set_issue_created_at
+ enable :set_issue_updated_at
enable :set_note_created_at
end
diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb
index c5675ef3ea3..91c9abe750b 100644
--- a/app/presenters/blob_presenter.rb
+++ b/app/presenters/blob_presenter.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class BlobPresenter < Gitlab::View::Presenter::Simple
+class BlobPresenter < Gitlab::View::Presenter::Delegated
presents :blob
def highlight(plain: nil)
diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb
index 6d46e0bf18a..b928988ed8c 100644
--- a/app/presenters/ci/build_runner_presenter.rb
+++ b/app/presenters/ci/build_runner_presenter.rb
@@ -25,14 +25,16 @@ module Ci
end
def git_depth
- strong_memoize(:git_depth) do
- git_depth = variables&.find { |variable| variable[:key] == 'GIT_DEPTH' }&.dig(:value)
- git_depth.to_i
- end
+ if git_depth_variable
+ git_depth_variable[:value]
+ elsif Feature.enabled?(:ci_project_git_depth, default_enabled: true)
+ project.ci_default_git_depth
+ end.to_i
end
def refspecs
specs = []
+ specs << refspec_for_merge_request_ref if merge_request_ref?
if git_depth > 0
specs << refspec_for_branch(ref) if branch? || legacy_detached_merge_request_pipeline?
@@ -42,8 +44,6 @@ module Ci
specs << refspec_for_tag
end
- specs << refspec_for_merge_request_ref if merge_request_ref?
-
specs
end
@@ -89,5 +89,11 @@ module Ci
def refspec_for_merge_request_ref
"+#{ref}:#{ref}"
end
+
+ def git_depth_variable
+ strong_memoize(:git_depth_variable) do
+ variables&.find { |variable| variable[:key] == 'GIT_DEPTH' }
+ end
+ end
end
end
diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb
index 914ad628a99..36e601f45c5 100644
--- a/app/serializers/issue_entity.rb
+++ b/app/serializers/issue_entity.rb
@@ -44,4 +44,12 @@ class IssueEntity < IssuableEntity
expose :preview_note_path do |issue|
preview_markdown_path(issue.project, target_type: 'Issue', target_id: issue.iid)
end
+
+ expose :confidential_issues_docs_path, if: -> (issue) { issue.confidential? } do |issue|
+ help_page_path('user/project/issues/confidential_issues.md')
+ end
+
+ expose :locked_discussion_docs_path, if: -> (issue) { issue.discussion_locked? } do |issue|
+ help_page_path('user/discussions/index.md', anchor: 'lock-discussions')
+ end
end
diff --git a/app/services/auto_merge/base_service.rb b/app/services/auto_merge/base_service.rb
new file mode 100644
index 00000000000..d726085b89a
--- /dev/null
+++ b/app/services/auto_merge/base_service.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module AutoMerge
+ class BaseService < ::BaseService
+ include Gitlab::Utils::StrongMemoize
+
+ def execute(merge_request)
+ merge_request.merge_params.merge!(params)
+ merge_request.auto_merge_enabled = true
+ merge_request.merge_user = current_user
+ merge_request.auto_merge_strategy = strategy
+
+ return :failed unless merge_request.save
+
+ yield if block_given?
+
+ # Notify the event that auto merge is enabled or merge param is updated
+ AutoMergeProcessWorker.perform_async(merge_request.id)
+
+ strategy.to_sym
+ end
+
+ def update(merge_request)
+ merge_request.merge_params.merge!(params)
+
+ return :failed unless merge_request.save
+
+ strategy.to_sym
+ end
+
+ def cancel(merge_request)
+ if cancel_auto_merge(merge_request)
+ yield if block_given?
+
+ success
+ else
+ error("Can't cancel the automatic merge", 406)
+ end
+ end
+
+ private
+
+ def strategy
+ strong_memoize(:strategy) do
+ self.class.name.demodulize.remove('Service').underscore
+ end
+ end
+
+ def cancel_auto_merge(merge_request)
+ merge_request.auto_merge_enabled = false
+ merge_request.merge_user = nil
+
+ merge_request.merge_params&.except!(
+ 'should_remove_source_branch',
+ 'commit_message',
+ 'squash_commit_message',
+ 'auto_merge_strategy'
+ )
+
+ merge_request.save
+ end
+ end
+end
diff --git a/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb b/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb
index d0586468859..c41073a73e9 100644
--- a/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb
+++ b/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb
@@ -1,32 +1,12 @@
# frozen_string_literal: true
module AutoMerge
- class MergeWhenPipelineSucceedsService < BaseService
+ class MergeWhenPipelineSucceedsService < AutoMerge::BaseService
def execute(merge_request)
- return :failed unless merge_request.actual_head_pipeline
-
- if merge_request.actual_head_pipeline.active?
- merge_request.merge_params.merge!(params)
-
- unless merge_request.auto_merge_enabled?
- merge_request.auto_merge_enabled = true
- merge_request.merge_user = @current_user
- merge_request.auto_merge_strategy = AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS
-
- SystemNoteService.merge_when_pipeline_succeeds(merge_request, @project, @current_user, merge_request.diff_head_commit)
+ super do
+ if merge_request.saved_change_to_auto_merge_enabled?
+ SystemNoteService.merge_when_pipeline_succeeds(merge_request, project, current_user, merge_request.diff_head_commit)
end
-
- return :failed unless merge_request.save
-
- :merge_when_pipeline_succeeds
- elsif merge_request.actual_head_pipeline.success?
- # This can be triggered when a user clicks the auto merge button while
- # the tests finish at about the same time
- merge_request.merge_async(current_user.id, merge_params)
-
- :success
- else
- :failed
end
end
@@ -38,12 +18,8 @@ module AutoMerge
end
def cancel(merge_request)
- if merge_request.reset_auto_merge
+ super do
SystemNoteService.cancel_merge_when_pipeline_succeeds(merge_request, @project, @current_user)
-
- success
- else
- error("Can't cancel the automatic merge", 406)
end
end
diff --git a/app/services/auto_merge_service.rb b/app/services/auto_merge_service.rb
index a3a780ff388..926d2f5fc66 100644
--- a/app/services/auto_merge_service.rb
+++ b/app/services/auto_merge_service.rb
@@ -24,6 +24,12 @@ class AutoMergeService < BaseService
service.execute(merge_request)
end
+ def update(merge_request)
+ return :failed unless merge_request.auto_merge_enabled?
+
+ get_service_instance(merge_request.auto_merge_strategy).update(merge_request)
+ end
+
def process(merge_request)
return unless merge_request.auto_merge_enabled?
diff --git a/app/services/ci/pipeline_schedule_service.rb b/app/services/ci/pipeline_schedule_service.rb
index 387d0351490..5b5e9a26520 100644
--- a/app/services/ci/pipeline_schedule_service.rb
+++ b/app/services/ci/pipeline_schedule_service.rb
@@ -7,7 +7,7 @@ module Ci
# Otherwise, multiple pipelines could be created in a short interval.
schedule.schedule_next_run!
- RunPipelineScheduleWorker.perform_async(schedule.id, schedule.owner.id)
+ RunPipelineScheduleWorker.perform_async(schedule.id, schedule.owner&.id)
end
end
end
diff --git a/app/services/git/branch_hooks_service.rb b/app/services/git/branch_hooks_service.rb
index d21a6bb1b9a..4aee48f22e7 100644
--- a/app/services/git/branch_hooks_service.rb
+++ b/app/services/git/branch_hooks_service.rb
@@ -20,8 +20,7 @@ module Git
strong_memoize(:commits) do
if creating_default_branch?
# The most recent PROCESS_COMMIT_LIMIT commits in the default branch
- offset = [count_commits_in_branch - PROCESS_COMMIT_LIMIT, 0].max
- project.repository.commits(params[:newrev], offset: offset, limit: PROCESS_COMMIT_LIMIT)
+ project.repository.commits(params[:newrev], limit: PROCESS_COMMIT_LIMIT)
elsif creating_branch?
# Use the pushed commits that aren't reachable by the default branch
# as a heuristic. This may include more commits than are actually
@@ -84,9 +83,6 @@ module Git
# Schedules processing of commit messages
def enqueue_process_commit_messages
- # don't process commits for the initial push to the default branch
- return if creating_default_branch?
-
limited_commits.each do |commit|
next unless commit.matches_cross_reference_regex?
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 2cfed62ce49..c34fbeb2adb 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -68,6 +68,10 @@ module MergeRequests
!merge_request.for_fork?
end
+ def cancel_auto_merge(merge_request)
+ AutoMergeService.new(project, current_user).cancel(merge_request)
+ end
+
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
# rubocop: disable CodeReuse/ActiveRecord
def merge_requests_for(source_branch, mr_states: [:opened])
diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb
index b0f6166ea1c..b81a4dd81d2 100644
--- a/app/services/merge_requests/close_service.rb
+++ b/app/services/merge_requests/close_service.rb
@@ -34,9 +34,5 @@ module MergeRequests
merge_request_metrics_service(merge_request).close(close_event)
end
end
-
- def cancel_auto_merge(merge_request)
- AutoMergeService.new(project, current_user).cancel(merge_request)
- end
end
end
diff --git a/app/services/merge_requests/merge_to_ref_service.rb b/app/services/merge_requests/merge_to_ref_service.rb
index 8670b9ccf3d..87147d90c32 100644
--- a/app/services/merge_requests/merge_to_ref_service.rb
+++ b/app/services/merge_requests/merge_to_ref_service.rb
@@ -20,14 +20,20 @@ module MergeRequests
raise_error('Conflicts detected during merge') unless commit_id
- success(commit_id: commit_id)
- rescue MergeError, ArgumentError => error
+ commit = project.commit(commit_id)
+ target_id, source_id = commit.parent_ids
+
+ success(commit_id: commit.id,
+ target_id: target_id,
+ source_id: source_id)
+ rescue MergeError => error
error(error.message)
end
private
def validate!
+ authorization_check!
error_check!
end
@@ -37,13 +43,21 @@ module MergeRequests
error =
if !hooks_validation_pass?(merge_request)
hooks_validation_error(merge_request)
- elsif source.blank?
+ elsif !@merge_request.mergeable_to_ref?
+ "Merge request is not mergeable to #{target_ref}"
+ elsif !source
'No source for merge'
end
raise_error(error) if error
end
+ def authorization_check!
+ unless Ability.allowed?(current_user, :admin_merge_request, project)
+ raise_error("You are not allowed to merge to this ref")
+ end
+ end
+
def target_ref
merge_request.merge_ref_path
end
diff --git a/app/services/merge_requests/mergeability_check_service.rb b/app/services/merge_requests/mergeability_check_service.rb
deleted file mode 100644
index ef833774e65..00000000000
--- a/app/services/merge_requests/mergeability_check_service.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-# frozen_string_literal: true
-
-module MergeRequests
- class MergeabilityCheckService < ::BaseService
- include Gitlab::Utils::StrongMemoize
-
- delegate :project, to: :@merge_request
- delegate :repository, to: :project
-
- def initialize(merge_request)
- @merge_request = merge_request
- end
-
- # Updates the MR merge_status. Whenever it switches to a can_be_merged state,
- # the merge-ref is refreshed.
- #
- # Returns a ServiceResponse indicating merge_status is/became can_be_merged
- # and the merge-ref is synced. Success in case of being/becoming mergeable,
- # error otherwise.
- def execute
- return ServiceResponse.error(message: 'Invalid argument') unless merge_request
- return ServiceResponse.error(message: 'Unsupported operation') if Gitlab::Database.read_only?
-
- update_merge_status
-
- unless merge_request.can_be_merged?
- return ServiceResponse.error(message: 'Merge request is not mergeable')
- end
-
- unless payload.fetch(:merge_ref_head)
- return ServiceResponse.error(message: 'Merge ref was not found')
- end
-
- ServiceResponse.success(payload: payload)
- end
-
- private
-
- attr_reader :merge_request
-
- def payload
- strong_memoize(:payload) do
- {
- merge_ref_head: merge_ref_head_payload
- }
- end
- end
-
- def merge_ref_head_payload
- commit = merge_request.merge_ref_head
-
- return unless commit
-
- target_id, source_id = commit.parent_ids
-
- {
- commit_id: commit.id,
- source_id: source_id,
- target_id: target_id
- }
- end
-
- def update_merge_status
- return unless merge_request.recheck_merge_status?
-
- if can_git_merge?
- merge_to_ref && merge_request.mark_as_mergeable
- else
- merge_request.mark_as_unmergeable
- end
- end
-
- def can_git_merge?
- !merge_request.broken? && repository.can_be_merged?(merge_request.diff_head_sha, merge_request.target_branch)
- end
-
- def merge_to_ref
- result = MergeRequests::MergeToRefService.new(project, merge_request.author).execute(merge_request)
- result[:status] == :success
- end
- end
-end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 08130a531ee..4b199bd8fa8 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -24,7 +24,7 @@ module MergeRequests
reload_merge_requests
outdate_suggestions
refresh_pipelines_on_merge_requests
- cancel_auto_merge
+ cancel_auto_merges
mark_pending_todos_done
cache_merge_requests_closing_issues
@@ -142,9 +142,9 @@ module MergeRequests
end
end
- def cancel_auto_merge
+ def cancel_auto_merges
merge_requests_for_source_branch.each do |merge_request|
- AutoMergeService.new(project, current_user).cancel(merge_request)
+ cancel_auto_merge(merge_request)
end
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 6a0f3000ffb..0066cd0491f 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -43,6 +43,8 @@ module MergeRequests
create_branch_change_note(merge_request, 'target',
merge_request.previous_changes['target_branch'].first,
merge_request.target_branch)
+
+ cancel_auto_merge(merge_request)
end
if merge_request.assignees != old_assignees
diff --git a/app/services/pages_domains/create_acme_order_service.rb b/app/services/pages_domains/create_acme_order_service.rb
new file mode 100644
index 00000000000..c600f497fa5
--- /dev/null
+++ b/app/services/pages_domains/create_acme_order_service.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module PagesDomains
+ class CreateAcmeOrderService
+ attr_reader :pages_domain
+
+ def initialize(pages_domain)
+ @pages_domain = pages_domain
+ end
+
+ def execute
+ lets_encrypt_client = Gitlab::LetsEncrypt::Client.new
+ order = lets_encrypt_client.new_order(pages_domain.domain)
+
+ challenge = order.new_challenge
+
+ private_key = OpenSSL::PKey::RSA.new(4096)
+ saved_order = pages_domain.acme_orders.create!(
+ url: order.url,
+ expires_at: order.expires,
+ private_key: private_key.to_pem,
+
+ challenge_token: challenge.token,
+ challenge_file_content: challenge.file_content
+ )
+
+ challenge.request_validation
+ saved_order
+ end
+ end
+end
diff --git a/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb b/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb
new file mode 100644
index 00000000000..2dfe1a3d8ca
--- /dev/null
+++ b/app/services/pages_domains/obtain_lets_encrypt_certificate_service.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module PagesDomains
+ class ObtainLetsEncryptCertificateService
+ attr_reader :pages_domain
+
+ def initialize(pages_domain)
+ @pages_domain = pages_domain
+ end
+
+ def execute
+ pages_domain.acme_orders.expired.delete_all
+ acme_order = pages_domain.acme_orders.first
+
+ unless acme_order
+ ::PagesDomains::CreateAcmeOrderService.new(pages_domain).execute
+ return
+ end
+
+ api_order = ::Gitlab::LetsEncrypt::Client.new.load_order(acme_order.url)
+
+ # https://tools.ietf.org/html/rfc8555#section-7.1.6 - statuses diagram
+ case api_order.status
+ when 'ready'
+ api_order.request_certificate(private_key: acme_order.private_key, domain: pages_domain.domain)
+ when 'valid'
+ save_certificate(acme_order.private_key, api_order)
+ acme_order.destroy!
+ # when 'invalid'
+ # TODO: implement error handling
+ end
+ end
+
+ private
+
+ def save_certificate(private_key, api_order)
+ certificate = api_order.certificate
+ pages_domain.update!(key: private_key, certificate: certificate)
+ end
+ end
+end
diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb
index 7386530f45f..2b4c4ae68e2 100644
--- a/app/services/preview_markdown_service.rb
+++ b/app/services/preview_markdown_service.rb
@@ -38,7 +38,9 @@ class PreviewMarkdownService < BaseService
head_sha: params[:head_sha],
start_sha: params[:start_sha])
- Gitlab::Diff::SuggestionsParser.parse(text, position: position, project: project)
+ Gitlab::Diff::SuggestionsParser.parse(text, position: position,
+ project: project,
+ supports_suggestion: params[:preview_suggestions])
end
def preview_sugestions?
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index fc234bafc57..d8fa9d37359 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -36,18 +36,22 @@ module Projects
def fork_new_project
new_params = {
- visibility_level: allowed_visibility_level,
- description: @project.description,
- name: target_name,
- path: target_path,
- shared_runners_enabled: @project.shared_runners_enabled,
- namespace_id: target_namespace.id,
- fork_network: fork_network,
+ visibility_level: allowed_visibility_level,
+ description: @project.description,
+ name: target_name,
+ path: target_path,
+ shared_runners_enabled: @project.shared_runners_enabled,
+ namespace_id: target_namespace.id,
+ fork_network: fork_network,
+ # We need to set ci_default_git_depth to 0 for the forked project when
+ # @project.ci_default_git_depth is nil in order to keep the same behaviour
+ # and not get ProjectCiCdSetting::DEFAULT_GIT_DEPTH set on create
+ ci_cd_settings_attributes: { default_git_depth: @project.ci_default_git_depth || 0 },
# We need to assign the fork network membership after the project has
# been instantiated to avoid ActiveRecord trying to create it when
# initializing the project, as that would cause a foreign key constraint
# exception.
- relations_block: -> (project) { build_fork_network_member(project) }
+ relations_block: -> (project) { build_fork_network_member(project) }
}
if @project.avatar.present? && @project.avatar.image?
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 302510341ac..e0cbfac2420 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -52,10 +52,6 @@ class SearchService
@search_objects ||= search_results.objects(scope, params[:page])
end
- def display_options
- @display_options ||= search_results.display_options(scope)
- end
-
private
def search_service
diff --git a/app/services/service_response.rb b/app/services/service_response.rb
index f3437ba16de..1de30e68d87 100644
--- a/app/services/service_response.rb
+++ b/app/services/service_response.rb
@@ -1,20 +1,19 @@
# frozen_string_literal: true
class ServiceResponse
- def self.success(message: nil, payload: {})
- new(status: :success, message: message, payload: payload)
+ def self.success(message: nil)
+ new(status: :success, message: message)
end
- def self.error(message:, payload: {}, http_status: nil)
- new(status: :error, message: message, payload: payload, http_status: http_status)
+ def self.error(message:, http_status: nil)
+ new(status: :error, message: message, http_status: http_status)
end
- attr_reader :status, :message, :http_status, :payload
+ attr_reader :status, :message, :http_status
- def initialize(status:, message: nil, payload: {}, http_status: nil)
+ def initialize(status:, message: nil, http_status: nil)
self.status = status
self.message = message
- self.payload = payload
self.http_status = http_status
end
@@ -28,5 +27,5 @@ class ServiceResponse
private
- attr_writer :status, :message, :http_status, :payload
+ attr_writer :status, :message, :http_status
end
diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml
index a161fbd064e..c6781e91cfd 100644
--- a/app/views/abuse_reports/new.html.haml
+++ b/app/views/abuse_reports/new.html.haml
@@ -1,10 +1,10 @@
-- page_title _("Report abuse to GitLab")
+- page_title _("Report abuse to admin")
%h3.page-title
- = _("Report abuse to GitLab")
+ = _("Report abuse to admin")
%p
- = _("Please use this form to report users to GitLab who create spam issues, comments or behave inappropriately.")
+ = _("Please use this form to report to the admin users who create spam issues, comments or behave inappropriately.")
%p
- = _("A member of GitLab's abuse team will review your report as soon as possible.")
+ = _("A member of the abuse team will review your report as soon as possible.")
%hr
= form_for @abuse_report, html: { class: 'js-quick-submit js-requires-input'} do |f|
= form_errors(@abuse_report)
diff --git a/app/views/admin/application_settings/_performance_bar.html.haml b/app/views/admin/application_settings/_performance_bar.html.haml
index f992d531ea5..1e66b635038 100644
--- a/app/views/admin/application_settings/_performance_bar.html.haml
+++ b/app/views/admin/application_settings/_performance_bar.html.haml
@@ -6,7 +6,7 @@
.form-check
= f.check_box :performance_bar_enabled, class: 'form-check-input'
= f.label :performance_bar_enabled, class: 'form-check-label qa-enable-performance-bar-checkbox' do
- Enable the Performance Bar
+ Enable access to the Performance Bar
.form-group
= f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'label-bold'
= f.text_field :performance_bar_allowed_group_path, class: 'form-control', placeholder: 'my-org/my-group', value: @application_setting.performance_bar_allowed_group&.full_path
diff --git a/app/views/admin/application_settings/_visibility_and_access.html.haml b/app/views/admin/application_settings/_visibility_and_access.html.haml
index 03ef2924617..c07bafbe302 100644
--- a/app/views/admin/application_settings/_visibility_and_access.html.haml
+++ b/app/views/admin/application_settings/_visibility_and_access.html.haml
@@ -8,6 +8,7 @@
.form-group
= f.label s_('ProjectCreationLevel|Default project creation protection'), class: 'label-bold'
= f.select :default_project_creation, options_for_select(Gitlab::Access.project_creation_options, @application_setting.default_project_creation), {}, class: 'form-control'
+ = render_if_exists 'admin/application_settings/default_project_deletion_protection_setting', form: f
.form-group.visibility-level-setting
= f.label :default_project_visibility, class: 'label-bold'
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new)
diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml
index d5ba6abe7af..01d61beaf53 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -31,7 +31,7 @@
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Enable the Performance Bar for a given group.')
+ = _('Enable access to the Performance Bar for a given group.')
= link_to icon('question-circle'), help_page_path('administration/monitoring/performance/performance_bar')
.settings-content
= render 'performance_bar'
diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml
index 464b9faf282..94102b4dcd0 100644
--- a/app/views/ci/variables/_index.html.haml
+++ b/app/views/ci/variables/_index.html.haml
@@ -6,7 +6,7 @@
= s_('Environment variables are configured by your administrator to be %{link_start}protected%{link_end} by default').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
.row
- .col-lg-12.js-ci-variable-list-section{ data: { save_endpoint: save_endpoint } }
+ .col-lg-12.js-ci-variable-list-section{ data: { save_endpoint: save_endpoint, maskable_regex: ci_variable_maskable_regex } }
.hide.alert.alert-danger.js-ci-variable-error-box
%ul.ci-variable-list
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 214630d245a..8212fb8bb33 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -34,7 +34,7 @@
= icon('spinner spin')
.todos-filters
- .row-content-block.second-block
+ .issues-details-filters.row-content-block.second-block
= form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form d-sm-flex' do
.filter-categories.flex-fill
.filter-item.inline
diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml
index 8745a4e9d3e..4cd03be572f 100644
--- a/app/views/devise/shared/_tabs_normal.html.haml
+++ b/app/views/devise/shared/_tabs_normal.html.haml
@@ -3,4 +3,4 @@
%a.nav-link.qa-sign-in-tab.active{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in
- if allow_signup?
%li.nav-item{ role: 'presentation' }
- %a.nav-link.qa-register-tab{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab' } Register
+ %a.nav-link.qa-register-tab{ href: '#register-pane', data: { track_label: 'sign_in_register', track_property: 'sign_in', track_event: 'click_button', track_value: 'register', toggle: 'tab' }, role: 'tab' } Register
diff --git a/app/views/discussions/_notes.html.haml b/app/views/discussions/_notes.html.haml
index 30b00ca86b3..0a5541c3e82 100644
--- a/app/views/discussions/_notes.html.haml
+++ b/app/views/discussions/_notes.html.haml
@@ -19,20 +19,24 @@
.discussion-reply-holder
- if can_create_note?
+ %a.user-avatar-link.d-none.d-sm-block{ href: user_path(current_user) }
+ = image_tag avatar_icon_for_user(current_user), alt: current_user.to_reference, class: 'avatar s40'
- if discussion.potentially_resolvable?
- line_type = local_assigns.fetch(:line_type, nil)
- .btn-group.discussion-with-resolve-btn{ role: "group" }
- .btn-group{ role: "group" }
- = link_to_reply_discussion(discussion, line_type)
+ .discussion-with-resolve-btn
+ .btn-group.discussion-with-resolve-btn{ role: "group" }
+ .btn-group{ role: "group" }
+ = link_to_reply_discussion(discussion, line_type)
- = render "discussions/resolve_all", discussion: discussion
+ = render "discussions/resolve_all", discussion: discussion
- .btn-group.discussion-actions
- = render "discussions/new_issue_for_discussion", discussion: discussion, merge_request: discussion.noteable
- = render "discussions/jump_to_next", discussion: discussion
+ .btn-group.discussion-actions
+ = render "discussions/new_issue_for_discussion", discussion: discussion, merge_request: discussion.noteable
+ = render "discussions/jump_to_next", discussion: discussion
- else
- = link_to_reply_discussion(discussion)
+ .discussion-with-resolve-btn
+ = link_to_reply_discussion(discussion)
- elsif !current_user
.disabled-comment.text-center
Please
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 77fe88dacb7..255a9ad038c 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -9,7 +9,7 @@
= render 'groups/home_panel'
.groups-listing{ data: { endpoints: { default: group_children_path(@group, format: :json), shared: group_shared_projects_path(@group, format: :json) } } }
- .top-area.group-nav-container
+ .top-area.group-nav-container.justify-content-between
.scrolling-tabs-container.inner-page-scroll-tabs
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 47710b9e9e5..54028dc8554 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -19,17 +19,17 @@
- if dashboard_nav_link?(:activity)
= nav_link(path: 'dashboard#activity', html_options: { class: ["d-none d-xl-block", ("d-lg-block" unless has_extra_nav_icons?)] }) do
- = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: _('Activity') do
+ = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity' do
= _('Activity')
- if dashboard_nav_link?(:milestones)
= nav_link(controller: 'dashboard/milestones', html_options: { class: ["d-none d-xl-block", ("d-lg-block" unless has_extra_nav_icons?)] }) do
- = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: _('Milestones') do
+ = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones' do
= _('Milestones')
- if dashboard_nav_link?(:snippets)
= nav_link(controller: 'dashboard/snippets', html_options: { class: ["d-none d-xl-block", ("d-lg-block" unless has_extra_nav_icons?)] }) do
- = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets qa-snippets-link', title: _('Snippets') do
+ = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets qa-snippets-link' do
= _('Snippets')
- if any_dashboard_nav_link?([:groups, :milestones, :activity, :snippets])
@@ -41,47 +41,47 @@
%ul
- if dashboard_nav_link?(:activity)
= nav_link(path: 'dashboard#activity') do
- = link_to activity_dashboard_path, title: _('Activity') do
+ = link_to activity_dashboard_path do
= _('Activity')
- if dashboard_nav_link?(:milestones)
= nav_link(controller: 'dashboard/milestones') do
- = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: _('Milestones') do
+ = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones' do
= _('Milestones')
- if dashboard_nav_link?(:snippets)
= nav_link(controller: 'dashboard/snippets') do
- = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: _('Snippets') do
+ = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets' do
= _('Snippets')
-
- = render_if_exists 'dashboard/operations/nav_link'
+ %li.dropdown.d-lg-none
+ = render_if_exists 'dashboard/operations/nav_link_list'
- if can?(current_user, :read_instance_statistics)
- = nav_link(controller: [:conversational_development_index, :cohorts]) do
- = link_to instance_statistics_root_path, title: _('Instance Statistics'), aria: { label: _('Instance Statistics') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = nav_link(controller: [:conversational_development_index, :cohorts], html_options: { class: 'd-lg-none' }) do
+ = link_to instance_statistics_root_path do
= _('Instance Statistics')
- if current_user.admin?
= nav_link(controller: 'admin/dashboard') do
- = link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: _('Admin Area'), aria: { label: _('Admin Area') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to admin_root_path, class: 'd-lg-none admin-icon qa-admin-area-link' do
= _('Admin Area')
- if Gitlab::Sherlock.enabled?
%li
- = link_to sherlock_transactions_path, class: 'admin-icon', title: _('Sherlock Transactions'),
- data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to sherlock_transactions_path, class: 'd-lg-none admin-icon' do
= _('Sherlock Transactions')
-# Shortcut to Dashboard > Projects
- if dashboard_nav_link?(:projects)
%li.hidden
- = link_to dashboard_projects_path, title: _('Projects'), class: 'dashboard-shortcuts-projects' do
+ = link_to dashboard_projects_path, class: 'dashboard-shortcuts-projects' do
= _('Projects')
- if current_controller?('ide')
%li.line-separator.d-none.d-sm-block
= nav_link(controller: 'ide') do
- = link_to '#', class: 'dashboard-shortcuts-web-ide', title: _('Web IDE') do
+ = link_to '#', class: 'dashboard-shortcuts-web-ide' do
= _('Web IDE')
- = render_if_exists 'dashboard/operations/nav_link'
+ %li.dropdown{ class: 'd-none d-lg-block' }
+ = render_if_exists 'dashboard/operations/nav_link'
- if can?(current_user, :read_instance_statistics)
= nav_link(controller: [:conversational_development_index, :cohorts], html_options: { class: "d-none d-lg-block d-xl-block"}) do
= link_to instance_statistics_root_path, title: _('Instance Statistics'), aria: { label: _('Instance Statistics') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 399305baec1..9b6551552c7 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -36,6 +36,8 @@
= render_if_exists 'projects/sidebar/security_dashboard'
+ = render_if_exists 'projects/sidebar/dependencies'
+
- if can?(current_user, :read_cycle_analytics, @project)
= nav_link(path: 'cycle_analytics#show') do
= link_to project_cycle_analytics_path(@project), title: _('Cycle Analytics'), class: 'shortcuts-project-cycle-analytics' do
diff --git a/app/views/notify/new_user_email.html.haml b/app/views/notify/new_user_email.html.haml
index dfbb5c75bd3..ec135ae994f 100644
--- a/app/views/notify/new_user_email.html.haml
+++ b/app/views/notify/new_user_email.html.haml
@@ -13,4 +13,5 @@
%p
= link_to "Click here to set your password", edit_password_url(@user, reset_password_token: @token)
%p
- = raw reset_token_expire_message
+ This link is valid for #{password_reset_token_valid_time}.
+ After it expires, you can #{link_to("request a new one", new_user_password_url(user_email: @user.email))}.
diff --git a/app/views/notify/new_user_email.text.erb b/app/views/notify/new_user_email.text.erb
index f3f20f3bfba..7e0db75472d 100644
--- a/app/views/notify/new_user_email.text.erb
+++ b/app/views/notify/new_user_email.text.erb
@@ -1,10 +1,17 @@
Hi <%= sanitize_name(@user.name) %>!
+<% if Gitlab::CurrentSettings.allow_signup? %>
+Your account has been created successfully.
+<% else %>
The Administrator created an account for you. Now you are a member of the company GitLab application.
+<% end %>
login.................. <%= @user.email %>
+
<% if @user.created_by_id %>
- <%= link_to "Click here to set your password", edit_password_url(@user, :reset_password_token => @token) %>
+Click here to set your password:
+<%= edit_password_url(@user, :reset_password_token => @token) %>
- <%= reset_token_expire_message %>
+This link is valid for <%= password_reset_token_valid_time %>. After it expires, you can request a new one here:
+<%= new_user_password_url(user_email: @user.email) %>
<% end %>
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index c90a0b3e329..3c20518c038 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -1,4 +1,4 @@
-- page_title "Emails"
+- page_title _('Emails')
- @content_class = "limit-container-width" unless fluid_layout
.row.prepend-top-default
@@ -6,58 +6,58 @@
%h4.prepend-top-0
= page_title
%p
- Control emails linked to your account
+ = _('Control emails linked to your account')
.col-lg-8
%h4.prepend-top-0
- Add email address
+ = _('Add email address')
= form_for 'email', url: profile_emails_path do |f|
.form-group
- = f.label :email, class: 'label-bold'
+ = f.label :email, _('Email'), class: 'label-bold'
= f.text_field :email, class: 'form-control'
.prepend-top-default
- = f.submit 'Add email address', class: 'btn btn-success'
+ = f.submit _('Add email address'), class: 'btn btn-success'
%hr
%h4.prepend-top-0
- Linked emails (#{@emails.count + 1})
+ = _('Linked emails (%{email_count})') % { email_count: @emails.count + 1 }
.account-well.append-bottom-default
%ul
%li
- Your Primary Email will be used for avatar detection.
+ = _('Your Primary Email will be used for avatar detection.')
%li
- Your Commit Email will be used for web based operations, such as edits and merges.
+ = _('Your Commit Email will be used for web based operations, such as edits and merges.')
%li
- Your Default Notification Email will be used for account notifications if a
- = link_to 'group-specific email address', profile_notifications_path
- is not set.
+ - address = profile_notifications_path
+ - notification_message = _('Your Default Notification Email will be used for account notifications if a %{openingTag}group-specific email address%{closingTag} is not set.') % { openingTag: "<a href='#{address}'>".html_safe, closingTag: '</a>'.html_safe}
+ = notification_message.html_safe
%li
- Your Public Email will be displayed on your public profile.
+ = _('Your Public Email will be displayed on your public profile.')
%li
- All email addresses will be used to identify your commits.
+ = _('All email addresses will be used to identify your commits.')
%ul.content-list
%li
= render partial: 'shared/email_with_badge', locals: { email: @primary_email, verified: current_user.confirmed? }
%span.float-right
- %span.badge.badge-success Primary email
+ %span.badge.badge-success= s_('Profiles|Primary email')
- if @primary_email === current_user.commit_email
- %span.badge.badge-info Commit email
+ %span.badge.badge-info= s_('Profiles|Commit email')
- if @primary_email === current_user.public_email
- %span.badge.badge-info Public email
+ %span.badge.badge-info= s_('Profiles|Public email')
- if @primary_email === current_user.notification_email
- %span.badge.badge-info Default notification email
+ %span.badge.badge-info= s_('Profiles|Default notification email')
- @emails.each do |email|
%li
= render partial: 'shared/email_with_badge', locals: { email: email.email, verified: email.confirmed? }
%span.float-right
- if email.email === current_user.commit_email
- %span.badge.badge-info Commit email
+ %span.badge.badge-info= s_('Profiles|Commit email')
- if email.email === current_user.public_email
- %span.badge.badge-info Public email
+ %span.badge.badge-info= s_('Profiles|Public email')
- if email.email === current_user.notification_email
- %span.badge.badge-info Notification email
+ %span.badge.badge-info= s_('Profiles|Notification email')
- unless email.confirmed?
- - confirm_title = "#{email.confirmation_sent_at ? 'Resend' : 'Send'} confirmation email"
+ - confirm_title = "#{email.confirmation_sent_at ? _('Resend confirmation email') : _('Send confirmation email')}"
= link_to confirm_title, resend_confirmation_instructions_profile_email_path(email), method: :put, class: 'btn btn-sm btn-warning prepend-left-10'
- = link_to profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-danger prepend-left-10' do
- %span.sr-only Remove
+ = link_to profile_email_path(email), data: { confirm: _('Are you sure?')}, method: :delete, class: 'btn btn-sm btn-danger prepend-left-10' do
+ %span.sr-only= _('Remove')
= icon('trash')
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index 7846cdbcd52..63ef5eaa172 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -4,8 +4,8 @@
.form-group
= f.label :key, s_('Profiles|Key'), class: 'label-bold'
- %p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key.")
- = f.text_area :key, class: "form-control js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"')
+ %p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Don't use your private SSH key.")
+ = f.text_area :key, class: "form-control js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-ed25519 …" or "ssh-rsa …"')
.form-group
= f.label :title, _('Title'), class: 'label-bold'
= f.text_field :title, class: "form-control input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 46384bc28ef..4ebfaff0860 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -5,7 +5,8 @@
.col-lg-4.application-theme
%h4.prepend-top-0
= s_('Preferences|Navigation theme')
- %p= _('Customize the appearance of the application header and navigation sidebar.')
+ %p
+ = s_('Preferences|Customize the appearance of the application header and navigation sidebar.')
.col-lg-8.application-theme
- Gitlab::Themes.each do |theme|
= label_tag do
@@ -18,9 +19,9 @@
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
- = _('Syntax highlighting theme')
+ = s_('Preferences|Syntax highlighting theme')
%p
- = _('This setting allows you to customize the appearance of the syntax.')
+ = s_('Preferences|This setting allows you to customize the appearance of the syntax.')
= succeed '.' do
= link_to _('Learn more'), help_page_path('user/profile/preferences', anchor: 'syntax-highlighting-theme'), target: '_blank'
.col-lg-8.syntax-theme
@@ -35,31 +36,31 @@
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
- = _('Behavior')
+ = s_('Preferences|Behavior')
%p
- = _('This setting allows you to customize the behavior of the system layout and default views.')
+ = s_('Preferences|This setting allows you to customize the behavior of the system layout and default views.')
= succeed '.' do
= link_to _('Learn more'), help_page_path('user/profile/preferences', anchor: 'behavior'), target: '_blank'
.col-lg-8
.form-group
= f.label :layout, class: 'label-bold' do
- = _('Layout width')
+ = s_('Preferences|Layout width')
= f.select :layout, layout_choices, {}, class: 'form-control'
.form-text.text-muted
- = _('Choose between fixed (max. 1280px) and fluid (100%%) application layout.')
+ = s_('Preferences|Choose between fixed (max. 1280px) and fluid (100%%) application layout.')
.form-group
= f.label :dashboard, class: 'label-bold' do
- = _('Default dashboard')
+ = s_('Preferences|Default dashboard')
= f.select :dashboard, dashboard_choices, {}, class: 'form-control'
= render_if_exists 'profiles/preferences/group_overview_selector', f: f # EE-specific
.form-group
= f.label :project_view, class: 'label-bold' do
- = _('Project overview content')
+ = s_('Preferences|Project overview content')
= f.select :project_view, project_view_choices, {}, class: 'form-control'
.form-text.text-muted
- = _('Choose what content you want to see on a project’s overview page.')
+ = s_('Preferences|Choose what content you want to see on a project’s overview page.')
.col-sm-12
%hr
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 9f5241344a7..824fe3c791d 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -64,7 +64,7 @@
.home-panel-home-desc.mt-1
- if @project.description.present?
- .home-panel-description
+ .home-panel-description.text-break
.home-panel-description-markdown.read-more-container
= markdown_field(@project, :description)
%button.btn.btn-blank.btn-link.js-read-more-trigger.d-lg-none{ type: "button" }
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 1074cd6bf4e..a5eaae2dff4 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -22,7 +22,7 @@
%span.badge.badge-success.prepend-left-5
= s_('Branches|protected')
- = render_if_exists 'projects/branches/diverged_from_upstream'
+ = render_if_exists 'projects/branches/diverged_from_upstream', branch: branch
.block-truncated
- if commit
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index ef2777e6601..77ea2c04b28 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -28,7 +28,7 @@
= link_to project_tree_path(@project, @commit), class: "btn btn-default append-right-10 d-none d-sm-none d-md-inline" do
#{ _('Browse files') }
.dropdown.inline
- %a.btn.btn-default.dropdown-toggle.qa-options-button{ data: { toggle: "dropdown" } }
+ %a.btn.btn-default.dropdown-toggle.qa-options-button.d-md-inline{ data: { toggle: "dropdown" } }
%span= _('Options')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-right
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index 311b0be19ab..9587ea4696b 100644
--- a/app/views/projects/diffs/_parallel_view.html.haml
+++ b/app/views/projects/diffs/_parallel_view.html.haml
@@ -1,7 +1,7 @@
/ Side-by-side diff view
.text-file{ data: diff_view_data }
- %table.diff-wrap-lines.code.js-syntax-highlight
+ %table.diff-wrap-lines.code.code-commit.js-syntax-highlight
- diff_file.parallel_diff_lines.each do |line|
- left = line[:left]
- right = line[:right]
diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml
index 018c5b38536..641a0689c26 100644
--- a/app/views/projects/diffs/_text_file.html.haml
+++ b/app/views/projects/diffs/_text_file.html.haml
@@ -3,7 +3,7 @@
.suppressed-container
%a.show-suppressed-diff.cursor-pointer.js-show-suppressed-diff= _("Changes suppressed. Click to show.")
-%table.text-file.diff-wrap-lines.code.js-syntax-highlight.commit-diff{ data: diff_view_data, class: too_big ? 'hide' : '' }
+%table.text-file.diff-wrap-lines.code.code-commit.js-syntax-highlight.commit-diff{ data: diff_view_data, class: too_big ? 'hide' : '' }
= render partial: "projects/diffs/line",
collection: diff_file.highlighted_diff_lines,
as: :line,
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index fbd70cd1906..457b2936278 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -8,18 +8,18 @@
- create_branch_path = project_branches_path(@project, branch_name: @issue.to_branch_name, ref: @project.default_branch, issue_iid: @issue.iid)
- refs_path = refs_namespace_project_path(@project.namespace, @project, search: '')
- .create-mr-dropdown-wrap.d-inline-block{ data: { can_create_path: can_create_path, create_mr_path: create_mr_path, create_branch_path: create_branch_path, refs_path: refs_path } }
- .btn-group.unavailable
+ .create-mr-dropdown-wrap.d-inline-block.full-width-mobile{ data: { can_create_path: can_create_path, create_mr_path: create_mr_path, create_branch_path: create_branch_path, refs_path: refs_path } }
+ .btn-group.btn-group-sm.unavailable
%button.btn.btn-grouped{ type: 'button', disabled: 'disabled' }
= icon('spinner', class: 'fa-spin')
%span.text
Checking branch availability…
- .btn-group.available.hidden
+ .btn-group.btn-group-sm.available.hidden
%button.btn.js-create-merge-request.btn-success.btn-inverted{ type: 'button', data: { action: data_action } }
= value
- %button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' }, display: 'static' } }
+ %button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle.flex-grow-0{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' }, display: 'static' } }
= icon('caret-down')
.droplab-dropdown
diff --git a/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml b/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
index 03226de120d..7bd5c437942 100644
--- a/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
+++ b/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
@@ -1,5 +1,5 @@
%inline-conflict-lines{ "inline-template" => "true", ":file" => "file" }
- %table.diff-wrap-lines.code.js-syntax-highlight
+ %table.diff-wrap-lines.code.code-commit.js-syntax-highlight
%tr.line_holder.diff-inline{ "v-for" => "line in file.inlineLines" }
%td.diff-line-num.new_line{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader" }
%a {{line.new_line}}
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 05aeb5d972b..a201fafb949 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -31,29 +31,26 @@
.merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
.merge-request-tabs-container
- .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
- .fade-left= icon('angle-left')
- .fade-right= icon('angle-right')
- %ul.merge-request-tabs.nav-tabs.nav.nav-links.scrolling-tabs
- %li.notes-tab.qa-notes-tab
- = tab_link_for @merge_request, :show, force_link: @commit.present? do
- = _("Discussion")
- %span.badge.badge-pill= @merge_request.related_notes.user.count
- - if @merge_request.source_project
- %li.commits-tab
- = tab_link_for @merge_request, :commits do
- = _("Commits")
- %span.badge.badge-pill= @commits_count
- - if @pipelines.any?
- %li.pipelines-tab
- = tab_link_for @merge_request, :pipelines do
- = _("Pipelines")
- %span.badge.badge-pill.js-pipelines-mr-count= @pipelines.size
- %li.diffs-tab.qa-diffs-tab
- = tab_link_for @merge_request, :diffs do
- = _("Changes")
- %span.badge.badge-pill= @merge_request.diff_size
- .d-inline-flex.flex-wrap
+ %ul.merge-request-tabs.nav-tabs.nav.nav-links
+ %li.notes-tab.qa-notes-tab
+ = tab_link_for @merge_request, :show, force_link: @commit.present? do
+ = _("Discussion")
+ %span.badge.badge-pill= @merge_request.related_notes.user.count
+ - if @merge_request.source_project
+ %li.commits-tab
+ = tab_link_for @merge_request, :commits do
+ = _("Commits")
+ %span.badge.badge-pill= @commits_count
+ - if @pipelines.any?
+ %li.pipelines-tab
+ = tab_link_for @merge_request, :pipelines do
+ = _("Pipelines")
+ %span.badge.badge-pill.js-pipelines-mr-count= @pipelines.size
+ %li.diffs-tab.qa-diffs-tab
+ = tab_link_for @merge_request, :diffs do
+ = _("Changes")
+ %span.badge.badge-pill= @merge_request.diff_size
+ .d-flex.flex-wrap.align-items-center.justify-content-lg-end
#js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(@merge_request),
notes_filters: UserPreference.notes_filters.to_json } }
#js-vue-discussion-counter
diff --git a/app/views/projects/notes/_more_actions_dropdown.html.haml b/app/views/projects/notes/_more_actions_dropdown.html.haml
index 8de84f82e9f..8a6e5fde99b 100644
--- a/app/views/projects/notes/_more_actions_dropdown.html.haml
+++ b/app/views/projects/notes/_more_actions_dropdown.html.haml
@@ -11,7 +11,7 @@
- unless is_current_user
%li
= link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
- = _('Report abuse to GitLab')
+ = _('Report abuse to admin')
- if note_editable
%li
= link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index b38b8e3f686..2d108a1cba5 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -26,6 +26,14 @@
%hr
.form-group
+ = f.fields_for :ci_cd_settings_attributes, @project.ci_cd_settings do |form|
+ = form.label :default_git_depth, _('Git shallow clone'), class: 'label-bold'
+ = form.number_field :default_git_depth, { class: 'form-control', min: 0, max: 1000 }
+ %p.form-text.text-muted
+ = _('The number of changes to be fetched from GitLab when cloning a repository. This can speed up Pipelines execution. Keep empty or set to 0 to disable shallow clone by default and make GitLab CI fetch all branches and tags each time.')
+
+ %hr
+ .form-group
= f.label :build_timeout_human_readable, _('Timeout'), class: 'label-bold'
= f.text_field :build_timeout_human_readable, class: 'form-control'
%p.form-text.text-muted
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index 458096f9dd6..2e78b0bff3e 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -9,7 +9,7 @@
.nav-text.row-main-content
= s_('TagsPage|Tags give the ability to mark specific points in history as being important')
- .nav-controls.row-fixed-content
+ .nav-controls
= form_tag(filter_tags_path, method: :get) do
= search_field_tag :search, params[:search], { placeholder: s_('TagsPage|Filter by tag name'), id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index 0be62bc5612..59232372150 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -18,7 +18,7 @@
- else
= s_("TagsPage|Can't find HEAD commit for this tag")
- .nav-controls.controls-flex
+ .nav-controls
- if can?(current_user, :push_code, @project)
= link_to edit_project_tag_release_path(@project, @tag.name), class: 'btn btn-edit controls-item has-tooltip', title: s_('TagsPage|Edit release notes') do
= icon("pencil")
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 12eb8d7fa81..cb8a8a24be8 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -21,7 +21,7 @@
.search-results
- if @scope == 'projects'
.term
- = render 'shared/projects/list', { projects: @search_objects, pipeline_status: false }.merge(@display_options)
+ = render 'shared/projects/list', projects: @search_objects, pipeline_status: false
- else
- locals = { projects: blob_projects(@search_objects) } if %w[blobs wiki_blobs].include?(@scope)
= render partial: "search/results/#{@scope.singularize}", collection: @search_objects, locals: locals
diff --git a/app/views/shared/_email_with_badge.html.haml b/app/views/shared/_email_with_badge.html.haml
index ad863b1967d..294fe74a5ca 100644
--- a/app/views/shared/_email_with_badge.html.haml
+++ b/app/views/shared/_email_with_badge.html.haml
@@ -1,6 +1,6 @@
- css_classes = %w(badge badge-verification-status)
- css_classes << (verified ? 'verified': 'unverified')
-- text = verified ? 'Verified' : 'Unverified'
+- text = verified ? _('Verified') : _('Unverified')
.email-badge
.email-badge-email= email
diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml
index 9ec76d82d18..e83ca5eaab8 100644
--- a/app/views/shared/members/_group.html.haml
+++ b/app/views/shared/members/_group.html.haml
@@ -2,9 +2,12 @@
- group = group_link.group
- can_admin_member = can?(current_user, :admin_project_member, @project)
- dom_id = "group_member_#{group_link.id}"
-%li.member.group_member{ id: dom_id }
- %span.list-item-name
- = group_icon(group, class: "avatar s40", alt: '')
+
+-# Note this is just for groups. For individual members please see shared/members/_member
+
+%li.member.group_member.py-2.px-3.d-flex.flex-column.flex-md-row{ id: dom_id }
+ %span.list-item-name.mb-2.m-md-0
+ = group_icon(group, class: "avatar s40 flex-shrink-0 flex-grow-0", alt: '')
.user-info
= link_to group.full_name, group_path(group), class: 'member'
.cgray
@@ -13,10 +16,10 @@
·
%span{ class: ('text-warning' if group_link.expires_soon?) }
= _("Expires in %{expires_at}").html_safe % { expires_at: distance_of_time_in_words_to_now(group_link.expires_at) }
- .controls.member-controls
- = form_tag project_group_link_path(@project, group_link), method: :put, remote: true, class: 'js-edit-member-form form-group row append-right-5' do
+ .controls.member-controls.align-items-center
+ = form_tag project_group_link_path(@project, group_link), method: :put, remote: true, class: 'js-edit-member-form form-group d-sm-flex' do
= hidden_field_tag "group_link[group_access]", group_link.group_access
- .member-form-control.dropdown.append-right-5
+ .member-form-control.dropdown.mr-sm-2.d-sm-inline-block
%button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button",
disabled: !can_admin_member,
data: { toggle: "dropdown", field_name: "group_link[group_access]" } }
@@ -32,14 +35,14 @@
= link_to role, "javascript:void(0)",
class: ("is-active" if group_link.group_access == role_id),
data: { id: role_id, el_id: dom_id }
- .prepend-left-5.clearable-input.member-form-control
+ .clearable-input.member-form-control.d-sm-inline-block
= text_field_tag 'group_link[expires_at]', group_link.expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: _('Expiration date'), id: "member_expires_at_#{group.id}", disabled: !can_admin_member
%i.clear-icon.js-clear-input
- if can_admin_member
= link_to project_group_link_path(@project, group_link),
method: :delete,
data: { confirm: _("Are you sure you want to remove %{group_name}?") % { group_name: group.name } },
- class: 'btn btn-remove prepend-left-10' do
+ class: 'btn btn-remove m-0 ml-sm-2 align-self-center' do
%span.d-block.d-sm-none
= _("Delete")
= icon('trash', class: 'd-none d-sm-block')
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index afcb2b71472..331283f7eec 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -6,10 +6,12 @@
- source = member.source
- override = member.try(:override)
-%li.member{ class: [dom_class(member), ("is-overridden" if override)], id: dom_id(member) }
- %span.list-item-name
+-# Note this is just for individual members. For groups please see shared/members/_group
+
+%li.member.py-2.px-3.d-flex.flex-column{ class: [dom_class(member), ("is-overridden" if override), ("flex-md-row" unless force_mobile_view)], id: dom_id(member) }
+ %span.list-item-name.mb-2.m-md-0
- if user
- = image_tag avatar_icon_for_user(user, 40), class: "avatar s40", alt: ''
+ = image_tag avatar_icon_for_user(user, 40), class: "avatar s40 flex-shrink-0 flex-grow-0", alt: ''
.user-info
= link_to user.name, user_path(user), class: 'member js-user-link', data: { user_id: user.id }
= user_status(user)
@@ -43,7 +45,7 @@
= _("Expires in %{expires_at}").html_safe % { expires_at: distance_of_time_in_words_to_now(member.expires_at) }
- else
- = image_tag avatar_icon_for_email(member.invite_email, 40), class: "avatar s40", alt: ''
+ = image_tag avatar_icon_for_email(member.invite_email, 40), class: "avatar s40 flex-shrink-0 flex-grow-0", alt: ''
.user-info
.member= member.invite_email
.cgray
@@ -54,20 +56,20 @@
= time_ago_with_tooltip(member.created_at)
- if show_roles
- current_resource = @project || @group
- .controls.member-controls
+ .controls.member-controls.align-items-center
= render_if_exists 'shared/members/ee/ldap_tag', can_override: member.can_override?
- if show_controls && member.source == current_resource
- if member.can_resend_invite?
= link_to icon('paper-plane'), polymorphic_path([:resend_invite, member]),
method: :post,
- class: 'btn btn-default prepend-left-10 d-none d-sm-block',
+ class: 'btn btn-default align-self-center mr-sm-2',
title: _('Resend invite')
- if user != current_user && member.can_update?
- = form_for member, remote: true, html: { class: 'js-edit-member-form form-group row append-right-5' } do |f|
+ = form_for member, remote: true, html: { class: "js-edit-member-form form-group #{'d-sm-flex' unless force_mobile_view}" } do |f|
= f.hidden_field :access_level
- .member-form-control.dropdown.append-right-5
+ .member-form-control.dropdown{ class: [("mr-sm-2 d-sm-inline-block" unless force_mobile_view)] }
%button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button",
disabled: member.can_override? && !override,
data: { toggle: "dropdown", field_name: "#{f.object_name}[access_level]" } }
@@ -87,7 +89,7 @@
group: @group,
member: member,
can_override: member.can_override?
- .prepend-left-5.clearable-input.member-form-control
+ .clearable-input.member-form-control{ class: [("d-sm-inline-block" unless force_mobile_view)] }
= f.text_field :expires_at,
disabled: member.can_override? && !override,
class: 'form-control js-access-expiration-date js-member-update-control',
@@ -96,12 +98,12 @@
data: { el_id: dom_id(member) }
%i.clear-icon.js-clear-input
- else
- %span.member-access-text= member.human_access
+ %span.member-access-text.user-access-role= member.human_access
- if member.can_approve?
= link_to polymorphic_path([:approve_access_request, member]),
method: :post,
- class: 'btn btn-success prepend-left-10',
+ class: "btn btn-success align-self-center m-0 mb-2 #{'mb-sm-0 ml-sm-2' unless force_mobile_view}",
title: _('Grant access') do
%span{ class: ('d-block d-sm-none' unless force_mobile_view) }
= _('Grant access')
@@ -113,12 +115,12 @@
= link_to icon('sign-out', text: _('Leave')), polymorphic_path([:leave, member.source, :members]),
method: :delete,
data: { confirm: leave_confirmation_message(member.source) },
- class: 'btn btn-remove prepend-left-10'
+ class: "btn btn-remove align-self-center m-0 #{'ml-sm-2' unless force_mobile_view}"
- else
= link_to member,
method: :delete,
data: { confirm: remove_member_message(member) },
- class: 'btn btn-remove prepend-left-10',
+ class: "btn btn-remove align-self-center m-0 #{'ml-sm-2' unless force_mobile_view}",
title: remove_member_title(member) do
%span{ class: ('d-block d-sm-none' unless force_mobile_view) }
= _("Delete")
@@ -126,6 +128,6 @@
= icon('trash', class: 'd-none d-sm-block')
= render_if_exists 'shared/members/ee/override_member_buttons', group: @group, member: member, user: user, action: :edit, can_override: member.can_override?
- else
- %span.member-access-text= member.human_access
+ %span.member-access-text.user-access-role= member.human_access
= render_if_exists 'shared/members/ee/override_member_buttons', group: @group, member: member, user: user, action: :confirm, can_override: member.can_override?
diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml
index 4a8f90937ea..acd90fa9178 100644
--- a/app/views/shared/milestones/_deprecation_message.html.haml
+++ b/app/views/shared/milestones/_deprecation_message.html.haml
@@ -11,4 +11,5 @@
%ol.milestone-popover-instructions-list.append-bottom-0
%li= _('Click any <strong>project name</strong> in the project list below to navigate to the project milestone.').html_safe
%li= _('Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone.').html_safe
+ %hr.popover-hr
.milestone-popover-footer= link_to _('Learn more'), help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-link prepend-left-0', target: '_blank'
diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml
index 6fec435cc87..5c9dd72418e 100644
--- a/app/views/shared/notes/_note.html.haml
+++ b/app/views/shared/notes/_note.html.haml
@@ -31,7 +31,7 @@
.note-header
.note-header-info
%a{ href: user_path(note.author) }
- %span.note-header-author-name
+ %span.note-header-author-name.bold
= sanitize(note.author.name)
= user_status(note.author)
%span.note-headline-light
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 6dc61088e65..a71bfd624e4 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -9,7 +9,7 @@
= auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
.user-profile
- .cover-block.user-cover-block.top-area
+ .cover-block.user-cover-block
.cover-controls
- if @user == current_user
= link_to profile_path, class: 'btn btn-default has-tooltip', title: s_('UserProfile|Edit profile'), 'aria-label': 'Edit profile' do
diff --git a/changelogs/archive.md b/changelogs/archive.md
index dd7c21dabd6..e36e859a351 100644
--- a/changelogs/archive.md
+++ b/changelogs/archive.md
@@ -274,7 +274,7 @@
- Pass variables from deployment project services to CI runner. !8107
- New Gitea importer. !8116
- Introduce "Set up autodeploy" button to help configure GitLab CI for deployment. !8135
-- Prevent enviroment table to overflow when name has underscores. !8142
+- Prevent environment table to overflow when name has underscores. !8142
- Fix missing service error importing from EE to CE. !8144
- Milestoneish SQL performance partially improved and memoized. !8146
- Allow unauthenticated access to Repositories API GET endpoints. !8148
@@ -562,7 +562,7 @@
- Fix broken issue/merge request links in JIRA comments. !6143 (Brian Kintz)
- Fix filtering of milestones with quotes in title (airatshigapov)
- Fix issue boards dragging bug in Safari
-- Refactor less readable existance checking code from CoffeeScript !6289 (jlogandavison)
+- Refactor less readable existence checking code from CoffeeScript !6289 (jlogandavison)
- Update mail_room and enable sentinel support to Reply By Email (!7101)
- Add task completion status in Issues and Merge Requests tabs: "X of Y tasks completed" (!6527, @gmesalazar)
- Simpler arguments passed to named_route on toggle_award_url helper method
@@ -956,7 +956,7 @@
## 8.12.0 (2016-09-22)
- - Removes inconsistency regarding tagging immediatelly as merged once you create a new branch. !6408
+ - Removes inconsistency regarding tagging immediately as merged once you create a new branch. !6408
- Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
- Only check :can_resolve permission if the note is resolvable
- Bump fog-aws to v0.11.0 to support ap-south-1 region
@@ -1389,7 +1389,7 @@
- Fix notification_service argument error of declined invitation emails
- Fix a memory leak caused by Banzai::Filter::SanitizationFilter
- Speed up todos queries by limiting the projects set we join with
- - Ensure file editing in UI does not overwrite commited changes without warning user
+ - Ensure file editing in UI does not overwrite committed changes without warning user
- Eliminate unneeded calls to Repository#blob_at when listing commits with no path
- Update gitlab_git gem to 10.4.7
- Simplify SQL queries of marking a todo as done
@@ -2076,7 +2076,7 @@
- Don't show forks button when user can't view forks
- Fix atom feed links and rendering
- Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718
- - Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes)
+ - Add support for suppressing text diffs using .gitattributes on the default branch (Matt Oakes)
- Add eager load paths to help prevent dependency load issues in Sidekiq workers. !3724
- Added multiple colors for labels in dropdowns when dups happen.
- Show commits in the same order as `git log`
@@ -2194,7 +2194,7 @@
- All images in discussions and wikis now link to their source files !3464 (Connor Shea).
- Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
- Add setting for customizing the list of trusted proxies !3524
- - Allow projects to be transfered to a lower visibility level group
+ - Allow projects to be transferred to a lower visibility level group
- Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524
- Improved Markdown rendering performance !3389
- Make shared runners text in box configurable
@@ -2209,7 +2209,7 @@
- API: Ability to update a group (Robert Schilling)
- API: Ability to move issues (Robert Schilling)
- Fix Error 500 after renaming a project path (Stan Hu)
- - Fix a bug whith trailing slash in teamcity_url (Charles May)
+ - Fix a bug with trailing slash in teamcity_url (Charles May)
- Allow back dating on issues when created or updated through the API
- Allow back dating on issue notes when created through the API
- Propose license template when creating a new LICENSE file
@@ -2219,7 +2219,7 @@
- Allow SAML to handle external users based on user's information !3530
- Allow Omniauth providers to be marked as `external` !3657
- Add endpoints to archive or unarchive a project !3372
- - Fix a bug whith trailing slash in bamboo_url
+ - Fix a bug with trailing slash in bamboo_url
- Add links to CI setup documentation from project settings and builds pages
- Display project members page to all members
- Handle nil descriptions in Slack issue messages (Stan Hu)
@@ -2610,7 +2610,7 @@
- Fixed logo animation on Safari (Roman Rott)
- Fix Merge When Succeeded when multiple stages
- Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg)
- - In seach autocomplete show only groups and projects you are member of
+ - In search autocomplete show only groups and projects you are member of
- Don't process cross-reference notes from forks
- Fix: init.d script not working on OS X
- Faster snippet search
@@ -2870,7 +2870,7 @@
- Bump Redis requirement to 2.8 for Sidekiq 4 (Stan Hu)
- Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
- WIP identifier on merge requests no longer requires trailing space
- - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg)
+ - Add rake tasks for git repository maintenance (Zeger-Jan van de Weg)
- Fix 500 error when update group member permission
- Fix: As an admin, cannot add oneself as a member to a group/project
- Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
@@ -3199,7 +3199,7 @@
- Sort issues by creation date in Bitbucket importer (Stan Hu)
- Prevent too many redirects upon login when home page URL is set to external_url (Stan Hu)
- Improve dropdown positioning on the project home page (Hannes Rosenögger)
- - Upgrade browser gem to 1.0.0 to avoid warning in IE11 compatibilty mode (Stan Hu)
+ - Upgrade browser gem to 1.0.0 to avoid warning in IE11 compatibility mode (Stan Hu)
- Remove user OAuth tokens from the database and request new tokens each session (Stan Hu)
- Restrict users API endpoints to use integer IDs (Stan Hu)
- Only show recent push event if the branch still exists or a recent merge request has not been created (Stan Hu)
@@ -3209,7 +3209,7 @@
- Ability to fetch merge requests from refs/merge-requests/:id
- Allow displaying of archived projects in the admin interface (Artem Sidorenko)
- Allow configuration of import sources for new projects (Artem Sidorenko)
- - Search for comments should be case insensetive
+ - Search for comments should be case insensitive
- Create cross-reference for closing references on commits pushed to non-default branches (Maël Valais)
- Ability to search milestones
- Gracefully handle SMTP user input errors (e.g. incorrect email addresses) to prevent Sidekiq retries (Stan Hu)
@@ -3477,7 +3477,7 @@
- Update Asciidoctor gem to version 1.5.2. (Jakub Jirutka)
- Fix resolving of relative links to repository files in AsciiDoc documents. (Jakub Jirutka)
- Use the user list from the target project in a merge request (Stan Hu)
-- Default extention for wiki pages is now .md instead of .markdown (Jeroen van Baarsen)
+- Default extension for wiki pages is now .md instead of .markdown (Jeroen van Baarsen)
- Add validation to wiki page creation (only [a-zA-Z0-9/_-] are allowed) (Jeroen van Baarsen)
- Fix new/empty milestones showing 100% completion value (Jonah Bishop)
- Add a note when an Issue or Merge Request's title changes
@@ -3613,7 +3613,7 @@
- Set EmailsOnPush reply-to address to committer email when enabled.
- Fix broken file browsing with a submodule that contains a relative link (Stan Hu)
- Fix persistent XSS vulnerability around profile website URLs.
-- Fix project import URL regex to prevent arbitary local repos from being imported.
+- Fix project import URL regex to prevent arbitrary local repos from being imported.
- Fix directory traversal vulnerability around uploads routes.
- Fix directory traversal vulnerability around help pages.
- Don't leak existence of project via search autocomplete.
@@ -3676,7 +3676,7 @@
- Don't show commit comment button when user is not signed in.
- Fix admin user projects lists.
- Don't leak private group existence by redirecting from namespace controller to group controller.
-- Ability to skip some items from backup (database, respositories or uploads)
+- Ability to skip some items from backup (database, repositories or uploads)
- Archive repositories in background worker.
- Import GitHub, Bitbucket or GitLab.com projects owned by authenticated user into current namespace.
- Project labels are now available over the API under the "tag_list" field (Cristian Medina)
@@ -3701,7 +3701,7 @@
## 7.9.4
-- Security: Fix project import URL regex to prevent arbitary local repos from being imported
+- Security: Fix project import URL regex to prevent arbitrary local repos from being imported
- Fixed issue where only 25 commits would load in file listings
- Fix LDAP identities after config update
@@ -4199,7 +4199,7 @@
- Only masters can rewrite/remove git tags
- Add X-Frame-Options SAMEORIGIN to Nginx config so Sidekiq admin is visible
- UI improvements
-- Case-insensetive search for issues
+- Case-insensitive search for issues
- Update to rails 4.1
- Improve performance of application for projects and groups with a lot of members
- Formally support Ruby 2.1
@@ -4358,7 +4358,7 @@
- Group avatar
- Pygments.rb replaced with highlight.js
- Improve Merge request diff store logic
-- Improve render performnace for MR show page
+- Improve render performances for MR show page
- Fixed Assembla hardcoded project name
- Jira integration documentation
- Refactored app/services
diff --git a/changelogs/unreleased/10088-move-code-differences-EE-to-CE.yml b/changelogs/unreleased/10088-move-code-differences-EE-to-CE.yml
new file mode 100644
index 00000000000..1297e9712fa
--- /dev/null
+++ b/changelogs/unreleased/10088-move-code-differences-EE-to-CE.yml
@@ -0,0 +1,5 @@
+---
+title: "Added code differnces from EE in file 'app/assets/javascripts/pages/projects/project.js' to CE"
+merge_request: 29271
+author: Michel Engelen
+type: other
diff --git a/changelogs/unreleased/10842-add-missing-environments-variable-to-the-sast-analyzer-docker-container.yml b/changelogs/unreleased/10842-add-missing-environments-variable-to-the-sast-analyzer-docker-container.yml
new file mode 100644
index 00000000000..112b472aa3b
--- /dev/null
+++ b/changelogs/unreleased/10842-add-missing-environments-variable-to-the-sast-analyzer-docker-container.yml
@@ -0,0 +1,5 @@
+---
+title: 'Fix: propagate all documented ENV vars to CI when using SAST'
+merge_request: 29564
+author:
+type: fixed
diff --git a/changelogs/unreleased/12106-sp-ce.yml b/changelogs/unreleased/12106-sp-ce.yml
new file mode 100644
index 00000000000..2d073401b45
--- /dev/null
+++ b/changelogs/unreleased/12106-sp-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Moves snowplow to CE repo
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/33064-add-labels-to-note-event-payload.yml b/changelogs/unreleased/33064-add-labels-to-note-event-payload.yml
new file mode 100644
index 00000000000..e0a6253e1ef
--- /dev/null
+++ b/changelogs/unreleased/33064-add-labels-to-note-event-payload.yml
@@ -0,0 +1,5 @@
+---
+title: Add labels to note event payload
+merge_request: 29384
+author: Sujay Patel
+type: added
diff --git a/changelogs/unreleased/37495.yml b/changelogs/unreleased/37495.yml
new file mode 100644
index 00000000000..f6d421fc45b
--- /dev/null
+++ b/changelogs/unreleased/37495.yml
@@ -0,0 +1,5 @@
+---
+title: Add documentation links for confidental and locked discussions
+merge_request: 29073
+author:
+type: changed
diff --git a/changelogs/unreleased/51636-task-list-api-pderichs.yml b/changelogs/unreleased/51636-task-list-api-pderichs.yml
new file mode 100644
index 00000000000..f18a0936ab2
--- /dev/null
+++ b/changelogs/unreleased/51636-task-list-api-pderichs.yml
@@ -0,0 +1,5 @@
+---
+title: Add task count and completed count to responses of Issue and MR
+merge_request: 28859
+author:
+type: added
diff --git a/changelogs/unreleased/55033-discussion-system-note-alignment.yml b/changelogs/unreleased/55033-discussion-system-note-alignment.yml
new file mode 100644
index 00000000000..27072ec7e12
--- /dev/null
+++ b/changelogs/unreleased/55033-discussion-system-note-alignment.yml
@@ -0,0 +1,5 @@
+---
+title: Align system note within discussion with other notes
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/55125-mr-tab-scrolling.yml b/changelogs/unreleased/55125-mr-tab-scrolling.yml
new file mode 100644
index 00000000000..e03ff6c5060
--- /dev/null
+++ b/changelogs/unreleased/55125-mr-tab-scrolling.yml
@@ -0,0 +1,5 @@
+---
+title: Update merge request tabs so they no longer scroll
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/5615-non-admins-only-archieve-ce.yml b/changelogs/unreleased/5615-non-admins-only-archieve-ce.yml
new file mode 100644
index 00000000000..ac1aa249b82
--- /dev/null
+++ b/changelogs/unreleased/5615-non-admins-only-archieve-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Add deletion protection setting column to application_settings table
+merge_request: 29268
+author:
+type: other
diff --git a/changelogs/unreleased/58297-remove-extraneous-gitaly-calls-from-md-rendering.yml b/changelogs/unreleased/58297-remove-extraneous-gitaly-calls-from-md-rendering.yml
new file mode 100644
index 00000000000..25cc973159f
--- /dev/null
+++ b/changelogs/unreleased/58297-remove-extraneous-gitaly-calls-from-md-rendering.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce Gitaly calls to improve performance when rendering suggestions
+merge_request: 29027
+author:
+type: performance
diff --git a/changelogs/unreleased/58433-email-notifications-do-not-work-properly-issue-due-date.yml b/changelogs/unreleased/58433-email-notifications-do-not-work-properly-issue-due-date.yml
new file mode 100644
index 00000000000..4579721446a
--- /dev/null
+++ b/changelogs/unreleased/58433-email-notifications-do-not-work-properly-issue-due-date.yml
@@ -0,0 +1,5 @@
+---
+title: Fix email notifications for user excluded actions
+merge_request: 28835
+author:
+type: fixed
diff --git a/changelogs/unreleased/58984-doc-missing-milestones-and-labels-links.yml b/changelogs/unreleased/58984-doc-missing-milestones-and-labels-links.yml
new file mode 100644
index 00000000000..6b74303c16e
--- /dev/null
+++ b/changelogs/unreleased/58984-doc-missing-milestones-and-labels-links.yml
@@ -0,0 +1,5 @@
+---
+title: Document when milestones and labels links are missing
+merge_request: 29355
+author:
+type: other
diff --git a/changelogs/unreleased/59376-Report-abuse-to-GitLab-should-be-Report-abuse-in-non-gitlab-com-instances.yml b/changelogs/unreleased/59376-Report-abuse-to-GitLab-should-be-Report-abuse-in-non-gitlab-com-instances.yml
new file mode 100644
index 00000000000..0904f788b6f
--- /dev/null
+++ b/changelogs/unreleased/59376-Report-abuse-to-GitLab-should-be-Report-abuse-in-non-gitlab-com-instances.yml
@@ -0,0 +1,5 @@
+---
+title: Change "Report abuse to GitLab" to more generic wording
+merge_request: 28884
+author: Marc Schwede
+type: other
diff --git a/changelogs/unreleased/59651-remove-unnecessary-decimal-places-on-chart-axes.yml b/changelogs/unreleased/59651-remove-unnecessary-decimal-places-on-chart-axes.yml
new file mode 100644
index 00000000000..fea34099135
--- /dev/null
+++ b/changelogs/unreleased/59651-remove-unnecessary-decimal-places-on-chart-axes.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unnecessary decimals on Metrics chart axis
+merge_request: 29468
+author:
+type: fixed
diff --git a/changelogs/unreleased/60303-replace-sidekiq-mtail-metrics.yml b/changelogs/unreleased/60303-replace-sidekiq-mtail-metrics.yml
new file mode 100644
index 00000000000..90b72ec05c7
--- /dev/null
+++ b/changelogs/unreleased/60303-replace-sidekiq-mtail-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Replaces sidekiq mtail metrics with ruby instrumentation metrics
+merge_request: 29215
+author:
+type: changed
diff --git a/changelogs/unreleased/61072-link-to-user-profile-not-distinguishable-on-latest-commit-widget.yml b/changelogs/unreleased/61072-link-to-user-profile-not-distinguishable-on-latest-commit-widget.yml
new file mode 100644
index 00000000000..5285ac767cc
--- /dev/null
+++ b/changelogs/unreleased/61072-link-to-user-profile-not-distinguishable-on-latest-commit-widget.yml
@@ -0,0 +1,5 @@
+---
+title: User link styling for commits
+merge_request: 29150
+author:
+type: other
diff --git a/changelogs/unreleased/61157-reviewer-roulette-shouldn-t-include-the-author-as-a-possibility.yml b/changelogs/unreleased/61157-reviewer-roulette-shouldn-t-include-the-author-as-a-possibility.yml
new file mode 100644
index 00000000000..8d1a38b3db5
--- /dev/null
+++ b/changelogs/unreleased/61157-reviewer-roulette-shouldn-t-include-the-author-as-a-possibility.yml
@@ -0,0 +1,5 @@
+---
+title: Excludes MR author from Review roulette
+merge_request: 28886
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/61246-fix-label-click-scroll-to-top.yml b/changelogs/unreleased/61246-fix-label-click-scroll-to-top.yml
deleted file mode 100644
index 7fa15e1c0fd..00000000000
--- a/changelogs/unreleased/61246-fix-label-click-scroll-to-top.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix label click scrolling to top
-merge_request: 29202
-author:
-type: fixed
diff --git a/changelogs/unreleased/61323-snippet-copy-icon-button-is-misaligned.yml b/changelogs/unreleased/61323-snippet-copy-icon-button-is-misaligned.yml
index 6f8c82c2dc8..94666ac12ec 100644
--- a/changelogs/unreleased/61323-snippet-copy-icon-button-is-misaligned.yml
+++ b/changelogs/unreleased/61323-snippet-copy-icon-button-is-misaligned.yml
@@ -1,5 +1,5 @@
---
title: Resolve Snippet icon button is misaligned
merge_request: 28522
-author: Marcel van Remmerden
+author:
type: other
diff --git a/changelogs/unreleased/61324-non-project-snippet-new-snippet-button-should-be-green-outline.yml b/changelogs/unreleased/61324-non-project-snippet-new-snippet-button-should-be-green-outline.yml
index 350fd525a30..a7f5706058d 100644
--- a/changelogs/unreleased/61324-non-project-snippet-new-snippet-button-should-be-green-outline.yml
+++ b/changelogs/unreleased/61324-non-project-snippet-new-snippet-button-should-be-green-outline.yml
@@ -1,5 +1,5 @@
---
title: Give New Snippet button green outline
merge_request: 28559
-author: Marcel van Remmerden
+author:
type: other
diff --git a/changelogs/unreleased/61339-Add-underline-to-attach-a-file.yml b/changelogs/unreleased/61339-Add-underline-to-attach-a-file.yml
index c3808709fca..e446459ffc8 100644
--- a/changelogs/unreleased/61339-Add-underline-to-attach-a-file.yml
+++ b/changelogs/unreleased/61339-Add-underline-to-attach-a-file.yml
@@ -1,5 +1,5 @@
---
title: Add hover and focus to Attach a file
merge_request: 28682
-author: Marcel van Remmerden
+author:
type: fixed
diff --git a/changelogs/unreleased/61565-merge-request-discussion-text-jumps-when-resolved.yml b/changelogs/unreleased/61565-merge-request-discussion-text-jumps-when-resolved.yml
new file mode 100644
index 00000000000..718604c9ceb
--- /dev/null
+++ b/changelogs/unreleased/61565-merge-request-discussion-text-jumps-when-resolved.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Merge request discussion text jumps when resolved
+merge_request: 28995
+author:
+type: fixed
diff --git a/changelogs/unreleased/61988-collapse-icon-on-merge-request-diff-larger-than-profile-picture.yml b/changelogs/unreleased/61988-collapse-icon-on-merge-request-diff-larger-than-profile-picture.yml
index 46d3f439a44..4d2f73ce2ff 100644
--- a/changelogs/unreleased/61988-collapse-icon-on-merge-request-diff-larger-than-profile-picture.yml
+++ b/changelogs/unreleased/61988-collapse-icon-on-merge-request-diff-larger-than-profile-picture.yml
@@ -1,5 +1,5 @@
---
-title: Change collapse icon size to size of profile picture
+title: Change collapse icon size to size of profile picture
merge_request: 28512
-author: Marcel van Remmerden
+author:
type: other
diff --git a/changelogs/unreleased/62091-remove-time-windows-flag.yml b/changelogs/unreleased/62091-remove-time-windows-flag.yml
new file mode 100644
index 00000000000..c6c11328312
--- /dev/null
+++ b/changelogs/unreleased/62091-remove-time-windows-flag.yml
@@ -0,0 +1,5 @@
+---
+title: Allow users to specify a time range on metrics dashboard
+merge_request: 28670
+author:
+type: added
diff --git a/changelogs/unreleased/62124-new-threaded-discussion-design.yml b/changelogs/unreleased/62124-new-threaded-discussion-design.yml
new file mode 100644
index 00000000000..6614e05be74
--- /dev/null
+++ b/changelogs/unreleased/62124-new-threaded-discussion-design.yml
@@ -0,0 +1,5 @@
+---
+title: Implement borderless discussion design with new reply field
+merge_request: 28580
+author:
+type: added
diff --git a/changelogs/unreleased/62134-fix-non-wraping-project-description.yml b/changelogs/unreleased/62134-fix-non-wraping-project-description.yml
new file mode 100644
index 00000000000..8c734c526fe
--- /dev/null
+++ b/changelogs/unreleased/62134-fix-non-wraping-project-description.yml
@@ -0,0 +1,5 @@
+---
+title: Correctly word-wrapping project descriptions with very long words
+merge_request: 28695
+author: Erik van der Gaag
+type: fixed
diff --git a/changelogs/unreleased/62144-fix-option-dropdown-button-size.yml b/changelogs/unreleased/62144-fix-option-dropdown-button-size.yml
new file mode 100644
index 00000000000..86d8f4536f9
--- /dev/null
+++ b/changelogs/unreleased/62144-fix-option-dropdown-button-size.yml
@@ -0,0 +1,5 @@
+---
+title: Fix inconsistent option dropdown button height to match adjacent button
+merge_request: 29096
+author:
+type: fixed
diff --git a/changelogs/unreleased/62154-fe-create-fix-long-branch-name-in-dropdown.yml b/changelogs/unreleased/62154-fe-create-fix-long-branch-name-in-dropdown.yml
new file mode 100644
index 00000000000..1d951c1dc24
--- /dev/null
+++ b/changelogs/unreleased/62154-fe-create-fix-long-branch-name-in-dropdown.yml
@@ -0,0 +1,6 @@
+---
+title: Add support to view entirety of long branch name in dropdown instead of it
+ being cut off
+merge_request: 29069
+author:
+type: fixed
diff --git a/changelogs/unreleased/62300-target-area-for-dropdown-list-items-is-too-small-on-metrics-dashboard.yml b/changelogs/unreleased/62300-target-area-for-dropdown-list-items-is-too-small-on-metrics-dashboard.yml
new file mode 100644
index 00000000000..7944e6faa27
--- /dev/null
+++ b/changelogs/unreleased/62300-target-area-for-dropdown-list-items-is-too-small-on-metrics-dashboard.yml
@@ -0,0 +1,5 @@
+---
+title: Enlarge metrics time-window dropdown links
+merge_request: 29458
+author:
+type: fixed
diff --git a/changelogs/unreleased/62418-project-default-git-depth.yml b/changelogs/unreleased/62418-project-default-git-depth.yml
new file mode 100644
index 00000000000..b5647cd0859
--- /dev/null
+++ b/changelogs/unreleased/62418-project-default-git-depth.yml
@@ -0,0 +1,5 @@
+---
+title: Add project level git depth CI/CD setting
+merge_request: 28919
+author:
+type: added
diff --git a/changelogs/unreleased/62656-adjusted-dropdown-styles.yml b/changelogs/unreleased/62656-adjusted-dropdown-styles.yml
new file mode 100644
index 00000000000..36f14ae2741
--- /dev/null
+++ b/changelogs/unreleased/62656-adjusted-dropdown-styles.yml
@@ -0,0 +1,5 @@
+---
+title: "changed the styles on `Add List` dropdown to look more like the EE vesion"
+merge_request: 29338
+author: Michel Engelen
+type: changed
diff --git a/changelogs/unreleased/62713-fix-uninstalling-cluster-apps.yml b/changelogs/unreleased/62713-fix-uninstalling-cluster-apps.yml
deleted file mode 100644
index 45fa668ae85..00000000000
--- a/changelogs/unreleased/62713-fix-uninstalling-cluster-apps.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix connection to Tiller error while uninstalling
-merge_request: 29131
-author:
-type: fixed
diff --git a/changelogs/unreleased/62788-clean-up-pagination.yml b/changelogs/unreleased/62788-clean-up-pagination.yml
new file mode 100644
index 00000000000..05e3a4527b8
--- /dev/null
+++ b/changelogs/unreleased/62788-clean-up-pagination.yml
@@ -0,0 +1,5 @@
+---
+title: Moves the table pagination shared component
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/62788-graphql-pagination.yml b/changelogs/unreleased/62788-graphql-pagination.yml
new file mode 100644
index 00000000000..a7bc317a08f
--- /dev/null
+++ b/changelogs/unreleased/62788-graphql-pagination.yml
@@ -0,0 +1,5 @@
+---
+title: Adds pagination component for graphql api
+merge_request: 29277
+author:
+type: added
diff --git a/changelogs/unreleased/62847-url-for-the-next-request-with-pagination-is-missing-port.yml b/changelogs/unreleased/62847-url-for-the-next-request-with-pagination-is-missing-port.yml
new file mode 100644
index 00000000000..fca92a0d4dc
--- /dev/null
+++ b/changelogs/unreleased/62847-url-for-the-next-request-with-pagination-is-missing-port.yml
@@ -0,0 +1,5 @@
+---
+title: Include the port in the URLs of the API Link headers
+merge_request: 29267
+author:
+type: fixed
diff --git a/changelogs/unreleased/62974-follow-up-from-wip-align-merge-request-icons-and-text.yml b/changelogs/unreleased/62974-follow-up-from-wip-align-merge-request-icons-and-text.yml
new file mode 100644
index 00000000000..811986e6857
--- /dev/null
+++ b/changelogs/unreleased/62974-follow-up-from-wip-align-merge-request-icons-and-text.yml
@@ -0,0 +1,5 @@
+---
+title: 'Make margin between buttons consistent'
+merge_request: 29378
+author:
+type: other
diff --git a/changelogs/unreleased/9186-implement-atmtwps-state-to-mr-widget.yml b/changelogs/unreleased/9186-implement-atmtwps-state-to-mr-widget.yml
new file mode 100644
index 00000000000..2c1d56f197e
--- /dev/null
+++ b/changelogs/unreleased/9186-implement-atmtwps-state-to-mr-widget.yml
@@ -0,0 +1,5 @@
+---
+title: Update the merge request widget's "Merge" button to support merge trains
+merge_request: 27594
+author:
+type: added
diff --git a/changelogs/unreleased/ac-63020-typeerror-nil-can-t-be-coerced-into-integer.yml b/changelogs/unreleased/ac-63020-typeerror-nil-can-t-be-coerced-into-integer.yml
new file mode 100644
index 00000000000..51ac2358fba
--- /dev/null
+++ b/changelogs/unreleased/ac-63020-typeerror-nil-can-t-be-coerced-into-integer.yml
@@ -0,0 +1,5 @@
+---
+title: Fix nil coercion updating storage size on project statistics
+merge_request: 29425
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-lfs-blob-ids-to-tree-type.yml b/changelogs/unreleased/add-lfs-blob-ids-to-tree-type.yml
new file mode 100644
index 00000000000..14a5ef1cef3
--- /dev/null
+++ b/changelogs/unreleased/add-lfs-blob-ids-to-tree-type.yml
@@ -0,0 +1,5 @@
+---
+title: Add LFS oid to GraphQL blob type
+merge_request: 28666
+author:
+type: added
diff --git a/changelogs/unreleased/allow-emoji-in-references.yml b/changelogs/unreleased/allow-emoji-in-references.yml
new file mode 100644
index 00000000000..3116b346c00
--- /dev/null
+++ b/changelogs/unreleased/allow-emoji-in-references.yml
@@ -0,0 +1,5 @@
+---
+title: Allow references to labels and milestones to contain emoji
+merge_request: 29284
+author:
+type: changed
diff --git a/changelogs/unreleased/cancel-auto-merge-when-branch-is-changed.yml b/changelogs/unreleased/cancel-auto-merge-when-branch-is-changed.yml
new file mode 100644
index 00000000000..c3c6e4322a2
--- /dev/null
+++ b/changelogs/unreleased/cancel-auto-merge-when-branch-is-changed.yml
@@ -0,0 +1,5 @@
+---
+title: Cancel Auto Merge when target branch is changed
+merge_request: 29416
+author:
+type: fixed
diff --git a/changelogs/unreleased/copy-button-in-modals.yml b/changelogs/unreleased/copy-button-in-modals.yml
new file mode 100644
index 00000000000..bc18eb9ab26
--- /dev/null
+++ b/changelogs/unreleased/copy-button-in-modals.yml
@@ -0,0 +1,5 @@
+---
+title: Add a New Copy Button That Works in Modals
+merge_request: 28676
+author:
+type: added
diff --git a/changelogs/unreleased/docs-add-chatops-request-doc.yml b/changelogs/unreleased/docs-add-chatops-request-doc.yml
new file mode 100644
index 00000000000..85ba86a73af
--- /dev/null
+++ b/changelogs/unreleased/docs-add-chatops-request-doc.yml
@@ -0,0 +1,5 @@
+---
+title: Add section to dev docs on accessing chatops
+merge_request: 28623
+author:
+type: other
diff --git a/changelogs/unreleased/expose-project-git-depth-via-api.yml b/changelogs/unreleased/expose-project-git-depth-via-api.yml
new file mode 100644
index 00000000000..e9d158fda16
--- /dev/null
+++ b/changelogs/unreleased/expose-project-git-depth-via-api.yml
@@ -0,0 +1,5 @@
+---
+title: Get and edit ci_default_git_depth via project API
+merge_request: 29353
+author:
+type: added
diff --git a/changelogs/unreleased/fe-fix-gl-dropdown-scrolling-to-top.yml b/changelogs/unreleased/fe-fix-gl-dropdown-scrolling-to-top.yml
new file mode 100644
index 00000000000..4125b4241e6
--- /dev/null
+++ b/changelogs/unreleased/fe-fix-gl-dropdown-scrolling-to-top.yml
@@ -0,0 +1,5 @@
+---
+title: Fix scrolling to top on assignee change
+merge_request: 29500
+author:
+type: fixed
diff --git a/changelogs/unreleased/feature-gb-use-gitlabktl-to-build-serverless-applications.yml b/changelogs/unreleased/feature-gb-use-gitlabktl-to-build-serverless-applications.yml
new file mode 100644
index 00000000000..443fff92f55
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-use-gitlabktl-to-build-serverless-applications.yml
@@ -0,0 +1,5 @@
+---
+title: Use to 'gitlabktl' build serverless applications
+merge_request: 29258
+author:
+type: added
diff --git a/changelogs/unreleased/fix-allow-lower-case-issue-ids.yml b/changelogs/unreleased/fix-allow-lower-case-issue-ids.yml
new file mode 100644
index 00000000000..46fa90ccda0
--- /dev/null
+++ b/changelogs/unreleased/fix-allow-lower-case-issue-ids.yml
@@ -0,0 +1,5 @@
+---
+title: Allow lowercase prefix for Youtrack issue ids
+merge_request: 29057
+author: Matthias Baur
+type: fixed
diff --git a/changelogs/unreleased/fix-diverged-branch-locals.yml b/changelogs/unreleased/fix-diverged-branch-locals.yml
new file mode 100644
index 00000000000..719d669fad3
--- /dev/null
+++ b/changelogs/unreleased/fix-diverged-branch-locals.yml
@@ -0,0 +1,5 @@
+---
+title: Fix diverged branch locals
+merge_request: 29508
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-gb-fix-serverless-apps-deployment-template.yml b/changelogs/unreleased/fix-gb-fix-serverless-apps-deployment-template.yml
new file mode 100644
index 00000000000..88656b7ef4c
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-fix-serverless-apps-deployment-template.yml
@@ -0,0 +1,5 @@
+---
+title: Fix serverless apps deployments by bumping 'tm' version
+merge_request: 29254
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-gb-remove-serverless-app-build-policies-from-template.yml b/changelogs/unreleased/fix-gb-remove-serverless-app-build-policies-from-template.yml
new file mode 100644
index 00000000000..f51ec273a57
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-remove-serverless-app-build-policies-from-template.yml
@@ -0,0 +1,5 @@
+---
+title: Remove build policies from serverless app template
+merge_request: 29253
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-issue-mr-badge.yml b/changelogs/unreleased/fix-issue-mr-badge.yml
new file mode 100644
index 00000000000..e777f52f173
--- /dev/null
+++ b/changelogs/unreleased/fix-issue-mr-badge.yml
@@ -0,0 +1,5 @@
+---
+title: Use grid and correct border radius for status badge
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/fix-pipeline-schedule-owner-is-nil.yml b/changelogs/unreleased/fix-pipeline-schedule-owner-is-nil.yml
new file mode 100644
index 00000000000..5c8644d2860
--- /dev/null
+++ b/changelogs/unreleased/fix-pipeline-schedule-owner-is-nil.yml
@@ -0,0 +1,5 @@
+---
+title: Fix pipeline schedules when owner is nil
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/gt-externalize-profiles-preferences.yml b/changelogs/unreleased/gt-externalize-profiles-preferences.yml
new file mode 100644
index 00000000000..1a72e92a241
--- /dev/null
+++ b/changelogs/unreleased/gt-externalize-profiles-preferences.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize profiles preferences
+merge_request: 28470
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/i18n-email-of-user-profile.yml b/changelogs/unreleased/i18n-email-of-user-profile.yml
new file mode 100644
index 00000000000..6cb718843d5
--- /dev/null
+++ b/changelogs/unreleased/i18n-email-of-user-profile.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize strings of email page in user profile
+merge_request: 28587
+author: antony liu
+type: other
diff --git a/changelogs/unreleased/ignore-artifact-attirbutes-in-project-import-export.yml b/changelogs/unreleased/ignore-artifact-attirbutes-in-project-import-export.yml
new file mode 100644
index 00000000000..536aae03f59
--- /dev/null
+++ b/changelogs/unreleased/ignore-artifact-attirbutes-in-project-import-export.yml
@@ -0,0 +1,5 @@
+---
+title: Ignore legacy artifact columns in Project Import/Export
+merge_request: 29427
+author:
+type: fixed
diff --git a/changelogs/unreleased/improve-email-text-part.yml b/changelogs/unreleased/improve-email-text-part.yml
new file mode 100644
index 00000000000..ce506cb1507
--- /dev/null
+++ b/changelogs/unreleased/improve-email-text-part.yml
@@ -0,0 +1,5 @@
+---
+title: Improve new user email markup unconsistency between text and html parts
+merge_request: 29111
+author: Haunui Saint-sevin
+type: fixed
diff --git a/changelogs/unreleased/jc-migration-for-source-project-id.yml b/changelogs/unreleased/jc-migration-for-source-project-id.yml
new file mode 100644
index 00000000000..3e2e8ebfcc5
--- /dev/null
+++ b/changelogs/unreleased/jc-migration-for-source-project-id.yml
@@ -0,0 +1,5 @@
+---
+title: Fix null source_project_id in pool_repositories
+merge_request: 29157
+author:
+type: other
diff --git a/changelogs/unreleased/osw-avoid-encoding-errors-on-merge-to-ref-service.yml b/changelogs/unreleased/osw-avoid-encoding-errors-on-merge-to-ref-service.yml
new file mode 100644
index 00000000000..176c8bf36f7
--- /dev/null
+++ b/changelogs/unreleased/osw-avoid-encoding-errors-on-merge-to-ref-service.yml
@@ -0,0 +1,5 @@
+---
+title: Handle encoding errors for MergeToRefService
+merge_request: 29440
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-fix-post-dep-migration-with-timeout.yml b/changelogs/unreleased/osw-fix-post-dep-migration-with-timeout.yml
new file mode 100644
index 00000000000..6b37bb11ad5
--- /dev/null
+++ b/changelogs/unreleased/osw-fix-post-dep-migration-with-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid DB timeouts when scheduling migrations
+merge_request: 29437
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-sync-merge-ref-upon-mergeability-check.yml b/changelogs/unreleased/osw-sync-merge-ref-upon-mergeability-check.yml
deleted file mode 100644
index 1f40089adb8..00000000000
--- a/changelogs/unreleased/osw-sync-merge-ref-upon-mergeability-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Sync merge ref upon mergeability check
-merge_request: 28513
-author:
-type: added
diff --git a/changelogs/unreleased/patch-65.yml b/changelogs/unreleased/patch-65.yml
new file mode 100644
index 00000000000..9ce628a6541
--- /dev/null
+++ b/changelogs/unreleased/patch-65.yml
@@ -0,0 +1,5 @@
+---
+title: Show tooltip on truncated commit title
+merge_request: 28865
+author: Timofey Trofimov
+type: other
diff --git a/changelogs/unreleased/patch-issue--56683.yml b/changelogs/unreleased/patch-issue--56683.yml
new file mode 100644
index 00000000000..9b82c6c1459
--- /dev/null
+++ b/changelogs/unreleased/patch-issue--56683.yml
@@ -0,0 +1,6 @@
+---
+title: Process up to 100 commit messages for references when pushing to a new default
+ branch
+merge_request: 29511
+author: Fabio Papa
+type: fixed
diff --git a/changelogs/unreleased/remove-grafana-dashboard-link-feature-flag.yml b/changelogs/unreleased/remove-grafana-dashboard-link-feature-flag.yml
new file mode 100644
index 00000000000..24b5fb50e40
--- /dev/null
+++ b/changelogs/unreleased/remove-grafana-dashboard-link-feature-flag.yml
@@ -0,0 +1,5 @@
+---
+title: Link to an external dashboard from metrics dashboard
+merge_request: 29369
+author:
+type: added
diff --git a/changelogs/unreleased/revert-git-depth-for-merge-request.yml b/changelogs/unreleased/revert-git-depth-for-merge-request.yml
deleted file mode 100644
index 3a258dff358..00000000000
--- a/changelogs/unreleased/revert-git-depth-for-merge-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove a default git depth in Pipelines for merge requests
-merge_request: 28926
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-add-backtrace-to-sql-queries.yml b/changelogs/unreleased/sh-add-backtrace-to-sql-queries.yml
new file mode 100644
index 00000000000..d4ca027d1b9
--- /dev/null
+++ b/changelogs/unreleased/sh-add-backtrace-to-sql-queries.yml
@@ -0,0 +1,5 @@
+---
+title: Add backtraces to Peek performance bar for SQL calls
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/sh-fix-fogbugz-import.yml b/changelogs/unreleased/sh-fix-fogbugz-import.yml
new file mode 100644
index 00000000000..1ac730fca24
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-fogbugz-import.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Fogbugz Importer not working
+merge_request: 29383
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-55869.yml b/changelogs/unreleased/sh-fix-issue-55869.yml
new file mode 100644
index 00000000000..7935cffc13b
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-55869.yml
@@ -0,0 +1,5 @@
+---
+title: Fix alignment of resend button in members page
+merge_request: 28202
+author:
+type: other
diff --git a/changelogs/unreleased/sh-fix-resolve-button-not-available.yml b/changelogs/unreleased/sh-fix-resolve-button-not-available.yml
new file mode 100644
index 00000000000..85a9007f570
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-resolve-button-not-available.yml
@@ -0,0 +1,5 @@
+---
+title: Fix "Resolve conflicts" button not appearing for some users
+merge_request: 29535
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-utf-8-encoding-resolve-conflicts.yml b/changelogs/unreleased/sh-fix-utf-8-encoding-resolve-conflicts.yml
new file mode 100644
index 00000000000..31039099788
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-utf-8-encoding-resolve-conflicts.yml
@@ -0,0 +1,5 @@
+---
+title: Fix UTF-8 conversion issues when resolving conflicts
+merge_request: 29453
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-project-import-visibility-error.yml b/changelogs/unreleased/sh-project-import-visibility-error.yml
deleted file mode 100644
index eb7e001c6aa..00000000000
--- a/changelogs/unreleased/sh-project-import-visibility-error.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix invalid visibility string comparison in project import
-merge_request: 28612
-author:
-type: fixed
diff --git a/changelogs/unreleased/thomas-nilsson-irfu-gitlab-ce-thomas-nilsson-irfu-master-patch-13137.yml b/changelogs/unreleased/thomas-nilsson-irfu-gitlab-ce-thomas-nilsson-irfu-master-patch-13137.yml
new file mode 100644
index 00000000000..3391fcc9537
--- /dev/null
+++ b/changelogs/unreleased/thomas-nilsson-irfu-gitlab-ce-thomas-nilsson-irfu-master-patch-13137.yml
@@ -0,0 +1,5 @@
+---
+title: Allow masking if 8 or more characters in base64.
+merge_request: 29143
+author: thomas-nilsson-irfu
+type: changed
diff --git a/changelogs/unreleased/update-gitlab-shell-9-3-0.yml b/changelogs/unreleased/update-gitlab-shell-9-3-0.yml
new file mode 100644
index 00000000000..781ff31c7d8
--- /dev/null
+++ b/changelogs/unreleased/update-gitlab-shell-9-3-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update to GitLab Shell v9.3.0
+merge_request: 29283
+author:
+type: other
diff --git a/changelogs/unreleased/upgrade-pages-to-v1-6-1.yml b/changelogs/unreleased/upgrade-pages-to-v1-6-1.yml
new file mode 100644
index 00000000000..0ab58bb50d8
--- /dev/null
+++ b/changelogs/unreleased/upgrade-pages-to-v1-6-1.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab Pages to v1.6.1
+merge_request: 29559
+author:
+type: other
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index 68f8487d377..4da683014d4 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -19,12 +19,6 @@ Gitlab::Application.configure do |config|
config.middleware.insert(1, Gitlab::Metrics::RequestsRackMiddleware)
end
-Sidekiq.configure_server do |config|
- config.on(:startup) do
- Gitlab::Metrics::SidekiqMetricsExporter.instance.start
- end
-end
-
if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
Gitlab::Cluster::LifecycleEvents.on_worker_start do
defined?(::Prometheus::Client.reinitialize_on_pid_change) && Prometheus::Client.reinitialize_on_pid_change
diff --git a/config/initializers/rack_timeout.rb b/config/initializers/rack_timeout.rb
index 5c4f2dd708c..58f46b55725 100644
--- a/config/initializers/rack_timeout.rb
+++ b/config/initializers/rack_timeout.rb
@@ -18,6 +18,6 @@ if defined?(::Puma) && !Rails.env.test?
wait_timeout: 90)
end
- observer = Gitlab::RackTimeoutObserver.new
+ observer = Gitlab::Cluster::RackTimeoutObserver.new
Rack::Timeout.register_state_change_observer(:gitlab_rack_timeout, &observer.callback)
end
diff --git a/config/puma.example.development.rb b/config/puma.example.development.rb
index 490c940077a..9df24bf74e3 100644
--- a/config/puma.example.development.rb
+++ b/config/puma.example.development.rb
@@ -42,7 +42,6 @@ bind 'unix:///home/git/gitlab.socket'
workers 2
require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"
-require_relative "/home/git/gitlab/lib/gitlab/cluster/puma_worker_killer_initializer"
on_restart do
# Signal application hooks that we're about to restart
diff --git a/config/routes.rb b/config/routes.rb
index f5957f43655..cb90a0134c4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -75,6 +75,8 @@ Rails.application.routes.draw do
resources :issues, module: :boards, only: [:index, :update]
end
+ get 'acme-challenge/' => 'acme_challenges#show'
+
# UserCallouts
resources :user_callouts, only: [:create]
diff --git a/config/routes/project.rb b/config/routes/project.rb
index d44ff62bc2a..a1e769f6ca3 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -330,7 +330,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :metrics_dashboard
get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil }
- get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#proxy'
+ get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#proxy', as: :prometheus_api
end
collection do
diff --git a/danger/commit_messages/Dangerfile b/danger/commit_messages/Dangerfile
index 048c539bcf9..bdb4343b1d6 100644
--- a/danger/commit_messages/Dangerfile
+++ b/danger/commit_messages/Dangerfile
@@ -163,10 +163,10 @@ def lint_commit(commit)
end
if emoji_checker.includes_emoji?(commit.message)
- fail_commit(
+ warn_commit(
commit,
'Avoid the use of Markdown Emoji such as `:+1:`. ' \
- 'These add no value to the commit message, ' \
+ 'These add limited value to the commit message, ' \
'and are displayed as plain text outside of GitLab'
)
diff --git a/danger/gitlab_ui_wg/Dangerfile b/danger/gitlab_ui_wg/Dangerfile
index 02d94fa5ab7..672b1deecb3 100644
--- a/danger/gitlab_ui_wg/Dangerfile
+++ b/danger/gitlab_ui_wg/Dangerfile
@@ -1,29 +1,36 @@
+FRONTEND_MAINTAINERS = %w[filipa iamphill psimyn sarahghp mishunov].freeze
+UX_MAINTAINERS = %w[tauriedavis rverissimo].freeze
+NO_REVIEWER = 'No reviewer available'.freeze
+
def mention_single_codebase_approvers
- frontend_maintainers = %w(@filipa @iamphill @psimyn @sarahghp @mishunov)
- ux_maintainers = %w(@tauriedavis @rverissimo)
+ canonical_branch_name =
+ roulette.canonical_branch_name(gitlab.mr_json['source_branch'])
+
+ random = roulette.new_random(canonical_branch_name)
+
+ frontend_maintainers = helper.new_teammates(FRONTEND_MAINTAINERS)
+ ux_maintainers = helper.new_teammates(UX_MAINTAINERS)
rows = []
- users = []
if gitlab.mr_labels.include?('frontend')
- frontend_maintainer = frontend_maintainers.sample
+ frontend_maintainer =
+ roulette.spin_for_person(frontend_maintainers, random: random)
- rows << "| ~frontend | `#{frontend_maintainer}`"
- users << frontend_maintainer
+ rows << "| ~frontend | #{frontend_maintainer&.markdown_name || NO_REVIEWER}"
end
if gitlab.mr_labels.include?('UX')
- ux_maintainers = ux_maintainers.sample
+ ux_maintainers =
+ roulette.spin_for_person(ux_maintainers, random: random)
- rows << "| ~UX | `#{ux_maintainers}`"
- users << ux_maintainers
+ rows << "| ~UX | #{ux_maintainers&.markdown_name || NO_REVIEWER}"
end
if rows.empty?
backup_maintainer = frontend_maintainers.sample
- rows << "| ~frontend / ~UX | `#{backup_maintainer}`"
- users << backup_maintainer
+ rows << "| ~frontend / ~UX | #{backup_maintainer.markdown_name}"
end
markdown(<<~MARKDOWN.strip)
diff --git a/danger/roulette/Dangerfile b/danger/roulette/Dangerfile
index de314c5b39f..6718e218233 100644
--- a/danger/roulette/Dangerfile
+++ b/danger/roulette/Dangerfile
@@ -31,12 +31,19 @@ Please consider creating a merge request to
for them.
MARKDOWN
+NO_REVIEWER = 'No reviewer available'.freeze
+NO_MAINTAINER = 'No maintainer available'.freeze
+
def spin_for_category(team, project, category, branch_name)
random = roulette.new_random(branch_name)
+ labels = gitlab.mr_labels
- reviewers = team.select { |member| member.reviewer?(project, category) }
- traintainers = team.select { |member| member.traintainer?(project, category) }
- maintainers = team.select { |member| member.maintainer?(project, category) }
+ reviewers, traintainers, maintainers =
+ %i[reviewer? traintainer? maintainer?].map do |kind|
+ team.select do |member|
+ member.public_send(kind, project, category, labels) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
# TODO: take CODEOWNERS into account?
# https://gitlab.com/gitlab-org/gitlab-ce/issues/57653
@@ -45,7 +52,7 @@ def spin_for_category(team, project, category, branch_name)
reviewer = roulette.spin_for_person(reviewers + traintainers + traintainers, random: random)
maintainer = roulette.spin_for_person(maintainers, random: random)
- "| #{helper.label_for_category(category)} | #{reviewer&.markdown_name} | #{maintainer&.markdown_name} |"
+ "| #{helper.label_for_category(category)} | #{reviewer&.markdown_name || NO_REVIEWER} | #{maintainer&.markdown_name || NO_MAINTAINER} |"
end
def build_list(items)
@@ -81,9 +88,6 @@ if changes.any? && !gitlab.mr_labels.include?('single codebase') && !gitlab.mr_l
[]
end
- # Exclude the MR author from the team for selection purposes
- team.delete_if { |teammate| teammate.username == gitlab.mr_author }
-
project = helper.project_name
unknown = changes.fetch(:unknown, [])
diff --git a/danger/single_codebase/Dangerfile b/danger/single_codebase/Dangerfile
index d1f538bec7f..f371a42e9b1 100644
--- a/danger/single_codebase/Dangerfile
+++ b/danger/single_codebase/Dangerfile
@@ -1,6 +1,6 @@
-def new_teammates(usernames)
- usernames.map { |u| ::Gitlab::Danger::Teammate.new('username' => u) }
-end
+FRONTEND_MAINTAINERS = %w[filipa iamphill].freeze
+BACKEND_MAINTAINERS = %w[rspeicher rymai yorickpeterse godfat].freeze
+NO_REVIEWER = 'No reviewer available'.freeze
def mention_single_codebase_approvers
canonical_branch_name =
@@ -8,8 +8,8 @@ def mention_single_codebase_approvers
random = roulette.new_random(canonical_branch_name)
- frontend_maintainers = new_teammates(%w[filipa iamphill])
- backend_maintainers = new_teammates(%w[rspeicher rymai yorickpeterse godfat])
+ frontend_maintainers = helper.new_teammates(FRONTEND_MAINTAINERS)
+ backend_maintainers = helper.new_teammates(BACKEND_MAINTAINERS)
rows = []
@@ -17,14 +17,14 @@ def mention_single_codebase_approvers
frontend_maintainer =
roulette.spin_for_person(frontend_maintainers, random: random)
- rows << "| ~frontend | #{frontend_maintainer.markdown_name}"
+ rows << "| ~frontend | #{frontend_maintainer&.markdown_name || NO_REVIEWER}"
end
if gitlab.mr_labels.include?('backend')
backend_maintainer =
roulette.spin_for_person(backend_maintainers, random: random)
- rows << "| ~backend | #{backend_maintainer.markdown_name}"
+ rows << "| ~backend | #{backend_maintainer&.markdown_name || NO_REVIEWER}"
end
if rows.empty?
diff --git a/db/migrate/20190429082448_create_pages_domain_acme_orders.rb b/db/migrate/20190429082448_create_pages_domain_acme_orders.rb
new file mode 100644
index 00000000000..af811e83518
--- /dev/null
+++ b/db/migrate/20190429082448_create_pages_domain_acme_orders.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class CreatePagesDomainAcmeOrders < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def change
+ create_table :pages_domain_acme_orders do |t|
+ t.references :pages_domain, null: false, index: true, foreign_key: { on_delete: :cascade }, type: :integer
+
+ t.datetime_with_timezone :expires_at, null: false
+ t.timestamps_with_timezone null: false
+
+ t.string :url, null: false
+
+ t.string :challenge_token, null: false, index: true
+ t.text :challenge_file_content, null: false
+
+ t.text :encrypted_private_key, null: false
+ t.text :encrypted_private_key_iv, null: false
+ end
+ end
+end
diff --git a/db/migrate/20190524071727_add_ssl_valid_period_to_pages_domain.rb b/db/migrate/20190524071727_add_ssl_valid_period_to_pages_domain.rb
new file mode 100644
index 00000000000..18544dcb6d3
--- /dev/null
+++ b/db/migrate/20190524071727_add_ssl_valid_period_to_pages_domain.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddSslValidPeriodToPagesDomain < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def change
+ add_column :pages_domains, :certificate_valid_not_before, :datetime_with_timezone
+ add_column :pages_domains, :certificate_valid_not_after, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20190530042141_add_default_git_depth_to_ci_cd_settings.rb b/db/migrate/20190530042141_add_default_git_depth_to_ci_cd_settings.rb
new file mode 100644
index 00000000000..8abea05def4
--- /dev/null
+++ b/db/migrate/20190530042141_add_default_git_depth_to_ci_cd_settings.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddDefaultGitDepthToCiCdSettings < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :project_ci_cd_settings, :default_git_depth, :integer
+ end
+end
diff --git a/db/migrate/20190604184643_fix_pool_repository_source_project_id.rb b/db/migrate/20190604184643_fix_pool_repository_source_project_id.rb
new file mode 100644
index 00000000000..30244760e6b
--- /dev/null
+++ b/db/migrate/20190604184643_fix_pool_repository_source_project_id.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class FixPoolRepositorySourceProjectId < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def up
+ execute "UPDATE pool_repositories SET source_project_id = (SELECT MIN(id) FROM projects WHERE pool_repository_id = pool_repositories.id) WHERE pool_repositories.source_project_id IS NULL"
+ end
+
+ def down
+ # nothing to do her
+ end
+end
diff --git a/db/migrate/20190605104727_add_default_project_deletion_protection_to_application_settings.rb b/db/migrate/20190605104727_add_default_project_deletion_protection_to_application_settings.rb
new file mode 100644
index 00000000000..ee04b49813b
--- /dev/null
+++ b/db/migrate/20190605104727_add_default_project_deletion_protection_to_application_settings.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddDefaultProjectDeletionProtectionToApplicationSettings < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :application_settings, :default_project_deletion_protection, :boolean, default: false, allow_null: false
+ end
+
+ def down
+ remove_column :application_settings, :default_project_deletion_protection
+ end
+end
diff --git a/db/migrate/20190606014128_add_last_ci_minutes_notification_at_to_namespaces.rb b/db/migrate/20190606014128_add_last_ci_minutes_notification_at_to_namespaces.rb
new file mode 100644
index 00000000000..c264a23cda0
--- /dev/null
+++ b/db/migrate/20190606014128_add_last_ci_minutes_notification_at_to_namespaces.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddLastCiMinutesNotificationAtToNamespaces < ActiveRecord::Migration[5.1]
+ DOWNTIME = false
+
+ def change
+ add_column :namespaces, :last_ci_minutes_notification_at, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20190611161641_add_target_project_id_to_merge_trains.rb b/db/migrate/20190611161641_add_target_project_id_to_merge_trains.rb
new file mode 100644
index 00000000000..c200208e4c3
--- /dev/null
+++ b/db/migrate/20190611161641_add_target_project_id_to_merge_trains.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class AddTargetProjectIdToMergeTrains < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ # rubocop: disable Rails/NotNullColumn
+ add_reference :merge_trains, :target_project, null: false, index: true, foreign_key: { on_delete: :cascade, to_table: :projects }, type: :integer
+ add_column :merge_trains, :target_branch, :text, null: false
+ # rubocop: enable Rails/NotNullColumn
+ end
+end
diff --git a/db/post_migrate/20190524073827_schedule_fill_valid_time_for_pages_domain_certificates.rb b/db/post_migrate/20190524073827_schedule_fill_valid_time_for_pages_domain_certificates.rb
new file mode 100644
index 00000000000..1d8510e4514
--- /dev/null
+++ b/db/post_migrate/20190524073827_schedule_fill_valid_time_for_pages_domain_certificates.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class ScheduleFillValidTimeForPagesDomainCertificates < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ MIGRATION = 'FillValidTimeForPagesDomainCertificate'
+ BATCH_SIZE = 500
+ BATCH_TIME = 5.minutes
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ class PagesDomain < ActiveRecord::Base
+ include ::EachBatch
+
+ self.table_name = 'pages_domains'
+ end
+
+ def up
+ queue_background_migration_jobs_by_range_at_intervals(
+ PagesDomain.where.not(certificate: [nil, '']),
+ MIGRATION,
+ BATCH_TIME,
+ batch_size: BATCH_SIZE)
+ end
+
+ def down
+ end
+end
diff --git a/db/post_migrate/20190528180441_enqueue_reset_merge_status.rb b/db/post_migrate/20190528180441_enqueue_reset_merge_status.rb
index 1b668d85bac..a3d2f497806 100644
--- a/db/post_migrate/20190528180441_enqueue_reset_merge_status.rb
+++ b/db/post_migrate/20190528180441_enqueue_reset_merge_status.rb
@@ -16,15 +16,10 @@ class EnqueueResetMergeStatus < ActiveRecord::Migration[5.1]
def up
say 'Scheduling `ResetMergeStatus` jobs'
- # We currently have around 135_000 opened, mergeable MRs in GitLab.com. This iteration
- # will schedule around 13 batches of 10_000 MRs, which should take around 1 hour to
- # complete.
- relation = MergeRequest.where(state: 'opened', merge_status: 'can_be_merged')
-
- relation.each_batch(of: BATCH_SIZE) do |batch, index|
- range = batch.pluck('MIN(id)', 'MAX(id)').first
-
- BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, range)
- end
+ # We currently have more than ~5_000_000 merge request records on GitLab.com.
+ # This means it'll schedule ~500 jobs (10k MRs each) with a 5 minutes gap,
+ # so this should take ~41 hours for all background migrations to complete.
+ # ((5_000_000 / 10_000) * 5) / 60 => 41.6666..
+ queue_background_migration_jobs_by_range_at_intervals(MergeRequest, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
end
end
diff --git a/db/schema.rb b/db/schema.rb
index fcf9e397ac1..392edf89430 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20190530154715) do
+ActiveRecord::Schema.define(version: 20190611161641) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -194,6 +194,7 @@ ActiveRecord::Schema.define(version: 20190530154715) do
t.text "encrypted_lets_encrypt_private_key"
t.text "encrypted_lets_encrypt_private_key_iv"
t.boolean "dns_rebinding_protection_enabled", default: true, null: false
+ t.boolean "default_project_deletion_protection", default: false, null: false
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree
end
@@ -1387,8 +1388,11 @@ ActiveRecord::Schema.define(version: 20190530154715) do
t.integer "pipeline_id"
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
+ t.integer "target_project_id", null: false
+ t.text "target_branch", null: false
t.index ["merge_request_id"], name: "index_merge_trains_on_merge_request_id", unique: true, using: :btree
t.index ["pipeline_id"], name: "index_merge_trains_on_pipeline_id", using: :btree
+ t.index ["target_project_id"], name: "index_merge_trains_on_target_project_id", using: :btree
t.index ["user_id"], name: "index_merge_trains_on_user_id", using: :btree
end
@@ -1436,6 +1440,7 @@ ActiveRecord::Schema.define(version: 20190530154715) do
t.string "runners_token_encrypted"
t.integer "project_creation_level"
t.boolean "auto_devops_enabled"
+ t.datetime_with_timezone "last_ci_minutes_notification_at"
t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree
t.index ["name", "parent_id"], name: "index_namespaces_on_name_and_parent_id", unique: true, using: :btree
t.index ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
@@ -1571,6 +1576,20 @@ ActiveRecord::Schema.define(version: 20190530154715) do
t.index ["access_grant_id"], name: "index_oauth_openid_requests_on_access_grant_id", using: :btree
end
+ create_table "pages_domain_acme_orders", force: :cascade do |t|
+ t.integer "pages_domain_id", null: false
+ t.datetime_with_timezone "expires_at", null: false
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
+ t.string "url", null: false
+ t.string "challenge_token", null: false
+ t.text "challenge_file_content", null: false
+ t.text "encrypted_private_key", null: false
+ t.text "encrypted_private_key_iv", null: false
+ t.index ["challenge_token"], name: "index_pages_domain_acme_orders_on_challenge_token", using: :btree
+ t.index ["pages_domain_id"], name: "index_pages_domain_acme_orders_on_pages_domain_id", using: :btree
+ end
+
create_table "pages_domains", id: :serial, force: :cascade do |t|
t.integer "project_id"
t.text "certificate"
@@ -1583,6 +1602,8 @@ ActiveRecord::Schema.define(version: 20190530154715) do
t.datetime_with_timezone "enabled_until"
t.datetime_with_timezone "remove_at"
t.boolean "auto_ssl_enabled", default: false, null: false
+ t.datetime_with_timezone "certificate_valid_not_before"
+ t.datetime_with_timezone "certificate_valid_not_after"
t.index ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree
t.index ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until", using: :btree
t.index ["project_id"], name: "index_pages_domains_on_project_id", using: :btree
@@ -1644,6 +1665,7 @@ ActiveRecord::Schema.define(version: 20190530154715) do
t.boolean "group_runners_enabled", default: true, null: false
t.boolean "merge_pipelines_enabled"
t.boolean "merge_trains_enabled", default: false, null: false
+ t.integer "default_git_depth"
t.index ["project_id"], name: "index_project_ci_cd_settings_on_project_id", unique: true, using: :btree
end
@@ -2553,6 +2575,7 @@ ActiveRecord::Schema.define(version: 20190530154715) do
add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade
add_foreign_key "merge_trains", "ci_pipelines", column: "pipeline_id", on_delete: :nullify
add_foreign_key "merge_trains", "merge_requests", on_delete: :cascade
+ add_foreign_key "merge_trains", "projects", column: "target_project_id", on_delete: :cascade
add_foreign_key "merge_trains", "users", on_delete: :cascade
add_foreign_key "milestones", "namespaces", column: "group_id", name: "fk_95650a40d4", on_delete: :cascade
add_foreign_key "milestones", "projects", name: "fk_9bd0a0c791", on_delete: :cascade
@@ -2560,6 +2583,7 @@ ActiveRecord::Schema.define(version: 20190530154715) do
add_foreign_key "notes", "projects", name: "fk_99e097b079", on_delete: :cascade
add_foreign_key "notification_settings", "users", name: "fk_0c95e91db7", on_delete: :cascade
add_foreign_key "oauth_openid_requests", "oauth_access_grants", column: "access_grant_id", name: "fk_oauth_openid_requests_oauth_access_grants_access_grant_id"
+ add_foreign_key "pages_domain_acme_orders", "pages_domains", on_delete: :cascade
add_foreign_key "pages_domains", "projects", name: "fk_ea2f6dfc6f", on_delete: :cascade
add_foreign_key "personal_access_tokens", "users"
add_foreign_key "pool_repositories", "projects", column: "source_project_id", on_delete: :nullify
diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md
index e55f7dbb4df..00422ec347c 100644
--- a/doc/administration/auth/oidc.md
+++ b/doc/administration/auth/oidc.md
@@ -31,6 +31,7 @@ The OpenID Connect will provide you with a client details and secret for you to
{ 'name' => 'openid_connect',
'label' => '<your_oidc_label>',
'args' => {
+ "name' => 'openid_connect',
'scope' => ['openid','profile'],
'response_type' => 'code',
'issuer' => '<your_oidc_url>',
@@ -53,6 +54,7 @@ The OpenID Connect will provide you with a client details and secret for you to
- { name: 'openid_connect',
label: '<your_oidc_label>',
args: {
+ name: 'openid_connect',
scope: ['openid','profile'],
response_type: 'code',
issuer: '<your_oidc_url>',
@@ -103,3 +105,59 @@ On the sign in page, there should now be an OpenID Connect icon below the regula
Click the icon to begin the authentication process. The OpenID Connect provider will ask the user to
sign in and authorize the GitLab application (if confirmation required by the client). If everything goes well, the user
will be redirected to GitLab and will be signed in.
+
+## Example configurations
+
+The following configurations illustrate how to set up OpenID with
+different providers with Omnibus GitLab.
+
+### Google
+
+See the [Google
+documentation](https://developers.google.com/identity/protocols/OpenIDConnect)
+for more details:
+
+```ruby
+ gitlab_rails['omniauth_providers'] = [
+ {
+ 'name' => 'openid_connect',
+ 'label' => 'Google OpenID',
+ 'args' => {
+ 'name' => 'openid_connect',
+ 'scope' => ['openid', 'profile', 'email'],
+ 'response_type' => 'code',
+ 'issuer' => 'https://accounts.google.com',
+ 'client_auth_method' => 'query',
+ 'discovery' => true,
+ 'uid_field' => 'preferred_username',
+ 'client_options' => {
+ 'identifier' => '<YOUR PROJECT CLIENT ID>',
+ 'secret' => '<YOUR PROJECT CLIENT SECRET>',
+ 'redirect_uri' => 'https://example.com/users/auth/openid_connect/callback',
+ }
+ }
+ }
+```
+
+### Troubleshooting
+
+If you're having trouble, here are some tips:
+
+1. Ensure `discovery` is set to `true`. Setting it to `false` requires
+specifying all the URLs and keys required to make OpenID work.
+
+1. Check your system clock to ensure the time is synchronized properly.
+
+1. As mentioned in [the
+documentation](https://github.com/m0n9oose/omniauth_openid_connect),
+make sure `issuer` corresponds to the base URL of the Discovery URL. For
+example, `https://accounts.google.com` is used for the URL
+`https://accounts.google.com/.well-known/openid-configuration`.
+
+1. The OpenID Connect client uses HTTP Basic Authentication to send the
+OAuth2 access token. For example, if you are seeing 401 errors upon
+retrieving the `userinfo` endpoint, you may want to check your OpenID
+Web server configuration. For example, for
+[oauth2-server-php](https://github.com/bshaffer/oauth2-server-php), you
+may need to [add a configuration parameter to
+Apache](https://github.com/bshaffer/oauth2-server-php/issues/926#issuecomment-387502778).
diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md
index 002deeaf945..d9c80b1ec59 100644
--- a/doc/administration/high_availability/README.md
+++ b/doc/administration/high_availability/README.md
@@ -26,8 +26,7 @@ experience with GitLab.com and Enterprise Edition on-premises customers.
For a detailed insight into how GitLab scales and configures GitLab.com, you can
watch [this 1 hour Q&A](https://www.youtube.com/watch?v=uCU8jdYzpac)
-with [John Northrup](https://gitlab.com/northrup), one of our infrastructure
-engineers, and live questions coming in from some of our customers.
+with [John Northrup](https://gitlab.com/northrup), and live questions coming in from some of our customers.
## GitLab Components
diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md
index 490c2699458..3013ce63bd5 100644
--- a/doc/administration/high_availability/nfs.md
+++ b/doc/administration/high_availability/nfs.md
@@ -37,6 +37,14 @@ options:
circumstances it could lead to data loss if a failure occurs before data has
synced.
+Due to the complexities of running Omnibus with LDAP and the complexities of
+maintaining ID mapping without LDAP, in most cases you should enable numeric UIDs
+and GIDs (which is off by default in some cases) for simplified permission
+management between systems:
+
+ - [NetApp instructions](https://library.netapp.com/ecmdocs/ECMP1401220/html/GUID-24367A9F-E17B-4725-ADC1-02D86F56F78E.html)
+ - For non-NetApp devices, disable NFSv4 `idmapping` by performing opposite of [enable NFSv4 idmapper](https://wiki.archlinux.org/index.php/NFS#Enabling_NFSv4_idmapping)
+
### Improving NFS performance with GitLab
NOTE: **Note:** This is only available starting in certain versions of GitLab: 11.5.11,
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 95a0e84deb6..06d900b152d 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -36,7 +36,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Omnibus support for external MySQL DB](https://docs.gitlab.com/omnibus/settings/database.html#using-a-mysql-database-management-server-enterprise-edition-only): Omnibus package supports configuring an external MySQL database. **[STARTER ONLY]**
- [Omnibus support for log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only) **[STARTER ONLY]**
- [High Availability](high_availability/README.md): Configure multiple servers for scaling or high availability.
- - [High Availability on AWS](../university/high-availability/aws/README.md): Set up GitLab HA on Amazon AWS.
+ - [Installing GitLab HA on Amazon Web Services (AWS)](../install/aws/index.md): Set up GitLab High Availability on Amazon AWS.
- [Geo](geo/replication/index.md): Replicate your GitLab instance to other geographic locations as a read-only fully operational version. **[PREMIUM ONLY]**
- [Disaster Recovery](geo/disaster_recovery/index.md): Quickly fail-over to a different site with minimal effort in a disaster situation. **[PREMIUM ONLY]**
- [Pivotal Tile](../install/pivotal/index.md): Deploy GitLab as a pre-configured appliance using Ops Manager (BOSH) for Pivotal Cloud Foundry. **[PREMIUM ONLY]**
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index ac41f9177dd..c5cfb8d5016 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -129,7 +129,7 @@ It contains information about [integrations](../user/project/integrations/projec
``` json
{"severity":"ERROR","time":"2018-09-06T14:56:20.439Z","service_class":"JiraService","project_id":8,"project_path":"h5bp/html5-boilerplate","message":"Error sending message","client_url":"http://jira.gitlap.com:8080","error":"execution expired"}
-{"severity":"INFO","time":"2018-09-06T17:15:16.365Z","service_class":"JiraService","project_id":3,"project_path":"namespace2/project2","message":"Successfully posted","client_url":"http://jira.example.net"}
+{"severity":"INFO","time":"2018-09-06T17:15:16.365Z","service_class":"JiraService","project_id":3,"project_path":"namespace2/project2","message":"Successfully posted","client_url":"http://jira.example.com"}
```
## `kubernetes.log`
diff --git a/doc/administration/monitoring/performance/index.md b/doc/administration/monitoring/performance/index.md
index f5f0363ed38..ef71ca1d6c3 100644
--- a/doc/administration/monitoring/performance/index.md
+++ b/doc/administration/monitoring/performance/index.md
@@ -14,7 +14,7 @@ documents in order to understand and properly configure GitLab Performance Monit
- [Performance bar](performance_bar.md)
- [Request profiling](request_profiling.md)
->**Note:**
+NOTE: **Note:**
Omnibus GitLab 8.16 includes Prometheus as an additional tool to collect
metrics. It will eventually replace InfluxDB when their metrics collection is
on par. Read more in the [Prometheus documentation](../prometheus/index.md).
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 3dcd1593099..84b71ae6f1c 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -125,6 +125,7 @@ When Puma is used instead of Unicorn, following metrics are available:
| puma_max_threads | Gauge | 12.0 | Maximum number of worker threads |
| puma_idle_threads | Gauge | 12.0 | Number of spawned threads which are not processing a request |
| rack_state_total | Gauge | 12.0 | Number of requests in a given rack state |
+| puma_killer_terminations_total | Gauge | 12.0 | Number of workers terminated by PumaWorkerKiller |
## Metrics shared directory
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 4db3cbb9958..38842693d73 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -106,6 +106,11 @@ enabled for individual projects by executing
be on hashed storage, should not be a fork itself, and hashed storage should be
enabled for all new projects.
+DANGER: **Danger:**
+Do not run `git prune` or `git gc` in pool repositories! This can
+cause data loss in "real" repositories that depend on the pool in
+question.
+
### How to migrate to Hashed Storage
To start a migration, enable Hashed Storage for new projects:
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 4fb3626f637..0d96cfa1b21 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -135,7 +135,11 @@ Example response:
"award_emoji":"http://example.com/api/v4/projects/1/issues/76/award_emoji",
"project":"http://example.com/api/v4/projects/1"
},
- "subscribed": false
+ "subscribed": false,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
]
```
@@ -265,7 +269,11 @@ Example response:
"award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji",
"project":"http://example.com/api/v4/projects/4"
},
- "subscribed": false
+ "subscribed": false,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
]
```
@@ -403,7 +411,11 @@ Example response:
"award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji",
"project":"http://example.com/api/v4/projects/4"
},
- "subscribed": false
+ "subscribed": false,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
]
```
@@ -500,6 +512,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1"
+ },
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
}
}
```
@@ -583,6 +599,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1"
+ },
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
}
}
```
@@ -674,6 +694,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1"
+ },
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
}
}
```
@@ -780,6 +804,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1"
+ },
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
}
}
```
@@ -865,6 +893,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1"
+ },
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
}
}
```
@@ -931,7 +963,11 @@ Example response:
"due_date": null,
"web_url": "http://example.com/example/example/issues/12",
"confidential": false,
- "discussion_locked": false
+ "discussion_locked": false,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
```
@@ -1029,7 +1065,11 @@ Example response:
"due_date": null,
"web_url": "http://example.com/example/example/issues/110",
"confidential": false,
- "discussion_locked": false
+ "discussion_locked": false,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
},
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/issues/10",
"body": "Vel voluptas atque dicta mollitia adipisci qui at.",
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 9529a9ec1f5..dd7810c3403 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -138,7 +138,11 @@ Parameters:
"human_time_estimate": null,
"human_total_time_spent": null
},
- "squash": false
+ "squash": false,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
]
```
@@ -280,7 +284,11 @@ Parameters:
"human_time_estimate": null,
"human_total_time_spent": null
},
- "squash": false
+ "squash": false,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
]
```
@@ -410,7 +418,11 @@ Parameters:
"human_time_estimate": null,
"human_total_time_spent": null
},
- "squash": false
+ "squash": false,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
]
```
@@ -545,7 +557,11 @@ Parameters:
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
},
"diverged_commits_count": 2,
- "rebase_in_progress": false
+ "rebase_in_progress": false,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
```
@@ -579,7 +595,7 @@ Parameters:
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon",
"web_url": "http://localhost/user2"
- },
+ }
]
```
@@ -702,7 +718,11 @@ Parameters:
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
- }
+ },
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ },
"changes": [
{
"old_path": "VERSION",
@@ -865,7 +885,11 @@ POST /projects/:id/merge_requests
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
},
- "diverged_commits_count": 2
+ "diverged_commits_count": 2,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
```
@@ -1002,7 +1026,11 @@ Must include at least one non-required attribute from above.
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
},
- "diverged_commits_count": 2
+ "diverged_commits_count": 2,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
```
@@ -1155,33 +1183,41 @@ Parameters:
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
},
- "diverged_commits_count": 2
+ "diverged_commits_count": 2,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
```
-## Returns the up to date merge-ref HEAD commit
+## Merge to default merge ref path
Merge the changes between the merge request source and target branches into `refs/merge-requests/:iid/merge`
-ref, of the target project repository, if possible. This ref will have the state the target branch would have if
+ref, of the target project repository. This ref will have the state the target branch would have if
a regular merge action was taken.
-This is not a regular merge action given it doesn't change the merge request target branch state in any manner.
+This is not a regular merge action given it doesn't change the merge request state in any manner.
-This ref (`refs/merge-requests/:iid/merge`) isn't necessarily overwritten when submitting
-requests to this API, though it'll make sure the ref has the latest possible state.
+This ref (`refs/merge-requests/:iid/merge`) is **always** overwritten when submitting
+requests to this API, so none of its state is kept or used in the process.
-If the merge request has conflicts, is empty or already merged, you'll get a `400` and a descriptive error message.
+If the merge request has conflicts, is empty or already merged,
+you'll get a `400` and a descriptive error message. If you don't have permissions to do so,
+you'll get a `403`.
-It returns the HEAD commit of `refs/merge-requests/:iid/merge` in the response body in case of `200`.
+It returns the HEAD commit of `refs/merge-requests/:iid/merge` in the response body in
+case of `200`.
```
-GET /projects/:id/merge_requests/:merge_request_iid/merge_ref
+PUT /projects/:id/merge_requests/:merge_request_iid/merge_to_ref
```
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - Internal ID of MR
+- `merge_commit_message` (optional) - Custom merge commit message
```json
{
@@ -1309,7 +1345,11 @@ Parameters:
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
},
- "diverged_commits_count": 2
+ "diverged_commits_count": 2,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
```
@@ -1345,7 +1385,7 @@ If the rebase operation is ongoing, the response will include the following:
```json
{
- "rebase_in_progress": true
+ "rebase_in_progress": true,
"merge_error": null
}
```
@@ -1356,7 +1396,7 @@ the following:
```json
{
"rebase_in_progress": false,
- "merge_error": null,
+ "merge_error": null
}
```
@@ -1365,7 +1405,7 @@ If the rebase operation fails, the response will include the following:
```json
{
"rebase_in_progress": false,
- "merge_error": "Rebase failed. Please rebase locally",
+ "merge_error": "Rebase failed. Please rebase locally"
}
```
@@ -1572,7 +1612,11 @@ Example response:
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
},
- "diverged_commits_count": 2
+ "diverged_commits_count": 2,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
```
@@ -1701,7 +1745,11 @@ Example response:
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
},
- "diverged_commits_count": 2
+ "diverged_commits_count": 2,
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ }
}
```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 75669d85803..1d58e390d9e 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -143,6 +143,7 @@ When the user is authenticated and `simple` is not set this returns something li
"forks_count": 0,
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
+ "ci_default_git_depth": 50,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -225,6 +226,7 @@ When the user is authenticated and `simple` is not set this returns something li
"forks_count": 0,
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
+ "ci_default_git_depth": 0,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -334,6 +336,7 @@ GET /users/:user_id/projects
"forks_count": 0,
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
+ "ci_default_git_depth": 50,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -416,6 +419,7 @@ GET /users/:user_id/projects
"forks_count": 0,
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
+ "ci_default_git_depth": 0,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -528,6 +532,7 @@ GET /projects/:id
"forks_count": 0,
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
+ "ci_default_git_depth": 50,
"public_jobs": true,
"shared_with_groups": [
{
@@ -763,6 +768,7 @@ PUT /projects/:id
| `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project |
| `avatar` | mixed | no | Image file for avatar of the project |
| `ci_config_path` | string | no | The path to CI config file |
+| `ci_default_git_depth` | integer | no | Default number of revisions for [shallow cloning](../user/project/pipelines/settings.md#git-shallow-clone) |
## Fork project
@@ -1160,6 +1166,7 @@ Example response:
"forks_count": 0,
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
+ "ci_default_git_depth": 50,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
@@ -1264,6 +1271,7 @@ Example response:
"forks_count": 0,
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
+ "ci_default_git_depth": 50,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 93b82a065b3..1743c38eb46 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -32,6 +32,9 @@ your app.
For a complete overview of these methodologies and GitLab CI/CD,
read the [Introduction to CI/CD with GitLab](introduction/index.md).
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For a video demonstration of GitLab CI/CD, see [Demo: CI/CD with GitLab](https://www.youtube.com/watch?v=1iXFbchozdY).
+
## Getting started
GitLab CI/CD is configured by a file called `.gitlab-ci.yml` placed
@@ -60,7 +63,7 @@ Once you're familiar with how GitLab CI/CD works, see the
for all the attributes you can set and use.
NOTE: **Note:**
-GitLab CI/CD and [shared runners](runners/README.md#shared-specific-and-group-runners) are enabled in GitLab.com and available for all users, limited only to the [user's pipelines quota](https://docs.gitlab.com/ee/user/admin_area/settings/continuous_integration.html#extra-shared-runners-pipeline-minutes-quota).
+GitLab CI/CD and [shared runners](runners/README.md#shared-specific-and-group-runners) are enabled in GitLab.com and available for all users, limited only to the [user's pipelines quota](../user/admin_area/settings/continuous_integration.md#extra-shared-runners-pipeline-minutes-quota).
## Configuration
@@ -98,10 +101,10 @@ Its feature set is listed on the table below according to DevOps stages.
| [ChatOps](chatops/README.md) | Trigger CI jobs from chat, with results sent back to the channel. |
|---+---|
| **Verify** ||
-| [Browser Performance Testing](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html) | Quickly determine the performance impact of pending code changes. |
+| [Browser Performance Testing](../user/project/merge_requests/browser_performance_testing.md) | Quickly determine the performance impact of pending code changes. |
| [CI services](services/README.md) | Link Docker containers with your base image.|
-| [Code Quality](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) **[STARTER]** | Analyze your source code quality. |
-| [GitLab CI/CD for external repositories](https://docs.gitlab.com/ee/ci/ci_cd_for_external_repos/) **[PREMIUM]** | Get the benefits of GitLab CI/CD combined with repositories in GitHub and BitBucket Cloud. |
+| [Code Quality](../user/project/merge_requests/code_quality.md) **[STARTER]** | Analyze your source code quality. |
+| [GitLab CI/CD for external repositories](ci_cd_for_external_repos/index.md) **[PREMIUM]** | Get the benefits of GitLab CI/CD combined with repositories in GitHub and BitBucket Cloud. |
| [Interactive Web Terminals](interactive_web_terminal/index.md) **[CORE ONLY]** | Open an interactive web terminal to debug the running jobs. |
| [JUnit tests](junit_test_reports.md) | Identify script failures directly on merge requests. |
| [Using Docker images](docker/using_docker_images.md) | Use GitLab and GitLab Runner with Docker to build and test applications. |
@@ -109,27 +112,25 @@ Its feature set is listed on the table below according to DevOps stages.
| **Release** ||
| [Auto Deploy](../topics/autodevops/index.md#auto-deploy) | Deploy your application to a production environment in a Kubernetes cluster. |
| [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
-| [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html) **[PREMIUM]** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
-| [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html) **[PREMIUM]** | Check the current health and status of each CI/CD environment running on Kubernetes. |
-| [Feature Flags](https://docs.gitlab.com/ee/user/project/operations/feature_flags.html) **[PREMIUM]** | Deploy your features behind Feature Flags. |
+| [Canary Deployments](../user/project/canary_deployments.md) **[PREMIUM]** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
+| [Deploy Boards](../user/project/deploy_boards.md) **[PREMIUM]** | Check the current health and status of each CI/CD environment running on Kubernetes. |
+| [Feature Flags](../user/project/operations/feature_flags.md) **[PREMIUM]** | Deploy your features behind Feature Flags. |
| [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. |
| [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. |
| [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes. |
|---+---|
| **Secure** ||
-| [Container Scanning](https://docs.gitlab.com/ee/ci/examples/container_scanning.html) **[ULTIMATE]** | Check your Docker containers for known vulnerabilities.|
-| [Dependency Scanning](https://docs.gitlab.com/ee/ci/examples/dependency_scanning.html) **[ULTIMATE]** | Analyze your dependencies for known vulnerabilities. |
-| [License Management](https://docs.gitlab.com/ee/user/application_security/license_management/) **[ULTIMATE]** | Search your project dependencies for their licenses. |
-| [Security Test reports](https://docs.gitlab.com/ee/user/project/merge_requests/#security-reports-ultimate) **[ULTIMATE]** | Check for app vulnerabilities. |
+| [Container Scanning](../user/application_security/container_scanning/index.md) **[ULTIMATE]** | Check your Docker containers for known vulnerabilities.|
+| [Dependency Scanning](../user/application_security/dependency_scanning/index.md) **[ULTIMATE]** | Analyze your dependencies for known vulnerabilities. |
+| [License Management](../user/application_security/license_management/index.md) **[ULTIMATE]** | Search your project dependencies for their licenses. |
+| [Security Test reports](../user/project/merge_requests/index.md#security-reports-ultimate) **[ULTIMATE]** | Check for app vulnerabilities. |
## Examples
-GitLab provides examples of configuring GitLab CI/CD in the form of:
+Find example project code and tutorials for using GitLab CI/CD with a variety of app frameworks, languages, and platforms
+on the [CI Examples](examples/README.md) page.
-- A collection of [examples and other resources](examples/README.md).
-- Example projects that are available at the [`gitlab-examples`](https://gitlab.com/gitlab-examples) group. For example, see:
- - [`multi-project-pipelines`](https://gitlab.com/gitlab-examples/multi-project-pipelines) for examples of implementing multi-project pipelines.
- - [`review-apps-nginx`](https://gitlab.com/gitlab-examples/review-apps-nginx/) provides an example of using Review Apps.
+GitLab also provides [example projects](https://gitlab.com/gitlab-examples) pre-configured to use GitLab CI/CD.
## Administration **[CORE ONLY]**
@@ -161,6 +162,33 @@ See also the [Why CI/CD?](https://docs.google.com/presentation/d/1OGgk2Tcxbpl7DJ
As GitLab CI/CD has evolved, certain breaking changes have
been necessary. These are:
+#### 12.0
+
+- [Use refspec to clone/fetch git
+ repository](https://gitlab.com/gitlab-org/gitlab-runner/issues/4069).
+- [Old cache
+ configuration](https://gitlab.com/gitlab-org/gitlab-runner/issues/4070).
+- [Old metrics server
+ configuration](https://gitlab.com/gitlab-org/gitlab-runner/issues/4072).
+- [Remove
+ `FF_K8S_USE_ENTRYPOINT_OVER_COMMAND`](https://gitlab.com/gitlab-org/gitlab-runner/issues/4073).
+- [Remove Linux distributions that reach
+ EOL](https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/1130).
+- [Update command line API for helper
+ images](https://gitlab.com/gitlab-org/gitlab-runner/issues/4013).
+- [Remove old `git clean`
+ flow](https://gitlab.com/gitlab-org/gitlab-runner/issues/4175).
+
+#### 11.0
+
+- No breaking changes.
+
+#### 10.0
+
+- No breaking changes.
+
+#### 9.0
+
- [CI variables renaming for GitLab 9.0](variables/deprecated_variables.md#gitlab-90-renamed-variables). Read about the
deprecated CI variables and what you should use for GitLab 9.0+.
- [New CI job permissions model](../user/project/new_ci_build_permissions_model.md).
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index d703e106e76..9a5a3624c73 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -1,3 +1,7 @@
+---
+type: index, concepts, howto
+---
+
# Cache dependencies in GitLab CI/CD
GitLab CI/CD provides a caching mechanism that can be used to save time
@@ -60,7 +64,7 @@ In summary:
- Caches are disabled if not defined globally or per job (using `cache:`).
- Caches are available for all jobs in your `.gitlab-ci.yml` if enabled globally.
-- Caches can be used by subsequent pipelines of that very same job (a script in
+- Caches can be used by subsequent pipelines of that same job (a script in
a stage) in which the cache was created (if not defined globally).
- Caches are stored where the Runner is installed **and** uploaded to S3 if
[distributed cache is enabled](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching).
@@ -87,7 +91,7 @@ cache, when declaring `cache` in your jobs, use one or a mix of the following:
that share their cache.
- [Use sticky Runners](../runners/README.md#locking-a-specific-runner-from-being-enabled-for-other-projects)
that will be only available to a particular project.
-- [Use a `key`](../yaml/README.md#cachekey) that fits your workflow (e.g.,
+- [Use a `key`](../yaml/README.md#cachekey) that fits your workflow (for example,
different caches on each branch). For that, you can take advantage of the
[CI/CD predefined variables](../variables/README.md#predefined-environment-variables).
@@ -420,7 +424,7 @@ mismatch and a few ideas how to fix it.
Let's explore some examples.
----
+#### Examples
Let's assume you have only one Runner assigned to your project, so the cache
will be stored in the Runner's machine by default. If two jobs, A and B,
@@ -462,8 +466,6 @@ job B:
To fix that, use different `keys` for each job.
----
-
In another case, let's assume you have more than one Runners assigned to your
project, but the distributed cache is not enabled. We want the second time the
pipeline is run, `job A` and `job B` to re-use their cache (which in this case
@@ -526,3 +528,15 @@ Behind the scenes, this works by increasing a counter in the database, and the
value of that counter is used to create the key for the cache by appending an
integer to it: `-1`, `-2`, etc. After a push, a new key is generated and the
old cache is not valid anymore.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/ci/chatops/README.md b/doc/ci/chatops/README.md
index 5ecdf0f8c54..241134783da 100644
--- a/doc/ci/chatops/README.md
+++ b/doc/ci/chatops/README.md
@@ -1,3 +1,7 @@
+---
+type: index, concepts, howto
+---
+
# GitLab ChatOps
> **Notes:**
@@ -10,7 +14,10 @@ GitLab ChatOps provides a method to interact with CI/CD jobs through chat servic
## How it works
-GitLab ChatOps is built upon two existing features, [GitLab CI/CD](../README.md) and [Slack Slash Commmands](../../user/project/integrations/slack_slash_commands.md).
+GitLab ChatOps is built upon two existing features:
+
+- [GitLab CI/CD](../README.md).
+- [Slack Slash Commands](../../user/project/integrations/slack_slash_commands.md).
A new `run` action has been added to the [slash commands](../../integration/slash_commands.md), which takes two arguments: a `<job name>` to execute and the `<job arguments>`. When executed, ChatOps will look up the specified job name and attempt to match it to a corresponding job in [.gitlab-ci.yml](../yaml/README.md). If a matching job is found on `master`, a pipeline containing just that job is scheduled. Two additional [CI/CD variables](../variables/README.md#predefined-environment-variables) are passed to the job: `CHAT_INPUT` contains any additional arguments, and `CHAT_CHANNEL` is set to the name of channel the action was triggered in.
@@ -22,9 +29,9 @@ After the job has finished, its output is sent back to Slack provided it has com
Since ChatOps is built upon GitLab CI/CD, the job has all the same features and functions available. There a few best practices to consider however when creating ChatOps jobs:
-* It is strongly recommended to set `only: [chat]` so the job does not run as part of the standard CI pipeline.
-* If the job is set to `when: manual`, the pipeline will be created however the job will wait to be started.
-* It is important to keep in mind that there is very limited support for access control. If the user who triggered the slash command is a developer in the project, the job will run. The job itself can utilize existing [CI/CD variables](../variables/README.html#predefined-environment-variables) like `GITLAB_USER_ID` to perform additional rights validation, however these variables can be [overridden](../variables/README.html#priority-of-environment-variables).
+- It is strongly recommended to set `only: [chat]` so the job does not run as part of the standard CI pipeline.
+- If the job is set to `when: manual`, the pipeline will be created however the job will wait to be started.
+- It is important to keep in mind that there is limited support for access control. If the user who triggered the slash command is a developer in the project, the job will run. The job itself can utilize existing [CI/CD variables](../variables/README.html#predefined-environment-variables) like `GITLAB_USER_ID` to perform additional rights validation, however these variables can be [overridden](../variables/README.html#priority-of-environment-variables).
### Controlling the ChatOps reply
@@ -59,3 +66,15 @@ You can find and download the official GitLab ChatOps icon here.
![GitLab ChatOps bot icon](img/gitlab-chatops-icon-small.png)
[Download bigger image](img/gitlab-chatops-icon.png)
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
index 1c8e12d229c..bbb25c78ec5 100644
--- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
@@ -1,8 +1,15 @@
+---
+type: howto
+---
+
# Using GitLab CI/CD with a Bitbucket Cloud repository **[PREMIUM]**
-GitLab CI/CD can be used with Bitbucket Cloud by creating a
-[CI/CD project](https://docs.gitlab.com/ee/user/project/ci_cd_for_external_repo.html) and connecting
-your Git repository via URL.
+GitLab CI/CD can be used with Bitbucket Cloud by:
+
+1. Creating a [CI/CD project](../../user/project/ci_cd_for_external_repo.md).
+1. Connecting your Git repository via URL.
+
+To use GitLab CI/CD with a Bitbucket Cloud repository:
1. In GitLab create a **CI/CD for external repo**, select **Repo by URL** and
create the project.
@@ -16,13 +23,13 @@ your Git repository via URL.
with `api` scope. This will be used to authenticate requests from the web
hook that will be created in Bitbucket to notify GitLab of new commits.
-1. In Bitbucket from **Settings > Webhooks** create a new web hook to notify
+1. In Bitbucket, from **Settings > Webhooks**, create a new web hook to notify
GitLab of new commits.
The web hook URL should be set to the GitLab API to trigger pull mirroring,
using the Personal Access Token we just generated for authentication.
- ```
+ ```text
https://gitlab.com/api/v4/projects/<NAMESPACE>%2F<PROJECT>/mirror/pull?private_token=<PERSONAL_ACCESS_TOKEN>
```
@@ -33,27 +40,27 @@ your Git repository via URL.
After saving, test the web hook by pushing a change to your Bitbucket
repository.
-1. In Bitbucket create an **App Password** from **Bitbucket Settings > App
+1. In Bitbucket, create an **App Password** from **Bitbucket Settings > App
Passwords** to authenticate the build status script setting commit build
statuses in Bitbucket. Repository write permissions are required.
![Bitbucket Cloud webhook](img/bitbucket_app_password.png)
-1. In GitLab from **Settings > CI/CD > Environment variables** add variables to allow
- communication with Bitbucket via the Bitbucket API.
+1. In GitLab, from **Settings > CI/CD > Environment variables**, add variables to allow
+ communication with Bitbucket via the Bitbucket API:
- `BITBUCKET_ACCESS_TOKEN`: the Bitbucket app password created above
+ `BITBUCKET_ACCESS_TOKEN`: the Bitbucket app password created above.
- `BITBUCKET_USERNAME`: the username of the Bitbucket account
+ `BITBUCKET_USERNAME`: the username of the Bitbucket account.
- `BITBUCKET_NAMESPACE`: set this if your GitLab and Bitbucket namespaces differ
+ `BITBUCKET_NAMESPACE`: set this if your GitLab and Bitbucket namespaces differ.
- `BITBUCKET_REPOSITORY`: set this if your GitLab and Bitbucket project names differ
+ `BITBUCKET_REPOSITORY`: set this if your GitLab and Bitbucket project names differ.
-1. In Bitbucket add a script to push the pipeline status to Bitbucket.
+1. In Bitbucket, add a script to push the pipeline status to Bitbucket.
> Note: changes made in GitLab will be overwritten by any changes made
- upstream in Bitbucket.
+ > upstream in Bitbucket.
Create a file `build_status` and insert the script below and run
`chmod +x build_status` in your terminal to make the script executable.
@@ -111,7 +118,7 @@ your Git repository via URL.
1. Still in Bitbucket, create a `.gitlab-ci.yml` file to use the script to push
pipeline success and failures to Bitbucket.
- ```
+ ```yaml
stages:
- test
- ci_status
@@ -145,3 +152,15 @@ GitLab is now configured to mirror changes from Bitbucket, run CI/CD pipelines
configured in `.gitlab-ci.yml` and push the status to Bitbucket.
[pull-mirroring]: ../../workflow/repository_mirroring.md#pulling-from-a-remote-repository-starter
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/ci/ci_cd_for_external_repos/github_integration.md b/doc/ci/ci_cd_for_external_repos/github_integration.md
index 0e2acf957e0..53b36181062 100644
--- a/doc/ci/ci_cd_for_external_repos/github_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/github_integration.md
@@ -1,24 +1,21 @@
+---
+type: howto
+---
+
# Using GitLab CI/CD with a GitHub repository **[PREMIUM]**
GitLab CI/CD can be used with **GitHub.com** and **GitHub Enterprise** by
-creating a [CI/CD project](https://docs.gitlab.com/ee/user/project/ci_cd_for_external_repo.html) to connect your GitHub repository to
+creating a [CI/CD project](index.md) to connect your GitHub repository to
GitLab.
-NOTE: **Note:**
-To use **GitHub Enterprise** with **GitLab.com** you should use the
-manual method.
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+Watch a video on [Using GitLab CI/CD pipelines with GitHub repositories](https://www.youtube.com/watch?v=qgl3F2j-1cI).
## Connect with GitHub integration
If the [GitHub integration](../../integration/github.md) has been enabled by your GitLab
administrator:
-NOTE: **Note:**
-Due to a 10-token limitation on the [GitHub OAuth Implementation](https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/#creating-multiple-tokens-for-oauth-apps),
-if you import more than 10 times, your oldest imported project's token will be
-revoked. See issue [#9147](https://gitlab.com/gitlab-org/gitlab-ee/issues/9147)
-for more information.
-
1. In GitLab create a **CI/CD for external repo** project and select
**GitHub**.
@@ -31,23 +28,33 @@ for more information.
1. In GitHub, add a `.gitlab-ci.yml` to [configure GitLab CI/CD](../quick_start/README.md).
-GitLab will import the project, enable [Pull Mirroring](../../workflow/repository_mirroring.md#pulling-from-a-remote-repository-starter), enable
-[GitHub project integration](https://docs.gitlab.com/ee/user/project/integrations/github.html), and create a web hook
-on GitHub to notify GitLab of new commits.
+GitLab will:
+
+1. Import the project.
+1. Enable [Pull Mirroring](../../workflow/repository_mirroring.md#pulling-from-a-remote-repository-starter).
+1. Enable [GitHub project integration](../../user/project/integrations/github.md).
+1. Create a web hook on GitHub to notify GitLab of new commits.
+
+CAUTION: **Caution:**
+Due to a 10-token limitation on the [GitHub OAuth Implementation](https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/#creating-multiple-tokens-for-oauth-apps),
+if you import more than 10 times, your oldest imported project's token will be
+revoked. See issue [#9147](https://gitlab.com/gitlab-org/gitlab-ee/issues/9147)
+for more information.
## Connect with Personal Access Token
-NOTE: **Note:** Personal access tokens can only be used to connect GitHub.com
+NOTE: **Note:**
+Personal access tokens can only be used to connect GitHub.com
repositories to GitLab.
If you are not using the [GitHub integration](../../integration/github.md), you can
still perform a one-off authorization with GitHub to grant GitLab access your
repositories:
-1. Open https://github.com/settings/tokens/new to create a **Personal Access
+1. Open <https://github.com/settings/tokens/new> to create a **Personal Access
Token**. This token with be used to access your repository and push commit
statuses to GitHub.
-
+
The `repo` and `admin:repo_hook` should be enable to allow GitLab access to
your project, update commit statuses, and create a web hook to notify
GitLab of new commits.
@@ -63,19 +70,22 @@ repositories:
1. In GitHub, add a `.gitlab-ci.yml` to [configure GitLab CI/CD](../quick_start/README.md).
GitLab will import the project, enable [Pull Mirroring](../../workflow/repository_mirroring.md#pulling-from-a-remote-repository-starter), enable
-[GitHub project integration](https://docs.gitlab.com/ee/user/project/integrations/github.html), and create a web hook
+[GitHub project integration](../../user/project/integrations/github.md), and create a web hook
on GitHub to notify GitLab of new commits.
## Connect manually
+NOTE: **Note:**
+To use **GitHub Enterprise** with **GitLab.com** use this method.
+
If the [GitHub integration](../../integration/github.md) is not enabled, or is enabled
for a different GitHub instance, you GitLab CI/CD can be manually enabled for
-your repository.
+your repository:
-1. In GitHub open https://github.com/settings/tokens/new create a **Personal
+1. In GitHub open <https://github.com/settings/tokens/new> create a **Personal
Access Token.** GitLab will use this token to access your repository and
push commit statuses.
-
+
Enter a **Token description** and update the scope to allow:
`repo` so that GitLab can access your project and update commit statuses
@@ -86,7 +96,7 @@ your repository.
GitLab will automatically configure polling-based pull mirroring.
-1. Still in GitLab, enable the [GitHub project integration](https://docs.gitlab.com/ee/user/project/integrations/github.html)
+1. Still in GitLab, enable the [GitHub project integration](../../user/project/integrations/github.md)
from **Settings > Integrations.**
Check the **Active** checkbox to enable the integration, paste your
@@ -109,3 +119,15 @@ your repository.
![Create web hook](img/github_push_webhook.png)
1. In GitHub add a `.gitlab-ci.yml` to configure GitLab CI/CD.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/ci/ci_cd_for_external_repos/index.md b/doc/ci/ci_cd_for_external_repos/index.md
index 450ffe512dc..d46e451c609 100644
--- a/doc/ci/ci_cd_for_external_repos/index.md
+++ b/doc/ci/ci_cd_for_external_repos/index.md
@@ -1,27 +1,35 @@
+---
+type: index, howto
+---
+
# GitLab CI/CD for external repositories **[PREMIUM]**
+>[Introduced][ee-4642] in [GitLab Premium][eep] 10.6.
+
NOTE: **Note:**
This feature [is available for free](https://about.gitlab.com/2019/03/21/six-more-months-ci-cd-github/) to
GitLab.com users until September 22nd, 2019.
->[Introduced][ee-4642] in [GitLab Premium][eep] 10.6.
+GitLab CI/CD can be used with:
+
+- [GitHub](github_integration.md).
+- [Bitbucket Cloud](bitbucket_integration.md).
+- Any other Git server.
-GitLab CI/CD can be used with GitHub or any other Git server.
Instead of moving your entire project to GitLab, you can connect your
external repository to get the benefits of GitLab CI/CD.
-- [GitHub](github_integration.md)
-- [Bitbucket Cloud](bitbucket_integration.md)
-
Connecting an external repository will set up [repository mirroring][mirroring]
and create a lightweight project where issues, merge requests, wiki, and
snippets disabled. These features
[can be re-enabled later][settings].
-1. From your GitLab dashboard click **New project**
-1. Switch to the **CI/CD for external repo** tab
-1. Choose **GitHub** or **Repo by URL**
-1. The next steps are similar to the [import flow](../../user/project/import/index.md)
+To connect to an external repository:
+
+1. From your GitLab dashboard, click **New project**.
+1. Switch to the **CI/CD for external repo** tab.
+1. Choose **GitHub** or **Repo by URL**.
+1. The next steps are similar to the [import flow](../../user/project/import/index.md).
![CI/CD for external repository project creation](img/ci_cd_for_external_repo.png)
diff --git a/doc/ci/docker/README.md b/doc/ci/docker/README.md
index 446f5b54f0c..f76471b50f2 100644
--- a/doc/ci/docker/README.md
+++ b/doc/ci/docker/README.md
@@ -1,9 +1,13 @@
---
comments: false
+type: index
---
# Docker integration
+GitLab CI/CD can be combined with [Docker](https://www.docker.com) to enable
+integration between the two.
+
The following documentation is available for using GitLab CI/CD with Docker:
- [Using Docker images](using_docker_images.md).
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index 6920ce17b46..b4c4bea6447 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -1,3 +1,7 @@
+---
+type: concepts, howto
+---
+
# Building Docker images with GitLab CI/CD
GitLab CI/CD allows you to use Docker Engine to build and test docker-based projects.
@@ -5,10 +9,10 @@ GitLab CI/CD allows you to use Docker Engine to build and test docker-based proj
One of the new trends in Continuous Integration/Deployment is to:
-1. Create an application image
-1. Run tests against the created image
-1. Push image to a remote registry
-1. Deploy to a server from the pushed image
+1. Create an application image.
+1. Run tests against the created image.
+1. Push image to a remote registry.
+1. Deploy to a server from the pushed image.
It's also useful when your application already has the `Dockerfile` that can be
used to create and test an image:
@@ -86,7 +90,7 @@ The second approach is to use the special docker-in-docker (dind)
(`docker`) and run the job script in context of that
image in privileged mode.
-NOTE: **Note:** `docker-compose` is not part of docker-in-docker (dind). In case you'd like to use `docker-compose` in your CI builds, please follow the (installation instructions for docker-compose)[https://docs.docker.com/compose/install/] provided by docker.
+NOTE: **Note:** `docker-compose` is not part of docker-in-docker (dind). In case you'd like to use `docker-compose` in your CI builds, please follow the [installation instructions for docker-compose](https://docs.docker.com/compose/install/) provided by docker.
In order to do that, follow the steps:
@@ -113,7 +117,7 @@ In order to do that, follow the steps:
The above command will create a `config.toml` entry similar to this:
- ```
+ ```toml
[[runners]]
url = "https://gitlab.com/"
token = TOKEN
@@ -227,7 +231,7 @@ In order to do that, follow the steps:
The above command will create a `config.toml` entry similar to this:
- ```
+ ```toml
[[runners]]
url = "https://gitlab.com/"
token = REGISTRATION_TOKEN
@@ -270,9 +274,9 @@ aware of the following implications:
create containers with specific names, they may conflict with each other.
- Sharing files and directories from the source repo into containers may not
work as expected since volume mounting is done in the context of the host
- machine, not the build container, e.g.:
+ machine, not the build container. For example:
- ```
+ ```sh
docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests
```
@@ -337,7 +341,7 @@ NOTE: **Note:**
The shared Runners on GitLab.com use the `overlay2` driver by default.
By default, when using `docker:dind`, Docker uses the `vfs` storage driver which
-copies the filesystem on every run. This is a very disk-intensive operation
+copies the filesystem on every run. This is a disk-intensive operation
which can be avoided if a different driver is used, for example `overlay2`.
### Requirements
@@ -345,13 +349,13 @@ which can be avoided if a different driver is used, for example `overlay2`.
1. Make sure a recent kernel is used, preferably `>= 4.2`.
1. Check whether the `overlay` module is loaded:
- ```
+ ```sh
sudo lsmod | grep overlay
```
If you see no result, then it isn't loaded. To load it use:
- ```
+ ```sh
sudo modprobe overlay
```
@@ -359,7 +363,7 @@ which can be avoided if a different driver is used, for example `overlay2`.
On Ubuntu systems, this is done by editing `/etc/modules`. Just add the
following line into it:
- ```
+ ```text
overlay
```
@@ -367,7 +371,7 @@ which can be avoided if a different driver is used, for example `overlay2`.
You can enable the driver for each project individually by editing the project's `.gitlab-ci.yml`:
-```
+```yaml
variables:
DOCKER_DRIVER: overlay2
```
@@ -571,3 +575,15 @@ deploy:
[docker-cap]: https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
[2fa]: ../../user/profile/account/two_factor_authentication.md
[pat]: ../../user/profile/personal_access_tokens.md
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 13c26bc5f47..29578efacbb 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -1,3 +1,7 @@
+---
+type: concepts, howto
+---
+
# Using Docker images
GitLab CI in conjunction with [GitLab Runner](../runners/README.md) can use
@@ -45,10 +49,10 @@ The `image` keyword is the name of the Docker image the Docker executor
will run to perform the CI tasks.
By default, the executor will only pull images from [Docker Hub][hub],
-but this can be configured in the `gitlab-runner/config.toml` by setting
+however this can be configured in the `gitlab-runner/config.toml` by setting
the [Docker pull policy][] to allow using local images.
-For more information about images and Docker Hub please read
+For more information about images and Docker Hub, please read
the [Docker Fundamentals][] documentation.
## What is a service
@@ -95,8 +99,8 @@ required for the CI/CD job to proceed and is accessed by network.
To make sure this works, the Runner:
-1. checks which ports are exposed from the container by default
-1. starts a special container that waits for these ports to be accessible
+1. Checks which ports are exposed from the container by default.
+1. Starts a special container that waits for these ports to be accessible.
When the second stage of the check fails, either because there is no opened port in the
service, or the service was not started properly before the timeout and the port is not
@@ -106,7 +110,7 @@ In most cases it will affect the job, but there may be situations when the job
will still succeed even if that warning was printed. For example:
- The service was started a little after the warning was raised, and the job is
- not using the linked service from the very beginning. In that case, when the
+ not using the linked service from the beginning. In that case, when the
job needed to access the service, it may have been already there waiting for
connections.
- The service container is not providing any networking service, but it's doing
@@ -143,9 +147,9 @@ job:
If you need to have `php`, `node` and `go` available for your script, you should
either:
-- choose an existing Docker image that contains all required tools, or
-- create your own Docker image, which will have all the required tools included
- and use that in your job
+- Choose an existing Docker image that contains all required tools.
+- Create your own Docker image, which will have all the required tools included
+ and use that in your job.
### Accessing the services
@@ -167,18 +171,18 @@ access to it from your build container under two hostnames to choose from:
- `tutum-wordpress`
- `tutum__wordpress`
->**Note:**
+NOTE: **Note:**
Hostnames with underscores are not RFC valid and may cause problems in 3rd party
applications.
The default aliases for the service's hostname are created from its image name
following these rules:
-- Everything after the colon (`:`) is stripped
+- Everything after the colon (`:`) is stripped.
- Slash (`/`) is replaced with double underscores (`__`) and the primary alias
- is created
+ is created.
- Slash (`/`) is replaced with a single dash (`-`) and the secondary alias is
- created (requires GitLab Runner v1.1.0 or higher)
+ created (requires GitLab Runner v1.1.0 or higher).
To override the default behavior, you can
[specify a service alias](#available-settings-for-services).
@@ -333,7 +337,7 @@ services:
```
The Runner will still start two containers using the `mysql:latest` image,
-but now each of them will also be accessible with the alias configured
+however now each of them will also be accessible with the alias configured
in `.gitlab-ci.yml` file.
### Setting a command for the service
@@ -408,8 +412,6 @@ you should check which one your Runner is using. Specifically:
The syntax of `image:entrypoint` is similar to [Dockerfile's `ENTRYPOINT`][entrypoint].
-----
-
Let's assume you have a `super/sql:experimental` image with some SQL database
inside it and you would like to use it as a base image for your job because you
want to execute some tests with this database binary. Let's also assume that
@@ -443,7 +445,7 @@ image:
Look for the `[runners.docker]` section:
-```
+```toml
[runners.docker]
image = "ruby:2.1"
services = ["mysql:latest", "postgres:latest"]
@@ -464,16 +466,39 @@ that runner.
> - If the repository is private you need to authenticate your GitLab Runner in the
> registry. Learn more about how [GitLab Runner works in this case][runner-priv-reg].
+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.
+- [Credentials Store](#using-credentials-store). For more information, see [the relevant Docker documentation](https://docs.docker.com/engine/reference/commandline/login/#credentials-store).
+- [Credential Helpers](#using-credential-helpers). For more information, see [the relevant Docker documentation](https://docs.docker.com/engine/reference/commandline/login/#credential-helpers).
+
+To define which should be used, the GitLab Runner process reads the configuration in the following order:
+
+- `DOCKER_AUTH_CONFIG` variable provided as either:
+ - A [variable](../variables/README.md#gitlab-cicd-environment-variables) in `.gitlab-ci.yml`.
+ - A project's variables stored on the projects **Settings > CI/CD** page.
+- `DOCKER_AUTH_CONFIG` variable provided as environment variable in `config.toml` of the Runner.
+- `config.json` file placed in `$HOME/docker` directory of the user running GitLab Runner process.
+ If the `--user` flag is provided to run the GitLab Runner child processes as unprivileged user,
+ the home directory of the main GitLab Runner process user will be used.
+
+NOTE: **Note:**
+GitLab Runner reads this configuration **only** from `config.toml` and ignores it if
+it's provided as an environment variable. This is because GitLab Runnner uses **only**
+`config.toml` configuration and doesn't interpolate **ANY** environment variables at
+runtime.
+
+### Using statically-defined credentials
As an example, let's assume that you want to use the `registry.example.com:5000/private/image:latest`
image which is private and requires you to login into a private container registry.
Let's also assume that these are the login credentials:
-| Key | Value |
-|----------|---------------------------|
-| registry | registry.example.com:5000 |
-| username | my_username |
-| password | my_password |
+| Key | Value |
+|:---------|:----------------------------|
+| registry | `registry.example.com:5000` |
+| username | `my_username` |
+| password | `my_password` |
To configure access for `registry.example.com:5000`, follow these steps:
@@ -534,12 +559,85 @@ To configure access for `registry.example.com:5000`, follow these steps:
You can add configuration for as many registries as you want, adding more
registries to the `"auths"` hash as described above.
-NOTE: **Note:** The full `hostname:port` combination is required everywhere
+NOTE: **Note:**
+The full `hostname:port` combination is required everywhere
for the Runner to match the `DOCKER_AUTH_CONFIG`. For example, if
`registry.example.com:5000/namespace/image:tag` is specified in `.gitlab-ci.yml`,
then the `DOCKER_AUTH_CONFIG` must also specify `registry.example.com:5000`.
Specifying only `registry.example.com` will not work.
+
+### Using Credentials Store
+
+> Support for using Credentials Store was added in GitLab Runner 9.5.
+
+To configure credentials store, follow these steps:
+
+1. To use a credentials store, you need an external helper program to interact with a specific keychain or external store.
+Make sure helper program is available in GitLab Runner `$PATH`.
+
+1. Make GitLab Runner use it. There are two ways to accomplish this. Either:
+ - Create a
+ [variable](../variables/README.md#gitlab-cicd-environment-variables)
+ `DOCKER_AUTH_CONFIG` with the content of the
+ Docker configuration file as the value:
+
+ ```json
+ {
+ "credsStore": "osxkeychain"
+ }
+ ```
+
+ - Or, if you are running self-hosted Runners, add the above JSON to
+ `${GITLAB_RUNNER_HOME}/.docker/config.json`. GitLab Runner will read this config file
+ and will use the needed helper for this specific repository.
+
+NOTE: **Note:** `credsStore` is used to access ALL the registries.
+If you will want to use both images from private registry and public images from DockerHub,
+pulling from DockerHub will fail, because Docker daemon will try to use the same credentials for **ALL** the registries.
+
+### Using Credential Helpers
+
+> Support for using Credential Helpers was added in GitLab Runner 12.0
+
+As an example, let's assume that you want to use the `aws_account_id.dkr.ecr.region.amazonaws.com/private/image:latest`
+image which is private and requires you to log in into a private container registry.
+
+To configure access for `aws_account_id.dkr.ecr.region.amazonaws.com`, follow these steps:
+
+1. Make sure `docker-credential-ecr-login` is available in GitLab Runner's `$PATH`.
+
+1. Make GitLab Runner use it. There are two ways to accomplish this. Either:
+ - Create a [variable](../variables/README.md#gitlab-cicd-environment-variables)
+ `DOCKER_AUTH_CONFIG` with the content of the
+ Docker configuration file as the value:
+
+ ```json
+ {
+ "credHelpers": {
+ "aws_account_id.dkr.ecr.region.amazonaws.com": "ecr-login"
+ }
+ }
+ ```
+
+ - Or, if you are running self-hosted Runners,
+ add the above JSON to `${GITLAB_RUNNER_HOME}/.docker/config.json`.
+ GitLab Runner will read this config file and will use the needed helper for this
+ specific repository.
+
+1. You can now use any private image from `aws_account_id.dkr.ecr.region.amazonaws.com` defined in
+ `image` and/or `services` in your `.gitlab-ci.yml` file:
+
+ ```yaml
+ image: aws_account_id.dkr.ecr.region.amazonaws.com/private/image:latest
+ ```
+
+ In the example above, GitLab Runner will look at `aws_account_id.dkr.ecr.region.amazonaws.com` for the
+ image `private/image:latest`.
+
+You can add configuration for as many registries as you want, adding more
+registries to the `"credHelpers"` hash as described above.
+
## Configuring services
Many services accept environment variables which allow you to easily change
@@ -551,8 +649,9 @@ service containers.
For all possible configuration variables check the documentation of each image
provided in their corresponding Docker hub page.
-*Note: All variables will be passed to all services containers. It's not
-designed to distinguish which variable should go where.*
+NOTE: **Note:**
+All variables will be passed to all services containers. It's not
+designed to distinguish which variable should go where.
### PostgreSQL service example
@@ -582,8 +681,9 @@ time.
## How to debug a job locally
-*Note: The following commands are run without root privileges. You should be
-able to run Docker with your regular user account.*
+NOTE: **Note:**
+The following commands are run without root privileges. You should be
+able to run Docker with your regular user account.
First start with creating a file named `build_script`:
@@ -602,7 +702,7 @@ is specific to your project.
Then create some service containers:
-```
+```sh
docker run -d --name service-mysql mysql:latest
docker run -d --name service-postgres postgres:latest
```
@@ -614,7 +714,7 @@ respectively. They will both run in the background (`-d`).
Finally, create a build container by executing the `build_script` file we
created earlier:
-```
+```sh
docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script
```
@@ -626,7 +726,7 @@ piped using STDIN to the bash interpreter which in turn executes the
When you finish testing and no longer need the containers, you can remove them
with:
-```
+```sh
docker rm -f -v build service-mysql service-postgres
```
diff --git a/doc/ci/docker/using_kaniko.md b/doc/ci/docker/using_kaniko.md
index f354cdb398e..50f1ac3d54a 100644
--- a/doc/ci/docker/using_kaniko.md
+++ b/doc/ci/docker/using_kaniko.md
@@ -1,3 +1,7 @@
+---
+type: howto
+---
+
# Building images with kaniko and GitLab CI/CD
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/45512) in GitLab 11.2.
@@ -9,17 +13,18 @@ container images from a Dockerfile, inside a container or Kubernetes cluster.
kaniko solves two problems with using the
[docker-in-docker build](using_docker_build.md#use-docker-in-docker-executor) method:
-1. Docker-in-docker requires [privileged mode](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities)
- in order to function, which is a significant security concern.
-1. Docker-in-docker generally incurs a performance penalty and can be quite slow.
+- Docker-in-docker requires [privileged mode](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities)
+ in order to function, which is a significant security concern.
+- Docker-in-docker generally incurs a performance penalty and can be quite slow.
## Requirements
In order to utilize kaniko with GitLab, a [GitLab Runner](https://docs.gitlab.com/runner/)
-using either the [Kubernetes](https://docs.gitlab.com/runner/executors/kubernetes.html),
-[Docker](https://docs.gitlab.com/runner/executors/docker.html), or
-[Docker Machine](https://docs.gitlab.com/runner/executors/docker_machine.html)
-executors is required.
+using one of the following executors is required:
+
+- [Kubernetes](https://docs.gitlab.com/runner/executors/kubernetes.html).
+- [Docker](https://docs.gitlab.com/runner/executors/docker.html).
+- [Docker Machine](https://docs.gitlab.com/runner/executors/docker_machine.html).
## Building a Docker image with kaniko
@@ -34,14 +39,17 @@ few important details:
- A Docker `config.json` file needs to be created with the authentication
information for the desired container registry.
----
+In the following example, kaniko is used to:
+
+1. Build a Docker image.
+1. Then push it to [GitLab Container Registry](../../user/project/container_registry.md).
-In the following example, kaniko is used to build a Docker image and then push
-it to [GitLab Container Registry](../../user/project/container_registry.md).
The job will run only when a tag is pushed. A `config.json` file is created under
`/kaniko/.docker` with the needed GitLab Container Registry credentials taken from the
[environment variables](../variables/README.md#predefined-environment-variables)
-GitLab CI/CD provides. In the last step, kaniko uses the `Dockerfile` under the
+GitLab CI/CD provides.
+
+In the last step, kaniko uses the `Dockerfile` under the
root directory of the project, builds the Docker image and pushes it to the
project's Container Registry while tagging it with the Git tag:
@@ -80,3 +88,15 @@ store:
...
-----END CERTIFICATE-----" >> /kaniko/ssl/certs/ca-certificates.crt
```
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/ci/enable_or_disable_ci.md b/doc/ci/enable_or_disable_ci.md
index 9934d543991..56200142055 100644
--- a/doc/ci/enable_or_disable_ci.md
+++ b/doc/ci/enable_or_disable_ci.md
@@ -1,29 +1,32 @@
---
-type: reference
+type: howto
---
# How to enable or disable GitLab CI/CD
-To effectively use GitLab CI/CD, you need a valid [`.gitlab-ci.yml`](yaml/README.md)
-file present at the root directory of your project and a
-[runner](runners/README.md) properly set up. You can read our
-[quick start guide](quick_start/README.md) to get you started.
+To effectively use GitLab CI/CD, you need:
+
+- A valid [`.gitlab-ci.yml`](yaml/README.md) file present at the root directory
+ of your project.
+- A [runner](runners/README.md) properly set up.
+
+You can read our [quick start guide](quick_start/README.md) to get you started.
If you are using an external CI/CD server like Jenkins or Drone CI, it is advised
to disable GitLab CI/CD in order to not have any conflicts with the commits status
API.
----
-
GitLab CI/CD is exposed via the `/pipelines` and `/jobs` pages of a project.
Disabling GitLab CI/CD in a project does not delete any previous jobs.
In fact, the `/pipelines` and `/jobs` pages can still be accessed, although
it's hidden from the left sidebar menu.
-GitLab CI/CD is enabled by default on new installations and can be disabled either
-individually under each project's settings, or site-wide by modifying the
-settings in `gitlab.yml` and `gitlab.rb` for source and Omnibus installations
-respectively.
+GitLab CI/CD is enabled by default on new installations and can be disabled
+either:
+
+- Individually under each project's settings.
+- Site-wide by modifying the settings in `gitlab.yml` and `gitlab.rb` for source
+ and Omnibus installations respectively.
## Per-project user setting
@@ -40,10 +43,10 @@ and `gitlab.rb` for source and Omnibus installations respectively.
Two things to note:
-1. Disabling GitLab CI/CD, will affect only newly-created projects. Projects that
- had it enabled prior to this modification, will work as before.
-1. Even if you disable GitLab CI/CD, users will still be able to enable it in the
- project's settings.
+- Disabling GitLab CI/CD, will affect only newly-created projects. Projects that
+ had it enabled prior to this modification, will work as before.
+- Even if you disable GitLab CI/CD, users will still be able to enable it in the
+ project's settings.
For installations from source, open `gitlab.yml` with your editor and set
`builds` to `false`:
@@ -58,12 +61,32 @@ default_projects_features:
builds: false
```
-Save the file and restart GitLab: `sudo service gitlab restart`.
+Save the file and restart GitLab:
+
+```sh
+sudo service gitlab restart
+```
For Omnibus installations, edit `/etc/gitlab/gitlab.rb` and add the line:
-```
+```ruby
gitlab_rails['gitlab_default_projects_features_builds'] = false
```
-Save the file and reconfigure GitLab: `sudo gitlab-ctl reconfigure`.
+Save the file and reconfigure GitLab:
+
+```sh
+sudo gitlab-ctl reconfigure
+```
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index 9f930833093..a32dbc11a33 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -92,7 +92,7 @@ deploy_staging:
- master
```
-We have defined 3 [stages](yaml/README.md#stages):
+We have defined three [stages](yaml/README.md#stages):
- `test`
- `build`
@@ -135,7 +135,7 @@ In summary, with the above `.gitlab-ci.yml` we have achieved the following:
> the name given in `.gitlab-ci.yml` (with any variables expanded), while the
> second is a "cleaned-up" version of the name, suitable for use in URLs, DNS,
> etc.
-
+>
> Starting with GitLab 9.3, the environment URL is exposed to the Runner via
> `$CI_ENVIRONMENT_URL`. The URL is expanded from `.gitlab-ci.yml`, or if
> the URL was not defined there, the external URL from the environment is used.
@@ -617,6 +617,10 @@ versions of the app, all without leaving GitLab.
![Monitoring dashboard](img/environments_monitoring.png)
+#### Linking to external dashboard
+
+Add a [button to the Monitoring dashboard](../user/project/operations/linking_to_an_external_dashboard.md) linking directly to your existing external dashboards.
+
### Web terminals
> Web terminals were added in GitLab 8.15 and are only available to project Maintainers and Owners.
@@ -730,7 +734,7 @@ Below are some links you may find interesting:
- [The `.gitlab-ci.yml` definition of environments](yaml/README.md#environment)
- [A blog post on Deployments & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
- [Review Apps - Use dynamic environments to deploy your code for every branch](review_apps/index.md)
-- [Deploy Boards for your applications running on Kubernetes](https://docs.gitlab.com/ee/user/project/deploy_boards.html) **[PREMIUM]**
+- [Deploy Boards for your applications running on Kubernetes](../user/project/deploy_boards.md) **[PREMIUM]**
<!-- ## Troubleshooting
diff --git a/doc/ci/environments/protected_environments.md b/doc/ci/environments/protected_environments.md
index ab5c0e2dbad..b72ebe838b8 100644
--- a/doc/ci/environments/protected_environments.md
+++ b/doc/ci/environments/protected_environments.md
@@ -1,3 +1,7 @@
+---
+type: concepts, howto
+---
+
# Protected Environments **[PREMIUM]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6303) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.3.
@@ -47,3 +51,15 @@ Maintainers can:
- Update existing protected environments at any time by changing the access in the
**Allowed to Deploy** dropdown menu.
- Unprotect a protected environment by clicking the **Unprotect** button for that environment.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index 340a41c196b..2b4fe321cb3 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -1,47 +1,42 @@
---
comments: false
+type: index
---
# GitLab CI/CD Examples
-Examples are a useful way of understanding how to implement GitLab CI/CD for your specific use case.
+This page contains links to a variety of examples that can help you understand how to
+implement [GitLab CI/CD](../README.md) for your specific use case.
Examples are available in several forms. As a collection of:
- `.gitlab-ci.yml` [template files](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates) maintained in GitLab. When you create a new file via the UI,
GitLab will give you the option to choose one of these templates. This will allow you to quickly bootstrap your project for CI/CD.
If your favorite programming language or framework are missing, we would love your help by sending a merge request with a new `.gitlab-ci.yml` to this project.
-- Repositories with [example projects](https://gitlab.com/gitlab-examples) for various languages. You can fork and adjust them to your own needs.
+- Repositories with [example projects](https://gitlab.com/gitlab-examples) for various languages. You can fork and adjust them to your own needs. Projects include demonstrations of [multi-project pipelines](https://gitlab.com/gitlab-examples/multi-project-pipelines) and using [Review Apps with a static site served by nginx](https://gitlab.com/gitlab-examples/review-apps-nginx/).
- Examples and [other resources](#other-resources) listed below.
## CI/CD examples
-The following table lists examples for different use cases:
-
-| Use case | Resource |
-|:-----------------------------------------------|:------------------------------------------------------------------------------------------------------------------------|
-| Browser performance testing | [Browser Performance Testing with the Sitespeed.io container](browser_performance.md). |
-| Clojure | [Test a Clojure application with GitLab CI/CD](test-clojure-application.md). |
-| Code quality analysis | [Analyze your project's Code Quality](code_quality.md). **[STARTER]** |
-| Container scanning | [Container Scanning with GitLab CI/CD](../../user/application_security/container_scanning/index.md). **[ULTIMATE]** |
-| Dependency scanning | [Dependency Scanning with GitLab CI/CD](../../user/application_security/dependency_scanning/index.md). **[ULTIMATE]** |
-| Deployment with `dpl` | [Using `dpl` as deployment tool](deployment/README.md). |
-| Dynamic application<br>security testing (DAST) | [Dynamic Application Security Testing with GitLab CI/CD](../../user/application_security/dast/index.md). **[ULTIMATE]** |
-| Elixir | [Testing a Phoenix application with GitLab CI/CD](test_phoenix_app_with_gitlab_ci_cd/index.md). |
-| Game development | [DevOps and Game Dev with GitLab CI/CD](devops_and_game_dev_with_gitlab_ci_cd/index.md). |
-| GitLab Pages | See the [GitLab Pages](../../user/project/pages/index.md) documentation for a complete example. |
-| Java | [Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD](deploy_spring_boot_to_cloud_foundry/index.md). |
-| JUnit | [JUnit test reports](../junit_test_reports.md). |
-| License management | [Dependencies license management with GitLab CI/CD](../../user/application_security/license_management/index.md). **[ULTIMATE]** |
-| Maven | [How to deploy Maven projects to Artifactory with GitLab CI/CD](artifactory_and_gitlab/index.md). |
-| PHP | [Testing PHP projects](php.md). |
-| PHP | [Running Composer and NPM scripts with deployment via SCP in GitLab CI/CD](deployment/composer-npm-deploy.md). |
-| PHP | [Test and deploy Laravel applications with GitLab CI/CD and Envoy](laravel_with_gitlab_and_envoy/index.md). |
-| Python | [Test and deploy a Python application with GitLab CI/CD](test-and-deploy-python-application-to-heroku.md). |
-| Ruby | [Test and deploy a Ruby application with GitLab CI/CD](test-and-deploy-ruby-application-to-heroku.md). |
-| Scala | [Test and deploy a Scala application to Heroku](test-scala-application.md). |
-| Static application<br>security testing (SAST) | [Static Application Security Testing with GitLab CI/CD](../../user/application_security/sast/index.md). **[ULTIMATE]** |
-| Testing | [End-to-end testing with GitLab CI/CD and WebdriverIO](end_to_end_testing_webdriverio/index.md). |
+The following table lists examples with step-by-step tutorials that are contained in this section.
+
+| Use case | Resource |
+|:----------------------------|:---------------------------------------------------------------------------------------------------------------------------|
+| Browser performance testing | [Browser Performance Testing with the Sitespeed.io container](browser_performance.md). |
+| Clojure | [Test a Clojure application with GitLab CI/CD](test-clojure-application.md). |
+| Deployment with Dpl | [Using `dpl` as deployment tool](deployment/README.md). |
+| Elixir | [Testing a Phoenix application with GitLab CI/CD](test_phoenix_app_with_gitlab_ci_cd/index.md). |
+| End-to-end testing | [End-to-end testing with GitLab CI/CD and WebdriverIO](end_to_end_testing_webdriverio/index.md). |
+| Game development | [DevOps and Game Dev with GitLab CI/CD](devops_and_game_dev_with_gitlab_ci_cd/index.md). |
+| GitLab Pages | See the [GitLab Pages](../../user/project/pages/index.md) documentation for a complete example of deploying a static site. |
+| Java with Spring Boot | [Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD](deploy_spring_boot_to_cloud_foundry/index.md). |
+| Java with Maven | [How to deploy Maven projects to Artifactory with GitLab CI/CD](artifactory_and_gitlab/index.md). |
+| PHP with PHPunit, atoum | [Testing PHP projects](php.md). |
+| PHP with NPM, SCP | [Running Composer and NPM scripts with deployment via SCP in GitLab CI/CD](deployment/composer-npm-deploy.md). |
+| PHP with Laravel, Ennvoy | [Test and deploy Laravel applications with GitLab CI/CD and Envoy](laravel_with_gitlab_and_envoy/index.md). |
+| Python on Heroku | [Test and deploy a Python application with GitLab CI/CD](test-and-deploy-python-application-to-heroku.md). |
+| Ruby on Heroku | [Test and deploy a Ruby application with GitLab CI/CD](test-and-deploy-ruby-application-to-heroku.md). |
+| Scala on Heroku | [Test and deploy a Scala application to Heroku](test-scala-application.md). |
### Contributing examples
@@ -50,24 +45,29 @@ language users and GitLab by sending a merge request with a guide for that langu
You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/)
to get paid for writing complete articles for GitLab.
-### Adding templates to your GitLab installation **[PREMIUM ONLY]**
+## Adding templates to your GitLab installation **[PREMIUM ONLY]**
-If you want to have customized examples and templates for your own self-managed GitLab instance available to your team, your GitLab administrator can [designate an instance template repository](https://docs.gitlab.com/ee/user/admin_area/settings/instance_template_repository.html) that contains examples and templates specific to your enterprise.
+If you want to have customized examples and templates for your own self-managed GitLab instance available to your team, your GitLab administrator can [designate an instance template repository](../../user/admin_area/settings/instance_template_repository.md) that contains examples and templates specific to your enterprise.
## Other resources
-This section provides further resources to help you get familiar with different aspects of GitLab CI/CD.
-
-NOTE: **Note:**
-These resources may no longer reflect the current state of GitLab CI/CD.
+This section provides further resources to help you get familiar with various uses of GitLab CI/CD.
+Note that older articles and videos may not reflect the state of the latest GitLab release.
### CI/CD in the cloud
For examples of setting up GitLab CI/CD for cloud-based environments, see:
- [How to set up multi-account AWS SAM deployments with GitLab CI](https://about.gitlab.com/2019/02/04/multi-account-aws-sam-deployments-with-gitlab-ci/)
+- [Automating Kubernetes Deployments with GitLab CI/CD](https://www.youtube.com/watch?v=wEDRfAz6_Uw)
- [How to autoscale continuous deployment with GitLab Runner on DigitalOcean](https://about.gitlab.com/2018/06/19/autoscale-continuous-deployment-gitlab-runner-digital-ocean/)
- [How to create a CI/CD pipeline with Auto Deploy to Kubernetes using GitLab and Helm](https://about.gitlab.com/2017/09/21/how-to-create-ci-cd-pipeline-with-autodeploy-to-kubernetes-using-gitlab-and-helm/)
+- [Demo - Deploying from GitLab to OpenShift Container Cluster](https://youtu.be/EwbhA53Jpp4)
+
+See also the following video overviews:
+
+- [Containers, Schedulers, and GitLab CI](https://www.youtube.com/watch?v=d-9awBxEbvQ).
+- [Deploying to IBM Cloud with GitLab CI/CD](https://www.youtube.com/watch?v=6ZF4vgKMd-g).
### Customer stories
@@ -83,7 +83,6 @@ For some examples to help get you started, see:
- [GitLab CI/CD's 2018 highlights](https://about.gitlab.com/2019/01/21/gitlab-ci-cd-features-improvements/)
- [A beginner's guide to continuous integration](https://about.gitlab.com/2018/01/22/a-beginners-guide-to-continuous-integration/)
-- [Making CI easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/)
### Implementing GitLab CI/CD
@@ -101,6 +100,10 @@ For examples of others who have implemented GitLab CI/CD, see:
- [Fast and natural continuous integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/)
- [Demo: CI/CD with GitLab in action](https://about.gitlab.com/2017/03/13/ci-cd-demo/)
+### Migrating to GitLab from third-party CI tools
+
+- [Migrating from Jenkins to GitLab](https://youtu.be/RlEVGOpYF5Y)
+
### Integrating GitLab CI/CD with other systems
To see how you can integrate GitLab CI/CD with third-party systems, see:
diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md
index 589912e7a2a..2117b342903 100644
--- a/doc/ci/examples/artifactory_and_gitlab/index.md
+++ b/doc/ci/examples/artifactory_and_gitlab/index.md
@@ -4,6 +4,7 @@ author: Fabio Busatto
author_gitlab: bikebilly
level: intermediate
article_type: tutorial
+type: tutorial
date: 2017-08-15
---
@@ -16,8 +17,8 @@ to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory
You'll create two different projects:
-- `simple-maven-dep`: the app built and deployed to Artifactory (available at <https://gitlab.com/gitlab-examples/maven/simple-maven-dep>)
-- `simple-maven-app`: the app using the previous one as a dependency (available at <https://gitlab.com/gitlab-examples/maven/simple-maven-app>)
+- `simple-maven-dep`: the app built and deployed to Artifactory (see the [simple-maven-dep](https://gitlab.com/gitlab-examples/maven/simple-maven-dep) example project)
+- `simple-maven-app`: the app using the previous one as a dependency (see the [simple-maven-app](https://gitlab.com/gitlab-examples/maven/simple-maven-app) example project)
We assume that you already have a GitLab account on [GitLab.com](https://gitlab.com/), and that you know the basic usage of Git and [GitLab CI/CD](https://about.gitlab.com/product/continuous-integration/).
We also assume that an Artifactory instance is available and reachable from the internet, and that you have valid credentials to deploy on it.
@@ -105,7 +106,7 @@ parameter in `.gitlab-ci.yml` to use the custom location instead of the default
Now it's time we set up [GitLab CI/CD](https://about.gitlab.com/product/continuous-integration/) to automatically build, test and deploy the dependency!
GitLab CI/CD uses a file in the root of the repo, named `.gitlab-ci.yml`, to read the definitions for jobs
-that will be executed by the configured GitLab Runners. You can read more about this file in the [GitLab Documentation](https://docs.gitlab.com/ee/ci/yaml/).
+that will be executed by the configured GitLab Runners. You can read more about this file in the [GitLab Documentation](../../yaml/README.md).
First of all, remember to set up variables for your deployment. Navigate to your project's **Settings > CI/CD > Environment variables** page
and add the following ones (replace them with your current values, of course):
diff --git a/doc/ci/examples/browser_performance.md b/doc/ci/examples/browser_performance.md
index 442d0788d37..8ecac4a5a4f 100644
--- a/doc/ci/examples/browser_performance.md
+++ b/doc/ci/examples/browser_performance.md
@@ -1,13 +1,17 @@
-# Browser Performance Testing with the Sitespeed.io container
+---
+type: howto
+---
-CAUTION: **Caution:**
+# Browser Performance Testing with the sitespeed.io container
+
+NOTE: **Note:**
The job definition shown below is supported on GitLab 11.5 and later versions.
It also requires the GitLab Runner 11.5 or later.
For earlier versions, use the [previous job definitions](#previous-job-definitions).
This example shows how to run the
-[Sitespeed.io container](https://hub.docker.com/r/sitespeedio/sitespeed.io/) on
-your code by using GitLab CI/CD and [Sitespeed.io](https://www.sitespeed.io)
+[sitespeed.io container](https://hub.docker.com/r/sitespeedio/sitespeed.io/) on
+your code by using GitLab CI/CD and [sitespeed.io](https://www.sitespeed.io)
using Docker-in-Docker.
First, you need GitLab Runner with
@@ -38,25 +42,25 @@ performance:
```
The above example will create a `performance` job in your CI/CD pipeline and will run
-Sitespeed.io against the webpage you defined in `URL` to gather key metrics.
+sitespeed.io against the webpage you defined in `URL` to gather key metrics.
The [GitLab plugin](https://gitlab.com/gitlab-org/gl-performance) for
-Sitespeed.io is downloaded in order to save the report as a
+sitespeed.io is downloaded in order to save the report as a
[Performance report artifact](../yaml/README.md#artifactsreportsperformance-premium)
that you can later download and analyze.
Due to implementation limitations we always take the latest Performance artifact available.
-The full HTML Sitespeed.io report will also be saved as an artifact, and if you have
+The full HTML sitespeed.io report will also be saved as an artifact, and if you have
[GitLab Pages](../../user/project/pages/index.md) enabled, it can be viewed
directly in your browser.
-For further customization options of Sitespeed.io, including the ability to
-provide a list of URLs to test, please consult
-[their documentation](https://www.sitespeed.io/documentation/sitespeed.io/configuration/).
+For further customization options for sitespeed.io, including the ability to
+provide a list of URLs to test, please see the
+[Sitespeed.io Configuration](https://www.sitespeed.io/documentation/sitespeed.io/configuration/) documentation.
TIP: **Tip:**
For [GitLab Premium](https://about.gitlab.com/pricing/) users, key metrics are automatically
extracted and shown right in the merge request widget.
-[Learn more on Browser Performance Testing in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html).
+[Learn more on Browser Performance Testing in merge requests](../../user/project/merge_requests/browser_performance_testing.md).
## Performance testing on Review Apps
@@ -73,7 +77,7 @@ set this up:
1. In the `performance` job, read the previous artifact into an environment
variable, like `$CI_ENVIRONMENT_URL`, and use it to parameterize the test
URLs.
-1. You can now run the Sitespeed.io container against the desired hostname and
+1. You can now run the sitespeed.io container against the desired hostname and
paths.
Your `.gitlab-ci.yml` file would look like:
diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md
index b34637efc8d..0aa108a2e73 100644
--- a/doc/ci/examples/code_climate.md
+++ b/doc/ci/examples/code_climate.md
@@ -1,6 +1,5 @@
---
-redirect_from: 'https://docs.gitlab.com/ee/ci/examples/code_climate.html'
-redirect_to: code_quality.md
+redirect_to: 'code_quality.md'
---
This document was moved to [another location](code_quality.md).
diff --git a/doc/ci/examples/code_quality.md b/doc/ci/examples/code_quality.md
index 186d4527bb6..43f773dab7c 100644
--- a/doc/ci/examples/code_quality.md
+++ b/doc/ci/examples/code_quality.md
@@ -1,3 +1,8 @@
+---
+redirect_from: 'https://docs.gitlab.com/ee/ci/examples/code_climate.html'
+type: reference, howto
+---
+
# Analyze your project's Code Quality
CAUTION: **Caution:**
@@ -27,7 +32,7 @@ Due to implementation limitations we always take the latest Code Quality artifac
TIP: **Tip:**
For [GitLab Starter][ee] users, this information will be automatically
extracted and shown right in the merge request widget.
-[Learn more on Code Quality in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html).
+[Learn more on Code Quality in merge requests](../../user/project/merge_requests/code_quality.md).
## Previous job definitions
diff --git a/doc/ci/examples/container_scanning.md b/doc/ci/examples/container_scanning.md
index 4d41e424f4a..6570f6b2d98 100644
--- a/doc/ci/examples/container_scanning.md
+++ b/doc/ci/examples/container_scanning.md
@@ -1,5 +1,5 @@
---
-redirect_to: 'https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html'
+redirect_to: '../../user/application_security/container_scanning/index.md'
---
-This document was moved to [another location](https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html).
+This document was moved to [another location](../../user/application_security/container_scanning/index.md).
diff --git a/doc/ci/examples/dast.md b/doc/ci/examples/dast.md
index b676c661267..9591abfc276 100644
--- a/doc/ci/examples/dast.md
+++ b/doc/ci/examples/dast.md
@@ -1,5 +1,5 @@
---
-redirect_to: 'https://docs.gitlab.com/ee/user/application_security/dast/index.html'
+redirect_to: '../../user/application_security/dast/index.md'
---
-This document was moved to [another location](https://docs.gitlab.com/ee/user/application_security/dast/index.html).
+This document was moved to [another location](../../user/application_security/dast/index.md).
diff --git a/doc/ci/examples/dependency_scanning.md b/doc/ci/examples/dependency_scanning.md
index 3a8b53b425c..dc234a3489f 100644
--- a/doc/ci/examples/dependency_scanning.md
+++ b/doc/ci/examples/dependency_scanning.md
@@ -1,5 +1,5 @@
---
-redirect_to: 'https://docs.gitlab.com/ee/user/application_security/dependency_scanning/index.html'
+redirect_to: '../../user/application_security/dependency_scanning/index.md'
---
-This document was moved to [another location](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/index.html).
+This document was moved to [another location](../../user/application_security/dependency_scanning/index.md).
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
index c622dd86828..538843ab8dc 100644
--- a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
+++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
@@ -3,6 +3,7 @@ author: Dylan Griffith
author_gitlab: DylanGriffith
level: intermediate
article_type: tutorial
+type: tutorial
date: 2018-06-07
last_updated: 2019-04-08
description: "Continuous Deployment of a Spring Boot application to Cloud Foundry with GitLab CI/CD"
@@ -26,13 +27,13 @@ using GitLab CI/CD, read through the blog post [Continuous Delivery of a Spring
## Requirements
-_We assume you are familiar with Java, GitLab, Cloud Foundry, and GitLab CI/CD._
+This tutorial assumes you are familiar with Java, GitLab, Cloud Foundry, and GitLab CI/CD.
-To follow along with this tutorial you will need the following:
+To follow along, you will need:
- An account on [Pivotal Web Services (PWS)](https://run.pivotal.io/) or any
- other Cloud Foundry instance
-- An account on GitLab
+ other Cloud Foundry (CF) instance.
+- An account on GitLab.
NOTE: **Note:**
You will need to replace the `api.run.pivotal.io` URL in the all below
diff --git a/doc/ci/examples/deployment/README.md b/doc/ci/examples/deployment/README.md
index 010ba6b66a2..26b10c7eeaf 100644
--- a/doc/ci/examples/deployment/README.md
+++ b/doc/ci/examples/deployment/README.md
@@ -1,12 +1,14 @@
+---
+type: tutorial
+---
+
# Using Dpl as deployment tool
-[Dpl](https://github.com/travis-ci/dpl) (dee-pee-ell) is a deploy tool made for
+[Dpl](https://github.com/travis-ci/dpl) (prouncounced like the letters D-P-L) is a deploy tool made for
continuous deployment that's developed and used by Travis CI, but can also be
used with GitLab CI.
->**Note:**
-We recommend to use Dpl if you're deploying to any of these services:
-<https://github.com/travis-ci/dpl#supported-providers>.
+Dpl can be used to deploy to any of the [supported providers](https://github.com/travis-ci/dpl#supported-providers).
## Requirements
@@ -50,8 +52,8 @@ To use different provider take a look at long list of [Supported Providers](http
## Using Dpl with Docker
-When you use GitLab Runner you most likely configured it to use your server's shell commands.
-This means that all commands are run in context of local user (ie. gitlab_runner or gitlab_ci_multi_runner).
+In most cases, you will have configured [GitLab Runner](https://docs.gitlab.com/runner/) to use your server's shell commands.
+This means that all commands are run in the context of local user (e.g. gitlab_runner or gitlab_ci_multi_runner).
It also means that most probably in your Docker container you don't have the Ruby runtime installed.
You will have to install it:
diff --git a/doc/ci/examples/deployment/composer-npm-deploy.md b/doc/ci/examples/deployment/composer-npm-deploy.md
index 4758ccad5aa..79b3cbd0c69 100644
--- a/doc/ci/examples/deployment/composer-npm-deploy.md
+++ b/doc/ci/examples/deployment/composer-npm-deploy.md
@@ -1,8 +1,12 @@
+---
+type: tutorial
+---
+
# Running Composer and NPM scripts with deployment via SCP in GitLab CI/CD
-This guide covers the building dependencies of a PHP project while compiling assets via an NPM script.
+This guide covers the building of dependencies of a PHP project while compiling assets via an NPM script using [GitLab CI/CD](../../README.md).
-While is possible to create your own image with custom PHP and Node JS versions, for brevity, we will use an existing [Docker image](https://hub.docker.com/r/tetraweb/php/) that contains both PHP and NodeJS installed.
+While it is possible to create your own image with custom PHP and Node JS versions, for brevity, we will use an existing [Docker image](https://hub.docker.com/r/tetraweb/php/) that contains both PHP and NodeJS installed.
```yaml
image: tetraweb/php
@@ -46,9 +50,9 @@ To make this work, you need to add a GitLab CI/CD Variable (accessible on _gitla
### Security tip
-Create a user that has access **only** to the folder that needs to be updated!
+Create a user that has access **only** to the folder that needs to be updated.
-After you create that variable, you need to make sure that key will be added to the docker container on run:
+After you create that variable, you need to make sure that key will be added to the Docker container on run:
```yaml
before_script:
@@ -68,7 +72,7 @@ In order, this means that:
And this is basically all you need in the `before_script` section.
-## How to deploy things
+## How to deploy
As we stated above, we need to deploy the `build` folder from the docker image to our server. To do so, we create a new job:
@@ -93,7 +97,7 @@ Here's the breakdown:
1. `ssh-add ...` we will add that private key you added on the web UI to the docker container
1. We will connect via `ssh` and create a new `_tmp` folder
1. We will connect via `scp` and upload the `build` folder (which was generated by a `npm` script) to our previously created `_tmp` folder
-1. We will connect again to `ssh` and move the `live` folder to an `_old` folder, then move `_tmp` to `live`.
+1. We will connect again via `ssh` and move the `live` folder to an `_old` folder, then move `_tmp` to `live`.
1. We connect to ssh and remove the `_old` folder
What's the deal with the artifacts? We just tell GitLab CI to keep the `build` directory (later on, you can download that as needed).
@@ -109,14 +113,14 @@ If you're using this only for stage server, you could do this in two steps:
The problem is that there will be a small period of time when you won't have the app on your server.
-So we use so many steps because we want to make sure that at any given time we have a functional app in place.
+Therefore, for a production environment we use additional steps to ensure that at any given time, a functional app is in place.
## Where to go next
-Since this was a WordPress project, I gave real life code snippets. Some ideas you can pursuit:
+Since this was a WordPress project, I gave real life code snippets. Some further ideas you can pursue:
-- Having a slightly different script for `master` branch will allow you to deploy to a production server from that branch and to a stage server from any other branches;
-- Instead of pushing it live, you can push it to WordPress official repo (with creating a SVN commit & stuff);
+- Having a slightly different script for `master` branch will allow you to deploy to a production server from that branch and to a stage server from any other branches.
+- Instead of pushing it live, you can push it to WordPress official repo (with creating a SVN commit, etc.).
- You could generate i18n text domains on the fly.
---
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index d6ad00a77da..50e61cafeb9 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -3,6 +3,7 @@ author: Ryan Hall
author_gitlab: blitzgren
level: intermediate
article_type: tutorial
+type: tutorial
date: 2018-03-07
last_updated: 2019-03-11
---
@@ -14,7 +15,7 @@ platforms without the use of plugins like Adobe Flash. Furthermore, by using Git
single game developers, as well as game dev teams, can easily host browser-based games online.
In this tutorial, we'll focus on DevOps, as well as testing and hosting games with Continuous
-Integration/Deployment methods. We assume you are familiar with GitLab, javascript,
+Integration/Deployment methods using [GitLab CI/CD](../../README.md). We assume you are familiar with GitLab, JavaScript,
and the basics of game development.
## The game
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
index bd221b7145e..7f1beb96bbf 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -3,6 +3,7 @@ author: Vincent Tunru
author_gitlab: Vinnl
level: advanced
article_type: user guide
+type: tutorial
date: 2019-02-18
description: 'Confidence checking your entire app every time a new feature is added can quickly become repetitive. Learn how to automate it with GitLab CI/CD.'
---
@@ -22,7 +23,9 @@ However, looking at the freshly deployed code to check whether it still looks an
expected is repetitive manual work, which means it is a prime candidate for automation. This is
where automated [end-to-end testing](https://martinfowler.com/bliki/BroadStackTest.html) comes in:
having the computer run through a few simple scenarios that requires the proper functioning of all
-layers of your application, from the frontend to the database. In this article, we will discuss how
+layers of your application, from the frontend to the database.
+
+In this article, we will discuss how
to write such end-to-end tests, and how to set up GitLab CI/CD to automatically run these tests
against your new code, on a branch-by-branch basis. For the scope of this article, we will walk you
through the process of setting up GitLab CI/CD for end-to-end testing Javascript-based applications
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
index f56d5429fb7..d7308a3a5ec 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -4,6 +4,7 @@ author: Mehran Rasulian
author_gitlab: mehranrasulian
level: intermediate
article_type: tutorial
+type: tutorial
date: 2017-08-31
last_updated: 2019-03-06
---
diff --git a/doc/ci/examples/license_management.md b/doc/ci/examples/license_management.md
index 08704425a75..53e38111bf3 100644
--- a/doc/ci/examples/license_management.md
+++ b/doc/ci/examples/license_management.md
@@ -1,5 +1,5 @@
---
-redirect_to: 'https://docs.gitlab.com/ee/user/application_security/license_management/index.html'
+redirect_to: '../../user/application_security/license_management/index.md'
---
-This document was moved to [another location](https://docs.gitlab.com/ee/user/application_security/license_management/index.html).
+This document was moved to [another location](../../user/application_security/license_management/index.md).
diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md
index c1048f3d2e3..c459bb7001f 100644
--- a/doc/ci/examples/php.md
+++ b/doc/ci/examples/php.md
@@ -1,8 +1,12 @@
+---
+type: tutorial
+---
+
# Testing PHP projects
This guide covers basic building instructions for PHP projects.
-There are covered two cases: testing using the Docker executor and testing
+Two testing scenarios are covered: using the Docker executor and
using the Shell executor.
## Test PHP projects using the Docker executor
@@ -245,7 +249,7 @@ before_script:
...
```
-## Access private packages / dependencies
+## Access private packages or dependencies
If your test suite needs to access a private repository, you need to configure
[the SSH keys](../ssh_keys/README.md) in order to be able to clone it.
@@ -254,7 +258,7 @@ If your test suite needs to access a private repository, you need to configure
Most of the time you will need a running database in order for your tests to
run. If you are using the Docker executor you can leverage Docker's ability to
-link to other containers. In GitLab Runner lingo, this can be achieved by
+link to other containers. With GitLab Runner, this can be achieved by
defining a `service`.
This functionality is covered in [the CI services](../services/README.md)
@@ -279,7 +283,7 @@ We have set up an [Example PHP Project][php-example-repo] for your convenience
that runs on [GitLab.com](https://gitlab.com) using our publicly available
[shared runners](../runners/README.md).
-Want to hack on it? Simply fork it, commit and push your changes. Within a few
+Want to hack on it? Simply fork it, commit, and push your changes. Within a few
moments the changes will be picked by a public runner and the job will begin.
[php-hub]: https://hub.docker.com/r/_/php/
diff --git a/doc/ci/examples/sast.md b/doc/ci/examples/sast.md
index 688cc79d0f6..7c644ac833d 100644
--- a/doc/ci/examples/sast.md
+++ b/doc/ci/examples/sast.md
@@ -1,5 +1,5 @@
---
-redirect_to: 'https://docs.gitlab.com/ee/user/application_security/sast/index.html'
+redirect_to: '../../user/application_security/sast/index.md'
---
-This document was moved to [another location](https://docs.gitlab.com/ee/user/application_security/sast/index.html).
+This document was moved to [another location](../../user/application_security/sast/index.md).
diff --git a/doc/ci/examples/sast_docker.md b/doc/ci/examples/sast_docker.md
index 570898b2d23..6570f6b2d98 100644
--- a/doc/ci/examples/sast_docker.md
+++ b/doc/ci/examples/sast_docker.md
@@ -1,5 +1,5 @@
---
-redirect_to: 'https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html'
+redirect_to: '../../user/application_security/container_scanning/index.md'
---
-This document was moved to [another location](../../user/application_security/container_scanning/index.html).
+This document was moved to [another location](../../user/application_security/container_scanning/index.md).
diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
index 47d20a4e1c1..f9d185f187c 100644
--- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
@@ -1,8 +1,12 @@
+---
+type: tutorial
+---
+
# Test and deploy a Python application with GitLab CI/CD
This example will guide you how to run tests in your Python application and deploy it automatically as Heroku application.
-You can checkout the [example source](https://gitlab.com/ayufan/python-getting-started).
+You can also view or fork the complete [example source](https://gitlab.com/ayufan/python-getting-started).
## Configure project
@@ -46,9 +50,9 @@ production:
This project has three jobs:
-- `test` - used to test Django application,
-- `staging` - used to automatically deploy staging environment every push to `master` branch
-- `production` - used to automatically deploy production environment for every created tag
+- `test` - used to test Django application.
+- `staging` - used to automatically deploy staging environment every push to `master` branch.
+- `production` - used to automatically deploy production environment for every created tag.
## Store API keys
@@ -67,8 +71,9 @@ You can do this through the [Dashboard](https://dashboard.heroku.com/).
## Create Runner
First install [Docker Engine](https://docs.docker.com/installation/).
+
To build this project you also need to have [GitLab Runner](https://docs.gitlab.com/runner).
-You can use public runners available on `gitlab.com`, but you can register your own:
+You can use public runners available on `gitlab.com` or you can register your own:
```sh
gitlab-runner register \
@@ -81,6 +86,6 @@ gitlab-runner register \
--docker-postgres latest
```
-With the command above, you create a runner that uses [python:3.5](https://hub.docker.com/r/_/python/) image and uses [postgres](https://hub.docker.com/r/_/postgres/) database.
+With the command above, you create a runner that uses the [python:3.5](https://hub.docker.com/r/_/python/) image and uses a [postgres](https://hub.docker.com/r/_/postgres/) database.
-To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password.
+To access the PostgreSQL database, connect to `host: postgres` as user `postgres` with no password.
diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
index 3a0ddf001b8..79d54b52b5a 100644
--- a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
@@ -1,8 +1,12 @@
+---
+type: tutorial
+---
+
# Test and deploy a Ruby application with GitLab CI/CD
-This example will guide you how to run tests in your Ruby on Rails application and deploy it automatically as Heroku application.
+This example will guide you through how to run tests in your Ruby on Rails application and deploy it automatically as a Heroku application.
-You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://gitlab.com/ayufan/ruby-getting-started/builds?scope=all).
+You can also view or fork the complete [example source](https://gitlab.com/ayufan/ruby-getting-started) and view the logs of its past [CI jobs](https://gitlab.com/ayufan/ruby-getting-started/-/jobs?scope=finished).
## Configure the project
@@ -53,13 +57,14 @@ Find your Heroku API key in [Manage Account](https://dashboard.heroku.com/accoun
## Create Heroku application
For each of your environments, you'll need to create a new Heroku application.
-You can do this through the [Dashboard](https://dashboard.heroku.com/).
+You can do this through the [Heroku Dashboard](https://dashboard.heroku.com/).
## Create Runner
First install [Docker Engine](https://docs.docker.com/installation/).
+
To build this project you also need to have [GitLab Runner](https://docs.gitlab.com/runner/).
-You can use public runners available on `gitlab.com`, but you can register your own:
+You can use public runners available on `gitlab.com` or register your own:
```sh
gitlab-runner register \
@@ -72,6 +77,6 @@ gitlab-runner register \
--docker-postgres latest
```
-With the command above, you create a Runner that uses [ruby:2.2](https://hub.docker.com/r/_/ruby/) image and uses [postgres](https://hub.docker.com/r/_/postgres/) database.
+With the command above, you create a Runner that uses the [ruby:2.2](https://hub.docker.com/r/_/ruby/) image and uses a [postgres](https://hub.docker.com/r/_/postgres/) database.
-To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password.
+To access the PostgreSQL database, connect to `host: postgres` as user `postgres` with no password.
diff --git a/doc/ci/examples/test-clojure-application.md b/doc/ci/examples/test-clojure-application.md
index 3b1026d174f..5cda8702b56 100644
--- a/doc/ci/examples/test-clojure-application.md
+++ b/doc/ci/examples/test-clojure-application.md
@@ -1,8 +1,15 @@
+---
+type: tutorial
+---
+
+NOTE: **Note:**
+This document has not been updated recently and could be out of date. For the latest documentation, see the [GitLab CI/CD](../README.md) page and the [GitLab CI/CD Pipeline Configuration Reference](../yaml/README.md).
+
# Test a Clojure application with GitLab CI/CD
-This example will guide you how to run tests in your Clojure application.
+This example will guide you how to run tests on your Clojure application.
-You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://gitlab.com/dzaporozhets/clojure-web-application/builds?scope=all).
+You can view or fork the [example source](https://gitlab.com/dzaporozhets/clojure-web-application) and view the logs of its past [CI jobs](https://gitlab.com/dzaporozhets/clojure-web-application/builds?scope=finished).
## Configure the project
@@ -28,8 +35,9 @@ test:
- lein test
```
-In before script we install JRE and [Leiningen](http://leiningen.org/).
-Sample project uses [migratus](https://github.com/yogthos/migratus) library to manage database migrations.
-So we added database migration as last step of `before_script` section
+In `before_script`, we install JRE and [Leiningen](http://leiningen.org/).
+
+The sample project uses the [migratus](https://github.com/yogthos/migratus) library to manage database migrations, and
+we have added a database migration as the last step of `before_script`.
-You can use public runners available on `gitlab.com` for testing your application with such configuration.
+You can use public runners available on `gitlab.com` for testing your application with this configuration.
diff --git a/doc/ci/examples/test-scala-application.md b/doc/ci/examples/test-scala-application.md
index e1164b8d03a..0e33a1ba060 100644
--- a/doc/ci/examples/test-scala-application.md
+++ b/doc/ci/examples/test-scala-application.md
@@ -1,9 +1,12 @@
+---
+type: tutorial
+---
+
# Test and deploy a Scala application to Heroku
This example demonstrates the integration of GitLab CI with Scala
-applications using SBT. Checkout the example
-[project](https://gitlab.com/gitlab-examples/scala-sbt) and
-[build status](https://gitlab.com/gitlab-examples/scala-sbt/builds).
+applications using SBT. You can view or fork the [example project](https://gitlab.com/gitlab-examples/scala-sbt)
+and view the logs of its past [CI jobs](https://gitlab.com/gitlab-examples/scala-sbt/-/jobs?scope=finished).
## Add `.gitlab-ci.yml` file to project
@@ -41,12 +44,14 @@ deploy:
- dpl --provider=heroku --app=gitlab-play-sample-app --api-key=$HEROKU_API_KEY
```
-The `before_script` installs [SBT](http://www.scala-sbt.org/) and
-displays the version that is being used. The `test` stage executes SBT
-to compile and test the project.
-[scoverage](https://github.com/scoverage/sbt-scoverage) is used as an SBT
+In the above configuration:
+
+- The `before_script` installs [SBT](http://www.scala-sbt.org/) and
+displays the version that is being used.
+- The `test` stage executes SBT to compile and test the project.
+ - [sbt-scoverage](https://github.com/scoverage/sbt-scoverage) is used as an SBT
plugin to measure test coverage.
-The `deploy` stage automatically deploys the project to Heroku using dpl.
+- The `deploy` stage automatically deploys the project to Heroku using dpl.
You can use other versions of Scala and SBT by defining them in
`build.sbt`.
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
index 4a5fda661df..ec25ca1bfc3 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
@@ -3,6 +3,7 @@ author: Alexandre S Hostert
author_gitlab: Hostert
level: beginner
article_type: tutorial
+type: tutorial
date: 2018-02-20
last_updated: 2019-03-06
---
@@ -16,11 +17,11 @@ simultaneous users.
That's why we're hearing so much about Phoenix today.
-In this tutorial, we'll teach you how to set up GitLab CI/CD to build and test a Phoenix
+In this tutorial, we'll teach you how to set up [GitLab CI/CD](../../README.md) to build and test a Phoenix
application.
-_We assume that you know how to create a Phoenix app, run tests locally, and how to work with Git
-and GitLab UI._
+The tutorial assumes that you know how to create a Phoenix app, run tests locally, and how to work with Git
+and the GitLab UI.
## Introduction
diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md
index bd2b9b099f2..ef9f9a9973c 100644
--- a/doc/ci/introduction/index.md
+++ b/doc/ci/introduction/index.md
@@ -177,22 +177,22 @@ according to each stage (Verify, Package, Release).
1. **Verify**:
- Automatically build and test your application with Continuous Integration.
- - Analyze your source code quality with [GitLab Code Quality](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html). **[STARTER]**
- - Determine the performance impact of code changes with [Browser Performance Testing](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html). **[PREMIUM]**
- - Perform a series of tests, such as [Container Scanning](https://docs.gitlab.com/ee/ci/examples/container_scanning.html) **[ULTIMATE]**, [Dependency Scanning](https://docs.gitlab.com/ee/ci/examples/dependency_scanning.html) **[ULTIMATE]**, and [JUnit tests](../junit_test_reports.md).
+ - Analyze your source code quality with [GitLab Code Quality](../../user/project/merge_requests/code_quality.md). **[STARTER]**
+ - Determine the performance impact of code changes with [Browser Performance Testing](../../user/project/merge_requests/browser_performance_testing.md). **[PREMIUM]**
+ - Perform a series of tests, such as [Container Scanning](../../user/application_security/container_scanning/index.md) **[ULTIMATE]**, [Dependency Scanning](../../user/application_security/dependency_scanning/index.md) **[ULTIMATE]**, and [JUnit tests](../junit_test_reports.md).
- Deploy your changes with [Review Apps](../review_apps/index.md) to preview the app changes on every branch.
1. **Package**:
- Store Docker images with [Container Registry](../../user/project/container_registry.md).
- - Store NPM packages with [NPM Registry](https://docs.gitlab.com/ee/user/project/packages/npm_registry.html). **[PREMIUM]**
- - Store Maven artifacts with [Maven Repository](https://docs.gitlab.com/ee/user/project/packages/maven_repository.html). **[PREMIUM]**
+ - Store NPM packages with [NPM Registry](../../user/project/packages/npm_registry.md). **[PREMIUM]**
+ - Store Maven artifacts with [Maven Repository](../../user/project/packages/maven_repository.md). **[PREMIUM]**
1. **Release**:
- Continuous Deployment, automatically deploying your app to production.
- Continuous Delivery, manually click to deploy your app to production.
- Deploy static websites with [GitLab Pages](../../user/project/pages/index.md).
- - Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html). **[PREMIUM]**
- - Deploy your features behind [Feature Flags](https://docs.gitlab.com/ee/user/project/operations/feature_flags.html). **[PREMIUM]**
+ - Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](../../user/project/canary_deployments.md). **[PREMIUM]**
+ - Deploy your features behind [Feature Flags](../../user/project/operations/feature_flags.md). **[PREMIUM]**
- Add release notes to any Git tag with [GitLab Releases](../../user/project/releases/index.md).
- - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html). **[PREMIUM]**
+ - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md). **[PREMIUM]**
- Deploy your application to a production environment in a Kubernetes cluster with [Auto Deploy](../../topics/autodevops/index.md#auto-deploy).
With GitLab CI/CD you can also:
@@ -201,10 +201,13 @@ With GitLab CI/CD you can also:
- Deploy your app to different [environments](../environments.md).
- Install your own [GitLab Runner](https://docs.gitlab.com/runner/).
- [Schedule pipelines](../../user/project/pipelines/schedules.md).
-- Check for app vulnerabilities with [Security Test reports](https://docs.gitlab.com/ee/user/project/merge_requests/#security-reports-ultimate). **[ULTIMATE]**
+- Check for app vulnerabilities with [Security Test reports](../../user/project/merge_requests/index.md#security-reports-ultimate). **[ULTIMATE]**
To see all CI/CD features, navigate back to the [CI/CD index](../README.md).
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+Watch the video [GitLab CI Live Demo](https://www.youtube.com/watch?v=pBe4t1CD8Fc) with a deeper overview of GitLab CI/CD.
+
### Setting up GitLab CI/CD for the first time
To get started with GitLab CI/CD, you need to familiarize yourself
diff --git a/doc/ci/metrics_reports.md b/doc/ci/metrics_reports.md
index b7824402d45..f9cfc0892a7 100644
--- a/doc/ci/metrics_reports.md
+++ b/doc/ci/metrics_reports.md
@@ -9,11 +9,11 @@ Requires GitLab Runner 11.10 and above.
## Overview
-GitLab provides a lot of great reporting tools for [merge requests](../user/project/merge_requests/index.md) - [JUnit reports](./junit_test_reports.md), [codequality](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html), performance tests, etc. While JUnit is a great open framework for tests that "pass" or "fail", it is also important to see other types of metrics from a given change.
+GitLab provides a lot of great reporting tools for [merge requests](../user/project/merge_requests/index.md) - [JUnit reports](junit_test_reports.md), [codequality](../user/project/merge_requests/code_quality.md), performance tests, etc. While JUnit is a great open framework for tests that "pass" or "fail", it is also important to see other types of metrics from a given change.
You can configure your job to use custom Metrics Reports, and GitLab will display a report on the merge request so that it's easier and faster to identify changes without having to check the entire log.
-![Metrics Reports](./img/metrics_reports.png)
+![Metrics Reports](img/metrics_reports.png)
## Use cases
@@ -28,6 +28,11 @@ Consider the following examples of data that can utilize Metrics Reports:
Metrics are read from the metrics report (default: `metrics.txt`). They are parsed and displayed in the MR widget.
+All values are considered strings and string compare is used to find differences between the latest available `metrics` artifact from:
+
+- `master`
+- The feature branch
+
## How to set it up
Add a job that creates a [metrics report](yaml/README.md#artifactsreportsmetrics-premium) (default filename: `metrics.txt`). The file should conform to the [OpenMetrics](https://openmetrics.io/) format.
diff --git a/doc/ci/multi_project_pipelines.md b/doc/ci/multi_project_pipelines.md
index 1091129a9dc..50c8d82602b 100644
--- a/doc/ci/multi_project_pipelines.md
+++ b/doc/ci/multi_project_pipelines.md
@@ -26,6 +26,10 @@ and when hovering or tapping (on touchscreen devices) they will expand and be sh
Multi-project pipelines are useful for larger products that require cross-project inter-dependencies, such as those
adopting a [microservices architecture](https://about.gitlab.com/2016/08/16/trends-in-version-control-land-microservices/).
+For a demonstration of how cross-functional development teams can use cross-pipeline
+triggering to trigger multiple pipelines for different microservices projects, see
+[Cross-project Pipeline Triggering and Visualization](https://about.gitlab.com/handbook/marketing/product-marketing/demo/#cross-project-pipeline-triggering-and-visualization-may-2019---1110).
+
## Use cases
Let's assume you deploy your web app from different projects in GitLab:
@@ -167,10 +171,6 @@ In this scenario, the `UPSTREAM_BRANCH` variable with a value related to the
upstream pipeline will be passed to the `downstream-job` job, and will be available
within the context of all downstream builds.
-### Demos
-
-[A click-through demo of cross-project pipeline is available](https://about.gitlab.com/handbook/marketing/product-marketing/demo/#cross-project-pipeline-triggering-and-visualization-may-2019---1110), demonstrates how cross-functional dev teams use cross-pipeline triggering to trigger multiple pipelines for different microservices projects.
-
### Limitations
Because bridge jobs are a little different to regular jobs, it is not
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index fe64f5ab2e0..df455857dee 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -92,7 +92,7 @@ This means that the value of the variable will be hidden in job logs,
though it must match certain requirements to do so:
- The value must be in a single line.
-- The value must contain only letters, numbers, or underscores.
+- The value must only consist of characters from the Base64 alphabet, defined in [RFC4648](https://tools.ietf.org/html/rfc4648).
- The value must be at least 8 characters long.
- The value must not use variables.
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index 8009b1d5e8a..7d3f39a8c19 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -89,6 +89,14 @@ Supported:
- In `script`, it will work in the following lines of `script`.
- In `after_script`, it will work in following lines of `after_script`.
+Please notice the specific case of `after_script` scripts, that can:
+
+- Only use variables defined before the script within the same `after_script` section.
+- Not use variables defined in `before_script` and `script`.
+
+Both restrictions are caused by the fact, that `after_script` script is executed in a
+[separated shell context](https://docs.gitlab.com/ee/ci/yaml/README.html#before_script-and-after_script).
+
## Persisted variables
NOTE: **Note:**
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index fa4b0378f61..b3aa1bcfad2 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -215,10 +215,20 @@ This can be an array or a multi-line string.
`after_script` is used to define the command that will be run after all
jobs, including failed ones. This has to be an array or a multi-line string.
-The `before_script` and the main `script` are concatenated and run in a single context/container.
-The `after_script` is run separately. The current working directory is set back to
-default. Depending on the executor, changes done outside of the working tree might
-not be visible, e.g. software installed in the `before_script`.
+Script specified in `before_script` is:
+
+- Concatenated with script specified in the main `script`. Job-level `before_script` definition
+ override global-level `before_script` definition when concatenated with `script` definition.
+- Executed together with main `script` script as one script in a single shell context.
+
+Script specified in `after_script`:
+
+- Have a current working directory set back to the default.
+- Is executed in a shell context separated from `before_script` and `script` scripts.
+- Because of separated context, cannot see changes done by scripts defined in `before_script` or `script` scripts:
+ - in shell - for example, command aliases and variables exported in `script` script,
+ - outside of the working tree (depending on the Runner executor) - for example, software installed
+ by a `before_script` or `script` script.
It's possible to overwrite the globally defined `before_script` and `after_script`
if you set it per-job:
@@ -1458,7 +1468,7 @@ combination thereof (`junit: [rspec.xml, test-results/TEST-*.xml]`).
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
-The `codequality` report collects [CodeQuality issues](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html)
+The `codequality` report collects [CodeQuality issues](../../user/project/merge_requests/code_quality.md)
as artifacts.
The collected Code Quality report will be uploaded to GitLab as an artifact and will
@@ -1468,7 +1478,7 @@ be automatically shown in merge requests.
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
-The `sast` report collects [SAST vulnerabilities](https://docs.gitlab.com/ee/user/application_security/sast/index.html)
+The `sast` report collects [SAST vulnerabilities](../../user/application_security/sast/index.md)
as artifacts.
The collected SAST report will be uploaded to GitLab as an artifact and will
@@ -1479,7 +1489,7 @@ dashboards.
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
-The `dependency_scanning` report collects [Dependency Scanning vulnerabilities](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/index.html)
+The `dependency_scanning` report collects [Dependency Scanning vulnerabilities](../../user/application_security/dependency_scanning/index.md)
as artifacts.
The collected Dependency Scanning report will be uploaded to GitLab as an artifact and will
@@ -1490,7 +1500,7 @@ dashboards.
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
-The `container_scanning` report collects [Container Scanning vulnerabilities](https://docs.gitlab.com/ee/user/application_security/container_scanning/index.html)
+The `container_scanning` report collects [Container Scanning vulnerabilities](../../user/application_security/container_scanning/index.md)
as artifacts.
The collected Container Scanning report will be uploaded to GitLab as an artifact and will
@@ -1501,7 +1511,7 @@ dashboards.
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
-The `dast` report collects [DAST vulnerabilities](https://docs.gitlab.com/ee/user/application_security/dast/index.html)
+The `dast` report collects [DAST vulnerabilities](../../user/application_security/dast/index.md)
as artifacts.
The collected DAST report will be uploaded to GitLab as an artifact and will
@@ -1512,7 +1522,7 @@ dashboards.
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
-The `license_management` report collects [Licenses](https://docs.gitlab.com/ee/user/project/merge_requests/license_management.html)
+The `license_management` report collects [Licenses](../../user/project/merge_requests/license_management.md)
as artifacts.
The collected License Management report will be uploaded to GitLab as an artifact and will
@@ -1523,7 +1533,7 @@ dashboards.
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
-The `performance` report collects [Performance metrics](https://docs.gitlab.com/ee//user/project/merge_requests/browser_performance_testing.html)
+The `performance` report collects [Performance metrics](../../user/project/merge_requests/browser_performance_testing.md)
as artifacts.
The collected Performance report will be uploaded to GitLab as an artifact and will
@@ -1716,7 +1726,7 @@ parallel. This value has to be greater than or equal to two (2) and less than or
This creates N instances of the same job that run in parallel. They're named
sequentially from `job_name 1/N` to `job_name N/N`.
-For every job, `CI_NODE_INDEX` and `CI_NODE_TOTAL` [environment variables](../variables/README.html#predefined-environment-variables) are set.
+For every job, `CI_NODE_INDEX` and `CI_NODE_TOTAL` [environment variables](../variables/README.md#predefined-environment-variables) are set.
A simple example:
diff --git a/doc/development/README.md b/doc/development/README.md
index 624665a42d1..d2f09fc01de 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -20,6 +20,7 @@ description: 'Learn how to contribute to GitLab.'
- [Automatic CE->EE merge](automatic_ce_ee_merge.md)
- [Guidelines for implementing Enterprise Edition features](ee_features.md)
- [Security process for developers](https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#security-releases-critical-non-critical-as-a-developer)
+- [Requesting access to Chatops on GitLab.com](chatops_on_gitlabcom.md#requesting-access) (for GitLabbers)
## UX and frontend guides
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index a0e4020da09..5f32cd7eba2 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -1,3 +1,7 @@
+---
+table_display_block: true
+---
+
# GitLab Architecture Overview
## Software delivery
@@ -26,6 +30,21 @@ Gitaly executes git operations from gitlab-shell and the GitLab web app, and pro
You may also be interested in the [production architecture of GitLab.com](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/).
+### Simplified Component Overview
+
+This is a simplified architecture diagram that can be used to
+understand GitLab's architecture.
+
+A complete architecture diagram is available in our
+[component diagram](#component-diagram) below.
+
+![Simplified Component Overview](img/architecture_simplified.png)
+
+<!--
+To update this diagram, GitLab team members can edit this source file:
+https://docs.google.com/drawings/d/1fBzAyklyveF-i-2q-OHUIqDkYfjjxC4mq5shwKSZHLs/edit.
+ -->
+
### Component diagram
```mermaid
@@ -121,7 +140,7 @@ Component statuses are linked to configuration documentation for each component.
| [PgBouncer](#pgbouncer) | Database connection pooling, failover | [⚙][pgbouncer-omnibus] | [❌][pgbouncer-charts] | [❌][pgbouncer-charts] | [✅](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#database-architecture) | ❌ | ❌ | EE Only |
| [Consul](#consul) | Database node discovery, failover | [⚙][consul-omnibus] | [❌][consul-charts] | [❌][consul-charts] | [✅](../user/gitlab_com/index.md#consul) | ❌ | ❌ | EE Only |
| [GitLab self-monitoring: Prometheus](#prometheus) | Time-series database, metrics collection, and query service | [✅][prometheus-omnibus] | [✅][prometheus-charts] | [⚙][prometheus-charts] | [✅](../user/gitlab_com/index.md#prometheus) | ❌ | ❌ | CE & EE |
-| [GitLab self-monitoring: Alertmanager](#alertmanager) | Deduplicates, groups, and routes alerts from Prometheus | [✅][alertmanager-omnibus] | [✅][alertmanager-charts] | [⚙][alertmanager-charts] | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
+| [GitLab self-monitoring: Alertmanager](#alertmanager) | Deduplicates, groups, and routes alerts from Prometheus | [⚙][alertmanager-omnibus] | [✅][alertmanager-charts] | [⚙][alertmanager-charts] | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | ❌ | ❌ | CE & EE |
| [GitLab self-monitoring: Grafana](#grafana) | Metrics dashboard | [⚙][grafana-omnibus] | [⤓][grafana-charts] | [⤓][grafana-charts] | [✅](https://dashboards.gitlab.com/d/RZmbBr7mk/gitlab-triage?refresh=30s) | ❌ | ❌ | CE & EE |
| [GitLab self-monitoring: Sentry](#sentry) | Track errors generated by the GitLab instance | [⤓][sentry-omnibus] | [❌][sentry-charts] | [❌][sentry-charts] | [✅](https://about.gitlab.com/handbook/support/workflows/services/gitlab_com/500_errors.html#searching-sentry) | [⤓][gitlab-yml] | [⤓][gitlab-yml] | CE & EE |
| [GitLab self-monitoring: Jaeger](#jaeger) | View traces generated by the GitLab instance | [❌][jaeger-omnibus] | [❌][jaeger-charts] | [❌][jaeger-charts] | [❌](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4104) | [⤓][jaeger-source] | [⚙][jaeger-gdk] | CE & EE |
@@ -676,7 +695,7 @@ We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/ha
[runner-gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/runner.md
[database-migrations-omnibus]: https://docs.gitlab.com/omnibus/settings/database.html#disabling-automatic-database-migration
[database-migrations-charts]: https://docs.gitlab.com/charts/charts/gitlab/migrations/
-[database-migrations-source]: ../update/upgrading_from_source.md#13-install-libs-migrations-etc
+[database-migrations-source]: ../update/upgrading_from_source.md#14-install-libs-migrations-etc
[certificate-management-omnibus]: https://docs.gitlab.com/omnibus/settings/ssl.html
[certificate-management-charts]: https://docs.gitlab.com/charts/installation/tls.html
[certificate-management-source]: ../install/installation.md#using-https
diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md
new file mode 100644
index 00000000000..c63ec53414c
--- /dev/null
+++ b/doc/development/chatops_on_gitlabcom.md
@@ -0,0 +1,21 @@
+# Chatops on GitLab.com
+
+Chatops on GitLab.com allows GitLabbers to run various automation tasks on GitLab.com using Slack.
+
+## Requesting access
+
+GitLabbers may need access to Chatops on GitLab.com for administration tasks such as:
+
+- Configuring feature flags on staging.
+- Running `EXPLAIN` queries against the GitLab.com production replica.
+
+To request access to Chatops on GitLab.com:
+
+1. Log into <https://ops.gitlab.net/users/sign_in> using the same username as for GitLab.com.
+1. Ask [anyone in the `chatops` project](https://gitlab.com/gitlab-com/chatops/project_members) to add you by running `/chatops run member add <username> gitlab-com/chatops --ops`.
+
+## See also
+
+ - [Chatops Usage](https://docs.gitlab.com/ee/ci/chatops/README.html)
+ - [Understanding EXPLAIN plans](understanding_explain_plans.md)
+ - [Feature Groups](feature_flags.md#feature-groups)
diff --git a/doc/development/fe_guide/frontend_faq.md b/doc/development/fe_guide/frontend_faq.md
index 77f064a21a9..e4225f2bc39 100644
--- a/doc/development/fe_guide/frontend_faq.md
+++ b/doc/development/fe_guide/frontend_faq.md
@@ -25,3 +25,17 @@ document.body.dataset.page
```
Find here the [source code setting the attribute](https://gitlab.com/gitlab-org/gitlab-ce/blob/cc5095edfce2b4d4083a4fb1cdc7c0a1898b9921/app/views/layouts/application.html.haml#L4).
+
+### `modal_copy_button` vs `clipboard_button`
+
+The `clipboard_button` uses the `copy_to_clipboard.js` behaviour, which is
+initialized on page load, so if there are vue-based clipboard buttons that
+don't exist at page load (such as ones in a `GlModal`), they do not have the
+click handlers associated with the clipboard package.
+
+`modal_copy_button` was added that manages an instance of the
+[`clipboard` plugin](https://www.npmjs.com/package/clipboard) specific to
+the instance of that component, which means that clipboard events are
+bound on mounting and destroyed when the button is, mitigating the above
+issue. It also has bindings to a particular container or modal ID
+available, to work with the focus trap created by our GlModal.
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index 8e06aa5d173..9fcd32fddfa 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -18,6 +18,12 @@ To save query compilation at runtime, webpack can directly import `.graphql`
files. This allows webpack to preprocess the query at compile time instead
of the client doing compilation of queries.
+To distinguish queries from mutations and fragments, the following naming convention is recommended:
+
+- `allUsers.query.graphql` for queries;
+- `addUser.mutation.graphql` for mutations;
+- `basicUser.fragment.graphql` for fragments.
+
## Usage in Vue
To use Vue Apollo, import the [Vue Apollo][vue-apollo] plugin as well
diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md
index c871015aaf6..13f0c5cc33e 100644
--- a/doc/development/feature_flags.md
+++ b/doc/development/feature_flags.md
@@ -20,7 +20,7 @@ dynamic (querying the DB etc.).
Once defined in `lib/feature.rb`, you will be able to activate a
feature for a given feature group via the [`feature_group` param of the features API](../api/features.md#set-or-create-a-feature)
-For GitLab.com, team members have access to feature flags through chatops. Only
+For GitLab.com, [team members have access to feature flags through Chatops](chatops_on_gitlabcom.md). Only
percentage gates are supported at this time. Setting a feature to be used 50% of
the time, you should execute `/chatops run feature set my_feature_flag 50`.
diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md
index 81c5f69c7b8..b512d7611d3 100644
--- a/doc/development/git_object_deduplication.md
+++ b/doc/development/git_object_deduplication.md
@@ -10,10 +10,10 @@ GitLab implements Git object deduplication.
## Enabling Git object deduplication via feature flags
-As of GitLab 11.9, Git object deduplication in GitLab is in beta. In this
-document, you can read about the caveats of enabling the feature. Also,
-note that Git object deduplication is limited to forks of public
-projects on hashed repository storage.
+As of GitLab 12.0, Git object deduplication in GitLab is still behind a
+feature flag. In this document, you can read about the effects of
+enabling the feature. Also, note that Git object deduplication is
+limited to forks of public projects on hashed repository storage.
You can enable deduplication globally by setting the `object_pools`
feature flag to `true`:
@@ -51,6 +51,15 @@ configuration. Objects in A that are not in B will remain in A. For this
to work, it is of course critical that **no objects ever get deleted from
B** because A might need them.
+DANGER: **Danger:**
+Do not run `git prune` or `git gc` in pool repositories! This can
+cause data loss in "real" repositories that depend on the pool in
+question.
+
+The danger lies in `git prune`, and `git gc` calls `git prune`. The
+problem is that `git prune`, when running in a pool repository, cannot
+reliable decide if an object is no longer needed.
+
### Git alternates in GitLab: pool repositories
GitLab organizes this object borrowing by creating special **pool
@@ -80,43 +89,10 @@ across a collection of GitLab project repositories at the Git level:
The effectiveness of Git object deduplication in GitLab depends on the
amount of overlap between the pool repository and each of its
-participants. As of GitLab 11.9, we have a somewhat optimistic system.
-The only data that will be deduplicated is the data in the source
-project repository at the time the pool repository is created. That is,
-the data in the source project at the time of the first fork *after* the
-deduplication feature has been enabled.
-
-When we enable the object deduplication feature for
-gitlab.com/gitlab-org/gitlab-ce, which is about 1GB at the time of
-writing, all new forks of that project would be 1GB smaller than they
-would have been without Git object deduplication. So even in its current
-optimistic form, we expect Git object deduplication in GitLab to make a
-difference.
-
-However, if a lot of Git objects get added to the project repositories
-in a pool after the pool repository was created these new Git objects
-will currently (GitLab 11.9) not get deduplicated. Over time, the
-deduplication factor of the pool will get worse and worse.
-
-As an extreme example, if we create an empty repository A, and fork that
-to repository B, behind the scenes we get an object pool P with no
-objects in it at all. If we then push 1GB of Git data to A, and push the
-same Git data to B, it will not get deduplicated, because that data was
-not in A at the time P was created.
-
-This also matters in less extreme examples. Consider a pool P with
-source project A and 500 active forks B1, B2,...,B500. Suppose,
-optimistically, that the forks are fully deduplicated at the start of
-our scenario. Now some time passes and 200MB of new Git data gets added
-to project A. Because of the forking workflow, this data makes also its way
-into the forks B1, ..., B500. That means we would now have 100GB of Git
-data sitting around (500 \* 200MB) across the forks, that could have
-been deduplicated. But because of the way we do deduplication this new
-data will not be deduplicated.
-
-> TODO Add periodic maintenance of object pools to prevent gradual loss
-> of deduplication over time.
-> https://gitlab.com/groups/gitlab-org/-/epics/524
+participants. Each time garbage collection runs on the source project,
+Git objects from the source project will get migrated to the pool
+repository. One by one, as garbage collection runs, other member
+projects will benefit from the new objects that got added to the pool.
## SQL model
@@ -136,6 +112,9 @@ are as follows:
- a `PoolRepository` has exactly one "source `Project`"
(`pool.source_project`)
+> TODO Fix invalid SQL data for pools created prior to GitLab 11.11
+> https://gitlab.com/gitlab-org/gitaly/issues/1653.
+
### Assumptions
- All repositories in a pool must use [hashed
@@ -146,10 +125,6 @@ are as follows:
The Git alternates mechanism relies on direct disk access across
multiple repositories, and we can only assume direct disk access to
be possible within a Gitaly storage shard.
-- All project repositories in a pool must have "Public" visibility in
- GitLab at the time they join. There are gotchas around visibility of
- Git objects across alternates links. This restriction is a defense
- against accidentally leaking private Git data.
- The only two ways to remove a member project from a pool are (1) to
delete the project or (2) to move the project to another Gitaly
storage shard.
@@ -187,17 +162,14 @@ are as follows:
### Consequences
- If a normal Project participating in a pool gets moved to another
- Gitaly storage shard, its "belongs to PoolRepository" relation must
+ Gitaly storage shard, its "belongs to PoolRepository" relation will
be broken. Because of the way moving repositories between shard is
implemented, we will automatically get a fresh self-contained copy
of the project's repository on the new storage shard.
- If the source project of a pool gets moved to another Gitaly storage
- shard or is deleted, we may have to break the "PoolRepository has
- one source Project" relation?
-
-> TODO What happens, or should happen, if a source project changes
-> visibility, is deleted, or moves to another storage shard?
-> https://gitlab.com/gitlab-org/gitaly/issues/1488
+ shard or is deleted the "source project" relation is not broken.
+ However, as of GitLab 12.0 a pool will not fetch from a source
+ unless the source is on the same Gitaly shard.
## Consistency between the SQL pool relation and Gitaly
@@ -209,16 +181,8 @@ repository and a pool.
### Pool existence
If GitLab thinks a pool repository exists (i.e. it exists according to
-SQL), but it does not on the Gitaly server, then certain RPC calls that
-take the object pool as an argument will fail.
-
-> TODO What happens if SQL says the pool repo exists but Gitaly says it
-> does not? https://gitlab.com/gitlab-org/gitaly/issues/1533
-
-If GitLab thinks a pool does not exist, while it does exist on disk,
-that has no direct consequences on its own. However, if other
-repositories on disk borrow objects from this unknown pool repository
-then we risk data loss, see below.
+SQL), but it does not on the Gitaly server, then it will be created on
+the fly by Gitaly.
### Pool relation existence
@@ -226,26 +190,19 @@ There are three different things that can go wrong here.
#### 1. SQL says repo A belongs to pool P but Gitaly says A has no alternate objects
-In this case, we miss out on disk space savings but all RPC's on A itself
-will function fine. As long as Git can find all its objects, it does not
-matter exactly where those objects are.
+In this case, we miss out on disk space savings but all RPC's on A
+itself will function fine. The next time garbage collection runs on A,
+the alternates connection gets established in Gitaly. This is done by
+`Projects::GitDeduplicationService` in gitlab-rails.
#### 2. SQL says repo A belongs to pool P1 but Gitaly says A has alternate objects in pool P2
-If we are not careful, this situation can lead to data loss. During some
-operations (repository maintenance), GitLab will try to re-link A to its
-pool P1. If this clobbers the existing link to P2, then A will loose Git
-objects and become invalid.
-
-Also, keep in mind that if GitLab's database got messed up, it may not
-even know that P2 exists.
-
-> TODO Ensure that Gitaly will not clobber existing, unexpected
-> alternates links. https://gitlab.com/gitlab-org/gitaly/issues/1534
+In this case `Projects::GitDeduplicationService` will throw an exception.
#### 3. SQL says repo A does not belong to any pool but Gitaly says A belongs to P
-This has the same data loss possibility as scenario 2 above.
+In this case `Projects::GitDeduplicationService` will try to
+"re-duplicate" the repository A using the DisconnectGitAlternates RPC.
## Git object deduplication and GitLab Geo
diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md
index 4ef20d5fd74..c8beb808a54 100644
--- a/doc/development/gitaly.md
+++ b/doc/development/gitaly.md
@@ -245,7 +245,7 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag.
// go implementation
} else {
findAllTagsRequests.WithLabelValues("ruby").Inc()
- // ruby impelmentation
+ // ruby implementation
}
```
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 8f23ad4732a..9fb8ea542d9 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -174,7 +174,7 @@ For example use `%{created_at}` in Ruby but `%{createdAt}` in JavaScript.
# => When size == 2: 'There are 2 mice.'
```
- Avoid using `%d` or count variables in sigular strings. This allows more natural translation in some languages.
+ Avoid using `%d` or count variables in singular strings. This allows more natural translation in some languages.
- In JavaScript:
@@ -332,7 +332,7 @@ Errors in `locale/zh_HK/gitlab.po`:
Syntax error in msgstr
Syntax error in message_line
There should be only whitespace until the end of line after the double quote character of a message text.
- Parseing result before error: '{:msgid=>["", "You are going to remove %{project_name_with_namespace}.\\n", "Removed project CANNOT be restored!\\n", "Are you ABSOLUTELY sure?"]}'
+ Parsing result before error: '{:msgid=>["", "You are going to remove %{project_name_with_namespace}.\\n", "Removed project CANNOT be restored!\\n", "Are you ABSOLUTELY sure?"]}'
SimplePoParser filtered backtrace: SimplePoParser::ParserError
Errors in `locale/zh_TW/gitlab.po`:
1 pipeline
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index fb5cfb6c157..35c5b155594 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -130,7 +130,7 @@ are very appreciative of the work done by translators and proofreaders!
your previous translations by [GitLab team members](https://about.gitlab.com/team/)
or [Core team members](https://about.gitlab.com/core-team/) who are fluent in
the language or current proofreaders.
- - When a request is made for the first proofreader for a lanuguage and there are no [GitLab team members](https://about.gitlab.com/team/)
+ - When a request is made for the first proofreader for a language and there are no [GitLab team members](https://about.gitlab.com/team/)
or [Core team members](https://about.gitlab.com/core-team/) who speak the language, we will request links to previous translation work in other communities or projects.
diff --git a/doc/development/img/architecture_simplified.png b/doc/development/img/architecture_simplified.png
new file mode 100644
index 00000000000..1698c167c5e
--- /dev/null
+++ b/doc/development/img/architecture_simplified.png
Binary files differ
diff --git a/doc/development/new_fe_guide/development/accessibility.md b/doc/development/new_fe_guide/development/accessibility.md
index 8420a504ec4..81a29170129 100644
--- a/doc/development/new_fe_guide/development/accessibility.md
+++ b/doc/development/new_fe_guide/development/accessibility.md
@@ -21,6 +21,7 @@ On the other hand, if an icon is crucial to understand the context we should do
## Form inputs
In forms we should use the `for` attribute in the label statement:
+
```
<div>
<label for="name">Fill in your name:</label>
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index 82439c94c5a..71e3b7740cb 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -17,7 +17,7 @@ our test design. We can find some helpful heuristics documented in the Handbook
## Run tests against MySQL
-By default, tests are only run againts PostgreSQL, but you can run them on
+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 |
diff --git a/doc/development/testing_guide/end_to_end/dynamic_element_validation.md b/doc/development/testing_guide/end_to_end/dynamic_element_validation.md
new file mode 100644
index 00000000000..f7b3ca8bc89
--- /dev/null
+++ b/doc/development/testing_guide/end_to_end/dynamic_element_validation.md
@@ -0,0 +1,113 @@
+# Dynamic Element Validation
+
+We devised a solution to solve common test automation problems such as the dreaded `NoSuchElementException`.
+
+Other problems that dynamic element validations solve are...
+
+- When we perform an action with the mouse, we expect something to occur.
+- When our test is navigating to (or from) a page, we ensure that we are on the page we expect before
+test continuation.
+
+## How it works
+
+We interpret user actions on the page to have some sort of effect. These actions are
+
+- [Navigation](#navigation)
+- [Clicks](#clicks)
+
+### Navigation
+
+When a page is navigated to, there are elements that will always appear on the page unconditionally.
+
+Dynamic element validation is instituted when using
+
+```ruby
+Runtime::Browser.visit(:gitlab, Some::Page)
+```
+
+### Clicks
+
+When we perform a click within our tests, we expect something to occur. That something could be a component to now
+appear on the webpage, or the test to navigate away from the page entirely.
+
+Dynamic element validation is instituted when using
+
+```ruby
+click_element :my_element, Some::Page
+```
+
+### Required Elements
+
+#### Definition
+
+First it is important to define what a "required element" is.
+
+Simply put, a required element is a visible HTML element that appears on a UI component without any user input.
+
+"Visible" can be defined as
+
+- Not having any CSS preventing its display. E.g.: `display: none` or `width: 0px; height: 0px;`
+- Being able to be interacted with by the user
+
+"UI component" can be defined as
+
+- Anything the user sees
+- A button, a text field
+- A layer that sits atop the page
+
+#### Application
+
+Requiring elements is very easy. By adding `required: true` as a parameter to an `element`, you've now made it
+a requirement that the element appear on the page upon navigation.
+
+## Examples
+
+Given ...
+
+```ruby
+class MyPage < Page::Base
+ view 'app/views/view.html.haml' do
+ element :my_element, required: true
+ element :another_element, required: true
+ element :conditional_element
+ end
+
+ def open_layer
+ click_element :my_element, Layer::MyLayer
+ end
+end
+
+class Layer < Page::Component
+ view 'app/views/mylayer/layer.html.haml' do
+ element :message_content, required: true
+ end
+end
+```
+
+### Navigating
+
+Given the [source](#examples) ...
+
+```ruby
+Runtime::Browser.visit(:gitlab, Page::MyPage)
+
+execute_stuff
+```
+
+will invoke GitLab QA to scan `MyPage` for `my_element` and `another_element` to be on the page before continuing to
+`execute_stuff`
+
+### Clicking
+
+Given the [source](#examples) ...
+
+```ruby
+def open_layer
+ click_element :my_element, Layer::MyLayer
+end
+```
+
+will invoke GitLab QA to ensure that `message_content` appears on
+the Layer upon clicking `my_element`.
+
+This will imply that the Layer is indeed rendered before we continue our test.
diff --git a/doc/development/testing_guide/end_to_end/page_objects.md b/doc/development/testing_guide/end_to_end/page_objects.md
index d0de33892c4..73e1fd862c1 100644
--- a/doc/development/testing_guide/end_to_end/page_objects.md
+++ b/doc/development/testing_guide/end_to_end/page_objects.md
@@ -82,15 +82,17 @@ module Page
end
# ...
+ end
end
end
```
-The `view` DSL method declares the filename of the view where an
-`element` is implemented.
+### Defining Elements
+
+The `view` DSL method will correspond to the rails View, partial, or vue component that renders the elements.
The `element` DSL method in turn declares an element for which a corresponding
-`qa-element-name-dasherized` CSS class need to be added to the view file.
+`qa-element-name-dasherized` CSS class will need to be added to the view file.
You can also define a value (String or Regexp) to match to the actual view
code but **this is deprecated** in favor of the above method for two reasons:
@@ -115,6 +117,37 @@ view 'app/views/my/view.html.haml' do
end
```
+### Adding Elements to a View
+
+Given the following elements...
+
+```ruby
+view 'app/views/my/view.html.haml' do
+ element :login_field
+ element :password_field
+ element :sign_in_button
+end
+```
+
+To add these elements to the view, you must change the rails View, partial, or vue component by adding a `qa-element-descriptor` class
+for each element defined.
+
+In our case, `qa-login-field`, `qa-password-field` and `qa-sign-in-button`
+
+**app/views/my/view.html.haml**
+
+```haml
+= f.text_field :login, class: "form-control top qa-login-field", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required."
+= f.password_field :password, class: "form-control bottom qa-password-field", required: true, title: "This field is required."
+= f.submit "Sign in", class: "btn btn-success qa-sign-in-button"
+```
+
+Things to note:
+
+- The CSS class must be `kebab-cased` (separated with hyphens "`-`")
+- If the element appears on the page unconditionally, add `required: true` to the element. See
+[Dynamic element validation](dynamic_element_validation.md)
+
## Running the test locally
During development, you can run the `qa:selectors` test by running
diff --git a/doc/development/testing_guide/end_to_end/quick_start_guide.md b/doc/development/testing_guide/end_to_end/quick_start_guide.md
index afe76acf9c9..521e3e56e7a 100644
--- a/doc/development/testing_guide/end_to_end/quick_start_guide.md
+++ b/doc/development/testing_guide/end_to_end/quick_start_guide.md
@@ -220,7 +220,7 @@ As the pre-conditions for our test suite, the things that needs to happen before
- The user logging in;
- A premium license already being set;
- A project being created with an issue and labels already set;
-- The issue page being opened with only one scoped label applied to the it.
+- The issue page being opened with only one scoped label applied to it.
> When running end-to-end tests as part of the GitLab's continuous integration process [a license is already set as an environment variable](https://gitlab.com/gitlab-org/gitlab-ee/blob/1a60d926740db10e3b5724713285780a4f470531/qa/qa/ee/strategy.rb#L20). For running tests locally you can set up such license by following the document [what tests can be run?](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md#supported-remote-grid-environment-variables), based on the [supported GitLab environment variables](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md#supported-gitlab-environment-variables).
@@ -359,10 +359,12 @@ Now, let's make it possible to create an issue resource through the API.
First, in the [issue resource](https://gitlab.com/gitlab-org/gitlab-ee/blob/d3584e80b4236acdf393d815d604801573af72cc/qa/qa/resource/issue.rb), let's expose its labels attribute.
-Add the following `attribute :labels` right below the [`attribute :title`](https://gitlab.com/gitlab-org/gitlab-ee/blob/d3584e80b4236acdf393d815d604801573af72cc/qa/qa/resource/issue.rb#L15).
+Add the following `attribute :labels` right above the [`attribute :title`](https://gitlab.com/gitlab-org/gitlab-ee/blob/d3584e80b4236acdf393d815d604801573af72cc/qa/qa/resource/issue.rb#L15).
> This line is needed to allow for labels to be automatically added to an issue when fabricating it via API.
+> We add the new line above the existing attribute to keep them alphabetically organized.
+
Next, add the following code right below the [`fabricate!`](https://gitlab.com/gitlab-org/gitlab-ee/blob/d3584e80b4236acdf393d815d604801573af72cc/qa/qa/resource/issue.rb#L27) method.
```ruby
@@ -376,8 +378,8 @@ end
def api_post_body
{
- title: title,
- labels: [labels]
+ labels: [labels],
+ title: title
}
end
```
@@ -392,7 +394,7 @@ By defining the `api_post_path` method, we allow the [`ApiFabricator`](https://g
By defining the `api_post_body` method, we allow the [`ApiFabricator.api_post`](https://gitlab.com/gitlab-org/gitlab-ee/blob/a9177ca1812bac57e2b2fa4560e1d5dd8ffac38b/qa/qa/resource/api_fabricator.rb#L68) method to know which data to send when making the `POST` request.
-> Notice that we pass both `title` and `labels` attributes in the `api_post_body`, where `labels` receives an array of labels, and [`title` is required](https://docs.gitlab.com/ee/api/issues.html#new-issue).
+> Notice that we pass both `labels` and `title` attributes in the `api_post_body`, where `labels` receives an array of labels, and [`title` is required](https://docs.gitlab.com/ee/api/issues.html#new-issue). Also, notice that we keep them alphabetically organized.
**Label resource**
@@ -417,8 +419,8 @@ end
def api_post_body
{
- name: @title,
- color: @color
+ color: @color,
+ name: @title
}
end
```
@@ -431,13 +433,13 @@ By defining the `api_post_path` method, we allow for the [`ApiFabricator `](http
By defining the `api_post_body` method, we we allow for the [`ApiFabricator.api_post`](https://gitlab.com/gitlab-org/gitlab-ee/blob/a9177ca1812bac57e2b2fa4560e1d5dd8ffac38b/qa/qa/resource/api_fabricator.rb#L68) method to know which data to send when making the `POST` request.
-> Notice that we pass both `name` and `color` attributes in the `api_post_body` since [those are required](https://docs.gitlab.com/ee/api/labels.html#create-a-new-label).
+> Notice that we pass both `color` and `name` attributes in the `api_post_body` since [those are required](https://docs.gitlab.com/ee/api/labels.html#create-a-new-label). Also, notice that we keep them alphabetically organized.
### 8. Page Objects
Page Objects are used in end-to-end tests for maintenance reasons, where a page's elements and methods are defined to be reused in any test.
-> Page Objects are auto-loaded in the `qa/qa.rb` file and available in all the test files (`*_spec.rb`).
+> Page Objects are auto-loaded in the [`qa/qa.rb`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/qa/qa.rb) file and available in all the test files (`*_spec.rb`).
Take a look at the [Page Objects] documentation.
@@ -487,25 +489,25 @@ Let's now update the Issue Page Object.
The file we will have to change is the [Issue Page Object](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/qa/qa/page/project/issue/show.rb).
-First, add the following code right below the definition of an already implemented view:
+First, add the following code right below the definition of an already implemented view (keep in mind that view's definitions and their elements should be alphabetically ordered):
```ruby
-view 'app/views/shared/issuable/_sidebar.html.haml' do
- element :labels_block
- element :edit_link_labels
- element :dropdown_menu_labels
-end
-
view 'app/helpers/dropdowns_helper.rb' do
element :dropdown_input_field
end
+
+view 'app/views/shared/issuable/_sidebar.html.haml' do
+ element :dropdown_menu_labels
+ element :edit_link_labels
+ element :labels_block
+end
```
Similarly to what we did before, let's first change the Page Object even without the elements being defined in the view (`_sidebar.html.haml`) and the `dropdowns_helper.rb` files, and later we will update them by adding the appropriate CSS selectors.
Now, let's implement the methods `select_labels_and_refresh` and `text_of_labels_block`.
-Somewhere between the definition of the views and the private methods, add the following snippet of code:
+Somewhere between the definition of the views and the private methods, add the following snippet of code (these should also be alphabetically ordered for organization reasons):
```ruby
def select_labels_and_refresh(labels)
@@ -545,11 +547,11 @@ Now let's change the view and the `dropdowns_helper` files to add the selectors
In the [app/views/shared/issuable/_sidebar.html.haml](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/app/views/shared/issuable/_sidebar.html.haml) file, on [line 105 ](https://gitlab.com/gitlab-org/gitlab-ee/blob/84043fa72ca7f83ae9cde48ad670e6d5d16501a3/app/views/shared/issuable/_sidebar.html.haml#L105), add an extra class `qa-edit-link-labels`.
-The code should look like this: `= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link qa-edit-link-labels float-right'`.
+The code should look like this: `= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right qa-edit-link-labels'`.
In the same file, on [line 121](https://gitlab.com/gitlab-org/gitlab-ee/blob/84043fa72ca7f83ae9cde48ad670e6d5d16501a3/app/views/shared/issuable/_sidebar.html.haml#L121), add an extra class `.qa-dropdown-menu-labels`.
-The code should look like this: `.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.qa-dropdown-menu-labels.dropdown-menu-selectable`.
+The code should look like this: `.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable.qa-dropdown-menu-labels`.
In the [`dropdowns_helper.rb`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/app/helpers/dropdowns_helper.rb) file, on [line 94](https://gitlab.com/gitlab-org/gitlab-ee/blob/99e51a374f2c20bee0989cac802e4b5621f72714/app/helpers/dropdowns_helper.rb#L94), add an extra class `qa-dropdown-input-field`.
@@ -557,7 +559,7 @@ The code should look like this: `filter_output = search_field_tag search_id, nil
> Classes starting with `qa-` are used for testing purposes only, and by defining such classes in the elements we add **testability** in the application.
-> When defining a class like `qa-labels-block`, it is transformed into `:labels_block` for usage in the Page Objects. So, `qa-edit-link-labels` is tranformed into `:edit_link_labels`, `qa-dropdown-menu-labels` is transformed into `:dropdown_menu_labels`, and `qa-dropdown-input-field` is transformed into `:dropdown_input_field`. Also, we use a [sanity test](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa/qa/page#how-did-we-solve-fragile-tests-problem) to check that defined elements have their respective `qa-` selectors in the specified views.
+> When defining a class like `qa-labels-block`, it is transformed into `:labels_block` for usage in the Page Objects. So, `qa-edit-link-labels` is transformed into `:edit_link_labels`, `qa-dropdown-menu-labels` is transformed into `:dropdown_menu_labels`, and `qa-dropdown-input-field` is transformed into `:dropdown_input_field`. Also, we use a [sanity test](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa/qa/page#how-did-we-solve-fragile-tests-problem) to check that defined elements have their respective `qa-` selectors in the specified views.
> We did not define the `qa-labels-block` class in the `app/views/shared/issuable/_sidebar.html.haml` file because it was already there to be used.
@@ -565,7 +567,7 @@ The code should look like this: `filter_output = search_field_tag search_id, nil
The last thing that we have to do is to update `QA::Page::Base` class to add the `send_keys_to_element` method on it.
-Add the following snippet of code somewhere where class methods are defined:
+Add the following snippet of code somewhere where class methods are defined (remember to organize methods alphabetically, and if you see a place where this standard is not being followed, it would be helpful if you could rearrange it):
```ruby
def send_keys_to_element(name, keys)
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
index b5fde92a23d..63b7b97c32f 100644
--- a/doc/development/testing_guide/review_apps.md
+++ b/doc/development/testing_guide/review_apps.md
@@ -160,6 +160,78 @@ secure note named **gitlab-{ce,ee} Review App's root password**.
`review-qa-raise-e-12chm0-migrations.1-nqwtx`.
1. Click on the `Container logs` link.
+### Troubleshoot a pending `dns-gitlab-review-app-external-dns` Deployment
+
+#### Finding the problem
+
+[In the past](https://gitlab.com/gitlab-org/gitlab-ce/issues/62834), it happened
+that the `dns-gitlab-review-app-external-dns` Deployment was in a pending state,
+effectively preventing all the Review Apps from getting a DNS record assigned,
+making them unreachable via domain name.
+
+This in turn prevented other components of the Review App to properly start
+(e.g. `gitlab-runner`).
+
+After some digging, we found that new mounts were failing, when being performed
+with transient scopes (e.g. pods) of `systemd-mount`:
+
+```
+MountVolume.SetUp failed for volume "dns-gitlab-review-app-external-dns-token-sj5jm" : mount failed: exit status 1
+Mounting command: systemd-run
+Mounting arguments: --description=Kubernetes transient mount for /var/lib/kubelet/pods/06add1c3-87b4-11e9-80a9-42010a800107/volumes/kubernetes.io~secret/dns-gitlab-review-app-external-dns-token-sj5jm --scope -- mount -t tmpfs tmpfs /var/lib/kubelet/pods/06add1c3-87b4-11e9-80a9-42010a800107/volumes/kubernetes.io~secret/dns-gitlab-review-app-external-dns-token-sj5jm
+Output: Failed to start transient scope unit: Connection timed out
+```
+
+This probably happened because the GitLab chart creates 67 resources, leading to
+a lot of mount points being created on the underlying GCP node.
+
+The [underlying issue seems to be a `systemd` bug](https://github.com/kubernetes/kubernetes/issues/57345#issuecomment-359068048)
+that was fixed in `systemd` `v237`. Unfortunately, our GCP nodes are currently
+using `v232`.
+
+For the record, the debugging steps to find out this issue were:
+
+1. Switch kubectl context to review-apps-ce (we recommend using [kubectx](https://kubectx.dev/))
+1. `kubectl get pods | grep dns`
+1. `kubectl describe pod <pod name>` & confirm exact error message
+1. Web search for exact error message, following rabbit hole to [a relevant kubernetes bug report](https://github.com/kubernetes/kubernetes/issues/57345)
+1. Access the node over SSH via the GCP console (**Computer Engine > VM
+ instances** then click the "SSH" button for the node where the `dns-gitlab-review-app-external-dns` pod runs)
+1. In the node: `systemctl --version` => systemd 232
+1. Gather some more information:
+ - `mount | grep kube | wc -l` => e.g. 290
+ - `systemctl list-units --all | grep -i var-lib-kube | wc -l` => e.g. 142
+1. Check how many pods are in a bad state:
+ - Get all pods running a given node: `kubectl get pods --field-selector=spec.nodeName=NODE_NAME`
+ - Get all the `Running` pods on a given node: `kubectl get pods --field-selector=spec.nodeName=NODE_NAME | grep Running`
+ - Get all the pods in a bad state on a given node: `kubectl get pods --field-selector=spec.nodeName=NODE_NAME | grep -v 'Running' | grep -v 'Completed'`
+
+#### Solving the problem
+
+To resolve the problem, we needed to (forcibly) drain some nodes:
+
+1. Try a normal drain on the node where the `dns-gitlab-review-app-external-dns`
+ pod runs so that Kubernetes automatically move it to another node: `kubectl drain NODE_NAME`
+1. If that doesn't work, you can also perform a forcible "drain" the node by removing all pods: `kubectl delete pods --field-selector=spec.nodeName=NODE_NAME`
+1. In the node:
+ - Perform `systemctl daemon-reload` to remove the dead/inactive units
+ - If that doesn't solve the problem, perform a hard reboot: `sudo systemctl reboot`
+1. Uncordon any cordoned nodes: `kubectl uncordon NODE_NAME`
+
+In parallel, since most Review Apps were in a broken state, we deleted them to
+clean up the list of non-`Running` pods.
+Following is a command to delete Review Apps based on their last deployment date
+(current date was June 6th at the time) with
+
+```
+helm ls -d | grep "Jun 4" | cut -f1 | xargs helm delete --purge
+```
+
+#### Mitigation steps taken to avoid this problem in the future
+
+We've created a new node pool with smaller machines so that it's less likely
+that a machine will hit the "too many mount points" problem in the future.
+
## Frequently Asked Questions
**Isn't it too much to trigger CNG image builds on every test run? This creates
@@ -172,11 +244,11 @@ thousands of unused Docker images.**
**How big are the Kubernetes clusters (`review-apps-ce` and `review-apps-ee`)?**
> The clusters are currently set up with a single pool of preemptible nodes,
- with a minimum of 1 node and a maximum of 50 nodes.
+ with a minimum of 1 node and a maximum of 500 nodes.
**What are the machine running on the cluster?**
- > We're currently using `n1-standard-16` (16 vCPUs, 60 GB memory) machines.
+ > We're currently using `n1-standard-1` (1 vCPU, 3.75 GB memory) machines.
**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/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md
index 2ef8b3148e4..bfbb7be70e3 100644
--- a/doc/development/understanding_explain_plans.md
+++ b/doc/development/understanding_explain_plans.md
@@ -654,6 +654,7 @@ and related tools such as:
- <https://explain.depesz.com/>
- <http://tatiyants.com/postgres-query-plan-visualization/>
+
## Producing query plans
There are a few ways to get the output of a query plan. Of course you
@@ -683,9 +684,9 @@ Execution time: 0.113 ms
### Chatops
-GitLab employees can also use our chatops solution, available in Slack using the
-`/chatops` slash command. You can use chatops to get a query plan by running the
-following:
+[GitLab employees can also use our chatops solution, available in Slack using the
+`/chatops` slash command](chatops_on_gitlabcom.md).
+You can use chatops to get a query plan by running the following:
```
/chatops run explain SELECT COUNT(*) FROM projects WHERE visibility_level IN (0, 20)
diff --git a/doc/gitlab-basics/README.md b/doc/gitlab-basics/README.md
index 00ac6d6b650..0c268eff9f1 100644
--- a/doc/gitlab-basics/README.md
+++ b/doc/gitlab-basics/README.md
@@ -24,6 +24,7 @@ The following are guides to basic GitLab functionality:
- [Add an image](add-image.md), to add new images to a project's repository.
- [Create an issue](../user/project/issues/create_new_issue.md), to start collaborating within a project.
- [Create a merge request](add-merge-request.md), to request changes made in a branch be merged into a project's repository.
+- See how these features come together in the [GitLab Flow introduction video](https://youtu.be/InKNIvky2KE) and [GitLab Flow page](../workflow/gitlab_flow.md).
## Git basics
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 10436a15a9e..eb484dde545 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -250,11 +250,11 @@ page](https://golang.org/dl).
# Remove former Go installation folder
sudo rm -rf /usr/local/go
-curl --remote-name --progress https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
-echo 'fa1b0e45d3b647c252f51f5e1204aba049cde4af177ef9f2181f43004f901035 go1.10.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
- sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz
+curl --remote-name --progress https://dl.google.com/go/go1.11.10.linux-amd64.tar.gz
+echo 'aefaa228b68641e266d1f23f1d95dba33f17552ba132878b65bb798ffa37e6d0 go1.11.10.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.11.10.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
-rm go1.10.3.linux-amd64.tar.gz
+rm go1.11.10.linux-amd64.tar.gz
```
## 4. Node
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 107d48fb90c..ee3d17704a2 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -168,14 +168,14 @@ CREATE EXTENSION postgres_fdw;
## Unicorn Workers
-It's possible to increase the amount of unicorn workers and this will usually help to reduce the response time of the applications and increase the ability to handle parallel requests.
-
For most instances we recommend using: CPU cores + 1 = unicorn workers.
So for a machine with 2 cores, 3 unicorn workers is ideal.
For all machines that have 2GB and up we recommend a minimum of three unicorn workers.
If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive swapping.
+As long as you have enough available CPU and memory capacity, it's okay to increase the number of unicorn workers and this will usually help to reduce the response time of the applications and increase the ability to handle parallel requests.
+
To change the Unicorn workers when you have the Omnibus package (which defaults to the recommendation above) please see [the Unicorn settings in the Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/settings/unicorn.html).
## Redis and Sidekiq
diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md
index 0a037b3876b..6064c417900 100644
--- a/doc/integration/elasticsearch.md
+++ b/doc/integration/elasticsearch.md
@@ -192,9 +192,6 @@ Performing asynchronous indexing, as this will describe, will generate a lot of
Make sure to prepare for this task by either [Horizontally Scaling](../administration/high_availability/README.md#basic-scaling)
or creating [extra sidekiq processes](../administration/operations/extra_sidekiq_processes.md)
-NOTE: **Note**:
-After indexing the repositories asynchronously, you **MUST** index the database to be able to search.
-
Configure Elasticsearch's host and port in **Admin > Settings > Integrations**. Then create empty indexes using one of the following commands:
```sh
@@ -217,78 +214,49 @@ curl --request PUT localhost:9200/gitlab-production/_settings --data '{
} }'
```
-Then enable Elasticsearch indexing and run repository indexing tasks:
+Then enable Elasticsearch indexing and run project indexing tasks:
```sh
# Omnibus installations
-sudo gitlab-rake gitlab:elastic:index_repositories_async
+sudo gitlab-rake gitlab:elastic:index_projects
# Installations from source
-bundle exec rake gitlab:elastic:index_repositories_async RAILS_ENV=production
+bundle exec rake gitlab:elastic:index_projects RAILS_ENV=production
```
-This enqueues a number of Sidekiq jobs to index your existing repositories.
-You can view the jobs in the admin panel (they are placed in the `elastic_batch_project_indexer`)
+This enqueues a Sidekiq job for each project that needs to be indexed.
+You can view the jobs in the admin panel (they are placed in the `elastic_indexer`
queue), or you can query indexing status using a rake task:
```sh
# Omnibus installations
-sudo gitlab-rake gitlab:elastic:index_repositories_status
+sudo gitlab-rake gitlab:elastic:index_projects_status
# Installations from source
-bundle exec rake gitlab:elastic:index_repositories_status RAILS_ENV=production
+bundle exec rake gitlab:elastic:index_projects_status RAILS_ENV=production
Indexing is 65.55% complete (6555/10000 projects)
```
-By default, one job is created for every 300 projects. For large numbers of
-projects, you may wish to increase the batch size, by setting the `BATCH`
-environment variable.
-
-You can also run the initial indexing synchronously - this is most useful if
-you have a small number of projects or need finer-grained control over indexing
-than Sidekiq permits:
+If you want to limit the index to a range of projects you can provide the
+`ID_FROM` and `ID_TO` parameters:
```sh
# Omnibus installations
-sudo gitlab-rake gitlab:elastic:index_repositories
+sudo gitlab-rake gitlab:elastic:index_projects ID_FROM=1001 ID_TO=2000
# Installations from source
-bundle exec rake gitlab:elastic:index_repositories RAILS_ENV=production
-```
-
-It might take a while depending on how big your Git repositories are.
-
-If you want to run several tasks in parallel (probably in separate terminal
-windows) you can provide the `ID_FROM` and `ID_TO` parameters:
-
-```sh
-# Omnibus installations
-sudo gitlab-rake gitlab:elastic:index_repositories ID_FROM=1001 ID_TO=2000
-
-# Installations from source
-bundle exec rake gitlab:elastic:index_repositories ID_FROM=1001 ID_TO=2000 RAILS_ENV=production
+bundle exec rake gitlab:elastic:index_projects ID_FROM=1001 ID_TO=2000 RAILS_ENV=production
```
Where `ID_FROM` and `ID_TO` are project IDs. Both parameters are optional.
-As an example, if you have 3,000 repositories and you want to run three separate indexing tasks, you might run:
+The above examples will index all projects starting with ID `1001` up to (and including) ID `2000`.
-```sh
-# Omnibus installations
-sudo gitlab-rake gitlab:elastic:index_repositories ID_TO=1000
-sudo gitlab-rake gitlab:elastic:index_repositories ID_FROM=1001 ID_TO=2000
-sudo gitlab-rake gitlab:elastic:index_repositories ID_FROM=2001
-
-# Installations from source
-bundle exec rake gitlab:elastic:index_repositories RAILS_ENV=production ID_TO=1000
-bundle exec rake gitlab:elastic:index_repositories RAILS_ENV=production ID_FROM=1001 ID_TO=2000
-bundle exec rake gitlab:elastic:index_repositories RAILS_ENV=production ID_FROM=2001
-```
-
-Sometimes your repository index process `gitlab:elastic:index_repositories` or
-`gitlab:elastic:index_repositories_async` can get interrupted. This may happen
-for many reasons, but it's always safe to run the indexing job again - it will
-skip those repositories that have already been indexed.
+TIP: **Troubleshooting:**
+Sometimes the project indexing jobs queued by `gitlab:elastic:index_projects`
+can get interrupted. This may happen for many reasons, but it's always safe
+to run the indexing task again - it will skip those repositories that have
+already been indexed.
As the indexer stores the last commit SHA of every indexed repository in the
database, you can run the indexer with the special parameter `UPDATE_INDEX` and
@@ -297,10 +265,10 @@ that repository is indexed, it can be useful in case if your index is outdated:
```sh
# Omnibus installations
-sudo gitlab-rake gitlab:elastic:index_repositories UPDATE_INDEX=true ID_TO=1000
+sudo gitlab-rake gitlab:elastic:index_projects UPDATE_INDEX=true ID_TO=1000
# Installations from source
-bundle exec rake gitlab:elastic:index_repositories UPDATE_INDEX=true ID_TO=1000 RAILS_ENV=production
+bundle exec rake gitlab:elastic:index_projects UPDATE_INDEX=true ID_TO=1000 RAILS_ENV=production
```
You can also use the `gitlab:elastic:clear_index_status` Rake task to force the
@@ -320,16 +288,6 @@ bundle exec rake gitlab:elastic:index_wikis RAILS_ENV=production
The wiki indexer also supports the `ID_FROM` and `ID_TO` parameters if you want
to limit a project set.
-Index all database entities (Keep in mind it can take a while, so consider using `screen` or `tmux`):
-
-```sh
-# Omnibus installations
-sudo gitlab-rake gitlab:elastic:index_database
-
-# Installations from source
-bundle exec rake gitlab:elastic:index_database RAILS_ENV=production
-```
-
Enable replication and refreshing again after indexing (only if you previously disabled it):
```bash
@@ -376,25 +334,15 @@ There are several rake tasks available to you via the command line:
- This is a wrapper task. It does the following:
- `sudo gitlab-rake gitlab:elastic:create_empty_index`
- `sudo gitlab-rake gitlab:elastic:clear_index_status`
+ - `sudo gitlab-rake gitlab:elastic:index_projects`
- `sudo gitlab-rake gitlab:elastic:index_wikis`
- - `sudo gitlab-rake gitlab:elastic:index_database`
- - `sudo gitlab-rake gitlab:elastic:index_repositories`
-- [sudo gitlab-rake gitlab:elastic:index_repositories_async](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- - This iterates over all projects and places them in batches. It then sends these batches to the background via sidekiq jobs to be indexed.
-- [sudo gitlab-rake gitlab:elastic:index_repositories_status](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
+ - `sudo gitlab-rake gitlab:elastic:index_snippets`
+- [sudo gitlab-rake gitlab:elastic:index_projects](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
+ - This iterates over all projects and queues sidekiq jobs to index them in the background.
+- [sudo gitlab-rake gitlab:elastic:index_projects_status](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- This determines the overall status of the indexing. It is done by counting the total number of indexed projects, dividing by a count of the total number of projects, then multiplying by 100.
-- [sudo gitlab-rake gitlab:elastic:index_repositories](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- - This iterates over all projects and places them in batches. It then performs indexing on said batches synchronously.
- [sudo gitlab-rake gitlab:elastic:index_wikis](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- Iterates over every project, determines if said project contains wiki data, and then indexes the blobs (content) of said wiki data.
-- [sudo gitlab-rake gitlab:elastic:index_database](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- - This is a [rake multitask](https://www.rubydoc.info/github/ruby/rake/Rake/MultiTask). It does the following:
- - `sudo gitlab-rake gitlab:elastic:index_projects`
- - `sudo gitlab-rake gitlab:elastic:index_issues`
- - `sudo gitlab-rake gitlab:elastic:index_merge_requests`
- - `sudo gitlab-rake gitlab:elastic:index_snippets`
- - `sudo gitlab-rake gitlab:elastic:index_notes`
- - `sudo gitlab-rake gitlab:elastic:index_milestones`
- [sudo gitlab-rake gitlab:elastic:create_empty_index](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- This generates an empty index on the Elasticsearch side.
- [sudo gitlab-rake gitlab:elastic:clear_index_status](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
@@ -403,20 +351,8 @@ There are several rake tasks available to you via the command line:
- This removes the GitLab index on the Elasticsearch instance.
- [sudo gitlab-rake gitlab:elastic:recreate_index](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- Does the same thing as `sudo gitlab-rake gitlab:elastic:create_empty_index`
-- [sudo gitlab-rake gitlab:elastic:add_feature_visibility_levels_to_project](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- - Adds visibility information to the indices for projects.
-- [sudo gitlab-rake gitlab:elastic:index_projects](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- - Performs an Elasticsearch import that indexes projects data.
-- [sudo gitlab-rake gitlab:elastic:index_issues](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- - Performs an Elasticsearch import that indexes issues data.
-- [sudo gitlab-rake gitlab:elastic:index_merge_requests](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- - Performs an Elasticsearch import that indexes merge requests data.
- [sudo gitlab-rake gitlab:elastic:index_snippets](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- Performs an Elasticsearch import that indexes the snippets data.
-- [sudo gitlab-rake gitlab:elastic:index_notes](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- - Performs an Elasticsearch import that indexes the notes data.
-- [sudo gitlab-rake gitlab:elastic:index_milestones](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/lib/tasks/gitlab/elastic.rake)
- - Performs an Elasticsearch import that indexes the milestones data.
### Environment Variables
@@ -424,40 +360,16 @@ In addition to the rake tasks, there are some environment variables that can be
| Environment Variable | Data Type | What it does |
| -------------------- |:---------:| ---------------------------------------------------------------------------- |
-| `BATCH` | Integer | Modifies the size of the indexing batch (default 300 projects). |
| `UPDATE_INDEX` | Boolean | Tells the indexer to overwrite any existing index data (true/false). |
| `ID_TO` | Integer | Tells the indexer to only index projects less than or equal to the value. |
| `ID_FROM` | Integer | Tells the indexer to only index projects greater than or equal to the value. |
-### Batching
-
-The ability to apply batching makes the indexer run more efficiently. The default
-size of a batch is 300 projects, which may or may not be ideal for your setup.
-Depending on the resources available to your GitLab instance (sidekiq) and your
-Elasticsearch instance (RAM, CPU), you may be able to increase or decrease the
-batch size for more efficiency.
-
-- The larger the batch size is, the less sidekiq jobs and indexing requests get created.
-- The larger the batch size is, the more time and RAM it takes to process.
-- The smaller the batch size, the more sidekiq jobs, and indexing requests get created.
-- The smaller the batch size, the more CPU gets utilized.
-
-Finding the ideal size can be tricky, and will vary from GitLab instance to GitLab instance.
-Generally speaking, if the default is not ideal for you, try reducing it to somewhere in
-the 50-150 range (for bigger sized repos) or 450-600 range (for many small-sized repos).
-
-Example use:
-
-```sh
-sudo gitlab-rake gitlab:elastic:index_repositories_async BATCH=50
-```
-
### Indexing a specific project
Because the `ID_TO` and `ID_FROM` environment variables use the `or equal to` comparison, you can index only one project by using both these variables with the same project ID number:
```sh
-root@git:~# sudo gitlab-rake gitlab:elastic:index_repositories ID_TO=5 ID_FROM=5
+root@git:~# sudo gitlab-rake gitlab:elastic:index_projects ID_TO=5 ID_FROM=5
Indexing project repositories...I, [2019-03-04T21:27:03.083410 #3384] INFO -- : Indexing GitLab User / test (ID=33)...
I, [2019-03-04T21:27:05.215266 #3384] INFO -- : Indexing GitLab User / test (ID=33) is done!
```
@@ -554,7 +466,7 @@ Here are some common pitfalls and how to overcome them:
- **The indexing process is taking a very long time**
- The more data present in your GitLab instance, the longer the indexing process takes. You might want to try adjusting the BATCH sizes for asynchronous indexing to help speed up the process.
+ The more data present in your GitLab instance, the longer the indexing process takes.
- **No new data is added to the Elasticsearch index when I push code**
diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md
index f60333aa07c..5950737b964 100644
--- a/doc/integration/jenkins.md
+++ b/doc/integration/jenkins.md
@@ -19,6 +19,9 @@ GitLab's Jenkins integration allows you to trigger a Jenkins build when you
push code to a repository, or when a merge request is created. Additionally,
it shows the pipeline status on merge requests widgets and on the project's home page.
+Videos are also available on [GitLab workflow with Jira issues and Jenkins pipelines](https://youtu.be/Jn-_fyra7xQ)
+and [Migrating from Jenkins to GitLab](https://www.youtube.com/watch?v=RlEVGOpYF5Y).
+
## Use cases
- Suppose you are new to GitLab, and want to keep using Jenkins until you prepare
@@ -30,6 +33,8 @@ running with Jenkins, but view the results in your project's repository in GitLa
therefore, you opt for keep using Jenkins to build your apps. Show the results of your
pipelines directly in GitLab.
+For a real use case, read the blog post [Continuous integration: From Jenkins to GitLab using Docker](https://about.gitlab.com/2017/07/27/docker-my-precious/).
+
## Requirements
* [Jenkins GitLab Plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+Plugin)
diff --git a/doc/migrate_ci_to_ce/README.md b/doc/migrate_ci_to_ce/README.md
index 4c4b423f40f..7659b743311 100644
--- a/doc/migrate_ci_to_ce/README.md
+++ b/doc/migrate_ci_to_ce/README.md
@@ -1,3 +1,7 @@
+---
+type: howto
+---
+
# Migrate GitLab CI to GitLab CE or EE
Beginning with version 8.0 of GitLab Community Edition (CE) and Enterprise
@@ -333,7 +337,9 @@ restoration](../raketasks/backup_restore.md) guide.
## Troubleshooting
### show:secrets problem (Omnibus-only)
+
If you see errors like this:
+
```
Missing `secret_key_base` or `db_key_base` for 'production' environment. The secrets will be generated and stored in `config/secrets.yml`
rake aborted!
@@ -344,6 +350,7 @@ This can happen if you are updating from versions prior to 7.13 straight to 8.0.
The fix for this is to update to Omnibus 7.14 first and then update it to 8.0.
### Permission denied when accessing /var/opt/gitlab/gitlab-ci/builds
+
To fix that issue you have to change builds/ folder permission before doing final backup:
```
sudo chown -R gitlab-ci:gitlab-ci /var/opt/gitlab/gitlab-ci/builds
@@ -355,8 +362,10 @@ sudo chown git:git /var/opt/gitlab/gitlab-ci/builds
```
### Problems when importing CI database to GitLab
+
If you were migrating CI database from MySQL to PostgreSQL manually you can see errors during import about missing sequences:
-```
+
+```sql
ALTER SEQUENCE
ERROR: relation "ci_builds_id_seq" does not exist
ERROR: relation "ci_commits_id_seq" does not exist
@@ -373,9 +382,9 @@ CREATE TABLE
To fix that you need to apply this SQL statement before doing final backup:
-```sql
-## Omnibus GitLab
+Omnibus GitLab installations:
+```sql
gitlab-ci-rails dbconsole <<EOF
-- ALTER TABLES - DROP DEFAULTS
ALTER TABLE ONLY ci_application_settings ALTER COLUMN id DROP DEFAULT;
@@ -428,9 +437,11 @@ ALTER TABLE ONLY ci_triggers ALTER COLUMN id SET DEFAULT nextval('ci_triggers_id
ALTER TABLE ONLY ci_variables ALTER COLUMN id SET DEFAULT nextval('ci_variables_id_seq'::regclass);
ALTER TABLE ONLY ci_web_hooks ALTER COLUMN id SET DEFAULT nextval('ci_web_hooks_id_seq'::regclass);
EOF
+```
-## Source installations
+Source installations:
+```
cd /home/gitlab_ci/gitlab-ci
sudo -u gitlab_ci -H bundle exec rails dbconsole production <<EOF
... COPY SQL STATEMENTS FROM ABOVE ...
diff --git a/doc/policy/maintenance.md b/doc/policy/maintenance.md
index 1d656574acd..72bace3d282 100644
--- a/doc/policy/maintenance.md
+++ b/doc/policy/maintenance.md
@@ -1,8 +1,15 @@
-# GitLab Maintenance Policy
+---
+type: concepts
+---
+
+# GitLab Release and Maintenance Policy
+
+GitLab has strict policies governing version naming, as well as release pace for major, minor,
+patch and security releases. New releases are usually announced on the [GitLab blog](https://about.gitlab.com/blog/categories/releases/).
## Versioning
-GitLab follows the [Semantic Versioning](http://semver.org/) for its releases:
+GitLab uses [Semantic Versioning](http://semver.org/) for its releases:
`(Major).(Minor).(Patch)` in a [pragmatic way](https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e).
For example, for GitLab version 10.5.7:
@@ -15,9 +22,9 @@ Any part of the version number can increment into multiple digits, for example,
The following table describes the version types and their release cadence:
-| Version type | Description | Cadence |
-|:-------------|:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Major | For significant changes, or when any backward-incompatible changes are introduced to the public API. | Yearly. The next major release is GitLab 12.0 on June 22, 2019. Subsequent major releases will be scheduled for May 22 each year, by default. | |
+| Version type | Description | Cadence |
+|:-------------|:------------|:--------|
+| Major | For significant changes, or when any backward-incompatible changes are introduced to the public API. | Yearly. The next major release is GitLab 12.0 on June 22, 2019. Subsequent major releases will be scheduled for May 22 each year, by default. |
| Minor | For when new backward-compatible functionality is introduced to the public API, a minor feature is introduced, or when a set of smaller features is rolled out. | Monthly on the 22nd. |
| Patch | For backward-compatible bug fixes that fix incorrect behavior. See [Patch releases](#patch-releases). | As needed. |
@@ -75,10 +82,10 @@ that could change behavior in the next major release.
Please see the table below for some examples:
| Latest stable version | Your version | Recommended upgrade path | Note |
-| -------------- | ------------ | ------------------------ | ---------------- |
-| 9.4.5 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.4.5` | `8.17.7` is the last version in version `8` |
-| 10.1.4 | 8.13.4 | `8.13.4 -> 8.17.7 -> 9.5.10 -> 10.1.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9` |
-| 11.3.4 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9`, `10.8.7` is the last version in version `10` |
+| --------------------- | ------------ | ------------------------ | ---- |
+| 9.4.5 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.4.5` | `8.17.7` is the last version in version `8` |
+| 10.1.4 | 8.13.4 | `8.13.4 -> 8.17.7 -> 9.5.10 -> 10.1.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9` |
+| 11.3.4 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9`, `10.8.7` is the last version in version `10` |
More information about the release procedures can be found in our
[release documentation](https://gitlab.com/gitlab-org/release/docs). You may also want to read our
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index 8601551e3bd..b1637181855 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -1,13 +1,12 @@
-# Public access
-
-GitLab allows [Owners](../user/permissions.md) to change a projects' visibility in order to be accessed
-**publicly** or **internally**.
+---
+type: reference
+---
-Projects with either of these visibility levels will be listed in the
-public access directory (`/public` under your GitLab instance).
-Here is the [GitLab.com example](https://gitlab.com/public).
+# Public access
-Internal projects will only be available to authenticated users.
+GitLab allows [Owners](../user/permissions.md) to set a projects' visibility as **public**, **internal**
+or **private**. These visibility levels affect who can see the project in the
+public access directory (`/public` under your GitLab instance), like at [https://gitlab.com/public]().
## Visibility of projects
@@ -15,26 +14,26 @@ Internal projects will only be available to authenticated users.
Public projects can be cloned **without any** authentication.
-They will also be listed on the public access directory (`/public`).
+They will be listed in the public access directory (`/public`) for all users.
-**Any logged in user** will have [Guest](../user/permissions.md)
-permissions on the repository.
+**Any logged in user** will have [Guest permissions](../user/permissions.md)
+on the repository.
### Internal projects
Internal projects can be cloned by any logged in user.
-They will also be listed on the public access directory (`/public`) for logged
+They will also be listed in the public access directory (`/public`), but only for logged
in users.
-Any logged in user will have [Guest](../user/permissions.md) permissions
+Any logged in user will have [Guest permissions](../user/permissions.md)
on the repository.
### Private projects
-Private projects can only be cloned and viewed by project members, and
-they will only appear to project members on the public access directory
-(`https://gitlab.example.com/public`).
+Private projects can only be cloned and viewed by project members.
+
+They will appear in the public access directory (`/public`) for project members only.
### How to change project visibility
@@ -43,10 +42,10 @@ they will only appear to project members on the public access directory
## Visibility of groups
->**Note:**
-[Starting with][3323] GitLab 8.6, the group visibility has changed and can be
-configured the same way as projects. In previous versions, a group's page was
-always visible to all users.
+NOTE: **Note:**
+[Starting with](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3323) GitLab 8.6,
+the group visibility has changed and can be configured the same way as projects.
+In previous versions, a group's page was always visible to all users.
Like with projects, the visibility of a group can be set to dictate whether
anonymous users, all signed in users, or only explicit group members can view
@@ -54,8 +53,6 @@ it. The restriction for visibility levels on the application setting level also
applies to groups, so if that's set to internal, the explore page will be empty
for anonymous users. The group page now has a visibility level icon.
-[3323]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3323
-
## Visibility of users
The public page of a user, located at `/username`, is always visible whether
@@ -76,3 +73,15 @@ snippet:
This is useful to prevent people exposing their repositories to public
by accident. The restricted visibility settings do not apply to admin users.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/push_rules/push_rules.md b/doc/push_rules/push_rules.md
index e44eab2556e..b2d626a0a74 100644
--- a/doc/push_rules/push_rules.md
+++ b/doc/push_rules/push_rules.md
@@ -1,6 +1,11 @@
+---
+type: reference, howto
+---
+
# Push Rules **[STARTER]**
-Gain additional control over pushes to your repository.
+Gain additional control over what can and can't be pushed to your repository by using
+regular expressions to reject pushes based on commit contents, branch names or file details.
## Overview
@@ -33,7 +38,7 @@ will be accepted.
### Restrict branch names
-Let's assume there's a strictly policy for branch names in your company, and
+Let's assume there's a strict policy for branch names in your company, and
you want the branches to start with a certain name because you have different
GitLab CI jobs (`feature`, `hotfix`, `docker`, `android`, etc.) that rely on the
branch name.
@@ -46,7 +51,7 @@ will get rejected.
## Enabling push rules
->**Note:**
+NOTE: **Note:**
GitLab administrators can set push rules globally under
**Admin area > Push Rules** that all new projects will inherit. You can later
override them in a project's settings.
@@ -71,8 +76,8 @@ The following options are available.
| Prohibited file names | **Starter** 7.10 | Any committed filenames that match this regular expression are not allowed to be pushed. Leave empty to allow any filenames. |
| Maximum file size | **Starter** 7.12 | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. |
->**Tip:**
-GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in push rules. You can check your regular expressions at <https://regex-golang.appspot.com>.
+TIP: **Tip:**
+GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in push rules, and you can test them at the [GoLang regex tester](https://regex-golang.appspot.com).
## Prevent pushing secrets to the repository
@@ -86,7 +91,7 @@ pushes to the repository when a file matches a regular expression as read from
[`files_blacklist.yml`][list] (make sure you are at the right branch
as your GitLab version when viewing this file).
-NOTE: **Note**:
+NOTE: **Note:**
Files already committed won't get restricted by this push rule.
Below is an example list of what will be rejected by these regular expressions:
@@ -148,6 +153,18 @@ pry.history
bash_history
```
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
+
[protected-branches]: ../user/project/protected_branches.md
[signing-commits]: ../user/project/repository/gpg_signed_commits/index.md
[ee-385]: https://gitlab.com/gitlab-org/gitlab-ee/issues/385
diff --git a/doc/security/README.md b/doc/security/README.md
index a90127e0356..c48d5bc2065 100644
--- a/doc/security/README.md
+++ b/doc/security/README.md
@@ -1,5 +1,6 @@
---
comments: false
+type: index
---
# Security
diff --git a/doc/security/crime_vulnerability.md b/doc/security/crime_vulnerability.md
index d61a205d954..9665cc0337f 100644
--- a/doc/security/crime_vulnerability.md
+++ b/doc/security/crime_vulnerability.md
@@ -1,3 +1,7 @@
+---
+type: reference
+---
+
# How we manage the TLS protocol CRIME vulnerability
> CRIME ("Compression Ratio Info-leak Made Easy") is a security exploit against
@@ -7,15 +11,15 @@ authentication cookies, it allows an attacker to perform session hijacking on an
authenticated web session, allowing the launching of further attacks.
([CRIME](https://en.wikipedia.org/w/index.php?title=CRIME&oldid=692423806))
-### Description
+## Description
-The TLS Protocol CRIME Vulnerability affects compression over HTTPS, therefore
-it warns against using SSL Compression (for example gzip) or SPDY which
-optionally uses compression as well.
+The TLS Protocol CRIME Vulnerability affects systems that use data compression
+over HTTPS. Your system might be vulnerable to the CRIME vulnerability if you use
+SSL Compression (for example, gzip) or SPDY (which optionally uses compression).
GitLab supports both gzip and [SPDY][ngx-spdy] and mitigates the CRIME
-vulnerability by deactivating gzip when HTTPS is enabled. You can see the
-sources of the files in question:
+vulnerability by deactivating gzip when HTTPS is enabled. The sources of the
+files are here:
- [Source installation NGINX file][source-nginx]
- [Omnibus installation NGINX file][omnibus-nginx]
@@ -24,7 +28,7 @@ Although SPDY is enabled in Omnibus installations, CRIME relies on compression
(the 'C') and the default compression level in NGINX's SPDY module is 0
(no compression).
-### Nessus
+## Nessus
The Nessus scanner, [reports a possible CRIME vulnerability][nessus] in GitLab
similar to the following format:
@@ -45,12 +49,12 @@ SPDY support earlier than version 4 is advertised.
```
From the report above it is important to note that Nessus is only checking if
-TLS advertises the SPDY protocol earlier than version 4, it does not perform an
-attack nor does it check if compression is enabled. With just this approach, it
+TLS advertises the SPDY protocol earlier than version 4. It does not perform an
+attack nor does it check if compression is enabled. The Nessus scanner alone
cannot tell that SPDY's compression is disabled and not subject to the CRIME
vulnerability.
-### References
+## References
- Nginx ["Module ngx_http_spdy_module"][ngx-spdy]
- Tenable Network Security, Inc. ["Transport Layer Security (TLS) Protocol CRIME Vulnerability"][nessus]
@@ -61,3 +65,15 @@ vulnerability.
[ngx-spdy]: http://nginx.org/en/docs/http/ngx_http_spdy_module.html
[nessus]: https://www.tenable.com/plugins/index.php?view=single&id=62565
[wiki-crime]: https://en.wikipedia.org/wiki/CRIME
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/information_exclusivity.md b/doc/security/information_exclusivity.md
index 22756232025..62a20d3f257 100644
--- a/doc/security/information_exclusivity.md
+++ b/doc/security/information_exclusivity.md
@@ -1,9 +1,36 @@
+---
+type: concepts
+---
# Information exclusivity
-Git is a distributed version control system (DVCS).
-This means that everyone that works with the source code has a local copy of the complete repository.
-In GitLab every project member that is not a guest (so reporters, developers and maintainers) can clone the repository to get a local copy.
-After obtaining this local copy the user can upload the full repository anywhere, including another project under their control or another server.
-The consequence is that you can't build access controls that prevent the intentional sharing of source code by users that have access to the source code.
-This is an inherent feature of a DVCS and all git management systems have this limitation.
-Obviously you can take steps to prevent unintentional sharing and information destruction, this is why only some people are allowed to invite others and nobody can force push a protected branch.
+Git is a distributed version control system (DVCS). This means that everyone
+who works with the source code has a local copy of the complete repository.
+
+In GitLab every project member that is not a guest (reporters, developers, and
+maintainers) can clone the repository to create a local copy. After obtaining
+a local copy, the user can upload the full repository anywhere, including to
+another project that is under their control, or onto another server.
+
+Therefore, it is impossible to build access controls that prevent the
+intentional sharing of source code by users that have access to the source code.
+
+This is an inherent feature of a DVCS. All git management systems have this
+limitation.
+
+You can take steps to prevent unintentional sharing and information
+destruction. This limitation is the reason why only certain people are allowed
+to [add users to a project](../user/project/members/index.md)
+and why only a GitLab admin can [force push a protected
+branch](../user/project/protected_branches.md).
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/password_length_limits.md b/doc/security/password_length_limits.md
index d21b26a43e8..d78293c75c6 100644
--- a/doc/security/password_length_limits.md
+++ b/doc/security/password_length_limits.md
@@ -1,11 +1,28 @@
+---
+type: reference, howto
+---
# Custom password length limits
-If you want to enforce longer user passwords you can create an extra Devise initializer with the steps below.
+If you want to enforce longer user passwords you can create an extra Devise
+initializer with the steps below.
-If you do not use the `devise_password_length.rb` initializer the password length is set to a minimum of 8 characters in `config/initializers/devise.rb`.
+If you do not use the `devise_password_length.rb` initializer the password
+length is set to a minimum of 8 characters in `config/initializers/devise.rb`.
```bash
cd /home/git/gitlab
sudo -u git -H cp config/initializers/devise_password_length.rb.example config/initializers/devise_password_length.rb
sudo -u git -H editor config/initializers/devise_password_length.rb # inspect and edit the new password length limits
```
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/rack_attack.md b/doc/security/rack_attack.md
index 66081d7e376..fa4b0d1fb09 100644
--- a/doc/security/rack_attack.md
+++ b/doc/security/rack_attack.md
@@ -1,24 +1,28 @@
+---
+type: reference, howto
+---
# Rack Attack
-Rack Attack, also known as Rack::Attack, is [a rubygem](https://github.com/kickstarter/rack-attack)
+[Rack Attack](https://github.com/kickstarter/rack-attack), also known as Rack::Attack, is a Ruby gem
that is meant to protect GitLab with the ability to customize throttling and
-blocking user IPs.
+to block user IP addresses.
+
You can prevent brute-force passwords attacks, scrapers, or any other offenders
-by throttling requests from IP addresses making large volumes of requests.
-In case you find throttling is not enough to protect you against abusive clients,
-Rack Attack offers IP whitelisting, blacklisting, Fail2ban style filtering and
+by throttling requests from IP addresses that are making large volumes of requests.
+If you find throttling is not enough to protect you against abusive clients,
+Rack Attack offers IP whitelisting, blacklisting, Fail2ban style filtering, and
tracking.
**Note:** Starting with 11.2, Rack Attack is disabled by default. To continue
-using this feature, please enable it by [configuring `gitlab.rb` as described in Settings](#settings).
+using Rack Attack, please enable it by [configuring `gitlab.rb` as described in Settings](#settings).
By default, user sign-in, user sign-up (if enabled), and user password reset is
limited to 6 requests per minute. After trying for 6 times, the client will
have to wait for the next minute to be able to try again.
-If you installed or upgraded GitLab by following the [official guides](../install/README.md)
-this should be disabled by default. If your instance is not exposed to any incoming
-connections, it is recommended to leave Rack Attack disabled.
+If you installed or upgraded GitLab by following the [official guides](../install/README.md),
+Rack Attack should be disabled by default. If your instance is not exposed to any incoming
+connections, it is recommended that you leave Rack Attack disabled.
For more information on how to use these options check out
[rack-attack README](https://github.com/kickstarter/rack-attack/blob/master/README.md).
@@ -27,7 +31,7 @@ For more information on how to use these options check out
**Omnibus GitLab**
-1. Open `/etc/gitlab/gitlab.rb` with you editor
+1. Open `/etc/gitlab/gitlab.rb` with your editor
1. Add the following:
```ruby
@@ -53,7 +57,7 @@ The following settings can be configured:
For example, `["127.0.0.1", "127.0.0.2", "127.0.0.3"]`.
- `maxretry`: The maximum amount of times a request can be made in the
specified time.
-- `findtime`: The maximum amount of time failed requests can count against an IP
+- `findtime`: The maximum amount of time that failed requests can count against an IP
before it's blacklisted (in seconds).
- `bantime`: The total amount of time that a blacklisted IP will be blocked (in
seconds).
diff --git a/doc/security/reset_root_password.md b/doc/security/reset_root_password.md
index 6a882ed6fe5..a58d70f0ff2 100644
--- a/doc/security/reset_root_password.md
+++ b/doc/security/reset_root_password.md
@@ -1,8 +1,11 @@
+---
+type: howto
+---
# How to reset your root password
-Log into your server with root privileges. Then start a Ruby on Rails console.
+To reset your root password, first log into your server with root privileges.
-Start the console with this command:
+Start a Ruby on Rails console with this command:
```bash
gitlab-rails console production
@@ -38,3 +41,15 @@ user.save!
```
Exit the console and try to login with your new password.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/ssh_keys_restrictions.md b/doc/security/ssh_keys_restrictions.md
index 6b6a8a06cc9..ae4cc44519e 100644
--- a/doc/security/ssh_keys_restrictions.md
+++ b/doc/security/ssh_keys_restrictions.md
@@ -1,3 +1,6 @@
+---
+type: reference, howto
+---
# Restrict allowed SSH key technologies and minimum length
`ssh-keygen` allows users to create RSA keys with as few as 768 bits, which
@@ -25,3 +28,15 @@ An icon will be visible to the user of a restricted key in the SSH keys section
![Restricted SSH key icon](img/ssh_keys_restricted_key_icon.png)
Hovering over this icon will tell you why the key is restricted.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/two_factor_authentication.md b/doc/security/two_factor_authentication.md
index 2ece4ed3fc9..ad5daef805a 100644
--- a/doc/security/two_factor_authentication.md
+++ b/doc/security/two_factor_authentication.md
@@ -1,3 +1,6 @@
+---
+type: howto
+---
# Enforce Two-factor Authentication (2FA)
Two-factor Authentication (2FA) provides an additional level of security to your
@@ -55,3 +58,15 @@ sudo -u git -H bundle exec rake gitlab:two_factor:disable_for_all_users RAILS_EN
CAUTION: **Caution:**
This is a permanent and irreversible action. Users will have to
reactivate 2FA from scratch if they want to use it again.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/unlock_user.md b/doc/security/unlock_user.md
index d5ecef7f605..75cf754e197 100644
--- a/doc/security/unlock_user.md
+++ b/doc/security/unlock_user.md
@@ -1,8 +1,12 @@
+---
+type: howto
+---
# How to unlock a locked user
-Log into your server with root privileges. Then start a Ruby on Rails console.
+To unlock a locked user, first log into your server with root privileges.
+
+Start a Ruby on Rails console with this command:
-Start the console with this command:
```bash
gitlab-rails console production
@@ -29,3 +33,15 @@ user.unlock_access!
```
Exit the console, the user should now be able to log in again.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/user_email_confirmation.md b/doc/security/user_email_confirmation.md
index 8c07e11dcb1..f0af0a7ac6a 100644
--- a/doc/security/user_email_confirmation.md
+++ b/doc/security/user_email_confirmation.md
@@ -1,7 +1,23 @@
+---
+type: howto
+---
# User email confirmation at sign-up
-GitLab admin can enable email confirmation on sign-up, if you want to confirm all
-user emails before they are able to sign-in.
+GitLab can be configured to require confirmation of a user's email address when
+the user signs up. When this setting is enabled, the user is unable to sign in until
+they confirm their email address.
In the Admin area under **Settings** (`/admin/application_settings`), go to section
-**Sign-up Restrictions** and look for **Send confirmation email on sign-up** option.
+**Sign-up Restrictions** and look for the **Send confirmation email on sign-up** option.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/user_file_uploads.md b/doc/security/user_file_uploads.md
index 98493d33b00..00a2607b607 100644
--- a/doc/security/user_file_uploads.md
+++ b/doc/security/user_file_uploads.md
@@ -1,11 +1,30 @@
+---
+type: reference
+---
# User File Uploads
-Images attached to issues, merge requests or comments do not require authentication
-to be viewed if someone knows the direct URL. This direct URL contains a random
-32-character ID that prevents unauthorized people from guessing the URL to an
-image containing sensitive information. We don't enable authentication because
-these images need to be visible in the body of notification emails, which are
-often read from email clients that are not authenticated with GitLab, like
-Outlook, Apple Mail, or the Mail app on your mobile device.
+Images that are attached to issues, merge requests, or comments
+do not require authentication to be viewed if they are accessed directly by URL.
+This direct URL contains a random 32-character ID that prevents unauthorized
+people from guessing the URL for an image, thus there is some protection if an
+image contains sensitive information.
-Note that non-image attachments do require authentication to be viewed.
+Authentication is not enabled because images must be visible in the body of
+notification emails, which are often read from email clients that are not
+authenticated with GitLab, such as Outlook, Apple Mail, or the Mail app on your
+mobile device.
+
+>**Note:**
+Non-image attachments do require authentication to be viewed.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/security/webhooks.md b/doc/security/webhooks.md
index 8c26bbac6a7..d4fa088cb15 100644
--- a/doc/security/webhooks.md
+++ b/doc/security/webhooks.md
@@ -1,20 +1,56 @@
+---
+type: concepts, reference, howto
+---
# Webhooks and insecure internal web services
-If you have non-GitLab web services running on your GitLab server or within its local network, these may be vulnerable to exploitation via Webhooks.
+If you have non-GitLab web services running on your GitLab server or within its
+local network, these may be vulnerable to exploitation via Webhooks.
-With [Webhooks](../user/project/integrations/webhooks.md), you and your project maintainers and owners can set up URLs to be triggered when specific things happen to projects. Normally, these requests are sent to external web services specifically set up for this purpose, that process the request and its attached data in some appropriate way.
+With [Webhooks](../user/project/integrations/webhooks.md), you and your project
+maintainers and owners can set up URLs to be triggered when specific changes
+occur in your projects. Normally, these requests are sent to external web services
+specifically set up for this purpose, that process the request and its attached
+data in some appropriate way.
-Things get hairy, however, when a Webhook is set up with a URL that doesn't point to an external, but to an internal service, that may do something completely unintended when the webhook is triggered and the POST request is sent.
+Things get hairy, however, when a Webhook is set up with a URL that doesn't
+point to an external, but to an internal service, that may do something
+completely unintended when the webhook is triggered and the POST request is
+sent.
-Because Webhook requests are made by the GitLab server itself, these have complete access to everything running on the server (`http://localhost:123`) or within the server's local network (`http://192.168.1.12:345`), even if these services are otherwise protected and inaccessible from the outside world.
+Because Webhook requests are made by the GitLab server itself, these have
+complete access to everything running on the server (`http://localhost:123`) or
+within the server's local network (`http://192.168.1.12:345`), even if these
+services are otherwise protected and inaccessible from the outside world.
-If a web service does not require authentication, Webhooks can be used to trigger destructive commands by getting the GitLab server to make POST requests to endpoints like `http://localhost:123/some-resource/delete`.
+If a web service does not require authentication, Webhooks can be used to
+trigger destructive commands by getting the GitLab server to make POST requests
+to endpoints like `http://localhost:123/some-resource/delete`.
-To prevent this type of exploitation from happening, starting with GitLab 10.6, all Webhook requests to the current GitLab instance server address and/or in a private network will be forbidden by default. That means that all requests made to 127.0.0.1, ::1 and 0.0.0.0, as well as IPv4 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 and IPv6 site-local (ffc0::/10) addresses won't be allowed.
+To prevent this type of exploitation from happening, starting with GitLab 10.6,
+all Webhook requests to the current GitLab instance server address and/or in a
+private network will be forbidden by default. That means that all requests made
+to 127.0.0.1, ::1 and 0.0.0.0, as well as IPv4 10.0.0.0/8, 172.16.0.0/12,
+192.168.0.0/16 and IPv6 site-local (ffc0::/10) addresses won't be allowed.
-This behavior can be overridden by enabling the option *"Allow requests to the local network from hooks and services"* in the *"Outbound requests"* section inside the Admin area under **Settings** (`/admin/application_settings/network`):
+This behavior can be overridden by enabling the option *"Allow requests to the
+local network from hooks and services"* in the *"Outbound requests"* section
+inside the Admin area under **Settings**
+(`/admin/application_settings/network`):
![Outbound requests admin settings](img/outbound_requests_section.png)
>**Note:**
-*System hooks* are exempt from this protection because they are set up by admins.
+*System hooks* are exempt from this protection because they are set up by
+admins.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 3bfebfc5d9b..09b5fbd9260 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -1,3 +1,7 @@
+---
+type: howto, reference
+---
+
# GitLab and SSH keys
Git is a distributed version control system, which means you can work locally
@@ -50,8 +54,7 @@ more information, you can read this
We'll focus on ED25519 and RSA and here.
NOTE: **Note:**
-As an admin, you can restrict
-[which keys should be permitted and their minimum length](../security/ssh_keys_restrictions.md).
+As an admin, you can [restrict which keys should be permitted and their minimum length](../security/ssh_keys_restrictions.md).
By default, all keys are permitted, which is also the case for
[GitLab.com](../user/gitlab_com/index.md#ssh-host-keys-fingerprints).
@@ -91,9 +94,8 @@ ssh-keygen -o -f ~/.ssh/id_rsa
## Generating a new SSH key pair
-Before creating an SSH key pair, make sure to read about the
-[different types of keys](#types-of-ssh-keys-and-which-to-choose) to understand
-their differences.
+Before creating an SSH key pair, make sure to understand the
+[different types of keys](#types-of-ssh-keys-and-which-to-choose).
To create a new SSH key pair:
@@ -332,7 +334,7 @@ not implicitly give any access just by setting them up.
### Eclipse
-How to add your SSH key to Eclipse: <https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration>
+If you are using [EGit](https://www.eclipse.org/egit/), you can [add your SSH key to Eclipse](https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration).
## SSH on the GitLab server
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index dfd80f8882e..37051f6b10f 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -1,3 +1,7 @@
+---
+type: index, reference
+---
+
# Subscription setup and management
This page will help get you started with your new subscription or manage an existing one, whether you have subscribed to GitLab.com or self-managed GitLab.
@@ -42,7 +46,8 @@ After purchase, the license file is sent to the email address tied to the Custom
### Link your GitLab.com account with your Customers Portal account
-NOTE: **Note:** This is *required* for GitLab.com subscriptions.
+NOTE: **Note:**
+This is *required* for GitLab.com subscriptions.
Once signed into the customers portal, if your account is not already linked, you should be prompted to link your account with a "Link my GitLab Account" button.
@@ -87,6 +92,14 @@ plan - in the included table:
| Subscription start date | The date your subscription started. If this is for a Free plan, this is the date you transitioned off your group's paid plan. |
| Subscription end date | The date your current subscription will end. This does not apply to Free plans. |
+### Subscription changes and your data
+
+When your subscription or trial expires, GitLab does not delete your data, however, depending on the tier and feature, it may become inaccessible. Please note that some features may not behave as expected if a graceful fallback is not currently implemented, such as [environment specific variables not being passed](https://gitlab.com/gitlab-org/gitlab-ce/issues/52825).
+
+If you renew or upgrade, your data will again be accessible.
+
+For self-managed customers, there is a two-week grace period when your features will continue to work as-is, after which the entire instance will become read only. However, if you remove the license, you will immediately revert to Core features.
+
## Need help?
[GitLab's Documentation](https://docs.gitlab.com/) offers a wide range of topics covering the use and administration of GitLab.
@@ -101,3 +114,15 @@ These issues are the best avenue for getting updates on specific product plans a
### Contacting Support
Learn more about the tiers of [GitLab Support](https://about.gitlab.com/support/) or [submit a request via the Support Portal](https://support.gitlab.com/hc/en-us/requests/new).
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index a3698d60f6d..a46f7d30892 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -1,3 +1,7 @@
+---
+type: reference
+---
+
# System hooks
Your GitLab instance can perform HTTP POST requests on the following events:
@@ -27,11 +31,9 @@ The triggers for most of these are self-explanatory, but `project_update` and `p
System hooks can be used, e.g. for logging or changing information in a LDAP server.
-> **Note:**
->
-> We follow the same structure from Webhooks for Push and Tag events, but we never display commits.
->
-> Same deprecations from Webhooks are valid here.
+NOTE: **Note:**
+We follow the same structure and deprecations as [Webhooks](../user/project/integrations/webhooks.md)
+for Push and Tag events, but we never display commits.
## Hooks request example
@@ -640,3 +642,15 @@ X-Gitlab-Event: System Hook
"refs":["refs/heads/master"]
}
```
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/tools/email.md b/doc/tools/email.md
index ab39206ffa4..a2d677484f0 100644
--- a/doc/tools/email.md
+++ b/doc/tools/email.md
@@ -1,12 +1,12 @@
-# Email from GitLab **[STARTER ONLY]**
-
-As a GitLab administrator you can email GitLab users from within GitLab.
+---
+type: howto, reference
+---
-## Overview
+# Email from GitLab **[STARTER ONLY]**
-GitLab provides a simple tool to email all users or users of a chosen group or
-project right from the admin area. Users will receive the email to their primary
-email address.
+GitLab provides a simple tool to administrators for emailing all users, or users of
+a chosen group or project, right from the admin area. Users will receive the email
+at their primary email address.
## Use-cases
@@ -28,11 +28,21 @@ email address.
## Unsubscribing from emails
-User can choose to unsubscribe from receiving emails from GitLab by following
-the unsubscribe link from the email. Unsubscribing is unauthenticated in order
-to keep the simplicity of this feature.
+Users can choose to unsubscribe from receiving emails from GitLab by following
+the unsubscribe link in the email. Unsubscribing is unauthenticated in order
+to keep this feature simple.
-On unsubscribe, user will receive an email notifying that unsubscribe happened.
+On unsubscribe, users will receive an email notification that unsubscribe happened.
The endpoint that provides the unsubscribe option is rate-limited.
-[ee]: https://about.gitlab.com/pricing/
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index b00a8afa386..34140a3260e 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -8,7 +8,16 @@ to simplify the setup and execution of a mature & modern software development li
## Overview
-NOTE: **Enabled by default:**
+With Auto DevOps, the software development process becomes easier to set up
+as every project can have a complete workflow from verification to monitoring
+with minimal configuration. Just push your code and GitLab takes
+care of everything else. This makes it easier to start new projects and brings
+consistency to how applications are set up throughout a company.
+
+For an introduction to Auto DevOps, watch [AutoDevOps in GitLab 11.0](https://youtu.be/0Tc0YYBxqi4).
+
+## Enabled by default
+
Starting with GitLab 11.3, the Auto DevOps pipeline is enabled by default for all
projects. If it has not been explicitly enabled for the project, Auto DevOps will be automatically
disabled on the first pipeline failure. Your project will continue to use an alternative
@@ -16,12 +25,6 @@ disabled on the first pipeline failure. Your project will continue to use an alt
administrator can [change this setting](../../user/admin_area/settings/continuous_integration.md#auto-devops-core-only)
in the admin area.
-With Auto DevOps, the software development process becomes easier to set up
-as every project can have a complete workflow from verification to monitoring
-with minimal configuration. Just push your code and GitLab takes
-care of everything else. This makes it easier to start new projects and brings
-consistency to how applications are set up throughout a company.
-
## Quick start
If you are using GitLab.com, see the [quick start guide](quick_start_guide.md)
@@ -62,7 +65,7 @@ project in a simple and automatic way:
1. [Auto SAST (Static Application Security Testing)](#auto-sast-ultimate) **[ULTIMATE]**
1. [Auto Dependency Scanning](#auto-dependency-scanning-ultimate) **[ULTIMATE]**
1. [Auto License Management](#auto-license-management-ultimate) **[ULTIMATE]**
-1. [Auto Container Scanning](#auto-container-scanning)
+1. [Auto Container Scanning](#auto-container-scanning-ultimate) **[ULTIMATE]**
1. [Auto Review Apps](#auto-review-apps)
1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast-ultimate) **[ULTIMATE]**
1. [Auto Deploy](#auto-deploy)
@@ -120,7 +123,6 @@ To make full use of Auto DevOps, you will need:
[default service template](../../user/project/integrations/services_templates.md)
for the entire GitLab installation.
-NOTE: **Note:**
If you do not have Kubernetes or Prometheus installed, then Auto Review Apps,
Auto Deploy, and Auto Monitoring will be silently skipped.
@@ -135,10 +137,13 @@ in any of the following places:
- or at the project level as a variable: `KUBE_INGRESS_BASE_DOMAIN`
- or at the group level as a variable: `KUBE_INGRESS_BASE_DOMAIN`.
-NOTE: **Note**
-The Auto DevOps base domain variable (`KUBE_INGRESS_BASE_DOMAIN`) follows the same order of precedence
+The base domain variable `KUBE_INGRESS_BASE_DOMAIN` follows the same order of precedence
as other environment [variables](../../ci/variables/README.md#priority-of-environment-variables).
+NOTE: **Note**
+`AUTO_DEVOPS_DOMAIN` environment variable is deprecated and
+[is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959).
+
A wildcard DNS A record matching the base domain(s) is required, for example,
given a base domain of `example.com`, you'd need a DNS entry like:
@@ -222,19 +227,20 @@ can enable/disable Auto DevOps at either the project-level or instance-level.
### Enabling/disabling Auto DevOps at the instance-level (Administrators only)
+Even when disabled at the instance level, group owners and project maintainers can still enable
+Auto DevOps at the group and project level, respectively.
+
1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
1. Toggle the checkbox labeled **Default to Auto DevOps pipeline for all projects**.
1. If enabling, optionally set up the Auto DevOps [base domain](#auto-devops-base-domain) which will be used for Auto Deploy and Auto Review Apps.
1. Click **Save changes** for the changes to take effect.
-NOTE: **Note:**
-Even when disabled at the instance level, group owners and project maintainers are still able to enable
-Auto DevOps at group-level and project-level, respectively.
-
### Enabling/disabling Auto DevOps at the group-level
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/52447) in GitLab 11.10.
+Only administrators and group owners can enable or disable Auto DevOps at the group level.
+
To enable or disable Auto DevOps at the group-level:
1. Go to group's **Settings > CI/CD > Auto DevOps** page.
@@ -245,9 +251,6 @@ When enabling or disabling Auto DevOps at group-level, group configuration will
the subgroups and projects inside that group, unless Auto DevOps is specifically enabled or disabled on
the subgroup or project.
-NOTE: **Note**
-Only administrators and group owners are allowed to enable or disable Auto DevOps at group-level.
-
### Enabling/disabling Auto DevOps at the project-level
If enabling, check that your project doesn't have a `.gitlab-ci.yml`, or if one exists, remove it.
@@ -261,16 +264,13 @@ If enabling, check that your project doesn't have a `.gitlab-ci.yml`, or if one
When the feature has been enabled, an Auto DevOps pipeline is triggered on the default branch.
-NOTE: **Note:**
-For GitLab versions 10.0 - 10.2, when enabling Auto DevOps, a pipeline needs to be
-manually triggered either by pushing a new commit to the repository or by visiting
-`https://example.gitlab.com/<username>/<project>/pipelines/new` and creating
-a new pipeline for your default branch, generally `master`.
+### Feature flag to enable for a percentage of projects
-NOTE: **Note:**
-There is also a feature flag to enable Auto DevOps to a percentage of projects
-which can be enabled from the console with
-`Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(10)`.
+There is also a feature flag to enable Auto DevOps by default to your chosen percentage of projects.
+
+This can be enabled from the console with the following, which uses the example of 10%:
+
+`Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(10)`
### Deployment strategy
@@ -350,7 +350,6 @@ frameworks are detected automatically, but if your language is not detected,
you may succeed with a [custom buildpack](#custom-buildpacks). Check the
[currently supported languages](#currently-supported-languages).
-NOTE: **Note:**
Auto Test uses tests you already have in your application. If there are no
tests, it's up to you to add them.
@@ -371,73 +370,66 @@ Any differences between the source and target branches are also
Static Application Security Testing (SAST) uses the
[SAST Docker image](https://gitlab.com/gitlab-org/security-products/sast) to run static
-analysis on the current code and checks for potential security issues. Once the
-report is created, it's uploaded as an artifact which you can later download and
+analysis on the current code and checks for potential security issues. The
+the Auto SAST stage will be skipped on licenses other than Ultimate and requires GitLab Runner 11.5 or above.
+
+Once the report is created, it's uploaded as an artifact which you can later download and
check out.
Any security warnings are also shown in the merge request widget. Read more how
[SAST works](../../user/application_security/sast/index.md).
-NOTE: **Note:**
-The Auto SAST stage will be skipped on licenses other than Ultimate.
-
-NOTE: **Note:**
-The Auto SAST job requires GitLab Runner 11.5 or above.
-
### Auto Dependency Scanning **[ULTIMATE]**
> Introduced in [GitLab Ultimate][ee] 10.7.
Dependency Scanning uses the
[Dependency Scanning Docker image](https://gitlab.com/gitlab-org/security-products/dependency-scanning)
-to run analysis on the project dependencies and checks for potential security issues. Once the
+to run analysis on the project dependencies and checks for potential security issues.
+The Auto Dependency Scanning stage will be skipped on licenses other than Ultimate
+and requires GitLab Runner 11.5 or above.
+
+Once the
report is created, it's uploaded as an artifact which you can later download and
check out.
Any security warnings are also shown in the merge request widget. Read more about
[Dependency Scanning](../../user/application_security/dependency_scanning/index.md).
-NOTE: **Note:**
-The Auto Dependency Scanning stage will be skipped on licenses other than Ultimate.
-
-NOTE: **Note:**
-The Auto Dependency Scanning job requires GitLab Runner 11.5 or above.
-
### Auto License Management **[ULTIMATE]**
> Introduced in [GitLab Ultimate][ee] 11.0.
License Management uses the
[License Management Docker image](https://gitlab.com/gitlab-org/security-products/license-management)
-to search the project dependencies for their license. Once the
+to search the project dependencies for their license. The Auto License Management stage
+will be skipped on licenses other than Ultimate.
+
+Once the
report is created, it's uploaded as an artifact which you can later download and
check out.
Any licenses are also shown in the merge request widget. Read more how
[License Management works](../../user/application_security/license_management/index.md).
-NOTE: **Note:**
-The Auto License Management stage will be skipped on licenses other than Ultimate.
-
-### Auto Container Scanning
+### Auto Container Scanning **[ULTIMATE]**
> Introduced in GitLab 10.4.
Vulnerability Static Analysis for containers uses
[Clair](https://github.com/coreos/clair) to run static analysis on a
-Docker image and checks for potential security issues. Once the report is
+Docker image and checks for potential security issues. The Auto Container Scanning stage
+will be skipped on licenses other than Ultimate.
+
+Once the report is
created, it's uploaded as an artifact which you can later download and
check out.
Any security warnings are also shown in the merge request widget. Read more how
[Container Scanning works](../../user/application_security/container_scanning/index.md).
-NOTE: **Note:**
-The Auto Container Scanning stage will be skipped on licenses other than Ultimate.
-
### Auto Review Apps
-NOTE: **Note:**
This is an optional step, since many projects do not have a Kubernetes cluster
available. If the [requirements](#requirements) are not met, the job will
silently be skipped.
@@ -482,15 +474,14 @@ in the first place, and thus not realize that it needs to re-apply the old confi
Dynamic Application Security Testing (DAST) uses the
popular open source tool [OWASP ZAProxy](https://github.com/zaproxy/zaproxy)
to perform an analysis on the current code and checks for potential security
-issues. Once the report is created, it's uploaded as an artifact which you can
+issues. The Auto DAST stage will be skipped on licenses other than Ultimate.
+
+Once the report is created, it's uploaded as an artifact which you can
later download and check out.
Any security warnings are also shown in the merge request widget. Read how
[DAST works](../../user/application_security/dast/index.md).
-NOTE: **Note:**
-The Auto DAST stage will be skipped on licenses other than Ultimate.
-
### Auto Browser Performance Testing **[PREMIUM]**
> Introduced in [GitLab Premium][ee] 10.4.
@@ -508,7 +499,6 @@ Any performance differences between the source and target branches are also
### Auto Deploy
-NOTE: **Note:**
This is an optional step, since many projects do not have a Kubernetes cluster
available. If the [requirements](#requirements) are not met, the job will
silently be skipped.
@@ -547,7 +537,7 @@ in the first place, and thus not realize that it needs to re-apply the old confi
For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md#gitlab-deploy-token)
will be automatically created, when Auto DevOps is enabled and the Auto DevOps settings are saved. This Deploy Token
-can be used for permanent access to the registry.
+can be used for permanent access to the registry. When the GitLab Deploy Token has been manually revoked, it won't be automatically created.
If the GitLab Deploy Token cannot be found, `CI_REGISTRY_PASSWORD` is
used. Note that `CI_REGISTRY_PASSWORD` is only valid during deployment.
@@ -557,9 +547,6 @@ be pulled again, e.g. after pod eviction, Kubernetes will fail to do so
as it will be attempting to fetch the image using
`CI_REGISTRY_PASSWORD`.
-NOTE: **Note:**
-When the GitLab Deploy Token has been manually revoked, it won't be automatically created.
-
#### Migrations
> [Introduced][ce-21955] in GitLab 11.4
@@ -588,17 +575,13 @@ For example, in a Rails application in an image built with
- `DB_INITIALIZE` can be set to `RAILS_ENV=production /bin/herokuish procfile exec bin/rails db:setup`
- `DB_MIGRATE` can be set to `RAILS_ENV=production /bin/herokuish procfile exec bin/rails db:migrate`
-NOTE: **Note:**
Unless you have a `Dockerfile` in your repo, your image is built with
-Herokuish. You must prefix commands run in these images with `/bin/herokuish
-procfile exec` in order to replicate the the environment your application is
-run in.
+Herokuish, and you must prefix commands run in these images with `/bin/herokuish
+procfile exec` to replicate the environment where your application will run.
### Auto Monitoring
-NOTE: **Note:**
-Check the [requirements](#requirements) for Auto Monitoring to make this stage
-work.
+See the [requirements](#requirements) for Auto Monitoring to enable this stage.
Once your application is deployed, Auto Monitoring makes it possible to monitor
your application's server and response metrics right out of the box. Auto
@@ -826,11 +809,6 @@ metadata:
type: Opaque
```
-CAUTION: **Caution:**
-Variables with multiline values are not currently supported due to
-limitations with the current Auto DevOps scripting environment.
-
-NOTE: **Note:**
Environment variables are generally considered immutable in a Kubernetes
pod. Therefore, if you update an application secret without changing any
code then manually create a new pipeline, you will find that any running
@@ -839,6 +817,10 @@ can either push a code update to GitLab to force the Kubernetes
Deployment to recreate pods or manually delete running pods to
cause Kubernetes to create new pods with updated secrets.
+NOTE: **Note:**
+Variables with multiline values are not currently supported due to
+limitations with the current Auto DevOps scripting environment.
+
#### Advanced replica variables setup
Apart from the two replica-related variables for production mentioned above,
@@ -1007,8 +989,7 @@ Everything behaves the same way, except:
## Currently supported languages
-NOTE: **Note:**
-Not all buildpacks support Auto Test yet, as it's a relatively new
+Note that not all buildpacks support Auto Test yet, as it's a relatively new
enhancement. All of Heroku's [officially supported
languages](https://devcenter.heroku.com/articles/heroku-ci#currently-supported-languages)
support it, and some third-party buildpacks as well e.g., Go, Node, Java, PHP,
diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md
index cc83d20d65a..6717e95266e 100644
--- a/doc/topics/autodevops/quick_start_guide.md
+++ b/doc/topics/autodevops/quick_start_guide.md
@@ -161,7 +161,7 @@ In the **test** stage, GitLab runs various checks on the application:
- The `code_quality` job checks the code quality and is allowed to fail
([Auto Code Quality](index.md#auto-code-quality-starter)) **[STARTER]**
- The `container_scanning` job checks the Docker container if it has any
- vulnerabilities and is allowed to fail ([Auto Container Scanning](index.md#auto-container-scanning))
+ vulnerabilities and is allowed to fail ([Auto Container Scanning](index.md#auto-container-scanning-ultimate))
- The `dependency_scanning` job checks if the application has any dependencies
susceptible to vulnerabilities and is allowed to fail ([Auto Dependency Scanning](index.md#auto-dependency-scanning-ultimate)) **[ULTIMATE]**
- The `sast` job runs static analysis on the current code to check for potential
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index 841746cc5de..cdcd8215b23 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -21,6 +21,8 @@ More information is also available on the [Git website](https://git-scm.com).
The following resources will help you get started with Git:
+- [Git-ing started with Git](https://www.youtube.com/watch?v=Ce5nz5n41z4),
+ a video introduction to Git.
- [Git Basics](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics)
- [Git on the Server - GitLab](https://git-scm.com/book/en/v2/Git-on-the-Server-GitLab)
- [How to install Git](how_to_install_git/index.md)
diff --git a/doc/university/README.md b/doc/university/README.md
index 61ed72d25fb..c116e54ad48 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -1,5 +1,6 @@
---
comments: false
+type: index
---
# GitLab University
@@ -8,26 +9,22 @@ GitLab University is a great place to start when learning about version control
If you're looking for a GitLab subscription for _your university_, see our [Education](https://about.gitlab.com/solutions/education/) page.
-## GitLab University Curriculum
+The GitLab University curriculum is composed of GitLab videos, screencasts, presentations, projects and external GitLab content hosted on other services and has been organized into the following sections:
-The curriculum is composed of GitLab videos, screencasts, presentations, projects and external GitLab content hosted on other services and has been organized into the following sections.
+1. [GitLab Beginner](#1-gitlab-beginner).
+1. [GitLab Intermediate](#2-gitlab-intermediate).
+1. [GitLab Advanced](#3-gitlab-advanced).
+1. [External Articles](#4-external-articles).
+1. [Resources for GitLab Team Members](#5-resources-for-gitlab-team-members).
-1. [GitLab Beginner](#1-gitlab-beginner)
-1. [GitLab Intermediate](#2-gitlab-intermediate)
-1. [GitLab Advanced](#3-gitlab-advanced)
-1. [External Articles](#4-external-articles)
-1. [Resources for GitLab Team Members](#5-resources-for-gitlab-team-members)
+## 1. GitLab Beginner
----
-
-### 1. GitLab Beginner
-
-#### 1.1. Version Control and Git
+### 1.1. Version Control and Git
1. [Version Control Systems](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.g72f2e4906_2_29)
1. [Code School: An Introduction to Git](https://www.codeschool.com/account/courses/try-git)
-#### 1.2. GitLab Basics
+### 1.2. GitLab Basics
1. [An Overview of GitLab.com - Video](https://www.youtube.com/watch?v=WaiL5DGEMR4)
1. [Why Use Git and GitLab - Slides](https://docs.google.com/a/gitlab.com/presentation/d/1RcZhFmn5VPvoFu6UMxhMOy7lAsToeBZRjLRn0LIdaNc/edit?usp=drive_web)
@@ -36,12 +33,12 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [Git and GitLab Basics - Online Course](https://courses.platzi.com/classes/git-gitlab/concepto/part-1/part-23370/material/)
1. [Comparison of GitLab Versions](https://about.gitlab.com/features/#compare)
-#### 1.3. Your GitLab Account
+### 1.3. Your GitLab Account
1. [Create a GitLab Account - Online Course](https://courses.platzi.com/classes/git-gitlab/concepto/first-steps/create-an-account-on-gitlab/material/)
1. [Create and Add your SSH key to GitLab - Video](https://www.youtube.com/watch?v=54mxyLo3Mqk)
-#### 1.4. GitLab Projects
+### 1.4. GitLab Projects
1. [Repositories, Projects and Groups - Video](https://www.youtube.com/watch?v=4TWfh1aKHHw&index=1&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
1. [Creating a Project in GitLab - Video](https://www.youtube.com/watch?v=7p0hrpNaJ14)
@@ -49,14 +46,14 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [GitLab Todos](https://about.gitlab.com/2016/03/02/gitlab-todos-feature-highlight/)
1. [GitLab's Work in Progress (WIP) Flag](https://about.gitlab.com/2016/01/08/feature-highlight-wip/)
-#### 1.5. Migrating from other Source Control
+### 1.5. Migrating from other Source Control
1. [Migrating from BitBucket/Stash](../user/project/import/bitbucket.md)
1. [Migrating from GitHub](../user/project/import/github.md)
1. [Migrating from SVN](../user/project/import/svn.md)
1. [Migrating from Fogbugz](../user/project/import/fogbugz.md)
-#### 1.6. GitLab Inc.
+### 1.6. GitLab Inc.
1. [About GitLab](https://about.gitlab.com/about/)
1. [GitLab Direction](https://about.gitlab.com/direction/)
@@ -67,7 +64,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [The GitLab Book Club](bookclub/index.md)
1. [GitLab Resources](https://about.gitlab.com/resources/)
-#### 1.7 Community and Support
+### 1.7 Community and Support
1. [Getting Help](https://about.gitlab.com/getting-help/)
- Proposing Features and Reporting and Tracking bugs for GitLab
@@ -79,22 +76,19 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [GitLab Training Workshops](https://docs.gitlab.com/ce/university/training/end-user/)
1. [GitLab Professional Services](https://about.gitlab.com/services/)
-#### 1.8 GitLab Training Material
+### 1.8 GitLab Training Material
-1. [Git and GitLab Terminology](glossary/README.md)
1. [Git and GitLab Workshop - Slides](https://docs.google.com/presentation/d/1JzTYD8ij9slejV2-TO-NzjCvlvj6mVn9BORePXNJoMI/edit?usp=drive_web)
----
-
-### 2. GitLab Intermediate
+## 2. GitLab Intermediate
-#### 2.1 GitLab Pages
+### 2.1 GitLab Pages
1. [Using any Static Site Generator with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
1. [Securing GitLab Pages with SSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/)
1. [GitLab Pages Documentation](../user/project/pages/index.md)
-#### 2.2. GitLab Issues
+### 2.2. GitLab Issues
1. [Markdown in GitLab](../user/markdown.md)
1. [Issues and Merge Requests - Video](https://www.youtube.com/watch?v=raXvuwet78M)
@@ -106,7 +100,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [Designing GitLab Issue Board](https://about.gitlab.com/2016/08/31/designing-issue-boards/)
1. [From Idea to Production with GitLab - Video](https://www.youtube.com/watch?v=25pHyknRgEo&index=14&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
-#### 2.3. Continuous Integration
+### 2.3. Continuous Integration
1. [Operating Systems, Servers, VMs, Containers and Unix - Video](https://www.youtube.com/watch?v=V61kL6IC-zY&index=8&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
1. [GitLab CI - Product Page](https://about.gitlab.com/gitlab-ci/)
@@ -125,7 +119,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [TechBeacon: Doing continuous delivery? Focus first on reducing release cycle times](https://techbeacon.com/doing-continuous-delivery-focus-first-reducing-release-cycle-times)
1. See **[Integrations](#39-integrations)** for integrations with other CI services.
-#### 2.4. Workflow
+### 2.4. Workflow
1. [GitLab Flow - Video](https://youtu.be/enMumwvLAug?list=PLFGfElNsQthZnwMUFi6rqkyUZkI00OxIV)
1. [GitLab Flow vs Forking in GitLab - Video](https://www.youtube.com/watch?v=UGotqAUACZA)
@@ -133,7 +127,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [Always Start with an Issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/)
1. [GitLab Flow Documentation](../workflow/gitlab_flow.md)
-#### 2.5. GitLab Comparisons
+### 2.5. GitLab Comparisons
1. [GitLab Compared to Other Tools](https://about.gitlab.com/comparison/)
1. [Comparing GitLab Terminology](https://about.gitlab.com/2016/01/27/comparing-terms-gitlab-github-bitbucket/)
@@ -141,17 +135,15 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [GitLab Position FAQ](https://about.gitlab.com/handbook/positioning-faq)
1. [Customer review of GitLab with points on why they prefer GitLab](https://www.enovate.co.uk/web-design-blog/2015/11/25/gitlab-review/)
----
+## 3. GitLab Advanced
-### 3. GitLab Advanced
-
-#### 3.1. Dev Ops
+### 3.1. Dev Ops
1. [Xebia Labs: Dev Ops Terminology](https://xebialabs.com/glossary/)
1. [Xebia Labs: Periodic Table of DevOps Tools](https://xebialabs.com/periodic-table-of-devops-tools/)
1. [Puppet Labs: State of Dev Ops 2016 - Book](https://puppet.com/resources/white-paper/2016-state-of-devops-report)
-#### 3.2. Installing GitLab with Omnibus
+### 3.2. Installing GitLab with Omnibus
1. [What is Omnibus - Video](https://www.youtube.com/watch?v=XTmpKudd-Oo)
1. [How to Install GitLab with Omnibus - Video](https://www.youtube.com/watch?v=Q69YaOjqNhg)
@@ -161,34 +153,34 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [Installing GitLab on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/)
1. [Installing GitLab on Digital Ocean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/)
-#### 3.3. Permissions
+### 3.3. Permissions
1. [How to Manage Permissions in GitLab EE - Video](https://www.youtube.com/watch?v=DjUoIrkiNuM)
-#### 3.4. Large Files
+### 3.4. Large Files
1. [Big files in Git (Git LFS) - Video](https://www.youtube.com/watch?v=DawznUxYDe4)
-#### 3.5. LDAP and Active Directory
+### 3.5. LDAP and Active Directory
1. [How to Manage LDAP, Active Directory in GitLab - Video](https://www.youtube.com/watch?v=HPMjM-14qa8)
-#### 3.6 Custom Languages
+### 3.6 Custom Languages
1. [How to add Syntax Highlighting Support for Custom Languages to GitLab - Video](https://youtu.be/6WxTMqatrrA)
-#### 3.7. Scalability and High Availability
+### 3.7. Scalability and High Availability
1. [Scalability and High Availability - Video](https://www.youtube.com/watch?v=cXRMJJb6sp4&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e&index=2)
1. [High Availability - Video](https://www.youtube.com/watch?v=36KS808u6bE&index=15&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
1. [High Availability Documentation](https://about.gitlab.com/high-availability/)
-#### 3.8 Cycle Analytics
+### 3.8 Cycle Analytics
1. [GitLab Cycle Analytics Overview](https://about.gitlab.com/2016/09/21/cycle-analytics-feature-highlight/)
1. [GitLab Cycle Analytics - Product Page](https://about.gitlab.com/product/cycle-analytics/)
-#### 3.9. Integrations
+### 3.9. Integrations
1. [How to Integrate JIRA and Jenkins with GitLab - Video](https://gitlabmeetings.webex.com/gitlabmeetings/ldr.php?RCID=44b548147a67ab4d8a62274047146415)
1. [How to Integrate Jira with GitLab](../user/project/integrations/jira.md)
@@ -198,25 +190,21 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [How to Integrate Convox with GitLab](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/)
1. [Getting Started with GitLab and Shippable CI](https://about.gitlab.com/2016/05/05/getting-started-gitlab-and-shippable/)
----
-
-### 4. External Articles
+## 4. External Articles
1. [2011 WSJ article by Marc Andreessen - Software is Eating the World](https://www.wsj.com/articles/SB10001424053111903480904576512250915629460)
1. [2014 Blog post by Chris Dixon - Software eats software development](http://cdixon.org/2014/04/13/software-eats-software-development/)
1. [2015 Venture Beat article - Actually, Open Source is Eating the World](http://venturebeat.com/2015/12/06/its-actually-open-source-software-thats-eating-the-world/)
----
-
-### 5. Resources for GitLab Team Members
+## 5. Resources for GitLab Team Members
NOTE: **Note:**
-Some content can only be accessed by GitLab team members
+Some content can only be accessed by GitLab team members.
1. [Support Path](support/README.md)
-1. [Sales Path (redirect to sales handbook)](https://about.gitlab.com/handbook/sales-onboarding/)
+1. [Sales Path](https://about.gitlab.com/handbook/sales-onboarding/)
1. [User Training](training/user_training.md)
1. [GitLab Flow Training](training/gitlab_flow.md)
-1. [Training Topics](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/university/training/topics/)
-1. [GitLab architecture for noobs](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/development/architecture.md)
+1. [Training Topics](training/index.md)
+1. [GitLab architecture](../development/architecture.md)
1. [Client Assessment of GitLab versus GitHub](https://docs.google.com/a/gitlab.com/spreadsheets/d/18cRF9Y5I6I7Z_ab6qhBEW55YpEMyU4PitZYjomVHM-M/edit?usp=sharing)
diff --git a/doc/university/bookclub/booklist.md b/doc/university/bookclub/booklist.md
index d5662be6fa6..33298e45393 100644
--- a/doc/university/bookclub/booklist.md
+++ b/doc/university/bookclub/booklist.md
@@ -1,117 +1,118 @@
---
comments: false
+type: index
---
# Books
-List of books and resources, that may be worth reading.
+List of books and resources that may be worth reading.
## Papers
-1. **The Humble Programmer**
+1. **The Humble Programmer**
- Edsger W. Dijkstra, 1972 ([paper](https://dl.acm.org/citation.cfm?id=361591))
+ Edsger W. Dijkstra, 1972 ([paper](https://dl.acm.org/citation.cfm?id=361591))
## Programming
-1. **Design Patterns: Elements of Reusable Object-Oriented Software**
+1. **Design Patterns: Elements of Reusable Object-Oriented Software**
- Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, 1994 ([amazon](https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612))
+ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, 1994 ([amazon](https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612))
-1. **Clean Code: A Handbook of Agile Software Craftsmanship**
+1. **Clean Code: A Handbook of Agile Software Craftsmanship**
- Robert C. "Uncle Bob" Martin, 2008 ([amazon](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882))
+ Robert C. "Uncle Bob" Martin, 2008 ([amazon](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882))
-1. **Code Complete: A Practical Handbook of Software Construction**, 2nd Edition
+1. **Code Complete: A Practical Handbook of Software Construction**, 2nd Edition
- Steve McConnell, 2004 ([amazon](https://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670))
+ Steve McConnell, 2004 ([amazon](https://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670))
-1. **The Pragmatic Programmer: From Journeyman to Master**
+1. **The Pragmatic Programmer: From Journeyman to Master**
- Andrew Hunt, David Thomas, 1999 ([amazon](https://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X))
+ Andrew Hunt, David Thomas, 1999 ([amazon](https://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X))
-1. **Working Effectively with Legacy Code**
+1. **Working Effectively with Legacy Code**
- Michael Feathers, 2004 ([amazon](https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052))
+ Michael Feathers, 2004 ([amazon](https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052))
-1. **Eloquent Ruby**
+1. **Eloquent Ruby**
- Russ Olsen, 2011 ([amazon](https://www.amazon.com/Eloquent-Ruby-Addison-Wesley-Professional/dp/0321584104))
+ Russ Olsen, 2011 ([amazon](https://www.amazon.com/Eloquent-Ruby-Addison-Wesley-Professional/dp/0321584104))
-1. **Domain-Driven Design: Tackling Complexity in the Heart of Software**
+1. **Domain-Driven Design: Tackling Complexity in the Heart of Software**
- Eric Evans, 2003 ([amazon](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215))
+ Eric Evans, 2003 ([amazon](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215))
-1. **How to Solve It: A New Aspect of Mathematical Method**
+1. **How to Solve It: A New Aspect of Mathematical Method**
- Polya G. 1957 ([amazon](https://www.amazon.com/How-Solve-Mathematical-Princeton-Science/dp/069116407X))
+ Polya G. 1957 ([amazon](https://www.amazon.com/How-Solve-Mathematical-Princeton-Science/dp/069116407X))
-1. **Software Creativity 2.0**
+1. **Software Creativity 2.0**
- Robert L. Glass, 2006 ([amazon](https://www.amazon.com/Software-Creativity-2-0-Robert-Glass/dp/0977213315))
+ Robert L. Glass, 2006 ([amazon](https://www.amazon.com/Software-Creativity-2-0-Robert-Glass/dp/0977213315))
-1. **Object-Oriented Software Construction**
+1. **Object-Oriented Software Construction**
- Bertrand Meyer, 1997 ([amazon](https://www.amazon.com/Object-Oriented-Software-Construction-Book-CD-ROM/dp/0136291554))
+ Bertrand Meyer, 1997 ([amazon](https://www.amazon.com/Object-Oriented-Software-Construction-Book-CD-ROM/dp/0136291554))
-1. **Refactoring: Improving the Design of Existing Code**
+1. **Refactoring: Improving the Design of Existing Code**
- Martin Fowler, Kent Beck, 1999 ([amazon](https://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672))
+ Martin Fowler, Kent Beck, 1999 ([amazon](https://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672))
-1. **Test Driven Development: By Example**
+1. **Test Driven Development: By Example**
- Kent Beck, 2002 ([amazon](https://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530))
+ Kent Beck, 2002 ([amazon](https://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530))
-1. **Algorithms in C++: Fundamentals, Data Structure, Sorting, Searching**
+1. **Algorithms in C++: Fundamentals, Data Structure, Sorting, Searching**
- Robert Sedgewick, 1990 ([amazon](https://www.amazon.com/Algorithms-Parts-1-4-Fundamentals-Structure/dp/0201350882))
+ Robert Sedgewick, 1990 ([amazon](https://www.amazon.com/Algorithms-Parts-1-4-Fundamentals-Structure/dp/0201350882))
-1. **Effective C++**
+1. **Effective C++**
- Scott Mayers, 1996 ([amazon](https://www.amazon.com/Effective-Specific-Improve-Programs-Designs/dp/0321334876))
+ Scott Mayers, 1996 ([amazon](https://www.amazon.com/Effective-Specific-Improve-Programs-Designs/dp/0321334876))
-1. **Extreme Programming Explained: Embrace Change**
+1. **Extreme Programming Explained: Embrace Change**
- Kent Beck, 1999 ([amazon](https://www.amazon.com/Extreme-Programming-Explained-Embrace-Change/dp/0321278658))
+ Kent Beck, 1999 ([amazon](https://www.amazon.com/Extreme-Programming-Explained-Embrace-Change/dp/0321278658))
-1. **The Art of Computer Programming**
+1. **The Art of Computer Programming**
- Donald E. Knuth, 1997 ([amazon](https://www.amazon.com/Computer-Programming-Volumes-1-4A-Boxed/dp/0321751043))
+ Donald E. Knuth, 1997 ([amazon](https://www.amazon.com/Computer-Programming-Volumes-1-4A-Boxed/dp/0321751043))
-1. **Writing Efficient Programs**
+1. **Writing Efficient Programs**
- Jon Louis Bentley, 1982 ([amazon](https://www.amazon.com/Writing-Efficient-Programs-Prentice-Hall-Software/dp/013970244X))
+ Jon Louis Bentley, 1982 ([amazon](https://www.amazon.com/Writing-Efficient-Programs-Prentice-Hall-Software/dp/013970244X))
-1. **The Mythical Man-Month: Essays on Software Engineering**
+1. **The Mythical Man-Month: Essays on Software Engineering**
- Frederick Phillips Brooks, 1975 ([amazon](https://www.amazon.com/Mythical-Man-Month-Essays-Software-Engineering/dp/0201006502))
+ Frederick Phillips Brooks, 1975 ([amazon](https://www.amazon.com/Mythical-Man-Month-Essays-Software-Engineering/dp/0201006502))
-1. **Peopleware: Productive Projects and Teams** 3rd Edition
+1. **Peopleware: Productive Projects and Teams** 3rd Edition
- Tom DeMarco, Tim Lister, 2013 ([amazon](https://www.amazon.com/Peopleware-Productive-Projects-Teams-3rd/dp/0321934113))
+ Tom DeMarco, Tim Lister, 2013 ([amazon](https://www.amazon.com/Peopleware-Productive-Projects-Teams-3rd/dp/0321934113))
-1. **Principles Of Software Engineering Management**
+1. **Principles Of Software Engineering Management**
- Tom Gilb, 1988 ([amazon](https://www.amazon.com/Principles-Software-Engineering-Management-Gilb/dp/0201192462))
+ Tom Gilb, 1988 ([amazon](https://www.amazon.com/Principles-Software-Engineering-Management-Gilb/dp/0201192462))
## Other
-1. **Thinking, Fast and Slow**
+1. **Thinking, Fast and Slow**
- Daniel Kahneman, 2013 ([amazon](https://www.amazon.com/Thinking-Fast-Slow-Daniel-Kahneman/dp/0374533555))
+ Daniel Kahneman, 2013 ([amazon](https://www.amazon.com/Thinking-Fast-Slow-Daniel-Kahneman/dp/0374533555))
-1. **The Social Animal** 11th Edition
+1. **The Social Animal** 11th Edition
- Elliot Aronson, 2011 ([amazon](https://www.amazon.com/Social-Animal-Elliot-Aronson/dp/1429233419))
+ Elliot Aronson, 2011 ([amazon](https://www.amazon.com/Social-Animal-Elliot-Aronson/dp/1429233419))
-1. **Influence: Science and Practice** 5th Edition
+1. **Influence: Science and Practice** 5th Edition
- Robert B. Cialdini, 2008 ([amazon](https://www.amazon.com/Influence-Practice-Robert-B-Cialdini/dp/0205609996))
+ Robert B. Cialdini, 2008 ([amazon](https://www.amazon.com/Influence-Practice-Robert-B-Cialdini/dp/0205609996))
-1. **Getting to Yes: Negotiating Agreement Without Giving In**
+1. **Getting to Yes: Negotiating Agreement Without Giving In**
- Roger Fisher, William L. Ury, Bruce Patton, 2011 ([amazon](https://www.amazon.com/Getting-Yes-Negotiating-Agreement-Without/dp/0143118757))
+ Roger Fisher, William L. Ury, Bruce Patton, 2011 ([amazon](https://www.amazon.com/Getting-Yes-Negotiating-Agreement-Without/dp/0143118757))
-1. **How to Win Friends & Influence People**
+1. **How to Win Friends & Influence People**
- Dale Carnegie, 1981 ([amazon](https://www.amazon.com/How-Win-Friends-Influence-People/dp/0671027034))
+ Dale Carnegie, 1981 ([amazon](https://www.amazon.com/How-Win-Friends-Influence-People/dp/0671027034))
diff --git a/doc/university/bookclub/index.md b/doc/university/bookclub/index.md
index 63238685b2b..330078e979f 100644
--- a/doc/university/bookclub/index.md
+++ b/doc/university/bookclub/index.md
@@ -1,5 +1,6 @@
---
comments: false
+type: index
---
# The GitLab Book Club
@@ -11,13 +12,13 @@ See the [book list](booklist.md) for additional recommendations.
## Currently reading : Books about remote work
-1. **Remote: Office not required**
+1. **Remote: Office not required**
- David Heinemeier Hansson and Jason Fried, 2013
- ([amazon](http://www.amazon.co.uk/Remote-Required-David-Heinemeier-Hansson/dp/0091954673))
+ David Heinemeier Hansson and Jason Fried, 2013
+ ([amazon](http://www.amazon.co.uk/Remote-Required-David-Heinemeier-Hansson/dp/0091954673))
-1. **The Year Without Pants**
+1. **The Year Without Pants**
- Scott Berkun, 2013 ([ScottBerkun.com](http://scottberkun.com/yearwithoutpants/))
+ Scott Berkun, 2013 ([ScottBerkun.com](http://scottberkun.com/yearwithoutpants/))
Any other books you'd like to suggest? Edit this page and add them to the queue.
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index f15b0107de5..21e2da3e47c 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -2,707 +2,10 @@
comments: false
---
-# What is the Glossary
+# Glossary
-This contains a simplified list and definitions of some of the terms that you will encounter in your day to day activities when working with GitLab.
-Please add any terms that you discover that you think would be useful for others.
+This page has been removed after an effort to ensure that all applicable GitLab-specific
+terms are available in context on the relevant [GitLab Documentation](https://docs.gitlab.com/)
+or [about.gitlab.com](https://about.gitlab.com/) pages.
-### 2FA
-
-User authentication by combination of 2 different steps during login. This allows for [more security](https://about.gitlab.com/handbook/security/).
-
-### Access Levels
-
-Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. See [GitLab's Permission Guidelines](../../user/permissions.md)
-
-### Active Directory (AD)
-
-A Microsoft-based [directory service](https://msdn.microsoft.com/en-us/library/bb742424.aspx) for windows domain networks. It uses LDAP technology under the hood.
-
-### Agile
-
-Building and [delivering software](http://agilemethodology.org/) in phases/parts rather than trying to build everything at once then delivering to the user/client. The latter is known as the WaterFall model.
-
-### Amazon RDS
-
-External reference: <http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html>
-
-### Application Lifecycle Management (ALM)
-
-The entire product lifecycle management process for an application, from requirements management, development, and testing until deployment. GitLab has [advantages](https://docs.google.com/presentation/d/1vCU-NbZWz8NTNK8Vu3y4zGMAHb5DpC8PE5mHtw1PWfI/edit#slide=id.g72f2e4906_2_288) over both legacy and modern ALM tools.
-
-### Artifactory
-
-A version control [system](https://www.jfrog.com/open-source/#os-arti) for non-text files.
-
-### Artifacts
-
-Objects (usually binary and large) created by a build process. These can include use cases, class diagrams, requirements and design documents.
-
-### Atlassian
-
-A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo.
-
-### Audit Log
-
-Also called an [audit trail](https://en.wikipedia.org/wiki/Audit_trail), an audit log is a document that records an event in an IT system.
-
-### Auto Defined User Group
-
-User groups are a way of centralizing control over important management tasks, particularly access control and password policies. A simple example of such groups are the users and the admins groups.
-In most of the cases these groups are auto defined in terms of access, rules of usage, conditions to be part of, etc.
-
-### Bamboo
-
-Atlassian's CI tool similar to GitLab CI and Jenkins.
-
-### Basic Subscription
-
-Entry level [subscription](https://about.gitlab.com/pricing/) for GitLab EE currently available in packs of 10.
-
-### Bitbucket
-
-Atlassian's web hosting service for Git and Mercurial Projects. Read about [migrating](../../user/project/import/bitbucket.md) from BitBucket to a GitLab instance.
-
-### Branch
-
-A branch is a parallel version of a repository. This allows you to work on the repository without affecting the "master" branch, and without affecting the current "live" version. When you have made all your changes to your branch you can then merge to the master. When your merge request is accepted your changes will be "live."
-
-### Branded Login
-
-Having your own logo on [your GitLab instance login page](../../customization/branded_login_page.md) instead of the GitLab logo.
-
-### Job triggers (Build Triggers)
-These protect your code base against breaks, for instance when a team is working on the same project. Learn about [setting up](../../ci/triggers/README.md) job triggers.
-
-### CEPH
-
- A distributed object store and file [system](http://ceph.com/) designed to provide excellent performance, reliability and scalability.
-
-### ChatOps
-
-The ability to [initiate an action](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/1412) from chat. ChatBots run in your chat application and give you the ability to do "anything" from chat.
-
-### Clone
-
-A [copy](https://git-scm.com/docs/git-clone) of a repository stored on your machine that allows you to use your own editor without being online, but still tracks the changes made remotely.
-
-### Code Review
-
-Examination of a program's code. The main aim is to maintain high quality standards of code that is being shipped. Merge requests [serve as a code review tool](https://about.gitlab.com/2014/09/29/gitlab-flow/) in GitLab.
-
-### Code Snippet
-
-A small amount of code, usually selected for the purpose of showing other developers how to do something specific or reproduce a problem.
-
-### Collaborator
-
-Person with read and write access to a repository who has been invited by repository owner.
-
-### Commit
-
-A [change](https://git-scm.com/docs/git-commit) (revision) to a file that also creates an ID, allowing you to see revision history and the author of the changes.
-
-### Community
-
-[Everyone](https://about.gitlab.com/community/) who uses GitLab.
-
-### Confluence
-
-Atlassian's product for collaboration on documents and projects.
-
-### Continuous Delivery
-
-A [software engineering approach](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which continuous integration, automated testing, and automated deployment capabilities allow software to be developed and deployed rapidly, reliably and repeatedly with minimal human intervention. Still, the deployment to production is defined strategically and triggered manually. [Amazon moves toward continuous delivery](https://www.youtube.com/watch?v=esEFaY0FDKc)
-
-### Continuous Deployment
-
-A [software development practice](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which every code change goes through the entire pipeline and is put into production automatically, resulting in many production deployments every day. It does everything that Continuous Delivery does, but the process is fully automated, there's no human intervention at all. [The difference between Continuous Delivery and Continuous Integration.](https://www.youtube.com/watch?v=igwFj8PPSnw)
-
-### Continuous Integration
-
-A [software development practice](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which you build and test software every time a developer pushes code to the application, and it happens several times a day. [Thoughtworks discusses continuous integration.](https://www.thoughtworks.com/continuous-integration)
-
-### Contributor
-
-Term used for a person contributing to an open source project.
-
-### Conversational Development (ConvDev)
-
-A [natural evolution](https://about.gitlab.com/2016/09/14/gitlab-live-event-recap/) of software development that carries a conversation across functional groups throughout the development process, enabling developers to track the full path of development in a cohesive and intuitive way. ConvDev accelerates the development lifecycle by fostering collaboration and knowledge sharing from idea to production.
-
-### Cycle Analytics
-
-See <https://gitlab.com/gitlab-org/gitlab-ce/issues/22458>
-
-### Cycle Time
-
-The time it takes to move from [idea to production](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab).
-
-### Data Centre
-
-Atlassian product for High Availability.
-
-### Dependencies
-
-As in "specify [dependencies](https://gitlab.com/gitlab-org/gitlab-ce/issues/14728) between stages."
-
-### Deploy Keys
-
-A [SSH key](../../gitlab-basics/create-your-ssh-keys.md)stored on your server that grants access to a single GitLab repository. This is used by a GitLab runner to clone a project's code so that tests can be run against the checked out code.
-
-### Developer
-
-For us at GitLab, this means a software developer, or someone who makes software. It is also one of the levels of access in our multi-level approval system.
-
-### DevOps
-
-The intersection of software engineering, quality assurance, and technology operations. Explore more DevOps topics in the [glossary by XebiaLabs](https://xebialabs.com/glossary/)
-
-### Diff
-
-The difference between two commits, or saved changes. This will also be shown visually after the changes.
-
-### Directory
-
-A folder used for storing multiple files.
-
-### Docker Container Registry
-
-A [feature](../../user/project/container_registry.md) of [GitLab projects](https://about.gitlab.com/2016/05/23/gitlab-container-registry/). Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in.
-
-### Dynamic Environment (review apps)
-
-### EC2 Instance
-
-### Elasticsearch
-
-Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data.
-
-### Emacs
-
-External reference: <https://www.masteringemacs.org/article/mastering-key-bindings-emacs>
-
-### First Byte
-
-External reference: <https://en.wikipedia.org/wiki/Time_To_First_Byte>
-
-First Byte (sometimes referred to as time to first byte or [TTFB](https://en.wikipedia.org/wiki/Time_To_First_Byte)) measures the time between making a request and receiving the first byte of information in return. As a result, First Byte encompasses everything that is the backend as well as network transit issues. It differs from [_Speed Index_](#speed-index) mostly by frontend related issues which are included in Speed Index such as javascript loading, page rendering, and so on.
-
-### Fork
-
-Your [own copy](../../workflow/forking_workflow.md) of a repository that allows you to make changes to the repository without affecting the original.
-
-### Funnel, or: TOFU, MOFU, BOFU
-
-External reference: [Blog post](https://www.weidert.com/whole_brain_marketing_blog/bid/113688/ToFu-MoFu-BoFu-Serving-Up-The-Right-Content-for-Lead-Nurturing)
-
-TOFU: top of funnel
-MOFU: middle of funnel
-BOFU: bottom of funnel
-
-### Gerrit
-
-A code review [tool](https://www.gerritcodereview.com/) built on top of Git.
-
-### Git Attributes
-
-A [git attributes file](https://git-scm.com/docs/gitattributes) is a simple text file that gives attributes to pathnames.
-
-### Git Hooks
-
-[Scripts](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) you can use to trigger actions at certain points.
-
-Difference between a [webhook](#webhooks) and a git hook: a git hook is local to its repo (usually) while a webhook is not (it can make API or http calls). So for example if you want your linter to fire before you commit, you can set that up with a git hook. If the linter fails, the commit does not go through. A git hook _can_ be configured to go beyond its repo, e.g. by having it make an API call.
-
-### GitHost.io
-
-A single-tenant solution that provides GitLab CE or EE as a managed service. GitLab Inc. is responsible for installing, updating, hosting, and backing up customers' own private and secure GitLab instance.
-
-### GitHub
-
-A web-based Git repository hosting service with an enterprise offering. Its main features are: issue tracking, pull request with code review, abundancy of integrations and wiki. It offers free public repos, private repos and enterprise services are paid. Read about [importing a project](../../workflow/importing/import_projects_from_github.md) from GitHub to GitLab.
-
-### GitLab CE
-
-Our free on Premise solution with >100,000 users
-
-### GitLab CI
-
-Our own Continuous Integration [feature](https://about.gitlab.com/gitlab-ci/) that is shipped with each instance
-
-### GitLab EE
-
-Our premium on premise [solution](https://about.gitlab.com/features/#enterprise) that currently has Basic, Standard and Plus subscription packages with additional features and support.
-
-### GitLab.com
-
-Our free SaaS for public and private repositories.
-
-### GitLab Geo
-
-Allows you to replicate your GitLab instance to other geographical locations as a read-only fully operational version. It [can be used](../../administration/geo/replication/index.md) for cloning and fetching projects, in addition to reading any data. This will make working with large repositories over large distances much faster.
-
-### GitLab High Availability
-
-### GitLab Master Plan
-
-Related blog post: <https://about.gitlab.com/2016/09/13/gitlab-master-plan/>.
-
-### GitLab Pages
-
-These allow you to [create websites](https://gitlab.com/help/pages/README.md) for your GitLab projects, groups, or user account.
-
-### GitLab Runner
-
-Related project: <https://gitlab.com/gitlab-org/gitlab-runner>
-
-### Gitolite
-
-An [access layer](https://git-scm.com/book/en/v1/Git-on-the-Server-Gitolite) that sits on top of Git. Users are granted access to repos via a simple config file. As an admin, you only need the users' public SSH key and a username.
-
-### Gitorious
-
-A web-based hosting service for projects using Git. It was acquired by GitLab and we discontinued the service. Read the[Gitorious Acquisition Blog Post](https://about.gitlab.com/2015/03/03/gitlab-acquires-gitorious/).
-
-### Go
-
-An open source programming [language](https://golang.org/).
-
-### Gogs
-
-External reference: <https://gogs.io/>
-
-### GUI/ Git GUI
-
-A portable [graphical interface](https://git-scm.com/docs/git-gui) to Git that allows users to make changes to their repository by making new commits, amending existing ones, creating branches, performing local merges, and fetching/pushing to remote repositories.
-
-### High Availability for Disaster Recovery (HADR)
-
-Sometimes written HA/DR, this usually refers to a strategy for having a failover server in place in case the main server fails.
-
-### Hip Chat
-
-Atlassian's real time chat application for teams, Hip Chat is a competitor to Slack, RocketChat and MatterMost.
-
-### High Availability
-
-Refers to a [system or component](https://about.gitlab.com/high-availability/) that is continuously operational for a desirably long length of time. Availability can be measured relative to "100% operational" or "never failing."
-
-### Inner-sourcing
-
-The [use of](https://about.gitlab.com/2014/09/05/innersourcing-using-the-open-source-workflow-to-improve-collaboration-within-an-organization/) open source development techniques within the corporation.
-
-### Internet Relay Chat (IRC)
-
-An [application layer protocol](http://www.irchelp.org/) that facilitates communication in the form of text.
-
-### Issue Tracker
-
-A [tool](../../integration/external-issue-tracker.md) used to manage, organize, and maintain a list of issues, making it easier for an organization to manage.
-
-### Jenkins
-
-An Open Source CI tool written using the Java programming language. [Jenkins](https://jenkins.io/) does the same job as GitLab CI, Bamboo, and Travis CI. It is extremely popular. Related [documentation](../../integration/jenkins.md).
-
-### Jira
-
-Atlassian's [project management software](https://www.atlassian.com/software/jira), i.e. a complex issue tracker. GitLab [can be configured](../../project_services/jira.md) to interact with JIRA Core either using an on-premise instance or the SaaS solution that Atlassian offers.
-
-### JUnit
-
-A testing framework for the Java programming language, [JUnit](http://junit.org/junit4/) has been important in the evolution of test-driven development.
-
-### Kerberos
-
-A network authentication [protocol](http://web.mit.edu/kerberos/) that uses secret-key cryptography for security.
-
-### Kubernetes
-
-An open source container cluster manager originally designed by Google. It's basically a platform for automating deployment, scaling, and operations of application containers over clusters of hosts.
-
-### Labels
-
-An [identifier](../../user/project/labels.md) to describe a group of one or more specific file revisions.
-
-### Lightweight Directory Access Protocol (LDAP)
-
- A directory (electronic address book) with user information (e.g. name, phone_number etc.)
-
-### LDAP User Authentication
-
-GitLab [integrates](../../administration/auth/ldap.md) with LDAP to support user authentication. This enables GitLab to sign in people from an LDAP server (i.e., allowing people whose names are on the electronic user directory server to be able to use their LDAP accounts to login.)
-
-### LDAP Group Sync
-
-Allows you to synchronize the members of a GitLab group with one or more LDAP groups.
-
-### Lint
-
-Static code analysis for our various file types. For example, we use [scss-lint](https://github.com/brigade/scss-lint) to ensure that a consistent code styling is respected. Similar tools: rubocop / eslint.
-
-### Load Balancer
-
-A [device](https://en.wikipedia.org/wiki/Load_balancing_(computing)) that distributes network or application traffic across multiple servers.
-
-### Git Large File Storage (LFS)
-
-A way [to enable](https://about.gitlab.com/2015/11/23/announcing-git-lfs-support-in-gitlab/) git to handle large binary files by using reference pointers within small text files to point to the large files. Large files such as high resolution images and videos, audio files, and assets can be called from a remote server.
-
-### Linux
-
-An operating system like Windows or OS X. It is mostly used by software developers and on servers.
-
-### Markdown
-
-A lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. Checkout GitLab's [Markdown guide](https://gitlab.com/help/user/markdown.md).
-
-### Maria DB
-
-A community developed fork/variation of MySQL. MySQL is owned by Oracle.
-
-### Master
-
-Name of the [default branch](https://git-scm.com/book/en/v1/Git-Branching-What-a-Branch-Is) in every git repository.
-
-### Mattermost
-
-An open source, self-hosted messaging alternative to Slack. View GitLab's Mattermost [feature](https://gitlab.com/gitlab-org/gitlab-mattermost).
-
-### Mercurial
-
-A free distributed version control system similar to and a competitor with Git.
-
-### Merge
-
-Takes changes from one branch, and [applies them](https://git-scm.com/docs/git-merge) into another branch.
-
-### Merge Conflict
-
-[Arises](https://about.gitlab.com/2016/09/06/resolving-merge-conflicts-from-the-gitlab-ui/) when a merge can't be performed cleanly between two versions of the same file.
-
-#### Merge Request (MR)
-
-[Takes changes](../../gitlab-basics/add-merge-request.md) from one branch, and applies them into another branch.
-
-### Meteor
-
-A [platform](https://www.meteor.com) for building javascript apps.
-
-### Milestones
-
-Allow you to [organize issues](../../user/project/milestones/index.md) and merge requests in GitLab into a cohesive group, optionally setting a due date. A common use is keeping track of an upcoming software version. Milestones are created per-project.
-
-### Mirror Repositories
-
-A project that is set up to automatically have its branches, tags, and commits [updated from an upstream repository](../../workflow/repository_mirroring.md). This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and activity using the familiar GitLab interface.
-
-### MIT License
-
-A type of software license. It lets people do anything with your code with proper attribution and without warranty. It is the most common license for open source applications written in Ruby on Rails. GitLab CE is issued under this [license](../../development/licensing.md). This means you can download the code, modify it as you want, and even build a new commercial product using the underlying code and it's not illegal. The only condition is that there is no form of warranty provided by GitLab so whatever happens when you use the code is your own problem.
-
-### Mondo Rescue
-
-A free disaster recovery [software](https://help.ubuntu.com/community/MondoMindi).
-
-#### Mount
-
-External reference:
-
-As stated on the [wikipedia page](https://en.wikipedia.org/wiki/Mount_(Unix)), "Mounting makes file systems, files, directories, devices and special files available for use and available to the user."
-
-For example, we have NFS servers where the _git files_ reside. In order for a worker node to "see" or "use" the git files, the NFS server needs to be _mounted_ on the worker; that is, the worker needs to know that the NFS server exists and how to connect to it. Think of it as getting a shared drive to show up in your Finder (on Mac) or Explorer (on Windows).
-
-### MySQL
-
-A relational [database](http://www.mysql.com/) owned by Oracle. Currently only supported if you are using EE.
-
-### Namespace
-
-A set of symbols that are used to organize objects of various kinds so that these objects may be referred to by name. Examples of namespaces in action include file systems that assign names to files; programming languages that organize their variables and subroutines in namespaces; and computer networks and distributed systems that assign names to resources, such as computers, printers, websites, (remote) files, etc.
-
-### Nginx
-
-A web [server](https://www.nginx.com/resources/wiki/) (pronounced "engine x"). [It can act](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/nginx.md) as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache.
-
-### OAuth
-
-An open standard for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. GitLab [is](../../integration/oauth_provider.md) an OAuth2 authentication service provider.
-
-### Omnibus Packages
-
-A way to [package different services and tools](https://docs.gitlab.com/omnibus/) required to run GitLab, so that most developers can install it without laborious configuration.
-
-### On Premise
-
-On your own server. In GitLab, this [refers](https://about.gitlab.com/2015/02/12/why-ship-on-premises-in-the-saas-era/) to the ability to download GitLab EE/GitLab CE and host it on your own server rather than using GitLab.com, which is hosted by GitLab Inc's servers.
-
-### Open Core
-
-GitLab's [business model](https://about.gitlab.com/2016/07/20/gitlab-is-open-core-github-is-closed-source/). Coined by Andrew Lampitt in 2008, the [open core model](https://en.wikipedia.org/wiki/Open_core) primarily involves offering a "core" or feature-limited version of a software product as free and open-source software, while offering "commercial" versions or add-ons as proprietary software.
-
-### Open Source Software
-
-Software for which the original source code is freely [available](https://opensource.org/docs/osd) and may be redistributed and modified. GitLab prioritizes open source [stewardship](https://about.gitlab.com/2016/01/11/being-a-good-open-source-steward/). Including to providing access to the source code, open source software must comply with a number of criteria, among them free distribution and no discrimination against persons, groups, or fields of endeavor.
-
-#### Open Source Stewardship
-
-[Related blog post](https://about.gitlab.com/2016/01/11/being-a-good-open-source-steward/).
-
-### Owner
-
-The most powerful person on a GitLab project. They have the permissions of all the other users plus the additional permission of being able to destroy (i.e. delete) the project.
-
-### Platform as a Service (PaaS)
-
-Typically referred to in regards to application development, PaaS is a model in which a cloud provider delivers hardware and software tools to its users as a service.
-
-### Perforce
-
-The company that produces Helix. A commercial, proprietary, centralised VCS well known for its ability to version files of any size and type. They OEM a re-branded version of GitLab called "GitSwarm" that is tightly integrated with their "GitFusion" product, which in turn represents a portion of a Helix repository (called a depot) as a git repo.
-
-### Phabricator
-
-A suite of web-based software development collaboration tools, including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion.
-
-### Piwik Analytics
-
-An open source analytics software to help you analyze web traffic. It is similar to Google Analytics, except that the latter is not open source and information is stored by Google. In Piwik, the information is stored on your own server and hence is fully private.
-
-### Plus Subscription
-
-GitLab Premium EE [subscription](https://about.gitlab.com/pricing/) that includes training and dedicated Account Management and Service Engineer and complete support package.
-
-### PostgreSQL
-
-An [object-relational](https://en.wikipedia.org/wiki/PostgreSQL) database. Touted as the most advanced open source database, it is one of two database management systems [supported by](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/database.md) GitLab, the other being MySQL.
-
-### Protected Branches
-
-A [feature](../../user/project/protected_branches.md) that protects branches from unauthorized pushes, force pushing or deletion.
-
-### Protected Tags
-
-A [feature](../../user/project/protected_tags.md) that protects tags from unauthorized creation, update or deletion
-
-### Pull
-
-Git command to [synchronize](https://git-scm.com/docs/git-pull) the local repository with the remote repository, by fetching all remote changes and merging them into the local repository.
-
-### Puppet
-
-A popular DevOps [automation tool](https://puppet.com/product/how-puppet-works).
-
-### Push
-
-Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. Read about [advanced push rules](https://gitlab.com/help/pages/README.md) in GitLab.
-
-### Raketasks
-
-### RE Read Only
-
-Permissions to see a file and its contents, but not change it.
-
-### Rebase
-
-In addition to the merge, the [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) is a main way to integrate changes from one branch into another.
-
-### Regression
-
-A regression is something that used to work one way in the last release and then we made a **breaking change** and it no longer works the same way.
-
-_or_
-
-A regression is defined as a change that results in a negative impact on the functionality of an existing feature due to recent changes, i.e. the latest release.
-
-### Remote mirroring
-
-### (Git) Repository
-
-A directory where Git [has been initiatlized](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository) to start version controlling your files. The history of your work is stored here. A remote repository is not on your machine, but usually online (like on GitLab.com, for instance). The main remote repository is usually called "Origin."
-
-##### Remote repository
-
-A [repository](https://about.gitlab.com/2015/05/18/simple-words-for-a-gitlab-newbie/) that is not-on-your-machine, so it's anything that is not your computer. Usually, it is online, GitLab.com for instance. The main remote repository is usually called “Origin”.
-
-### Requirements management
-
-Gives your distributed teams a single shared repository to collaborate and share requirements, understand their relationship to tests, and evaluate linked defects. It includes multiple, preconfigured requirement types.
-
-### Revision Control
-
-Also known as version control or source control, this is the management of changes to documents, computer programs, large web sites, and other collections of information. Changes are usually identified by a number or letter code, termed the "revision number," "revision level," or simply "revision."
-
-### RocketChat
-
-An open source chat application for teams, RocketChat is very similar to Slack but it is also open-source.
-
-### Route Table
-
-A route table contains rules (called routes) that determine where network traffic is directed. Each [subnet in a VPC](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Route_Tables.html) must be associated with a route table.
-
-### Runners
-
-Actual build machines/containers that [run and execute tests](https://gitlab.com/gitlab-org/gitlab-runner) you have specified to be run on GitLab CI.
-
-### Sidekiq
-
-The background job processor GitLab [uses](../../administration/troubleshooting/sidekiq.md) to asynchronously run tasks.
-
-### Software as a service (SaaS)
-
-Software that is hosted centrally and accessed on-demand (i.e. whenever you want to). This applies to GitLab.com.
-
-### Software Configuration Management (SCM)
-
-This term is often used by people when they mean "Version Control."
-
-### Scrum
-
-An Agile [framework](https://www.scrum.org/Resources/What-is-Scrum) designed to typically help complete complex software projects. It's made up of several parts: product requirements backlog, sprint planning, sprint (development), sprint review, and retrospec (analyzing the sprint). The goal is to end up with potentially shippable products.
-
-### Scrum Board
-
-The board used to track the status and progress of each of the sprint backlog items.
-
-### Shell
-
-Terminal on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. You [use git](../../gitlab-basics/start-using-git.md) and make changes to GitLab projects in your shell. You [use git](../../gitlab-basics/start-using-git.md) and make changes to GitLab projects in your shell.
-
-### Shell command runner
-
-### Single-tenant
-
-The tenant purchases their own copy of the software and the software can be customized to meet the specific and needs of that customer. [GitHost.io](https://about.gitlab.com/handbook/positioning-faq/) is our provider of single-tenant 'managed cloud' GitLab instances.
-
-### Slack
-
-Real time messaging app for teams that is used internally by GitLab team members. GitLab users can enable [Slack integration](../../project_services/slack.md) to trigger push, issue, and merge request events among others.
-
-### Slash commands
-
-### Slave Servers
-
-Also known as secondary servers, these help to spread the load over multiple machines. They also provide backups when the master/primary server crashes.
-
-### Source Code
-
-Program code as typed by a computer programmer (i.e. it has not yet been compiled/translated by the computer to machine language).
-
-### Speed Index
-
-[Speed Index](https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/metrics/speed-index) is "the average time at which visible parts of the page are displayed".
-
-### SSH Key
-
-A unique identifier of a computer. It is used to identify computers without the need for a password (e.g., On GitLab I have [added the ssh key](../../gitlab-basics/create-your-ssh-keys.md) of all my work machines so that the GitLab instance knows that it can accept code pushes and pulls from this trusted machines whose keys are I have added.)
-
-### Single Sign On (SSO)
-
-An authentication process that allows you enter one username and password to access multiple applications.
-
-### Staging Area
-
-[Staging occurs](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics) before the commit process in git. The staging area is a file, generally contained in your Git directory, that stores information about what will go into your next commit. It’s sometimes referred to as the “index.""
-
-### Standard Subscription
-
-Our mid range EE subscription that includes 24/7 support and support for High Availability [Standard Subscription](https://about.gitlab.com/pricing/).
-
-### Stash
-
-Atlassian's Git on-premise solution. Think of it as Atlassian's GitLab EE, now known as BitBucket Server.
-
-### Static Site Generators (SSGs)
-
-A [software](https://wiki.python.org/moin/StaticSiteGenerator) that takes some text and templates as input and produces html files on the output.
-
-### Subversion
-
-Non-proprietary, centralized version control system.
-
-### Sudo
-
-A program that allows you to perform superuser/administrator actions on Unix Operating Systems (e.g., Linux, OS X.) It actually stands for 'superuser do.'
-
-### Subversion (SVN)
-
-An open source version control system. Read about [migrating from SVN](../../workflow/importing/migrating_from_svn.md) to GitLab using SubGit.
-
-### Tag
-
-[Represents](../../api/tags.md) a version of a particular branch at a moment in time.
-
-### Tenancy
-
-#### Multi-tenant
-
-A [multi-tenant](http://whatis.techtarget.com/definition/multi-tenancy) GitLab instance can have any number of customers - such as companies or groups of users using it. GitLab.com is an example of a multi-tenant GitLab instance.
-
-#### Single-tenant
-
-A [single-tenant](http://searchcloudapplications.techtarget.com/definition/single-tenancy) GitLab instance has only one customer - such as a company - using it. On premise GitLab instances are almost exclusively single-tenant.
-
-### Tool Stack
-
-The set of tools used in a process to achieve a common outcome (e.g. set of tools used in Application Lifecycle Management).
-
-### Trac
-
-An open source project management and bug tracking web [application](https://trac.edgewall.org/).
-
-### True-Up licensing model
-
-### Ubuntu
-
-### Untracked files
-
-New files that Git has not [been told](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) to track previously. Add them by using the command "git add [file path]"
-
-### Upstream repository vs. GitLab repository
-
-[External conversation](https://news.ycombinator.com/item?id=12487112)
-
-### User
-
-Anyone interacting with the software.
-
-### Version Control Software (VCS)
-
-Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. VCS [has evolved](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.gd69537a19_0_32) from local version control systems, to centralized version control systems, to the present [distributed version control systems](https://en.wikipedia.org/wiki/Distributed_version_control) like Git, Mercurial, Bazaar, and Darcs. If any server dies, and these systems were collaborating via it, any of the client repositories can be copied back up to the server to restore it.
-
-### Virtual Private Cloud (VPC)
-
-A [VPC](#virtual-private-cloud-vpc) is an on demand configurable pool of shared computing resources allocated within a public cloud environment, providing some isolation between the different users using the resources. GitLab users need to create a new Amazon VPC in order to [set up High Availability](../../install/aws/index.md).
-
-### Virtual private server (VPS)
-
-A [virtual machine](https://en.wikipedia.org/wiki/Virtual_private_server) sold as a service by an Internet hosting service. A VPS runs its own copy of an operating system, and customers have superuser-level access to that operating system instance, so they can install almost any software that runs on that OS.
-
-### VM Instance
-
-In object-oriented programming, an [instance](http://stackoverflow.com/questions/20461907/what-is-meaning-of-instance-in-programming) is a specific realization of any [object](https://cloud.google.com/compute/docs/instances/). An object may be varied in a number of ways. Each realized variation of that object is an instance. Therefore, a VM instance is an instance of a virtual machine, which is an emulation of a computer system.
-
-### Waterfall
-
-A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the complete software to the customer that meets all the requirements they specified.
-
-### Webhooks
-
-A way for an app to [provide](../../user/project/integrations/webhooks.md) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) Read about setting up [custom git hooks](../../administration/custom_hooks.md) for when webhooks are insufficient.
-
-### Wiki
-
-A [website/system](http://www.wiki.com/) that allows for collaborative editing of its content by the users. In programming, wikis usually contain documentation of how to use the software.
-
-### Working area
-
-Files that have been modified but are not committed. Check them by using the command "git status".
-
-### Working Tree
-
-[Consists of files](http://stackoverflow.com/questions/3689838/difference-between-head-working-tree-index-in-git) that you are currently working on.
-
-### YAML
-
-A human-readable data serialization [language](http://www.yaml.org/about.html) that takes concepts from programming languages such as C, Perl, and Python, and ideas from XML and the data format of electronic mail.
+If you are looking for a definition of a specific term, please search these sites.
diff --git a/doc/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md
index fa04e988042..caaa0a3675b 100644
--- a/doc/university/high-availability/aws/README.md
+++ b/doc/university/high-availability/aws/README.md
@@ -1,394 +1,5 @@
---
-comments: false
+redirect_to: '../../../install/aws/index.md'
---
-> **Note**: We **do not** recommend using the AWS Elastic File System (EFS), as it can result
-in [significantly degraded performance](../../../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs).
-
-# High Availability on AWS
-
-GitLab on AWS can leverage many of the services that are already
-configurable with High Availability. These services have a lot of
-flexibility and are able to adapt to most companies, best of all is the
-ability to automate both vertical and horizontal scaling.
-
-In this article we'll go through a basic HA setup where we'll start by
-configuring our Virtual Private Cloud and subnets to later integrate
-services such as RDS for our database server and ElastiCache as a Redis
-cluster to finally manage them within an auto scaling group with custom
-scaling policies.
-
-***
-
-## Where to Start
-
-Login to your AWS account through the `My Account` dropdown on
-`https://aws.amazon.com` or through the URI assigned to your team such as
-`https://myteam.signin.aws.amazon.com/console/`. You'll start on the
-Amazon Web Services console from where we can choose all of the services
-we'll be using to configure our cloud infrastructure.
-
-### Reference Architecture
-
-![Reference Architecture](img/reference-arch2.png)
-
-***
-
-## Network
-
-We'll start by creating a VPC for our GitLab cloud infrastructure, then
-we can create subnets to have public and private instances in at least
-two AZs. Public subnets will require a Route Table keep an associated
-Internet Gateway.
-
-### VPC
-
-Start by looking for the VPC option on the web console. Now create a new
-VPC. We can use `10.0.0.0/16` for the CIDR block and leave tenancy as
-default if we don't require dedicated hardware.
-
-![New VPC](img/new_vpc.png)
-
-If you're setting up the Elastic File System service then select the VPC
-and from the Actions dropdown choose Edit DNS Hostnames and select Yes.
-
-### Subnet
-
-Now let's create some subnets in different Availability Zones. Make sure
-that each subnet is associated to the VPC we just created, that it has
-a distinct VPC and lastly that CIDR blocks don't overlap. This will also
-allow us to enable multi-AZ for redundancy.
-
-We will create private and public subnets to match load balancers and
-RDS instances as well.
-
-![Subnet Creation](img/subnet.png)
-
-The subnets are listed with their name, AZ and CIDR block:
-
-- gitlab-public-10.0.0.0 - us-west-2a - 10.0.0.0
-- gitlab-private-10.0.1.0 - us-west-2a - 10.0.1.0
-- gitlab-public-10.0.2.0 - us-west-2b - 10.0.2.0
-- gitlab-private-10.0.3.0 - us-west-2b - 10.0.3.0
-
-### Route Table
-
-Up to now all our subnets are private. We need to create a Route Table
-to associate an Internet Gateway. On the same VPC dashboard choose
-Route Tables on the left column and give it a name and associate it to
-our newly created VPC.
-
-![Route Table](img/route_table.png)
-
-### Internet Gateway
-
-Now still on the same dashboard head over to Internet Gateways and
-create a new one. After its created press on the `Attach to VPC` button and
-select our VPC.
-
-![Internet Gateway](img/ig.png)
-
-### Configure Subnets
-
-Go back to the Router Tables screen and select the newly created one,
-press the Routes tab on the bottom section and edit it. We need to add a
-new target which will be our Internet Gateway and have it receive
-traffic from any destination.
-
-![Subnet Config](img/ig-rt.png)
-
-Before leaving this screen select the next tab to the right which is
-Subnet Associations and add our public subnets. If you followed our
-naming convention they should be easy to find.
-
-***
-
-## Database with RDS
-
-For our database server we will use Amazon RDS which offers Multi-AZ
-for redundancy. Let's start by creating a subnet group and then we'll
-create the actual RDS instance.
-
-### Subnet Group
-
-From the RDS dashboard select Subnet Groups. Lets select our VPC from
-the VPC ID dropdown and at the bottom we can add our private subnets.
-
-![Subnet Group](img/db-subnet-group.png)
-
-### RDS
-
-Select the RDS service from the Database section and create a new
-PostgreSQL instance. After choosing between a Production or
-Development instance we'll start with the actual configuration. On the
-image below we have the settings for this article but note the
-following two options which are of particular interest for HA:
-
-1. Multi-AZ-Deployment is recommended as redundancy. Read more at
- [High Availability (Multi-AZ)](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html)
-1. While we chose a General Purpose (SSD) for this article a Provisioned
- IOPS (SSD) is best suited for HA. Read more about it at
- [Storage for Amazon RDS](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html)
-
-![RDS Instance Specs](img/instance_specs.png)
-
-The rest of the setting on this page request a DB identifier, username,
-and a master password. We've chosen to use `gitlab-ha`, `gitlab` and a
-very secure password respectively. Keep these in hand for later.
-
-![Network and Security](img/rds-net-opt.png)
-
-Make sure to choose our gitlab VPC, our subnet group, not have it public,
-and to leave it to create a new security group. The only additional
-change which will be helpful is the database name for which we can use
-`gitlabhq_production`.
-
-***
-
-## ElastiCache
-
-EC is an in-memory hosted caching solution. Redis maintains its own
-persistence and is used for certain types of application.
-
-Let's choose the ElastiCache service in the Database section from our
-AWS console. Now let's create a cache subnet group which will be very
-similar to the RDS subnet group. Make sure to select our VPC and its
-private subnets.
-
-![ElastiCache](img/ec-subnet.png)
-
-Now press the Launch a Cache Cluster and choose Redis for our
-DB engine. You'll be able to configure details such as replication,
-Multi-AZ and node types. The second section will allow us to choose our
-subnet and security group and
-
-![Redis Cluster details](img/redis-cluster-det.png)
-
-![Redis Network](img/redis-net.png)
-
-***
-
-## Network File System
-
-GitLab requires a shared filesystem such as NFS. The file share(s) will be
-mounted on all application servers. There are a variety of ways to build an
-NFS server on AWS.
-
-One option is to use a third-party AMI that offers NFS as a service. A [search
-for 'NFS' in the AWS Marketplace](https://aws.amazon.com/marketplace/search/results?x=0&y=0&searchTerms=NFS&page=1&ref_=nav_search_box)
-shows options such as NetApp, SoftNAS and others.
-
-Another option is to build a simple NFS server using a vanilla Linux server backed
-by AWS Elastic Block Storage (EBS).
-
-> **Note:** GitLab does not recommend using AWS Elastic File System (EFS). See
- details in [High Availability NFS documentation](../../../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs)
-
-***
-
-## Initiate AMI
-
-We are going to launch an EC2 instance and bake an image so that we can
-later use it for auto scaling. We'll also take this opportunity to add an
-extension to our RDS through this temporary EC2 instance.
-
-### EC2 Instance
-
-Look for the EC2 option and choose to create an instance. We'll need at
-least a t2.medium type and for this article we'll choose an Ubuntu 14.04
-HVM 64-bit. In the Configure Instance section choose our GitLab VPC and
-a public subnet. I'd choose at least 10GB of storage.
-
-In the security group we'll create a new one considering that we need to
-SSH into the instance and also try it out through http. So let's add the
-http traffic from anywhere and name it something such as
-`gitlab-ec2-security-group`.
-
-While we wait for it to launch we can allocate an Elastic IP and
-associate it with our new EC2 instance.
-
-### RDS and Redis Security Group
-
-After the instance is being created we will navigate to our EC2 security
-groups and add a small change for our EC2 instances to be able to
-connect to RDS. First copy the security group name we just defined,
-namely `gitlab-ec2-security-group`, and edit select the RDS security
-group and edit the inbound rules. Choose the rule type to be PostgreSQL
-and paste the name under source.
-
-![RDS security group](img/rds-sec-group.png)
-
-Similar to the above we'll jump to the `gitlab-ec2-security-group` group
-and add a custom TCP rule for port 6379 accessible within itself.
-
-### Install GitLab
-
-To connect through SSH you will need to have the `pem` file which you
-chose available and with the correct permissions such as `400`.
-
-After accessing your server don't forget to update and upgrade your
-packages.
-
- sudo apt-get update && sudo apt-get upgrade -y
-
-Then follow installation instructions from
-[GitLab](https://about.gitlab.com/downloads-ee/#ubuntu1404), but before
-running reconfigure we need to make sure all our services are tied down
-so just leave the reconfigure command until after we edit our gitlab.rb
-file.
-
-### Extension for PostgreSQL
-
-Connect to your new RDS instance to verify access and to install
-a required extension. We can find the host or endpoint by selecting the
-instance and we just created and after the details drop down we'll find
-it labeled as 'Endpoint'; do remember not to include the colon and port
-number.
-
- sudo /opt/gitlab/embedded/bin/psql -U gitlab -h <rds-endpoint> -d gitlabhq_production
- psql (9.4.7)
- Type "help" for help.
-
- gitlab=# CREATE EXTENSION pg_trgm;
- gitlab=# \q
-
-### Configure GitLab
-
-While connected to your server edit the `gitlab.rb` file at `/etc/gitlab/gitlab.rb`
-find the `external_url 'http://gitlab.example.com'` option and change it
-to the domain you will be using or the public IP address of the current
-instance to test the configuration.
-
-For a more detailed description about configuring GitLab read [Configuring GitLab for HA](http://docs.gitlab.com/ee/administration/high_availability/gitlab.html)
-
-Now look for the GitLab database settings and uncomment as necessary. In
-our current case we'll specify the adapter, encoding, host, db name,
-username, and password.
-
- gitlab_rails['db_adapter'] = "postgresql"
- gitlab_rails['db_encoding'] = "unicode"
- gitlab_rails['db_database'] = "gitlabhq_production"
- gitlab_rails['db_username'] = "gitlab"
- gitlab_rails['db_password'] = "mypassword"
- gitlab_rails['db_host'] = "<rds-endpoint>"
-
-Next, we only need to configure the Redis section by adding the host and
-uncommenting the port.
-
-The last configuration step is to [change the default file locations ](http://docs.gitlab.com/ee/administration/high_availability/nfs.html)
-to make the EFS integration easier to manage.
-
- gitlab_rails['redis_host'] = "<redis-endpoint>"
- gitlab_rails['redis_port'] = 6379
-
-Finally, run reconfigure. You might find it useful to run a check and
-a service status to make sure everything has been set up correctly.
-
- sudo gitlab-ctl reconfigure
- sudo gitlab-rake gitlab:check
- sudo gitlab-ctl status
-
-If everything looks good copy the Elastic IP over to your browser and
-test the instance manually.
-
-### AMI
-
-After you finish testing your EC2 instance go back to its dashboard and
-while the instance is selected press on the Actions dropdown to choose
-Image -> Create an Image. Give it a name and description and confirm.
-
-***
-
-## Load Balancer
-
-On the same dashboard look for Load Balancer on the left column and press
-the Create button. Choose a classic Load Balancer, our gitlab VPC, not
-internal and make sure its listening for HTTP and HTTPS on port 80.
-
-Here is a tricky part though, when adding subnets we need to associate
-public subnets instead of the private ones where our instances will
-actually live.
-
-On the security group section let's create a new one named
-`gitlab-loadbalancer-sec-group` and allow both HTTP ad HTTPS traffic
-from anywhere.
-
-The Load Balancer Health will allow us to indicate where to ping and what
-makes up a healthy or unhealthy instance.
-
-We won't add the instance on the next session because we'll destroy it
-momentarily as we'll be using the image we were creating. We will keep
-the Enable Cross-Zone and Enable Connection Draining active.
-
-After we finish creating the Load Balancer we can revisit our Security
-Groups to improve access only through the ELB and any other requirement
-you might have.
-
-***
-
-## Auto Scaling Group
-
-Our AMI should be done by now so we can start working on our Auto
-Scaling Group.
-
-This option is also available through the EC2 dashboard on the left
-sidebar. Press on the create button. Select the new image on My AMIs and
-give it a `t2.medium` size. To be able to use Elastic File System we need
-to add a script to mount EFS automatically at launch. We'll do this at
-the Advanced Details section where we have a [User Data](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html)
-text area that allows us to add a lot of custom configurations which
-allows you to add a custom script for when launching an instance. Let's
-add the following script to the User Data section:
-
- #cloud-config
- package_upgrade: true
- packages:
- - nfs-common
- runcmd:
- - mkdir -p /gitlab-data
- - chown ec2-user:ec2-user /gitlab-data
- - echo "$(curl --silent http://169.254.169.254/latest/meta-data/placement/availability-zone).file-system-id.aws-region.amazonaws.com:/ /gitlab-data nfs defaults,vers=4.1 0 0" >> /etc/fstab
- - mount -a -t nfs
- - sudo gitlab-ctl reconfigure
-
-On the security group section we can choose our existing
-`gitlab-ec2-security-group` group which has already been tested.
-
-After this is launched we are able to start creating our Auto Scaling
-Group. Start by giving it a name and assigning it our VPC and private
-subnets. We also want to always start with two instances and if you
-scroll down to Advanced Details we can choose to receive traffic from ELBs.
-Let's enable that option and select our ELB. We also want to use the ELB's
-health check.
-
-![Auto scaling](img/auto-scaling-det.png)
-
-### Policies
-
-This is the really great part of Auto Scaling, we get to choose when AWS
-launches new instances and when it removes them. For this group we'll
-scale between 2 and 4 instances where one instance will be added if CPU
-utilization is greater than 60% and one instance is removed if it falls
-to less than 45%. Here are the complete policies:
-
-![Policies](img/policies.png)
-
-You'll notice that after we save this AWS starts launching our two
-instances in different AZs and without a public IP which is exactly what
-we where aiming for.
-
-***
-
-## Final Thoughts
-
-After you're done with the policies section have some fun trying to break
-instances. You should be able to see how the Auto Scaling Group and the
-EC2 screen starts bringing them up again.
-
-High Availability is a vast area, we went mostly through scaling and
-some redundancy options but it might also imply Geographic replication.
-There is a lot of ground yet to cover so have a read through these other
-resources and feel free to open an issue to request additional material.
-
-- [GitLab High Availability](../../../administration/high_availability/README.md)
-- [GitLab Geo](../../../administration/geo/replication/index.md)
+This document was moved to [another location](../../../install/aws/index.md).
diff --git a/doc/university/high-availability/aws/img/auto-scaling-det.png b/doc/university/high-availability/aws/img/auto-scaling-det.png
deleted file mode 100644
index cf32c024bf8..00000000000
--- a/doc/university/high-availability/aws/img/auto-scaling-det.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/db-subnet-group.png b/doc/university/high-availability/aws/img/db-subnet-group.png
deleted file mode 100644
index 875184af310..00000000000
--- a/doc/university/high-availability/aws/img/db-subnet-group.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/ec-subnet.png b/doc/university/high-availability/aws/img/ec-subnet.png
deleted file mode 100644
index 43ef76b62d3..00000000000
--- a/doc/university/high-availability/aws/img/ec-subnet.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/ig-rt.png b/doc/university/high-availability/aws/img/ig-rt.png
deleted file mode 100644
index 62cca074a1e..00000000000
--- a/doc/university/high-availability/aws/img/ig-rt.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/ig.png b/doc/university/high-availability/aws/img/ig.png
deleted file mode 100644
index 2798d4beac3..00000000000
--- a/doc/university/high-availability/aws/img/ig.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/instance_specs.png b/doc/university/high-availability/aws/img/instance_specs.png
deleted file mode 100644
index 2a2b80103fb..00000000000
--- a/doc/university/high-availability/aws/img/instance_specs.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/new_vpc.png b/doc/university/high-availability/aws/img/new_vpc.png
deleted file mode 100644
index d872554fab7..00000000000
--- a/doc/university/high-availability/aws/img/new_vpc.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/policies.png b/doc/university/high-availability/aws/img/policies.png
deleted file mode 100644
index e99497a52a2..00000000000
--- a/doc/university/high-availability/aws/img/policies.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/rds-net-opt.png b/doc/university/high-availability/aws/img/rds-net-opt.png
deleted file mode 100644
index 13130ac96b8..00000000000
--- a/doc/university/high-availability/aws/img/rds-net-opt.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/rds-sec-group.png b/doc/university/high-availability/aws/img/rds-sec-group.png
deleted file mode 100644
index a88caba62c2..00000000000
--- a/doc/university/high-availability/aws/img/rds-sec-group.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/redis-cluster-det.png b/doc/university/high-availability/aws/img/redis-cluster-det.png
deleted file mode 100644
index 51d3a08eab6..00000000000
--- a/doc/university/high-availability/aws/img/redis-cluster-det.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/redis-net.png b/doc/university/high-availability/aws/img/redis-net.png
deleted file mode 100644
index 9022a9ada78..00000000000
--- a/doc/university/high-availability/aws/img/redis-net.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/reference-arch2.png b/doc/university/high-availability/aws/img/reference-arch2.png
deleted file mode 100644
index 0f8790d0f74..00000000000
--- a/doc/university/high-availability/aws/img/reference-arch2.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/route_table.png b/doc/university/high-availability/aws/img/route_table.png
deleted file mode 100644
index c8bef75f01a..00000000000
--- a/doc/university/high-availability/aws/img/route_table.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/high-availability/aws/img/subnet.png b/doc/university/high-availability/aws/img/subnet.png
deleted file mode 100644
index 681c29bf07a..00000000000
--- a/doc/university/high-availability/aws/img/subnet.png
+++ /dev/null
Binary files differ
diff --git a/doc/university/support/README.md b/doc/university/support/README.md
index c8ade54a77c..9563492c137 100644
--- a/doc/university/support/README.md
+++ b/doc/university/support/README.md
@@ -1,12 +1,13 @@
---
comments: false
+type: reference
---
# Support Boot Camp
**Goal:** Prepare new Service Engineers at GitLab
-For each stage there are learning goals and content to support the learning of the engineer.
+For each stage, there are learning goals and content to support the learning of the engineer.
The goal of this boot camp is to have every Service Engineer prepared to help our customers
with whatever needs they might have and to also assist our awesome community with their
questions.
@@ -15,7 +16,7 @@ Always start with the [University Overview](../README.md) and then work
your way here for more advanced and specific training. Once you feel comfortable
with the topics of the current stage, move to the next.
-### Stage 1
+## Stage 1
Follow the topics on the [University Overview](../README.md), concentrate on it
during your first Stage, but also:
@@ -23,22 +24,22 @@ during your first Stage, but also:
- Perform the [first steps](https://about.gitlab.com/handbook/support/onboarding/#first-steps) of
the on-boarding process for new Service Engineers
-#### Goals
+### Goals
Aim to have a good overview of the Product and main features, Git and the Company
-### Stage 2
+## Stage 2
Continue to look over remaining portions of the [University Overview](../README.md) and continue on to these topics:
-#### Set up your development machine
+### Set up your development machine
Get your development machine ready to familiarize yourself with the codebase, the components, and to be prepared to reproduce issues that our users encounter
- Install the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit)
- [Set up OpenLDAP as part of this](https://gitlab.com/gitlab-org/gitlab-development-kit#openldap)
-#### Become comfortable with the Installation processes that we support
+### Become comfortable with the Installation processes that we support
It's important to understand how to install GitLab in the same way that our users do. Try installing different versions and upgrading and downgrading between them. Installation from source will give you a greater understanding of the components that we employ and how everything fits together.
@@ -54,13 +55,13 @@ Sometimes we need to upgrade customers from old versions of GitLab to latest, so
- Keep this up-to-date as patch and version releases become available, just like our customers would
- Try out the following installation path
- [Install GitLab 4.2 from source](https://gitlab.com/gitlab-org/gitlab-ce/blob/d67117b5a185cfb15a1d7e749588ff981ffbf779/doc/install/installation.md)
- - External MySQL database
- - External NGINX
+ - External MySQL database
+ - External NGINX
- Create some test data
- - Populated Repos
- - Users
- - Groups
- - Projects
+ - Populated Repos
+ - Users
+ - Groups
+ - Projects
- [Backup using our Backup rake task](../../raketasks/backup_restore.md#creating-a-backup-of-the-gitlab-system)
- [Upgrade to 5.0 source using our Upgrade documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/4.2-to-5.0.md)
- [Upgrade to 5.1 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.0-to-5.1.md)
@@ -70,10 +71,10 @@ Sometimes we need to upgrade customers from old versions of GitLab to latest, so
- [Upgrade to Omnibus 7.14](https://docs.gitlab.com/omnibus/update/README.html#upgrading-from-a-non-omnibus-installation-to-an-omnibus-installation)
- [Restore backup using our Restore rake task](../../raketasks/backup_restore.md#restore)
- [Upgrade to latest EE](https://about.gitlab.com/downloads-ee)
- - (GitLab inc. only) Acquire and apply a license for the Enterprise Edition product, ask in #support
+ - (GitLab inc. only) Acquire and apply a license for the Enterprise Edition product, ask in #support
- Perform a downgrade from [EE to CE](../../downgrade_ee_to_ce/README.md)
-#### Start to learn about some of the integrations that we support
+### Start to learn about some of the integrations that we support
Our integrations add great value to GitLab. User questions often relate to integrating GitLab with existing external services and the configuration involved
@@ -83,28 +84,28 @@ Our integrations add great value to GitLab. User questions often relate to integ
- [Jenkins](../../integration/jenkins.md)
- [SAML](../../integration/saml.md)
-#### Goals
+### Goals
- Aim to be comfortable with installation of the GitLab product and configuration of some of the major integrations
- Aim to have an installation available for reproducing customer reports
-### Stage 3
+## Stage 3
-#### Understand the gathering of diagnostics for GitLab instances
+### Understand the gathering of diagnostics for GitLab instances
-- Learn about the GitLab checks that are available
+- Learn about the GitLab checks that are available:
- [Environment Information and maintenance checks](../../raketasks/maintenance.md)
- [GitLab check](../../raketasks/check.md)
- Omnibus commands
- - [Status](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#get-service-status)
- - [Starting and stopping services](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#starting-and-stopping)
- - [Starting a rails console](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#invoking-rake-tasks)
+ - [Status](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#get-service-status)
+ - [Starting and stopping services](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#starting-and-stopping)
+ - [Starting a rails console](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#invoking-rake-tasks)
-#### Learn about the Support process
+### Learn about the Support process
Zendesk is our Support Centre and our main communication line with our Customers. We communicate with customers through several other channels too
-- Familiarize yourself with ZenDesk
+- Familiarize yourself with ZenDesk:
- [UI Overview](https://support.zendesk.com/hc/en-us/articles/203661806-Introduction-to-the-Zendesk-agent-interface)
- [Updating Tickets](https://support.zendesk.com/hc/en-us/articles/212530318-Updating-and-solving-tickets)
- [Working w/ Tickets](https://support.zendesk.com/hc/en-us/articles/203690856-Working-with-tickets) *Read: avoiding agent collision.*
@@ -116,16 +117,16 @@ Zendesk is our Support Centre and our main communication line with our Customers
- Here you will find a large variety of queries mainly from our Users who are self hosting GitLab CE
- Understand the questions that are asked and dig in to try to find a solution
- [Proceed on to the GitLab.com Support Forum](https://about.gitlab.com/handbook/support/#gitlabcom-support-trackera-namesupp-foruma)
- - Here you will find queries regarding our own GitLab.com
- - Helping Users here will give you an understanding of our Admin interface and other tools
+ - Here you will find queries regarding our own GitLab.com
+ - Helping Users here will give you an understanding of our Admin interface and other tools
- [Proceed on to the Twitter tickets in Zendesk](https://about.gitlab.com/handbook/support/#twitter)
- - Here you will gain a great insight into our userbase
- - Learn from any complaints and problems and feed them back to the team
- - Tweets can range from help needed with GitLab installations, the API and just general queries
+ - Here you will gain a great insight into our userbase
+ - Learn from any complaints and problems and feed them back to the team
+ - Tweets can range from help needed with GitLab installations, the API and just general queries
- [Proceed on to Regular email Support tickets](https://about.gitlab.com/handbook/support/#regular-zendesk-tickets-a-nameregulara)
- - Here you will find tickets from our GitLab EE Customers and GitLab CE Users
- - Tickets here are extremely varied and often very technical
- - You should be prepared for these tickets, given the knowledge gained from previous tiers and your training
+ - Here you will find tickets from our GitLab EE Customers and GitLab CE Users
+ - Tickets here are extremely varied and often very technical
+ - You should be prepared for these tickets, given the knowledge gained from previous tiers and your training
- Check out your colleagues' responses
- Hop on to the #support-live-feed channel in Slack and see the tickets as they come in and are updated
- Read through old tickets that your colleagues have worked on
@@ -133,14 +134,14 @@ Zendesk is our Support Centre and our main communication line with our Customers
- [Learn about Cisco WebEx](https://about.gitlab.com/handbook/support/onboarding/#webexa-namewebexa)
- Training calls
- Information gathering calls
- - It's good to find out how new and prospective customers are going to be using the product and how they will set up their infrastructure
+ - It's good to find out how new and prospective customers are going to be using the product and how they will set up their infrastructure
- Diagnosis calls
- - When email isn't enough we may need to hop on a call and do some debugging along side the customer
- - These paired calls are a great learning experience
+ - When email isn't enough we may need to hop on a call and do some debugging along side the customer
+ - These paired calls are a great learning experience
- Upgrade calls
- Emergency calls
-#### Learn about the Escalation process for tickets
+### Learn about the Escalation process for tickets
Some tickets need specific knowledge or a deep understanding of a particular component and will need to be escalated to a Senior Service Engineer or Developer
@@ -148,7 +149,7 @@ Some tickets need specific knowledge or a deep understanding of a particular com
- Find the macros in Zendesk for ticket escalations
- Take a look at the [GitLab.com Team page](https://about.gitlab.com/team/) to find the resident experts in their fields
-#### Learn about raising issues and fielding feature proposals
+### Learn about raising issues and fielding feature proposals
- Understand what's in the pipeline and proposed features at GitLab: [Direction Page](https://about.gitlab.com/direction/)
- Practice searching issues and filtering using [labels](https://gitlab.com/gitlab-org/gitlab-ce/labels) to find existing feature proposals and bugs
@@ -157,15 +158,15 @@ Some tickets need specific knowledge or a deep understanding of a particular com
- Take a look at the [existing issue templates](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker) to see what is expected
- Raise issues for bugs in a manner that would make the issue easily reproducible. A Developer or a contributor may work on your issue
-#### Goals
+### Goals
- Aim to have a good understanding of the problems that customers are facing
- Aim to have gained experience in scheduling and participating in calls with customers
- Aim to have a good understanding of ticket flow through Zendesk and how to interact with our various channels
-### Stage 4
+## Stage 4
-#### Advanced GitLab topics
+### Advanced GitLab topics
Move on to understanding some of GitLab's more advanced features. You can make use of GitLab.com to understand the features from an end-user perspective and then use your own instance to understand setup and configuration of the feature from an Administrative perspective
@@ -179,11 +180,23 @@ Move on to understanding some of GitLab's more advanced features. You can make u
and the [CE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
- Ask as many questions as you can think of on the `#support` chat channel
-#### Get initiated for on-call duty
+### Get initiated for on-call duty
- Read over the [public run-books to understand common tasks](https://gitlab.com/gitlab-com/runbooks)
- Create an issue on the internal Organization tracker to schedule time with the DevOps / Production team, so that you learn how to handle GitLab.com going down. Once you are trained for this, you are ready to be added to the on-call rotation.
-#### Goals
+### Goals
- Aim to become a fully-fledged Service Engineer!
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/university/training/gitlab_flow.md b/doc/university/training/gitlab_flow.md
index d7bc7bda43f..0ce92be4994 100644
--- a/doc/university/training/gitlab_flow.md
+++ b/doc/university/training/gitlab_flow.md
@@ -1,5 +1,6 @@
---
comments: false
+type: reference
---
# What is the GitLab Flow
@@ -41,5 +42,17 @@ comments: false
## More details
-For more information read through the [GitLab Flow](../../workflow/gitlab_flow.md)
+For more information, read through the [GitLab Flow](../../workflow/gitlab_flow.md)
documentation.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/university/training/index.md b/doc/university/training/index.md
index 14f096b130f..ddfc662123d 100644
--- a/doc/university/training/index.md
+++ b/doc/university/training/index.md
@@ -1,5 +1,6 @@
---
comments: false
+type: index
---
# GitLab Training Material
@@ -8,3 +9,34 @@ All GitLab training material is stored in markdown format. Slides are
generated using [Deskset](http://www.decksetapp.com/).
All training material is open to public contribution.
+
+This section contains the following topics:
+
+- [Agile and Git](topics/agile_git.md).
+- [Bisect](topics/bisect.md).
+- [Cherry pick](topics/cherry_picking.md).
+- [Code review and collaboration with Merge Requests](topics/merge_requests.md).
+- [Configure your environment](topics/env_setup.md).
+- [Explore GitLab](topics/explore_gitlab.md).
+- [Feature branching](topics/feature_branching.md).
+- [Getting started](topics/getting_started.md).
+- [GitLab flow](gitlab_flow.md).
+- [GitLab Git workshop](user_training.md).
+- [Git add](topics/git_add.md).
+- [Git introduction](topics/git_intro.md).
+- [Git log](topics/git_log.md).
+- [Git stash](topics/stash.md).
+- [Merge conflicts](topics/merge_conflicts.md).
+- [Rollback commits](topics/rollback_commits.md).
+- [Subtree](topics/subtree.md).
+- [Tags](topics/tags.md).
+- [Unstage](topics/unstage.md).
+
+## Additional Resources
+
+1. [GitLab Documentation](http://docs.gitlab.com)
+1. [GUI Clients](http://git-scm.com/downloads/guis)
+1. [Pro Git book](http://git-scm.com/book)
+1. [Platzi Course](https://courses.platzi.com/courses/git-gitlab/)
+1. [Code School tutorial](http://try.github.io/)
+1. Contact us at `subscribers@gitlab.com`
diff --git a/doc/university/training/topics/additional_resources.md b/doc/university/training/topics/additional_resources.md
deleted file mode 100644
index 4871372d105..00000000000
--- a/doc/university/training/topics/additional_resources.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-comments: false
----
-
-# Additional Resources
-
-1. GitLab Documentation: <http://docs.gitlab.com>.
-1. GUI Clients: <http://git-scm.com/downloads/guis>.
-1. Pro Git book: <http://git-scm.com/book>.
-1. Platzi Course: <https://courses.platzi.com/courses/git-gitlab/>.
-1. Code School tutorial: <http://try.github.io/>.
-1. Contact us at `subscribers@gitlab.com`.
diff --git a/doc/university/training/topics/agile_git.md b/doc/university/training/topics/agile_git.md
index 251af99bed7..42d91a50045 100644
--- a/doc/university/training/topics/agile_git.md
+++ b/doc/university/training/topics/agile_git.md
@@ -4,22 +4,16 @@ comments: false
# Agile and Git
-----------
-
## Agile
Lean software development methods focused on collaboration and interaction
with fast and smaller deployment cycles.
-----------
-
## Where Git comes in
Git is an excellent tool for an Agile team considering that it allows
decentralized and simultaneous development.
-----------
-
### Branching And Workflows
Branching in an Agile environment usually happens around user stories with one
@@ -30,8 +24,7 @@ with his/her initials, and US id.
After its tested merge into master and remove the branch.
-----------
-
## What about GitLab
+
Tools like GitLab enhance collaboration by adding dialog around code mainly
through issues and merge requests.
diff --git a/doc/university/training/topics/bisect.md b/doc/university/training/topics/bisect.md
index 4848d0412c1..4178afa2086 100644
--- a/doc/university/training/topics/bisect.md
+++ b/doc/university/training/topics/bisect.md
@@ -4,16 +4,12 @@ comments: false
# Bisect
-----------
-
## Bisect
- Find a commit that introduced a bug
- Works through a process of elimination
- Specify a known good and bad revision to begin
-----------
-
## Bisect
1. Start the bisect process
@@ -23,11 +19,9 @@ comments: false
1. Tell bisect the result
1. Repeat the previous 2 items until you find the offending commit
-----------
-
## Setup
-```
+```sh
mkdir bisect-ex
cd bisect-ex
touch index.html
@@ -44,9 +38,7 @@ comments: false
vi index.html
```
-----------
-
-```
+```sh
# Add all good 3
git add -A
git commit -m "fourth commit"
@@ -64,11 +56,9 @@ comments: false
git commit -m "seventh commit"
```
-----------
-
## Commands
-```
+```sh
git bisect start
# Test your code
git bisect bad
diff --git a/doc/university/training/topics/cherry_picking.md b/doc/university/training/topics/cherry_picking.md
index df23024b6ee..fa0cb5fe6a4 100644
--- a/doc/university/training/topics/cherry_picking.md
+++ b/doc/university/training/topics/cherry_picking.md
@@ -4,16 +4,12 @@ comments: false
# Cherry Pick
-----------
-
## Cherry Pick
- Given an existing commit on one branch, apply the change to another branch
- Useful for backporting bug fixes to previous release branches
- Make the commit on the master branch and pick in to stable
-----------
-
## Cherry Pick
1. Check out a new 'stable' branch from 'master'
@@ -23,8 +19,6 @@ comments: false
1. Check out the 'stable' branch
1. Cherry pick the commit using the SHA obtained earlier
-----------
-
## Commands
```bash
diff --git a/doc/university/training/topics/env_setup.md b/doc/university/training/topics/env_setup.md
index 78ca30e0f55..305f5ecb1fb 100644
--- a/doc/university/training/topics/env_setup.md
+++ b/doc/university/training/topics/env_setup.md
@@ -4,7 +4,6 @@ comments: false
# Configure your environment
-----------
## Install
- **Windows**
@@ -22,8 +21,6 @@ comments: false
sudo apt-get install git-all
```
-----------
-
## Configure Git
One-time configuration of the Git client
@@ -33,8 +30,6 @@ git config --global user.name "Your Name"
git config --global user.email you@example.com
```
-----------
-
## Configure SSH Key
```bash
diff --git a/doc/university/training/topics/explore_gitlab.md b/doc/university/training/topics/explore_gitlab.md
index 84a1879cd92..4ca931d0e26 100644
--- a/doc/university/training/topics/explore_gitlab.md
+++ b/doc/university/training/topics/explore_gitlab.md
@@ -4,8 +4,6 @@ comments: false
# Explore GitLab projects
-----------
-
- Dashboard
- User Preferences
- Issues
diff --git a/doc/university/training/topics/feature_branching.md b/doc/university/training/topics/feature_branching.md
index 0df5f26dbea..d2efe634533 100644
--- a/doc/university/training/topics/feature_branching.md
+++ b/doc/university/training/topics/feature_branching.md
@@ -4,8 +4,6 @@ comments: false
# Feature branching
-----------
-
- Efficient parallel workflow for teams
- Develop each feature in a branch
- Keeps changes isolated
@@ -13,8 +11,6 @@ comments: false
- Push branches to the server frequently
- Hint: This is a cheap backup for your work-in-progress code
-----------
-
## Feature branching
1. Create a new feature branch called 'squash_some_bugs'
@@ -22,11 +18,9 @@ comments: false
1. Commit
1. Push
-----------
-
## Commands
-```
+```sh
git checkout -b squash_some_bugs
# Edit `bugs.rb`
git status
diff --git a/doc/university/training/topics/getting_started.md b/doc/university/training/topics/getting_started.md
index d76ff57bfa3..08027c5d15b 100644
--- a/doc/university/training/topics/getting_started.md
+++ b/doc/university/training/topics/getting_started.md
@@ -4,8 +4,6 @@ comments: false
# Getting Started
-----------
-
## Instantiating Repositories
- Create a new repository by instantiating it through:
@@ -19,8 +17,6 @@ comments: false
git clone <url>
```
-----------
-
## Central Repos
- To instantiate a central repository a `--bare` flag is required.
@@ -31,27 +27,22 @@ comments: false
git init --bare project-name.git
```
-----------
-
## Instantiate workflow with clone
1. Create a project in your user namespace.
- - Choose to import from 'Any Repo by URL' and use <https://gitlab.com/gitlab-org/training-examples.git>.
+ - Choose to import from 'Any Repo by URL' and use <https://gitlab.com/gitlab-org/training-examples.git>.
1. Create a '`Workspace`' directory in your home directory.
1. Clone the '`training-examples`' project.
-----------
-
## Commands
-```
+```sh
mkdir ~/workspace
cd ~/workspace
git clone git@gitlab.example.com:<username>/training-examples.git
cd training-examples
```
-----------
## Git concepts
@@ -67,8 +58,6 @@ Files that have been modified but are not committed.
Modified files that have been marked to go in the next commit.
-----------
-
## Committing Workflow
1. Edit '`edit_this_file.rb`' in '`training-examples`'
@@ -79,11 +68,9 @@ Modified files that have been marked to go in the next commit.
1. Push the commit to the remote
1. View the git log
-----------
-
## Commands
-```
+```sh
# Edit `edit_this_file.rb`
git status
git diff
@@ -93,8 +80,6 @@ git push origin master
git log
```
-----------
-
## Note
- git fetch vs pull
diff --git a/doc/university/training/topics/git_add.md b/doc/university/training/topics/git_add.md
index e02a7deab91..4c61d5eb175 100644
--- a/doc/university/training/topics/git_add.md
+++ b/doc/university/training/topics/git_add.md
@@ -4,8 +4,6 @@ comments: false
# Git Add
-----------
-
## Git Add
Adds content to the index or staging area.
@@ -22,8 +20,6 @@ Adds content to the index or staging area.
git add -A
```
-----------
-
## Git add continued
- Add all text files in current dir:
diff --git a/doc/university/training/topics/git_intro.md b/doc/university/training/topics/git_intro.md
index 127323c292c..845bb7f0a81 100644
--- a/doc/university/training/topics/git_intro.md
+++ b/doc/university/training/topics/git_intro.md
@@ -4,8 +4,6 @@ comments: false
# Git introduction
-----------
-
## Intro
<https://git-scm.com/about>
@@ -17,8 +15,6 @@ comments: false
- Adapts to nearly any workflow
- Fast, reliable and stable file format
-----------
-
## Help!
Use the tools at your disposal when you get stuck.
diff --git a/doc/university/training/topics/git_log.md b/doc/university/training/topics/git_log.md
index 763ef802a04..11addcd3ee1 100644
--- a/doc/university/training/topics/git_log.md
+++ b/doc/university/training/topics/git_log.md
@@ -4,44 +4,38 @@ comments: false
# Git Log
-----------
-
Git log lists commit history. It allows searching and filtering.
- Initiate log:
- ```
+ ```sh
git log
```
- Retrieve set number of records:
- ```
+ ```sh
git log -n 2
```
- Search commits by author. Allows user name or a regular expression.
- ```
+ ```sh
git log --author="user_name"
```
-----------
-
- Search by comment message:
- ```
+ ```sh
git log --grep="<pattern>"
```
- Search by date:
- ```
+ ```sh
git log --since=1.month.ago --until=3.weeks.ago
```
-----------
-
## Git Log Workflow
1. Change to workspace directory
@@ -51,11 +45,9 @@ Git log lists commit history. It allows searching and filtering.
1. Search by date
1. Combine
-----------
-
## Commands
-```
+```sh
cd ~/workspace
git clone git@gitlab.com:gitlab-org/gitlab-runner.git
cd gitlab-runner
diff --git a/doc/university/training/topics/merge_conflicts.md b/doc/university/training/topics/merge_conflicts.md
index a7d42904229..dd235fe3a81 100644
--- a/doc/university/training/topics/merge_conflicts.md
+++ b/doc/university/training/topics/merge_conflicts.md
@@ -4,15 +4,11 @@ comments: false
# Merge conflicts
-----------
-
- Happen often
- Learning to fix conflicts is hard
- Practice makes perfect
- Force push after fixing conflicts. Be careful!
-----------
-
## Merge conflicts
1. Checkout a new branch and edit `conflicts.rb`. Add 'Line4' and 'Line5'.
@@ -26,11 +22,9 @@ comments: false
1. Force push the changes.
1. Finally continue with the Merge Request.
-----------
-
## Commands
-```
+```sh
git checkout -b conflicts_branch
# vi conflicts.rb
@@ -49,7 +43,7 @@ git push origin master
Create a merge request on the GitLab web UI. You'll see a conflict warning.
-```
+```sh
git checkout conflicts_branch
git fetch
git rebase master
@@ -65,7 +59,6 @@ git rebase --continue
# need to force push so that our remote branch is restructured
git push origin conflicts_branch -f
```
-----------
## Note
diff --git a/doc/university/training/topics/merge_requests.md b/doc/university/training/topics/merge_requests.md
index d7b771cd87b..b5bbe7b2e1e 100644
--- a/doc/university/training/topics/merge_requests.md
+++ b/doc/university/training/topics/merge_requests.md
@@ -4,8 +4,6 @@ comments: false
# Code review and collaboration with Merge Requests
-----------
-
- When you want feedback create a merge request
- Target is the default branch (usually master)
- Assign or mention the person you would like to review
@@ -14,8 +12,6 @@ comments: false
- Anyone can comment, not just the assignee
- Push corrections to the same branch
-----------
-
## Merge requests
**Create your first merge request**
@@ -25,8 +21,6 @@ comments: false
1. Push a new commit to the same branch
1. Review the changes again and notice the update
-----------
-
## Feedback and Collaboration
- Merge requests are a time for feedback and collaboration
@@ -36,8 +30,6 @@ comments: false
- Be as receptive as possible
- Feedback is about the best code, not the person. You are not your code
-----------
-
## Feedback and Collaboration
Review the Thoughtbot code-review guide for suggestions to follow when reviewing merge requests:
diff --git a/doc/university/training/topics/rollback_commits.md b/doc/university/training/topics/rollback_commits.md
index 96b89e3319a..1e6602deef2 100644
--- a/doc/university/training/topics/rollback_commits.md
+++ b/doc/university/training/topics/rollback_commits.md
@@ -4,40 +4,34 @@ comments: false
# Rollback Commits
-----------
-
## Undo Commits
- Undo last commit putting everything back into the staging area:
- ```
+ ```sh
git reset --soft HEAD^
```
- Add files and change message with:
- ```
+ ```sh
git commit --amend -m "New Message"
```
-----------
-
- Undo last and remove changes:
- ```
+ ```sh
git reset --hard HEAD^
```
- Same as last one but for two commits back:
- ```
+ ```sh
git reset --hard HEAD^^
```
** Don't reset after pushing **
-----------
-
## Reset Workflow
1. Edit file again 'edit_this_file.rb'
@@ -51,11 +45,9 @@ comments: false
1. Pull for updates
1. Push changes
-----------
-
## Commands
-```
+```sh
# Change file edit_this_file.rb
git status
git commit -am "kjkfjkg"
@@ -68,15 +60,13 @@ git pull origin master
git push origin master
```
-----------
-
## Note
- git revert vs git reset
- Reset removes the commit while revert removes the changes but leaves the commit
- Revert is safer considering we can revert a revert
-```
+```sh
# Changed file
git commit -am "bug introduced"
git revert HEAD
diff --git a/doc/university/training/topics/stash.md b/doc/university/training/topics/stash.md
index dfd28fbcbc9..02b2f610266 100644
--- a/doc/university/training/topics/stash.md
+++ b/doc/university/training/topics/stash.md
@@ -4,14 +4,12 @@ comments: false
# Git Stash
-----------
-
We use git stash to store our changes when they are not ready to be committed
and we need to change to a different branch.
- Stash:
- ```
+ ```sh
git stash save
# or
git stash
@@ -21,18 +19,16 @@ and we need to change to a different branch.
- Apply stash to keep working on it:
- ```
+ ```sh
git stash apply
# or apply a specific one from out stack
git stash apply stash@{3}
```
-----------
-
- Every time we save a stash it gets stacked so by using list we can see all our
stashes.
- ```
+ ```sh
git stash list
# or for more information (log methods)
git stash list --stat
@@ -40,7 +36,7 @@ and we need to change to a different branch.
- To clean our stack we need to manually remove them:
- ```
+ ```sh
# drop top stash
git stash drop
# or
@@ -49,19 +45,15 @@ and we need to change to a different branch.
git stash clear
```
-----------
-
- Apply and drop on one command:
- ```
+ ```sh
git stash pop
```
- If we meet conflicts we need to either reset or commit our changes.
- Conflicts through `pop` will not drop a stash afterwards.
-----------
-
## Git Stash
1. Modify a file
@@ -72,11 +64,9 @@ and we need to change to a different branch.
1. Apply with pop
1. View list to confirm changes
-----------
-
## Commands
-```
+```sh
# Modify edit_this_file.rb file
git add .
diff --git a/doc/university/training/topics/subtree.md b/doc/university/training/topics/subtree.md
index ba7c3394938..981918d4758 100644
--- a/doc/university/training/topics/subtree.md
+++ b/doc/university/training/topics/subtree.md
@@ -9,19 +9,15 @@ comments: false
- For these cases we need a dependency control system.
- Command are painfully long so aliases are necessary.
-----------
-
## Subtree Aliases
-- Add: git subtree add --prefix <target-folder> <url> <branch> --squash.
-- Pull: git subtree add --prefix <target-folder> <url> <branch> --squash.
-- Push: git subtree add --prefix <target-folder> <url> <branch>.
-- Ex: git config alias.sbp 'subtree pull --prefix st /
- git@gitlab.com:balameb/subtree-nested-example.git master --squash'.
-
-----------
+- Add: `git subtree add --prefix <target-folder> <url> <branch> --squash`.
+- Pull: `git subtree add --prefix <target-folder> <url> <branch> --squash`.
+- Push: `git subtree add --prefix <target-folder> <url> <branch>`.
+- Ex: `git config alias.sbp 'subtree pull --prefix st /
+ git@gitlab.com:balameb/subtree-nested-example.git master --squash'`.
-```
+```sh
# Add an alias
# Add
git config alias.sba 'subtree add --prefix st /
@@ -41,9 +37,7 @@ comments: false
```
-----------
-
-```
+```sh
# Adding, or committing won't change the sub repo at remote
# even if we push
git add -A
diff --git a/doc/university/training/topics/tags.md b/doc/university/training/topics/tags.md
index 14c39457838..cdbb8a2da7c 100644
--- a/doc/university/training/topics/tags.md
+++ b/doc/university/training/topics/tags.md
@@ -1,19 +1,16 @@
---
comments: false
+type: reference
---
# Tags
-----------
-
- Useful for marking deployments and releases
- Annotated tags are an unchangeable part of Git history
- Soft/lightweight tags can be set and removed at will
- Many projects combine an annotated release tag with a stable branch
- Consider setting deployment/release tags automatically
-----------
-
# Tags
- Create a lightweight tag
@@ -24,11 +21,9 @@ comments: false
<https://git-scm.com/book/en/Git-Basics-Tagging>
-----------
-
# Commands
-```
+```sh
git checkout master
# Lightweight tag
@@ -40,3 +35,15 @@ git tag
git push origin --tags
```
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/university/training/topics/unstage.md b/doc/university/training/topics/unstage.md
index c926f0b4888..af16afdc5d1 100644
--- a/doc/university/training/topics/unstage.md
+++ b/doc/university/training/topics/unstage.md
@@ -4,8 +4,6 @@ comments: false
# Unstage
-----------
-
## Unstage
- To remove files from stage use reset HEAD where HEAD is the last commit of the current branch. This will unstage the file but maintain the modifications.
@@ -20,17 +18,15 @@ comments: false
git checkout -- <file>
```
-----------
-
- To remove a file from disk and repo use 'git rm' and to rm a dir use the '-r' flag:
- ```
+ ```sh
git rm '*.txt'
git rm -r <dirname>
```
- If we want to remove a file from the repository but keep it on disk, say we forgot to add it to our `.gitignore` file then use `--cache`:
- ```
+ ```sh
git rm <filename> --cache
```
diff --git a/doc/university/training/user_training.md b/doc/university/training/user_training.md
index ca3f777f403..e440652edfc 100644
--- a/doc/university/training/user_training.md
+++ b/doc/university/training/user_training.md
@@ -1,11 +1,10 @@
---
comments: false
+type: reference
---
# GitLab Git Workshop
----
-
## Agenda
1. Brief history of Git.
@@ -13,8 +12,6 @@ comments: false
1. Configure your environment.
1. Workshop.
----
-
## Git introduction
<https://git-scm.com/about>
@@ -26,8 +23,6 @@ comments: false
- Adapts to nearly any workflow.
- Fast, reliable and stable file format.
----
-
## Help!
Use the tools at your disposal when you get stuck.
@@ -36,14 +31,10 @@ Use the tools at your disposal when you get stuck.
- Use Google.
- Read documentation at <https://git-scm.com>.
----
-
## GitLab Walkthrough
![fit](logo.png)
----
-
## Configure your environment
- Windows: Install 'Git for Windows'
@@ -56,8 +47,6 @@ Use the tools at your disposal when you get stuck.
- Debian: '`sudo apt-get install git-all`' or Red Hat '`sudo yum install git-all`'
----
-
## Git Workshop
### Overview
@@ -70,8 +59,6 @@ Use the tools at your disposal when you get stuck.
1. Merge requests.
1. Feedback and Collaboration.
----
-
## Configure Git
One-time configuration of the Git client:
@@ -81,8 +68,6 @@ git config --global user.name "Your Name"
git config --global user.email you@example.com
```
----
-
## Configure SSH Key
```sh
@@ -111,8 +96,6 @@ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQEL17Ufacg8cDhlQMS5NhV8z3GHZdhCrZbl4gz you@example.com
```
----
-
## Create a project
- Create a project in your user namespace.
@@ -120,8 +103,6 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQEL17Ufacg8cDhlQMS5NhV8z3GHZdhCrZbl4gz you@example.
- Create a '`development`' or '`workspace`' directory in your home directory.
- Clone the '`training-examples`' project.
----
-
## Commands (project)
```sh
@@ -137,8 +118,6 @@ git clone git@gitlab.example.com:<username>/training-examples.git
cd training-examples
```
----
-
## Git concepts
### Untracked files
@@ -153,8 +132,6 @@ Files that have been modified but are not committed.
Modified files that have been marked to go in the next commit.
----
-
## Committing
1. Edit '`edit_this_file.rb`' in '`training-examples`'.
@@ -165,8 +142,6 @@ Modified files that have been marked to go in the next commit.
1. Push the commit to the remote.
1. View the git log.
----
-
## Commands (committing)
```sh
@@ -179,8 +154,6 @@ git push origin master
git log
```
----
-
## Feature branching
- Efficient parallel workflow for teams.
@@ -190,8 +163,6 @@ git log
- Push branches to the server frequently.
- Hint: This is a cheap backup for your work-in-progress code.
----
-
## Feature branching steps
1. Create a new feature branch called 'squash_some_bugs'.
@@ -199,8 +170,6 @@ git log
1. Commit.
1. Push.
----
-
## Commands (feature branching)
```sh
@@ -212,8 +181,6 @@ git commit -m 'Fix some buggy code'
git push origin squash_some_bugs
```
----
-
## Merge requests
- When you want feedback create a merge request.
@@ -224,8 +191,6 @@ git push origin squash_some_bugs
- Anyone can comment, not just the assignee.
- Push corrections to the same branch.
----
-
## Merge requests steps
Create your first merge request:
@@ -235,8 +200,6 @@ Create your first merge request:
1. Push a new commit to the same branch.
1. Review the changes again and notice the update.
----
-
## Feedback and Collaboration
- Merge requests are a time for feedback and collaboration.
@@ -246,8 +209,6 @@ Create your first merge request:
- Be as receptive as possible.
- Feedback is about the best code, not the person. You are not your code.
----
-
## Feedback and Collaboration resources
Review the Thoughtbot code-review guide for suggestions to follow when reviewing merge requests:
@@ -255,8 +216,6 @@ Review the Thoughtbot code-review guide for suggestions to follow when reviewing
See GitLab merge requests for examples: <https://gitlab.com/gitlab-org/gitlab-ce/merge_requests>.
----
-
## Explore GitLab projects
![fit](logo.png)
@@ -269,8 +228,6 @@ See GitLab merge requests for examples: <https://gitlab.com/gitlab-org/gitlab-ce
- Manage project members
- Project settings
----
-
## Tags
- Useful for marking deployments and releases.
@@ -279,8 +236,6 @@ See GitLab merge requests for examples: <https://gitlab.com/gitlab-org/gitlab-ce
- Many projects combine an annotated release tag with a stable branch.
- Consider setting deployment/release tags automatically.
----
-
## Tags steps
1. Create a lightweight tag.
@@ -289,8 +244,6 @@ See GitLab merge requests for examples: <https://gitlab.com/gitlab-org/gitlab-ce
Additional resources: <http://git-scm.com/book/en/Git-Basics-Tagging>.
----
-
## Commands (tags)
```sh
@@ -306,8 +259,6 @@ git tag
git push origin --tags
```
----
-
## Merge conflicts
- Happen often.
@@ -315,8 +266,6 @@ git push origin --tags
- Practice makes perfect.
- Force push after fixing conflicts. Be careful!
----
-
## Merge conflicts steps
1. Checkout a new branch and edit `conflicts.rb`. Add 'Line4' and 'Line5'.
@@ -325,8 +274,6 @@ git push origin --tags
1. Commit and push to master.
1. Create a merge request.
----
-
## Merge conflicts commands
After creating a merge request you should notice that conflicts exist. Resolve
@@ -343,8 +290,6 @@ git rebase --continue
git push origin <branch> -f
```
----
-
## Rebase with squash
You may end up with a commit log that looks like this:
@@ -361,8 +306,6 @@ Does this work?
Squash these in to meaningful commits using an interactive rebase.
----
-
## Rebase with squash commands
Squash the commits on the same branch we used for the merge conflicts step.
@@ -373,8 +316,6 @@ git rebase -i master
In the editor, leave the first commit as 'pick' and set others to 'fixup'.
----
-
## Questions?
![fit](logo.png)
@@ -383,9 +324,16 @@ Thank you for your hard work!
## Additional Resources
-- GitLab Documentation: <http://docs.gitlab.com/>.
-- GUI Clients: <http://git-scm.com/downloads/guis>.
-- Pro Git book: <http://git-scm.com/book>.
-- Platzi Course: <https://courses.platzi.com/courses/git-gitlab/>.
-- Code School tutorial: <http://try.github.io/>.
-- Contact us at `subscribers@gitlab.com`.
+See [additional resources](index.md#additional-resources).
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index 82a86f3f343..bc8e5fed774 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -107,14 +107,54 @@ Download and install Go:
# Remove former Go installation folder
sudo rm -rf /usr/local/go
-curl --remote-name --progress https://dl.google.com/go/go1.10.5.linux-amd64.tar.gz
-echo 'a035d9beda8341b645d3f45a1b620cf2d8fb0c5eb409be36b389c0fd384ecc3a go1.10.5.linux-amd64.tar.gz' | shasum -a256 -c - && \
- sudo tar -C /usr/local -xzf go1.10.5.linux-amd64.tar.gz
+curl --remote-name --progress https://dl.google.com/go/go1.11.10.linux-amd64.tar.gz
+echo 'aefaa228b68641e266d1f23f1d95dba33f17552ba132878b65bb798ffa37e6d0 go1.11.10.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.11.10.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
-rm go1.10.5.linux-amd64.tar.gz
+rm go1.11.10.linux-amd64.tar.gz
```
-### 6. Get latest code
+### 6. Update git
+
+NOTE: **Note:**
+GitLab 11.11 and higher only supports Git 2.21.x and newer, and
+[dropped support for older versions](https://gitlab.com/gitlab-org/gitlab-ce/issues/54255).
+Be sure to upgrade your installation if necessary.
+
+```bash
+# Make sure Git is version 2.21.0 or higher
+git --version
+
+# Remove packaged Git
+sudo apt-get remove git-core
+
+# Install dependencies
+sudo apt-get install -y libcurl4-openssl-dev libexpat1-dev gettext libz-dev libssl-dev build-essential
+
+# Download and compile pcre2 from source
+curl --silent --show-error --location https://ftp.pcre.org/pub/pcre/pcre2-10.33.tar.gz --output pcre2.tar.gz
+tar -xzf pcre2.tar.gz
+cd pcre2-10.33
+chmod +x configure
+./configure --prefix=/usr --enable-jit
+make
+make install
+
+# Download and compile from source
+cd /tmp
+curl --remote-name --location --progress https://www.kernel.org/pub/software/scm/git/git-2.21.0.tar.gz
+echo '85eca51c7404da75e353eba587f87fea9481ba41e162206a6f70ad8118147bee git-2.21.0.tar.gz' | shasum -a256 -c - && tar -xzf git-2.21.0.tar.gz
+cd git-2.21.0/
+./configure --with-libpcre
+make prefix=/usr/local all
+
+# Install into /usr/local/bin
+sudo make prefix=/usr/local install
+
+# You should edit config/gitlab.yml, change the git -> bin_path to /usr/local/bin/git
+```
+
+### 7. Get latest code
```bash
cd /home/git/gitlab
@@ -142,7 +182,7 @@ cd /home/git/gitlab
sudo -u git -H git checkout BRANCH-ee
```
-### 7. Update gitlab-shell
+### 8. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
@@ -152,7 +192,7 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
sudo -u git -H bin/compile
```
-### 8. Update gitlab-workhorse
+### 9. Update gitlab-workhorse
Install and compile gitlab-workhorse. GitLab-Workhorse uses
[GNU Make](https://www.gnu.org/software/make/).
@@ -167,7 +207,7 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
sudo -u git -H make
```
-### 9. Update Gitaly
+### 10. Update Gitaly
#### Compile Gitaly
@@ -178,7 +218,7 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
sudo -u git -H make
```
-### 10. Update gitlab-pages
+### 11. Update gitlab-pages
#### Only needed if you use GitLab Pages
@@ -195,7 +235,7 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
sudo -u git -H make
```
-### 11. Update MySQL permissions
+### 12. Update MySQL permissions
If you are using MySQL you need to grant the GitLab user the necessary
permissions on the database:
@@ -217,7 +257,7 @@ You can make this setting permanent by adding it to your `my.cnf`:
log_bin_trust_function_creators=1
```
-### 12. Update configuration files
+### 13. Update configuration files
#### New configuration options for `gitlab.yml`
@@ -291,7 +331,7 @@ For Ubuntu 16.04.1 LTS:
sudo systemctl daemon-reload
```
-### 13. Install libs, migrations, etc.
+### 14. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
@@ -323,14 +363,14 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
**MySQL installations**: Run through the `MySQL strings limits` and `Tables and
data conversion to utf8mb4` [tasks](../install/database_mysql.md).
-### 14. Start application
+### 15. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
-### 15. Check application status
+### 16. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
index 527110d53df..91c771764f8 100644
--- a/doc/user/admin_area/index.md
+++ b/doc/user/admin_area/index.md
@@ -20,7 +20,7 @@ The Admin Area is made up of the following sections:
| Section | Description |
|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Overview | View your GitLab [Dashboard](#admin-dashboard), and administer [projects](#administer-projects), [users](#administer-users), groups, [jobs](#administer-jobs), [Runners](#administer-runners), and [Gitaly servers](#administer-gitaly-servers). |
+| Overview | View your GitLab [Dashboard](#admin-dashboard), and administer [projects](#administer-projects), [users](#administer-users), [groups](#administer-groups), [jobs](#administer-jobs), [Runners](#administer-runners), and [Gitaly servers](#administer-gitaly-servers). |
| Monitoring | View GitLab system information, and information on background jobs, logs, [health checks](monitoring/health_check.md), request profiles, and audit logs. |
| Messages | Send and manage [broadcast messages](broadcast_messages.md) for your users. |
| System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. |
@@ -120,13 +120,32 @@ To search for users, enter your criteria in the search field. The user search is
insensitive, and applies partial matching to name and username. To search for an email address,
you must provide the complete email address.
+## Administer Groups
+
+You can administer all groups in the GitLab instance from the Admin Area's Groups page.
+
+To access the Groups page, go to **Admin Area > Overview > Groups**.
+
+For each group, the page displays their name, description, size, number of projects in the group,
+number of members, and whether the group is private, internal, or public. To edit a group, click
+the **Edit** button in that group's row. To delete the group, click the **Delete** button in
+that group's row.
+
+To change the sort order, click the sort dropdown and select the desired order. The default
+sort order is by **Last created**.
+
+To search for groups by name, enter your criteria in the search field. The group search is case
+insensitive, and applies partial matching.
+
+To [Create a new group](../group/index.md#create-a-new-group) click **New group**.
+
## Administer Jobs
You can administer all jobs in the GitLab instance from the Admin Area's Jobs page.
To access the Jobs page, go to **Admin Area > Overview > Jobs**.
-All jobs are listed, in reverse order of their job ID.
+All jobs are listed, in descending order of job ID.
Click the **All** tab to list all jobs. Click the **Pending**, **Running**, or **Finished** tab to list only jobs of that status.
@@ -169,7 +188,7 @@ find.
You can also filter Runners by status, type, and tag. To filter:
1. Click in the **Search or filter results...** field.
-1. Select **status:**, **type:**, or **tag:**
+1. Select **status:**, **type:**, or **tag:**.
1. Select or enter your search criteria.
![Attributes of a Runner, with the **Search or filter results...** field active](img/index_runners_search_or_filter.png)
diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md
index 1e8ce04da92..8ddb9c3d707 100644
--- a/doc/user/admin_area/license.md
+++ b/doc/user/admin_area/license.md
@@ -49,16 +49,10 @@ Otherwise, you can:
## Add your license at install time
-The license may be automatically injected during installation using one of
-two methods.
+A license can be automatically imported at install time, by placing a file named
+`Gitlab.gitlab-license` in `/etc/gitlab/` for Omnibus, or `config/` for source installations.
-The first requires a license file named `Gitlab.gitlab-release`.
-
-Place it in the `config/` directory if installing from source or in the
-`/etc/gitlab/` directory if installing Omnibus.
-
-The second allows the administrator to configure the location and
-filename of the license.
+It is also possible to specify a custom location and filename for the license.
Source installations should set the `GITLAB_LICENSE_FILE` environment
variable with the path to a valid GitLab Enterprise Edition license.
diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md
index ff056490653..f80d4e9d2aa 100644
--- a/doc/user/admin_area/monitoring/health_check.md
+++ b/doc/user/admin_area/monitoring/health_check.md
@@ -6,7 +6,7 @@ type: concepts, howto
> - Liveness and readiness probes were [introduced][ce-10416] in GitLab 9.1.
> - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and was
-> be deprecated in GitLab 9.1.
+> deprecated in GitLab 9.1.
> - [Access token](#access-token-deprecated) has been deprecated in GitLab 9.4
> in favor of [IP whitelist](#ip-whitelist).
diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md
index 1d355824760..001e4b6bf48 100644
--- a/doc/user/admin_area/settings/account_and_limit_settings.md
+++ b/doc/user/admin_area/settings/account_and_limit_settings.md
@@ -40,11 +40,9 @@ These settings can be found within:
- **Admin Area > Settings > General**.
- The path `/admin/application_settings`.
-The very first push of a new project cannot be checked for size as of now, so
-the first push will allow you to upload more than the limit dictates, but every
-subsequent push will be denied. LFS objects, however, can be checked on first
-push and **will** be rejected if the sum of their sizes exceeds the maximum
-allowed repository size.
+The first push of a new project, including LFS objects, will be checked for size
+and **will** be rejected if the sum of their sizes exceeds the maximum allowed
+repository size.
For details on manually purging files, see [reducing the repository size using Git](../../project/repository/reducing_the_repo_size_using_git.md).
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index 6c4abce83c2..d2605cbfb5e 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -134,6 +134,14 @@ to a different Group.
be deducted from your Additional Minutes quota immediately after your purchase of additional
minutes.
+## What happens when my CI minutes quota run out
+
+When the CI minutes quota run out, an email is sent automatically to notifies the owner(s) of the group/namespace which
+includes a link to [purchase more minutes](https://customers.gitlab.com/plans).
+
+If you are not the owner of the group, you will need to contact them to let them know they need to
+[purchase more minutes](https://customers.gitlab.com/plans).
+
## Archive jobs **[CORE ONLY]**
Archiving jobs is useful for reducing the CI/CD footprint on the system by
@@ -160,4 +168,4 @@ questions that you know someone might ask.
Each scenario can be a third-level heading, e.g. `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
-but commented out to help encourage others to add to it in the future. --> \ No newline at end of file
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
index 0e697b9e445..eed087ae52b 100644
--- a/doc/user/admin_area/settings/index.md
+++ b/doc/user/admin_area/settings/index.md
@@ -1,9 +1,13 @@
-# Admin area settings **[CORE ONLY]**
+---
+type: index
+---
-In the admin area settings, you can find various options for your GitLab
+# Admin Area settings **[CORE ONLY]**
+
+In the Admin Area **Settings** page, you can find various options for your GitLab
instance like sign-up restrictions, account limits and quota, metrics, etc.
-Navigate to it by going to **Admin area > Settings**. Some of the settings
+Navigate to it by going to **Admin Area > Settings**. Some of the settings
include:
- [Account and limit settings](account_and_limit_settings.md) **[STARTER]**
@@ -22,7 +26,7 @@ in the **Localization** section of **Admin area > Settings > Preferences**.
## GitLab.com admin area settings
-Most of the settings under the admin area change the behavior of the whole
+Most of the settings under the Admin Area change the behavior of the whole
GitLab instance. For GitLab.com, the admin settings are available only for the
GitLab.com administrators, and the parameters can be found on the
[GitLab.com settings](../../gitlab_com/index.md) documentation.
diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md
index 4d1b7d0f252..a1229484388 100644
--- a/doc/user/admin_area/settings/visibility_and_access_controls.md
+++ b/doc/user/admin_area/settings/visibility_and_access_controls.md
@@ -1,8 +1,25 @@
+---
+type: reference
+---
+
# Visibility and access controls
+GitLab allows admins to:
+
+- Control access and visibility to GitLab resources including branches and projects.
+- Select from which hosting sites code can be imported into GitLab.
+- Select the protocols permitted to access GitLab.
+- Enable or disable repository mirroring.
+
+To access the visibility and access control options:
+
+1. Log in to GitLab as an admin.
+1. Go to **Admin Area > Settings > General**.
+1. Expand the **Visibility and access controls** section.
+
## Import sources
-Choose from which hosting sites the users can
+Choose from which hosting sites users can
[import their projects](../../project/import/index.md).
![import sources](img/import_sources.png)
@@ -11,12 +28,10 @@ Choose from which hosting sites the users can
> [Introduced][ce-4696] in GitLab 8.10.
-With GitLab's Access restrictions you can choose which Git access protocols you
-want your users to use to communicate with GitLab. This feature can be enabled
-via the `Application Settings` in the Admin interface.
+With GitLab's access restrictions, you can select with which protocols users can communicate with
+GitLab.
-The setting is called `Enabled Git access protocols`, and it gives you the option
-to choose between:
+From the **Enabled Git access protocols** dropdown, select one of the following:
- Both SSH and HTTP(S)
- Only SSH
@@ -24,10 +39,9 @@ to choose between:
![Settings Overview](img/access_restrictions.png)
-When both SSH and HTTP(S) are enabled, GitLab will behave as usual, it will give
-your users the option to choose which protocol they would like to use.
+When both SSH and HTTP(S) are enabled, your users can choose either protocol.
-When you choose to allow only one of the protocols, a couple of things will happen:
+When only one protocol is enabled:
- The project page will only show the allowed protocol's URL, with no option to
change it.
@@ -54,10 +68,22 @@ application level.
> [Introduced][ee-3586] in GitLab 10.3.
This option is enabled by default. By disabling it, both pull and push mirroring will no longer
-work in every repository and can only be re-enabled on a per-project basis by an admin.
+work in every repository and can only be re-enabled by an admin on a per-project basis.
![Mirror settings](img/mirror_settings.png)
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
+
[ce-4696]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4696
[ce-18021]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18021
[ee-3586]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3586
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index adb6868516e..a24374dff1d 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -12,7 +12,7 @@ two open source tools for Vulnerability Static Analysis for containers.
You can take advantage of Container Scanning by either [including the CI job](#including-the-provided-template) in
your existing `.gitlab-ci.yml` file or by implicitly using
-[Auto Container Scanning](../../../topics/autodevops/index.md#auto-container-scanning)
+[Auto Container Scanning](../../../topics/autodevops/index.md#auto-container-scanning-ultimate)
that is provided by [Auto DevOps](../../../topics/autodevops/index.md).
GitLab checks the Container Scanning report, compares the found vulnerabilities
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index 028ff72a160..a722aa88f9d 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -58,7 +58,7 @@ To enable DAST in your project, define a job in your `.gitlab-ci.yml` file that
This can be done in two ways:
-- For GitLab 11.9 and later, including the provided DAST `.gitlab-ci.yml` template (recommended).
+- For GitLab 11.9 and later, including the provided `DAST.gitlab-ci.yml` template (recommended).
- Manually specifying the job definition. Not recommended unless using GitLab
11.8 and earlier.
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 658d493ba39..f3bf743ad03 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -66,8 +66,7 @@ file that generates the
This can be done in two ways:
-- For GitLab 11.9 and later, including the provided Dependency Scanning
- `.gitlab-ci.yml` template (recommended).
+- For GitLab 11.9 and later, including the provided `Dependency-Scanning.gitlab-ci.yml` template (recommended).
- Manually specifying the job definition. Not recommended unless using GitLab
11.8 and earlier.
@@ -214,6 +213,159 @@ dependency_scanning:
paths: [gl-dependency-scanning-report.json]
```
+## Reports JSON format
+
+CAUTION: **Caution:**
+The JSON report artifacts are not a public API of Dependency Scanning and their format may change in future.
+
+The Dependency Scanning tool emits a JSON report file. Here is an example of a structure for a report will all important parts of
+it highlighted:
+
+```json-doc
+{
+ "version": "2.0",
+ "vulnerabilities": [
+ {
+ "category": "dependency_scanning",
+ "name": "Regular Expression Denial of Service",
+ "message": "Regular Expression Denial of Service in debug",
+ "description": "The debug module is vulnerable to regular expression denial of service when untrusted user input is passed into the `o` formatter. It takes around 50k characters to block for 2 seconds making this a low severity issue.",
+ "cve": "yarn.lock:debug:gemnasium:37283ed4-0380-40d7-ada7-2d994afcc62a",
+ "severity": "Unknown",
+ "solution": "Upgrade to latest versions.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {
+ "file": "yarn.lock",
+ "dependency": {
+ "package": {
+ "name": "debug"
+ },
+ "version": "1.0.5"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-37283ed4-0380-40d7-ada7-2d994afcc62a",
+ "value": "37283ed4-0380-40d7-ada7-2d994afcc62a",
+ "url": "https://deps.sec.gitlab.com/packages/npm/debug/versions/1.0.5/advisories"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://nodesecurity.io/advisories/534"
+ },
+ {
+ "url": "https://github.com/visionmedia/debug/issues/501"
+ },
+ {
+ "url": "https://github.com/visionmedia/debug/pull/504"
+ }
+ ]
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "Authentication bypass via incorrect DOM traversal and canonicalization",
+ "message": "Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js",
+ "description": "Some XML DOM traversal and canonicalization APIs may be inconsistent in handling of comments within XML nodes. Incorrect use of these APIs by some SAML libraries results in incorrect parsing of the inner text of XML nodes such that any inner text after the comment is lost prior to cryptographically signing the SAML message. Text after the comment therefore has no impact on the signature on the SAML message.\r\n\r\nA remote attacker can modify SAML content for a SAML service provider without invalidating the cryptographic signature, which may allow attackers to bypass primary authentication for the affected SAML service provider.",
+ "cve": "yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98",
+ "severity": "Unknown",
+ "solution": "Upgrade to fixed version.\r\n",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {
+ "file": "yarn.lock",
+ "dependency": {
+ "package": {
+ "name": "saml2-js"
+ },
+ "version": "1.5.0"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-9952e574-7b5b-46fa-a270-aeb694198a98",
+ "value": "9952e574-7b5b-46fa-a270-aeb694198a98",
+ "url": "https://deps.sec.gitlab.com/packages/npm/saml2-js/versions/1.5.0/advisories"
+ },
+ {
+ "type": "cve",
+ "name": "CVE-2017-11429",
+ "value": "CVE-2017-11429",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-11429"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/Clever/saml2/commit/3546cb61fd541f219abda364c5b919633609ef3d#diff-af730f9f738de1c9ad87596df3f6de84R279"
+ },
+ {
+ "url": "https://github.com/Clever/saml2/issues/127"
+ },
+ {
+ "url": "https://www.kb.cert.org/vuls/id/475445"
+ }
+ ]
+ }
+ ],
+ "remediations": [
+ {
+ "fixes": [
+ {
+ "cve": "yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98"
+ }
+ ],
+ "summary": "Upgrade saml2-js",
+ "diff": "ZGlmZiAtLWdpdCBhL...OR0d1ZUc2THh3UT09Cg==" // some content is omitted for brevity
+ }
+ ]
+}
+```
+
+Here is the description of the report file structure nodes and their meaning. All fields are mandatory to be present in
+the report JSON unless stated otherwise. Presence of optional fields depends on the underlying analyzers being used.
+
+| Report JSON node | Function |
+|------------------------------------------------------|----------|
+| `version` | Report syntax version used to generate this JSON. |
+| `vulnerabilities` | Array of vulnerability objects. |
+| `vulnerabilities[].category` | Where this vulnerability belongs (SAST, Dependency Scanning etc.). For Dependency Scanning, it will always be `dependency_scanning`. |
+| `vulnerabilities[].name` | Name of the vulnerability, this must not include the occurrence's specific information. Optional. |
+| `vulnerabilities[].message` | A short text that describes the vulnerability, it may include occurrence's specific information. Optional. |
+| `vulnerabilities[].description` | A long text that describes the vulnerability. Optional. |
+| `vulnerabilities[].cve` | A fingerprint string value that represents a concrete occurrence of the vulnerability. It's used to determine whether two vulnerability occurrences are same or different. May not be 100% accurate. **This is NOT a [CVE](https://cve.mitre.org/)**. |
+| `vulnerabilities[].severity` | How much the vulnerability impacts the software. Possible values: `Undefined` (an analyzer has not provided this info), `Info`, `Unknown`, `Low`, `Medium`, `High`, `Critical`. |
+| `vulnerabilities[].confidence` | How reliable the vulnerability's assessment is. Possible values: `Undefined` (an analyzer has not provided this info), `Ignore`, `Unknown`, `Experimental`, `Low`, `Medium`, `High`, `Confirmed`. |
+| `vulnerabilities[].solution` | Explanation of how to fix the vulnerability. Optional. |
+| `vulnerabilities[].scanner` | A node that describes the analyzer used find this vulnerability. |
+| `vulnerabilities[].scanner.id` | Id of the scanner as a snake_case string. |
+| `vulnerabilities[].scanner.name` | Name of the scanner, for display purposes. |
+| `vulnerabilities[].location` | A node that tells which class and/or method is affected by the vulnerability. |
+| `vulnerabilities[].location.file` | Path to the dependencies file (e.g., `yarn.lock`). Optional. |
+| `vulnerabilities[].location.dependency` | A node that describes the dependency of a project where the vulnerability is located. |
+| `vulnerabilities[].location.dependency.package` | A node that provides the information on the package where the vulnerability is located. |
+| `vulnerabilities[].location.dependency.package.name` | Name of the package where the vulnerability is located. Optional. |
+| `vulnerabilities[].location.dependency.version` | Version of the vulnerable package. Optional. |
+| `vulnerabilities[].identifiers` | An ordered array of references that identify a vulnerability on internal or external DBs. |
+| `vulnerabilities[].identifiers[].type` | Type of the identifier. Possible values: common identifier types (among `cve`, `cwe`, `osvdb`, and `usn`) or analyzer-dependent ones (e.g. `gemnasium` for [Gemnasium](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/)). |
+| `vulnerabilities[].identifiers[].name` | Name of the identifier for display purpose. |
+| `vulnerabilities[].identifiers[].value` | Value of the identifier for matching purpose. |
+| `vulnerabilities[].identifiers[].url` | URL to identifier's documentation. Optional. |
+| `vulnerabilities[].links` | An array of references to external documentation pieces or articles that describe the vulnerability further. Optional. |
+| `vulnerabilities[].links[].name` | Name of the vulnerability details link. Optional. |
+| `vulnerabilities[].links[].url` | URL of the vulnerability details document. Optional. |
+| `remediations` | An array of objects containing information on cured vulnerabilities along with patch diffs to apply. |
+| `remediations[].fixes` | An array of strings that represent references to vulnerabilities fixed by this particular remediation. |
+| `remediations[].fixes[].cve` | A string value that describes a fixed vulnerability occurrence in the same format as `vulnerabilities[].cve`. |
+| `remediations[].summary` | Overview of how the vulnerabilities have been fixed. |
+| `remediations[].diff` | base64-encoded remediation code diff, compatible with [`git apply`](https://git-scm.com/docs/git-format-patch#_discussion). |
+
## Security Dashboard
The Security Dashboard is a good place to get an overview of all the security
@@ -225,6 +377,17 @@ vulnerabilities in your groups and projects. Read more about the
Once a vulnerability is found, you can interact with it. Read more on how to
[interact with the vulnerabilities](../index.md#interacting-with-the-vulnerabilities).
+## Dependency List
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/10075)
+in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
+
+An additional benefit of Dependency Scanning is the ability to get a list of your project's dependencies with their versions.
+
+This list can be generated only for [supported languages and package managers](#supported-languages-and-package-managers).
+
+To see the generated dependency list, navigate to the Dependency List page under your project's left sidebar menu **Project > Dependency List**.
+
## Contributing to the vulnerability database
You can search the [gemnasium-db](https://gitlab.com/gitlab-org/security-products/gemnasium-db) project
diff --git a/doc/user/application_security/img/dismissed_info.png b/doc/user/application_security/img/dismissed_info.png
new file mode 100644
index 00000000000..64d5cf26ed2
--- /dev/null
+++ b/doc/user/application_security/img/dismissed_info.png
Binary files differ
diff --git a/doc/user/application_security/img/interactive_reports.png b/doc/user/application_security/img/interactive_reports.png
index 9f9812dc69d..373b39104db 100644
--- a/doc/user/application_security/img/interactive_reports.png
+++ b/doc/user/application_security/img/interactive_reports.png
Binary files differ
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 64e72fab198..679847b76d7 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -47,6 +47,16 @@ You can dismiss vulnerabilities by clicking the **Dismiss vulnerability** button
This will dismiss the vulnerability and re-render it to reflect its dismissed state.
If you wish to undo this dismissal, you can click the **Undo dismiss** button.
+#### Adding a dismissal reason
+
+> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing) 12.0.
+
+When dismissing a vulnerability, it's often helpful to provide a reason for doing so.
+If you press the comment button next to **Dismiss vulnerability** in the modal, a text box will appear, allowing you to add a comment with your dismissal.
+This comment can not currently be edited or removed, but [future versions](https://gitlab.com/gitlab-org/gitlab-ee/issues/11721) will add this functionality.
+
+![Dismissed vulnerability comment](img/dismissed_info.png)
+
### Creating an issue for a vulnerability
You can create an issue for a vulnerability by selecting the **Create issue**
diff --git a/doc/user/application_security/license_management/index.md b/doc/user/application_security/license_management/index.md
index 8b75995c377..7878dc77084 100644
--- a/doc/user/application_security/license_management/index.md
+++ b/doc/user/application_security/license_management/index.md
@@ -65,7 +65,7 @@ file that generates the [License Management report artifact](../../../ci/yaml/RE
This can be done in two ways:
-- For GitLab 11.9 and later, including the provided License Management `.gitlab-ci.yml` template (recommended).
+- For GitLab 11.9 and later, including the provided `License-Management.gitlab-ci.yml` template (recommended).
- Manually specifying the job definition. Not recommended unless using GitLab
11.8 and earlier.
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index db328262aba..11361f8ad89 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -80,7 +80,7 @@ To enable SAST in your project, define a job in your `.gitlab-ci.yml` file that
This can be done in two ways:
-- For GitLab 11.9 and later, including the provided SAST `.gitlab-ci.yml` template (recommended).
+- For GitLab 11.9 and later, including the provided `SAST.gitlab-ci.yml` template (recommended).
- Manually specifying the job definition. Not recommended unless using GitLab
11.8 and earlier.
@@ -191,7 +191,7 @@ sast:
You can supply many other [settings variables](https://gitlab.com/gitlab-org/security-products/sast#settings)
via `docker run --env` to customize your job execution.
-## Manual job definition for GitLab 11.4 and earlier (deprecated)
+### Manual job definition for GitLab 11.4 and earlier (deprecated)
CAUTION: **Deprecated:**
Before GitLab 11.5, the SAST job and artifact had to be named specifically
@@ -221,6 +221,118 @@ sast:
paths: [gl-sast-report.json]
```
+## Reports JSON format
+
+CAUTION: **Caution:**
+The JSON report artifacts are not a public API of SAST and their format may change in the future.
+
+The SAST tool emits a JSON report report file. Here is an example of a structure for a report will all important parts of
+it highlighted:
+
+```json-doc
+{
+ "version": "2.0",
+ "vulnerabilities": [
+ {
+ "category": "sast",
+ "name": "Predictable pseudorandom number generator",
+ "message": "Predictable pseudorandom number generator",
+ "description": "The use of java.util.Random is predictable",
+ "cve": "818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "find_sec_bugs",
+ "name": "Find Security Bugs"
+ },
+ "location": {
+ "file": "groovy/src/main/groovy/com/gitlab/security_products/tests/App.groovy",
+ "start_line": 47,
+ "end_line": 47,
+ "class": "com.gitlab.security_products.tests.App",
+ "method": "generateSecretToken2",
+ "dependency": {
+ "package": {}
+ }
+ },
+ "identifiers": [
+ {
+ "type": "find_sec_bugs_type",
+ "name": "Find Security Bugs-PREDICTABLE_RANDOM",
+ "value": "PREDICTABLE_RANDOM",
+ "url": "https://find-sec-bugs.github.io/bugs.htm#PREDICTABLE_RANDOM"
+ },
+ {
+ "type": "cwe",
+ "name": "CWE-330",
+ "value": "330",
+ "url": "https://cwe.mitre.org/data/definitions/330.html"
+ }
+ ]
+ },
+ {
+ "category": "sast",
+ // "name" may be omitted because it could be not reported by a particular analyzer
+ "message": "Probable insecure usage of temp file/directory.",
+ "cve": "python/hardcoded/hardcoded-tmp.py:4ad6d4c40a8c263fc265f3384724014e0a4f8dd6200af83e51ff120420038031:B108",
+ "severity": "Medium",
+ "confidence": "Medium",
+ "scanner": {
+ "id": "bandit",
+ "name": "Bandit"
+ },
+ "location": {
+ "file": "python/hardcoded/hardcoded-tmp.py",
+ "start_line": 10,
+ "end_line": 10,
+ "dependency": {
+ "package": {}
+ }
+ },
+ "identifiers": [
+ {
+ "type": "bandit_test_id",
+ "name": "Bandit Test ID B108",
+ "value": "B108",
+ "url": "https://docs.openstack.org/bandit/latest/plugins/b108_hardcoded_tmp_directory.html"
+ }
+ ]
+ },
+ ],
+ "remediations": []
+}
+```
+
+Here is the description of the report file structure nodes and their meaning. All fields are mandatory to be present in
+the report JSON unless stated otherwise. Presence of optional fields depends on the underlying analyzers being used.
+
+| Report JSON node | Function |
+|-----------------------------------------|----------|
+| `version` | Report syntax version used to generate this JSON. |
+| `vulnerabilities` | Array of vulnerability objects. |
+| `vulnerabilities[].category` | Where this vulnerability belongs (SAST, Dependency Scanning etc.). For SAST, it will always be `sast`. |
+| `vulnerabilities[].name` | Name of the vulnerability, this must not include the occurrence's specific information. Optional. |
+| `vulnerabilities[].message` | A short text that describes the vulnerability, it may include the occurrence's specific information. Optional. |
+| `vulnerabilities[].description` | A long text that describes the vulnerability. Optional. |
+| `vulnerabilities[].cve` | A fingerprint string value that represents a concrete occurrence of the vulnerability. Is used to determine whether two vulnerability occurrences are same or different. May not be 100% accurate. **This is NOT a [CVE](https://cve.mitre.org/)**. |
+| `vulnerabilities[].severity` | How much the vulnerability impacts the software. Possible values: `Undefined` (an analyzer has not provided this info), `Info`, `Unknown`, `Low`, `Medium`, `High`, `Critical`. |
+| `vulnerabilities[].confidence` | How reliable the vulnerability's assessment is. Possible values: `Undefined` (an analyzer has not provided this info), `Ignore`, `Unknown`, `Experimental`, `Low`, `Medium`, `High`, `Confirmed`. |
+| `vulnerabilities[].solution` | Explanation of how to fix the vulnerability. Optional. |
+| `vulnerabilities[].scanner` | A node that describes the analyzer used find this vulnerability. |
+| `vulnerabilities[].scanner.id` | Id of the scanner as a snake_case string. |
+| `vulnerabilities[].scanner.name` | Name of the scanner, for display purposes. |
+| `vulnerabilities[].location` | A node that tells which class and/or method is affected by the vulnerability. |
+| `vulnerabilities[].location.file` | Path to the file where the vulnerability is located. Optional. |
+| `vulnerabilities[].location.start_line` | The first line of the code affected by the vulnerability. Optional. |
+| `vulnerabilities[].location.end_line` | The last line of the code affected by the vulnerability. Optional. |
+| `vulnerabilities[].location.class` | If specified, provides the name of the class where the vulnerability is located. Optional. |
+| `vulnerabilities[].location.method` | If specified, provides the name of the method where the vulnerability is located. Optional. |
+| `vulnerabilities[].identifiers` | An ordered array of references that identify a vulnerability on internal or external DBs. |
+| `vulnerabilities[].identifiers[].type` | Type of the identifier. Possible values: common identifier types (among `cve`, `cwe`, `osvdb`, and `usn`) or analyzer-dependent ones (e.g., `bandit_test_id` for [Bandit analyzer](https://wiki.openstack.org/wiki/Security/Projects/Bandit)). |
+| `vulnerabilities[].identifiers[].name` | Name of the identifier for display purposes. |
+| `vulnerabilities[].identifiers[].value` | Value of the identifier for matching purposes. |
+| `vulnerabilities[].identifiers[].url` | URL to identifier's documentation. Optional. |
+
## Secret detection
GitLab is also able to detect secrets and credentials that have been unintentionally pushed to the repository.
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 5d69efc3600..20eabdada79 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -53,6 +53,8 @@ persist through a commit ID change when:
- force-pushing after a rebase
- amending a commit
+This functionality is also demonstrated in the video [How to use Merge Request Commit Discussions](https://www.youtube.com/watch?v=TviJH6oRboo).
+
To create a commit diff discussion:
1. Navigate to the merge request **Commits** tab. A list of commits that
diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md
index e0e89051be4..3c5e820c1ca 100644
--- a/doc/user/group/clusters/index.md
+++ b/doc/user/group/clusters/index.md
@@ -52,7 +52,7 @@ Add another cluster similar to the first one and make sure to
[set an environment scope](#environment-scopes-premium) that will
differentiate the new cluster from the rest.
-## Gitlab-managed clusters
+## GitLab-managed clusters
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22011) in GitLab 11.5.
> Became [optional](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/26565) in GitLab 11.11.
@@ -149,3 +149,15 @@ The following features are not currently available for group-level clusters:
1. Terminals (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55487)).
1. Pod logs (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55488)).
1. Deployment boards (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55489)).
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index eb0c7bc998f..97d309caf7c 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -9,6 +9,8 @@ With GitLab Groups, you can:
- Assemble related projects together.
- Grant members access to several projects at once.
+For a video introduction to GitLab Groups, see [GitLab University: Repositories, Projects and Groups](https://www.youtube.com/watch?v=4TWfh1aKHHw).
+
Groups can also be nested in [subgroups](subgroups/index.md).
Find your groups by clicking **Groups > Your Groups** in the top navigation.
@@ -111,6 +113,8 @@ Add the following information:
1. Optionally, choose an avatar for your group.
1. Choose the [visibility level](../../public_access/public_access.md).
+For more details on creating groups, watch the video [GitLab Namespaces (users, groups and subgroups)](https://youtu.be/r0sJgjR2f5A).
+
## Add users to a group
A benefit of putting multiple projects in one group is that you can
@@ -213,10 +217,8 @@ Get an overview of the vulnerabilities of all the projects in a group and its su
## Insights **[ULTIMATE]**
-> Introduced in [GitLab Ultimate][ee] 11.9 behind the `insights` feature flag.
-
-Configure the Insights that matter for your groups or projects, allowing users to explore data
-such as:
+Configure the Insights that matter for your groups or projects, allowing users
+to explore data such as:
- Triage hygiene
- Issues created/closed per a given period
@@ -230,8 +232,8 @@ such as:
From GitLab 10.5, you can transfer groups in the following ways:
- Transfer a subgroup to a new parent group.
-- Convert a top-level group into a subgroup by transfering it to the desired group.
-- Convert a subgroup into a top-level group by transfering it out of its current group.
+- Convert a top-level group into a subgroup by transferring it to the desired group.
+- Convert a subgroup into a top-level group by transferring it out of its current group.
When transferring groups, note:
diff --git a/doc/user/group/insights/index.md b/doc/user/group/insights/index.md
index a4ea71074ec..1aba5d0986b 100644
--- a/doc/user/group/insights/index.md
+++ b/doc/user/group/insights/index.md
@@ -5,11 +5,7 @@ type: reference, howto
# Insights **[ULTIMATE]**
> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.9 behind the `insights` feature flag.
-
-CAUTION: **Beta:**
-Insights is considered beta, and is not ready for production use.
-Follow [gitlab-org/quality/team-tasks#137](https://gitlab.com/gitlab-org/quality/team-tasks/issues/137#general-availability)
-for updates.
+> **Generally Available** (GA) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
Configure the Insights that matter for your groups to explore data such as
triage hygiene, issues created/closed per a given period, average time for merge
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index c00628bf909..96cc523f4ec 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -1,7 +1,15 @@
+---
+type: howto, reference
+---
+
# SCIM provisioning using SAML SSO for Groups **[SILVER ONLY]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9388) in [GitLab.com Silver](https://about.gitlab.com/pricing/) 11.10.
+System for Cross-domain Identity Management (SCIM), is an open standard that enables the
+automation of user provisioning. When SCIM is provisioned for a GitLab group, membership of
+that group is synchronized between GitLab and the identity provider.
+
GitLab's [SCIM API](../../../api/scim.md) implements part of [the RFC7644 protocol](https://tools.ietf.org/html/rfc7644).
Currently, the following actions are available:
@@ -100,3 +108,15 @@ the `Provisioning Status` to `ON`.
Once enabled, the synchronization details and any errors will appear on the
bottom of the **Provisioning** screen, together with a link to the audit logs.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 4e81e28a45a..79ae94dd9ef 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -1,11 +1,17 @@
+---
+type: reference, howto, concepts
+---
+
# Subgroups
NOTE: **Note:**
[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2772) in GitLab 9.0. Not available when using MySQL as external
database (support removed in GitLab 9.3 [due to performance reasons](https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600)).
-With subgroups (aka nested groups or hierarchical groups) you can have
-up to 20 levels of nested groups, which among other things can help you to:
+Subgroups, also known as nested groups or hierarchical groups, allow you to have up to 20
+levels of groups.
+
+By using subgroups you can do the following:
- **Separate internal / external organizations.** Since every group
can have its own visibility level, you are able to host groups for different
@@ -17,8 +23,9 @@ up to 20 levels of nested groups, which among other things can help you to:
## Database Requirements
-Nested groups are only supported when you use PostgreSQL. Supporting nested
-groups on MySQL in an efficient way is not possible due to MySQL's limitations.
+Subgroups are only supported when you use PostgreSQL. Supporting subgroups on MySQL in an
+efficient way is not possible due to MySQL's limitations.
+
See the following links for more information:
- <https://gitlab.com/gitlab-org/gitlab-ce/issues/30472>
@@ -37,7 +44,7 @@ only 1 parent group. It resembles a directory behavior or a nested items list:
- Group 1.2.2.1
In a real world example, imagine maintaining a GNU/Linux distribution with the
-first group being the name of the distro and subsequent groups split like:
+first group being the name of the distribution, and subsequent groups split as follows:
- Organization Group - GNU/Linux distro
- Category Subgroup - Packages
@@ -69,22 +76,22 @@ Another example of GitLab as a company would be the following:
---
-The maximum nested groups a group can have, including the first one in the
+The maximum subgroups a group can have, including the first one in the
hierarchy, is 21.
-Things like transferring or importing a project inside nested groups, work like
+Actions such as transferring or importing a project inside subgroups, work like
when performing these actions the traditional way with the `group/project`
structure.
## Creating a subgroup
NOTE: **Note:**
-You need to be an Owner of a group in order to be able to create a subgroup. For
+You must be an Owner of a group to create a subgroup. For
more information check the [permissions table](../../permissions.md#group-members-permissions).
For a list of words that are not allowed to be used as group names see the
[reserved names](../../reserved_names.md).
Users can always create subgroups if they are explicitly added as an Owner to
-a parent group even if group creation is disabled by an administrator in their
+a parent group, even if group creation is disabled by an administrator in their
settings.
To create a subgroup:
@@ -111,7 +118,7 @@ When you add a member to a subgroup, they inherit the membership and permission
level from the parent group. This model allows access to nested groups if you
have membership in one of its parents.
-The group permissions for a member can be changed only by Owners and only on
+The group permissions for a member can be changed only by Owners, and only on
the **Members** page of the group the member was added.
You can tell if a member has inherited the permissions from a parent group by
@@ -119,24 +126,24 @@ looking at the group's **Members** page.
![Group members page](img/group_members.png)
-From the image above, we can deduct the following things:
+From the image above, we can deduce the following things:
-- There are 5 members that have access to the group `four`
+- There are 5 members that have access to the group `four`.
- User0 is a Reporter and has inherited their permissions from group `one`
- which is above the hierarchy of group `four`
+ which is above the hierarchy of group `four`.
- User1 is a Developer and has inherited their permissions from group
- `one/two` which is above the hierarchy of group `four`
+ `one/two` which is above the hierarchy of group `four`.
- User2 is a Developer and has inherited their permissions from group
- `one/two/three` which is above the hierarchy of group `four`
+ `one/two/three` which is above the hierarchy of group `four`.
- For User3 there is no indication of a parent group, therefore they belong to
- group `four`, the one we're inspecting
+ group `four`, the one we're inspecting.
- Administrator is the Owner and member of **all** subgroups and for that reason,
- same as User3, there is no indication of an ancestor group
+ as with User3, there is no indication of an ancestor group.
### Overriding the ancestor group membership
NOTE: **Note:**
-You need to be an Owner of a group in order to be able to add members to it.
+You must be an Owner of a group to be able to add members to it.
NOTE: **Note:**
A user's permissions in a subgroup cannot be lower than in any of its ancestor groups.
@@ -154,7 +161,7 @@ the permissions will fallback to those of the ancestor group.
## Mentioning subgroups
Mentioning groups (`@group`) in issues, commits and merge requests, would
-notify all members of that group. Now with subgroups, there is a more granular
+notify all members of that group. Now with subgroups, there is more granular
support if you want to split your group's structure. Mentioning works as before
and you can choose the group of people to be notified.
@@ -179,3 +186,15 @@ Here's a list of what you can't do with subgroups:
[permissions]: ../../permissions.md#group-members-permissions
[reserved]: ../../reserved_names.md
[issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/img/markdown_inline_diffs_tags_rendered.png b/doc/user/img/markdown_inline_diffs_tags_rendered.png
new file mode 100644
index 00000000000..4279a20b5a0
--- /dev/null
+++ b/doc/user/img/markdown_inline_diffs_tags_rendered.png
Binary files differ
diff --git a/doc/user/instance_statistics/convdev.md b/doc/user/instance_statistics/convdev.md
index 2c9e0ecbf65..d486ecc39a4 100644
--- a/doc/user/instance_statistics/convdev.md
+++ b/doc/user/instance_statistics/convdev.md
@@ -5,7 +5,7 @@
NOTE: **Note:**
Your GitLab instance's [usage ping](../admin_area/settings/usage_statistics.md#usage-ping-core-only) must be activated in order to use this feature.
-The Conversational Development Index (ConvDev Index) gives you an overview of your entire
+The [Conversational Development](http://conversationaldevelopment.com/2017/04/16/what-is-conversational-development/) Index (ConvDev Index) gives you an overview of your entire
instance's adoption of [Concurrent DevOps](https://about.gitlab.com/concurrent-devops/)
from planning to monitoring.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 6b6e5ab7634..31c8093ced7 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -246,7 +246,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-
With inline diffs tags you can display {+ additions +} or [- deletions -].
-The wrapping tags can be either curly braces or square brackets: [+ additions +] or {- deletions -}.
+The wrapping tags can be either curly braces or square brackets.
Examples:
@@ -257,6 +257,10 @@ Examples:
- [- deletions -]
```
+becomes:
+
+![inline diffs tags rendered](img/markdown_inline_diffs_tags_rendered.png)
+
However the wrapping tags cannot be mixed as such:
```
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index a6e2f187090..5b348b1ac60 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -196,21 +196,22 @@ Any user can remove themselves from a group, unless they are the last Owner of
the group. The following table depicts the various user permission levels in a
group.
-| Action | Guest | Reporter | Developer | Maintainer | Owner |
-|---------------------------------------|-------|----------|-----------|------------|-------|
-| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
-| View Insights charts **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
-| View group epic **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
-| Create/edit group epic **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
-| Manage group labels | | ✓ | ✓ | ✓ | ✓ |
-| Create project in group | | | ✓ | ✓ | ✓ |
-| Create/edit/delete group milestones | | | ✓ | ✓ | ✓ |
-| Edit group | | | | | ✓ |
-| Create subgroup | | | | | ✓ |
-| Manage group members | | | | | ✓ |
-| Remove group | | | | | ✓ |
-| Delete group epic **[ULTIMATE]** | | | | | ✓ |
-| View group Audit Events | | | | | ✓ |
+| Action | Guest | Reporter | Developer | Maintainer | Owner |
+|-------------------------------------------------|-------|----------|-----------|------------|-------|
+| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
+| View Insights charts **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| View group epic **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Create/edit group epic **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
+| Manage group labels | | ✓ | ✓ | ✓ | ✓ |
+| Create project in group | | | ✓ | ✓ | ✓ |
+| Create/edit/delete group milestones | | | ✓ | ✓ | ✓ |
+| Enable/disable a dependency proxy **[PREMIUM]** | | | ✓ | ✓ | ✓ |
+| Edit group | | | | | ✓ |
+| Create subgroup | | | | | ✓ |
+| Manage group members | | | | | ✓ |
+| Remove group | | | | | ✓ |
+| Delete group epic **[ULTIMATE]** | | | | | ✓ |
+| View group Audit Events | | | | | ✓ |
### Subgroup permissions
diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md
index 0d7bbb0af79..304a7984191 100644
--- a/doc/user/profile/account/delete_account.md
+++ b/doc/user/profile/account/delete_account.md
@@ -23,7 +23,7 @@ Here's a list of things that will **not** be deleted:
- Award emoji that the user created
Instead of being deleted, these records will be moved to a system-wide
-"Ghost User", whose sole purpose is to act as a container for such records.
+user with the username "Ghost User", whose sole purpose is to act as a container for such records. Any commits made by a deleted user will still display the username of the original user.
When a user is deleted from an [abuse report](../../admin_area/abuse_reports.md) or spam log, these associated
records are not ghosted and will be removed, along with any groups the user
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index dfbed0ec95f..2cda850f24e 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -216,7 +216,7 @@ The sample function can now be triggered from any HTTP client using a simple `PO
--header "Content-Type: application/json" \
--request POST \
--data '{"GitLab":"FaaS"}' \
- http://functions-echo.functions-1.functions.example.com
+ http://functions-echo.functions-1.functions.example.com/
```
2. Using a web-based tool (ie. postman, restlet, etc)
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index 19ab911e39f..dc97a44fd68 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -1,10 +1,7 @@
# Cycle Analytics
-> [Introduced][ce-5986] in GitLab 8.12. Further features were added in GitLab
- 8.14.
-
-Cycle Analytics measures the time spent to go from an [idea to production] for
-each of your projects. Cycle Analytics displays the median time for an idea to
+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
@@ -54,9 +51,9 @@ Below you can see in more detail what the various stages of Cycle Analytics mean
| 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. `master` is not excluded. It does not attempt to track time for any particular stages. |
-| Review | Measures the median time taken to review the merge request, between its creation and until it's merged. |
-| Staging | Measures the median time between merging the merge request 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. |
+| 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. |
---
@@ -77,8 +74,7 @@ To sum up, anything that doesn't follow the [GitLab flow] won't be tracked at al
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.
-- For issues not assigned a milestone.
+- 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.
@@ -86,7 +82,7 @@ So, the Cycle Analytics dashboard won't present any data:
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/stop mark, it is not measured and hence not calculated in the median
+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.
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index 8fba892594b..e194d57e2e0 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -60,6 +60,8 @@ For additional technical details, you can refer to the
[GitHub Importer](../../../development/github_importer.md "Working with the GitHub importer")
developer documentation.
+For an overview of the import process, see the video [Migrating from GitHub to GitLab](https://youtu.be/VYOXuOg9tQI).
+
## Import your GitHub repository into GitLab
### Using the GitHub integration
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
index a24f525253d..587b4121e4e 100644
--- a/doc/user/project/index.md
+++ b/doc/user/project/index.md
@@ -84,6 +84,7 @@ When you create a project in GitLab, you'll have access to a large number of
- [Wiki](wiki/index.md): document your GitLab project in an integrated Wiki.
- [Snippets](../snippets.md): store, share and collaborate on code snippets.
- [Cycle Analytics](cycle_analytics.md): review your development lifecycle.
+- [Insights](insights/index.md): configure the Insights that matter for your projects. **[ULTIMATE]**
- [Security Dashboard](security_dashboard.md): Security Dashboard. **[ULTIMATE]**
- [Syntax highlighting](highlighting.md): an alternative to customize
your code blocks, overriding GitLab's default choice of language.
diff --git a/doc/user/project/insights/index.md b/doc/user/project/insights/index.md
index 3344e560870..2e2a27f112e 100644
--- a/doc/user/project/insights/index.md
+++ b/doc/user/project/insights/index.md
@@ -1,11 +1,7 @@
# Insights **[ULTIMATE]**
> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.9 behind the `insights` feature flag.
-
-CAUTION: **Beta:**
-Insights is considered beta, and is not ready for production use.
-Follow [gitlab-org/quality/team-tasks#137](https://gitlab.com/gitlab-org/quality/team-tasks/issues/137#general-availability)
-for updates.
+> **Generally Available** (GA) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
Configure the Insights that matter for your projects to explore data such as
triage hygiene, issues created/closed per a given period, average time for merge
diff --git a/doc/user/project/integrations/img/jira_add_user_to_group.png b/doc/user/project/integrations/img/jira_add_user_to_group.png
index 27dac49260c..d8cf541a81e 100644
--- a/doc/user/project/integrations/img/jira_add_user_to_group.png
+++ b/doc/user/project/integrations/img/jira_add_user_to_group.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_added_user_to_group.png b/doc/user/project/integrations/img/jira_added_user_to_group.png
new file mode 100644
index 00000000000..b3e29a65d6e
--- /dev/null
+++ b/doc/user/project/integrations/img/jira_added_user_to_group.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_create_new_group.png b/doc/user/project/integrations/img/jira_create_new_group.png
index 06c4e84fc61..84be3a94a45 100644
--- a/doc/user/project/integrations/img/jira_create_new_group.png
+++ b/doc/user/project/integrations/img/jira_create_new_group.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_create_new_user.png b/doc/user/project/integrations/img/jira_create_new_user.png
index e9c03ed770d..8460dc98ef9 100644
--- a/doc/user/project/integrations/img/jira_create_new_user.png
+++ b/doc/user/project/integrations/img/jira_create_new_user.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_group_access.png b/doc/user/project/integrations/img/jira_group_access.png
index 448cc55504d..58cf114bd55 100644
--- a/doc/user/project/integrations/img/jira_group_access.png
+++ b/doc/user/project/integrations/img/jira_group_access.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_user_management_link.png b/doc/user/project/integrations/img/jira_user_management_link.png
index 5eb9d031c3e..43ef18da6c8 100644
--- a/doc/user/project/integrations/img/jira_user_management_link.png
+++ b/doc/user/project/integrations/img/jira_user_management_link.png
Binary files differ
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index a90167b9767..234e3ad31cc 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -8,6 +8,8 @@ While you can always migrate content and process from Jira to GitLab Issues,
you can also opt to continue using Jira and use it together with GitLab through
our integration.
+For a video demonstration of integration with Jira, watch [GitLab workflow with Jira issues and Jenkins pipelines](https://youtu.be/Jn-_fyra7xQ).
+
Once you integrate your GitLab project with your Jira instance, you can automatically
detect and cross-reference activity between the GitLab project and any of your projects
in Jira. This includes the ability to close or transition Jira issues when the work
@@ -47,11 +49,11 @@ project in Jira and then enter the correct values in GitLab.
When connecting to **JIRA Server**, which supports basic authentication, a **username and password** are required. Check the link below and proceed to the next step:
-- [Setting up an user in JIRA server](jira_server_configuration.md)
+- [Setting up a user in JIRA server](jira_server_configuration.md)
When connecting to **JIRA Cloud**, which supports authentication via API token, an **email and API token**, are required. Check the link below and proceed to the next step:
-- [Setting up an user in JIRA cloud](jira_cloud_configuration.md)
+- [Setting up a user in JIRA cloud](jira_cloud_configuration.md)
### Configuring GitLab
diff --git a/doc/user/project/integrations/jira_server_configuration.md b/doc/user/project/integrations/jira_server_configuration.md
index 20036183187..13d65c4d8e4 100644
--- a/doc/user/project/integrations/jira_server_configuration.md
+++ b/doc/user/project/integrations/jira_server_configuration.md
@@ -1,18 +1,16 @@
# Creating a username and password for JIRA server
We need to create a user in Jira which will have access to all projects that
-need to integrate with GitLab. Login to your Jira instance as admin and under
-*Administration*, go to *User Management* and create a new user.
+need to integrate with GitLab.
As an example, we'll create a user named `gitlab` and add it to the `Jira-developers`
group.
NOTE: **Note**
-It is important that the user `gitlab` has 'write' access to projects in Jira.
+It is important that the Jira user created for the integration is given 'write'
+access to your Jira projects. This is covered in the process below.
-We have split this stage in steps so it is easier to follow.
-
-1. Log in to your Jira instance as an administrator and under **Administration**
+1. Log in to your Jira instance as an administrator and under **Jira Administration**
go to **User Management** to create a new user.
![Jira user management link](img/jira_user_management_link.png)
@@ -27,27 +25,34 @@ We have split this stage in steps so it is easier to follow.
![Jira create new user](img/jira_create_new_user.png)
-1. Create a `gitlab-developers` group which will have write access
- to projects in Jira. Go to the **Groups** tab and select **Create group**.
+1. Create a `gitlab-developers` group. (We will give this group write access to Jira
+ projects in a later step). Go to the **Groups** tab on the left, and select **Add group**.
![Jira create new user](img/jira_create_new_group.png)
- Give it an optional description and click **Create group**.
+ Give it a name and click **Add group**.
- ![Jira create new group](img/jira_create_new_group_name.png)
+1. Add the `gitlab` user to the `gitlab-developers` group by clicking **Edit members**.
+ The `gitlab-developers` group should be listed in the leftmost box as a selected group.
+ Under **Add members to selected group(s)**, enter `gitlab`.
-1. To give the newly-created group 'write' access, go to
- **Application access > View configuration** and add the `gitlab-developers`
- group to Jira Core.
+ ![Jira add user to group](img/jira_add_user_to_group.png)
+
+ Click **Add selected users** and `gitlab` should appear in the **Group member(s)** box.
+ This membership is saved automatically.
+
+ ![Jira added user to group](img/jira_added_user_to_group.png)
+
+1. To give the newly-created group 'write' access, you need to create a **Permission Scheme**.
+ To do this, click the gear icon and select **Issues**. Then click **Permission Schemes**.
+ Click **Add Permission Scheme** and enter a **Name** and, optionally, a **Description**.
+
+1. Once your permission scheme is created, you'll be taken back to the permissions scheme list.
+ Locate your new permissions scheme and click **Permissions**. Next to **Administer Projects**,
+ click **Edit**. In the resulting dialog box, select **Group** and select `gitlab-developers`
+ from the dropdown.
![Jira group access](img/jira_group_access.png)
-1. Add the `gitlab` user to the `gitlab-developers` group by going to
- **Users > GitLab user > Add group** and selecting the `gitlab-developers`
- group from the dropdown menu. Notice that the group says _Access_, which is
- intended as part of this process.
-
- ![Jira add user to group](img/jira_add_user_to_group.png)
-
The Jira configuration is complete. Write down the new Jira username and its
password as they will be needed when [configuring GitLab in the next section](jira.md#configuring-gitlab).
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index a0f500a939f..d5f28ddabc1 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -653,7 +653,33 @@ X-Gitlab-Event: Note Hook
"description": "test",
"milestone_id": null,
"state": "closed",
- "iid": 17
+ "iid": 17,
+ "labels": [
+ {
+ "id": 25,
+ "title": "Afterpod",
+ "color": "#3e8068",
+ "project_id": null,
+ "created_at": "2019-06-05T14:32:20.211Z",
+ "updated_at": "2019-06-05T14:32:20.211Z",
+ "template": false,
+ "description": null,
+ "type": "GroupLabel",
+ "group_id": 4
+ },
+ {
+ "id": 86,
+ "title": "Element",
+ "color": "#231afe",
+ "project_id": 4,
+ "created_at": "2019-06-05T14:32:20.637Z",
+ "updated_at": "2019-06-05T14:32:20.637Z",
+ "template": false,
+ "description": null,
+ "type": "ProjectLabel",
+ "group_id": null
+ }
+ ],
}
}
```
diff --git a/doc/user/project/integrations/youtrack.md b/doc/user/project/integrations/youtrack.md
index a2a468b6fe4..81c148e41fd 100644
--- a/doc/user/project/integrations/youtrack.md
+++ b/doc/user/project/integrations/youtrack.md
@@ -31,8 +31,8 @@ To disable the internal issue tracker in a project:
## Referencing YouTrack issues in GitLab
Issues in YouTrack can be referenced as `<PROJECT>-<ID>`. `<PROJECT>`
-must start with a capital letter and can then be followed by capital or lower case
-letters, numbers or underscores. `<ID>` is a number. An example reference is `YT-101` or `Api_32-143`.
+must start with a letter and is followed by letters, numbers, or underscores.
+`<ID>` is a number. An example reference is `YT-101`, `Api_32-143` or `gl-030`.
References to `<PROJECT>-<ID>` in merge requests, commits, or comments are automatically linked to the YouTrack issue URL.
For more information, see the [External Issue Tracker](../../../integration/external-issue-tracker.md) documentation.
diff --git a/doc/user/project/issues/img/comment-or-discussion.png b/doc/user/project/issues/img/comment-or-discussion.png
new file mode 100644
index 00000000000..ccecc9fa39f
--- /dev/null
+++ b/doc/user/project/issues/img/comment-or-discussion.png
Binary files differ
diff --git a/doc/user/project/issues/img/create_mr_from_issue.png b/doc/user/project/issues/img/create_mr_from_issue.png
new file mode 100644
index 00000000000..680c42b5df0
--- /dev/null
+++ b/doc/user/project/issues/img/create_mr_from_issue.png
Binary files differ
diff --git a/doc/user/project/issues/img/issues_main_view_numbered.jpg b/doc/user/project/issues/img/issues_main_view_numbered.jpg
deleted file mode 100644
index b4b68476d24..00000000000
--- a/doc/user/project/issues/img/issues_main_view_numbered.jpg
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/issues/img/issues_main_view_numbered.png b/doc/user/project/issues/img/issues_main_view_numbered.png
new file mode 100644
index 00000000000..16cb6b497b0
--- /dev/null
+++ b/doc/user/project/issues/img/issues_main_view_numbered.png
Binary files differ
diff --git a/doc/user/project/issues/img/reopen-issue.png b/doc/user/project/issues/img/reopen-issue.png
new file mode 100644
index 00000000000..1749be31239
--- /dev/null
+++ b/doc/user/project/issues/img/reopen-issue.png
Binary files differ
diff --git a/doc/user/project/issues/img/report-abuse.png b/doc/user/project/issues/img/report-abuse.png
new file mode 100644
index 00000000000..f189cbf1d36
--- /dev/null
+++ b/doc/user/project/issues/img/report-abuse.png
Binary files differ
diff --git a/doc/user/project/issues/img/show-all-activity.png b/doc/user/project/issues/img/show-all-activity.png
new file mode 100644
index 00000000000..c43ba75ce25
--- /dev/null
+++ b/doc/user/project/issues/img/show-all-activity.png
Binary files differ
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index 94865ad46ee..76dc6e49bce 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -86,7 +86,9 @@ For more information, see the [Issue Data and Actions](issue_data_and_actions.md
On the Issues List, you can view all issues in the current project, or from multiple projects when opening the Issues List from the higher-level group context. Filter the issue list by [any search query](../../search/index.md#issues-and-merge-requests-per-project) and/or specific metadata, such as label(s), assignees(s), status, and more. From this view, you can also make certain changes [in bulk](../bulk_editing.md) to the displayed issues.
-For more information, see the [Issue Data and Actions](issue_data_and_actions.md) page.
+For more information on interacting with Issues, see the [Issue Data and Actions](issue_data_and_actions.md) page.
+
+For sorting by issue priority, see [Label Priority](../labels.md#label-priority).
### Issue boards
@@ -123,7 +125,7 @@ For more information, see [Crosslinking issues](crosslinking_issues.md).
- [Close an issue](closing_issues.md)
- [Move an issue](moving_issues.md)
- [Delete an issue](deleting_issues.md)
-- [Create a merge request from an issue](issue_data_and_actions.md#18-new-merge-request)
+- [Create a merge request from an issue](issue_data_and_actions.md#22-create-merge-request)
## Advanced issue management
diff --git a/doc/user/project/issues/issue_data_and_actions.md b/doc/user/project/issues/issue_data_and_actions.md
index fc11c0251e0..da585022263 100644
--- a/doc/user/project/issues/issue_data_and_actions.md
+++ b/doc/user/project/issues/issue_data_and_actions.md
@@ -4,11 +4,13 @@ Please read through the [GitLab Issue Documentation](index.md) for an overview o
## Parts of an Issue
-The image below illustrates what an issue looks like:
+The image below illustrates what an issue may look like. Note that certain parts will
+look slightly different or will be absent, depending on the version of GitLab being used
+and the permissions of the user viewing the issue.
-![Issue view](img/issues_main_view_numbered.jpg)
+![Issue view](img/issues_main_view_numbered.png)
-You can find all the information on that issue on one screen.
+You can find all the information for that issue on one screen.
### Issue screen
@@ -16,157 +18,215 @@ An issue starts with its status (open or closed), followed by its author,
and includes many other functionalities, numbered in the image above to
explain what they mean, one by one.
-Many of the elements of the issue screen refresh automatically, such as the title and description, when they are changed by another user.
-Comments and system notes also appear automatically in response to various actions and content updates.
+Many of the elements of the issue screen refresh automatically, such as the title and
+description, when they are changed by another user. Comments and system notes also
+update automatically in response to various actions and content updates.
-#### 1. New Issue, close issue, edit
+#### 1. New Issue, close issue (reopen issue, report issue)
-- New issue: create a new issue in the same project
-- Close issue: close this issue
-- Edit: edit the same fields available when you create an issue.
+Clicking on **New issue** will open a new window to create a new issue in the same project.
+Clicking on **Close issue** will close this issue, but it will not be deleted. If the
+issue is already closed, you can still access it and the button will show **Reopen issue**, as shown below,
+which you can click to reopen the issue. A reopened issue is no different from any
+other issue.
+
+![Reopen Issue](img/reopen-issue.png)
+
+If you do not have rights to modify the issue, the **close issue** button will be
+replaced with **report issue**, which you can click to [submit an abuse report](../../abuse_reports.md)
+about the issue. It will also appear if you have rights to modify the issue, but only
+after it is closed.
+
+![Report Abuse](img/report-abuse.png)
#### 2. Todos
-- Add todo: add that issue to your [GitLab Todo](../../../workflow/todos.md) list
-- Mark todo as done: mark that issue as done (reflects on the Todo list)
+You can click **add todo** to add the issue to your [GitLab Todo](../../../workflow/todos.md)
+list. If it is already on your todo list, the buttom will show **mark todo as done**,
+which you can click to mark that issue as done (which will be reflected in the Todo list).
#### 3. Assignee
-Whenever someone starts to work on an issue, it can be assigned
-to that person. The assignee can be changed as much as needed.
-The idea is that the assignee is responsible for that issue until
-it's reassigned to someone else to take it from there.
+An issue can be assigned to yourself, another person, or [many people](#31-multiple-assignees-STARTER).
+The assignee(s) can be changed as much as needed. The idea is that the assignees are
+responsible for that issue until it's reassigned to someone else to take it from there.
+When assigned to someone, it will appear in their assigned issues list.
-> **Tip:**
-if a user is not member of that project, it can only be
+TIP: **Tip:**
+If a user is not member of that project, it can only be
assigned to them if they created the issue themselves.
##### 3.1. Multiple Assignees **[STARTER]**
-Often multiple people work on the same issue together,
-which can be especially difficult to track in large teams
-where there is shared ownership of an issue.
+Often multiple people work on the same issue together, which can be especially difficult
+to track in large teams where there is shared ownership of an issue.
In [GitLab Starter](https://about.gitlab.com/pricing/), you can
-assign multiple people to an issue.
+[assign multiple people](multiple_assignees_for_issues.md) to an issue.
+
+#### 4. Epic **[ULTIMATE]**
+
+You can assign issues to an [Epic](../../group/epics/index.md), which allows better
+management of groups of related issues.
-Learn more in the [Multiple Assignees documentation](multiple_assignees_for_issues.md).
+#### 5. Milestone
-#### 4. Milestone
+Select a [milestone](../milestones/index.md) to attribute that issue to.
-- Select a [milestone](../milestones/index.md) to attribute that issue to.
+#### 6. Time Tracking
-#### 5. Time Tracking
+Use [GitLab Quick Actions](../quick_actions.md) to [track estimates and time spent on issues](../../../workflow/time_tracking.md).
+You can add an [estimate of the time it will take](../../../workflow/time_tracking.md#estimates)
+to resolve the issue, and also add [the time spent](../../../workflow/time_tracking.md#time-spent)
+on the resolution of the issue.
-- Estimate time: add an estimate of the time it will take to resolve the issue.
-- Spend: add the time spent on the resolution of the issue
+#### 7. Due date
-> **Note:**
-Both estimate and spend times are set via [GitLab Quick Actions](../quick_actions.md).
+When you work on a tight schedule, it's important to have a way to set a deadline for
+implementations and for solving problems. This can be done in the [due date](due_dates.md)
+element. Due dates can be changed as many times as needed.
-Learn more in the [Time Tracking documentation](../../../workflow/time_tracking.md).
+#### 8. Labels
-#### 6. Due date
+Categorize issues by giving them [labels](../labels.md). They help to organize workflows,
+and they enable you to work with the [GitLab Issue Board](index.md#issue-boards).
-When you work on a tight schedule, it's important to
-have a way to set a deadline for implementations and for solving
-problems. This can be done in the [due date](due_dates.md) element. Due dates
-can be changed as many times as needed.
+Group Labels, which allow you to use the same labels for all projects within the same
+group, can be also given to issues. They work exactly the same, but they are immediately
+available to all projects in the group.
-#### 7. Labels
+TIP: **Tip:**
+If a label doesn't exist yet, you can click **Edit**, and it opens a dropdown menu
+from which you can select **Create new label**.
-Categorize issues by giving them [labels](../labels.md). They help to
-organize workflows, and they enable you to work with the
-[GitLab Issue Board](index.md#issue-boards).
+#### 9. Weight **[STARTER]**
-Group Labels, which allow you to use the same labels for a
-group of projects, can be also given to issues. They work exactly the same,
-but they are immediately available to all projects in the group.
+[Assign a weight](../../../workflow/issue_weight.md) to an issue.
+Larger values are used to indicate more effort is required to complete the issue. Only
+positive values or zero are allowed.
-> **Tip:**
-If a label doesn't exist yet, you can click **Edit**, and it opens a dropdown menu from which you can select **Create new label**.
+#### 10. Confidentiality
-#### 8. Weight **[STARTER]**
+You can [set an issue to be confidential](confidential_issues.md). When set, unauthorized
+users will not be able to access the issue, and will not see it listed in project
+issue boards or the issue list.
-- Assign a weight. Larger values are used to indicate more effort is required to complete the issue. Only positive values or zero are allowed.
+#### 11. Lock issue
-Learn more in the [Issue Weight documentation](../../../workflow/issue_weight.md).
+You can [lock the discussions](../../discussions/index.md#lock-discussions) in the issue,
+to prevent further comments from being added.
-#### 9. Participants
+#### 12. Participants
-- People involved in that issue (mentioned in the description or in the [discussion](../../discussions/index.md)).
+All the users involved in that issue. Either they participated in the [discussion](../../discussions/index.md),
+or were mentioned in the description or discussions.
-#### 10. Notifications
+#### 13. Notifications
-- Subscribe: if you are not a participant of the discussion on that issue, but
- want to receive notifications on each new input, subscribe to it.
-- Unsubscribe: if you are receiving notifications on that issue but no
+Click on the icon to enable/disable [notifications](../../../workflow/notifications.md#issue--epics--merge-request-events)
+for the issue. This will automatically enable if you participate in the issue in any way.
+
+- **Enable**: If you are not a participant in the discussion on that issue, but
+ want to receive notifications on each update, subscribe to it.
+- **Disable**: If you are receiving notifications for updates to that issue but no
longer want to receive them, unsubscribe from it.
-Read more in the [notifications documentation](../../../workflow/notifications.md#issue--epics--merge-request-events).
+#### 14. Reference
+
+- A quick "copy to clipboard" button for that issue's reference, which looks like `foo/bar#xxx`,
+ where `foo` is the `username` or `groupname`, `bar` is the `project-name`, and
+ `xxx` is the issue number.
+
+#### 15. Edit
+
+Clicking this icon opens the issue for editing, and you will have access to all the
+same fields as when the issue was created. This icon will not display if the user
+does not have permission to edit the issue.
+
+#### 16. Description
+
+The plain text title and description of the issue fill the top center of the issue page.
+The description fully supports [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm),
+allowing many formatting options.
+
+#### 17. Mentions
+
+You can mention a user or a group present in your GitLab instance with `@username` or
+`@groupname` and they will be notified via todos and email, unless they have disabled
+all notifications in their profile settings. This is controlled in the
+[notification settings](../../../workflow/notifications.md).
+
+Mentions for yourself (the current logged in user), will be highlighted in a different
+color, allowing you to easily see which comments involve you, helping you focus on
+them quickly.
+
+TIP: **Tip:**
+Avoid mentioning `@all` in issues and merge requests, as it sends an email notification
+to all the members of that project's group, which can be interpreted as spam.
+
+#### 18. Related Issues **[STARTER]**
+
+Issues that were mentioned as [related issues](related_issues.md) are listed here.
+You can also click the `+` to add more related issues.
+
+#### 19. Related Merge Requests
-#### 11. Reference
+Merge requests that were mentioned in that issue's description or in the issue discussion
+thread are listed as [related merge requests](crosslinking_issues.md#from-merge-requests) here.
+Also, if the current issue was mentioned as related in another merge request, that
+merge request will be listed here.
-- A quick "copy to clipboard" button for that issue's reference, `foo/bar#xxx`, where `foo` is the `username` or `groupname`, `bar`
- is the `project-name`, and `xxx` is the issue number.
+#### 20. Award emoji
-#### 12. Title and description
+You can award an emoji to that issue. There are shortcuts to "thumbs_up" and "thumbs_down",
+or you can click on the light gray "face" to choose a different reaction from the
+dropdown list of available [GitLab Flavored Markdown Emoji](../../markdown.md#emoji).
-- Title: a plain text title for describing the subject of the issue.
-- Description: a large text field which fully supports [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm),
- to describe all the details of the issue.
+TIP: **Tip:**
+Posting "+1" as a comment in a thread spams all subscribed participants of that issue,
+clutters the discussion threads, and is not recommended. Awarding an emoji is a way
+to let them know your reaction without spamming them.
-#### 13. Mentions
+#### 21. Show all activity
-- You can mention a user or a group present in your GitLab instance with
- `@username` or `@groupname` and they will be notified via todos and email, unless
- they have disabled all notifications in their profile settings.
-- Mentions for yourself (the current logged in user), will be highlighted
- in a different color, allowing you to easily see which comments involve you,
- helping you focus on them quickly.
+You can filter what is displayed in the issue history by clicking on **Show all activity**
+and selecting either **Show comments only**, which only shows discussions and hides
+updates to the issue, or **Show history only**, which hides discussions and only shows updates.
-To change your [notification settings](../../../workflow/notifications.md), navigate to
-**Profile Settings** > **Notifications** > **Global notification level**
-and choose your preference from the dropdown menu.
+![Show all activity](img/show-all-activity.png)
-> **Tip:**
-Avoid mentioning `@all` in issues and merge requests,
-as it sends an email notification
-to all the members of that project's group, which can be
-interpreted as spam.
+#### 22. Create Merge Request
-#### 14. Related Merge Requests
+Create a new branch and [WIP merge request](../merge_requests/work_in_progress_merge_requests.md)
+in one action. The branch will be named `issuenumber-title` by default, but you can
+choose any name, and GitLab will verify that it is not already in use. The merge request
+will automatically inherit the milestone and labels of the issue, and will be set to
+close the issue when it is merged.
-- Any merge requests mentioned in that issue's description
- or in the issue discussion thread.
+![Create MR from issue](img/create_mr_from_issue.png)
-#### 15. Award emoji
+Optionally, you can choose to create a [new branch](../repository/web_editor.md#create-a-new-branch-from-an-issue)
+only, named after that issue.
-- Award an emoji to that issue.
+#### 23. Issue history
-> **Tip:**
-Posting "+1" as a comment in a thread spams all subscribed
-participants of that issue. Awarding an emoji is a way to let them
-know you like it without spamming them.
+All comments and updates to the issue are tracked and listed here, but this can be
+filtered, as shown above.
-#### 16. Thread
+#### 24. Comments
-- Comments: collaborate to that issue by posting comments in its thread.
- These text fields also fully support
- [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm).
+Collaborate in the issue by posting comments in its thread. This text field also fully
+supports [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm).
-#### 17. Comment, start a discussion, or comment and close
+#### 25. Submit Comment, start a discussion, or comment and close
-Once you write a comment, you can either:
+Once you write a comment, you can:
-- Click "Comment" and your comment will be published.
-- Click "Start discussion": start a thread within that issue's thread to discuss specific points.
-- Click "Comment and close issue": post your comment and close that issue in one click.
+- Click **Comment** and your comment will be published.
+- Choose **Start discussion** from the dropdown list and start a new [discussion thread](../../discussions/index.md#threaded-discussions)
+ within that issue's main thread to discuss specific points. This invites other participants
+ to reply directly to your discussion, keeping related comments grouped together.
-#### 18. New Merge Request
+![Comment or discussion](img/comment-or-discussion.png)
-- Create a new merge request (with a new source branch named after the issue) in one action.
- The merge request will automatically inherit the milestone and labels of the issue. The merge
- request will automatically close that issue when it is merged.
-- Optionally, you can just create a [new branch](../repository/web_editor.md#create-a-new-branch-from-an-issue)
- named after that issue.
+You can also close the issue from here, so you don't need to scroll to the top of the issue page.
diff --git a/doc/user/project/issues/moving_issues.md b/doc/user/project/issues/moving_issues.md
index 211a651b89e..8aac2c01444 100644
--- a/doc/user/project/issues/moving_issues.md
+++ b/doc/user/project/issues/moving_issues.md
@@ -8,3 +8,28 @@ There will also be a system note added to both issues indicating where it came f
You can move an issue with the "Move issue" button at the bottom of the right-sidebar when viewing the issue.
![move issue - button](img/sidebar_move_issue.png)
+
+## Troubleshooting
+
+### Moving Issues in Bulk
+
+If you have advanced technical skills you can also bulk move all the issues from one project to another in the rails console. The below script will move all the issues from one project to another that are not in status **closed**.
+
+To access rails console run `sudo gitlab-rails console` on the GitLab server and run the below script. Please be sure to change **project**, **admin_user** and **target_project** to your values. We do also recommend [creating a backup](https://docs.gitlab.com/ee/raketasks/backup_restore.html#creating-a-backup-of-the-gitlab-system) before attempting any changes in the console.
+
+```ruby
+project = Project.find_by_full_path('full path of the project where issues are moved from')
+issues = project.issues
+admin_user = User.find_by_username('username of admin user') # make sure user has permissions to move the issues
+target_project = Project.find_by_full_path('full path of target project where issues moved to')
+
+issues.each do |issue|
+ if issue.state != "closed" && issue.moved_to.nil?
+ Issues::MoveService.new(project, admin_user).execute(issue, target_project)
+ else
+ puts "issue with id: #{issue.id} and title: #{issue.title} was not moved"
+ end
+end; nil
+
+```
+
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index 8e9e9aa79cf..3eca1313a18 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -24,14 +24,20 @@ in the label’s title, using the format `key::value`. For example:
![A sample scoped label](img/key_value_labels.png)
-Two scoped labels with the same key but a different value cannot simultaneously
-apply to an issue, epic, or merge request. For example, if an issue already has `priority::3`
-and you apply `priority::2` to it, `priority::3` is automatically removed from the issue.
-
An issue, epic, or merge request cannot have two scoped labels with the same key.
For example, if an issue is already labeled `priority::3` and you apply the label `priority::2` to it,
`priority::3` is automatically removed.
+This functionality is demonstrated in a video titled [Use scoped labels in GitLab 11.10 for custom fields and custom workflows](https://www.youtube.com/watch?v=4BCBby6du3c).
+
+### Labels with multiple colon pairs
+
+If labels have multiple instances of `::`, the longest path from left to right, until the last `::`, is considered the "key" or the "scope".
+
+For example, `nested::key1::value1` and `nested::key1::value2` cannot both exist on the same issue. Adding the latter label will automatically remove the former due to the shared scope of `nested::key1`.
+
+`nested::key1::value1` and `nested::key2::value1` can both exist on the same issue, as these are considered to use two different label scopes, `nested::key1` and `nested::key2`.
+
### Workflows with scoped labels **[PREMIUM]**
Suppose you wanted a custom field in issues to track the platform operating system
diff --git a/doc/user/project/merge_requests/allow_collaboration.md b/doc/user/project/merge_requests/allow_collaboration.md
index da6e6b5fd3a..e94125e658d 100644
--- a/doc/user/project/merge_requests/allow_collaboration.md
+++ b/doc/user/project/merge_requests/allow_collaboration.md
@@ -1,3 +1,7 @@
+---
+type: reference, howto
+---
+
# Allow collaboration on merge requests across forks
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17395)
@@ -70,3 +74,15 @@ Here's how the process would look like:
Note the colon (`:`) between the two branches. The above command will push the
local branch `thedude-awesome-project-update-docs` to the
`update-docs` branch of the `git@gitlab.com:thedude/awesome-project.git` repository.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/authorization_for_merge_requests.md b/doc/user/project/merge_requests/authorization_for_merge_requests.md
index 79444ee5682..0579e3568da 100644
--- a/doc/user/project/merge_requests/authorization_for_merge_requests.md
+++ b/doc/user/project/merge_requests/authorization_for_merge_requests.md
@@ -1,8 +1,12 @@
+---
+type: concepts
+---
+
# Authorization for Merge requests
There are two main ways to have a merge request flow with GitLab:
-1. Working with [protected branches] in a single repository.
+1. Working with [protected branches](../protected_branches.md) in a single repository.
1. Working with forks of an authoritative project.
## Protected branch flow
@@ -53,4 +57,14 @@ forks.
- The project need to keep their forks up to date, which requires more advanced
Git skills (managing multiple remotes).
-[protected branches]: ../protected_branches.md
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/browser_performance_testing.md b/doc/user/project/merge_requests/browser_performance_testing.md
index 65ee2e128ae..f6c4f767e3c 100644
--- a/doc/user/project/merge_requests/browser_performance_testing.md
+++ b/doc/user/project/merge_requests/browser_performance_testing.md
@@ -1,14 +1,18 @@
+---
+type: reference, howto
+---
+
# Browser Performance Testing **[PREMIUM]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3507)
in [GitLab Premium](https://about.gitlab.com/pricing/) 10.3.
-## Overview
-
If your application offers a web interface and you are using
[GitLab CI/CD](../../../ci/README.md), you can quickly determine the performance
impact of pending code changes.
+## Overview
+
GitLab uses [Sitespeed.io](https://www.sitespeed.io), a free and open source
tool for measuring the performance of web sites, and has built a simple
[Sitespeed plugin](https://gitlab.com/gitlab-org/gl-performance)
@@ -52,3 +56,15 @@ Consecutive merge requests will have something to compare to and the Performance
report will be shown properly.
![Performance Widget](img/browser_performance_testing.png)
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/cherry_pick_changes.md b/doc/user/project/merge_requests/cherry_pick_changes.md
index 06b3779668b..a5c191c150f 100644
--- a/doc/user/project/merge_requests/cherry_pick_changes.md
+++ b/doc/user/project/merge_requests/cherry_pick_changes.md
@@ -1,8 +1,11 @@
-# Cherry-pick changes
+---
+type: reference, concepts
+---
-> [Introduced][ce-3514] in GitLab 8.7.
+# Cherry-pick changes
-GitLab implements Git's powerful feature to [cherry-pick any commit][git-cherry-pick]
+GitLab implements Git's powerful feature to
+[cherry-pick any commit](https://git-scm.com/docs/git-cherry-pick "Git cherry-pick documentation")
with introducing a **Cherry-pick** button in merge requests and commit details.
## Cherry-picking a merge request
@@ -18,9 +21,9 @@ where you can choose to either:
- Cherry-pick the changes directly into the selected branch.
- Create a new merge request with the cherry-picked changes.
-## Cherry-picking a Commit
+## Cherry-picking a commit
-You can cherry-pick a Commit from the Commit details page:
+You can cherry-pick a commit from the commit details page:
![Cherry-pick commit](img/cherry_pick_changes_commit.png)
@@ -39,5 +42,14 @@ mainline:
git cherry-pick -m 2 7a39eb0
```
-[ce-3514]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3514 "Cherry-pick button Merge Request"
-[git-cherry-pick]: https://git-scm.com/docs/git-cherry-pick "Git cherry-pick documentation"
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md
index 705ff333579..c3c2bdd94b3 100644
--- a/doc/user/project/merge_requests/code_quality.md
+++ b/doc/user/project/merge_requests/code_quality.md
@@ -1,14 +1,16 @@
+---
+type: reference, howto
+---
+
# Code Quality **[STARTER]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1984)
in [GitLab Starter](https://about.gitlab.com/pricing/) 9.3.
-## Overview
-
-If you are using [GitLab CI/CD](../../../ci/README.md), you can analyze your
+With the help of [GitLab CI/CD](../../../ci/README.md), you can analyze your
source code quality using GitLab Code Quality.
Code Quality uses [Code Climate Engines](https://codeclimate.com), which are
-free and open source. Code Quality doesn’t require a Code Climate subscription.
+free and open source. Code Quality doesn't require a Code Climate subscription.
Going a step further, GitLab can show the Code Quality report right
in the merge request widget area:
@@ -69,14 +71,24 @@ example on [analyzing a project's code quality](../../../ci/examples/code_qualit
GitLab then checks this report, compares the metrics between the source and target
branches, and shows the information right on the merge request.
-CAUTION: **Caution:**
If multiple jobs in a pipeline generate a code quality artifact, only the artifact from
the last created job (the job with the largest job ID) is used. To avoid confusion,
configure only one job to generate a code quality artifact.
-NOTE: **Note:**
If the Code Quality report doesn't have anything to compare to, no information
will be displayed in the merge request area. That is the case when you add the
Code Quality job in your `.gitlab-ci.yml` for the very first time.
Consecutive merge requests will have something to compare to and the Code Quality
report will be shown properly.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/fast_forward_merge.md b/doc/user/project/merge_requests/fast_forward_merge.md
index 3cd91a185e3..4f3c68090e4 100644
--- a/doc/user/project/merge_requests/fast_forward_merge.md
+++ b/doc/user/project/merge_requests/fast_forward_merge.md
@@ -1,21 +1,24 @@
+---
+type: reference, concepts
+---
+
# Fast-forward merge requests
-Retain a linear Git history and a way to accept merge requests without
-creating merge commits.
+Sometimes, a workflow policy might mandate a clean commit history without
+merge commits. In such cases, the fast-forward merge is the perfect candidate.
+
+With fast-forward merge requests, you can retain a linear Git history and a way
+to accept merge requests without creating merge commits.
## Overview
-When the fast-forward merge ([`--ff-only`][ffonly]) setting is enabled, no merge
-commits will be created and all merges are fast-forwarded, which means that
-merging is only allowed if the branch could be fast-forwarded.
+When the fast-forward merge
+([`--ff-only`](https://git-scm.com/docs/git-merge#git-merge---ff-only)) setting
+is enabled, no merge commits will be created and all merges are fast-forwarded,
+which means that merging is only allowed if the branch could be fast-forwarded.
When a fast-forward merge is not possible, the user is given the option to rebase.
-## Use cases
-
-Sometimes, a workflow policy might mandate a clean commit history without
-merge commits. In such cases, the fast-forward merge is the perfect candidate.
-
## Enabling fast-forward merges
1. Navigate to your project's **Settings** and search for the 'Merge method'
@@ -32,4 +35,14 @@ source branch locally before you will be able to do a fast-forward merge.
![Fast forward merge rebase locally](img/ff_merge_rebase_locally.png)
-[ffonly]: https://git-scm.com/docs/git-merge#git-merge---ff-only
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index c6f4798e0d2..447b338928c 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -1,7 +1,11 @@
+---
+type: index, reference, concepts
+---
+
# Merge requests
-Merge requests allow you to exchange changes you made to source code and
-collaborate with other people on the same project.
+Merge requests allow you to visualize and collaborate on the proposed changes
+to source code that exist as commits on a given Git branch.
![Merge request view](img/merge_request.png)
@@ -511,7 +515,7 @@ seconds and the status will update automatically.
Merge Request pipeline statuses can't be retrieved when the following occurs:
-1. A Merge Requst is created
+1. A Merge Request is created
1. The Merge Request is closed
1. Changes are made in the project
1. The Merge Request is reopened
diff --git a/doc/user/project/merge_requests/merge_request_approvals.md b/doc/user/project/merge_requests/merge_request_approvals.md
index 2e9db949890..fd151a6df45 100644
--- a/doc/user/project/merge_requests/merge_request_approvals.md
+++ b/doc/user/project/merge_requests/merge_request_approvals.md
@@ -1,7 +1,23 @@
+---
+type: reference, concepts
+---
+
# Merge request approvals **[STARTER]**
> Introduced in [GitLab Enterprise Edition 7.12](https://about.gitlab.com/2015/06/22/gitlab-7-12-released/#merge-request-approvers-ee-only).
+Merge request approvals enable enforced code review by requiring specified people
+to approve a merge request before it can be unblocked for merging.
+
+## Use cases
+
+1. Enforcing review of all code that gets merged into a repository.
+2. Specifying code maintainers for an entire repository.
+3. Specifying reviewers for a given proposed code change.
+4. Specifying categories of reviewers, such as BE, FE, QA, DB, etc., for all proposed code changes.
+
+## Enabling the new approvals interface
+
NOTE: **Note:**
Prior to 12.0, if you are running a self-managed instance, the new interface shown on
this page will not be available unless the feature flag
@@ -21,20 +37,6 @@ sudo -u git -H bin/rails console RAILS_ENV=production
Then run `Feature.enable(:approval_rules)` to enable the feature flag.
-The documentation for the older interface can be accessed
-[here](/11.7/ee/user/project/merge_requests/merge_request_approvals.html).
-
-## Overview
-
-Merge request approvals enable enforced code review by requiring specified people to approve a merge request before it can be unblocked for merging.
-
-## Use cases
-
-1. Enforcing review of all code that gets merged into a repository.
-2. Specifying code maintainers for an entire repository.
-3. Specifying reviewers for a given proposed code change.
-4. Specifying categories of reviewers, such as BE, FE, QA, DB, etc., for all proposed code changes.
-
## Editing approvals
To edit the merge request approvals:
@@ -328,3 +330,15 @@ To filter merge requests by an individual approver, you can type (or select from
the dropdown) `approver` and select the user.
![Filter MRs by an approver](img/filter_approver_merge_requests.png)
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
index 1477e35dca8..c93c7a5fe08 100644
--- a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
+++ b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
@@ -1,3 +1,7 @@
+---
+type: reference, concepts
+---
+
# Merge when pipeline succeeds
When reviewing a merge request that looks ready to merge but still has one or
@@ -7,6 +11,8 @@ finish and remember to merge the request manually.
![Enable](img/merge_when_pipeline_succeeds_enable.png)
+## How it works
+
When you hit the "Merge When Pipeline Succeeds" button, the status of the merge
request will be updated to represent the impending merge. If you cannot wait
for the pipeline to succeed and want to merge immediately, this option is
@@ -29,9 +35,6 @@ changes to be reviewed.
## Only allow merge requests to be merged if the pipeline succeeds
-> **Note:**
-You need to have jobs configured to enable this feature.
-
You can prevent merge requests from being merged if their pipeline did not succeed
or if there are discussions to be resolved.
@@ -39,9 +42,21 @@ Navigate to your project's settings page and expand the **Merge requests** secti
In the **Merge checks** subsection, select the **Pipelines must succeed** check
box and hit **Save** for the changes to take effect.
-![Pipelines must succeed settings](img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png)
+![Pipelines must succeed settings](img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png)
From now on, every time the pipeline fails you will not be able to merge the
merge request from the UI, until you make all relevant jobs pass.
![Only allow merge if pipeline succeeds message](img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png)
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/resolve_conflicts.md b/doc/user/project/merge_requests/resolve_conflicts.md
index ccef2853e3f..6a0f2d18629 100644
--- a/doc/user/project/merge_requests/resolve_conflicts.md
+++ b/doc/user/project/merge_requests/resolve_conflicts.md
@@ -1,3 +1,7 @@
+---
+type: reference, concepts
+---
+
# Merge request conflict resolution
Merge conflicts occur when two branches have different changes that cannot be
@@ -66,3 +70,15 @@ Additionally, GitLab does not detect conflicts in renames away from a path. For
example, this will not create a conflict: on branch `a`, doing `git mv file1
file2`; on branch `b`, doing `git mv file1 file3`. Instead, both files will be
present in the branch after the merge request is merged.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/revert_changes.md b/doc/user/project/merge_requests/revert_changes.md
index b9102798a49..1cbbcf45400 100644
--- a/doc/user/project/merge_requests/revert_changes.md
+++ b/doc/user/project/merge_requests/revert_changes.md
@@ -1,11 +1,13 @@
-# Reverting changes
+---
+type: reference, concepts
+---
-> [Introduced][ce-1990] in GitLab 8.5.
+# Reverting changes
-GitLab implements Git's powerful feature to [revert any commit][git-revert]
-with introducing a **Revert** button in merge requests and commit details.
+You can use Git's powerful feature to [revert any commit](https://git-scm.com/docs/git-revert "Git revert documentation")
+by clicking the **Revert** button in merge requests and commit details.
-## Reverting a Merge Request
+## Reverting a merge request
NOTE: **Note:**
The **Revert** button will only be available for merge requests
@@ -30,9 +32,9 @@ create a new merge request with the revert changes.
After the merge request has been reverted, the **Revert** button will not be
available anymore.
-## Reverting a Commit
+## Reverting a commit
-You can revert a Commit from the Commit details page:
+You can revert a commit from the commit details page:
![Revert commit](img/cherry_pick_changes_commit.png)
@@ -54,5 +56,14 @@ mainline:
git revert -m 2 7a39eb0
```
-[ce-1990]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1990 "Revert button Merge Request"
-[git-revert]: https://git-scm.com/docs/git-revert "Git revert documentation"
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/squash_and_merge.md b/doc/user/project/merge_requests/squash_and_merge.md
index 4ff8ec3a7e6..39fd2588811 100644
--- a/doc/user/project/merge_requests/squash_and_merge.md
+++ b/doc/user/project/merge_requests/squash_and_merge.md
@@ -1,8 +1,14 @@
+---
+type: reference, concepts
+---
+
# Squash and merge
-> [Introduced][ee-1024] in [GitLab Starter][ee] 8.17, and in [GitLab Core][ce] [11.0][ce-18956].
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1024) in [GitLab Starter](https://about.gitlab.com/pricing/) 8.17.
+> - [Ported](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18956) to GitLab Core 11.0.
-Combine all commits of your merge request into one and retain a clean history.
+With squash and merge you can combine all your merge request's commits into one
+and retain a clean history.
## Overview
@@ -12,11 +18,11 @@ and then merges that commit using the merge method set for the project.
In other words, squashing a merge request turns a long list of commits:
-![List of commits from a merge request][mr-commits]
+![List of commits from a merge request](img/squash_mr_commits.png)
Into a single commit on merge:
-![A squashed commit followed by a merge commit][squashed-commit]
+![A squashed commit followed by a merge commit](img/squash_squashed_commit.png)
The squashed commit's commit message will be either:
@@ -44,20 +50,18 @@ all you have to do is enable squashing before you press merge to join
the commits in the merge request into a single commit.
This way, the history of your base branch remains clean with
-meaningful commit messages and is simpler to [revert] if necessary.
+meaningful commit messages and is simpler to [revert](revert_changes.md) if necessary.
## Enabling squash for a merge request
Anyone who can create or edit a merge request can choose for it to be squashed
on the merge request form:
-![Squash commits checkbox on edit form][squash-edit-form]
-
----
+![Squash commits checkbox on edit form](img/squash_edit_form.png)
This can then be overridden at the time of accepting the merge request:
-![Squash commits checkbox on accept merge request form][squash-mr-widget]
+![Squash commits checkbox on accept merge request form](img/squash_mr_widget.png)
## Commit metadata for squashed commits
@@ -69,19 +73,20 @@ The squashed commit has the following metadata:
## Squash and fast-forward merge
-When a project has the [fast-forward merge setting enabled][ff-merge], the merge
+When a project has the [fast-forward merge setting enabled](fast_forward_merge.md#enabling-fast-forward-merges), the merge
request must be able to be fast-forwarded without squashing in order to squash
it. This is because squashing is only available when accepting a merge request,
so a merge request may need to be rebased before squashing, even though
squashing can itself be considered equivalent to rebasing.
-[ee-1024]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1024
-[ce-18956]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18956
-[mr-commits]: img/squash_mr_commits.png
-[squashed-commit]: img/squash_squashed_commit.png
-[squash-edit-form]: img/squash_edit_form.png
-[squash-mr-widget]: img/squash_mr_widget.png
-[ff-merge]: fast_forward_merge.md#enabling-fast-forward-merges
-[ce]: https://about.gitlab.com/pricing/
-[ee]: https://about.gitlab.com/pricing/
-[revert]: revert_changes.md
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/versions.md b/doc/user/project/merge_requests/versions.md
index 70bd1e60594..fbe216c3aed 100644
--- a/doc/user/project/merge_requests/versions.md
+++ b/doc/user/project/merge_requests/versions.md
@@ -1,13 +1,8 @@
-# Merge requests versions
+---
+type: reference, concepts
+---
-> **Notes:**
->
-> - [Introduced][ce-5467] in GitLab 8.12.
-> - Comments are disabled while viewing outdated merge versions or comparing to
-> versions other than base.
-> - Merge request versions are based on push not on commit. So, if you pushed 5
-> commits in a single push, it will be a single option in the dropdown. If you
-> pushed 5 times, that will count for 5 options.
+# Merge requests versions
Every time you push to a branch that is tied to a merge request, a new version
of merge request diff is created. When you visit a merge request that contains
@@ -16,25 +11,38 @@ request diffs.
![Merge request versions](img/versions.png)
----
+## Selecting a version
By default, the latest version of changes is shown. However, you
can select an older one from version dropdown.
![Merge request versions dropdown](img/versions_dropdown.png)
----
+Merge request versions are based on push not on commit. So, if you pushed 5
+commits in a single push, it will be a single option in the dropdown. If you
+pushed 5 times, that will count for 5 options.
You can also compare the merge request version with an older one to see what has
changed since then.
![Merge request versions compare](img/versions_compare.png)
----
+Comments are disabled while viewing outdated merge versions or comparing to
+versions other than base.
Every time you push new changes to the branch, a link to compare the last
changes appears as a system note.
![Merge request versions system note](img/versions_system_note.png)
-[ce-5467]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5467
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/merge_requests/work_in_progress_merge_requests.md b/doc/user/project/merge_requests/work_in_progress_merge_requests.md
index 6f33eb9a482..9cd868067bc 100644
--- a/doc/user/project/merge_requests/work_in_progress_merge_requests.md
+++ b/doc/user/project/merge_requests/work_in_progress_merge_requests.md
@@ -1,4 +1,8 @@
-# "Work In Progress" Merge Requests
+---
+type: reference, concepts
+---
+
+# "Work In Progress" merge requests
If a merge request is not yet ready to be merged, perhaps due to continued development
or open discussions, you can prevent it from being accepted before it's ready by flagging
@@ -7,7 +11,7 @@ being merged, and it will stay disabled until the "WIP" flag has been removed.
![Blocked Accept Button](img/wip_blocked_accept_button.png)
-## Adding the "Work In Progress" flag to a Merge Request
+## Adding the "Work In Progress" flag to a merge request
There are several ways to flag a merge request as a Work In Progress:
@@ -21,7 +25,7 @@ There are several ways to flag a merge request as a Work In Progress:
source branch. This is not a toggle, and doing it again in another commit will have
no effect.
-## Removing the "Work In Progress" flag from a Merge Request
+## Removing the "Work In Progress" flag from a merge request
Similar to above, when a Merge Request is ready to be merged, you can remove the
"Work in Progress" flag in several ways:
@@ -37,10 +41,22 @@ Similar to above, when a Merge Request is ready to be merged, you can remove the
Must have at least Developer level permissions on the project for the button to
be visible.
-## Including/Excluding WIP Merge Requests when searching
+## Including/excluding WIP merge requests when searching
When viewing/searching the merge requests list, you can choose to include or exclude
WIP merge requests by adding a "WIP" filter in the search box, and choosing "Yes"
(to include) or "No" (to exclude).
![Filter WIP MRs](img/filter_wip_merge_requests.png)
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index d36312c9b8d..c07c4099f22 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -28,10 +28,10 @@ The reasons to do it like that are:
and maximizing security.
With the new behavior, any job that is triggered by the user, is also marked
-with their permissions. When a user does a `git push` or changes files through
+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
-pipeline will have the permissions of the pusher.
+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
@@ -67,9 +67,10 @@ Let's consider the following scenario:
## Job token
-A unique job token is generated for each job and it allows the user to
+A unique job token is generated for each job and provides the user read
access all projects that would be normally accessible to the user creating that
-job.
+job. The unique job token does not have any write permissions, but there
+is a [proposal to add support](https://gitlab.com/gitlab-org/gitlab-ce/issues/18106).
We try to make sure that this token doesn't leak by:
diff --git a/doc/user/project/operations/img/external_dashboard_link.png b/doc/user/project/operations/img/external_dashboard_link.png
new file mode 100644
index 00000000000..4fb8bce7cd0
--- /dev/null
+++ b/doc/user/project/operations/img/external_dashboard_link.png
Binary files differ
diff --git a/doc/user/project/operations/img/external_dashboard_settings.png b/doc/user/project/operations/img/external_dashboard_settings.png
new file mode 100644
index 00000000000..8dc380f01e2
--- /dev/null
+++ b/doc/user/project/operations/img/external_dashboard_settings.png
Binary files differ
diff --git a/doc/user/project/operations/index.md b/doc/user/project/operations/index.md
index 0086c15c98a..84711d1146f 100644
--- a/doc/user/project/operations/index.md
+++ b/doc/user/project/operations/index.md
@@ -9,3 +9,4 @@ your applications:
- Discover and view errors generated by your applications with [Error Tracking](error_tracking.md).
- Create, toggle, and remove [Feature Flags](feature_flags.md). **[PREMIUM]**
- [Trace](tracing.md) the performance and health of a deployed application. **[ULTIMATE]**
+- Add a [button to the Monitoring dashboard](linking_to_an_external_dashboard.md) linking directly to your existing external dashboards.
diff --git a/doc/user/project/operations/linking_to_an_external_dashboard.md b/doc/user/project/operations/linking_to_an_external_dashboard.md
new file mode 100644
index 00000000000..0371a40a217
--- /dev/null
+++ b/doc/user/project/operations/linking_to_an_external_dashboard.md
@@ -0,0 +1,19 @@
+# Linking to an external dashboard
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/57171) in GitLab 12.0.
+
+You can add a button to the Monitoring dashboard linking directly to your existing external dashboards.
+
+## Enabling the external dashboard link
+
+1. Go to **Settings > Operations** and scroll to the section titled **External dashboard**.
+
+1. Fill in the URL to your external dashboard and click **Save changes**.
+
+ ![External Dashboard Settings](img/external_dashboard_settings.png)
+
+1. There should now be a button on your
+ [Monitoring dashboard](../../../ci/environments.md#monitoring-environments) which
+ will open the URL you entered in the above step.
+
+ ![External Dashboard Link](img/external_dashboard_link.png)
diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md
index 8b762307ac4..16f48c462eb 100644
--- a/doc/user/project/pipelines/settings.md
+++ b/doc/user/project/pipelines/settings.md
@@ -20,6 +20,22 @@ There are two options. Using:
The default Git strategy can be overridden by the [GIT_STRATEGY variable](../../../ci/yaml/README.md#git-strategy)
in `.gitlab-ci.yml`.
+## Git shallow clone
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/28919) in GitLab 12.0.
+
+NOTE: **Note**: As of GitLab 12.0, newly created projects will automaticallyl have a default
+`git depth` value of `50`.
+
+It is possible to limit the number of changes that GitLab CI/CD will fetch when cloning
+a repository. Setting a limit to `git depth` can speed up Pipelines execution. Maximum
+allowed value is `1000`.
+
+To disable shallow clone and make GitLab CI/CD fetch all branches and tags each time,
+keep the value empty or set to `0`.
+
+This value can also be [overridden by `GIT_DEPTH`](../../../ci/large_repositories/index.md#shallow-cloning) variable in `.gitlab-ci.yml` file.
+
## Timeout
Timeout defines the maximum amount of time in minutes that a job is able run.
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index e3502a632d9..ba890c5ac01 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -26,6 +26,12 @@ Set up your project's access, [visibility](../../../public_access/public_access.
![projects sharing permissions](img/sharing_and_permissions_settings.png)
+If Issues are disabled, or you can't access Issues because you're not a project member, then Lables and Milestones
+links will be missing from the sidebar UI.
+
+You can still access them with direct links if you can access Merge Requests. This is deliberate, if you can see
+Issues or Merge Requests, both of which use Labels and Milestones, then you shouldn't be denied access to Labels and Milestones pages.
+
### Issue settings
Add an [issue description template](../description_templates.md#description-templates) to your project, so that every new issue will start with a custom template.
diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md
index 7d0abb93262..3e24557591c 100644
--- a/doc/workflow/gitlab_flow.md
+++ b/doc/workflow/gitlab_flow.md
@@ -27,6 +27,8 @@ People have a hard time figuring out which branch has the latest code, or which
Frequently, the reaction to this problem is to adopt a standardized pattern such as [Git flow](https://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html).
We think there is still room for improvement. In this document, we describe a set of practices we call GitLab flow.
+For a video introduction of how this works in GitLab, see [GitLab Flow](https://youtu.be/InKNIvky2KE).
+
## Git flow and its problems
![Git Flow timeline by Vincent Driessen, used with permission](gitdashflow.png)
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index b1b6e7bd7b9..8840accf675 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -265,6 +265,7 @@ module API
expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
+ expose :ci_default_git_depth
expose :public_builds, as: :public_jobs
expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
expose :shared_with_groups do |project, options|
@@ -287,6 +288,7 @@ module API
# N+1 is solved then by using `subject.tags.map(&:name)`
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555
super(projects_relation).preload(:group)
+ .preload(:ci_cd_settings)
.preload(project_group_links: { group: :route },
fork_network: :root_project,
fork_network_member: :forked_from_project,
@@ -576,6 +578,8 @@ module API
expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue|
issue
end
+
+ expose :task_completion_status
end
class Issue < IssueBasic
@@ -699,7 +703,7 @@ module API
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/42344 for more
# information.
expose :merge_status do |merge_request|
- merge_request.check_mergeability
+ merge_request.check_if_can_be_merged
merge_request.merge_status
end
expose :diff_head_sha, as: :sha
@@ -724,6 +728,8 @@ module API
end
expose :squash
+
+ expose :task_completion_status
end
class MergeRequest < MergeRequestBasic
diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb
index 94b58a64d26..2a9b17ad22a 100644
--- a/lib/api/helpers/pagination.rb
+++ b/lib/api/helpers/pagination.rb
@@ -23,7 +23,7 @@ module API
def base_request_uri
@base_request_uri ||= URI.parse(request.url).tap do |uri|
uri.host = Gitlab.config.gitlab.host
- uri.port = nil
+ uri.port = Gitlab.config.gitlab.port
end
end
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 813e46e9520..f242f1fea0e 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -29,6 +29,7 @@ module API
optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests'
optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project'
+ optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning'
end
params :optional_project_params_ee do
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 56960a2eb64..039ebf92187 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -252,14 +252,9 @@ module API
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue
- # Setting updated_at only allowed for admins and owners as well
- if params[:updated_at].present?
- if current_user.admin? || user_project.owner == current_user || current_user.owned_groups.include?(user_project.owner)
- issue.system_note_timestamp = params[:updated_at]
- else
- params.delete(:updated_at)
- end
- end
+ # Setting updated_at is allowed only for admins and owners
+ params.delete(:updated_at) unless current_user.can?(:set_issue_updated_at, user_project)
+ issue.system_note_timestamp = params[:updated_at]
update_params = declared_params(include_missing: false).merge(request: request, api: true)
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 5bbf6df78b0..955624404f1 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -397,16 +397,28 @@ module API
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
- desc 'Returns the up to date merge-ref HEAD commit'
- get ':id/merge_requests/:merge_request_iid/merge_ref' do
+ desc 'Merge a merge request to its default temporary merge ref path'
+ params do
+ optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
+ end
+ put ':id/merge_requests/:merge_request_iid/merge_to_ref' do
merge_request = find_project_merge_request(params[:merge_request_iid])
- result = ::MergeRequests::MergeabilityCheckService.new(merge_request).execute
+ authorize! :admin_merge_request, user_project
+
+ merge_params = {
+ commit_message: params[:merge_commit_message]
+ }
+
+ result = ::MergeRequests::MergeToRefService
+ .new(merge_request.target_project, current_user, merge_params)
+ .execute(merge_request)
- if result.success?
- present :commit_id, result.payload.dig(:merge_ref_head, :commit_id)
+ if result[:status] == :success
+ present result.slice(:commit_id), 200
else
- render_api_error!(result.message, 400)
+ http_status = result[:http_status] || 400
+ render_api_error!(result[:message], http_status)
end
end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 30cafd11834..d67f461be57 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -25,7 +25,6 @@ module Banzai
Filter::VideoLinkFilter,
Filter::ImageLazyLoadFilter,
Filter::ImageLinkFilter,
- Filter::EmojiFilter,
Filter::TableOfContentsFilter,
Filter::AutolinkFilter,
Filter::ExternalLinkFilter,
@@ -34,6 +33,7 @@ module Banzai
*reference_filters,
+ Filter::EmojiFilter,
Filter::TaskListFilter,
Filter::InlineDiffFilter,
diff --git a/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb b/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb
new file mode 100644
index 00000000000..0e93b2cb2fa
--- /dev/null
+++ b/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # save validity time pages domain
+ class FillValidTimeForPagesDomainCertificate
+ # define PagesDomain with only needed code
+ class PagesDomain < ActiveRecord::Base
+ self.table_name = 'pages_domains'
+
+ def x509
+ return unless certificate.present?
+
+ @x509 ||= OpenSSL::X509::Certificate.new(certificate)
+ rescue OpenSSL::X509::CertificateError
+ nil
+ end
+ end
+
+ def perform(start_id, stop_id)
+ PagesDomain.where(id: start_id..stop_id).find_each do |domain|
+ if Gitlab::Database.mysql?
+ domain.update_columns(
+ certificate_valid_not_before: domain.x509&.not_before,
+ certificate_valid_not_after: domain.x509&.not_after
+ )
+ else
+ # for some reason activerecord doesn't append timezone, iso8601 forces this
+ domain.update_columns(
+ certificate_valid_not_before: domain.x509&.not_before&.iso8601,
+ certificate_valid_not_after: domain.x509&.not_after&.iso8601
+ )
+ end
+ rescue => e
+ Rails.logger.error "Failed to update pages domain certificate valid time. id: #{domain.id}, message: #{e.message}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb
index ecfab627226..d7f47c0e7e6 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb
@@ -36,7 +36,7 @@ module Gitlab
private
def ci_variables_complex_expressions?
- Feature.enabled?(:ci_variables_complex_expressions, default_enabled: true)
+ Feature.enabled?(:ci_variables_complex_expressions)
end
end
end
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb
index e4cf360a1c1..eb32dffc454 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb
@@ -34,7 +34,7 @@ module Gitlab
end
def self.eager_matching_with_escape_characters?
- Feature.enabled?(:ci_variables_complex_expressions, default_enabled: true)
+ Feature.enabled?(:ci_variables_complex_expressions)
end
end
end
diff --git a/lib/gitlab/ci/pipeline/expression/lexer.rb b/lib/gitlab/ci/pipeline/expression/lexer.rb
index 22c210ae26b..23aa1ca4a36 100644
--- a/lib/gitlab/ci/pipeline/expression/lexer.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexer.rb
@@ -73,7 +73,7 @@ module Gitlab
end
def available_lexemes
- Feature.enabled?(:ci_variables_complex_expressions, default_enabled: true) ? NEW_LEXEMES : LEXEMES
+ Feature.enabled?(:ci_variables_complex_expressions) ? NEW_LEXEMES : LEXEMES
end
end
end
diff --git a/lib/gitlab/ci/pipeline/expression/parser.rb b/lib/gitlab/ci/pipeline/expression/parser.rb
index 589bf32a4d7..0679ed329e5 100644
--- a/lib/gitlab/ci/pipeline/expression/parser.rb
+++ b/lib/gitlab/ci/pipeline/expression/parser.rb
@@ -13,7 +13,7 @@ module Gitlab
end
def tree
- if Feature.enabled?(:ci_variables_complex_expressions, default_enabled: true)
+ if Feature.enabled?(:ci_variables_complex_expressions)
rpn_parse_tree
else
reverse_descent_parse_tree
diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
index abf16e5b2e7..8713b833011 100644
--- a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
@@ -31,19 +31,29 @@ sast:
- |
docker run \
$(propagate_env_vars \
+ SAST_BANDIT_EXCLUDED_PATHS \
SAST_ANALYZER_IMAGES \
SAST_ANALYZER_IMAGE_PREFIX \
SAST_ANALYZER_IMAGE_TAG \
SAST_DEFAULT_ANALYZERS \
- SAST_EXCLUDED_PATHS \
- SAST_BANDIT_EXCLUDED_PATHS \
+ SAST_PULL_ANALYZER_IMAGES \
SAST_BRAKEMAN_LEVEL \
- SAST_GOSEC_LEVEL \
SAST_FLAWFINDER_LEVEL \
SAST_GITLEAKS_ENTROPY_LEVEL \
+ SAST_GOSEC_LEVEL \
+ SAST_EXCLUDED_PATHS \
SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
SAST_PULL_ANALYZER_IMAGE_TIMEOUT \
SAST_RUN_ANALYZER_TIMEOUT \
+ ANT_HOME \
+ ANT_PATH \
+ GRADLE_PATH \
+ JAVA_OPTS \
+ JAVA_PATH \
+ MAVEN_CLI_OPTS \
+ MAVEN_PATH \
+ MAVEN_REPO_PATH \
+ SBT_PATH \
) \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
diff --git a/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml b/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml
index 0fb7c57ab72..a3db2705bf6 100644
--- a/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml
@@ -8,25 +8,13 @@ stages:
- deploy
.serverless:build:image:
- variables:
- DOCKERFILE: "Dockerfile"
stage: build
- image:
- name: gcr.io/kaniko-project/executor:debug
- entrypoint: [""]
- only:
- refs:
- - master
- script:
- - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/$DOCKERFILE --destination $CI_REGISTRY_IMAGE
+ image: registry.gitlab.com/gitlab-org/gitlabktl:latest
+ script: /usr/bin/gitlabktl app build
.serverless:deploy:image:
stage: deploy
- image: gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5
- only:
- refs:
- - master
+ image: gcr.io/triggermesh/tm@sha256:3cfdd470a66b741004fb02354319d79f1598c70117ce79978d2e07e192bfb336 # v0.0.11
environment: development
script:
- echo "$CI_REGISTRY_IMAGE"
diff --git a/lib/gitlab/cluster/puma_worker_killer_initializer.rb b/lib/gitlab/cluster/puma_worker_killer_initializer.rb
index 4ed9a9a02ab..4affc52b7b0 100644
--- a/lib/gitlab/cluster/puma_worker_killer_initializer.rb
+++ b/lib/gitlab/cluster/puma_worker_killer_initializer.rb
@@ -27,6 +27,9 @@ module Gitlab
# is restarted already, thus periodically restarting workers shouldn't be
# needed.
config.rolling_restart_frequency = false
+
+ observer = Gitlab::Cluster::PumaWorkerKillerObserver.new
+ config.pre_term = observer.callback
end
PumaWorkerKiller.start
diff --git a/lib/gitlab/cluster/puma_worker_killer_observer.rb b/lib/gitlab/cluster/puma_worker_killer_observer.rb
new file mode 100644
index 00000000000..3b4ebc3fbae
--- /dev/null
+++ b/lib/gitlab/cluster/puma_worker_killer_observer.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Cluster
+ class PumaWorkerKillerObserver
+ def initialize
+ @counter = Gitlab::Metrics.counter(:puma_killer_terminations_total, 'Number of workers terminated by PumaWorkerKiller')
+ end
+
+ # returns the Proc to be used as the observer callback block
+ def callback
+ method(:log_termination)
+ end
+
+ private
+
+ def log_termination(worker)
+ labels = { worker: "worker_#{worker.index}" }
+
+ @counter.increment(labels)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cluster/rack_timeout_observer.rb b/lib/gitlab/cluster/rack_timeout_observer.rb
new file mode 100644
index 00000000000..5182b2be148
--- /dev/null
+++ b/lib/gitlab/cluster/rack_timeout_observer.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Cluster
+ class RackTimeoutObserver
+ TRANSITION_STATES = %i(ready active).freeze
+
+ def initialize
+ @counter = Gitlab::Metrics.counter(:rack_requests_total, 'Number of requests in a given rack state')
+ end
+
+ # returns the Proc to be used as the observer callback block
+ def callback
+ method(:log_timeout_exception)
+ end
+
+ private
+
+ def log_timeout_exception(env)
+ info = env[::Rack::Timeout::ENV_INFO_KEY]
+ return unless info
+ return if TRANSITION_STATES.include?(info.state)
+
+ @counter.increment(labels(info, env))
+ end
+
+ def labels(info, env)
+ params = controller_params(env) || grape_params(env) || {}
+
+ {
+ controller: params['controller'],
+ action: params['action'],
+ route: params['route'],
+ state: info.state
+ }
+ end
+
+ def controller_params(env)
+ env['action_dispatch.request.parameters']
+ end
+
+ def grape_params(env)
+ endpoint = env[Grape::Env::API_ENDPOINT]
+ route = endpoint&.route&.pattern&.origin
+ return unless route
+
+ { 'route' => route }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb
index 7effb802678..1ecf4a12db7 100644
--- a/lib/gitlab/danger/helper.rb
+++ b/lib/gitlab/danger/helper.rb
@@ -72,7 +72,8 @@ module Gitlab
CATEGORY_LABELS = {
docs: "~Documentation", # Docs are reviewed along DevOps stages, so don't need roulette for now.
none: "",
- qa: "~QA"
+ qa: "~QA",
+ test: "~test for `spec/features/*`"
}.freeze
CATEGORIES = {
%r{\Adoc/} => :none, # To reinstate roulette for documentation, set to `:docs`.
@@ -104,6 +105,7 @@ module Gitlab
%r{\A(ee/)?app/(?!assets|views)[^/]+} => :backend,
%r{\A(ee/)?(bin|config|danger|generator_templates|lib|rubocop|scripts)/} => :backend,
+ %r{\A(ee/)?spec/features/} => :test,
%r{\A(ee/)?spec/(?!javascripts|frontend)[^/]+} => :backend,
%r{\A(ee/)?vendor/(?!assets)[^/]+} => :backend,
%r{\A(ee/)?vendor/(languages\.yml|licenses\.csv)\z} => :backend,
@@ -122,6 +124,10 @@ module Gitlab
%r{\.(md|txt)\z} => :none, # To reinstate roulette for documentation, set to `:docs`.
%r{\.js\z} => :frontend
}.freeze
+
+ def new_teammates(usernames)
+ usernames.map { |u| Gitlab::Danger::Teammate.new('username' => u) }
+ end
end
end
end
diff --git a/lib/gitlab/danger/roulette.rb b/lib/gitlab/danger/roulette.rb
index 062eda38ee4..25de0a87c9d 100644
--- a/lib/gitlab/danger/roulette.rb
+++ b/lib/gitlab/danger/roulette.rb
@@ -45,21 +45,19 @@ module Gitlab
# Known issue: If someone is rejected due to OOO, and then becomes not OOO, the
# selection will change on next spin
def spin_for_person(people, random:)
- person = nil
- people = people.dup
-
- people.size.times do
- person = people.sample(random: random)
-
- break person unless out_of_office?(person)
+ people.shuffle(random: random)
+ .find(&method(:valid_person?))
+ end
- people -= [person]
- end
+ private
- person
+ def valid_person?(person)
+ !mr_author?(person) && !out_of_office?(person)
end
- private
+ def mr_author?(person)
+ person.username == gitlab.mr_author
+ end
def out_of_office?(person)
username = CGI.escape(person.username)
diff --git a/lib/gitlab/danger/teammate.rb b/lib/gitlab/danger/teammate.rb
index c4e66da8ed1..b44f134f2c1 100644
--- a/lib/gitlab/danger/teammate.rb
+++ b/lib/gitlab/danger/teammate.rb
@@ -3,11 +3,12 @@
module Gitlab
module Danger
class Teammate
- attr_reader :name, :username, :projects
+ attr_reader :name, :username, :role, :projects
def initialize(options = {})
@username = options['username']
@name = options['name'] || @username
+ @role = options['role']
@projects = options['projects']
end
@@ -20,20 +21,32 @@ module Gitlab
end
# Traintainers also count as reviewers
- def reviewer?(project, category)
- capabilities(project).include?("reviewer #{category}") || traintainer?(project, category)
+ def reviewer?(project, category, labels)
+ has_capability?(project, category, :reviewer, labels) ||
+ traintainer?(project, category, labels)
end
- def traintainer?(project, category)
- capabilities(project).include?("trainee_maintainer #{category}")
+ def traintainer?(project, category, labels)
+ has_capability?(project, category, :trainee_maintainer, labels)
end
- def maintainer?(project, category)
- capabilities(project).include?("maintainer #{category}")
+ def maintainer?(project, category, labels)
+ has_capability?(project, category, :maintainer, labels)
end
private
+ def has_capability?(project, category, kind, labels)
+ case category
+ when :test
+ area = role[/Test Automation Engineer, (\w+)/, 1]
+
+ area && labels.any?(area) if kind == :reviewer
+ else
+ capabilities(project).include?("#{kind} #{category}")
+ end
+ end
+
def capabilities(project)
Array(projects.fetch(project, []))
end
diff --git a/lib/gitlab/data_builder/note.rb b/lib/gitlab/data_builder/note.rb
index 65601dcdf31..16e62622ed4 100644
--- a/lib/gitlab/data_builder/note.rb
+++ b/lib/gitlab/data_builder/note.rb
@@ -44,6 +44,7 @@ module Gitlab
data[:commit] = build_data_for_commit(project, user, note)
elsif note.for_issue?
data[:issue] = note.noteable.hook_attrs
+ data[:issue][:labels] = note.noteable.labels(&:hook_attrs)
elsif note.for_merge_request?
data[:merge_request] = note.noteable.hook_attrs
elsif note.for_snippet?
diff --git a/lib/gitlab/diff/suggestions_parser.rb b/lib/gitlab/diff/suggestions_parser.rb
index c8c03d5d001..6e17ffaf6ff 100644
--- a/lib/gitlab/diff/suggestions_parser.rb
+++ b/lib/gitlab/diff/suggestions_parser.rb
@@ -10,10 +10,12 @@ module Gitlab
# Returns an array of Gitlab::Diff::Suggestion which represents each
# suggestion in the given text.
#
- def parse(text, position:, project:)
+ def parse(text, position:, project:, supports_suggestion: true)
return [] unless position.complete?
- html = Banzai.render(text, project: nil, no_original_data: true)
+ html = Banzai.render(text, project: nil,
+ no_original_data: true,
+ suggestions_filter_enabled: supports_suggestion)
doc = Nokogiri::HTML(html)
suggestion_nodes = doc.search('pre.suggestion')
diff --git a/lib/gitlab/gitaly_client/conflicts_service.rb b/lib/gitlab/gitaly_client/conflicts_service.rb
index 077b63205a8..d16e45c964d 100644
--- a/lib/gitlab/gitaly_client/conflicts_service.rb
+++ b/lib/gitlab/gitaly_client/conflicts_service.rb
@@ -65,9 +65,9 @@ module Gitlab
our_commit_oid: @our_commit_oid,
target_repository: target_repository.gitaly_repository,
their_commit_oid: @their_commit_oid,
- source_branch: source_branch,
- target_branch: target_branch,
- commit_message: resolution.commit_message,
+ source_branch: encode_binary(source_branch),
+ target_branch: encode_binary(target_branch),
+ commit_message: encode_binary(resolution.commit_message),
user: Gitlab::Git::User.from_gitlab(resolution.user).to_gitaly
)
end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index e4a59ee3f9b..b42e6cbad8d 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -107,7 +107,7 @@ module Gitlab
branch: encode_binary(branch),
target_ref: encode_binary(target_ref),
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
- message: message
+ message: encode_binary(message)
)
response = GitalyClient.call(@repository.storage, :operation_service, :user_merge_to_ref, request)
diff --git a/lib/gitlab/graphql/loaders/batch_lfs_oid_loader.rb b/lib/gitlab/graphql/loaders/batch_lfs_oid_loader.rb
new file mode 100644
index 00000000000..8f34e58a771
--- /dev/null
+++ b/lib/gitlab/graphql/loaders/batch_lfs_oid_loader.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module Loaders
+ class BatchLfsOidLoader
+ def initialize(repository, blob_id)
+ @repository, @blob_id = repository, blob_id
+ end
+
+ def find
+ BatchLoader.for(blob_id).batch(key: repository) do |blob_ids, loader, batch_args|
+ Gitlab::Git::Blob.batch_lfs_pointers(batch_args[:key], blob_ids).each do |loaded_blob|
+ loader.call(loaded_blob.id, loaded_blob.lfs_oid)
+ end
+ end
+ end
+
+ private
+
+ attr_reader :repository, :blob_id
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hook_data/issue_builder.rb b/lib/gitlab/hook_data/issue_builder.rb
index d39ff8c21cc..cfc9ebe4f92 100644
--- a/lib/gitlab/hook_data/issue_builder.rb
+++ b/lib/gitlab/hook_data/issue_builder.rb
@@ -44,7 +44,8 @@ module Gitlab
human_total_time_spent: issue.human_total_time_spent,
human_time_estimate: issue.human_time_estimate,
assignee_ids: issue.assignee_ids,
- assignee_id: issue.assignee_ids.first # This key is deprecated
+ assignee_id: issue.assignee_ids.first, # This key is deprecated
+ labels: issue.labels
}
issue.attributes.with_indifferent_access.slice(*self.class.safe_hook_attributes)
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index 7bbcb53f016..71c44af9254 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -156,6 +156,9 @@ excluded_attributes:
- :when
- :artifacts_file
- :artifacts_metadata
+ - :artifacts_file_store
+ - :artifacts_metadata_store
+ - :artifacts_size
- :commands
push_event_payload:
- :event_id
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index e1e70a008d9..efd3f550a22 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -153,6 +153,9 @@ module Gitlab
@relation_hash.delete('trace') # old export files have trace
@relation_hash.delete('token')
@relation_hash.delete('commands')
+ @relation_hash.delete('artifacts_file_store')
+ @relation_hash.delete('artifacts_metadata_store')
+ @relation_hash.delete('artifacts_size')
imported_object
elsif @relation_name == :merge_requests
diff --git a/lib/gitlab/lets_encrypt/challenge.rb b/lib/gitlab/lets_encrypt/challenge.rb
index 6a7f5e965c5..136268c974b 100644
--- a/lib/gitlab/lets_encrypt/challenge.rb
+++ b/lib/gitlab/lets_encrypt/challenge.rb
@@ -7,7 +7,7 @@ module Gitlab
@acme_challenge = acme_challenge
end
- delegate :url, :token, :file_content, :status, :request_validation, to: :acme_challenge
+ delegate :token, :file_content, :status, :request_validation, to: :acme_challenge
private
diff --git a/lib/gitlab/lets_encrypt/order.rb b/lib/gitlab/lets_encrypt/order.rb
index 5109b5e9843..9c2365f98a8 100644
--- a/lib/gitlab/lets_encrypt/order.rb
+++ b/lib/gitlab/lets_encrypt/order.rb
@@ -13,7 +13,16 @@ module Gitlab
::Gitlab::LetsEncrypt::Challenge.new(challenge)
end
- delegate :url, :status, to: :acme_order
+ def request_certificate(domain:, private_key:)
+ csr = ::Acme::Client::CertificateRequest.new(
+ private_key: OpenSSL::PKey.read(private_key),
+ subject: { common_name: domain }
+ )
+
+ acme_order.finalize(csr: csr)
+ end
+
+ delegate :url, :status, :expires, :certificate, to: :acme_order
private
diff --git a/lib/gitlab/metrics/dashboard/base_service.rb b/lib/gitlab/metrics/dashboard/base_service.rb
index 94aabd0466c..090014aa597 100644
--- a/lib/gitlab/metrics/dashboard/base_service.rb
+++ b/lib/gitlab/metrics/dashboard/base_service.rb
@@ -6,19 +6,20 @@ module Gitlab
module Metrics
module Dashboard
class BaseService < ::BaseService
- DASHBOARD_LAYOUT_ERROR = Gitlab::Metrics::Dashboard::Stages::BaseStage::DashboardLayoutError
+ PROCESSING_ERROR = Gitlab::Metrics::Dashboard::Stages::BaseStage::DashboardProcessingError
+ NOT_FOUND_ERROR = Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
def get_dashboard
- return error("#{dashboard_path} could not be found.", :not_found) unless path_available?
-
success(dashboard: process_dashboard)
- rescue DASHBOARD_LAYOUT_ERROR => e
+ rescue NOT_FOUND_ERROR
+ error("#{dashboard_path} could not be found.", :not_found)
+ rescue PROCESSING_ERROR => e
error(e.message, :unprocessable_entity)
end
# Summary of all known dashboards for the service.
# @return [Array<Hash>] ex) [{ path: String, default: Boolean }]
- def all_dashboard_paths(_project)
+ def self.all_dashboard_paths(_project)
raise NotImplementedError
end
@@ -38,7 +39,7 @@ module Gitlab
# Returns an un-processed dashboard from the cache.
def raw_dashboard
- Rails.cache.fetch(cache_key) { get_raw_dashboard }
+ Gitlab::Metrics::Dashboard::Cache.fetch(cache_key) { get_raw_dashboard }
end
# @return [Hash] an unmodified dashboard
@@ -56,17 +57,6 @@ module Gitlab
def insert_project_metrics?
false
end
-
- # Checks if dashboard path exists or should be rejected
- # as a result of file-changes to the project repository.
- # @return [Boolean]
- def path_available?
- available_paths = Gitlab::Metrics::Dashboard::Finder.find_all_paths(project)
-
- available_paths.any? do |path_params|
- path_params[:path] == dashboard_path
- end
- end
end
end
end
diff --git a/lib/gitlab/metrics/dashboard/cache.rb b/lib/gitlab/metrics/dashboard/cache.rb
new file mode 100644
index 00000000000..a9ccf0fea9b
--- /dev/null
+++ b/lib/gitlab/metrics/dashboard/cache.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'set'
+
+module Gitlab
+ module Metrics
+ module Dashboard
+ class Cache
+ CACHE_KEYS = 'all_cached_metric_dashboards'
+
+ class << self
+ # Stores a dashboard in the cache, documenting the key
+ # so the cached can be cleared in bulk at another time.
+ def fetch(key)
+ register_key(key)
+
+ Rails.cache.fetch(key) { yield }
+ end
+
+ # Resets all dashboard caches, such that all
+ # dashboard content will be loaded from source on
+ # subsequent dashboard calls.
+ def delete_all!
+ all_keys.each { |key| Rails.cache.delete(key) }
+
+ Rails.cache.delete(CACHE_KEYS)
+ end
+
+ private
+
+ def register_key(key)
+ new_keys = all_keys.add(key).to_a.join('|')
+
+ Rails.cache.write(CACHE_KEYS, new_keys)
+ end
+
+ def all_keys
+ Set.new(Rails.cache.read(CACHE_KEYS)&.split('|'))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/metrics/dashboard/finder.rb b/lib/gitlab/metrics/dashboard/finder.rb
index 4a41590f000..49ea5c7d4f2 100644
--- a/lib/gitlab/metrics/dashboard/finder.rb
+++ b/lib/gitlab/metrics/dashboard/finder.rb
@@ -27,6 +27,8 @@ module Gitlab
# Summary of all known dashboards. Used to populate repo cache.
# Prefer #find_all_paths.
def find_all_paths_from_source(project)
+ Gitlab::Metrics::Dashboard::Cache.delete_all!
+
system_service.all_dashboard_paths(project)
.+ project_service.all_dashboard_paths(project)
end
diff --git a/lib/gitlab/metrics/dashboard/processor.rb b/lib/gitlab/metrics/dashboard/processor.rb
index dd986020693..a33a010ad97 100644
--- a/lib/gitlab/metrics/dashboard/processor.rb
+++ b/lib/gitlab/metrics/dashboard/processor.rb
@@ -11,11 +11,13 @@ module Gitlab
SYSTEM_SEQUENCE = [
Stages::CommonMetricsInserter,
Stages::ProjectMetricsInserter,
+ Stages::EndpointInserter,
Stages::Sorter
].freeze
PROJECT_SEQUENCE = [
Stages::CommonMetricsInserter,
+ Stages::EndpointInserter,
Stages::Sorter
].freeze
diff --git a/lib/gitlab/metrics/dashboard/project_dashboard_service.rb b/lib/gitlab/metrics/dashboard/project_dashboard_service.rb
index fdffd067c93..e88658e4f9f 100644
--- a/lib/gitlab/metrics/dashboard/project_dashboard_service.rb
+++ b/lib/gitlab/metrics/dashboard/project_dashboard_service.rb
@@ -13,20 +13,12 @@ module Gitlab
def all_dashboard_paths(project)
file_finder(project)
.list_files_for(DASHBOARD_ROOT)
- .map do |filepath|
- Rails.cache.delete(cache_key(project.id, filepath))
-
- { path: filepath, default: false }
- end
+ .map { |filepath| { path: filepath, default: false } }
end
def file_finder(project)
Gitlab::Template::Finders::RepoTemplateFinder.new(project, DASHBOARD_ROOT, '.yml')
end
-
- def cache_key(id, dashboard_path)
- "project_#{id}_metrics_dashboard_#{dashboard_path}"
- end
end
private
@@ -39,7 +31,7 @@ module Gitlab
end
def cache_key
- self.class.cache_key(project.id, dashboard_path)
+ "project_#{project.id}_metrics_dashboard_#{dashboard_path}"
end
end
end
diff --git a/lib/gitlab/metrics/dashboard/stages/base_stage.rb b/lib/gitlab/metrics/dashboard/stages/base_stage.rb
index a6d1f974556..0db7b176e8d 100644
--- a/lib/gitlab/metrics/dashboard/stages/base_stage.rb
+++ b/lib/gitlab/metrics/dashboard/stages/base_stage.rb
@@ -5,7 +5,8 @@ module Gitlab
module Dashboard
module Stages
class BaseStage
- DashboardLayoutError = Class.new(StandardError)
+ DashboardProcessingError = Class.new(StandardError)
+ LayoutError = Class.new(DashboardProcessingError)
DEFAULT_PANEL_TYPE = 'area-chart'
@@ -25,15 +26,15 @@ module Gitlab
protected
def missing_panel_groups!
- raise DashboardLayoutError.new('Top-level key :panel_groups must be an array')
+ raise LayoutError.new('Top-level key :panel_groups must be an array')
end
def missing_panels!
- raise DashboardLayoutError.new('Each "panel_group" must define an array :panels')
+ raise LayoutError.new('Each "panel_group" must define an array :panels')
end
def missing_metrics!
- raise DashboardLayoutError.new('Each "panel" must define an array :metrics')
+ raise LayoutError.new('Each "panel" must define an array :metrics')
end
def for_metrics
diff --git a/lib/gitlab/metrics/dashboard/stages/endpoint_inserter.rb b/lib/gitlab/metrics/dashboard/stages/endpoint_inserter.rb
new file mode 100644
index 00000000000..2a959854be0
--- /dev/null
+++ b/lib/gitlab/metrics/dashboard/stages/endpoint_inserter.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Metrics
+ module Dashboard
+ module Stages
+ class EndpointInserter < BaseStage
+ MissingQueryError = Class.new(DashboardProcessingError)
+
+ def transform!
+ for_metrics do |metric|
+ metric[:prometheus_endpoint_path] = endpoint_for_metric(metric)
+ end
+ end
+
+ private
+
+ def endpoint_for_metric(metric)
+ Gitlab::Routing.url_helpers.prometheus_api_project_environment_path(
+ project,
+ environment,
+ proxy_path: query_type(metric),
+ query: query_for_metric(metric)
+ )
+ end
+
+ def query_type(metric)
+ metric[:query] ? :query : :query_range
+ end
+
+ def query_for_metric(metric)
+ query = metric[query_type(metric)]
+
+ raise MissingQueryError.new('Each "metric" must define one of :query or :query_range') unless query
+
+ query
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb
index 4d9c43f37e7..17eacbd21d8 100644
--- a/lib/gitlab/metrics/samplers/ruby_sampler.rb
+++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb
@@ -77,10 +77,10 @@ module Gitlab
end
def worker_label
+ return { worker: 'sidekiq' } if Sidekiq.server?
return {} unless defined?(Unicorn::Worker)
worker_no = ::Prometheus::Client::Support::Unicorn.worker_id
-
if worker_no
{ worker: worker_no }
else
diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb
index 16c16aa0265..3a27e26eaba 100644
--- a/lib/gitlab/performance_bar/peek_query_tracker.rb
+++ b/lib/gitlab/performance_bar/peek_query_tracker.rb
@@ -27,15 +27,16 @@ module Gitlab
subscribe('sql.active_record') do |_, start, finish, _, data|
if Gitlab::SafeRequestStore.store[:peek_enabled]
unless data[:cached]
- track_query(data[:sql].strip, data[:binds], start, finish)
+ backtrace = Gitlab::Profiler.clean_backtrace(caller)
+ track_query(data[:sql].strip, data[:binds], backtrace, start, finish)
end
end
end
end
- def track_query(raw_query, bindings, start, finish)
+ def track_query(raw_query, bindings, backtrace, start, finish)
duration = (finish - start) * 1000.0
- query_info = { duration: duration.round(3), sql: raw_query }
+ query_info = { duration: duration.round(3), sql: raw_query, backtrace: backtrace }
PEEK_DB_CLIENT.query_details << query_info
end
diff --git a/lib/gitlab/rack_timeout_observer.rb b/lib/gitlab/rack_timeout_observer.rb
deleted file mode 100644
index 80d3f7dea60..00000000000
--- a/lib/gitlab/rack_timeout_observer.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- class RackTimeoutObserver
- def initialize
- @counter = Gitlab::Metrics.counter(:rack_state_total, 'Number of requests in a given rack state')
- end
-
- # returns the Proc to be used as the observer callback block
- def callback
- method(:log_timeout_exception)
- end
-
- private
-
- def log_timeout_exception(env)
- info = env[::Rack::Timeout::ENV_INFO_KEY]
- return unless info
-
- @counter.increment(labels(info, env))
- end
-
- def labels(info, env)
- params = controller_params(env) || grape_params(env) || {}
-
- {
- controller: params['controller'],
- action: params['action'],
- route: params['route'],
- state: info.state
- }
- end
-
- def controller_params(env)
- env['action_dispatch.request.parameters']
- end
-
- def grape_params(env)
- endpoint = env[Grape::Env::API_ENDPOINT]
- route = endpoint&.route&.pattern&.origin
- return unless route
-
- { 'route' => route }
- end
- end
-end
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 3daa03d01d6..7c1e6b1baff 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -85,10 +85,6 @@ module Gitlab
UsersFinder.new(current_user, search: query).execute
end
- def display_options(_scope)
- {}
- end
-
private
def projects
diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb
index 61fcb562106..0d3e78c0a66 100644
--- a/lib/gitlab/setup_helper.rb
+++ b/lib/gitlab/setup_helper.rb
@@ -43,7 +43,13 @@ module Gitlab
config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby
config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path }
config[:bin_dir] = Gitlab.config.gitaly.client_path
- config[:git] = { catfile_cache_size: 5 }
+
+ if Rails.env.test?
+ # Compared to production, tests run in constrained environments. This
+ # number is meant to grow with the number of concurrent rails requests /
+ # sidekiq jobs, and concurrency will be low anyway in test.
+ config[:git] = { catfile_cache_size: 5 }
+ end
TomlRB.dump(config)
end
diff --git a/lib/tasks/gettext.rake b/lib/tasks/gettext.rake
index 2235a6ba194..91a52144dd2 100644
--- a/lib/tasks/gettext.rake
+++ b/lib/tasks/gettext.rake
@@ -19,6 +19,7 @@ namespace :gettext do
Rake::Task['gettext:po_to_json'].invoke
end
+ desc 'Regenerate gitlab.pot file'
task :regenerate do
pot_file = 'locale/gitlab.pot'
# Remove all translated files, this speeds up finding
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 6258208234e..88ace6057cf 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -427,7 +427,7 @@ msgstr ""
msgid "A fork is a copy of a project.<br />Forking a repository allows you to make changes without affecting the original project."
msgstr ""
-msgid "A member of GitLab's abuse team will review your report as soon as possible."
+msgid "A member of the abuse team will review your report as soon as possible."
msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
@@ -565,6 +565,9 @@ msgstr ""
msgid "Add comment now"
msgstr ""
+msgid "Add email address"
+msgstr ""
+
msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
msgstr ""
@@ -775,6 +778,9 @@ msgstr ""
msgid "All changes are committed"
msgstr ""
+msgid "All email addresses will be used to identify your commits."
+msgstr ""
+
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
@@ -1485,9 +1491,6 @@ msgstr ""
msgid "Begin with the selected commit"
msgstr ""
-msgid "Behavior"
-msgstr ""
-
msgid "Below are examples of regex for existing tools:"
msgstr ""
@@ -1911,9 +1914,6 @@ msgstr ""
msgid "Choose between <code>clone</code> or <code>fetch</code> to get the recent application code"
msgstr ""
-msgid "Choose between fixed (max. 1280px) and fluid (100%%) application layout."
-msgstr ""
-
msgid "Choose file…"
msgstr ""
@@ -1923,9 +1923,6 @@ msgstr ""
msgid "Choose visibility level, enable/disable project features (issues, repository, wiki, snippets) and set permissions."
msgstr ""
-msgid "Choose what content you want to see on a project’s overview page."
-msgstr ""
-
msgid "Choose your merge method, options, checks, and set up a default merge request description template."
msgstr ""
@@ -2136,7 +2133,7 @@ msgstr ""
msgid "ClusterIntegration|%{title} uninstalled successfully."
msgstr ""
-msgid "ClusterIntegration|%{title} upgraded successfully."
+msgid "ClusterIntegration|%{title} updated successfully."
msgstr ""
msgid "ClusterIntegration|A service token scoped to %{code}kube-system%{end_code} with %{code}cluster-admin%{end_code} privileges."
@@ -2331,15 +2328,6 @@ msgstr ""
msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
msgstr ""
-msgid "ClusterIntegration|Install"
-msgstr ""
-
-msgid "ClusterIntegration|Installed"
-msgstr ""
-
-msgid "ClusterIntegration|Installing"
-msgstr ""
-
msgid "ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}."
msgstr ""
@@ -2427,9 +2415,6 @@ msgstr ""
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage"
-msgstr ""
-
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
@@ -2496,9 +2481,6 @@ msgstr ""
msgid "ClusterIntegration|Request to begin uninstalling failed"
msgstr ""
-msgid "ClusterIntegration|Retry update"
-msgstr ""
-
msgid "ClusterIntegration|Save changes"
msgstr ""
@@ -2598,21 +2580,6 @@ msgstr ""
msgid "ClusterIntegration|Update failed. Please check the logs and try again."
msgstr ""
-msgid "ClusterIntegration|Updating"
-msgstr ""
-
-msgid "ClusterIntegration|Upgrade"
-msgstr ""
-
-msgid "ClusterIntegration|Upgrade failed"
-msgstr ""
-
-msgid "ClusterIntegration|Upgraded"
-msgstr ""
-
-msgid "ClusterIntegration|Upgrading"
-msgstr ""
-
msgid "ClusterIntegration|Validating project billing status"
msgstr ""
@@ -2837,6 +2804,9 @@ msgstr ""
msgid "Confirm"
msgstr ""
+msgid "Confirmation email sent to %{email}"
+msgstr ""
+
msgid "Confirmation required"
msgstr ""
@@ -2945,6 +2915,9 @@ msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
msgstr ""
+msgid "Control emails linked to your account"
+msgstr ""
+
msgid "Control the display of third party offers."
msgstr ""
@@ -3197,9 +3170,6 @@ msgstr ""
msgid "Customize language and region related settings."
msgstr ""
-msgid "Customize the appearance of the application header and navigation sidebar."
-msgstr ""
-
msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr ""
@@ -3287,9 +3257,6 @@ msgstr ""
msgid "Default classification label"
msgstr ""
-msgid "Default dashboard"
-msgstr ""
-
msgid "Default first day of the week"
msgstr ""
@@ -3802,6 +3769,9 @@ msgstr ""
msgid "Enable Sentry for error reporting and logging."
msgstr ""
+msgid "Enable access to the Performance Bar for a given group."
+msgstr ""
+
msgid "Enable and configure InfluxDB metrics."
msgstr ""
@@ -3835,9 +3805,6 @@ msgstr ""
msgid "Enable shared Runners"
msgstr ""
-msgid "Enable the Performance Bar for a given group."
-msgstr ""
-
msgid "Enable two-factor authentication"
msgstr ""
@@ -4698,6 +4665,9 @@ msgstr ""
msgid "Git revision"
msgstr ""
+msgid "Git shallow clone"
+msgstr ""
+
msgid "Git strategy for pipelines"
msgstr ""
@@ -5339,6 +5309,9 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Install"
+msgstr ""
+
msgid "Install GitLab Runner"
msgstr ""
@@ -5348,6 +5321,12 @@ msgstr ""
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
+msgid "Installed"
+msgstr ""
+
+msgid "Installing"
+msgstr ""
+
msgid "Instance Statistics"
msgstr ""
@@ -5390,6 +5369,9 @@ msgstr ""
msgid "Invalid Login or password"
msgstr ""
+msgid "Invalid date"
+msgstr ""
+
msgid "Invalid feature"
msgstr ""
@@ -5791,9 +5773,6 @@ msgstr ""
msgid "Latest pipeline for this branch"
msgstr ""
-msgid "Layout width"
-msgstr ""
-
msgid "Lead"
msgstr ""
@@ -5850,6 +5829,9 @@ msgid_plural "Limited to showing %d events at most"
msgstr[0] ""
msgstr[1] ""
+msgid "Linked emails (%{email_count})"
+msgstr ""
+
msgid "LinkedIn"
msgstr ""
@@ -5940,6 +5922,9 @@ msgstr ""
msgid "Makes this issue confidential"
msgstr ""
+msgid "Manage"
+msgstr ""
+
msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
msgstr ""
@@ -6060,6 +6045,9 @@ msgstr ""
msgid "Members of <strong>%{project_name}</strong>"
msgstr ""
+msgid "Merge"
+msgstr ""
+
msgid "Merge Request"
msgstr ""
@@ -6970,7 +6958,7 @@ msgstr ""
msgid "Paste a machine public key here. Read more about how to generate it %{link_start}here%{link_end}"
msgstr ""
-msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key."
+msgid "Paste your public SSH key, which is usually contained in the file '~/.ssh/id_ed25519.pub' or '~/.ssh/id_rsa.pub' and begins with 'ssh-ed25519' or 'ssh-rsa'. Don't use your private SSH key."
msgstr ""
msgid "Path"
@@ -6994,6 +6982,9 @@ msgstr ""
msgid "People without permission will never get a notification and won't be able to comment."
msgstr ""
+msgid "People without permission will never get a notification."
+msgstr ""
+
msgid "Perform advanced options such as changing path, transferring, or removing the group."
msgstr ""
@@ -7315,7 +7306,7 @@ msgstr ""
msgid "Please try again"
msgstr ""
-msgid "Please use this form to report users to GitLab who create spam issues, comments or behave inappropriately."
+msgid "Please use this form to report to the admin users who create spam issues, comments or behave inappropriately."
msgstr ""
msgid "Please wait a moment, this page will automatically refresh when ready."
@@ -7330,21 +7321,51 @@ msgstr ""
msgid "Preferences saved."
msgstr ""
+msgid "Preferences|Behavior"
+msgstr ""
+
+msgid "Preferences|Choose between fixed (max. 1280px) and fluid (100%%) application layout."
+msgstr ""
+
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgstr ""
+
+msgid "Preferences|Customize the appearance of the application header and navigation sidebar."
+msgstr ""
+
+msgid "Preferences|Default dashboard"
+msgstr ""
+
msgid "Preferences|Display time in 24-hour format"
msgstr ""
msgid "Preferences|For example: 30 mins ago."
msgstr ""
+msgid "Preferences|Layout width"
+msgstr ""
+
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|Project overview content"
+msgstr ""
+
+msgid "Preferences|Syntax highlighting theme"
+msgstr ""
+
msgid "Preferences|These settings will update how dates and times are displayed for you."
msgstr ""
msgid "Preferences|This feature is experimental and translations are not complete yet"
msgstr ""
+msgid "Preferences|This setting allows you to customize the appearance of the syntax."
+msgstr ""
+
+msgid "Preferences|This setting allows you to customize the behavior of the system layout and default views."
+msgstr ""
+
msgid "Preferences|Time display"
msgstr ""
@@ -7471,6 +7492,9 @@ msgstr ""
msgid "Profiles|Click on icon to activate signin with one of the following services"
msgstr ""
+msgid "Profiles|Commit email"
+msgstr ""
+
msgid "Profiles|Connect"
msgstr ""
@@ -7483,6 +7507,9 @@ msgstr ""
msgid "Profiles|Current status"
msgstr ""
+msgid "Profiles|Default notification email"
+msgstr ""
+
msgid "Profiles|Delete Account"
msgstr ""
@@ -7549,6 +7576,9 @@ msgstr ""
msgid "Profiles|No file chosen"
msgstr ""
+msgid "Profiles|Notification email"
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -7558,6 +7588,9 @@ msgstr ""
msgid "Profiles|Position and size your new avatar"
msgstr ""
+msgid "Profiles|Primary email"
+msgstr ""
+
msgid "Profiles|Private contributions"
msgstr ""
@@ -7567,6 +7600,9 @@ msgstr ""
msgid "Profiles|Public Avatar"
msgstr ""
+msgid "Profiles|Public email"
+msgstr ""
+
msgid "Profiles|Remove avatar"
msgstr ""
@@ -7609,7 +7645,7 @@ msgstr ""
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr ""
-msgid "Profiles|Typically starts with \"ssh-rsa …\""
+msgid "Profiles|Typically starts with \"ssh-ed25519 …\" or \"ssh-rsa …\""
msgstr ""
msgid "Profiles|Update profile settings"
@@ -7780,9 +7816,6 @@ msgstr ""
msgid "Project name"
msgstr ""
-msgid "Project overview content"
-msgstr ""
-
msgid "Project slug"
msgstr ""
@@ -8385,7 +8418,7 @@ msgstr ""
msgid "Reply to this email directly or %{view_it_on_gitlab}."
msgstr ""
-msgid "Report abuse to GitLab"
+msgid "Report abuse to admin"
msgstr ""
msgid "Reporting"
@@ -8466,6 +8499,9 @@ msgstr ""
msgid "Require users to prove ownership of custom domains"
msgstr ""
+msgid "Resend confirmation email"
+msgstr ""
+
msgid "Resend invite"
msgstr ""
@@ -8532,6 +8568,9 @@ msgstr ""
msgid "Retry this job in order to create the necessary resources."
msgstr ""
+msgid "Retry update"
+msgstr ""
+
msgid "Retry verification"
msgstr ""
@@ -8867,6 +8906,9 @@ msgstr ""
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
msgstr ""
+msgid "Send confirmation email"
+msgstr ""
+
msgid "Send email"
msgstr ""
@@ -9685,9 +9727,6 @@ msgstr ""
msgid "Switch to the source to copy it to the clipboard"
msgstr ""
-msgid "Syntax highlighting theme"
-msgstr ""
-
msgid "System Hooks"
msgstr ""
@@ -9978,6 +10017,9 @@ msgstr ""
msgid "The name %{entryName} is already taken in this directory."
msgstr ""
+msgid "The number of changes to be fetched from GitLab when cloning a repository. This can speed up Pipelines execution. Keep empty or set to 0 to disable shallow clone by default and make GitLab CI fetch all branches and tags each time."
+msgstr ""
+
msgid "The number of times an upload record could not find its file"
msgstr ""
@@ -10146,6 +10188,9 @@ msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
+msgid "There was a problem sending the confirmation email"
+msgstr ""
+
msgid "There was an error %{message} todo."
msgstr ""
@@ -10188,6 +10233,9 @@ msgstr ""
msgid "Third party offers"
msgstr ""
+msgid "This %{issuableDisplayName} is locked. Only project members can comment."
+msgstr ""
+
msgid "This %{issuable} is locked. Only <strong>project members</strong> can comment."
msgstr ""
@@ -10284,9 +10332,6 @@ msgstr ""
msgid "This issue is confidential"
msgstr ""
-msgid "This issue is confidential and locked."
-msgstr ""
-
msgid "This issue is locked."
msgstr ""
@@ -10398,12 +10443,6 @@ msgstr ""
msgid "This runner will only run on pipelines triggered on protected branches"
msgstr ""
-msgid "This setting allows you to customize the appearance of the syntax."
-msgstr ""
-
-msgid "This setting allows you to customize the behavior of the system layout and default views."
-msgstr ""
-
msgid "This setting can be overridden in each project."
msgstr ""
@@ -11008,6 +11047,9 @@ msgstr ""
msgid "UpdateProject|Project could not be updated!"
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -11871,9 +11913,15 @@ msgstr ""
msgid "YouTube"
msgstr ""
+msgid "Your Commit Email will be used for web based operations, such as edits and merges."
+msgstr ""
+
msgid "Your Conversational Development Index gives an overview of how you are using GitLab from a feature perspective. View how you compare with other organizations, discover features you are not using, and learn best practices through blog posts and white papers."
msgstr ""
+msgid "Your Default Notification Email will be used for account notifications if a %{openingTag}group-specific email address%{closingTag} is not set."
+msgstr ""
+
msgid "Your GPG keys (%{count})"
msgstr ""
@@ -11883,12 +11931,18 @@ msgstr ""
msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
msgstr ""
+msgid "Your Primary Email will be used for avatar detection."
+msgstr ""
+
msgid "Your Projects (default)"
msgstr ""
msgid "Your Projects' Activity"
msgstr ""
+msgid "Your Public Email will be displayed on your public profile."
+msgstr ""
+
msgid "Your SSH keys (%{count})"
msgstr ""
@@ -11937,9 +11991,6 @@ msgstr ""
msgid "Your comment could not be updated! Please check your network connection and try again."
msgstr ""
-msgid "Your comment will not be visible to the public."
-msgstr ""
-
msgid "Your deployment services will be broken, you will need to manually fix the services after renaming."
msgstr ""
@@ -12360,9 +12411,6 @@ msgstr ""
msgid "mrWidget|The source branch will not be deleted"
msgstr ""
-msgid "mrWidget|The target branch has advanced, which invalidates the merge request pipeline. Please update the source branch and retry merging"
-msgstr ""
-
msgid "mrWidget|There are merge conflicts"
msgstr ""
diff --git a/package.json b/package.json
index ab7268642ec..0758c27c75b 100644
--- a/package.json
+++ b/package.json
@@ -147,6 +147,7 @@
"devDependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.2.0",
"@gitlab/eslint-config": "^1.6.0",
+ "@gitlab/eslint-plugin-i18n": "^1.0.0",
"@vue/test-utils": "^1.0.0-beta.25",
"axios-mock-adapter": "^1.15.0",
"babel-jest": "^24.1.0",
diff --git a/qa/qa/ce/strategy.rb b/qa/qa/ce/strategy.rb
index 6c1820ffdc8..018a1eb1bfc 100644
--- a/qa/qa/ce/strategy.rb
+++ b/qa/qa/ce/strategy.rb
@@ -10,18 +10,11 @@ module QA
end
def perform_before_hooks
- retries ||= 0
-
# The login page could take some time to load the first time it is visited.
# We visit the login page and wait for it to properly load only once before the tests.
- QA::Runtime::Browser.visit(:gitlab, QA::Page::Main::Login)
- rescue QA::Page::Validatable::PageValidationError
- if (retries += 1) < 3
- Runtime::Logger.warn("The login page did not appear as expected. Retrying... (attempt ##{retries})")
- retry
+ QA::Support::Retrier.retry_on_exception do
+ QA::Runtime::Browser.visit(:gitlab, QA::Page::Main::Login)
end
-
- raise
end
end
end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index 9df3db1bba0..77bad7481d8 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -29,7 +29,8 @@ module QA
# Adds a comment to an issue
# attachment option should be an absolute path
- def comment(text, attachment: nil)
+ def comment(text, attachment: nil, filter: :all_activities)
+ method("select_#{filter}_filter").call
fill_element :comment_input, text
unless attachment.nil?
diff --git a/qa/qa/page/project/operations/kubernetes/show.rb b/qa/qa/page/project/operations/kubernetes/show.rb
index 4096f57b7db..c81e13e9b91 100644
--- a/qa/qa/page/project/operations/kubernetes/show.rb
+++ b/qa/qa/page/project/operations/kubernetes/show.rb
@@ -8,8 +8,8 @@ module QA
class Show < Page::Base
view 'app/assets/javascripts/clusters/components/application_row.vue' do
element :application_row, 'js-cluster-application-row-${this.id}' # rubocop:disable QA/ElementWithPattern
- element :install_button, "s__('ClusterIntegration|Install')" # rubocop:disable QA/ElementWithPattern
- element :installed_button, "s__('ClusterIntegration|Installed')" # rubocop:disable QA/ElementWithPattern
+ element :install_button, "__('Install')" # rubocop:disable QA/ElementWithPattern
+ element :installed_button, "__('Installed')" # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/clusters/components/applications.vue' do
diff --git a/qa/qa/resource/repository/project_push.rb b/qa/qa/resource/repository/project_push.rb
index cad89ebb0bb..e98880ce195 100644
--- a/qa/qa/resource/repository/project_push.rb
+++ b/qa/qa/resource/repository/project_push.rb
@@ -32,8 +32,8 @@ module QA
def fabricate!
super
- project.visit!
project.wait_for_push @commit_message if @wait_for_push
+ project.visit!
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
index 9b083d59a5e..00094161f61 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -23,7 +23,6 @@ module QA
create_issue
Page::Project::Issue::Show.perform do |show|
- show.select_all_activities_filter
show.comment('See attached banana for scale', attachment: file_to_attach)
show.refresh
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
index c0d597af076..b2164bb5fab 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
@@ -16,9 +16,8 @@ module QA
expect(page).to have_content(issue_title)
Page::Project::Issue::Show.perform do |show_page|
- show_page.select_comments_only_filter
- show_page.comment('/confidential')
- show_page.comment('My own comment')
+ show_page.comment('/confidential', filter: :comments_only)
+ show_page.comment('My own comment', filter: :comments_only)
expect(show_page).not_to have_content("made the issue confidential")
expect(show_page).to have_content("My own comment")
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index 86ba5e819ba..5ca9ddb6b19 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -9,7 +9,8 @@ module QA
Page::Main::Login.perform(&:sign_in_using_credentials)
end
- describe 'Auto DevOps support', :orchestrated, :kubernetes do
+ # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/108
+ describe 'Auto DevOps support', :orchestrated, :kubernetes, :quarantine do
context 'when rbac is enabled' do
before(:all) do
@cluster = Service::KubernetesCluster.new.create!
diff --git a/spec/controllers/acme_challenges_controller_spec.rb b/spec/controllers/acme_challenges_controller_spec.rb
new file mode 100644
index 00000000000..cee06bed27b
--- /dev/null
+++ b/spec/controllers/acme_challenges_controller_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe AcmeChallengesController do
+ describe '#show' do
+ let!(:acme_order) { create(:pages_domain_acme_order) }
+
+ def make_request(domain, token)
+ get(:show, params: { domain: domain, token: token })
+ end
+
+ before do
+ make_request(domain, token)
+ end
+
+ context 'with right domain and token' do
+ let(:domain) { acme_order.pages_domain.domain }
+ let(:token) { acme_order.challenge_token }
+
+ it 'renders acme challenge file content' do
+ expect(response.body).to eq(acme_order.challenge_file_content)
+ end
+ end
+
+ context 'when domain is invalid' do
+ let(:domain) { 'somewrongdomain.com' }
+ let(:token) { acme_order.challenge_token }
+
+ it 'renders not found' do
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when token is invalid' do
+ let(:domain) { acme_order.pages_domain.domain }
+ let(:token) { 'wrongtoken' }
+
+ it 'renders not found' do
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/import/fogbugz_controller_spec.rb b/spec/controllers/import/fogbugz_controller_spec.rb
index f1e0923f316..f7c813576aa 100644
--- a/spec/controllers/import/fogbugz_controller_spec.rb
+++ b/spec/controllers/import/fogbugz_controller_spec.rb
@@ -11,6 +11,44 @@ describe Import::FogbugzController do
sign_in(user)
end
+ describe 'POST #callback' do
+ let(:token) { FFaker::Lorem.characters(8) }
+ let(:uri) { 'https://example.com' }
+ let(:xml_response) { %Q(<?xml version=\"1.0\" encoding=\"UTF-8\"?><response><token><![CDATA[#{token}]]></token></response>) }
+
+ it 'attempts to contact Fogbugz server' do
+ stub_request(:post, "https://example.com/api.asp").to_return(status: 200, body: xml_response, headers: {})
+
+ post :callback, params: { uri: uri, email: 'test@example.com', password: 'mypassword' }
+
+ expect(session[:fogbugz_token]).to eq(token)
+ expect(session[:fogbugz_uri]).to eq(uri)
+ expect(response).to redirect_to(new_user_map_import_fogbugz_path)
+ end
+ end
+
+ describe 'POST #create_user_map' do
+ let(:user_map) do
+ {
+ "2" => {
+ "name" => "Test User",
+ "email" => "testuser@example.com",
+ "gitlab_user" => "3"
+ }
+ }
+ end
+
+ it 'stores the user map in the session' do
+ client = double(user_map: {})
+ expect(controller).to receive(:client).and_return(client)
+
+ post :create_user_map, params: { users: user_map }
+
+ expect(session[:fogbugz_user_map]).to eq(user_map)
+ expect(response).to redirect_to(status_import_fogbugz_path)
+ end
+ end
+
describe 'GET status' do
before do
@repo = OpenStruct.new(name: 'vim')
diff --git a/spec/controllers/projects/environments/prometheus_api_controller_spec.rb b/spec/controllers/projects/environments/prometheus_api_controller_spec.rb
index d232408b775..fdef9bc5638 100644
--- a/spec/controllers/projects/environments/prometheus_api_controller_spec.rb
+++ b/spec/controllers/projects/environments/prometheus_api_controller_spec.rb
@@ -85,12 +85,12 @@ describe Projects::Environments::PrometheusApiController do
context 'with nil result' do
let(:service_result) { nil }
- it 'returns 202 accepted' do
+ it 'returns 204 no_content' do
get :proxy, params: environment_params
expect(json_response['status']).to eq('processing')
expect(json_response['message']).to eq('Not ready yet. Try again later.')
- expect(response).to have_gitlab_http_status(:accepted)
+ expect(response).to have_gitlab_http_status(:no_content)
end
end
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index d5eea5b0439..9699f2952f2 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -433,20 +433,6 @@ describe Projects::EnvironmentsController do
end
context 'when only one time param is provided' do
- context 'when :metrics_time_window feature flag is disabled' do
- before do
- stub_feature_flags(metrics_time_window: false)
- expect(environment).to receive(:additional_metrics).with(no_args).and_return(nil)
- end
-
- it 'returns a time-window agnostic response' do
- additional_metrics(start: '1552647300.651094')
-
- expect(response).to have_gitlab_http_status(204)
- expect(json_response).to eq({})
- end
- end
-
it 'raises an error when start is missing' do
expect { additional_metrics(end: '1552647300.651094') }
.to raise_error(ActionController::ParameterMissing)
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index f8c0ab55eb4..34cbf0c8723 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -412,7 +412,7 @@ describe Projects::MergeRequestsController do
end
end
- context 'when the pipeline succeeds is passed' do
+ context 'when merge when pipeline succeeds option is passed' do
let!(:head_pipeline) do
create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch, head_pipeline_of: merge_request)
end
@@ -462,6 +462,30 @@ describe Projects::MergeRequestsController do
expect(json_response).to eq('status' => 'merge_when_pipeline_succeeds')
end
end
+
+ context 'when auto merge has not been enabled yet' do
+ it 'calls AutoMergeService#execute' do
+ expect_next_instance_of(AutoMergeService) do |service|
+ expect(service).to receive(:execute).with(merge_request, 'merge_when_pipeline_succeeds')
+ end
+
+ merge_when_pipeline_succeeds
+ end
+ end
+
+ context 'when auto merge has already been enabled' do
+ before do
+ merge_request.update!(auto_merge_enabled: true, merge_user: user)
+ end
+
+ it 'calls AutoMergeService#update' do
+ expect_next_instance_of(AutoMergeService) do |service|
+ expect(service).to receive(:update).with(merge_request)
+ end
+
+ merge_when_pipeline_succeeds
+ end
+ end
end
describe 'only_allow_merge_if_all_discussions_are_resolved? setting' do
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index b91a4df40a5..5bfbcf6eeb5 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -200,6 +200,21 @@ describe Projects::Settings::CiCdController do
expect(response).to redirect_to(namespace_project_settings_ci_cd_path)
end
end
+
+ context 'when default_git_depth is not specified' do
+ let(:params) { { ci_cd_settings_attributes: { default_git_depth: 10 } } }
+
+ before do
+ project.ci_cd_settings.update!(default_git_depth: nil)
+ end
+
+ it 'set specified git depth' do
+ subject
+
+ project.reload
+ expect(project.ci_default_git_depth).to eq(10)
+ end
+ end
end
end
end
diff --git a/spec/factories/pages_domain_acme_orders.rb b/spec/factories/pages_domain_acme_orders.rb
new file mode 100644
index 00000000000..7f9ee1c8f9c
--- /dev/null
+++ b/spec/factories/pages_domain_acme_orders.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :pages_domain_acme_order do
+ pages_domain
+ url { 'https://example.com/' }
+ expires_at { 1.day.from_now }
+ challenge_token { 'challenge_token' }
+ challenge_file_content { 'filecontent' }
+
+ private_key { OpenSSL::PKey::RSA.new(4096).to_pem }
+
+ trait :expired do
+ expires_at { 1.day.ago }
+ end
+ end
+end
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 83cd686818c..f6c498f7a4c 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'Admin Appearance' do
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index c4dbe23f6b4..93ccb03d822 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -302,22 +302,22 @@ describe 'Admin updates settings' do
group = create(:group)
page.within('.as-performance-bar') do
- check 'Enable the Performance Bar'
+ check 'Enable access to the Performance Bar'
fill_in 'Allowed group', with: group.path
click_on 'Save changes'
end
expect(page).to have_content "Application settings saved successfully"
- expect(find_field('Enable the Performance Bar')).to be_checked
+ expect(find_field('Enable access to the Performance Bar')).to be_checked
expect(find_field('Allowed group').value).to eq group.path
page.within('.as-performance-bar') do
- uncheck 'Enable the Performance Bar'
+ uncheck 'Enable access to the Performance Bar'
click_on 'Save changes'
end
expect(page).to have_content 'Application settings saved successfully'
- expect(find_field('Enable the Performance Bar')).not_to be_checked
+ expect(find_field('Enable access to the Performance Bar')).not_to be_checked
expect(find_field('Allowed group').value).to be_nil
end
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index dfa1c92ea49..d523e2992db 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe "Dashboard Issues Feed" do
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 2adeb37c98a..e6f44aa7d20 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -82,7 +82,7 @@ describe 'Commits' do
it 'shows pipeline`s data' do
expect(page).to have_content pipeline.sha[0..7]
- expect(page).to have_content pipeline.git_commit_message
+ expect(page).to have_content pipeline.git_commit_message.gsub!(/\s+/, ' ')
expect(page).to have_content pipeline.user.name
end
end
@@ -125,7 +125,7 @@ describe 'Commits' do
it 'Renders header', :js do
expect(page).to have_content pipeline.sha[0..7]
- expect(page).to have_content pipeline.git_commit_message
+ expect(page).to have_content pipeline.git_commit_message.gsub!(/\s+/, ' ')
expect(page).to have_content pipeline.user.name
expect(page).not_to have_link('Cancel running')
expect(page).not_to have_link('Retry')
@@ -147,7 +147,7 @@ describe 'Commits' do
it do
expect(page).to have_content pipeline.sha[0..7]
- expect(page).to have_content pipeline.git_commit_message
+ expect(page).to have_content pipeline.git_commit_message.gsub!(/\s+/, ' ')
expect(page).to have_content pipeline.user.name
expect(page).not_to have_link('Cancel running')
diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb
index e1bc4eca619..59230d6891a 100644
--- a/spec/features/groups/merge_requests_spec.rb
+++ b/spec/features/groups/merge_requests_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'Group merge requests page' do
diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb
index debae0ea930..b74bbf848ac 100644
--- a/spec/features/ics/dashboard_issues_spec.rb
+++ b/spec/features/ics/dashboard_issues_spec.rb
@@ -91,7 +91,7 @@ describe 'Dashboard Issues Calendar Feed' do
expect(body).to have_text("SUMMARY:test title (in #{project.full_path})")
# line length for ics is 75 chars
- expected_description = "DESCRIPTION:Find out more at #{issue_url(issue)}".insert(75, "\r\n")
+ expected_description = "DESCRIPTION:Find out more at #{issue_url(issue)}".insert(75, ' ')
expect(body).to have_text(expected_description)
expect(body).to have_text("DTSTART;VALUE=DATE:#{Date.tomorrow.strftime('%Y%m%d')}")
expect(body).to have_text("URL:#{issue_url(issue)}")
diff --git a/spec/features/ics/group_issues_spec.rb b/spec/features/ics/group_issues_spec.rb
index 4177c7f8704..86da720c8be 100644
--- a/spec/features/ics/group_issues_spec.rb
+++ b/spec/features/ics/group_issues_spec.rb
@@ -66,7 +66,7 @@ describe 'Group Issues Calendar Feed' do
expect(body).to have_text("SUMMARY:test title (in #{project.full_path})")
# line length for ics is 75 chars
- expected_description = "DESCRIPTION:Find out more at #{issue_url(issue)}".insert(75, "\r\n")
+ expected_description = "DESCRIPTION:Find out more at #{issue_url(issue)}".insert(75, ' ')
expect(body).to have_text(expected_description)
expect(body).to have_text("DTSTART;VALUE=DATE:#{Date.tomorrow.strftime('%Y%m%d')}")
expect(body).to have_text("URL:#{issue_url(issue)}")
diff --git a/spec/features/ics/project_issues_spec.rb b/spec/features/ics/project_issues_spec.rb
index 0d9844be099..37b90c666bc 100644
--- a/spec/features/ics/project_issues_spec.rb
+++ b/spec/features/ics/project_issues_spec.rb
@@ -65,7 +65,7 @@ describe 'Project Issues Calendar Feed' do
expect(body).to have_text("SUMMARY:test title (in #{project.full_path})")
# line length for ics is 75 chars
- expected_description = "DESCRIPTION:Find out more at #{issue_url(issue)}".insert(75, "\r\n")
+ expected_description = "DESCRIPTION:Find out more at #{issue_url(issue)}".insert(75, ' ')
expect(body).to have_text(expected_description)
expect(body).to have_text("DTSTART;VALUE=DATE:#{Date.tomorrow.strftime('%Y%m%d')}")
expect(body).to have_text("URL:#{issue_url(issue)}")
diff --git a/spec/features/instance_statistics/conversational_development_index_spec.rb b/spec/features/instance_statistics/conversational_development_index_spec.rb
index d8be554d734..713cd944f8c 100644
--- a/spec/features/instance_statistics/conversational_development_index_spec.rb
+++ b/spec/features/instance_statistics/conversational_development_index_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'Conversational Development Index' do
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index bc0ec58bd24..5bdd9113b06 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'Issues' do
@@ -208,7 +210,7 @@ describe 'Issues' do
let(:issue) { @issue }
it 'allows filtering by issues with no specified assignee' do
- visit project_issues_path(project, assignee_id: IssuableFinder::NONE)
+ visit project_issues_path(project, assignee_id: IssuableFinder::FILTER_NONE)
expect(page).to have_content 'foobar'
expect(page).not_to have_content 'barbaz'
diff --git a/spec/features/merge_request/user_accepts_merge_request_spec.rb b/spec/features/merge_request/user_accepts_merge_request_spec.rb
index 5fa23dbb998..3d029ccec1a 100644
--- a/spec/features/merge_request/user_accepts_merge_request_spec.rb
+++ b/spec/features/merge_request/user_accepts_merge_request_spec.rb
@@ -15,8 +15,7 @@ describe 'User accepts a merge request', :js do
click_button('Merge')
- expect(page).to have_content("The changes were merged into #{merge_request.target_branch} with \
- #{merge_request.short_merge_commit_sha}")
+ expect(page).to have_content("The changes were merged into #{merge_request.target_branch} with #{merge_request.short_merge_commit_sha}")
end
context 'with removing the source branch' do
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index 5db54f42264..3a9a06a6bc3 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -313,7 +313,7 @@ describe 'Merge request > User creates image diff notes', :js do
def create_image_diff_note
expand_text = 'Click to expand it.'
- page.all('a', text: expand_text).each do |element|
+ page.all('a', text: expand_text, wait: false).each do |element|
element.click
end
diff --git a/spec/features/merge_request/user_merges_merge_request_spec.rb b/spec/features/merge_request/user_merges_merge_request_spec.rb
index 6539e6e9208..da15a4bda4b 100644
--- a/spec/features/merge_request/user_merges_merge_request_spec.rb
+++ b/spec/features/merge_request/user_merges_merge_request_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe "User merges a merge request", :js do
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 04b07525919..8dc5912b8be 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -43,8 +43,7 @@ describe 'Merge request > User sees diff', :js do
visit diffs_project_merge_request_path(project, merge_request)
page.within('.alert') do
- expect(page).to have_text("Too many changes to show. Plain diff Email patch To preserve
- performance only 3 of 3+ files are displayed.")
+ expect(page).to have_text("Too many changes to show. Plain diff Email patch To preserve performance only 3 of 3+ files are displayed.")
end
end
end
diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb
index 0066e985fbb..393077a916f 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -565,7 +565,7 @@ describe 'Merge request > User sees merge widget', :js do
click_button 'subtractTest'
expect(page).to have_content('6.66')
- expect(page).to have_content(sample_java_failed_message)
+ expect(page).to have_content(sample_java_failed_message.gsub!(/\s+/, ' ').strip)
end
end
end
@@ -610,7 +610,7 @@ describe 'Merge request > User sees merge widget', :js do
click_button 'Test#sum when a is 2 and b is 2 returns summary'
expect(page).to have_content('2.22')
- expect(page).to have_content(sample_rspec_failed_message)
+ expect(page).to have_content(sample_rspec_failed_message.gsub!(/\s+/, ' ').strip)
end
end
end
diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
index 04c7f4b6c76..780de76d2c5 100644
--- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
+++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
@@ -7,8 +7,8 @@ describe 'User comments on a diff', :js do
include RepoHelpers
def expect_suggestion_has_content(element, expected_changing_content, expected_suggested_content)
- changing_content = element.all(:css, '.line_holder.old').map(&:text)
- suggested_content = element.all(:css, '.line_holder.new').map(&:text)
+ changing_content = element.all(:css, '.line_holder.old').map { |el| el.text(normalize_ws: true) }
+ suggested_content = element.all(:css, '.line_holder.new').map { |el| el.text(normalize_ws: true) }
expect(changing_content).to eq(expected_changing_content)
expect(suggested_content).to eq(expected_suggested_content)
diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
index bd91fae1453..2dee0e26954 100644
--- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
@@ -33,7 +33,7 @@ describe 'Merge requests > User lists merge requests' do
end
it 'filters on no assignee' do
- visit_merge_requests(project, assignee_id: IssuableFinder::NONE)
+ visit_merge_requests(project, assignee_id: IssuableFinder::FILTER_NONE)
expect(current_path).to eq(project_merge_requests_path(project))
expect(page).to have_content 'merge-test'
diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb
index 76abc640077..95685a3c7ff 100644
--- a/spec/features/project_variables_spec.rb
+++ b/spec/features/project_variables_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'Project variables', :js do
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index a85e7333ba8..ce382c19fc1 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'Clusters', :js do
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 953517cdff9..a84fee34669 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -13,7 +13,7 @@ describe 'User browses commits' do
it 'renders commit' do
visit project_commit_path(project, sample_commit.id)
- expect(page).to have_content(sample_commit.message)
+ expect(page).to have_content(sample_commit.message.gsub!(/\s+/, ' '))
.and have_content("Showing #{sample_commit.files_changed_count} changed files")
.and have_content('Side-by-side')
end
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index ab16fdee883..254e885ce46 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -68,7 +68,8 @@ describe 'Edit Project Settings' do
end
it "hides builds when disabled" do
- allow(Ability).to receive(:allowed?).with(member, :read_builds, project).and_return(false)
+ allow(Ability).to receive(:allowed?).and_return(true)
+ allow(Ability).to receive(:allowed?).with(member, :read_build, project).and_return(false)
visit project_pipelines_path(project)
diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb
index fa785ed10ef..91618145391 100644
--- a/spec/features/projects/files/undo_template_spec.rb
+++ b/spec/features/projects/files/undo_template_spec.rb
@@ -50,7 +50,7 @@ end
def check_content_reverted(template_content)
find('.template-selectors-undo-menu .btn-info').click
expect(page).not_to have_content(template_content)
- expect(find('.template-type-selector .dropdown-toggle-text')).to have_content
+ expect(page).to have_css('.template-type-selector .dropdown-toggle-text')
end
def select_file_template(template_selector_selector, template_name)
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index 69f8bd4d319..264b288ab38 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -174,7 +174,6 @@ describe 'Projects > Files > User creates files' do
it 'creates and commit new file in forked project', :js do
expect(page).to have_selector('.file-editor')
- expect(page).to have_content
find('#editor')
execute_script("ace.edit('editor').setValue('*.rbca')")
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index 26efb5e6787..683268d064a 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -172,7 +172,7 @@ describe 'Projects > Files > User edits files', :js do
wait_for_requests
end
- it 'links to the forked project for editing', :quarantine do
+ it 'links to the forked project for editing' do
click_link('.gitignore')
find('.js-edit-blob').click
diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb
index c231e54decd..7f49ddf560f 100644
--- a/spec/features/projects/labels/user_removes_labels_spec.rb
+++ b/spec/features/projects/labels/user_removes_labels_spec.rb
@@ -41,7 +41,7 @@ describe "User removes labels" do
it "removes all labels" do
loop do
- li = page.first(".label-list-item")
+ li = page.first(".label-list-item", minimum: 0)
break unless li
li.find('.js-label-options-dropdown').click
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 25b3ac00604..77f0f237d0a 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'Pipeline', :js do
@@ -89,7 +91,7 @@ describe 'Pipeline', :js do
within '.pipeline-info' do
expect(page).to have_content("#{pipeline.statuses.count} jobs " \
- "for #{pipeline.ref} ")
+ "for #{pipeline.ref}")
expect(page).to have_link(pipeline.ref,
href: project_commits_path(pipeline.project, pipeline.ref))
end
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
index 32959969f54..016ccf63f58 100644
--- a/spec/features/projects/settings/integration_settings_spec.rb
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -41,7 +41,7 @@ describe 'Projects > Settings > Integration settings' do
expect(page).to have_content('Issues events')
expect(page).to have_content('Confidential issues events')
expect(page).to have_content('Note events')
- expect(page).to have_content('Merge requests events')
+ expect(page).to have_content('Merge requests events')
expect(page).to have_content('Pipeline events')
expect(page).to have_content('Wiki page events')
end
diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb
index dcf7d314f8e..e5dd2f40fdf 100644
--- a/spec/features/projects/tree/upload_file_spec.rb
+++ b/spec/features/projects/tree/upload_file_spec.rb
@@ -29,6 +29,6 @@ describe 'Multi-file editor upload file', :js do
attach_file('file-upload', txt_file)
expect(page).to have_selector('.multi-file-tab', text: 'doc_sample.txt')
- expect(find('.blob-editor-container .lines-content')['innerText']).to have_content(File.open(txt_file, &:readline))
+ expect(find('.blob-editor-container .lines-content')['innerText']).to have_content(File.open(txt_file, &:readline).gsub!(/\s+/, ' '))
end
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 27f6ed56283..b5112758475 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'Project' do
diff --git a/spec/features/search/user_searches_for_commits_spec.rb b/spec/features/search/user_searches_for_commits_spec.rb
index 28cae444588..998f8521384 100644
--- a/spec/features/search/user_searches_for_commits_spec.rb
+++ b/spec/features/search/user_searches_for_commits_spec.rb
@@ -35,7 +35,7 @@ describe 'User searches for commits' do
fill_in('search', with: 'deadbeef')
click_button('Search')
- expect(page).to have_current_path('/search', only_path: true)
+ expect(page).to have_current_path('/search', ignore_query: true)
end
it 'finds multiple commits' do
diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb
index a198e65046f..044a47567be 100644
--- a/spec/features/security/profile_access_spec.rb
+++ b/spec/features/security/profile_access_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe "Profile access" do
diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb
index db2970f3340..f80ddd050d7 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/master_creates_tag_spec.rb
@@ -39,7 +39,7 @@ describe 'Maintainer creates tag' do
project_tag_path(project, 'v3.0'))
expect(page).to have_content 'v3.0'
page.within 'pre.wrap' do
- expect(page).to have_content "Awesome tag message\n\n- hello\n- world"
+ expect(page).to have_content "Awesome tag message - hello - world"
end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 89fdaceaa9f..bf38d083ca6 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -241,14 +241,6 @@ describe IssuesFinder do
end
end
- context 'filtering by legacy No+Label' do
- let(:params) { { label_name: Label::NONE } }
-
- it 'returns issues with no labels' do
- expect(issues).to contain_exactly(issue1, issue3, issue4)
- end
- end
-
context 'filtering by any label' do
let(:params) { { label_name: described_class::FILTER_ANY } }
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json
index 2d0af57ec2c..33393805464 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json
@@ -2,7 +2,8 @@
"type": "object",
"required": [
"unit",
- "label"
+ "label",
+ "prometheus_endpoint_path"
],
"oneOf": [
{ "required": ["query"] },
@@ -14,7 +15,8 @@
"query": { "type": "string" },
"unit": { "type": "string" },
"label": { "type": "string" },
- "track": { "type": "string" }
+ "track": { "type": "string" },
+ "prometheus_endpoint_path": { "type": "string" }
},
"additionalProperties": false
}
diff --git a/spec/frontend/boards/modal_store_spec.js b/spec/frontend/boards/modal_store_spec.js
index 3257a3fb8a3..4dd27e94d97 100644
--- a/spec/frontend/boards/modal_store_spec.js
+++ b/spec/frontend/boards/modal_store_spec.js
@@ -1,7 +1,7 @@
/* global ListIssue */
-import '~/vue_shared/models/label';
-import '~/vue_shared/models/assignee';
+import '~/boards/models/label';
+import '~/boards/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import Store from '~/boards/stores/modal_store';
diff --git a/spec/frontend/clusters/clusters_bundle_spec.js b/spec/frontend/clusters/clusters_bundle_spec.js
index 66b22fa2681..6de06a9e2d5 100644
--- a/spec/frontend/clusters/clusters_bundle_spec.js
+++ b/spec/frontend/clusters/clusters_bundle_spec.js
@@ -1,5 +1,10 @@
import Clusters from '~/clusters/clusters_bundle';
-import { APPLICATION_STATUS, INGRESS_DOMAIN_SUFFIX, APPLICATIONS } from '~/clusters/constants';
+import {
+ APPLICATION_STATUS,
+ INGRESS_DOMAIN_SUFFIX,
+ APPLICATIONS,
+ RUNNER,
+} from '~/clusters/constants';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import { loadHTMLFixture } from 'helpers/fixtures';
@@ -353,4 +358,30 @@ describe('Clusters', () => {
});
});
});
+
+ describe('updateApplication', () => {
+ const params = { version: '1.0.0' };
+ let storeUpdateApplication;
+ let installApplication;
+
+ beforeEach(() => {
+ storeUpdateApplication = jest.spyOn(cluster.store, 'updateApplication');
+ installApplication = jest.spyOn(cluster.service, 'installApplication');
+
+ cluster.updateApplication({ id: RUNNER, params });
+ });
+
+ afterEach(() => {
+ storeUpdateApplication.mockRestore();
+ installApplication.mockRestore();
+ });
+
+ it('calls store updateApplication method', () => {
+ expect(storeUpdateApplication).toHaveBeenCalledWith(RUNNER);
+ });
+
+ it('sends installApplication request', () => {
+ expect(installApplication).toHaveBeenCalledWith(RUNNER, params);
+ });
+ });
});
diff --git a/spec/frontend/clusters/components/application_row_spec.js b/spec/frontend/clusters/components/application_row_spec.js
index 7c781b72355..9f127ccb690 100644
--- a/spec/frontend/clusters/components/application_row_spec.js
+++ b/spec/frontend/clusters/components/application_row_spec.js
@@ -245,26 +245,26 @@ describe('Application Row', () => {
});
});
- describe('Upgrade button', () => {
+ describe('Update button', () => {
it('has indeterminate state on page load', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
status: null,
});
- const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
+ const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
- expect(upgradeBtn).toBe(null);
+ expect(updateBtn).toBe(null);
});
- it('has enabled "Upgrade" when "upgradeAvailable" is true', () => {
+ it('has enabled "Update" when "updateAvailable" is true', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- upgradeAvailable: true,
+ updateAvailable: true,
});
- const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
+ const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
- expect(upgradeBtn).not.toBe(null);
- expect(upgradeBtn.innerHTML).toContain('Upgrade');
+ expect(updateBtn).not.toBe(null);
+ expect(updateBtn.innerHTML).toContain('Update');
});
it('has enabled "Retry update" when update process fails', () => {
@@ -273,10 +273,10 @@ describe('Application Row', () => {
status: APPLICATION_STATUS.INSTALLED,
updateFailed: true,
});
- const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
+ const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
- expect(upgradeBtn).not.toBe(null);
- expect(upgradeBtn.innerHTML).toContain('Retry update');
+ expect(updateBtn).not.toBe(null);
+ expect(updateBtn.innerHTML).toContain('Retry update');
});
it('has disabled "Updating" when APPLICATION_STATUS.UPDATING', () => {
@@ -284,53 +284,51 @@ describe('Application Row', () => {
...DEFAULT_APPLICATION_STATE,
status: APPLICATION_STATUS.UPDATING,
});
- const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
+ const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
- expect(upgradeBtn).not.toBe(null);
- expect(vm.isUpgrading).toBe(true);
- expect(upgradeBtn.innerHTML).toContain('Updating');
+ expect(updateBtn).not.toBe(null);
+ expect(vm.isUpdating).toBe(true);
+ expect(updateBtn.innerHTML).toContain('Updating');
});
- it('clicking upgrade button emits event', () => {
+ it('clicking update button emits event', () => {
jest.spyOn(eventHub, '$emit');
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
status: APPLICATION_STATUS.INSTALLED,
- upgradeAvailable: true,
+ updateAvailable: true,
});
- const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
+ const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
- upgradeBtn.click();
+ updateBtn.click();
- expect(eventHub.$emit).toHaveBeenCalledWith('upgradeApplication', {
+ expect(eventHub.$emit).toHaveBeenCalledWith('updateApplication', {
id: DEFAULT_APPLICATION_STATE.id,
params: {},
});
});
- it('clicking disabled upgrade button emits nothing', () => {
+ it('clicking disabled update button emits nothing', () => {
jest.spyOn(eventHub, '$emit');
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
status: APPLICATION_STATUS.UPDATING,
});
- const upgradeBtn = vm.$el.querySelector('.js-cluster-application-upgrade-button');
+ const updateBtn = vm.$el.querySelector('.js-cluster-application-update-button');
- upgradeBtn.click();
+ updateBtn.click();
expect(eventHub.$emit).not.toHaveBeenCalled();
});
- it('displays an error message if application upgrade failed', () => {
+ it('displays an error message if application update failed', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
title: 'GitLab Runner',
status: APPLICATION_STATUS.INSTALLED,
updateFailed: true,
});
- const failureMessage = vm.$el.querySelector(
- '.js-cluster-application-upgrade-failure-message',
- );
+ const failureMessage = vm.$el.querySelector('.js-cluster-application-update-details');
expect(failureMessage).not.toBe(null);
expect(failureMessage.innerHTML).toContain(
@@ -338,7 +336,7 @@ describe('Application Row', () => {
);
});
- it('displays a success toast message if application upgrade was successful', () => {
+ it('displays a success toast message if application update was successful', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
title: 'GitLab Runner',
@@ -349,13 +347,13 @@ describe('Application Row', () => {
vm.updateSuccessful = true;
return vm.$nextTick(() => {
- expect(vm.$toast.show).toHaveBeenCalledWith('GitLab Runner upgraded successfully.');
+ expect(vm.$toast.show).toHaveBeenCalledWith('GitLab Runner updated successfully.');
});
});
});
describe('Version', () => {
- it('displays a version number if application has been upgraded', () => {
+ it('displays a version number if application has been updated', () => {
const version = '0.1.45';
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
@@ -363,15 +361,15 @@ describe('Application Row', () => {
updateSuccessful: true,
version,
});
- const upgradeDetails = vm.$el.querySelector('.js-cluster-application-upgrade-details');
- const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version');
+ const updateDetails = vm.$el.querySelector('.js-cluster-application-update-details');
+ const versionEl = vm.$el.querySelector('.js-cluster-application-update-version');
- expect(upgradeDetails.innerHTML).toContain('Upgraded');
+ expect(updateDetails.innerHTML).toContain('Updated');
expect(versionEl).not.toBe(null);
expect(versionEl.innerHTML).toContain(version);
});
- it('contains a link to the chart repo if application has been upgraded', () => {
+ it('contains a link to the chart repo if application has been updated', () => {
const version = '0.1.45';
const chartRepo = 'https://gitlab.com/charts/gitlab-runner';
vm = mountComponent(ApplicationRow, {
@@ -381,13 +379,13 @@ describe('Application Row', () => {
chartRepo,
version,
});
- const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version');
+ const versionEl = vm.$el.querySelector('.js-cluster-application-update-version');
expect(versionEl.href).toEqual(chartRepo);
expect(versionEl.target).toEqual('_blank');
});
- it('does not display a version number if application upgrade failed', () => {
+ it('does not display a version number if application update failed', () => {
const version = '0.1.45';
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
@@ -395,10 +393,10 @@ describe('Application Row', () => {
updateFailed: true,
version,
});
- const upgradeDetails = vm.$el.querySelector('.js-cluster-application-upgrade-details');
- const versionEl = vm.$el.querySelector('.js-cluster-application-upgrade-version');
+ const updateDetails = vm.$el.querySelector('.js-cluster-application-update-details');
+ const versionEl = vm.$el.querySelector('.js-cluster-application-update-version');
- expect(upgradeDetails.innerHTML).toContain('failed');
+ expect(updateDetails.innerHTML).toContain('failed');
expect(versionEl).toBe(null);
});
});
diff --git a/spec/frontend/clusters/services/application_state_machine_spec.js b/spec/frontend/clusters/services/application_state_machine_spec.js
index e057e2ac955..c146ef79be7 100644
--- a/spec/frontend/clusters/services/application_state_machine_spec.js
+++ b/spec/frontend/clusters/services/application_state_machine_spec.js
@@ -127,7 +127,7 @@ describe('applicationStateMachine', () => {
describe(`current state is ${UPDATING}`, () => {
it.each`
expectedState | event | effects
- ${INSTALLED} | ${UPDATED} | ${{ updateSuccessful: true, updateAcknowledged: false }}
+ ${INSTALLED} | ${UPDATED} | ${{ updateSuccessful: true }}
${INSTALLED} | ${UPDATE_ERRORED} | ${{ updateFailed: true }}
`(`transitions to $expectedState on $event event and applies $effects`, data => {
const { expectedState, event, effects } = data;
diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js
index 0d129349799..f2cc413512d 100644
--- a/spec/frontend/clusters/stores/clusters_store_spec.js
+++ b/spec/frontend/clusters/stores/clusters_store_spec.js
@@ -85,11 +85,10 @@ describe('Clusters Store', () => {
statusReason: mockResponseData.applications[2].status_reason,
requestReason: null,
version: mockResponseData.applications[2].version,
- upgradeAvailable: mockResponseData.applications[2].update_available,
+ updateAvailable: mockResponseData.applications[2].update_available,
chartRepo: 'https://gitlab.com/charts/gitlab-runner',
installed: false,
installFailed: false,
- updateAcknowledged: true,
updateFailed: false,
updateSuccessful: false,
uninstallable: false,
diff --git a/spec/frontend/helpers/timeout.js b/spec/frontend/helpers/timeout.js
index e74598ae20a..702ef0be5aa 100644
--- a/spec/frontend/helpers/timeout.js
+++ b/spec/frontend/helpers/timeout.js
@@ -5,7 +5,13 @@ const IS_DEBUGGING = process.execArgv.join(' ').includes('--inspect-brk');
let testTimeoutNS;
export const setTestTimeout = newTimeoutMS => {
- testTimeoutNS = newTimeoutMS * NS_PER_MS;
+ const newTimeoutNS = newTimeoutMS * NS_PER_MS;
+ // never accept a smaller timeout than the default
+ if (newTimeoutNS < testTimeoutNS) {
+ return;
+ }
+
+ testTimeoutNS = newTimeoutNS;
jest.setTimeout(newTimeoutMS);
};
@@ -13,7 +19,13 @@ export const setTestTimeout = newTimeoutMS => {
// Useful for tests with jQuery, which is very slow in big DOMs.
let temporaryTimeoutNS = null;
export const setTestTimeoutOnce = newTimeoutMS => {
- temporaryTimeoutNS = newTimeoutMS * NS_PER_MS;
+ const newTimeoutNS = newTimeoutMS * NS_PER_MS;
+ // never accept a smaller timeout than the default
+ if (newTimeoutNS < testTimeoutNS) {
+ return;
+ }
+
+ temporaryTimeoutNS = newTimeoutNS;
};
export const initializeTestTimeout = defaultTimeoutMS => {
diff --git a/spec/frontend/ide/components/ide_status_list_spec.js b/spec/frontend/ide/components/ide_status_list_spec.js
new file mode 100644
index 00000000000..4e0e8a9f0e3
--- /dev/null
+++ b/spec/frontend/ide/components/ide_status_list_spec.js
@@ -0,0 +1,91 @@
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import IdeStatusList from '~/ide/components/ide_status_list';
+
+const TEST_FILE = {
+ name: 'lorem.md',
+ eol: 'LF',
+ editorRow: 3,
+ editorColumn: 23,
+ fileLanguage: 'markdown',
+};
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('ide/components/ide_status_list', () => {
+ let activeFile;
+ let store;
+ let wrapper;
+
+ const createComponent = (options = {}) => {
+ store = new Vuex.Store({
+ getters: {
+ activeFile: () => activeFile,
+ },
+ });
+
+ wrapper = shallowMount(localVue.extend(IdeStatusList), {
+ localVue,
+ sync: false,
+ store,
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ activeFile = TEST_FILE;
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+
+ store = null;
+ wrapper = null;
+ });
+
+ const getEditorPosition = file => `${file.editorRow}:${file.editorColumn}`;
+
+ describe('with regular file', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('shows file name', () => {
+ expect(wrapper.text()).toContain(TEST_FILE.name);
+ });
+
+ it('shows file eol', () => {
+ expect(wrapper.text()).toContain(TEST_FILE.name);
+ });
+
+ it('shows file editor position', () => {
+ expect(wrapper.text()).toContain(getEditorPosition(TEST_FILE));
+ });
+
+ it('shows file language', () => {
+ expect(wrapper.text()).toContain(TEST_FILE.fileLanguage);
+ });
+ });
+
+ describe('with binary file', () => {
+ beforeEach(() => {
+ activeFile.binary = true;
+ createComponent();
+ });
+
+ it('does not show file editor position', () => {
+ expect(wrapper.text()).not.toContain(getEditorPosition(TEST_FILE));
+ });
+ });
+
+ it('adds slot as child of list', () => {
+ createComponent({
+ slots: {
+ default: ['<div class="js-test">Hello</div>', '<div class="js-test">World</div>'],
+ },
+ });
+
+ expect(wrapper.find('.ide-status-list').findAll('.js-test').length).toEqual(2);
+ });
+});
diff --git a/spec/frontend/lib/utils/number_utility_spec.js b/spec/frontend/lib/utils/number_utility_spec.js
index 818404bad81..77d7478d317 100644
--- a/spec/frontend/lib/utils/number_utility_spec.js
+++ b/spec/frontend/lib/utils/number_utility_spec.js
@@ -5,6 +5,7 @@ import {
bytesToGiB,
numberToHumanSize,
sum,
+ isOdd,
} from '~/lib/utils/number_utils';
describe('Number Utils', () => {
@@ -98,4 +99,14 @@ describe('Number Utils', () => {
expect([1, 2, 3, 4, 5].reduce(sum)).toEqual(15);
});
});
+
+ describe('isOdd', () => {
+ it('should return 0 with a even number', () => {
+ expect(isOdd(2)).toEqual(0);
+ });
+
+ it('should return 1 with a odd number', () => {
+ expect(isOdd(1)).toEqual(1);
+ });
+ });
});
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index eca240c9c18..c771984a137 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -1,5 +1,12 @@
import * as urlUtils from '~/lib/utils/url_utility';
+const setWindowLocation = value => {
+ Object.defineProperty(window, 'location', {
+ writable: true,
+ value,
+ });
+};
+
describe('URL utility', () => {
describe('webIDEUrl', () => {
afterEach(() => {
@@ -110,12 +117,9 @@ describe('URL utility', () => {
describe('getBaseURL', () => {
beforeEach(() => {
- global.window = Object.create(window);
- Object.defineProperty(window, 'location', {
- value: {
- host: 'gitlab.com',
- protocol: 'https:',
- },
+ setWindowLocation({
+ protocol: 'https:',
+ host: 'gitlab.com',
});
});
@@ -191,4 +195,32 @@ describe('URL utility', () => {
});
});
});
+
+ describe('getWebSocketProtocol', () => {
+ it.each`
+ protocol | expectation
+ ${'http:'} | ${'ws:'}
+ ${'https:'} | ${'wss:'}
+ `('returns "$expectation" with "$protocol" protocol', ({ protocol, expectation }) => {
+ setWindowLocation({
+ protocol,
+ host: 'example.com',
+ });
+
+ expect(urlUtils.getWebSocketProtocol()).toEqual(expectation);
+ });
+ });
+
+ describe('getWebSocketUrl', () => {
+ it('joins location host to path', () => {
+ setWindowLocation({
+ protocol: 'http:',
+ host: 'example.com',
+ });
+
+ const path = '/lorem/ipsum?a=bc';
+
+ expect(urlUtils.getWebSocketUrl(path)).toEqual('ws://example.com/lorem/ipsum?a=bc');
+ });
+ });
});
diff --git a/spec/frontend/reports/components/report_section_spec.js b/spec/frontend/reports/components/report_section_spec.js
index 3b609484b9e..d4a3073374a 100644
--- a/spec/frontend/reports/components/report_section_spec.js
+++ b/spec/frontend/reports/components/report_section_spec.js
@@ -197,4 +197,44 @@ describe('Report section', () => {
expect(vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand');
});
});
+
+ describe('Success and Error slots', () => {
+ const createComponent = status => {
+ vm = mountComponentWithSlots(ReportSection, {
+ props: {
+ status,
+ hasIssues: true,
+ },
+ slots: {
+ success: ['This is a success'],
+ loading: ['This is loading'],
+ error: ['This is an error'],
+ },
+ });
+ };
+
+ it('only renders success slot when status is "SUCCESS"', () => {
+ createComponent('SUCCESS');
+
+ expect(vm.$el.textContent.trim()).toContain('This is a success');
+ expect(vm.$el.textContent.trim()).not.toContain('This is an error');
+ expect(vm.$el.textContent.trim()).not.toContain('This is loading');
+ });
+
+ it('only renders error slot when status is "ERROR"', () => {
+ createComponent('ERROR');
+
+ expect(vm.$el.textContent.trim()).toContain('This is an error');
+ expect(vm.$el.textContent.trim()).not.toContain('This is a success');
+ expect(vm.$el.textContent.trim()).not.toContain('This is loading');
+ });
+
+ it('only renders loading slot when status is "LOADING"', () => {
+ createComponent('LOADING');
+
+ expect(vm.$el.textContent.trim()).toContain('This is loading');
+ expect(vm.$el.textContent.trim()).not.toContain('This is an error');
+ expect(vm.$el.textContent.trim()).not.toContain('This is a success');
+ });
+ });
});
diff --git a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
index 1b4564303e4..1f06d693411 100644
--- a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
+++ b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
@@ -15,6 +15,7 @@ exports[`Repository table row component renders table row 1`] = `
<a
class="str-truncated"
+ href="https://test.com"
>
test
@@ -22,6 +23,8 @@ exports[`Repository table row component renders table row 1`] = `
</a>
<!---->
+
+ <!---->
</td>
<td
diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js
index a70dc7bb866..5a345ddeacd 100644
--- a/spec/frontend/repository/components/table/row_spec.js
+++ b/spec/frontend/repository/components/table/row_spec.js
@@ -1,6 +1,10 @@
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
+import { GlBadge } from '@gitlab/ui';
+import { visitUrl } from '~/lib/utils/url_utility';
import TableRow from '~/repository/components/table/row.vue';
+jest.mock('~/lib/utils/url_utility');
+
let vm;
let $router;
@@ -10,7 +14,10 @@ function factory(propsData = {}) {
};
vm = shallowMount(TableRow, {
- propsData,
+ propsData: {
+ ...propsData,
+ url: `https://test.com`,
+ },
mocks: {
$router,
},
@@ -25,6 +32,7 @@ function factory(propsData = {}) {
describe('Repository table row component', () => {
afterEach(() => {
vm.destroy();
+ jest.clearAllMocks();
});
it('renders table row', () => {
@@ -76,6 +84,28 @@ describe('Repository table row component', () => {
}
});
+ it.each`
+ type | pushes
+ ${'tree'} | ${true}
+ ${'file'} | ${false}
+ ${'commit'} | ${false}
+ `('calls visitUrl if $type is not tree', ({ type, pushes }) => {
+ factory({
+ id: '1',
+ path: 'test',
+ type,
+ currentPath: '/',
+ });
+
+ vm.trigger('click');
+
+ if (pushes) {
+ expect(visitUrl).not.toHaveBeenCalled();
+ } else {
+ expect(visitUrl).toHaveBeenCalledWith('https://test.com');
+ }
+ });
+
it('renders commit ID for submodule', () => {
factory({
id: '1',
@@ -98,4 +128,16 @@ describe('Repository table row component', () => {
expect(vm.find('a').attributes('href')).toEqual('https://test.com');
});
+
+ it('renders LFS badge', () => {
+ factory({
+ id: '1',
+ path: 'test',
+ type: 'commit',
+ currentPath: '/',
+ lfsOid: '1',
+ });
+
+ expect(vm.find(GlBadge).exists()).toBe(true);
+ });
});
diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js
index c24f0bc4776..7e7cc1488b8 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -15,7 +15,7 @@ afterEach(() =>
}),
);
-initializeTestTimeout(500);
+initializeTestTimeout(process.env.CI ? 5000 : 500);
// fail tests for unmocked requests
beforeEach(done => {
diff --git a/spec/frontend/vue_shared/components/issue/issue_warning_spec.js b/spec/frontend/vue_shared/components/issue/issue_warning_spec.js
index 4a8de5fc4f1..63880b85625 100644
--- a/spec/frontend/vue_shared/components/issue/issue_warning_spec.js
+++ b/spec/frontend/vue_shared/components/issue/issue_warning_spec.js
@@ -15,31 +15,37 @@ function formatWarning(string) {
describe('Issue Warning Component', () => {
describe('isLocked', () => {
it('should render locked issue warning information', () => {
- const vm = mountComponent(IssueWarning, {
+ const props = {
isLocked: true,
- });
+ lockedIssueDocsPath: 'docs/issues/locked',
+ };
+ const vm = mountComponent(IssueWarning, props);
expect(
vm.$el.querySelector('.icon use').getAttributeNS('http://www.w3.org/1999/xlink', 'href'),
).toMatch(/lock$/);
expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual(
- 'This issue is locked. Only project members can comment.',
+ 'This issue is locked. Only project members can comment. Learn more',
);
+ expect(vm.$el.querySelector('a').href).toContain(props.lockedIssueDocsPath);
});
});
describe('isConfidential', () => {
it('should render confidential issue warning information', () => {
- const vm = mountComponent(IssueWarning, {
+ const props = {
isConfidential: true,
- });
+ confidentialIssueDocsPath: '/docs/issues/confidential',
+ };
+ const vm = mountComponent(IssueWarning, props);
expect(
vm.$el.querySelector('.icon use').getAttributeNS('http://www.w3.org/1999/xlink', 'href'),
).toMatch(/eye-slash$/);
expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual(
- 'This is a confidential issue. Your comment will not be visible to the public.',
+ 'This is a confidential issue. People without permission will never get a notification. Learn more',
);
+ expect(vm.$el.querySelector('a').href).toContain(props.confidentialIssueDocsPath);
});
});
diff --git a/spec/frontend/vue_shared/components/modal_copy_button_spec.js b/spec/frontend/vue_shared/components/modal_copy_button_spec.js
new file mode 100644
index 00000000000..f1943861523
--- /dev/null
+++ b/spec/frontend/vue_shared/components/modal_copy_button_spec.js
@@ -0,0 +1,40 @@
+import Vue from 'vue';
+import { shallowMount } from '@vue/test-utils';
+import modalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
+
+describe('modal copy button', () => {
+ const Component = Vue.extend(modalCopyButton);
+ let wrapper;
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ beforeEach(() => {
+ wrapper = shallowMount(Component, {
+ propsData: {
+ text: 'copy me',
+ title: 'Copy this value into Clipboard!',
+ },
+ });
+ });
+
+ describe('clipboard', () => {
+ it('should fire a `success` event on click', () => {
+ document.execCommand = jest.fn(() => true);
+ window.getSelection = jest.fn(() => ({
+ toString: jest.fn(() => 'test'),
+ removeAllRanges: jest.fn(),
+ }));
+ wrapper.trigger('click');
+ expect(wrapper.emitted().success).not.toBeEmpty();
+ expect(document.execCommand).toHaveBeenCalledWith('copy');
+ });
+ it("should propagate the clipboard error event if execCommand doesn't work", () => {
+ document.execCommand = jest.fn(() => false);
+ wrapper.trigger('click');
+ expect(wrapper.emitted().error).not.toBeEmpty();
+ expect(document.execCommand).toHaveBeenCalledWith('copy');
+ });
+ });
+});
diff --git a/spec/graphql/resolvers/base_resolver_spec.rb b/spec/graphql/resolvers/base_resolver_spec.rb
index 9982288e206..c162fdbbb47 100644
--- a/spec/graphql/resolvers/base_resolver_spec.rb
+++ b/spec/graphql/resolvers/base_resolver_spec.rb
@@ -29,18 +29,20 @@ describe Resolvers::BaseResolver do
end
end
- it 'increases complexity based on arguments' do
- field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: described_class, null: false, max_page_size: 1)
+ context 'when field is a connection' do
+ it 'increases complexity based on arguments' do
+ field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: described_class, null: false, max_page_size: 1)
- expect(field.to_graphql.complexity.call({}, { sort: 'foo' }, 1)).to eq 3
- expect(field.to_graphql.complexity.call({}, { search: 'foo' }, 1)).to eq 7
- end
+ expect(field.to_graphql.complexity.call({}, { sort: 'foo' }, 1)).to eq 3
+ expect(field.to_graphql.complexity.call({}, { search: 'foo' }, 1)).to eq 7
+ end
- it 'does not increase complexity when filtering by iids' do
- field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: described_class, null: false, max_page_size: 100)
+ it 'does not increase complexity when filtering by iids' do
+ field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
- expect(field.to_graphql.complexity.call({}, { sort: 'foo' }, 1)).to eq 6
- expect(field.to_graphql.complexity.call({}, { sort: 'foo', iid: 1 }, 1)).to eq 3
- expect(field.to_graphql.complexity.call({}, { sort: 'foo', iids: [1, 2, 3] }, 1)).to eq 3
+ expect(field.to_graphql.complexity.call({}, { sort: 'foo' }, 1)).to eq 6
+ expect(field.to_graphql.complexity.call({}, { sort: 'foo', iid: 1 }, 1)).to eq 3
+ expect(field.to_graphql.complexity.call({}, { sort: 'foo', iids: [1, 2, 3] }, 1)).to eq 3
+ end
end
end
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index bffcdbfe915..798fe00de97 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -121,7 +121,7 @@ describe Resolvers::IssuesResolver do
end
it 'increases field complexity based on arguments' do
- field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: described_class, null: false, max_page_size: 100)
+ field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
expect(field.to_graphql.complexity.call({}, {}, 1)).to eq 4
expect(field.to_graphql.complexity.call({}, { labelName: 'foo' }, 1)).to eq 8
diff --git a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
index 395e08081d3..20e197e9f73 100644
--- a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
@@ -57,7 +57,7 @@ describe Resolvers::NamespaceProjectsResolver, :nested_groups do
end
it 'has an high complexity regardless of arguments' do
- field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: described_class, null: false, max_page_size: 100)
+ field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
expect(field.to_graphql.complexity.call({}, {}, 1)).to eq 24
expect(field.to_graphql.complexity.call({}, { include_subgroups: true }, 1)).to eq 24
diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb
index a7fb156d9a8..0d3c3e37daf 100644
--- a/spec/graphql/types/base_field_spec.rb
+++ b/spec/graphql/types/base_field_spec.rb
@@ -28,18 +28,29 @@ describe Types::BaseField do
expect(field.to_graphql.complexity).to eq 12
end
- it 'sets complexity depending on arguments for resolvers' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, max_page_size: 100, null: true)
+ context 'when field has a resolver proc' do
+ context 'and is a connection' do
+ let(:field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: resolver, max_page_size: 100, null: true) }
- expect(field.to_graphql.complexity.call({}, {}, 2)).to eq 4
- expect(field.to_graphql.complexity.call({}, { first: 50 }, 2)).to eq 3
- end
+ it 'sets complexity depending on arguments for resolvers' do
+ expect(field.to_graphql.complexity.call({}, {}, 2)).to eq 4
+ expect(field.to_graphql.complexity.call({}, { first: 50 }, 2)).to eq 3
+ end
- it 'sets complexity depending on number load limits for resolvers' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, max_page_size: 100, null: true)
+ it 'sets complexity depending on number load limits for resolvers' do
+ expect(field.to_graphql.complexity.call({}, { first: 1 }, 2)).to eq 2
+ expect(field.to_graphql.complexity.call({}, { first: 1, foo: true }, 2)).to eq 4
+ end
+ end
- expect(field.to_graphql.complexity.call({}, { first: 1 }, 2)).to eq 2
- expect(field.to_graphql.complexity.call({}, { first: 1, foo: true }, 2)).to eq 4
+ context 'and is not a connection' do
+ it 'sets complexity as normal' do
+ field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, max_page_size: 100, null: true)
+
+ expect(field.to_graphql.complexity.call({}, {}, 2)).to eq 2
+ expect(field.to_graphql.complexity.call({}, { first: 50 }, 2)).to eq 2
+ end
+ end
end
end
end
diff --git a/spec/graphql/types/tree/blob_type_spec.rb b/spec/graphql/types/tree/blob_type_spec.rb
index b12e214ca84..22c11aff90a 100644
--- a/spec/graphql/types/tree/blob_type_spec.rb
+++ b/spec/graphql/types/tree/blob_type_spec.rb
@@ -5,5 +5,5 @@ require 'spec_helper'
describe Types::Tree::BlobType do
it { expect(described_class.graphql_name).to eq('Blob') }
- it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path, :web_url) }
+ it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path, :web_url, :lfs_oid) }
end
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index e1017130bed..13b708a03d5 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -7,8 +7,8 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import eventHub from '~/boards/eventhub';
-import '~/vue_shared/models/label';
-import '~/vue_shared/models/assignee';
+import '~/boards/models/label';
+import '~/boards/models/assignee';
import '~/boards/models/list';
import boardsStore from '~/boards/stores/boards_store';
import boardCard from '~/boards/components/board_card.vue';
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index b5559db8784..e81115e10c9 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -6,8 +6,8 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import Cookies from 'js-cookie';
-import '~/vue_shared/models/label';
-import '~/vue_shared/models/assignee';
+import '~/boards/models/label';
+import '~/boards/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import '~/boards/services/board_service';
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index a5bf97bdcc2..8a20911cc66 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -4,8 +4,8 @@
import Vue from 'vue';
-import '~/vue_shared/models/label';
-import '~/vue_shared/models/assignee';
+import '~/boards/models/label';
+import '~/boards/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import IssueCardInner from '~/boards/components/issue_card_inner.vue';
diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js
index e4ff3eb381f..bb7abe52eae 100644
--- a/spec/javascripts/boards/issue_spec.js
+++ b/spec/javascripts/boards/issue_spec.js
@@ -1,8 +1,8 @@
/* global ListIssue */
import Vue from 'vue';
-import '~/vue_shared/models/label';
-import '~/vue_shared/models/assignee';
+import '~/boards/models/label';
+import '~/boards/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import '~/boards/services/board_service';
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index bb6fc6c693d..15c9ff6dfb4 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -4,8 +4,8 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import _ from 'underscore';
-import '~/vue_shared/models/label';
-import '~/vue_shared/models/assignee';
+import '~/boards/models/label';
+import '~/boards/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import '~/boards/services/board_service';
diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
index 2839922fbd3..e6a969bd855 100644
--- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
@@ -32,6 +32,7 @@ describe('AjaxFormVariableList', () => {
saveButton,
errorBox,
saveEndpoint: container.dataset.saveEndpoint,
+ maskableRegex: container.dataset.maskableRegex,
});
spyOn(ajaxVariableList, 'updateRowsWithPersistedVariables').and.callThrough();
@@ -220,4 +221,11 @@ describe('AjaxFormVariableList', () => {
expect(row.dataset.isPersisted).toEqual('true');
});
});
+
+ describe('maskableRegex', () => {
+ it('takes in the regex provided by the data attribute', () => {
+ expect(container.dataset.maskableRegex).toBe('^[a-zA-Z0-9_+=/-]{8,}$');
+ expect(ajaxVariableList.maskableRegex).toBe(container.dataset.maskableRegex);
+ });
+ });
});
diff --git a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
index 394e60fc22c..064113e879a 100644
--- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
@@ -150,6 +150,65 @@ describe('VariableList', () => {
.then(done)
.catch(done.fail);
});
+
+ describe('validateMaskability', () => {
+ let $row;
+
+ const maskingErrorElement = '.js-row:last-child .masking-validation-error';
+
+ beforeEach(() => {
+ $row = $wrapper.find('.js-row:last-child');
+ $row.find('.ci-variable-masked-item .js-project-feature-toggle').click();
+ });
+
+ it('has a regex provided via a data attribute', () => {
+ expect($wrapper.attr('data-maskable-regex')).toBe('^[a-zA-Z0-9_+=/-]{8,}$');
+ });
+
+ it('allows values that are 8 characters long', done => {
+ $row.find('.js-ci-variable-input-value').val('looooong');
+
+ getSetTimeoutPromise()
+ .then(() => {
+ expect($wrapper.find(maskingErrorElement)).toHaveClass('hide');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('rejects values that are shorter than 8 characters', done => {
+ $row.find('.js-ci-variable-input-value').val('short');
+
+ getSetTimeoutPromise()
+ .then(() => {
+ expect($wrapper.find(maskingErrorElement)).toBeVisible();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('allows values with base 64 characters', done => {
+ $row.find('.js-ci-variable-input-value').val('abcABC123_+=/-');
+
+ getSetTimeoutPromise()
+ .then(() => {
+ expect($wrapper.find(maskingErrorElement)).toHaveClass('hide');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('rejects values with other special characters', done => {
+ $row.find('.js-ci-variable-input-value').val('1234567$');
+
+ getSetTimeoutPromise()
+ .then(() => {
+ expect($wrapper.find(maskingErrorElement)).toBeVisible();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
describe('toggleEnableRow method', () => {
diff --git a/spec/javascripts/diffs/components/commit_item_spec.js b/spec/javascripts/diffs/components/commit_item_spec.js
index cfe0c4bad71..dc3fb16eb40 100644
--- a/spec/javascripts/diffs/components/commit_item_spec.js
+++ b/spec/javascripts/diffs/components/commit_item_spec.js
@@ -18,7 +18,7 @@ const getDescExpandElement = vm =>
vm.$el.querySelector('.commit-content .text-expander.js-toggle-button');
const getShaElement = vm => vm.$el.querySelector('.commit-sha-group');
const getAvatarElement = vm => vm.$el.querySelector('.user-avatar-link');
-const getCommitterElement = vm => vm.$el.querySelector('.commiter');
+const getCommitterElement = vm => vm.$el.querySelector('.committer');
const getCommitActionsElement = vm => vm.$el.querySelector('.commit-actions');
describe('diffs/components/commit_item', () => {
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index e6fb08bcc49..dd2313dc800 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -719,4 +719,20 @@ describe('IDE store file actions', () => {
.catch(done.fail);
});
});
+
+ describe('triggerFilesChange', () => {
+ beforeEach(() => {
+ spyOn(eventHub, '$emit');
+ });
+
+ it('emits event that files have changed', done => {
+ store
+ .dispatch('triggerFilesChange')
+ .then(() => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('ide.files.change');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index 04e236fb042..37354283cab 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -488,7 +488,7 @@ describe('Multi-file store actions', () => {
'path',
store.state,
[{ type: types.DELETE_ENTRY, payload: 'path' }],
- [{ type: 'burstUnusedSeal' }],
+ [{ type: 'burstUnusedSeal' }, { type: 'triggerFilesChange' }],
done,
);
});
@@ -510,7 +510,7 @@ describe('Multi-file store actions', () => {
payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
},
],
- [{ type: 'deleteEntry', payload: 'test' }],
+ [{ type: 'deleteEntry', payload: 'test' }, { type: 'triggerFilesChange' }],
done,
);
});
@@ -558,6 +558,7 @@ describe('Multi-file store actions', () => {
},
},
{ type: 'deleteEntry', payload: 'test' },
+ { type: 'triggerFilesChange' },
],
done,
);
diff --git a/spec/javascripts/monitoring/charts/area_spec.js b/spec/javascripts/monitoring/charts/area_spec.js
index 56609665b88..ac7e0bb12a1 100644
--- a/spec/javascripts/monitoring/charts/area_spec.js
+++ b/spec/javascripts/monitoring/charts/area_spec.js
@@ -214,6 +214,20 @@ describe('Area component', () => {
});
});
+ describe('chartOptions', () => {
+ describe('yAxis formatter', () => {
+ let format;
+
+ beforeEach(() => {
+ format = areaChart.vm.chartOptions.yAxis.axisLabel.formatter;
+ });
+
+ it('rounds to 3 decimal places', () => {
+ expect(format(0.88888)).toBe('0.889');
+ });
+ });
+ });
+
describe('scatterSeries', () => {
it('utilizes deployment data', () => {
expect(areaChart.vm.scatterSeries.data).toEqual([
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index 80b9b740b94..f1d578648b8 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -49,9 +49,6 @@ describe('Dashboard', () => {
window.gon = {
...window.gon,
ee: false,
- features: {
- grafanaDashboardLink: true,
- },
};
store = createStore();
@@ -68,7 +65,7 @@ describe('Dashboard', () => {
it('shows a getting started empty state when no metrics are present', () => {
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, showTimeWindowDropdown: false },
+ propsData: { ...propsData },
store,
});
@@ -85,7 +82,7 @@ describe('Dashboard', () => {
it('shows up a loading state', done => {
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: false },
+ propsData: { ...propsData, hasMetrics: true },
store,
});
@@ -102,7 +99,6 @@ describe('Dashboard', () => {
...propsData,
hasMetrics: true,
showLegend: false,
- showTimeWindowDropdown: false,
},
store,
});
@@ -122,7 +118,6 @@ describe('Dashboard', () => {
...propsData,
hasMetrics: true,
showPanels: false,
- showTimeWindowDropdown: false,
},
store,
});
@@ -142,7 +137,6 @@ describe('Dashboard', () => {
...propsData,
hasMetrics: true,
showPanels: false,
- showTimeWindowDropdown: false,
},
store,
});
@@ -173,7 +167,6 @@ describe('Dashboard', () => {
...propsData,
hasMetrics: true,
showPanels: false,
- showTimeWindowDropdown: false,
},
store,
});
@@ -203,7 +196,6 @@ describe('Dashboard', () => {
...propsData,
hasMetrics: true,
showPanels: false,
- showTimeWindowDropdown: false,
},
store,
});
@@ -237,7 +229,6 @@ describe('Dashboard', () => {
hasMetrics: true,
showPanels: false,
environmentsEndpoint: '',
- showTimeWindowDropdown: false,
},
store,
});
@@ -250,27 +241,6 @@ describe('Dashboard', () => {
});
});
- it('does not show the time window dropdown when the feature flag is not set', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- showTimeWindowDropdown: false,
- },
- store,
- });
-
- setTimeout(() => {
- const timeWindowDropdown = component.$el.querySelector('.js-time-window-dropdown');
-
- expect(timeWindowDropdown).toBeNull();
-
- done();
- });
- });
-
it('renders the time window dropdown with a set of options', done => {
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
@@ -278,7 +248,6 @@ describe('Dashboard', () => {
...propsData,
hasMetrics: true,
showPanels: false,
- showTimeWindowDropdown: true,
},
store,
});
@@ -304,7 +273,6 @@ describe('Dashboard', () => {
...propsData,
hasMetrics: true,
showPanels: false,
- showTimeWindowDropdown: true,
},
store,
});
@@ -338,14 +306,12 @@ describe('Dashboard', () => {
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: true },
+ propsData: { ...propsData, hasMetrics: true },
store,
});
setTimeout(() => {
- const selectedTimeWindow = component.$el.querySelector(
- '.js-time-window-dropdown [active="true"]',
- );
+ const selectedTimeWindow = component.$el.querySelector('.js-time-window-dropdown .active');
expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes');
done();
@@ -359,7 +325,7 @@ describe('Dashboard', () => {
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: true },
+ propsData: { ...propsData, hasMetrics: true },
store,
});
@@ -388,7 +354,6 @@ describe('Dashboard', () => {
...propsData,
hasMetrics: true,
showPanels: false,
- showTimeWindowDropdown: false,
},
store,
});
@@ -414,54 +379,26 @@ describe('Dashboard', () => {
describe('external dashboard link', () => {
beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- });
-
- describe('with feature flag enabled', () => {
- beforeEach(() => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- showTimeWindowDropdown: false,
- externalDashboardUrl: '/mockUrl',
- },
- store,
- });
- });
- it('shows the link', done => {
- setTimeout(() => {
- expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain(
- 'View full dashboard',
- );
- done();
- });
+ component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: false,
+ externalDashboardUrl: '/mockUrl',
+ },
+ store,
});
});
- describe('without feature flage enabled', () => {
- beforeEach(() => {
- window.gon.features.grafanaDashboardLink = false;
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- showTimeWindowDropdown: false,
- externalDashboardUrl: '',
- },
- store,
- });
- });
-
- it('does not show the link', done => {
- setTimeout(() => {
- expect(component.$el.querySelector('.js-external-dashboard-link')).toBe(null);
- done();
- });
+ it('shows the link', done => {
+ setTimeout(() => {
+ expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain(
+ 'View full dashboard',
+ );
+ done();
});
});
});
diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index d9d8cb66749..82e42fe9ade 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -857,3 +857,68 @@ export const environmentData = [
updated_at: '2018-07-04T18:44:54.010Z',
},
];
+
+export const metricsDashboardResponse = {
+ dashboard: {
+ dashboard: 'Environment metrics',
+ priority: 1,
+ panel_groups: [
+ {
+ group: 'System metrics (Kubernetes)',
+ priority: 5,
+ panels: [
+ {
+ title: 'Memory Usage (Total)',
+ type: 'area-chart',
+ y_label: 'Total Memory Used',
+ weight: 4,
+ metrics: [
+ {
+ id: 'system_metrics_kubernetes_container_memory_total',
+ query_range:
+ 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024',
+ label: 'Total',
+ unit: 'GB',
+ metric_id: 12,
+ prometheus_endpoint_path: 'http://test',
+ },
+ ],
+ },
+ {
+ title: 'Core Usage (Total)',
+ type: 'area-chart',
+ y_label: 'Total Cores',
+ weight: 3,
+ metrics: [
+ {
+ id: 'system_metrics_kubernetes_container_cores_total',
+ query_range:
+ 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job)',
+ label: 'Total',
+ unit: 'cores',
+ metric_id: 13,
+ },
+ ],
+ },
+ {
+ title: 'Memory Usage (Pod average)',
+ type: 'area-chart',
+ y_label: 'Memory Used per Pod',
+ weight: 2,
+ metrics: [
+ {
+ id: 'system_metrics_kubernetes_container_memory_average',
+ query_range:
+ 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024',
+ label: 'Pod average',
+ unit: 'MB',
+ metric_id: 14,
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ status: 'success',
+};
diff --git a/spec/javascripts/monitoring/store/actions_spec.js b/spec/javascripts/monitoring/store/actions_spec.js
index a848cd24fe3..8c02e21eda2 100644
--- a/spec/javascripts/monitoring/store/actions_spec.js
+++ b/spec/javascripts/monitoring/store/actions_spec.js
@@ -3,8 +3,13 @@ import MockAdapter from 'axios-mock-adapter';
import store from '~/monitoring/stores';
import * as types from '~/monitoring/stores/mutation_types';
import {
+ fetchDashboard,
+ receiveMetricsDashboardSuccess,
+ receiveMetricsDashboardFailure,
fetchDeploymentsData,
fetchEnvironmentsData,
+ fetchPrometheusMetrics,
+ fetchPrometheusMetric,
requestMetricsData,
setEndpoints,
setGettingStartedEmptyState,
@@ -12,7 +17,12 @@ import {
import storeState from '~/monitoring/stores/state';
import testAction from 'spec/helpers/vuex_action_helper';
import { resetStore } from '../helpers';
-import { deploymentData, environmentData } from '../mock_data';
+import {
+ deploymentData,
+ environmentData,
+ metricsDashboardResponse,
+ metricsGroupsAPIResponse,
+} from '../mock_data';
describe('Monitoring store actions', () => {
let mock;
@@ -155,4 +165,158 @@ describe('Monitoring store actions', () => {
);
});
});
+
+ describe('fetchDashboard', () => {
+ let dispatch;
+ let state;
+ const response = metricsDashboardResponse;
+
+ beforeEach(() => {
+ dispatch = jasmine.createSpy();
+ state = storeState();
+ state.dashboardEndpoint = '/dashboard';
+ });
+
+ it('dispatches receive and success actions', done => {
+ const params = {};
+ mock.onGet(state.dashboardEndpoint).reply(200, response);
+
+ fetchDashboard({ state, dispatch }, params)
+ .then(() => {
+ expect(dispatch).toHaveBeenCalledWith('requestMetricsDashboard');
+ expect(dispatch).toHaveBeenCalledWith('receiveMetricsDashboardSuccess', {
+ response,
+ params,
+ });
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('dispatches failure action', done => {
+ const params = {};
+ mock.onGet(state.dashboardEndpoint).reply(500);
+
+ fetchDashboard({ state, dispatch }, params)
+ .then(() => {
+ expect(dispatch).toHaveBeenCalledWith(
+ 'receiveMetricsDashboardFailure',
+ new Error('Request failed with status code 500'),
+ );
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('receiveMetricsDashboardSuccess', () => {
+ let commit;
+ let dispatch;
+
+ beforeEach(() => {
+ commit = jasmine.createSpy();
+ dispatch = jasmine.createSpy();
+ });
+
+ it('stores groups ', () => {
+ const params = {};
+ const response = metricsDashboardResponse;
+
+ receiveMetricsDashboardSuccess({ commit, dispatch }, { response, params });
+
+ expect(commit).toHaveBeenCalledWith(
+ types.RECEIVE_METRICS_DATA_SUCCESS,
+ metricsDashboardResponse.dashboard.panel_groups,
+ );
+
+ expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetrics', params);
+ });
+ });
+
+ describe('receiveMetricsDashboardFailure', () => {
+ let commit;
+
+ beforeEach(() => {
+ commit = jasmine.createSpy();
+ });
+
+ it('commits failure action', () => {
+ receiveMetricsDashboardFailure({ commit });
+
+ expect(commit).toHaveBeenCalledWith(types.RECEIVE_METRICS_DATA_FAILURE, undefined);
+ });
+
+ it('commits failure action with error', () => {
+ receiveMetricsDashboardFailure({ commit }, 'uh-oh');
+
+ expect(commit).toHaveBeenCalledWith(types.RECEIVE_METRICS_DATA_FAILURE, 'uh-oh');
+ });
+ });
+
+ describe('fetchPrometheusMetrics', () => {
+ let commit;
+ let dispatch;
+
+ beforeEach(() => {
+ commit = jasmine.createSpy();
+ dispatch = jasmine.createSpy();
+ });
+
+ it('commits empty state when state.groups is empty', done => {
+ const state = storeState();
+ const params = {};
+
+ fetchPrometheusMetrics({ state, commit, dispatch }, params)
+ .then(() => {
+ expect(commit).toHaveBeenCalledWith(types.SET_NO_DATA_EMPTY_STATE);
+ expect(dispatch).not.toHaveBeenCalled();
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('dispatches fetchPrometheusMetric for each panel query', done => {
+ const params = {};
+ const state = storeState();
+ state.groups = metricsDashboardResponse.dashboard.panel_groups;
+
+ const metric = state.groups[0].panels[0].metrics[0];
+
+ fetchPrometheusMetrics({ state, commit, dispatch }, params)
+ .then(() => {
+ expect(dispatch.calls.count()).toEqual(3);
+ expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetric', { metric, params });
+ done();
+ })
+ .catch(done.fail);
+
+ done();
+ });
+ });
+
+ describe('fetchPrometheusMetric', () => {
+ it('commits prometheus query result', done => {
+ const commit = jasmine.createSpy();
+ const params = {
+ start: '1557216349.469',
+ end: '1557218149.469',
+ };
+ const metric = metricsDashboardResponse.dashboard.panel_groups[0].panels[0].metrics[0];
+ const state = storeState();
+
+ const data = metricsGroupsAPIResponse.data[0].metrics[0].queries[0];
+ const response = { data };
+ mock.onGet('http://test').reply(200, response);
+
+ fetchPrometheusMetric({ state, commit }, { metric, params });
+
+ setTimeout(() => {
+ expect(commit).toHaveBeenCalledWith(types.SET_QUERY_RESULT, {
+ metricId: metric.metric_id,
+ result: data.result,
+ });
+ done();
+ });
+ });
+ });
});
diff --git a/spec/javascripts/monitoring/store/mutations_spec.js b/spec/javascripts/monitoring/store/mutations_spec.js
index 882ee1dec14..02ff5847b34 100644
--- a/spec/javascripts/monitoring/store/mutations_spec.js
+++ b/spec/javascripts/monitoring/store/mutations_spec.js
@@ -1,7 +1,7 @@
import mutations from '~/monitoring/stores/mutations';
import * as types from '~/monitoring/stores/mutation_types';
import state from '~/monitoring/stores/state';
-import { metricsGroupsAPIResponse, deploymentData } from '../mock_data';
+import { metricsGroupsAPIResponse, deploymentData, metricsDashboardResponse } from '../mock_data';
describe('Monitoring mutations', () => {
let stateCopy;
@@ -11,14 +11,16 @@ describe('Monitoring mutations', () => {
});
describe(types.RECEIVE_METRICS_DATA_SUCCESS, () => {
+ let groups;
+
beforeEach(() => {
stateCopy.groups = [];
- const groups = metricsGroupsAPIResponse.data;
-
- mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups);
+ groups = metricsGroupsAPIResponse.data;
});
it('normalizes values', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups);
+
const expectedTimestamp = '2017-05-25T08:22:34.925Z';
const expectedValue = 0.0010794445585559514;
const [timestamp, value] = stateCopy.groups[0].metrics[0].queries[0].result[0].values[0];
@@ -28,22 +30,27 @@ describe('Monitoring mutations', () => {
});
it('contains two groups that contains, one of which has two queries sorted by priority', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups);
+
expect(stateCopy.groups).toBeDefined();
expect(stateCopy.groups.length).toEqual(2);
expect(stateCopy.groups[0].metrics.length).toEqual(2);
});
it('assigns queries a metric id', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups);
+
expect(stateCopy.groups[1].metrics[0].queries[0].metricId).toEqual('100');
});
it('removes the data if all the values from a query are not defined', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups);
+
expect(stateCopy.groups[1].metrics[0].queries[0].result.length).toEqual(0);
});
it('assigns metric id of null if metric has no id', () => {
stateCopy.groups = [];
- const groups = metricsGroupsAPIResponse.data;
const noId = groups.map(group => ({
...group,
...{
@@ -63,6 +70,26 @@ describe('Monitoring mutations', () => {
});
});
});
+
+ describe('dashboard endpoint enabled', () => {
+ const dashboardGroups = metricsDashboardResponse.dashboard.panel_groups;
+
+ beforeEach(() => {
+ stateCopy.useDashboardEndpoint = true;
+ });
+
+ it('aliases group panels to metrics for backwards compatibility', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, dashboardGroups);
+
+ expect(stateCopy.groups[0].metrics[0]).toBeDefined();
+ });
+
+ it('aliases panel metrics to queries for backwards compatibility', () => {
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, dashboardGroups);
+
+ expect(stateCopy.groups[0].metrics[0].queries).toBeDefined();
+ });
+ });
});
describe(types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS, () => {
@@ -82,11 +109,51 @@ describe('Monitoring mutations', () => {
metricsEndpoint: 'additional_metrics.json',
environmentsEndpoint: 'environments.json',
deploymentsEndpoint: 'deployments.json',
+ dashboardEndpoint: 'dashboard.json',
});
expect(stateCopy.metricsEndpoint).toEqual('additional_metrics.json');
expect(stateCopy.environmentsEndpoint).toEqual('environments.json');
expect(stateCopy.deploymentsEndpoint).toEqual('deployments.json');
+ expect(stateCopy.dashboardEndpoint).toEqual('dashboard.json');
+ });
+ });
+
+ describe('SET_QUERY_RESULT', () => {
+ const metricId = 12;
+ const result = [{ values: [[0, 1], [1, 1], [1, 3]] }];
+
+ beforeEach(() => {
+ stateCopy.useDashboardEndpoint = true;
+ const dashboardGroups = metricsDashboardResponse.dashboard.panel_groups;
+ mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, dashboardGroups);
+ });
+
+ it('clears empty state', () => {
+ mutations[types.SET_QUERY_RESULT](stateCopy, {
+ metricId,
+ result,
+ });
+
+ expect(stateCopy.showEmptyState).toBe(false);
+ });
+
+ it('sets metricsWithData value', () => {
+ mutations[types.SET_QUERY_RESULT](stateCopy, {
+ metricId,
+ result,
+ });
+
+ expect(stateCopy.metricsWithData).toEqual([12]);
+ });
+
+ it('does not store empty results', () => {
+ mutations[types.SET_QUERY_RESULT](stateCopy, {
+ metricId,
+ result: [],
+ });
+
+ expect(stateCopy.metricsWithData).toEqual([]);
});
});
});
diff --git a/spec/javascripts/notes/components/note_actions_spec.js b/spec/javascripts/notes/components/note_actions_spec.js
index 2159e4ddf16..1f2c07385a7 100644
--- a/spec/javascripts/notes/components/note_actions_spec.js
+++ b/spec/javascripts/notes/components/note_actions_spec.js
@@ -66,7 +66,7 @@ describe('noteActions', () => {
expect(wrapper.find('.js-note-edit').exists()).toBe(true);
});
- it('should be possible to report abuse to GitLab', () => {
+ it('should be possible to report abuse to admin', () => {
expect(wrapper.find(`a[href="${props.reportAbusePath}"]`).exists()).toBe(true);
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js
index 8e0415b813b..2ea8c169add 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_enabled_spec.js
@@ -1,17 +1,19 @@
import Vue from 'vue';
-import mwpsComponent from '~/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue';
+import autoMergeEnabledComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
import eventHub from '~/vue_merge_request_widget/event_hub';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { trimText } from 'spec/helpers/text_helper';
+import { MWPS_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
-describe('MRWidgetMergeWhenPipelineSucceeds', () => {
+describe('MRWidgetAutoMergeEnabled', () => {
let vm;
const targetBranchPath = '/foo/bar';
const targetBranch = 'foo';
const sha = '1EA2EZ34';
beforeEach(() => {
- const Component = Vue.extend(mwpsComponent);
+ const Component = Vue.extend(autoMergeEnabledComponent);
spyOn(eventHub, '$emit');
vm = mountComponent(Component, {
@@ -25,6 +27,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
sha,
targetBranchPath,
targetBranch,
+ autoMergeStrategy: MWPS_MERGE_STRATEGY,
},
service: new MRWidgetService({}),
});
@@ -66,6 +69,32 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
expect(vm.canRemoveSourceBranch).toBeFalsy();
});
});
+
+ describe('statusTextBeforeAuthor', () => {
+ it('should return "Set by" if the MWPS is selected', () => {
+ Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
+
+ expect(vm.statusTextBeforeAuthor).toBe('Set by');
+ });
+ });
+
+ describe('statusTextAfterAuthor', () => {
+ it('should return "to be merged automatically..." if MWPS is selected', () => {
+ Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
+
+ expect(vm.statusTextAfterAuthor).toBe(
+ 'to be merged automatically when the pipeline succeeds',
+ );
+ });
+ });
+
+ describe('cancelButtonText', () => {
+ it('should return "Cancel automatic merge" if MWPS is selected', () => {
+ Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
+
+ expect(vm.cancelButtonText).toBe('Cancel automatic merge');
+ });
+ });
});
describe('methods', () => {
@@ -96,7 +125,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
spyOn(vm.service, 'merge').and.returnValue(
Promise.resolve({
data: {
- status: 'merge_when_pipeline_succeeds',
+ status: MWPS_MERGE_STRATEGY,
},
}),
);
@@ -106,7 +135,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
expect(vm.service.merge).toHaveBeenCalledWith({
sha,
- auto_merge_strategy: 'merge_when_pipeline_succeeds',
+ auto_merge_strategy: MWPS_MERGE_STRATEGY,
should_remove_source_branch: true,
});
done();
@@ -119,6 +148,7 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
it('should have correct elements', () => {
expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
expect(vm.$el.innerText).toContain('to be merged automatically when the pipeline succeeds');
+
expect(vm.$el.innerText).toContain('The changes will be merged into');
expect(vm.$el.innerText).toContain(targetBranch);
expect(vm.$el.innerText).toContain('The source branch will not be deleted');
@@ -174,5 +204,27 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => {
done();
});
});
+
+ it('should render the status text as "...to merged automatically" if MWPS is selected', done => {
+ Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
+
+ Vue.nextTick(() => {
+ const statusText = trimText(vm.$el.querySelector('.js-status-text-after-author').innerText);
+
+ expect(statusText).toBe('to be merged automatically when the pipeline succeeds');
+ done();
+ });
+ });
+
+ it('should render the cancel button as "Cancel automatic merge" if MWPS is selected', done => {
+ Vue.set(vm.mr, 'autoMergeStrategy', MWPS_MERGE_STRATEGY);
+
+ Vue.nextTick(() => {
+ const cancelButtonText = trimText(vm.$el.querySelector('.js-cancel-auto-merge').innerText);
+
+ expect(cancelButtonText).toBe('Cancel automatic merge');
+ done();
+ });
+ });
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
index 39b879612ae..55073f5537c 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
@@ -23,11 +23,78 @@ describe('MRWidgetConflicts', () => {
vm.destroy();
});
- describe('when allowed to merge', () => {
+ // There are two permissions we need to consider:
+ //
+ // 1. Is the user allowed to merge to the target branch?
+ // 2. Is the user allowed to push to the source branch?
+ //
+ // This yields 4 possible permutations that we need to test, and
+ // we test them below. A user who can push to the source
+ // branch should be allowed to resolve conflicts. This is
+ // consistent with what the backend does.
+ describe('when allowed to merge but not allowed to push to source branch', () => {
beforeEach(() => {
createComponent({
mr: {
canMerge: true,
+ canPushToSourceBranch: false,
+ conflictResolutionPath: path,
+ conflictsDocsPath: '',
+ },
+ });
+ });
+
+ it('should tell you about conflicts without bothering other people', () => {
+ expect(vm.text()).toContain('There are merge conflicts');
+ expect(vm.text()).not.toContain('ask someone with write access');
+ });
+
+ it('should not allow you to resolve the conflicts', () => {
+ expect(vm.text()).not.toContain('Resolve conflicts');
+ });
+
+ it('should have merge buttons', () => {
+ const mergeLocallyButton = vm.find('.js-merge-locally-button');
+
+ expect(mergeLocallyButton.text()).toContain('Merge locally');
+ });
+ });
+
+ describe('when not allowed to merge but allowed to push to source branch', () => {
+ beforeEach(() => {
+ createComponent({
+ mr: {
+ canMerge: false,
+ canPushToSourceBranch: true,
+ conflictResolutionPath: path,
+ conflictsDocsPath: '',
+ },
+ });
+ });
+
+ it('should tell you about conflicts', () => {
+ expect(vm.text()).toContain('There are merge conflicts');
+ expect(vm.text()).toContain('ask someone with write access');
+ });
+
+ it('should allow you to resolve the conflicts', () => {
+ const resolveButton = vm.find('.js-resolve-conflicts-button');
+
+ expect(resolveButton.text()).toContain('Resolve conflicts');
+ expect(resolveButton.attributes('href')).toEqual(path);
+ });
+
+ it('should not have merge buttons', () => {
+ expect(vm.text()).not.toContain('Merge locally');
+ });
+ });
+
+ describe('when allowed to merge and push to source branch', () => {
+ beforeEach(() => {
+ createComponent({
+ mr: {
+ canMerge: true,
+ canPushToSourceBranch: true,
conflictResolutionPath: path,
conflictsDocsPath: '',
},
@@ -53,11 +120,12 @@ describe('MRWidgetConflicts', () => {
});
});
- describe('when user does not have permission to merge', () => {
+ describe('when user does not have permission to push to source branch', () => {
it('should show proper message', () => {
createComponent({
mr: {
canMerge: false,
+ canPushToSourceBranch: false,
conflictsDocsPath: '',
},
});
@@ -74,6 +142,7 @@ describe('MRWidgetConflicts', () => {
createComponent({
mr: {
canMerge: false,
+ canPushToSourceBranch: false,
conflictsDocsPath: '',
},
});
@@ -115,6 +184,7 @@ describe('MRWidgetConflicts', () => {
createComponent({
mr: {
canMerge: true,
+ canPushToSourceBranch: true,
conflictResolutionPath: gl.TEST_HOST,
sourceBranchProtected: true,
conflictsDocsPath: '',
@@ -136,6 +206,7 @@ describe('MRWidgetConflicts', () => {
createComponent({
mr: {
canMerge: true,
+ canPushToSourceBranch: true,
conflictResolutionPath: gl.TEST_HOST,
sourceBranchProtected: false,
conflictsDocsPath: '',
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index 3ae773b6ccb..bb76616be56 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -6,6 +6,7 @@ import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit
import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { MWPS_MERGE_STRATEGY, ATMTWPS_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
const commitMessage = 'This is the commit message';
const squashCommitMessage = 'This is the squash commit message';
@@ -29,6 +30,8 @@ const createTestMr = customConfig => {
shouldRemoveSourceBranch: true,
canRemoveSourceBranch: false,
targetBranch: 'master',
+ preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY,
+ availableAutoMergeStrategies: [MWPS_MERGE_STRATEGY],
};
Object.assign(mr, customConfig.mr);
@@ -80,7 +83,6 @@ describe('ReadyToMerge', () => {
it('should have default data', () => {
expect(vm.mergeWhenBuildSucceeds).toBeFalsy();
expect(vm.useCommitMessageWithDescription).toBeFalsy();
- expect(vm.autoMergeStrategy).toBeUndefined();
expect(vm.showCommitMessageEditor).toBeFalsy();
expect(vm.isMakingRequest).toBeFalsy();
expect(vm.isMergingImmediately).toBeFalsy();
@@ -91,47 +93,51 @@ describe('ReadyToMerge', () => {
});
describe('computed', () => {
- describe('shouldShowAutoMergeText', () => {
- it('should return true with active pipeline', () => {
- vm.mr.isPipelineActive = true;
+ describe('isAutoMergeAvailable', () => {
+ it('should return true when at least one merge strategy is available', () => {
+ vm.mr.availableAutoMergeStrategies = [MWPS_MERGE_STRATEGY];
- expect(vm.shouldShowAutoMergeText).toBeTruthy();
+ expect(vm.isAutoMergeAvailable).toBe(true);
});
- it('should return false with inactive pipeline', () => {
- vm.mr.isPipelineActive = false;
+ it('should return false when no merge strategies are available', () => {
+ vm.mr.availableAutoMergeStrategies = [];
- expect(vm.shouldShowAutoMergeText).toBeFalsy();
+ expect(vm.isAutoMergeAvailable).toBe(false);
});
});
describe('status', () => {
it('defaults to success', () => {
- vm.mr.pipeline = true;
+ Vue.set(vm.mr, 'pipeline', true);
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
expect(vm.status).toEqual('success');
});
it('returns failed when MR has CI but also has an unknown status', () => {
- vm.mr.hasCI = true;
+ Vue.set(vm.mr, 'hasCI', true);
expect(vm.status).toEqual('failed');
});
it('returns default when MR has no pipeline', () => {
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+
expect(vm.status).toEqual('success');
});
it('returns pending when pipeline is active', () => {
- vm.mr.pipeline = {};
- vm.mr.isPipelineActive = true;
+ Vue.set(vm.mr, 'pipeline', {});
+ Vue.set(vm.mr, 'isPipelineActive', true);
expect(vm.status).toEqual('pending');
});
it('returns failed when pipeline is failed', () => {
- vm.mr.pipeline = {};
- vm.mr.isPipelineFailed = true;
+ Vue.set(vm.mr, 'pipeline', {});
+ Vue.set(vm.mr, 'isPipelineFailed', true);
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
expect(vm.status).toEqual('failed');
});
@@ -143,18 +149,20 @@ describe('ReadyToMerge', () => {
const inActionClass = `${defaultClass} btn-info`;
it('defaults to success class', () => {
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+
expect(vm.mergeButtonClass).toEqual(defaultClass);
});
it('returns success class for success status', () => {
- vm.mr.pipeline = true;
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+ Vue.set(vm.mr, 'pipeline', true);
expect(vm.mergeButtonClass).toEqual(defaultClass);
});
it('returns info class for pending status', () => {
- vm.mr.pipeline = {};
- vm.mr.isPipelineActive = true;
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', [ATMTWPS_MERGE_STRATEGY]);
expect(vm.mergeButtonClass).toEqual(inActionClass);
});
@@ -198,69 +206,82 @@ describe('ReadyToMerge', () => {
});
describe('mergeButtonText', () => {
- it('should return Merge', () => {
+ it('should return "Merge" when no auto merge strategies are available', () => {
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+
expect(vm.mergeButtonText).toEqual('Merge');
});
- it('should return Merge in progress', () => {
- vm.isMergingImmediately = true;
+ it('should return "Merge in progress"', () => {
+ Vue.set(vm, 'isMergingImmediately', true);
expect(vm.mergeButtonText).toEqual('Merge in progress');
});
- it('should return Merge when pipeline succeeds', () => {
- vm.isMergingImmediately = false;
- vm.mr.isPipelineActive = true;
+ it('should return "Merge when pipeline succeeds" when the MWPS auto merge strategy is available', () => {
+ Vue.set(vm, 'isMergingImmediately', false);
+ Vue.set(vm.mr, 'preferredAutoMergeStrategy', MWPS_MERGE_STRATEGY);
expect(vm.mergeButtonText).toEqual('Merge when pipeline succeeds');
});
});
+ describe('autoMergeText', () => {
+ it('should return Merge when pipeline succeeds', () => {
+ Vue.set(vm.mr, 'preferredAutoMergeStrategy', MWPS_MERGE_STRATEGY);
+
+ expect(vm.autoMergeText).toEqual('Merge when pipeline succeeds');
+ });
+ });
+
describe('shouldShowMergeOptionsDropdown', () => {
- it('should return false with initial data', () => {
- expect(vm.shouldShowMergeOptionsDropdown).toBeFalsy();
+ it('should return false when no auto merge strategies are available', () => {
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+
+ expect(vm.shouldShowMergeOptionsDropdown).toBe(false);
});
- it('should return true when pipeline active', () => {
- vm.mr.isPipelineActive = true;
+ it('should return true when at least one auto merge strategy is available', () => {
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', [ATMTWPS_MERGE_STRATEGY]);
- expect(vm.shouldShowMergeOptionsDropdown).toBeTruthy();
+ expect(vm.shouldShowMergeOptionsDropdown).toBe(true);
});
it('should return false when pipeline active but only merge when pipeline succeeds set in project options', () => {
- vm.mr.isPipelineActive = true;
- vm.mr.onlyAllowMergeIfPipelineSucceeds = true;
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', [ATMTWPS_MERGE_STRATEGY]);
+ Vue.set(vm.mr, 'onlyAllowMergeIfPipelineSucceeds', true);
- expect(vm.shouldShowMergeOptionsDropdown).toBeFalsy();
+ expect(vm.shouldShowMergeOptionsDropdown).toBe(false);
});
});
describe('isMergeButtonDisabled', () => {
it('should return false with initial data', () => {
- vm.mr.isMergeAllowed = true;
+ Vue.set(vm.mr, 'isMergeAllowed', true);
- expect(vm.isMergeButtonDisabled).toBeFalsy();
+ expect(vm.isMergeButtonDisabled).toBe(false);
});
it('should return true when there is no commit message', () => {
- vm.mr.isMergeAllowed = true;
- vm.commitMessage = '';
+ Vue.set(vm.mr, 'isMergeAllowed', true);
+ Vue.set(vm, 'commitMessage', '');
- expect(vm.isMergeButtonDisabled).toBeTruthy();
+ expect(vm.isMergeButtonDisabled).toBe(true);
});
it('should return true if merge is not allowed', () => {
- vm.mr.isMergeAllowed = false;
- vm.mr.onlyAllowMergeIfPipelineSucceeds = true;
+ Vue.set(vm.mr, 'isMergeAllowed', false);
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+ Vue.set(vm.mr, 'onlyAllowMergeIfPipelineSucceeds', true);
- expect(vm.isMergeButtonDisabled).toBeTruthy();
+ expect(vm.isMergeButtonDisabled).toBe(true);
});
it('should return true when the vm instance is making request', () => {
- vm.mr.isMergeAllowed = true;
- vm.isMakingRequest = true;
+ Vue.set(vm.mr, 'isMergeAllowed', true);
+ Vue.set(vm, 'isMakingRequest', true);
- expect(vm.isMergeButtonDisabled).toBeTruthy();
+ expect(vm.isMergeButtonDisabled).toBe(true);
});
});
});
@@ -268,31 +289,31 @@ describe('ReadyToMerge', () => {
describe('methods', () => {
describe('shouldShowMergeControls', () => {
it('should return false when an external pipeline is running and required to succeed', () => {
- vm.mr.isMergeAllowed = false;
- vm.mr.isPipelineActive = false;
+ Vue.set(vm.mr, 'isMergeAllowed', false);
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
- expect(vm.shouldShowMergeControls).toBeFalsy();
+ expect(vm.shouldShowMergeControls).toBe(false);
});
it('should return true when the build succeeded or build not required to succeed', () => {
- vm.mr.isMergeAllowed = true;
- vm.mr.isPipelineActive = false;
+ Vue.set(vm.mr, 'isMergeAllowed', true);
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
- expect(vm.shouldShowMergeControls).toBeTruthy();
+ expect(vm.shouldShowMergeControls).toBe(true);
});
it('should return true when showing the MWPS button and a pipeline is running that needs to be successful', () => {
- vm.mr.isMergeAllowed = false;
- vm.mr.isPipelineActive = true;
+ Vue.set(vm.mr, 'isMergeAllowed', false);
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', [MWPS_MERGE_STRATEGY]);
- expect(vm.shouldShowMergeControls).toBeTruthy();
+ expect(vm.shouldShowMergeControls).toBe(true);
});
it('should return true when showing the MWPS button but not required for the pipeline to succeed', () => {
- vm.mr.isMergeAllowed = true;
- vm.mr.isPipelineActive = true;
+ Vue.set(vm.mr, 'isMergeAllowed', true);
+ Vue.set(vm.mr, 'availableAutoMergeStrategies', [MWPS_MERGE_STRATEGY]);
- expect(vm.shouldShowMergeControls).toBeTruthy();
+ expect(vm.shouldShowMergeControls).toBe(true);
});
});
@@ -325,7 +346,6 @@ describe('ReadyToMerge', () => {
vm.handleMergeButtonClick(true);
setTimeout(() => {
- expect(vm.autoMergeStrategy).toBe('merge_when_pipeline_succeeds');
expect(vm.isMakingRequest).toBeTruthy();
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
@@ -349,14 +369,13 @@ describe('ReadyToMerge', () => {
vm.handleMergeButtonClick(false, true);
setTimeout(() => {
- expect(vm.autoMergeStrategy).toBeUndefined();
expect(vm.isMakingRequest).toBeTruthy();
expect(eventHub.$emit).toHaveBeenCalledWith('FailedToMerge', undefined);
const params = vm.service.merge.calls.argsFor(0)[0];
expect(params.should_remove_source_branch).toBeTruthy();
- expect(params.merge_when_pipeline_succeeds).toBeFalsy();
+ expect(params.auto_merge_strategy).toBeUndefined();
done();
}, 333);
});
@@ -367,14 +386,13 @@ describe('ReadyToMerge', () => {
vm.handleMergeButtonClick();
setTimeout(() => {
- expect(vm.autoMergeStrategy).toBeUndefined();
expect(vm.isMakingRequest).toBeTruthy();
expect(vm.initiateMergePolling).toHaveBeenCalled();
const params = vm.service.merge.calls.argsFor(0)[0];
expect(params.should_remove_source_branch).toBeTruthy();
- expect(params.merge_when_pipeline_succeeds).toBeFalsy();
+ expect(params.auto_merge_strategy).toBeUndefined();
done();
}, 333);
});
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index edbd0d54151..3c9a5cece90 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -25,7 +25,6 @@ export default {
},
merge_status: 'can_be_merged',
merge_user_id: null,
- merge_when_pipeline_succeeds: false,
source_branch: 'daaaa',
source_branch_link: 'daaaa',
source_project_id: 19,
@@ -210,8 +209,7 @@ export default {
source_branch_path: '/root/acets-app/branches/daaaa',
conflict_resolution_ui_path: '/root/acets-app/merge_requests/22/conflicts',
remove_wip_path: '/root/acets-app/merge_requests/22/remove_wip',
- cancel_merge_when_pipeline_succeeds_path:
- '/root/acets-app/merge_requests/22/cancel_merge_when_pipeline_succeeds',
+ cancel_auto_merge_path: '/root/acets-app/merge_requests/22/cancel_auto_merge',
create_issue_to_resolve_discussions_path:
'/root/acets-app/issues/new?merge_request_to_resolve_discussions_of=22',
merge_path: '/root/acets-app/merge_requests/22/merge',
@@ -237,6 +235,9 @@ export default {
merge_request_pipelines_docs_path: '/help/ci/merge_request_pipelines/index.md',
squash: true,
visual_review_app_available: true,
+ merge_trains_enabled: true,
+ merge_trains_count: 3,
+ merge_train_index: 1,
};
export const mockStore = {
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 918717c4547..08f7a17515e 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -222,60 +222,6 @@ describe('mrWidgetOptions', () => {
});
});
});
-
- describe('showTargetBranchAdvancedError', () => {
- describe(`when the pipeline's target_sha property doesn't exist`, () => {
- beforeEach(done => {
- Vue.set(vm.mr, 'isOpen', true);
- Vue.set(vm.mr.pipeline, 'target_sha', undefined);
- Vue.set(vm.mr, 'targetBranchSha', 'abcd');
- vm.$nextTick(done);
- });
-
- it('should be false', () => {
- expect(vm.showTargetBranchAdvancedError).toEqual(false);
- });
- });
-
- describe(`when the pipeline's target_sha matches the target branch's sha`, () => {
- beforeEach(done => {
- Vue.set(vm.mr, 'isOpen', true);
- Vue.set(vm.mr.pipeline, 'target_sha', 'abcd');
- Vue.set(vm.mr, 'targetBranchSha', 'abcd');
- vm.$nextTick(done);
- });
-
- it('should be false', () => {
- expect(vm.showTargetBranchAdvancedError).toEqual(false);
- });
- });
-
- describe(`when the merge request is not open`, () => {
- beforeEach(done => {
- Vue.set(vm.mr, 'isOpen', false);
- Vue.set(vm.mr.pipeline, 'target_sha', 'abcd');
- Vue.set(vm.mr, 'targetBranchSha', 'bcde');
- vm.$nextTick(done);
- });
-
- it('should be false', () => {
- expect(vm.showTargetBranchAdvancedError).toEqual(false);
- });
- });
-
- describe(`when the pipeline's target_sha does not match the target branch's sha`, () => {
- beforeEach(done => {
- Vue.set(vm.mr, 'isOpen', true);
- Vue.set(vm.mr.pipeline, 'target_sha', 'abcd');
- Vue.set(vm.mr, 'targetBranchSha', 'bcde');
- vm.$nextTick(done);
- });
-
- it('should be true', () => {
- expect(vm.showTargetBranchAdvancedError).toEqual(true);
- });
- });
- });
});
describe('methods', () => {
diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
index e2cd0f084fd..e27a506f426 100644
--- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
+++ b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
@@ -82,5 +82,47 @@ describe('MergeRequestStore', () => {
expect(store.isNothingToMergeState).toEqual(false);
});
});
+
+ describe('mergePipelinesEnabled', () => {
+ it('should set mergePipelinesEnabled = true when merge_pipelines_enabled is true', () => {
+ store.setData({ ...mockData, merge_pipelines_enabled: true });
+
+ expect(store.mergePipelinesEnabled).toBe(true);
+ });
+
+ it('should set mergePipelinesEnabled = false when merge_pipelines_enabled is not provided', () => {
+ store.setData({ ...mockData, merge_pipelines_enabled: undefined });
+
+ expect(store.mergePipelinesEnabled).toBe(false);
+ });
+ });
+
+ describe('mergeTrainsCount', () => {
+ it('should set mergeTrainsCount when merge_trains_count is provided', () => {
+ store.setData({ ...mockData, merge_trains_count: 3 });
+
+ expect(store.mergeTrainsCount).toBe(3);
+ });
+
+ it('should set mergeTrainsCount = 0 when merge_trains_count is not provided', () => {
+ store.setData({ ...mockData, merge_trains_count: undefined });
+
+ expect(store.mergeTrainsCount).toBe(0);
+ });
+ });
+
+ describe('mergeTrainIndex', () => {
+ it('should set mergeTrainIndex when merge_train_index is provided', () => {
+ store.setData({ ...mockData, merge_train_index: 3 });
+
+ expect(store.mergeTrainIndex).toBe(3);
+ });
+
+ it('should not set mergeTrainIndex when merge_train_index is not provided', () => {
+ store.setData({ ...mockData, merge_train_index: undefined });
+
+ expect(store.mergeTrainIndex).toBeUndefined();
+ });
+ });
});
});
diff --git a/spec/javascripts/vue_shared/components/pagination/graphql_pagination_spec.js b/spec/javascripts/vue_shared/components/pagination/graphql_pagination_spec.js
new file mode 100644
index 00000000000..7445da6cdee
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/pagination/graphql_pagination_spec.js
@@ -0,0 +1,70 @@
+import { shallowMount } from '@vue/test-utils';
+import GraphqlPagination from '~/vue_shared/components/pagination/graphql_pagination.vue';
+
+describe('Graphql Pagination component', () => {
+ let wrapper;
+ function factory({ hasNextPage = true, hasPreviousPage = true }) {
+ wrapper = shallowMount(GraphqlPagination, {
+ propsData: {
+ hasNextPage,
+ hasPreviousPage,
+ },
+ });
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('without previous page', () => {
+ beforeEach(() => {
+ factory({ hasPreviousPage: false });
+ });
+
+ it('renders disabled previous button', () => {
+ expect(wrapper.find('.js-prev-btn').attributes().disabled).toEqual('true');
+ });
+ });
+
+ describe('with previous page', () => {
+ beforeEach(() => {
+ factory({ hasPreviousPage: true });
+ });
+
+ it('renders enabled previous button', () => {
+ expect(wrapper.find('.js-prev-btn').attributes().disabled).toEqual(undefined);
+ });
+
+ it('emits previousClicked on click', () => {
+ wrapper.find('.js-prev-btn').vm.$emit('click');
+
+ expect(wrapper.emitted().previousClicked.length).toBe(1);
+ });
+ });
+
+ describe('without next page', () => {
+ beforeEach(() => {
+ factory({ hasNextPage: false });
+ });
+
+ it('renders disabled next button', () => {
+ expect(wrapper.find('.js-next-btn').attributes().disabled).toEqual('true');
+ });
+ });
+
+ describe('with next page', () => {
+ beforeEach(() => {
+ factory({ hasNextPage: true });
+ });
+
+ it('renders enabled next button', () => {
+ expect(wrapper.find('.js-next-btn').attributes().disabled).toEqual(undefined);
+ });
+
+ it('emits nextClicked on click', () => {
+ wrapper.find('.js-next-btn').vm.$emit('click');
+
+ expect(wrapper.emitted().nextClicked.length).toBe(1);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js
index 42cd41381dc..42abb4d83f0 100644
--- a/spec/javascripts/vue_shared/components/table_pagination_spec.js
+++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import paginationComp from '~/vue_shared/components/table_pagination.vue';
+import paginationComp from '~/vue_shared/components/pagination/table_pagination.vue';
describe('Pagination component', () => {
let component;
diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb
index 6e215ea1561..c788da55cd2 100644
--- a/spec/lib/api/helpers/pagination_spec.rb
+++ b/spec/lib/api/helpers/pagination_spec.rb
@@ -2,8 +2,12 @@ require 'spec_helper'
describe API::Helpers::Pagination do
let(:resource) { Project.all }
- let(:incoming_api_projects_url) { "#{Gitlab.config.gitlab.url}:8080/api/v4/projects" }
- let(:canonical_api_projects_url) { "#{Gitlab.config.gitlab.url}/api/v4/projects" }
+ let(:custom_port) { 8080 }
+ let(:incoming_api_projects_url) { "#{Gitlab.config.gitlab.url}:#{custom_port}/api/v4/projects" }
+
+ before do
+ stub_config_setting(port: custom_port)
+ end
subject do
Class.new.include(described_class).new
@@ -48,7 +52,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[1].id).to_query}")
+ expect_header('X-Next-Page', "#{incoming_api_projects_url}?#{query.merge(ks_prev_id: projects[1].id).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
@@ -71,7 +75,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[2].id).to_query}")
+ expect_header('X-Next-Page', "#{incoming_api_projects_url}?#{query.merge(ks_prev_id: projects[2].id).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
@@ -171,7 +175,7 @@ describe API::Helpers::Pagination do
it 'returns the right link to the next page' do
expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name).to_query}")
+ expect_header('X-Next-Page', "#{incoming_api_projects_url}?#{query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
end
@@ -224,9 +228,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).not_to include('rel="prev"')
end
@@ -290,8 +294,8 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).not_to include('rel="last"')
expect(val).not_to include('rel="prev"')
end
@@ -318,9 +322,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '1')
expect_header('Link', anything) do |_key, val|
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="prev"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="prev"))
expect(val).not_to include('rel="next"')
end
@@ -367,8 +371,8 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
- expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="last"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="last"))
expect(val).not_to include('rel="prev"')
expect(val).not_to include('rel="next"')
expect(val).not_to include('page=0')
diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index 43222ddb5e2..7c94cf37e32 100644
--- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -155,6 +155,13 @@ describe Banzai::Filter::ExternalIssueReferenceFilter do
it_behaves_like "external issue tracker"
end
+
+ context "with a lowercase prefix" do
+ let(:issue) { ExternalIssue.new("gl-030", project) }
+ let(:reference) { issue.to_reference }
+
+ it_behaves_like "external issue tracker"
+ end
end
context "jira project" do
diff --git a/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb b/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
index 91b0499375d..7119c826bca 100644
--- a/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
@@ -117,4 +117,27 @@ describe Banzai::Pipeline::GfmPipeline do
expect(output).not_to include("javascript")
end
end
+
+ describe 'emoji in references' do
+ set(:project) { create(:project, :public) }
+ let(:emoji) { '💯' }
+
+ it 'renders a label reference with emoji inside' do
+ create(:label, project: project, name: emoji)
+
+ output = described_class.to_html("#{Label.reference_prefix}\"#{emoji}\"", project: project)
+
+ expect(output).to include(emoji)
+ expect(output).to include(Gitlab::Routing.url_helpers.project_issues_path(project, label_name: emoji))
+ end
+
+ it 'renders a milestone reference with emoji inside' do
+ milestone = create(:milestone, project: project, title: emoji)
+
+ output = described_class.to_html("#{Milestone.reference_prefix}\"#{emoji}\"", project: project)
+
+ expect(output).to include(emoji)
+ expect(output).to include(Gitlab::Routing.url_helpers.milestone_path(milestone))
+ end
+ end
end
diff --git a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
index 27281333348..0a5b99d27e7 100644
--- a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
+++ b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
@@ -3,6 +3,12 @@ require 'spec_helper'
# rubocop:disable RSpec/FactoriesInMigrationSpecs
describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, :sidekiq, schema: 20180619121030 do
describe '#perform' do
+ before do
+ # This migration was created before we introduced ProjectCiCdSetting#default_git_depth
+ allow_any_instance_of(ProjectCiCdSetting).to receive(:default_git_depth=).and_return(0)
+ allow_any_instance_of(ProjectCiCdSetting).to receive(:default_git_depth).and_return(nil)
+ end
+
context 'when diff files can be deleted' do
let(:merge_request) { create(:merge_request, :merged) }
let!(:merge_request_diff) do
diff --git a/spec/lib/gitlab/background_migration/populate_external_pipeline_source_spec.rb b/spec/lib/gitlab/background_migration/populate_external_pipeline_source_spec.rb
index 3e009fed0f1..c6bc3db88a3 100644
--- a/spec/lib/gitlab/background_migration/populate_external_pipeline_source_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_external_pipeline_source_spec.rb
@@ -9,6 +9,9 @@ describe Gitlab::BackgroundMigration::PopulateExternalPipelineSource, :migration
before do
# This migration was created before we introduced metadata configs
stub_feature_flags(ci_build_metadata_config: false)
+ # This migration was created before we introduced ProjectCiCdSetting#default_git_depth
+ allow_any_instance_of(ProjectCiCdSetting).to receive(:default_git_depth).and_return(nil)
+ allow_any_instance_of(ProjectCiCdSetting).to receive(:default_git_depth=).and_return(0)
end
let!(:internal_pipeline) { create(:ci_pipeline, source: :web) }
diff --git a/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb b/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
new file mode 100644
index 00000000000..180520b27e7
--- /dev/null
+++ b/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Cluster::PumaWorkerKillerObserver do
+ let(:counter) { Gitlab::Metrics::NullMetric.instance }
+
+ before do
+ allow(Gitlab::Metrics).to receive(:counter)
+ .with(any_args)
+ .and_return(counter)
+ end
+
+ describe '#callback' do
+ subject { described_class.new }
+
+ it 'increments timeout counter' do
+ worker = double(index: 0)
+
+ expect(counter)
+ .to receive(:increment)
+ .with({ worker: 'worker_0' })
+
+ subject.callback.call(worker)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/rack_timeout_observer_spec.rb b/spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb
index 3dc1a8b68fb..68e5435450c 100644
--- a/spec/lib/gitlab/rack_timeout_observer_spec.rb
+++ b/spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Gitlab::RackTimeoutObserver do
+describe Gitlab::Cluster::RackTimeoutObserver do
let(:counter) { Gitlab::Metrics::NullMetric.instance }
before do
@@ -25,7 +25,7 @@ describe Gitlab::RackTimeoutObserver do
subject { described_class.new }
- it 'increments timeout counter' do
+ it 'increments counter' do
expect(counter)
.to receive(:increment)
.with({ controller: 'foo', action: 'bar', route: nil, state: :timed_out })
@@ -45,7 +45,7 @@ describe Gitlab::RackTimeoutObserver do
subject { described_class.new }
- it 'increments timeout counter' do
+ it 'increments counter' do
allow(endpoint).to receive_message_chain('route.pattern.origin') { 'foobar' }
expect(counter)
.to receive(:increment)
@@ -54,5 +54,24 @@ describe Gitlab::RackTimeoutObserver do
subject.callback.call(env)
end
end
+
+ context 'when request is being processed' do
+ let(:endpoint) { double }
+ let(:env) do
+ {
+ ::Rack::Timeout::ENV_INFO_KEY => double(state: :active),
+ Grape::Env::API_ENDPOINT => endpoint
+ }
+ end
+
+ subject { described_class.new }
+
+ it 'does not increment counter' do
+ allow(endpoint).to receive_message_chain('route.pattern.origin') { 'foobar' }
+ expect(counter).not_to receive(:increment)
+
+ subject.callback.call(env)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index f7642182a17..22e52901758 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -202,4 +202,14 @@ describe Gitlab::Danger::Helper do
it { is_expected.to eq(expected_label) }
end
end
+
+ describe '#new_teammates' do
+ it 'returns an array of Teammate' do
+ usernames = %w[filipa iamphil]
+
+ teammates = helper.new_teammates(usernames)
+
+ expect(teammates.map(&:username)).to eq(usernames)
+ end
+ end
end
diff --git a/spec/lib/gitlab/danger/roulette_spec.rb b/spec/lib/gitlab/danger/roulette_spec.rb
index 40dce0c5378..121c5d8ecd9 100644
--- a/spec/lib/gitlab/danger/roulette_spec.rb
+++ b/spec/lib/gitlab/danger/roulette_spec.rb
@@ -98,4 +98,47 @@ describe Gitlab::Danger::Roulette do
is_expected.to contain_exactly(ce_teammate_matcher)
end
end
+
+ describe '#spin_for_person' do
+ let(:person1) { Gitlab::Danger::Teammate.new('username' => 'rymai') }
+ let(:person2) { Gitlab::Danger::Teammate.new('username' => 'godfat') }
+ let(:author) { Gitlab::Danger::Teammate.new('username' => 'filipa') }
+ let(:ooo) { Gitlab::Danger::Teammate.new('username' => 'jacopo-beschi') }
+
+ before do
+ stub_person_message(person1, 'making GitLab magic')
+ stub_person_message(person2, 'making GitLab magic')
+ stub_person_message(ooo, 'OOO till 15th')
+ # we don't stub Filipa, as she is the author and
+ # we should not fire request checking for her
+
+ allow(subject).to receive_message_chain(:gitlab, :mr_author).and_return(author.username)
+ end
+
+ it 'returns a random person' do
+ persons = [person1, person2]
+
+ selected = subject.spin_for_person(persons, random: Random.new)
+
+ expect(selected.username).to be_in(persons.map(&:username))
+ end
+
+ it 'excludes OOO persons' do
+ expect(subject.spin_for_person([ooo], random: Random.new)).to be_nil
+ end
+
+ it 'excludes mr.author' do
+ expect(subject.spin_for_person([author], random: Random.new)).to be_nil
+ end
+
+ private
+
+ def stub_person_message(person, message)
+ body = { message: message }.to_json
+
+ WebMock
+ .stub_request(:get, "https://gitlab.com/api/v4/users/#{person.username}/status")
+ .to_return(body: body)
+ end
+ end
end
diff --git a/spec/lib/gitlab/danger/teammate_spec.rb b/spec/lib/gitlab/danger/teammate_spec.rb
index 753c74ff814..6a6cf1429c8 100644
--- a/spec/lib/gitlab/danger/teammate_spec.rb
+++ b/spec/lib/gitlab/danger/teammate_spec.rb
@@ -5,39 +5,66 @@ require 'fast_spec_helper'
require 'gitlab/danger/teammate'
describe Gitlab::Danger::Teammate do
- subject { described_class.new({ 'projects' => projects }) }
+ subject { described_class.new(options) }
+ let(:options) { { 'projects' => projects, 'role' => role } }
let(:projects) { { project => capabilities } }
+ let(:role) { 'Engineer, Manage' }
+ let(:labels) { [] }
let(:project) { double }
- describe 'multiple roles project project' do
- let(:capabilities) { ['reviewer backend', 'maintainer frontend', 'trainee_maintainer database'] }
+ context 'when having multiple capabilities' do
+ let(:capabilities) { ['reviewer backend', 'maintainer frontend', 'trainee_maintainer qa'] }
it '#reviewer? supports multiple roles per project' do
- expect(subject.reviewer?(project, :backend)).to be_truthy
+ expect(subject.reviewer?(project, :backend, labels)).to be_truthy
end
it '#traintainer? supports multiple roles per project' do
- expect(subject.traintainer?(project, :database)).to be_truthy
+ expect(subject.traintainer?(project, :qa, labels)).to be_truthy
end
it '#maintainer? supports multiple roles per project' do
- expect(subject.maintainer?(project, :frontend)).to be_truthy
+ expect(subject.maintainer?(project, :frontend, labels)).to be_truthy
+ end
+
+ context 'when labels contain Create and the category is test' do
+ let(:labels) { ['Create'] }
+
+ context 'when role is Test Automation Engineer, Create' do
+ let(:role) { 'Test Automation Engineer, Create' }
+
+ it '#reviewer? returns true' do
+ expect(subject.reviewer?(project, :test, labels)).to be_truthy
+ end
+
+ it '#maintainer? returns false' do
+ expect(subject.maintainer?(project, :test, labels)).to be_falsey
+ end
+ end
+
+ context 'when role is Test Automation Engineer, Manage' do
+ let(:role) { 'Test Automation Engineer, Manage' }
+
+ it '#reviewer? returns false' do
+ expect(subject.reviewer?(project, :test, labels)).to be_falsey
+ end
+ end
end
end
- describe 'one role project project' do
+ context 'when having single capability' do
let(:capabilities) { 'reviewer backend' }
it '#reviewer? supports one role per project' do
- expect(subject.reviewer?(project, :backend)).to be_truthy
+ expect(subject.reviewer?(project, :backend, labels)).to be_truthy
end
it '#traintainer? supports one role per project' do
- expect(subject.traintainer?(project, :database)).to be_falsey
+ expect(subject.traintainer?(project, :database, labels)).to be_falsey
end
it '#maintainer? supports one role per project' do
- expect(subject.maintainer?(project, :frontend)).to be_falsey
+ expect(subject.maintainer?(project, :frontend, labels)).to be_falsey
end
end
end
diff --git a/spec/lib/gitlab/data_builder/note_spec.rb b/spec/lib/gitlab/data_builder/note_spec.rb
index b236c1a9c49..ed9a1e23529 100644
--- a/spec/lib/gitlab/data_builder/note_spec.rb
+++ b/spec/lib/gitlab/data_builder/note_spec.rb
@@ -53,6 +53,8 @@ describe Gitlab::DataBuilder::Note do
.to eq(issue.reload.hook_attrs.except('updated_at'))
expect(data[:issue]['updated_at'])
.to be >= issue.hook_attrs['updated_at']
+ expect(data[:issue]['labels'])
+ .to eq(issue.hook_attrs['labels'])
end
context 'with confidential issue' do
diff --git a/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb b/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
index e4fe01a671f..52630ba0223 100644
--- a/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
@@ -35,7 +35,7 @@ describe Gitlab::GitalyClient::ConflictsService do
end
let(:source_branch) { 'master' }
let(:target_branch) { 'feature' }
- let(:commit_message) { 'Solving conflicts' }
+ let(:commit_message) { 'Solving conflicts\n\nTést' }
let(:resolution) do
Gitlab::Git::Conflict::Resolution.new(user, files, commit_message)
end
@@ -51,6 +51,25 @@ describe Gitlab::GitalyClient::ConflictsService do
subject
end
+ context 'with branches with UTF-8 characters' do
+ let(:source_branch) { 'testòbranch' }
+ let(:target_branch) { 'ábranch' }
+
+ it 'handles commit messages with UTF-8 characters' do
+ allow(::Gitlab::GitalyClient).to receive(:call).and_call_original
+ expect(::Gitlab::GitalyClient).to receive(:call).with(anything, :conflicts_service, :resolve_conflicts, any_args) do |*args|
+ # Force the generation of request messages by iterating through the enumerator
+ message = args[3].to_a.first
+ params = [message.header.commit_message, message.header.source_branch, message.header.target_branch]
+ expect(params.map(&:encoding).uniq).to eq([Encoding::ASCII_8BIT])
+
+ double(resolution_error: nil)
+ end
+
+ subject
+ end
+ end
+
it 'raises a relevant exception if resolution_error is present' do
expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:resolve_conflicts)
.with(kind_of(Enumerator), kind_of(Hash)).and_return(double(resolution_error: "something happened"))
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 7579a6577b9..18663a72fcd 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -78,6 +78,24 @@ describe Gitlab::GitalyClient::OperationService do
subject
end
+ describe '#user_merge_to_ref' do
+ let(:branch) { 'my-branch' }
+ let(:source_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' }
+ let(:ref) { 'refs/merge-requests/x/merge' }
+ let(:message) { 'validación' }
+ let(:response) { Gitaly::UserMergeToRefResponse.new(commit_id: 'new-commit-id') }
+
+ subject { client.user_merge_to_ref(user, source_sha, branch, ref, message) }
+
+ it 'sends a user_merge_to_ref message' do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_merge_to_ref).with(kind_of(Gitaly::UserMergeToRefRequest), kind_of(Hash))
+ .and_return(response)
+
+ subject
+ end
+ end
+
context "when pre_receive_error is present" do
let(:response) do
Gitaly::UserUpdateBranchResponse.new(pre_receive_error: "GitLab: something failed")
diff --git a/spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb b/spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb
new file mode 100644
index 00000000000..46dd1777285
--- /dev/null
+++ b/spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Loaders::BatchLfsOidLoader do
+ include GraphqlHelpers
+
+ set(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(repository.blob_at('master', 'files/lfs/lfs_object.iso'), repository) }
+ let(:otherblob) { Gitlab::Graphql::Representation::TreeEntry.new(repository.blob_at('master', 'README'), repository) }
+
+ describe '#find' do
+ it 'batch-resolves LFS blob IDs' do
+ expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).once.and_call_original
+
+ result = batch do
+ [blob, otherblob].map { |b| described_class.new(repository, b.id).find }
+ end
+
+ expect(result.first).to eq(blob.lfs_oid)
+ expect(result.last).to eq(nil)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index fb7bddb386c..6512fe80a3b 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -6223,7 +6223,10 @@
"erased_by_id": null,
"erased_at": null,
"type": "Ci::Build",
- "token": "abcd"
+ "token": "abcd",
+ "artifacts_file_store": 1,
+ "artifacts_metadata_store": 1,
+ "artifacts_size": 10
},
{
"id": 72,
diff --git a/spec/lib/gitlab/lets_encrypt/challenge_spec.rb b/spec/lib/gitlab/lets_encrypt/challenge_spec.rb
index 74622f356de..fcd92586362 100644
--- a/spec/lib/gitlab/lets_encrypt/challenge_spec.rb
+++ b/spec/lib/gitlab/lets_encrypt/challenge_spec.rb
@@ -3,23 +3,11 @@
require 'spec_helper'
describe ::Gitlab::LetsEncrypt::Challenge do
- delegated_methods = {
- url: 'https://example.com/',
- status: 'pending',
- token: 'tokenvalue',
- file_content: 'hereisfilecontent',
- request_validation: true
- }
+ include LetsEncryptHelpers
- let(:acme_challenge) do
- acme_challenge = instance_double('Acme::Client::Resources::Challenge')
- allow(acme_challenge).to receive_messages(delegated_methods)
- acme_challenge
- end
-
- let(:challenge) { described_class.new(acme_challenge) }
+ let(:challenge) { described_class.new(acme_challenge_double) }
- delegated_methods.each do |method, value|
+ LetsEncryptHelpers::ACME_CHALLENGE_METHODS.each do |method, value|
describe "##{method}" do
it 'delegates to Acme::Client::Resources::Challenge' do
expect(challenge.public_send(method)).to eq(value)
diff --git a/spec/lib/gitlab/lets_encrypt/order_spec.rb b/spec/lib/gitlab/lets_encrypt/order_spec.rb
index ee7058baf8d..1a759103c44 100644
--- a/spec/lib/gitlab/lets_encrypt/order_spec.rb
+++ b/spec/lib/gitlab/lets_encrypt/order_spec.rb
@@ -3,20 +3,13 @@
require 'spec_helper'
describe ::Gitlab::LetsEncrypt::Order do
- delegated_methods = {
- url: 'https://example.com/',
- status: 'valid'
- }
-
- let(:acme_order) do
- acme_order = instance_double('Acme::Client::Resources::Order')
- allow(acme_order).to receive_messages(delegated_methods)
- acme_order
- end
+ include LetsEncryptHelpers
+
+ let(:acme_order) { acme_order_double }
let(:order) { described_class.new(acme_order) }
- delegated_methods.each do |method, value|
+ LetsEncryptHelpers::ACME_ORDER_METHODS.each do |method, value|
describe "##{method}" do
it 'delegates to Acme::Client::Resources::Order' do
expect(order.public_send(method)).to eq(value)
@@ -25,15 +18,24 @@ describe ::Gitlab::LetsEncrypt::Order do
end
describe '#new_challenge' do
- before do
- challenge = instance_double('Acme::Client::Resources::Challenges::HTTP01')
- authorization = instance_double('Acme::Client::Resources::Authorization')
- allow(authorization).to receive(:http).and_return(challenge)
- allow(acme_order).to receive(:authorizations).and_return([authorization])
- end
-
it 'returns challenge' do
expect(order.new_challenge).to be_a(::Gitlab::LetsEncrypt::Challenge)
end
end
+
+ describe '#request_certificate' do
+ let(:private_key) do
+ OpenSSL::PKey::RSA.new(4096).to_pem
+ end
+
+ it 'generates csr and finalizes order' do
+ expect(acme_order).to receive(:finalize) do |csr:|
+ expect do
+ csr.csr # it's being evaluated lazily
+ end.not_to raise_error
+ end
+
+ order.request_certificate(domain: 'example.com', private_key: private_key)
+ end
+ end
end
diff --git a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
index e88eb140b35..bdcb5914575 100644
--- a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_cachi
include MetricsDashboardHelpers
set(:project) { build(:project) }
- set(:environment) { build(:environment, project: project) }
+ set(:environment) { create(:environment, project: project) }
let(:system_dashboard_path) { Gitlab::Metrics::Dashboard::SystemDashboardService::SYSTEM_DASHBOARD_PATH}
describe '.find' do
@@ -27,6 +27,13 @@ describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_cachi
it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity
end
+ context 'when the dashboard contains a metric without a query' do
+ let(:dashboard) { { 'panel_groups' => [{ 'panels' => [{ 'metrics' => [{ 'id' => 'mock' }] }] }] } }
+ let(:project) { project_with_dashboard(dashboard_path, dashboard.to_yaml) }
+
+ it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity
+ end
+
context 'when the system dashboard is specified' do
let(:dashboard_path) { system_dashboard_path }
diff --git a/spec/lib/gitlab/metrics/dashboard/processor_spec.rb b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb
index be3c1095bd7..797d4daabe3 100644
--- a/spec/lib/gitlab/metrics/dashboard/processor_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb
@@ -4,13 +4,19 @@ require 'spec_helper'
describe Gitlab::Metrics::Dashboard::Processor do
let(:project) { build(:project) }
- let(:environment) { build(:environment, project: project) }
+ let(:environment) { create(:environment, project: project) }
let(:dashboard_yml) { YAML.load_file('spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml') }
describe 'process' do
let(:process_params) { [project, environment, dashboard_yml] }
let(:dashboard) { described_class.new(*process_params).process(insert_project_metrics: true) }
+ it 'includes a path for the prometheus endpoint with each metric' do
+ expect(all_metrics).to satisfy_all do |metric|
+ metric[:prometheus_endpoint_path] == prometheus_path(metric[:query_range])
+ end
+ end
+
context 'when dashboard config corresponds to common metrics' do
let!(:common_metric) { create(:prometheus_metric, :common, identifier: 'metric_a1') }
@@ -61,7 +67,7 @@ describe Gitlab::Metrics::Dashboard::Processor do
shared_examples_for 'errors with message' do |expected_message|
it 'raises a DashboardLayoutError' do
- error_class = Gitlab::Metrics::Dashboard::Stages::BaseStage::DashboardLayoutError
+ error_class = Gitlab::Metrics::Dashboard::Stages::BaseStage::DashboardProcessingError
expect { dashboard }.to raise_error(error_class, expected_message)
end
@@ -84,6 +90,12 @@ describe Gitlab::Metrics::Dashboard::Processor do
it_behaves_like 'errors with message', 'Each "panel" must define an array :metrics'
end
+
+ context 'when the dashboard contains a metric which is missing a query' do
+ let(:dashboard_yml) { { panel_groups: [{ panels: [{ metrics: [{}] }] }] } }
+
+ it_behaves_like 'errors with message', 'Each "metric" must define one of :query or :query_range'
+ end
end
private
@@ -99,7 +111,17 @@ describe Gitlab::Metrics::Dashboard::Processor do
query_range: metric.query,
unit: metric.unit,
label: metric.legend,
- metric_id: metric.id
+ metric_id: metric.id,
+ prometheus_endpoint_path: prometheus_path(metric.query)
}
end
+
+ def prometheus_path(query)
+ Gitlab::Routing.url_helpers.prometheus_api_project_environment_path(
+ project,
+ environment,
+ proxy_path: :query_range,
+ query: query
+ )
+ end
end
diff --git a/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb b/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb
index 162beb0268a..794ac5f109b 100644
--- a/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::Metrics::Dashboard::ProjectDashboardService, :use_clean_rails_m
set(:user) { build(:user) }
set(:project) { build(:project) }
- set(:environment) { build(:environment, project: project) }
+ set(:environment) { create(:environment, project: project) }
before do
project.add_maintainer(user)
diff --git a/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb b/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb
index e71ce2481a3..2648fe990de 100644
--- a/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::Metrics::Dashboard::SystemDashboardService, :use_clean_rails_me
include MetricsDashboardHelpers
set(:project) { build(:project) }
- set(:environment) { build(:environment, project: project) }
+ set(:environment) { create(:environment, project: project) }
describe 'get_dashboard' do
let(:dashboard_path) { described_class::SYSTEM_DASHBOARD_PATH }
diff --git a/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb b/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb
index fe46c67a920..5f0a7e925ca 100644
--- a/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb
+++ b/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb
@@ -15,6 +15,13 @@ describe Gitlab::Template::GitlabCiYmlTemplate do
expect(all).to include('Docker')
expect(all).to include('Ruby')
end
+
+ it 'ensure that the template name is used exactly once' do
+ all = subject.all.group_by(&:name)
+ duplicates = all.select { |_, templates| templates.length > 1 }
+
+ expect(duplicates).to be_empty
+ end
end
describe '.find' do
diff --git a/spec/migrations/enqueue_reset_merge_status_spec.rb b/spec/migrations/enqueue_reset_merge_status_spec.rb
index 0d5e33bfd46..a6dd2e08079 100644
--- a/spec/migrations/enqueue_reset_merge_status_spec.rb
+++ b/spec/migrations/enqueue_reset_merge_status_spec.rb
@@ -40,9 +40,12 @@ describe EnqueueResetMergeStatus, :migration, :sidekiq do
.to be_scheduled_delayed_migration(5.minutes, 1, 2)
expect(described_class::MIGRATION)
- .to be_scheduled_delayed_migration(10.minutes, 3, 3)
+ .to be_scheduled_delayed_migration(10.minutes, 3, 4)
- expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ expect(described_class::MIGRATION)
+ .to be_scheduled_delayed_migration(15.minutes, 5, 5)
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(3)
end
end
end
diff --git a/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb b/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb
index afcaefa0591..abf39317188 100644
--- a/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb
+++ b/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb
@@ -8,9 +8,13 @@ describe EnqueueVerifyPagesDomainWorkers, :sidekiq, :migration do
end
end
+ let(:domains_table) { table(:pages_domains) }
+
describe '#up' do
it 'enqueues a verification worker for every domain' do
- domains = 1.upto(3).map { |i| PagesDomain.create!(domain: "my#{i}.domain.com") }
+ domains = Array.new(3) do |i|
+ domains_table.create!(domain: "my#{i}.domain.com", verification_code: "123#{i}")
+ end
expect { migrate! }.to change(PagesDomainVerificationWorker.jobs, :size).by(3)
diff --git a/spec/migrations/fix_pool_repository_source_project_id_spec.rb b/spec/migrations/fix_pool_repository_source_project_id_spec.rb
new file mode 100644
index 00000000000..8ddee9bb575
--- /dev/null
+++ b/spec/migrations/fix_pool_repository_source_project_id_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20190604184643_fix_pool_repository_source_project_id.rb')
+
+describe FixPoolRepositorySourceProjectId, :migration do
+ let(:projects) { table(:projects) }
+ let(:pool_repositories) { table(:pool_repositories) }
+ let(:shards) { table(:shards) }
+
+ it 'fills in source_project_ids' do
+ shard = shards.create!(name: 'default')
+
+ # gitaly is a project with a pool repository that has a source_project_id
+ gitaly = projects.create!(name: 'gitaly', path: 'gitlab-org/gitaly', namespace_id: 1)
+ pool_repository = pool_repositories.create(shard_id: shard.id, source_project_id: gitaly.id)
+ gitaly.update_column(:pool_repository_id, pool_repository.id)
+
+ # gitlab is a project with a pool repository that's missing a source_project_id
+ pool_repository_without_source_project = pool_repositories.create(shard_id: shard.id, source_project_id: nil)
+ gitlab = projects.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce', namespace_id: 1, pool_repository_id: pool_repository_without_source_project.id)
+ projects.create!(name: 'gitlab-fork-1', path: 'my-org-1/gitlab-ce', namespace_id: 1, pool_repository_id: pool_repository_without_source_project.id)
+
+ migrate!
+
+ expect(pool_repositories.find(pool_repository_without_source_project.id).source_project_id).to eq(gitlab.id)
+ expect(pool_repositories.find(pool_repository.id).source_project_id).to eq(gitaly.id)
+ end
+end
diff --git a/spec/migrations/remove_orphaned_label_links_spec.rb b/spec/migrations/remove_orphaned_label_links_spec.rb
index 13b8919343e..e8c44c141c3 100644
--- a/spec/migrations/remove_orphaned_label_links_spec.rb
+++ b/spec/migrations/remove_orphaned_label_links_spec.rb
@@ -10,6 +10,12 @@ describe RemoveOrphanedLabelLinks, :migration do
let(:project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
let(:label) { create_label }
+ before do
+ # This migration was created before we introduced ProjectCiCdSetting#default_git_depth
+ allow_any_instance_of(ProjectCiCdSetting).to receive(:default_git_depth).and_return(nil)
+ allow_any_instance_of(ProjectCiCdSetting).to receive(:default_git_depth=).and_return(0)
+ end
+
context 'add foreign key on label_id' do
let!(:label_link_with_label) { create_label_link(label_id: label.id) }
let!(:label_link_without_label) { create_label_link(label_id: nil) }
diff --git a/spec/migrations/schedule_fill_valid_time_for_pages_domain_certificates_spec.rb b/spec/migrations/schedule_fill_valid_time_for_pages_domain_certificates_spec.rb
new file mode 100644
index 00000000000..54f3e264df0
--- /dev/null
+++ b/spec/migrations/schedule_fill_valid_time_for_pages_domain_certificates_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20190524073827_schedule_fill_valid_time_for_pages_domain_certificates.rb')
+
+describe ScheduleFillValidTimeForPagesDomainCertificates, :migration, :sidekiq do
+ let(:migration_class) { described_class::MIGRATION }
+ let(:migration_name) { migration_class.to_s.demodulize }
+
+ let(:domains_table) { table(:pages_domains) }
+
+ let(:certificate) do
+ File.read('spec/fixtures/passphrase_x509_certificate.crt')
+ end
+
+ before do
+ domains_table.create!(domain: "domain1.example.com", verification_code: "123")
+ domains_table.create!(domain: "domain2.example.com", verification_code: "123", certificate: '')
+ domains_table.create!(domain: "domain3.example.com", verification_code: "123", certificate: certificate)
+ domains_table.create!(domain: "domain4.example.com", verification_code: "123", certificate: certificate)
+ end
+
+ it 'correctly schedules background migrations' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ migrate!
+
+ first_id = domains_table.find_by_domain("domain3.example.com").id
+ last_id = domains_table.find_by_domain("domain4.example.com").id
+
+ expect(migration_name).to be_scheduled_delayed_migration(5.minutes, first_id, last_id)
+ expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ end
+ end
+ end
+
+ it 'sets certificate valid_not_before/not_after' do
+ perform_enqueued_jobs do
+ migrate!
+
+ domain = domains_table.find_by_domain("domain3.example.com")
+ expect(domain.certificate_valid_not_before)
+ .to eq(Time.parse("2018-03-23 14:02:08 UTC"))
+ expect(domain.certificate_valid_not_after)
+ .to eq(Time.parse("2019-03-23 14:02:08 UTC"))
+ end
+ end
+end
diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb
index 3ab013ddc0e..4d53e4aad8a 100644
--- a/spec/models/broadcast_message_spec.rb
+++ b/spec/models/broadcast_message_spec.rb
@@ -88,13 +88,6 @@ describe BroadcastMessage do
expect(Rails.cache).not_to receive(:delete).with(described_class::CACHE_KEY)
expect(described_class.current.length).to eq(0)
end
-
- it 'clears the legacy cache key' do
- create(:broadcast_message, :future)
-
- expect(Rails.cache).to receive(:delete).with(described_class::LEGACY_CACHE_KEY)
- expect(described_class.current.length).to eq(0)
- end
end
describe '#attributes' do
@@ -164,7 +157,6 @@ describe BroadcastMessage do
message = create(:broadcast_message)
expect(Rails.cache).to receive(:delete).with(described_class::CACHE_KEY)
- expect(Rails.cache).to receive(:delete).with(described_class::LEGACY_CACHE_KEY)
message.flush_redis_cache
end
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index 53df9e0bc05..7faa196623f 100644
--- a/spec/models/concerns/reactive_caching_spec.rb
+++ b/spec/models/concerns/reactive_caching_spec.rb
@@ -232,4 +232,17 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
end
end
end
+
+ describe 'default options' do
+ let(:cached_class) { Class.new { include ReactiveCaching } }
+
+ subject { cached_class.new }
+
+ it { expect(subject.reactive_cache_lease_timeout).to be_a(ActiveSupport::Duration) }
+ it { expect(subject.reactive_cache_refresh_interval).to be_a(ActiveSupport::Duration) }
+ it { expect(subject.reactive_cache_lifetime).to be_a(ActiveSupport::Duration) }
+
+ it { expect(subject.reactive_cache_key).to respond_to(:call) }
+ it { expect(subject.reactive_cache_worker_finder).to respond_to(:call) }
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 956c5675f38..c6251326c22 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1088,22 +1088,6 @@ describe MergeRequest do
end
end
- describe "#reset_auto_merge" do
- let(:merge_if_green) do
- create :merge_request, merge_when_pipeline_succeeds: true, merge_user: create(:user),
- merge_params: { "should_remove_source_branch" => "1", "commit_message" => "msg" }
- end
-
- it "sets the item to false" do
- merge_if_green.reset_auto_merge
- merge_if_green.reload
-
- expect(merge_if_green.merge_when_pipeline_succeeds).to be_falsey
- expect(merge_if_green.merge_params["should_remove_source_branch"]).to be_nil
- expect(merge_if_green.merge_params["commit_message"]).to be_nil
- end
- end
-
describe '#committers' do
it 'returns all the committers of every commit in the merge request' do
users = subject.commits.without_merge_commits.map(&:committer_email).uniq.map do |email|
@@ -2012,6 +1996,57 @@ describe MergeRequest do
end
end
+ describe '#check_if_can_be_merged' do
+ let(:project) { create(:project, only_allow_merge_if_pipeline_succeeds: true) }
+
+ shared_examples 'checking if can be merged' do
+ context 'when it is not broken and has no conflicts' do
+ before do
+ allow(subject).to receive(:broken?) { false }
+ allow(project.repository).to receive(:can_be_merged?).and_return(true)
+ end
+
+ it 'is marked as mergeable' do
+ expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('can_be_merged')
+ end
+ end
+
+ context 'when broken' do
+ before do
+ allow(subject).to receive(:broken?) { true }
+ allow(project.repository).to receive(:can_be_merged?).and_return(false)
+ end
+
+ it 'becomes unmergeable' do
+ expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
+ end
+ end
+
+ context 'when it has conflicts' do
+ before do
+ allow(subject).to receive(:broken?) { false }
+ allow(project.repository).to receive(:can_be_merged?).and_return(false)
+ end
+
+ it 'becomes unmergeable' do
+ expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
+ end
+ end
+ end
+
+ context 'when merge_status is unchecked' do
+ subject { create(:merge_request, source_project: project, merge_status: :unchecked) }
+
+ it_behaves_like 'checking if can be merged'
+ end
+
+ context 'when merge_status is unchecked' do
+ subject { create(:merge_request, source_project: project, merge_status: :cannot_be_merged_recheck) }
+
+ it_behaves_like 'checking if can be merged'
+ end
+ end
+
describe '#mergeable?' do
let(:project) { create(:project) }
@@ -2025,7 +2060,7 @@ describe MergeRequest do
it 'return true if #mergeable_state? is true and the MR #can_be_merged? is true' do
allow(subject).to receive(:mergeable_state?) { true }
- expect(subject).to receive(:check_mergeability)
+ expect(subject).to receive(:check_if_can_be_merged)
expect(subject).to receive(:can_be_merged?) { true }
expect(subject.mergeable?).to be_truthy
@@ -2039,7 +2074,7 @@ describe MergeRequest do
it 'checks if merge request can be merged' do
allow(subject).to receive(:mergeable_ci_state?) { true }
- expect(subject).to receive(:check_mergeability)
+ expect(subject).to receive(:check_if_can_be_merged)
subject.mergeable?
end
@@ -3107,6 +3142,38 @@ describe MergeRequest do
end
end
+ describe '#mergeable_to_ref?' do
+ it 'returns true when merge request is mergeable' do
+ subject = create(:merge_request)
+
+ expect(subject.mergeable_to_ref?).to be(true)
+ end
+
+ it 'returns false when merge request is already merged' do
+ subject = create(:merge_request, :merged)
+
+ expect(subject.mergeable_to_ref?).to be(false)
+ end
+
+ it 'returns false when merge request is closed' do
+ subject = create(:merge_request, :closed)
+
+ expect(subject.mergeable_to_ref?).to be(false)
+ end
+
+ it 'returns false when merge request is work in progress' do
+ subject = create(:merge_request, title: 'WIP: The feature')
+
+ expect(subject.mergeable_to_ref?).to be(false)
+ end
+
+ it 'returns false when merge request has no commits' do
+ subject = create(:merge_request, source_branch: 'empty-branch', target_branch: 'master')
+
+ expect(subject.mergeable_to_ref?).to be(false)
+ end
+ end
+
describe '#merge_participants' do
it 'contains author' do
expect(subject.merge_participants).to eq([subject.author])
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
index 1b1ede6b14c..20278d81f6d 100644
--- a/spec/models/notification_recipient_spec.rb
+++ b/spec/models/notification_recipient_spec.rb
@@ -91,4 +91,237 @@ describe NotificationRecipient do
end
end
end
+
+ describe '#suitable_notification_level?' do
+ context 'when notification level is mention' do
+ before do
+ user.notification_settings_for(project).mention!
+ end
+
+ context 'when type is mention' do
+ let(:recipient) { described_class.new(user, :mention, target: target, project: project) }
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context 'when type is not mention' do
+ it 'returns false' do
+ expect(recipient.suitable_notification_level?).to eq false
+ end
+ end
+ end
+
+ context 'when notification level is participating' do
+ let(:notification_setting) { user.notification_settings_for(project) }
+
+ context 'when type is participating' do
+ let(:recipient) { described_class.new(user, :participating, target: target, project: project) }
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context 'when type is mention' do
+ let(:recipient) { described_class.new(user, :mention, target: target, project: project) }
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context 'with custom action' do
+ context "when action is failed_pipeline" do
+ let(:recipient) do
+ described_class.new(
+ user,
+ :watch,
+ custom_action: :failed_pipeline,
+ target: target,
+ project: project
+ )
+ end
+
+ before do
+ notification_setting.update!(failed_pipeline: true)
+ end
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context "when action is not failed_pipeline" do
+ let(:recipient) do
+ described_class.new(
+ user,
+ :watch,
+ custom_action: :success_pipeline,
+ target: target,
+ project: project
+ )
+ end
+
+ before do
+ notification_setting.update!(success_pipeline: true)
+ end
+
+ it 'returns false' do
+ expect(recipient.suitable_notification_level?).to eq false
+ end
+ end
+ end
+ end
+
+ context 'when notification level is custom' do
+ before do
+ user.notification_settings_for(project).custom!
+ end
+
+ context 'when type is participating' do
+ let(:notification_setting) { user.notification_settings_for(project) }
+ let(:recipient) do
+ described_class.new(
+ user,
+ :participating,
+ custom_action: :new_note,
+ target: target,
+ project: project
+ )
+ end
+
+ context 'with custom event enabled' do
+ before do
+ notification_setting.update!(new_note: true)
+ end
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context 'without custom event enabled' do
+ before do
+ notification_setting.update!(new_note: false)
+ end
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+ end
+
+ context 'when type is mention' do
+ let(:notification_setting) { user.notification_settings_for(project) }
+ let(:recipient) do
+ described_class.new(
+ user,
+ :mention,
+ custom_action: :new_issue,
+ target: target,
+ project: project
+ )
+ end
+
+ context 'with custom event enabled' do
+ before do
+ notification_setting.update!(new_issue: true)
+ end
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context 'without custom event enabled' do
+ before do
+ notification_setting.update!(new_issue: false)
+ end
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+ end
+
+ context 'when type is watch' do
+ let(:notification_setting) { user.notification_settings_for(project) }
+ let(:recipient) do
+ described_class.new(
+ user,
+ :watch,
+ custom_action: :failed_pipeline,
+ target: target,
+ project: project
+ )
+ end
+
+ context 'with custom event enabled' do
+ before do
+ notification_setting.update!(failed_pipeline: true)
+ end
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context 'without custom event enabled' do
+ before do
+ notification_setting.update!(failed_pipeline: false)
+ end
+
+ it 'returns false' do
+ expect(recipient.suitable_notification_level?).to eq false
+ end
+ end
+ end
+ end
+
+ context 'when notification level is watch' do
+ before do
+ user.notification_settings_for(project).watch!
+ end
+
+ context 'when type is watch' do
+ context 'without excluded watcher events' do
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context 'with excluded watcher events' do
+ let(:recipient) do
+ described_class.new(user, :watch, custom_action: :issue_due, target: target, project: project)
+ end
+
+ it 'returns false' do
+ expect(recipient.suitable_notification_level?).to eq false
+ end
+ end
+ end
+
+ context 'when type is not watch' do
+ context 'without excluded watcher events' do
+ let(:recipient) { described_class.new(user, :participating, target: target, project: project) }
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+
+ context 'with excluded watcher events' do
+ let(:recipient) do
+ described_class.new(user, :participating, custom_action: :issue_due, target: target, project: project)
+ end
+
+ it 'returns true' do
+ expect(recipient.suitable_notification_level?).to eq true
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/pages_domain_acme_order_spec.rb b/spec/models/pages_domain_acme_order_spec.rb
new file mode 100644
index 00000000000..4ffb4fc7389
--- /dev/null
+++ b/spec/models/pages_domain_acme_order_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PagesDomainAcmeOrder do
+ using RSpec::Parameterized::TableSyntax
+
+ describe '.expired' do
+ let!(:not_expired_order) { create(:pages_domain_acme_order) }
+ let!(:expired_order) { create(:pages_domain_acme_order, :expired) }
+
+ it 'returns only expired orders' do
+ expect(described_class.count).to eq(2)
+ expect(described_class.expired).to eq([expired_order])
+ end
+ end
+
+ describe '.find_by_domain_and_token' do
+ let!(:domain) { create(:pages_domain, domain: 'test.com') }
+ let!(:acme_order) { create(:pages_domain_acme_order, challenge_token: 'righttoken', pages_domain: domain) }
+
+ where(:domain_name, :challenge_token, :present) do
+ 'test.com' | 'righttoken' | true
+ 'test.com' | 'wrongtoken' | false
+ 'test.org' | 'righttoken' | false
+ end
+
+ with_them do
+ subject { described_class.find_by_domain_and_token(domain_name, challenge_token).present? }
+
+ it { is_expected.to eq(present) }
+ end
+ end
+
+ subject { create(:pages_domain_acme_order) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:pages_domain) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:pages_domain) }
+ it { is_expected.to validate_presence_of(:expires_at) }
+ it { is_expected.to validate_presence_of(:url) }
+ it { is_expected.to validate_presence_of(:challenge_token) }
+ it { is_expected.to validate_presence_of(:challenge_file_content) }
+ it { is_expected.to validate_presence_of(:private_key) }
+ end
+end
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index ec4d4517f82..fdc81359d34 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -81,6 +81,17 @@ describe PagesDomain do
end
end
+ describe 'when certificate is specified' do
+ let(:domain) { build(:pages_domain) }
+
+ it 'saves validity time' do
+ domain.save
+
+ expect(domain.certificate_valid_not_before).to be_like_time(Time.parse("2016-02-12 14:32:00 UTC"))
+ expect(domain.certificate_valid_not_after).to be_like_time(Time.parse("2020-04-12 14:32:00 UTC"))
+ end
+ end
+
describe 'validate certificate' do
subject { domain }
diff --git a/spec/models/project_ci_cd_setting_spec.rb b/spec/models/project_ci_cd_setting_spec.rb
index 4aa62028169..eb3a7e527c9 100644
--- a/spec/models/project_ci_cd_setting_spec.rb
+++ b/spec/models/project_ci_cd_setting_spec.rb
@@ -21,4 +21,44 @@ describe ProjectCiCdSetting do
2.times { described_class.available? }
end
end
+
+ describe 'validations' do
+ it 'validates default_git_depth is between 0 and 1000 or nil' do
+ expect(subject).to validate_numericality_of(:default_git_depth)
+ .only_integer
+ .is_greater_than_or_equal_to(0)
+ .is_less_than_or_equal_to(1000)
+ .allow_nil
+ end
+ end
+
+ describe '#default_git_depth' do
+ let(:default_value) { described_class::DEFAULT_GIT_DEPTH }
+
+ it 'sets default value for new records' do
+ project = create(:project)
+
+ expect(project.ci_cd_settings.default_git_depth).to eq(default_value)
+ end
+
+ it 'does not set default value if present' do
+ project = build(:project)
+ project.build_ci_cd_settings(default_git_depth: 0)
+ project.save!
+
+ expect(project.reload.ci_cd_settings.default_git_depth).to eq(0)
+ end
+
+ context 'when feature flag :ci_set_project_default_git_depth is disabled' do
+ let(:project) { create(:project) }
+
+ before do
+ stub_feature_flags(ci_set_project_default_git_depth: { enabled: false } )
+ end
+
+ it 'does not set default value for new records' do
+ expect(project.ci_cd_settings.default_git_depth).to eq(nil)
+ end
+ end
+ end
end
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb
index 358873f9a2f..1cb49d83ffa 100644
--- a/spec/models/project_statistics_spec.rb
+++ b/spec/models/project_statistics_spec.rb
@@ -197,6 +197,18 @@ describe ProjectStatistics do
expect(statistics.storage_size).to eq 9
end
+
+ it 'works during wiki_size backfill' do
+ statistics.update!(
+ repository_size: 2,
+ wiki_size: nil,
+ lfs_objects_size: 3
+ )
+
+ statistics.reload
+
+ expect(statistics.storage_size).to eq 5
+ end
end
describe '.increment_statistic' do
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 8075fcade5f..ed0e82ef179 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -66,7 +66,7 @@ describe ProjectPolicy do
%i[
change_namespace change_visibility_level rename_project remove_project
archive_project remove_fork_project destroy_merge_request destroy_issue
- set_issue_iid set_issue_created_at set_note_created_at
+ set_issue_iid set_issue_created_at set_issue_updated_at set_note_created_at
]
end
diff --git a/spec/presenters/ci/build_runner_presenter_spec.rb b/spec/presenters/ci/build_runner_presenter_spec.rb
index 3430111ca9e..a4234d14255 100644
--- a/spec/presenters/ci/build_runner_presenter_spec.rb
+++ b/spec/presenters/ci/build_runner_presenter_spec.rb
@@ -119,21 +119,31 @@ describe Ci::BuildRunnerPresenter do
end
describe '#git_depth' do
- subject { presenter.git_depth }
-
let(:build) { create(:ci_build) }
- it 'returns the correct git depth' do
- is_expected.to eq(0)
- end
+ subject(:git_depth) { presenter.git_depth }
context 'when GIT_DEPTH variable is specified' do
before do
create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: build.pipeline)
end
- it 'returns the correct git depth' do
- is_expected.to eq(1)
+ it 'returns its value' do
+ expect(git_depth).to eq(1)
+ end
+ end
+
+ it 'defaults to git depth setting for the project' do
+ expect(git_depth).to eq(build.project.ci_default_git_depth)
+ end
+
+ context 'when feature flag :ci_project_git_depth is disabled' do
+ before do
+ stub_feature_flags(ci_project_git_depth: { enabled: false })
+ end
+
+ it 'defaults to 0' do
+ expect(git_depth).to eq(0)
end
end
end
@@ -144,24 +154,24 @@ describe Ci::BuildRunnerPresenter do
let(:build) { create(:ci_build) }
it 'returns the correct refspecs' do
- is_expected.to contain_exactly('+refs/tags/*:refs/tags/*',
- '+refs/heads/*:refs/remotes/origin/*')
+ is_expected.to contain_exactly("+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
end
- context 'when GIT_DEPTH variable is specified' do
- before do
- create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: build.pipeline)
- end
+ context 'when ref is tag' do
+ let(:build) { create(:ci_build, :tag) }
it 'returns the correct refspecs' do
- is_expected.to contain_exactly("+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
+ is_expected.to contain_exactly("+refs/tags/#{build.ref}:refs/tags/#{build.ref}")
end
- context 'when ref is tag' do
- let(:build) { create(:ci_build, :tag) }
+ context 'when GIT_DEPTH is zero' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 0, pipeline: build.pipeline)
+ end
it 'returns the correct refspecs' do
- is_expected.to contain_exactly("+refs/tags/#{build.ref}:refs/tags/#{build.ref}")
+ is_expected.to contain_exactly('+refs/tags/*:refs/tags/*',
+ '+refs/heads/*:refs/remotes/origin/*')
end
end
end
@@ -173,17 +183,27 @@ describe Ci::BuildRunnerPresenter do
it 'returns the correct refspecs' do
is_expected
- .to contain_exactly('+refs/heads/*:refs/remotes/origin/*',
- '+refs/tags/*:refs/tags/*',
- '+refs/merge-requests/1/head:refs/merge-requests/1/head')
+ .to contain_exactly('+refs/merge-requests/1/head:refs/merge-requests/1/head')
+ end
+
+ context 'when GIT_DEPTH is zero' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 0, pipeline: build.pipeline)
+ end
+
+ it 'returns the correct refspecs' do
+ is_expected
+ .to contain_exactly('+refs/merge-requests/1/head:refs/merge-requests/1/head',
+ '+refs/heads/*:refs/remotes/origin/*',
+ '+refs/tags/*:refs/tags/*')
+ end
end
context 'when pipeline is legacy detached merge request pipeline' do
let(:merge_request) { create(:merge_request, :with_legacy_detached_merge_request_pipeline) }
it 'returns the correct refspecs' do
- is_expected.to contain_exactly('+refs/tags/*:refs/tags/*',
- '+refs/heads/*:refs/remotes/origin/*')
+ is_expected.to contain_exactly("+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
end
end
end
diff --git a/spec/rack_servers/puma_spec.rb b/spec/rack_servers/puma_spec.rb
index 8290473821c..a4b37905af3 100644
--- a/spec/rack_servers/puma_spec.rb
+++ b/spec/rack_servers/puma_spec.rb
@@ -20,7 +20,7 @@ describe 'Puma' do
File.write(config_path, config_lines)
cmd = %W[puma -e test -C #{config_path} #{File.join(__dir__, 'configs/config.ru')}]
- @puma_master_pid = spawn(*cmd)
+ @puma_master_pid = spawn({ 'DISABLE_PUMA_WORKER_KILLER' => '1' }, *cmd)
wait_puma_boot!(@puma_master_pid, File.join(project_root, 'tmp/tests/puma-worker-ready'))
WebMock.allow_net_connect!
end
diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb
index 9b9cc778fb3..f32ffd1c77b 100644
--- a/spec/requests/api/issues/issues_spec.rb
+++ b/spec/requests/api/issues/issues_spec.rb
@@ -276,14 +276,6 @@ describe API::Issues do
it 'returns issues with no assignee' do
issue2 = create(:issue, author: user2, project: project)
- get api('/issues', user), params: { assignee_id: 0, scope: 'all' }
-
- expect_paginated_array_response(issue2.id)
- end
-
- it 'returns issues with no assignee' do
- issue2 = create(:issue, author: user2, project: project)
-
get api('/issues', user), params: { assignee_id: 'None', scope: 'all' }
expect_paginated_array_response(issue2.id)
@@ -496,18 +488,6 @@ describe API::Issues do
expect_paginated_array_response(closed_issue.id)
end
-
- it 'returns an array of issues with no label when using the legacy No+Label filter' do
- get api('/issues', user), params: { labels: 'No Label' }
-
- expect_paginated_array_response(closed_issue.id)
- end
-
- it 'returns an array of issues with no label when using the legacy No+Label filter with labels param as array' do
- get api('/issues', user), params: { labels: ['No Label'] }
-
- expect_paginated_array_response(closed_issue.id)
- end
end
it 'returns an empty array if no issue matches milestone' do
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 4cb4fcc890d..9f9180bc8c9 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -1546,65 +1546,52 @@ describe API::MergeRequests do
end
end
- describe "GET /projects/:id/merge_requests/:merge_request_iid/merge_ref" do
- before do
- merge_request.mark_as_unchecked!
- end
-
- let(:merge_request_iid) { merge_request.iid }
-
+ describe "PUT /projects/:id/merge_requests/:merge_request_iid/merge_to_ref" do
+ let(:pipeline) { create(:ci_pipeline_without_jobs) }
let(:url) do
- "/projects/#{project.id}/merge_requests/#{merge_request_iid}/merge_ref"
+ "/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge_to_ref"
end
it 'returns the generated ID from the merge service in case of success' do
- get api(url, user)
+ put api(url, user), params: { merge_commit_message: 'Custom message' }
+
+ commit = project.commit(json_response['commit_id'])
expect(response).to have_gitlab_http_status(200)
- expect(json_response['commit_id']).to eq(merge_request.merge_ref_head.sha)
+ expect(json_response['commit_id']).to be_present
+ expect(commit.message).to eq('Custom message')
end
it "returns 400 if branch can't be merged" do
- merge_request.update!(merge_status: 'cannot_be_merged')
+ merge_request.update!(state: 'merged')
- get api(url, user)
+ put api(url, user)
expect(response).to have_gitlab_http_status(400)
- expect(json_response['message']).to eq('Merge request is not mergeable')
+ expect(json_response['message'])
+ .to eq("Merge request is not mergeable to #{merge_request.merge_ref_path}")
end
- context 'when user has no access to the MR' do
- let(:project) { create(:project, :private) }
- let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
-
- it 'returns 404' do
- project.add_guest(user)
+ it 'returns 403 if user has no permissions to merge to the ref' do
+ user2 = create(:user)
+ project.add_reporter(user2)
- get api(url, user)
+ put api(url, user2)
- expect(response).to have_gitlab_http_status(404)
- expect(json_response['message']).to eq('404 Not found')
- end
+ expect(response).to have_gitlab_http_status(403)
+ expect(json_response['message']).to eq('403 Forbidden')
end
- context 'when invalid merge request IID' do
- let(:merge_request_iid) { '12345' }
-
- it 'returns 404' do
- get api(url, user)
+ it 'returns 404 for an invalid merge request IID' do
+ put api("/projects/#{project.id}/merge_requests/12345/merge_to_ref", user)
- expect(response).to have_gitlab_http_status(404)
- end
+ expect(response).to have_gitlab_http_status(404)
end
- context 'when merge request ID is used instead IID' do
- let(:merge_request_iid) { merge_request.id }
-
- it 'returns 404' do
- get api(url, user)
+ it "returns 404 if the merge request id is used instead of iid" do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
- expect(response).to have_gitlab_http_status(404)
- end
+ expect(response).to have_gitlab_http_status(404)
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 799e84e83c1..5f7d2fa6d9c 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1125,6 +1125,7 @@ describe API::Projects do
expect(json_response['shared_with_groups'][0]['expires_at']).to be_nil
expect(json_response['only_allow_merge_if_pipeline_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds)
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
+ expect(json_response['ci_default_git_depth']).to eq(project.ci_default_git_depth)
expect(json_response['merge_method']).to eq(project.merge_method.to_s)
expect(json_response['readme_url']).to eq(project.readme_url)
end
@@ -1963,6 +1964,7 @@ describe API::Projects do
snippets_enabled: true,
merge_requests_enabled: true,
merge_method: 'ff',
+ ci_default_git_depth: 20,
description: 'new description' }
put api("/projects/#{project3.id}", user4), params: project_param
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 3202050ac20..d9ef5edb848 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -444,8 +444,8 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
'sha' => job.sha,
'before_sha' => job.before_sha,
'ref_type' => 'branch',
- 'refspecs' => %w[+refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/*],
- 'depth' => 0 }
+ 'refspecs' => ["+refs/heads/#{job.ref}:refs/remotes/origin/#{job.ref}"],
+ 'depth' => project.ci_default_git_depth }
end
let(:expected_steps) do
@@ -531,7 +531,11 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
end
- context 'when GIT_DEPTH is not specified' do
+ context 'when GIT_DEPTH is not specified and there is no default git depth for the project' do
+ before do
+ project.update!(ci_default_git_depth: nil)
+ end
+
it 'specifies refspecs' do
request_job
@@ -587,7 +591,11 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
end
- context 'when GIT_DEPTH is not specified' do
+ context 'when GIT_DEPTH is not specified and there is no default git depth for the project' do
+ before do
+ project.update!(ci_default_git_depth: nil)
+ end
+
it 'specifies refspecs' do
request_job
diff --git a/spec/requests/api/task_completion_status_spec.rb b/spec/requests/api/task_completion_status_spec.rb
new file mode 100644
index 00000000000..ee2531197b1
--- /dev/null
+++ b/spec/requests/api/task_completion_status_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'task completion status response' do
+ set(:user) { create(:user) }
+ set(:project) do
+ create(:project, :public, creator_id: user.id, namespace: user.namespace)
+ end
+
+ shared_examples 'taskable completion status provider' do |path|
+ samples = [
+ {
+ description: '',
+ expected_count: 0,
+ expected_completed_count: 0
+ },
+ {
+ description: 'Lorem ipsum',
+ expected_count: 0,
+ expected_completed_count: 0
+ },
+ {
+ description: %{- [ ] task 1
+ - [x] task 2 },
+ expected_count: 2,
+ expected_completed_count: 1
+ },
+ {
+ description: %{- [ ] task 1
+ - [ ] task 2 },
+ expected_count: 2,
+ expected_completed_count: 0
+ },
+ {
+ description: %{- [x] task 1
+ - [x] task 2 },
+ expected_count: 2,
+ expected_completed_count: 2
+ },
+ {
+ description: %{- [ ] task 1},
+ expected_count: 1,
+ expected_completed_count: 0
+ },
+ {
+ description: %{- [x] task 1},
+ expected_count: 1,
+ expected_completed_count: 1
+ }
+ ]
+ samples.each do |sample_data|
+ context "with a description of #{sample_data[:description].inspect}" do
+ before do
+ taskable.update!(description: sample_data[:description])
+
+ get api("#{path}?iids[]=#{taskable.iid}", user)
+ end
+
+ it { expect(response).to have_gitlab_http_status(200) }
+
+ it 'returns the expected results' do
+ expect(json_response).to be_an Array
+ expect(json_response).not_to be_empty
+
+ task_completion_status = json_response.first['task_completion_status']
+ expect(task_completion_status['count']).to eq(sample_data[:expected_count])
+ expect(task_completion_status['completed_count']).to eq(sample_data[:expected_completed_count])
+ end
+ end
+ end
+ end
+
+ context 'task list completion status for issues' do
+ it_behaves_like 'taskable completion status provider', '/issues' do
+ let(:taskable) { create(:issue, project: project, author: user) }
+ end
+ end
+
+ context 'task list completion status for merge_requests' do
+ it_behaves_like 'taskable completion status provider', '/merge_requests' do
+ let(:taskable) { create(:merge_request, source_project: project, target_project: project, author: user) }
+ end
+ end
+end
diff --git a/spec/services/auto_merge/base_service_spec.rb b/spec/services/auto_merge/base_service_spec.rb
new file mode 100644
index 00000000000..cd08e0b6f32
--- /dev/null
+++ b/spec/services/auto_merge/base_service_spec.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe AutoMerge::BaseService do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(project, user, params) }
+ let(:merge_request) { create(:merge_request) }
+ let(:params) { {} }
+
+ describe '#execute' do
+ subject { service.execute(merge_request) }
+
+ before do
+ allow(AutoMergeProcessWorker).to receive(:perform_async) {}
+ end
+
+ it 'sets properies to the merge request' do
+ subject
+
+ merge_request.reload
+ expect(merge_request).to be_auto_merge_enabled
+ expect(merge_request.merge_user).to eq(user)
+ expect(merge_request.auto_merge_strategy).to eq('base')
+ end
+
+ it 'yields block' do
+ expect { |b| service.execute(merge_request, &b) }.to yield_control.once
+ end
+
+ it 'returns activated strategy name' do
+ is_expected.to eq(:base)
+ end
+
+ context 'when merge parameters are given' do
+ let(:params) do
+ {
+ 'commit_message' => "Merge branch 'patch-12' into 'master'",
+ 'sha' => "200fcc9c260f7219eaf0daba87d818f0922c5b18",
+ 'should_remove_source_branch' => false,
+ 'squash' => false,
+ 'squash_commit_message' => "Update README.md"
+ }
+ end
+
+ it 'sets merge parameters' do
+ subject
+
+ merge_request.reload
+ expect(merge_request.merge_params['commit_message']).to eq("Merge branch 'patch-12' into 'master'")
+ expect(merge_request.merge_params['sha']).to eq('200fcc9c260f7219eaf0daba87d818f0922c5b18')
+ expect(merge_request.merge_params['should_remove_source_branch']).to eq(false)
+ expect(merge_request.merge_params['squash']).to eq(false)
+ expect(merge_request.merge_params['squash_commit_message']).to eq('Update README.md')
+ end
+ end
+
+ context 'when strategy is merge when pipeline succeeds' do
+ let(:service) { AutoMerge::MergeWhenPipelineSucceedsService.new(project, user) }
+
+ it 'sets the auto merge strategy' do
+ subject
+
+ merge_request.reload
+ expect(merge_request.auto_merge_strategy).to eq(AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
+ end
+
+ it 'returns activated strategy name' do
+ is_expected.to eq(AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS.to_sym)
+ end
+
+ it 'calls AutoMergeProcessWorker' do
+ expect(AutoMergeProcessWorker).to receive(:perform_async).with(merge_request.id).once
+
+ subject
+ end
+ end
+
+ context 'when failed to save' do
+ before do
+ allow(merge_request).to receive(:save) { false }
+ end
+
+ it 'does not yield block' do
+ expect { |b| service.execute(merge_request, &b) }.not_to yield_control
+ end
+
+ it 'returns failed' do
+ is_expected.to eq(:failed)
+ end
+ end
+ end
+
+ describe '#update' do
+ subject { service.update(merge_request) }
+
+ let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
+
+ context 'when merge params are specified' do
+ let(:params) do
+ {
+ 'commit_message' => "Merge branch 'patch-12' into 'master'",
+ 'sha' => "200fcc9c260f7219eaf0daba87d818f0922c5b18",
+ 'should_remove_source_branch' => false,
+ 'squash' => false,
+ 'squash_commit_message' => "Update README.md"
+ }
+ end
+
+ it 'updates merge params' do
+ expect { subject }.to change {
+ merge_request.reload.merge_params.slice(*params.keys)
+ }.from({}).to(params)
+ end
+ end
+ end
+
+ describe '#cancel' do
+ subject { service.cancel(merge_request) }
+
+ let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
+
+ it 'removes properies from the merge request' do
+ subject
+
+ merge_request.reload
+ expect(merge_request).not_to be_auto_merge_enabled
+ expect(merge_request.merge_user).to be_nil
+ expect(merge_request.auto_merge_strategy).to be_nil
+ end
+
+ it 'yields block' do
+ expect { |b| service.cancel(merge_request, &b) }.to yield_control.once
+ end
+
+ it 'returns success status' do
+ expect(subject[:status]).to eq(:success)
+ end
+
+ context 'when merge params are set' do
+ before do
+ merge_request.update!(merge_params:
+ {
+ 'should_remove_source_branch' => false,
+ 'commit_message' => "Merge branch 'patch-12' into 'master'",
+ 'squash_commit_message' => "Update README.md",
+ 'auto_merge_strategy' => 'merge_when_pipeline_succeeds'
+ })
+ end
+
+ it 'removes merge parameters' do
+ subject
+
+ merge_request.reload
+ expect(merge_request.merge_params['should_remove_source_branch']).to be_nil
+ expect(merge_request.merge_params['commit_message']).to be_nil
+ expect(merge_request.merge_params['squash_commit_message']).to be_nil
+ expect(merge_request.merge_params['auto_merge_strategy']).to be_nil
+ end
+ end
+
+ context 'when failed to save' do
+ before do
+ allow(merge_request).to receive(:save) { false }
+ end
+
+ it 'does not yield block' do
+ expect { |b| service.execute(merge_request, &b) }.not_to yield_control
+ end
+
+ it 'returns error status' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to eq("Can't cancel the automatic merge")
+ end
+ end
+ end
+end
diff --git a/spec/services/auto_merge_service_spec.rb b/spec/services/auto_merge_service_spec.rb
index d0eefed3150..93a22e60498 100644
--- a/spec/services/auto_merge_service_spec.rb
+++ b/spec/services/auto_merge_service_spec.rb
@@ -10,8 +10,8 @@ describe AutoMergeService do
describe '.all_strategies' do
subject { described_class.all_strategies }
- it 'returns all strategies' do
- is_expected.to eq(AutoMergeService::STRATEGIES)
+ it 'includes merge when pipeline succeeds' do
+ is_expected.to include(AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
end
end
@@ -92,6 +92,30 @@ describe AutoMergeService do
end
end
+ describe '#update' do
+ subject { service.update(merge_request) }
+
+ context 'when auto merge is enabled' do
+ let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
+
+ it 'delegates to a relevant service instance' do
+ expect_next_instance_of(AutoMerge::MergeWhenPipelineSucceedsService) do |service|
+ expect(service).to receive(:update).with(merge_request)
+ end
+
+ subject
+ end
+ end
+
+ context 'when auto merge is not enabled' do
+ let(:merge_request) { create(:merge_request) }
+
+ it 'returns failed' do
+ is_expected.to eq(:failed)
+ end
+ end
+ end
+
describe '#process' do
subject { service.process(merge_request) }
diff --git a/spec/services/ci/pipeline_schedule_service_spec.rb b/spec/services/ci/pipeline_schedule_service_spec.rb
index f2ac53cb25a..867ed0acc0d 100644
--- a/spec/services/ci/pipeline_schedule_service_spec.rb
+++ b/spec/services/ci/pipeline_schedule_service_spec.rb
@@ -24,5 +24,13 @@ describe Ci::PipelineScheduleService do
subject
end
+
+ context 'when owner is nil' do
+ let(:schedule) { create(:ci_pipeline_schedule, project: project, owner: nil) }
+
+ it 'does not raise an error' do
+ expect { subject }.not_to raise_error
+ end
+ end
end
end
diff --git a/spec/services/git/branch_hooks_service_spec.rb b/spec/services/git/branch_hooks_service_spec.rb
index 22faa996015..b5694628269 100644
--- a/spec/services/git/branch_hooks_service_spec.rb
+++ b/spec/services/git/branch_hooks_service_spec.rb
@@ -287,8 +287,8 @@ describe Git::BranchHooksService do
context 'creating the default branch' do
let(:oldrev) { Gitlab::Git::BLANK_SHA }
- it 'does not process commit messages' do
- expect(ProcessCommitWorker).not_to receive(:perform_async)
+ it 'processes a limited number of commit messages' do
+ expect(ProcessCommitWorker).to receive(:perform_async).once
service.execute
end
diff --git a/spec/services/merge_requests/merge_to_ref_service_spec.rb b/spec/services/merge_requests/merge_to_ref_service_spec.rb
index 5d492e4b013..0ac23050caf 100644
--- a/spec/services/merge_requests/merge_to_ref_service_spec.rb
+++ b/spec/services/merge_requests/merge_to_ref_service_spec.rb
@@ -32,8 +32,10 @@ describe MergeRequests::MergeToRefService do
expect(result[:status]).to eq(:success)
expect(result[:commit_id]).to be_present
+ expect(result[:source_id]).to eq(merge_request.source_branch_sha)
+ expect(result[:target_id]).to eq(merge_request.target_branch_sha)
expect(repository.ref_exists?(target_ref)).to be(true)
- expect(ref_head.sha).to eq(result[:commit_id])
+ expect(ref_head.id).to eq(result[:commit_id])
end
end
@@ -70,6 +72,10 @@ describe MergeRequests::MergeToRefService do
let(:merge_request) { create(:merge_request, :simple) }
let(:project) { merge_request.project }
+ before do
+ project.add_maintainer(user)
+ end
+
describe '#execute' do
let(:service) do
described_class.new(project, user, commit_message: 'Awesome message',
@@ -86,12 +92,6 @@ describe MergeRequests::MergeToRefService do
it_behaves_like 'successfully evaluates pre-condition checks'
context 'commit history comparison with regular MergeService' do
- before do
- # The merge service needs an authorized user while merge-to-ref
- # doesn't.
- project.add_maintainer(user)
- end
-
let(:merge_ref_service) do
described_class.new(project, user, {})
end
@@ -136,9 +136,9 @@ describe MergeRequests::MergeToRefService do
let(:merge_method) { :merge }
it 'returns error' do
- allow(project).to receive_message_chain(:repository, :merge_to_ref) { nil }
+ allow(merge_request).to receive(:mergeable_to_ref?) { false }
- error_message = 'Conflicts detected during merge'
+ error_message = "Merge request is not mergeable to #{merge_request.merge_ref_path}"
result = service.execute(merge_request)
@@ -170,5 +170,28 @@ describe MergeRequests::MergeToRefService do
it { expect(todo).not_to be_done }
end
+
+ context 'when merge request is WIP state' do
+ it 'fails to merge' do
+ merge_request = create(:merge_request, title: 'WIP: The feature')
+
+ result = service.execute(merge_request)
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq("Merge request is not mergeable to #{merge_request.merge_ref_path}")
+ end
+ end
+
+ it 'returns error when user has no authorization to admin the merge request' do
+ unauthorized_user = create(:user)
+ project.add_reporter(unauthorized_user)
+
+ service = described_class.new(project, unauthorized_user)
+
+ result = service.execute(merge_request)
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq('You are not allowed to merge to this ref')
+ end
end
end
diff --git a/spec/services/merge_requests/mergeability_check_service_spec.rb b/spec/services/merge_requests/mergeability_check_service_spec.rb
deleted file mode 100644
index aa0485467ed..00000000000
--- a/spec/services/merge_requests/mergeability_check_service_spec.rb
+++ /dev/null
@@ -1,187 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe MergeRequests::MergeabilityCheckService do
- shared_examples_for 'unmergeable merge request' do
- it 'updates or keeps merge status as cannot_be_merged' do
- subject
-
- expect(merge_request.merge_status).to eq('cannot_be_merged')
- end
-
- it 'does not change the merge ref HEAD' do
- expect { subject }.not_to change(merge_request, :merge_ref_head)
- end
-
- it 'returns ServiceResponse.error' do
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result).to be_error
- end
- end
-
- shared_examples_for 'mergeable merge request' do
- it 'updates or keeps merge status as can_be_merged' do
- subject
-
- expect(merge_request.merge_status).to eq('can_be_merged')
- end
-
- it 'updates the merge ref' do
- expect { subject }.to change(merge_request, :merge_ref_head).from(nil)
- end
-
- it 'returns ServiceResponse.success' do
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result).to be_success
- end
-
- it 'ServiceResponse has merge_ref_head payload' do
- result = subject
-
- expect(result.payload.keys).to contain_exactly(:merge_ref_head)
- expect(result.payload[:merge_ref_head].keys)
- .to contain_exactly(:commit_id, :target_id, :source_id)
- end
- end
-
- describe '#execute' do
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, merge_status: :unchecked, source_project: project, target_project: project) }
- let(:repo) { project.repository }
-
- subject { described_class.new(merge_request).execute }
-
- before do
- project.add_developer(merge_request.author)
- end
-
- it_behaves_like 'mergeable merge request'
-
- context 'when multiple calls to the service' do
- it 'returns success' do
- subject
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result.success?).to be(true)
- end
-
- it 'second call does not change the merge-ref' do
- expect { subject }.to change(merge_request, :merge_ref_head).from(nil)
- expect { subject }.not_to change(merge_request, :merge_ref_head)
- end
- end
-
- context 'when broken' do
- before do
- allow(merge_request).to receive(:broken?) { true }
- allow(project.repository).to receive(:can_be_merged?) { false }
- end
-
- it_behaves_like 'unmergeable merge request'
-
- it 'returns ServiceResponse.error' do
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result.error?).to be(true)
- expect(result.message).to eq('Merge request is not mergeable')
- end
- end
-
- context 'when it has conflicts' do
- before do
- allow(merge_request).to receive(:broken?) { false }
- allow(project.repository).to receive(:can_be_merged?) { false }
- end
-
- it_behaves_like 'unmergeable merge request'
-
- it 'returns ServiceResponse.error' do
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result.error?).to be(true)
- expect(result.message).to eq('Merge request is not mergeable')
- end
- end
-
- context 'when MR cannot be merged and has no merge ref' do
- before do
- merge_request.mark_as_unmergeable!
- end
-
- it_behaves_like 'unmergeable merge request'
-
- it 'returns ServiceResponse.error' do
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result.error?).to be(true)
- expect(result.message).to eq('Merge request is not mergeable')
- end
- end
-
- context 'when MR cannot be merged and has outdated merge ref' do
- before do
- MergeRequests::MergeToRefService.new(project, merge_request.author).execute(merge_request)
- merge_request.mark_as_unmergeable!
- end
-
- it_behaves_like 'unmergeable merge request'
-
- it 'returns ServiceResponse.error' do
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result.error?).to be(true)
- expect(result.message).to eq('Merge request is not mergeable')
- end
- end
-
- context 'when merge request is not given' do
- subject { described_class.new(nil).execute }
-
- it 'returns ServiceResponse.error' do
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result.message).to eq('Invalid argument')
- end
- end
-
- context 'when read only DB' do
- it 'returns ServiceResponse.error' do
- allow(Gitlab::Database).to receive(:read_only?) { true }
-
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result.message).to eq('Unsupported operation')
- end
- end
-
- context 'when MR is mergeable but merge-ref does not exists' do
- before do
- merge_request.mark_as_mergeable!
- end
-
- it 'keeps merge status as can_be_merged' do
- expect { subject }.not_to change(merge_request, :merge_status).from('can_be_merged')
- end
-
- it 'returns ServiceResponse.error' do
- result = subject
-
- expect(result).to be_a(ServiceResponse)
- expect(result.error?).to be(true)
- expect(result.message).to eq('Merge ref was not found')
- end
- end
- end
-end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index fbfcd95e204..f566d235787 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -406,6 +406,18 @@ describe MergeRequests::UpdateService, :mailer do
expect(pending_todo.reload).to be_done
end
end
+
+ context 'when auto merge is enabled and target branch changed' do
+ before do
+ AutoMergeService.new(project, user).execute(merge_request, AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS)
+
+ update_merge_request({ target_branch: 'target' })
+ end
+
+ it 'marks pending todos as done' do
+ expect(pending_todo.reload).to be_done
+ end
+ end
end
context 'when the merge request is relabeled' do
diff --git a/spec/services/pages_domains/create_acme_order_service_spec.rb b/spec/services/pages_domains/create_acme_order_service_spec.rb
new file mode 100644
index 00000000000..d59aa9b979e
--- /dev/null
+++ b/spec/services/pages_domains/create_acme_order_service_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PagesDomains::CreateAcmeOrderService do
+ include LetsEncryptHelpers
+
+ let(:pages_domain) { create(:pages_domain) }
+
+ let(:challenge) { ::Gitlab::LetsEncrypt::Challenge.new(acme_challenge_double) }
+
+ let(:order_double) do
+ Gitlab::LetsEncrypt::Order.new(acme_order_double).tap do |order|
+ allow(order).to receive(:new_challenge).and_return(challenge)
+ end
+ end
+
+ let(:lets_encrypt_client) do
+ instance_double('Gitlab::LetsEncrypt::Client').tap do |client|
+ allow(client).to receive(:new_order).with(pages_domain.domain)
+ .and_return(order_double)
+ end
+ end
+
+ let(:service) { described_class.new(pages_domain) }
+
+ before do
+ allow(::Gitlab::LetsEncrypt::Client).to receive(:new).and_return(lets_encrypt_client)
+ end
+
+ it 'saves order to database before requesting validation' do
+ allow(pages_domain.acme_orders).to receive(:create!).and_call_original
+ allow(challenge).to receive(:request_validation).and_call_original
+
+ service.execute
+
+ expect(pages_domain.acme_orders).to have_received(:create!).ordered
+ expect(challenge).to have_received(:request_validation).ordered
+ end
+
+ it 'generates and saves private key' do
+ service.execute
+
+ saved_order = PagesDomainAcmeOrder.last
+ expect { OpenSSL::PKey::RSA.new(saved_order.private_key) }.not_to raise_error
+ end
+
+ it 'properly saves order attributes' do
+ service.execute
+
+ saved_order = PagesDomainAcmeOrder.last
+ expect(saved_order.url).to eq(order_double.url)
+ expect(saved_order.expires_at).to be_like_time(order_double.expires)
+ end
+
+ it 'properly saves challenge attributes' do
+ service.execute
+
+ saved_order = PagesDomainAcmeOrder.last
+ expect(saved_order.challenge_token).to eq(challenge.token)
+ expect(saved_order.challenge_file_content).to eq(challenge.file_content)
+ end
+end
diff --git a/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
new file mode 100644
index 00000000000..6d7be27939c
--- /dev/null
+++ b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
@@ -0,0 +1,146 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PagesDomains::ObtainLetsEncryptCertificateService do
+ include LetsEncryptHelpers
+
+ let(:pages_domain) { create(:pages_domain, :without_certificate, :without_key) }
+ let(:service) { described_class.new(pages_domain) }
+
+ before do
+ stub_lets_encrypt_settings
+ end
+
+ def expect_to_create_acme_challenge
+ expect(::PagesDomains::CreateAcmeOrderService).to receive(:new).with(pages_domain)
+ .and_wrap_original do |m, *args|
+ create_service = m.call(*args)
+
+ expect(create_service).to receive(:execute)
+
+ create_service
+ end
+ end
+
+ def stub_lets_encrypt_order(url, status)
+ order = ::Gitlab::LetsEncrypt::Order.new(acme_order_double(status: status))
+
+ allow_any_instance_of(::Gitlab::LetsEncrypt::Client).to(
+ receive(:load_order).with(url).and_return(order)
+ )
+
+ order
+ end
+
+ context 'when there is no acme order' do
+ it 'creates acme order' do
+ expect_to_create_acme_challenge
+
+ service.execute
+ end
+ end
+
+ context 'when there is expired acme order' do
+ let!(:existing_order) do
+ create(:pages_domain_acme_order, :expired, pages_domain: pages_domain)
+ end
+
+ it 'removes acme order and creates new one' do
+ expect_to_create_acme_challenge
+
+ service.execute
+
+ expect(PagesDomainAcmeOrder.find_by_id(existing_order.id)).to be_nil
+ end
+ end
+
+ %w(pending processing).each do |status|
+ context "there is an order in '#{status}' status" do
+ let(:existing_order) do
+ create(:pages_domain_acme_order, pages_domain: pages_domain)
+ end
+
+ before do
+ stub_lets_encrypt_order(existing_order.url, status)
+ end
+
+ it 'does not raise errors' do
+ expect do
+ service.execute
+ end.not_to raise_error
+ end
+ end
+ end
+
+ context 'when order is ready' do
+ let(:existing_order) do
+ create(:pages_domain_acme_order, pages_domain: pages_domain)
+ end
+
+ let!(:api_order) do
+ stub_lets_encrypt_order(existing_order.url, 'ready')
+ end
+
+ it 'request certificate' do
+ expect(api_order).to receive(:request_certificate).and_call_original
+
+ service.execute
+ end
+ end
+
+ context 'when order is valid' do
+ let(:existing_order) do
+ create(:pages_domain_acme_order, pages_domain: pages_domain)
+ end
+
+ let!(:api_order) do
+ stub_lets_encrypt_order(existing_order.url, 'valid')
+ end
+
+ let(:certificate) do
+ key = OpenSSL::PKey.read(existing_order.private_key)
+
+ subject = "/C=BE/O=Test/OU=Test/CN=#{pages_domain.domain}"
+
+ cert = OpenSSL::X509::Certificate.new
+ cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
+ cert.not_before = Time.now
+ cert.not_after = 1.year.from_now
+ cert.public_key = key.public_key
+ cert.serial = 0x0
+ cert.version = 2
+
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.subject_certificate = cert
+ ef.issuer_certificate = cert
+ cert.extensions = [
+ ef.create_extension("basicConstraints", "CA:TRUE", true),
+ ef.create_extension("subjectKeyIdentifier", "hash")
+ ]
+ cert.add_extension ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always")
+
+ cert.sign key, OpenSSL::Digest::SHA1.new
+
+ cert.to_pem
+ end
+
+ before do
+ expect(api_order).to receive(:certificate) { certificate }
+ end
+
+ it 'saves private_key and certificate for domain' do
+ service.execute
+
+ expect(pages_domain.key).to be_present
+ expect(pages_domain.certificate).to eq(certificate)
+ end
+
+ it 'removes order from database' do
+ service.execute
+
+ expect(PagesDomainAcmeOrder.find_by_id(existing_order.id)).to be_nil
+ end
+ end
+end
diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb
index f7261cd7125..d25e9958831 100644
--- a/spec/services/preview_markdown_service_spec.rb
+++ b/spec/services/preview_markdown_service_spec.rb
@@ -56,7 +56,9 @@ describe PreviewMarkdownService do
expect(Gitlab::Diff::SuggestionsParser)
.to receive(:parse)
- .with(text, position: position, project: merge_request.project)
+ .with(text, position: position,
+ project: merge_request.project,
+ supports_suggestion: true)
.and_call_original
result = service.execute
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index ec3f1782e8f..6afc91d5e95 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -145,6 +145,30 @@ describe Projects::ForkService do
end
end
+ context "CI/CD settings" do
+ let(:to_project) { fork_project(@from_project, @to_user) }
+
+ context "when origin has git depth specified" do
+ before do
+ @from_project.update(ci_default_git_depth: 42)
+ end
+
+ it "inherits default_git_depth from the origin project" do
+ expect(to_project.ci_default_git_depth).to eq(42)
+ end
+ end
+
+ context "when origin does not define git depth" do
+ before do
+ @from_project.update!(ci_default_git_depth: nil)
+ end
+
+ it "the fork has git depth set to 0" do
+ expect(to_project.ci_default_git_depth).to eq(0)
+ end
+ end
+ end
+
context "when project has restricted visibility level" do
context "and only one visibility level is restricted" do
before do
diff --git a/spec/services/service_response_spec.rb b/spec/services/service_response_spec.rb
index e790d272e61..30bd4d6820b 100644
--- a/spec/services/service_response_spec.rb
+++ b/spec/services/service_response_spec.rb
@@ -16,13 +16,6 @@ describe ServiceResponse do
expect(response).to be_success
expect(response.message).to eq('Good orange')
end
-
- it 'creates a successful response with payload' do
- response = described_class.success(payload: { good: 'orange' })
-
- expect(response).to be_success
- expect(response.payload).to eq(good: 'orange')
- end
end
describe '.error' do
@@ -40,15 +33,6 @@ describe ServiceResponse do
expect(response.message).to eq('Bad apple')
expect(response.http_status).to eq(400)
end
-
- it 'creates a failed response with payload' do
- response = described_class.error(message: 'Bad apple',
- payload: { bad: 'apple' })
-
- expect(response).to be_error
- expect(response.message).to eq('Bad apple')
- expect(response.payload).to eq(bad: 'apple')
- end
end
describe '#success?' do
diff --git a/spec/services/suggestions/create_service_spec.rb b/spec/services/suggestions/create_service_spec.rb
index ccd44e615a8..d95f9e3349b 100644
--- a/spec/services/suggestions/create_service_spec.rb
+++ b/spec/services/suggestions/create_service_spec.rb
@@ -96,7 +96,7 @@ describe Suggestions::CreateService do
it 'creates no suggestion when diff file is not found' do
expect_next_instance_of(DiffNote) do |diff_note|
- expect(diff_note).to receive(:latest_diff_file).twice { nil }
+ expect(diff_note).to receive(:latest_diff_file).once { nil }
end
expect { subject.execute }.not_to change(Suggestion, :count)
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 875a9a76e12..56ac208a025 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -42,6 +42,9 @@ Capybara.register_driver :chrome do |app|
# Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab-ee/issues/4252
options.add_argument("disable-dev-shm-usage") if ENV['CI'] || ENV['CI_SERVER']
+ # Explicitly set user-data-dir to prevent crashes. See https://gitlab.com/gitlab-org/gitlab-ce/issues/58882#note_179811508
+ options.add_argument("user-data-dir=/tmp/chrome") if ENV['CI'] || ENV['CI_SERVER']
+
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
@@ -50,9 +53,11 @@ Capybara.register_driver :chrome do |app|
)
end
+Capybara.server = :webrick
Capybara.javascript_driver = :chrome
Capybara.default_max_wait_time = timeout
Capybara.ignore_hidden_elements = true
+Capybara.default_normalize_ws = true
# Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index 542f533d590..0f8af2c5d6d 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -84,7 +84,7 @@ shared_examples 'discussion comments' do |resource_name|
#
# if dropdown menu is not toggled (and also not present),
# it's "issue-type" dropdown
- if first(menu_selector).nil?
+ if first(menu_selector, minimum: 0).nil?
expect(find(dropdown_selector)).to have_content 'Comment'
find(toggle_selector).click
diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb
index 89dfbf931d2..5d5a0a7b5d2 100644
--- a/spec/support/features/reportable_note_shared_examples.rb
+++ b/spec/support/features/reportable_note_shared_examples.rb
@@ -20,7 +20,7 @@ shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- expect(dropdown).to have_link('Report abuse to GitLab', href: abuse_report_path)
+ expect(dropdown).to have_link('Report abuse to admin', href: abuse_report_path)
if type == 'issue' || type == 'merge_request'
expect(dropdown).to have_button('Delete comment')
@@ -33,7 +33,7 @@ shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- dropdown.click_link('Report abuse to GitLab')
+ dropdown.click_link('Report abuse to admin')
expect(find('#user_name')['value']).to match(note.author.username)
expect(find('#abuse_report_message')['value']).to match(noteable_note_url(note))
diff --git a/spec/support/helpers/lets_encrypt_helpers.rb b/spec/support/helpers/lets_encrypt_helpers.rb
index 7f0886b451c..2857416ad95 100644
--- a/spec/support/helpers/lets_encrypt_helpers.rb
+++ b/spec/support/helpers/lets_encrypt_helpers.rb
@@ -1,6 +1,26 @@
# frozen_string_literal: true
module LetsEncryptHelpers
+ ACME_ORDER_METHODS = {
+ url: 'https://example.com/',
+ status: 'valid',
+ expires: 2.days.from_now
+ }.freeze
+
+ ACME_CHALLENGE_METHODS = {
+ status: 'pending',
+ token: 'tokenvalue',
+ file_content: 'hereisfilecontent',
+ request_validation: true
+ }.freeze
+
+ def stub_lets_encrypt_settings
+ stub_application_setting(
+ lets_encrypt_notification_email: 'myemail@test.example.com',
+ lets_encrypt_terms_of_service_accepted: true
+ )
+ end
+
def stub_lets_encrypt_client
client = instance_double('Acme::Client')
@@ -16,4 +36,24 @@ module LetsEncryptHelpers
client
end
+
+ def acme_challenge_double
+ challenge = instance_double('Acme::Client::Resources::Challenges::HTTP01')
+ allow(challenge).to receive_messages(ACME_CHALLENGE_METHODS)
+ challenge
+ end
+
+ def acme_authorization_double
+ authorization = instance_double('Acme::Client::Resources::Authorization')
+ allow(authorization).to receive(:http).and_return(acme_challenge_double)
+ authorization
+ end
+
+ def acme_order_double(attributes = {})
+ acme_order = instance_double('Acme::Client::Resources::Order')
+ allow(acme_order).to receive_messages(ACME_ORDER_METHODS.merge(attributes))
+ allow(acme_order).to receive(:authorizations).and_return([acme_authorization_double])
+ allow(acme_order).to receive(:finalize)
+ acme_order
+ end
end
diff --git a/spec/support/shared_context/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index 54d9f5b15f2..1aa40dcde3d 100644
--- a/spec/support/shared_context/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -64,7 +64,7 @@ RSpec.shared_context 'ProjectPolicy context' do
%i[
change_namespace change_visibility_level rename_project remove_project
archive_project remove_fork_project destroy_merge_request destroy_issue
- set_issue_iid set_issue_created_at set_note_created_at
+ set_issue_iid set_issue_created_at set_issue_updated_at set_note_created_at
]
end
diff --git a/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb
index 782a2d97746..a931c4df99f 100644
--- a/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb
+++ b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb
@@ -20,12 +20,6 @@ shared_examples 'no assignee filter' do
end
it 'returns issuables not assigned to any assignee' do
- params[:assignee_id] = 0
-
- expect(issuables).to contain_exactly(*expected_issuables)
- end
-
- it 'returns issuables not assigned to any assignee' do
params[:assignee_id] = 'none'
expect(issuables).to contain_exactly(*expected_issuables)
diff --git a/spec/support/shared_examples/quick_actions/issuable/shrug_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/shrug_quick_action_shared_examples.rb
index 0a526808585..b592cf16f67 100644
--- a/spec/support/shared_examples/quick_actions/issuable/shrug_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/shrug_quick_action_shared_examples.rb
@@ -27,7 +27,7 @@ shared_examples 'shrug quick action' do |issuable_type|
expect(issuable.description).to eq "bug description\noops ¯\\_(ツ)_/¯"
expect(page).to have_content 'bug 345'
- expect(page).to have_content "bug description\noops ¯\\_(ツ)_/¯"
+ expect(page).to have_content "bug description oops ¯\\_(ツ)_/¯"
end
end
diff --git a/spec/support/shared_examples/quick_actions/issuable/tableflip_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/tableflip_quick_action_shared_examples.rb
index ef831e39872..c174127c4ff 100644
--- a/spec/support/shared_examples/quick_actions/issuable/tableflip_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/tableflip_quick_action_shared_examples.rb
@@ -27,7 +27,7 @@ shared_examples 'tableflip quick action' do |issuable_type|
expect(issuable.description).to eq "bug description\noops (╯°□°)╯︵ ┻━┻"
expect(page).to have_content 'bug 345'
- expect(page).to have_content "bug description\noops (╯°□°)╯︵ ┻━┻"
+ expect(page).to have_content "bug description oops (╯°□°)╯︵ ┻━┻"
end
end
diff --git a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
index d9d73f789c5..d04affc7df1 100644
--- a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
+++ b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
@@ -28,7 +28,7 @@ describe 'notify/pipeline_failed_email.html.haml' do
expect(rendered).to have_content "Your pipeline has failed"
expect(rendered).to have_content pipeline.project.name
- expect(rendered).to have_content pipeline.git_commit_message.truncate(50)
+ expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' ')
expect(rendered).to have_content pipeline.commit.author_name
expect(rendered).to have_content "##{pipeline.id}"
expect(rendered).to have_content pipeline.user.name
@@ -45,7 +45,7 @@ describe 'notify/pipeline_failed_email.html.haml' do
expect(rendered).to have_content "Your pipeline has failed"
expect(rendered).to have_content pipeline.project.name
- expect(rendered).to have_content pipeline.git_commit_message.truncate(50)
+ expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' ')
expect(rendered).to have_content pipeline.commit.author_name
expect(rendered).to have_content "##{pipeline.id}"
expect(rendered).to have_content "by API"
diff --git a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
index a7d3dc09fd4..079fb865d7b 100644
--- a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
+++ b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
@@ -30,7 +30,7 @@ describe 'notify/pipeline_failed_email.text.erb' do
expect(rendered).to have_content('Your pipeline has failed')
expect(rendered).to have_content(pipeline.project.name)
- expect(rendered).to have_content(pipeline.git_commit_message.truncate(50))
+ expect(rendered).to have_content(pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' '))
expect(rendered).to have_content(pipeline.commit.author_name)
expect(rendered).to have_content("##{pipeline.id}")
expect(rendered).to have_content(pipeline.user.name)
diff --git a/spec/views/notify/pipeline_success_email.html.haml_spec.rb b/spec/views/notify/pipeline_success_email.html.haml_spec.rb
index a793b37e412..8ee7f954d70 100644
--- a/spec/views/notify/pipeline_success_email.html.haml_spec.rb
+++ b/spec/views/notify/pipeline_success_email.html.haml_spec.rb
@@ -28,7 +28,7 @@ describe 'notify/pipeline_success_email.html.haml' do
expect(rendered).to have_content "Your pipeline has passed"
expect(rendered).to have_content pipeline.project.name
- expect(rendered).to have_content pipeline.git_commit_message.truncate(50)
+ expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' ')
expect(rendered).to have_content pipeline.commit.author_name
expect(rendered).to have_content "##{pipeline.id}"
expect(rendered).to have_content pipeline.user.name
@@ -45,7 +45,7 @@ describe 'notify/pipeline_success_email.html.haml' do
expect(rendered).to have_content "Your pipeline has passed"
expect(rendered).to have_content pipeline.project.name
- expect(rendered).to have_content pipeline.git_commit_message.truncate(50)
+ expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub!(/\s+/, ' ')
expect(rendered).to have_content pipeline.commit.author_name
expect(rendered).to have_content "##{pipeline.id}"
expect(rendered).to have_content "by API"
diff --git a/spec/views/profiles/show.html.haml_spec.rb b/spec/views/profiles/show.html.haml_spec.rb
index e89a8cb9626..9e7ac0b84fa 100644
--- a/spec/views/profiles/show.html.haml_spec.rb
+++ b/spec/views/profiles/show.html.haml_spec.rb
@@ -12,8 +12,8 @@ describe 'profiles/show' do
it 'displays the correct elements' do
render
- expect(rendered).to have_field('user_name', user.name)
- expect(rendered).to have_field('user_id', user.id)
+ expect(rendered).to have_field('user_name', with: user.name)
+ expect(rendered).to have_field('user_id', with: user.id)
end
end
end
diff --git a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
index 8a9ab02eaca..ae47f364296 100644
--- a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
+++ b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
@@ -12,10 +12,10 @@ describe 'projects/notes/_more_actions_dropdown' do
assign(:project, project)
end
- it 'shows Report abuse to GitLab button if not editable and not current users comment' do
+ it 'shows Report abuse to admin button if not editable and not current users comment' do
render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: false, note: note
- expect(rendered).to have_link('Report abuse to GitLab')
+ expect(rendered).to have_link('Report abuse to admin')
end
it 'does not show the More actions button if not editable and current users comment' do
@@ -24,10 +24,10 @@ describe 'projects/notes/_more_actions_dropdown' do
expect(rendered).not_to have_selector('.dropdown.more-actions')
end
- it 'shows Report abuse to GitLab and Delete buttons if editable and not current users comment' do
+ it 'shows Report abuse to admin and Delete buttons if editable and not current users comment' do
render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: true, note: note
- expect(rendered).to have_link('Report abuse to GitLab')
+ expect(rendered).to have_link('Report abuse to admin')
expect(rendered).to have_link('Delete comment')
end
diff --git a/vendor/assets/javascripts/snowplow/sp.js b/vendor/assets/javascripts/snowplow/sp.js
new file mode 100644
index 00000000000..7ea5f1a464f
--- /dev/null
+++ b/vendor/assets/javascripts/snowplow/sp.js
@@ -0,0 +1,22 @@
+/*
+ * Snowplow - The world's most powerful web analytics platform
+ *
+ * @description JavaScript tracker for Snowplow
+ * @version 2.10.0
+ * @author Alex Dean, Simon Andersson, Anthon Pang, Fred Blundun, Joshua Beemster, Michael Hadam
+ * @copyright Anthon Pang, Snowplow Analytics Ltd
+ * @license Simplified BSD
+ *
+ * For technical documentation:
+ * https://github.com/snowplow/snowplow/wiki/javascript-tracker
+ *
+ * For the setup guide:
+ * https://github.com/snowplow/snowplow/wiki/javascript-tracker-setup
+ *
+ * Minimum supported browsers:
+ * - Firefox 27
+ * - Chrome 32
+ * - IE 9
+ * - Safari 8
+ */
+"use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}!function o(i,c,s){function u(t,e){if(!c[t]){if(!i[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(l)return l(t,!0);var r=new Error("Cannot find module '"+t+"'");throw r.code="MODULE_NOT_FOUND",r}var a=c[t]={exports:{}};i[t][0].call(a.exports,function(e){return u(i[t][1][e]||e)},a,a.exports,o,i,c,s)}return c[t].exports}for(var l="function"==typeof require&&require,e=0;e<s.length;e++)u(s[e]);return u}({1:[function(e,t,n){this.cookie=function(e,t,n,r,a,o){return 1<arguments.length?document.cookie=e+"="+escape(t)+(n?"; expires="+new Date(+new Date+1e3*n).toUTCString():"")+(r?"; path="+r:"")+(a?"; domain="+a:"")+(o?"; secure":""):unescape((("; "+document.cookie).split("; "+e+"=")[1]||"").split(";")[0])}},{}],2:[function(e,t,n){var r={utf8:{stringToBytes:function(e){return r.bin.stringToBytes(unescape(encodeURIComponent(e)))},bytesToString:function(e){return decodeURIComponent(escape(r.bin.bytesToString(e)))}},bin:{stringToBytes:function(e){for(var t=[],n=0;n<e.length;n++)t.push(255&e.charCodeAt(n));return t},bytesToString:function(e){for(var t=[],n=0;n<e.length;n++)t.push(String.fromCharCode(e[n]));return t.join("")}}};t.exports=r},{}],3:[function(e,t,n){var o,r;o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",r={rotl:function(e,t){return e<<t|e>>>32-t},rotr:function(e,t){return e<<32-t|e>>>t},endian:function(e){if(e.constructor==Number)return 16711935&r.rotl(e,8)|4278255360&r.rotl(e,24);for(var t=0;t<e.length;t++)e[t]=r.endian(e[t]);return e},randomBytes:function(e){for(var t=[];0<e;e--)t.push(Math.floor(256*Math.random()));return t},bytesToWords:function(e){for(var t=[],n=0,r=0;n<e.length;n++,r+=8)t[r>>>5]|=e[n]<<24-r%32;return t},wordsToBytes:function(e){for(var t=[],n=0;n<32*e.length;n+=8)t.push(e[n>>>5]>>>24-n%32&255);return t},bytesToHex:function(e){for(var t=[],n=0;n<e.length;n++)t.push((e[n]>>>4).toString(16)),t.push((15&e[n]).toString(16));return t.join("")},hexToBytes:function(e){for(var t=[],n=0;n<e.length;n+=2)t.push(parseInt(e.substr(n,2),16));return t},bytesToBase64:function(e){for(var t=[],n=0;n<e.length;n+=3)for(var r=e[n]<<16|e[n+1]<<8|e[n+2],a=0;a<4;a++)8*n+6*a<=8*e.length?t.push(o.charAt(r>>>6*(3-a)&63)):t.push("=");return t.join("")},base64ToBytes:function(e){e=e.replace(/[^A-Z0-9+\/]/gi,"");for(var t=[],n=0,r=0;n<e.length;r=++n%4)0!=r&&t.push((o.indexOf(e.charAt(n-1))&Math.pow(2,-2*r+8)-1)<<2*r|o.indexOf(e.charAt(n))>>>6-2*r);return t}},t.exports=r},{}],4:[function(e,t,n){var r,a,o,i,c,s;r=this,a=function(e){var t=-e.getTimezoneOffset();return null!==t?t:0},o=function(e,t,n){var r=new Date;return void 0!==e&&r.setFullYear(e),r.setMonth(t),r.setDate(n),r},i=function(e){return a(o(e,0,2))},c=function(e){return a(o(e,5,2))},(s={determine:function(){var e,t,n,r=(e=i(),t=c(),(n=e-t)<0?e+",1":0<n?t+",1,s":e+",0");return new s.TimeZone(s.olson.timezones[r])},date_is_dst:function(e){var t=7<e.getMonth(),n=t?c(e.getFullYear()):i(e.getFullYear()),r=n-a(e);return n<0||t?0!==r:r<0},dst_start_for:function(e){var t=new Date(2010,6,15,1,0,0,0);return{"America/Denver":new Date(2011,2,13,3,0,0,0),"America/Mazatlan":new Date(2011,3,3,3,0,0,0),"America/Chicago":new Date(2011,2,13,3,0,0,0),"America/Mexico_City":new Date(2011,3,3,3,0,0,0),"America/Asuncion":new Date(2012,9,7,3,0,0,0),"America/Santiago":new Date(2012,9,3,3,0,0,0),"America/Campo_Grande":new Date(2012,9,21,5,0,0,0),"America/Montevideo":new Date(2011,9,2,3,0,0,0),"America/Sao_Paulo":new Date(2011,9,16,5,0,0,0),"America/Los_Angeles":new Date(2011,2,13,8,0,0,0),"America/Santa_Isabel":new Date(2011,3,5,8,0,0,0),"America/Havana":new Date(2012,2,10,2,0,0,0),"America/New_York":new Date(2012,2,10,7,0,0,0),"Europe/Helsinki":new Date(2013,2,31,5,0,0,0),"Pacific/Auckland":new Date(2011,8,26,7,0,0,0),"America/Halifax":new Date(2011,2,13,6,0,0,0),"America/Goose_Bay":new Date(2011,2,13,2,1,0,0),"America/Miquelon":new Date(2011,2,13,5,0,0,0),"America/Godthab":new Date(2011,2,27,1,0,0,0),"Europe/Moscow":t,"Asia/Amman":new Date(2013,2,29,1,0,0,0),"Asia/Beirut":new Date(2013,2,31,2,0,0,0),"Asia/Damascus":new Date(2013,3,6,2,0,0,0),"Asia/Jerusalem":new Date(2013,2,29,5,0,0,0),"Asia/Yekaterinburg":t,"Asia/Omsk":t,"Asia/Krasnoyarsk":t,"Asia/Irkutsk":t,"Asia/Yakutsk":t,"Asia/Vladivostok":t,"Asia/Baku":new Date(2013,2,31,4,0,0),"Asia/Yerevan":new Date(2013,2,31,3,0,0),"Asia/Kamchatka":t,"Asia/Gaza":new Date(2010,2,27,4,0,0),"Africa/Cairo":new Date(2010,4,1,3,0,0),"Europe/Minsk":t,"Pacific/Apia":new Date(2010,10,1,1,0,0,0),"Pacific/Fiji":new Date(2010,11,1,0,0,0),"Australia/Perth":new Date(2008,10,1,1,0,0,0)}[e]},TimeZone:function(e){var a={"America/Denver":["America/Denver","America/Mazatlan"],"America/Chicago":["America/Chicago","America/Mexico_City"],"America/Santiago":["America/Santiago","America/Asuncion","America/Campo_Grande"],"America/Montevideo":["America/Montevideo","America/Sao_Paulo"],"Asia/Beirut":["Asia/Amman","Asia/Jerusalem","Asia/Beirut","Europe/Helsinki","Asia/Damascus"],"Pacific/Auckland":["Pacific/Auckland","Pacific/Fiji"],"America/Los_Angeles":["America/Los_Angeles","America/Santa_Isabel"],"America/New_York":["America/Havana","America/New_York"],"America/Halifax":["America/Goose_Bay","America/Halifax"],"America/Godthab":["America/Miquelon","America/Godthab"],"Asia/Dubai":["Europe/Moscow"],"Asia/Dhaka":["Asia/Yekaterinburg"],"Asia/Jakarta":["Asia/Omsk"],"Asia/Shanghai":["Asia/Krasnoyarsk","Australia/Perth"],"Asia/Tokyo":["Asia/Irkutsk"],"Australia/Brisbane":["Asia/Yakutsk"],"Pacific/Noumea":["Asia/Vladivostok"],"Pacific/Tarawa":["Asia/Kamchatka","Pacific/Fiji"],"Pacific/Tongatapu":["Pacific/Apia"],"Asia/Baghdad":["Europe/Minsk"],"Asia/Baku":["Asia/Yerevan","Asia/Baku"],"Africa/Johannesburg":["Asia/Gaza","Africa/Cairo"]},o=e;return void 0!==a[o]&&function(){for(var e=a[o],t=e.length,n=0,r=e[0];n<t;n+=1)if(r=e[n],s.date_is_dst(s.dst_start_for(r)))return o=r}(),{name:function(){return o}}},olson:{}}).olson.timezones={"-720,0":"Pacific/Majuro","-660,0":"Pacific/Pago_Pago","-600,1":"America/Adak","-600,0":"Pacific/Honolulu","-570,0":"Pacific/Marquesas","-540,0":"Pacific/Gambier","-540,1":"America/Anchorage","-480,1":"America/Los_Angeles","-480,0":"Pacific/Pitcairn","-420,0":"America/Phoenix","-420,1":"America/Denver","-360,0":"America/Guatemala","-360,1":"America/Chicago","-360,1,s":"Pacific/Easter","-300,0":"America/Bogota","-300,1":"America/New_York","-270,0":"America/Caracas","-240,1":"America/Halifax","-240,0":"America/Santo_Domingo","-240,1,s":"America/Santiago","-210,1":"America/St_Johns","-180,1":"America/Godthab","-180,0":"America/Argentina/Buenos_Aires","-180,1,s":"America/Montevideo","-120,0":"America/Noronha","-120,1":"America/Noronha","-60,1":"Atlantic/Azores","-60,0":"Atlantic/Cape_Verde","0,0":"UTC","0,1":"Europe/London","60,1":"Europe/Berlin","60,0":"Africa/Lagos","60,1,s":"Africa/Windhoek","120,1":"Asia/Beirut","120,0":"Africa/Johannesburg","180,0":"Asia/Baghdad","180,1":"Europe/Moscow","210,1":"Asia/Tehran","240,0":"Asia/Dubai","240,1":"Asia/Baku","270,0":"Asia/Kabul","300,1":"Asia/Yekaterinburg","300,0":"Asia/Karachi","330,0":"Asia/Kolkata","345,0":"Asia/Kathmandu","360,0":"Asia/Dhaka","360,1":"Asia/Omsk","390,0":"Asia/Rangoon","420,1":"Asia/Krasnoyarsk","420,0":"Asia/Jakarta","480,0":"Asia/Shanghai","480,1":"Asia/Irkutsk","525,0":"Australia/Eucla","525,1,s":"Australia/Eucla","540,1":"Asia/Yakutsk","540,0":"Asia/Tokyo","570,0":"Australia/Darwin","570,1,s":"Australia/Adelaide","600,0":"Australia/Brisbane","600,1":"Asia/Vladivostok","600,1,s":"Australia/Sydney","630,1,s":"Australia/Lord_Howe","660,1":"Asia/Kamchatka","660,0":"Pacific/Noumea","690,0":"Pacific/Norfolk","720,1,s":"Pacific/Auckland","720,0":"Pacific/Tarawa","765,1,s":"Pacific/Chatham","780,0":"Pacific/Tongatapu","780,1,s":"Pacific/Apia","840,0":"Pacific/Kiritimati"},void 0!==n?n.jstz=s:r.jstz=s},{}],5:[function(e,t,n){var r=e("./_getNative")(e("./_root"),"DataView");t.exports=r},{"./_getNative":68,"./_root":105}],6:[function(e,t,n){var r=e("./_hashClear"),a=e("./_hashDelete"),o=e("./_hashGet"),i=e("./_hashHas"),c=e("./_hashSet");function s(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}s.prototype.clear=r,s.prototype.delete=a,s.prototype.get=o,s.prototype.has=i,s.prototype.set=c,t.exports=s},{"./_hashClear":75,"./_hashDelete":76,"./_hashGet":77,"./_hashHas":78,"./_hashSet":79}],7:[function(e,t,n){var r=e("./_listCacheClear"),a=e("./_listCacheDelete"),o=e("./_listCacheGet"),i=e("./_listCacheHas"),c=e("./_listCacheSet");function s(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}s.prototype.clear=r,s.prototype.delete=a,s.prototype.get=o,s.prototype.has=i,s.prototype.set=c,t.exports=s},{"./_listCacheClear":87,"./_listCacheDelete":88,"./_listCacheGet":89,"./_listCacheHas":90,"./_listCacheSet":91}],8:[function(e,t,n){var r=e("./_getNative")(e("./_root"),"Map");t.exports=r},{"./_getNative":68,"./_root":105}],9:[function(e,t,n){var r=e("./_mapCacheClear"),a=e("./_mapCacheDelete"),o=e("./_mapCacheGet"),i=e("./_mapCacheHas"),c=e("./_mapCacheSet");function s(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}s.prototype.clear=r,s.prototype.delete=a,s.prototype.get=o,s.prototype.has=i,s.prototype.set=c,t.exports=s},{"./_mapCacheClear":92,"./_mapCacheDelete":93,"./_mapCacheGet":94,"./_mapCacheHas":95,"./_mapCacheSet":96}],10:[function(e,t,n){var r=e("./_getNative")(e("./_root"),"Promise");t.exports=r},{"./_getNative":68,"./_root":105}],11:[function(e,t,n){var r=e("./_getNative")(e("./_root"),"Set");t.exports=r},{"./_getNative":68,"./_root":105}],12:[function(e,t,n){var r=e("./_MapCache"),a=e("./_setCacheAdd"),o=e("./_setCacheHas");function i(e){var t=-1,n=null==e?0:e.length;for(this.__data__=new r;++t<n;)this.add(e[t])}i.prototype.add=i.prototype.push=a,i.prototype.has=o,t.exports=i},{"./_MapCache":9,"./_setCacheAdd":106,"./_setCacheHas":107}],13:[function(e,t,n){var r=e("./_ListCache"),a=e("./_stackClear"),o=e("./_stackDelete"),i=e("./_stackGet"),c=e("./_stackHas"),s=e("./_stackSet");function u(e){var t=this.__data__=new r(e);this.size=t.size}u.prototype.clear=a,u.prototype.delete=o,u.prototype.get=i,u.prototype.has=c,u.prototype.set=s,t.exports=u},{"./_ListCache":7,"./_stackClear":109,"./_stackDelete":110,"./_stackGet":111,"./_stackHas":112,"./_stackSet":113}],14:[function(e,t,n){var r=e("./_root").Symbol;t.exports=r},{"./_root":105}],15:[function(e,t,n){var r=e("./_root").Uint8Array;t.exports=r},{"./_root":105}],16:[function(e,t,n){var r=e("./_getNative")(e("./_root"),"WeakMap");t.exports=r},{"./_getNative":68,"./_root":105}],17:[function(e,t,n){t.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n<r&&!1!==t(e[n],n,e););return e}},{}],18:[function(e,t,n){t.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n<r;)if(!t(e[n],n,e))return!1;return!0}},{}],19:[function(e,t,n){t.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,a=0,o=[];++n<r;){var i=e[n];t(i,n,e)&&(o[a++]=i)}return o}},{}],20:[function(e,t,n){var l=e("./_baseTimes"),f=e("./isArguments"),d=e("./isArray"),p=e("./isBuffer"),h=e("./_isIndex"),m=e("./isTypedArray"),v=Object.prototype.hasOwnProperty;t.exports=function(e,t){var n=d(e),r=!n&&f(e),a=!n&&!r&&p(e),o=!n&&!r&&!a&&m(e),i=n||r||a||o,c=i?l(e.length,String):[],s=c.length;for(var u in e)!t&&!v.call(e,u)||i&&("length"==u||a&&("offset"==u||"parent"==u)||o&&("buffer"==u||"byteLength"==u||"byteOffset"==u)||h(u,s))||c.push(u);return c}},{"./_baseTimes":50,"./_isIndex":80,"./isArguments":128,"./isArray":129,"./isBuffer":131,"./isTypedArray":140}],21:[function(e,t,n){t.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,a=Array(r);++n<r;)a[n]=t(e[n],n,e);return a}},{}],22:[function(e,t,n){t.exports=function(e,t){for(var n=-1,r=t.length,a=e.length;++n<r;)e[a+n]=t[n];return e}},{}],23:[function(e,t,n){t.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n<r;)if(t(e[n],n,e))return!0;return!1}},{}],24:[function(e,t,n){var r=e("./eq");t.exports=function(e,t){for(var n=e.length;n--;)if(r(e[n][0],t))return n;return-1}},{"./eq":118}],25:[function(e,t,n){var r=e("./_defineProperty");t.exports=function(e,t,n){"__proto__"==t&&r?r(e,t,{configurable:!0,enumerable:!0,value:n,writable:!0}):e[t]=n}},{"./_defineProperty":60}],26:[function(e,t,n){var r=e("./_baseForOwn"),a=e("./_createBaseEach")(r);t.exports=a},{"./_baseForOwn":31,"./_createBaseEach":57}],27:[function(e,t,n){var o=e("./_baseEach");t.exports=function(e,r){var a=!0;return o(e,function(e,t,n){return a=!!r(e,t,n)}),a}},{"./_baseEach":26}],28:[function(e,t,n){var o=e("./_baseEach");t.exports=function(e,r){var a=[];return o(e,function(e,t,n){r(e,t,n)&&a.push(e)}),a}},{"./_baseEach":26}],29:[function(e,t,n){t.exports=function(e,t,n,r){for(var a=e.length,o=n+(r?1:-1);r?o--:++o<a;)if(t(e[o],o,e))return o;return-1}},{}],30:[function(e,t,n){var r=e("./_createBaseFor")();t.exports=r},{"./_createBaseFor":58}],31:[function(e,t,n){var r=e("./_baseFor"),a=e("./keys");t.exports=function(e,t){return e&&r(e,t,a)}},{"./_baseFor":30,"./keys":142}],32:[function(e,t,n){var a=e("./_castPath"),o=e("./_toKey");t.exports=function(e,t){for(var n=0,r=(t=a(t,e)).length;null!=e&&n<r;)e=e[o(t[n++])];return n&&n==r?e:void 0}},{"./_castPath":55,"./_toKey":115}],33:[function(e,t,n){var a=e("./_arrayPush"),o=e("./isArray");t.exports=function(e,t,n){var r=t(e);return o(e)?r:a(r,n(e))}},{"./_arrayPush":22,"./isArray":129}],34:[function(e,t,n){var r=e("./_Symbol"),a=e("./_getRawTag"),o=e("./_objectToString"),i=r?r.toStringTag:void 0;t.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":i&&i in Object(e)?a(e):o(e)}},{"./_Symbol":14,"./_getRawTag":70,"./_objectToString":103}],35:[function(e,t,n){var r=Object.prototype.hasOwnProperty;t.exports=function(e,t){return null!=e&&r.call(e,t)}},{}],36:[function(e,t,n){t.exports=function(e,t){return null!=e&&t in Object(e)}},{}],37:[function(e,t,n){var r=e("./_baseGetTag"),a=e("./isObjectLike");t.exports=function(e){return a(e)&&"[object Arguments]"==r(e)}},{"./_baseGetTag":34,"./isObjectLike":136}],38:[function(e,t,n){var i=e("./_baseIsEqualDeep"),c=e("./isObjectLike");t.exports=function e(t,n,r,a,o){return t===n||(null==t||null==n||!c(t)&&!c(n)?t!=t&&n!=n:i(t,n,r,a,e,o))}},{"./_baseIsEqualDeep":39,"./isObjectLike":136}],39:[function(e,t,n){var g=e("./_Stack"),y=e("./_equalArrays"),_=e("./_equalByTag"),b=e("./_equalObjects"),w=e("./_getTag"),k=e("./isArray"),A=e("./isBuffer"),x=e("./isTypedArray"),C="[object Arguments]",S="[object Array]",j="[object Object]",T=Object.prototype.hasOwnProperty;t.exports=function(e,t,n,r,a,o){var i=k(e),c=k(t),s=i?S:w(e),u=c?S:w(t),l=(s=s==C?j:s)==j,f=(u=u==C?j:u)==j,d=s==u;if(d&&A(e)){if(!A(t))return!1;l=!(i=!0)}if(d&&!l)return o||(o=new g),i||x(e)?y(e,t,n,r,a,o):_(e,t,s,n,r,a,o);if(!(1&n)){var p=l&&T.call(e,"__wrapped__"),h=f&&T.call(t,"__wrapped__");if(p||h){var m=p?e.value():e,v=h?t.value():t;return o||(o=new g),a(m,v,n,r,o)}}return!!d&&(o||(o=new g),b(e,t,n,r,a,o))}},{"./_Stack":13,"./_equalArrays":61,"./_equalByTag":62,"./_equalObjects":63,"./_getTag":72,"./isArray":129,"./isBuffer":131,"./isTypedArray":140}],40:[function(e,t,n){var p=e("./_Stack"),h=e("./_baseIsEqual");t.exports=function(e,t,n,r){var a=n.length,o=a,i=!r;if(null==e)return!o;for(e=Object(e);a--;){var c=n[a];if(i&&c[2]?c[1]!==e[c[0]]:!(c[0]in e))return!1}for(;++a<o;){var s=(c=n[a])[0],u=e[s],l=c[1];if(i&&c[2]){if(void 0===u&&!(s in e))return!1}else{var f=new p;if(r)var d=r(u,l,s,e,t,f);if(!(void 0===d?h(l,u,3,r,f):d))return!1}}return!0}},{"./_Stack":13,"./_baseIsEqual":38}],41:[function(e,t,n){var r=e("./isFunction"),a=e("./_isMasked"),o=e("./isObject"),i=e("./_toSource"),c=/^\[object .+?Constructor\]$/,s=Function.prototype,u=Object.prototype,l=s.toString,f=u.hasOwnProperty,d=RegExp("^"+l.call(f).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=function(e){return!(!o(e)||a(e))&&(r(e)?d:c).test(i(e))}},{"./_isMasked":84,"./_toSource":116,"./isFunction":133,"./isObject":135}],42:[function(e,t,n){var r=e("./_baseGetTag"),a=e("./isLength"),o=e("./isObjectLike"),i={};i["[object Float32Array]"]=i["[object Float64Array]"]=i["[object Int8Array]"]=i["[object Int16Array]"]=i["[object Int32Array]"]=i["[object Uint8Array]"]=i["[object Uint8ClampedArray]"]=i["[object Uint16Array]"]=i["[object Uint32Array]"]=!0,i["[object Arguments]"]=i["[object Array]"]=i["[object ArrayBuffer]"]=i["[object Boolean]"]=i["[object DataView]"]=i["[object Date]"]=i["[object Error]"]=i["[object Function]"]=i["[object Map]"]=i["[object Number]"]=i["[object Object]"]=i["[object RegExp]"]=i["[object Set]"]=i["[object String]"]=i["[object WeakMap]"]=!1,t.exports=function(e){return o(e)&&a(e.length)&&!!i[r(e)]}},{"./_baseGetTag":34,"./isLength":134,"./isObjectLike":136}],43:[function(e,t,n){var r=e("./_baseMatches"),a=e("./_baseMatchesProperty"),o=e("./identity"),i=e("./isArray"),c=e("./property");t.exports=function(e){return"function"==typeof e?e:null==e?o:"object"==_typeof(e)?i(e)?a(e[0],e[1]):r(e):c(e)}},{"./_baseMatches":46,"./_baseMatchesProperty":47,"./identity":127,"./isArray":129,"./property":146}],44:[function(e,t,n){var r=e("./_isPrototype"),a=e("./_nativeKeys"),o=Object.prototype.hasOwnProperty;t.exports=function(e){if(!r(e))return a(e);var t=[];for(var n in Object(e))o.call(e,n)&&"constructor"!=n&&t.push(n);return t}},{"./_isPrototype":85,"./_nativeKeys":101}],45:[function(e,t,n){var i=e("./_baseEach"),c=e("./isArrayLike");t.exports=function(e,r){var a=-1,o=c(e)?Array(e.length):[];return i(e,function(e,t,n){o[++a]=r(e,t,n)}),o}},{"./_baseEach":26,"./isArrayLike":130}],46:[function(e,t,n){var r=e("./_baseIsMatch"),a=e("./_getMatchData"),o=e("./_matchesStrictComparable");t.exports=function(t){var n=a(t);return 1==n.length&&n[0][2]?o(n[0][0],n[0][1]):function(e){return e===t||r(e,t,n)}}},{"./_baseIsMatch":40,"./_getMatchData":67,"./_matchesStrictComparable":98}],47:[function(e,t,n){var a=e("./_baseIsEqual"),o=e("./get"),i=e("./hasIn"),c=e("./_isKey"),s=e("./_isStrictComparable"),u=e("./_matchesStrictComparable"),l=e("./_toKey");t.exports=function(n,r){return c(n)&&s(r)?u(l(n),r):function(e){var t=o(e,n);return void 0===t&&t===r?i(e,n):a(r,t,3)}}},{"./_baseIsEqual":38,"./_isKey":82,"./_isStrictComparable":86,"./_matchesStrictComparable":98,"./_toKey":115,"./get":124,"./hasIn":126}],48:[function(e,t,n){t.exports=function(t){return function(e){return null==e?void 0:e[t]}}},{}],49:[function(e,t,n){var r=e("./_baseGet");t.exports=function(t){return function(e){return r(e,t)}}},{"./_baseGet":32}],50:[function(e,t,n){t.exports=function(e,t){for(var n=-1,r=Array(e);++n<e;)r[n]=t(n);return r}},{}],51:[function(e,t,n){var r=e("./_Symbol"),a=e("./_arrayMap"),o=e("./isArray"),i=e("./isSymbol"),c=1/0,s=r?r.prototype:void 0,u=s?s.toString:void 0;t.exports=function e(t){if("string"==typeof t)return t;if(o(t))return a(t,e)+"";if(i(t))return u?u.call(t):"";var n=t+"";return"0"==n&&1/t==-c?"-0":n}},{"./_Symbol":14,"./_arrayMap":21,"./isArray":129,"./isSymbol":139}],52:[function(e,t,n){t.exports=function(t){return function(e){return t(e)}}},{}],53:[function(e,t,n){t.exports=function(e,t){return e.has(t)}},{}],54:[function(e,t,n){var r=e("./identity");t.exports=function(e){return"function"==typeof e?e:r}},{"./identity":127}],55:[function(e,t,n){var r=e("./isArray"),a=e("./_isKey"),o=e("./_stringToPath"),i=e("./toString");t.exports=function(e,t){return r(e)?e:a(e,t)?[e]:o(i(e))}},{"./_isKey":82,"./_stringToPath":114,"./isArray":129,"./toString":152}],56:[function(e,t,n){var r=e("./_root")["__core-js_shared__"];t.exports=r},{"./_root":105}],57:[function(e,t,n){var c=e("./isArrayLike");t.exports=function(o,i){return function(e,t){if(null==e)return e;if(!c(e))return o(e,t);for(var n=e.length,r=i?n:-1,a=Object(e);(i?r--:++r<n)&&!1!==t(a[r],r,a););return e}}},{"./isArrayLike":130}],58:[function(e,t,n){t.exports=function(s){return function(e,t,n){for(var r=-1,a=Object(e),o=n(e),i=o.length;i--;){var c=o[s?i:++r];if(!1===t(a[c],c,a))break}return e}}},{}],59:[function(e,t,n){var c=e("./_baseIteratee"),s=e("./isArrayLike"),u=e("./keys");t.exports=function(i){return function(e,t,n){var r=Object(e);if(!s(e)){var a=c(t,3);e=u(e),t=function(e){return a(r[e],e,r)}}var o=i(e,t,n);return-1<o?r[a?e[o]:o]:void 0}}},{"./_baseIteratee":43,"./isArrayLike":130,"./keys":142}],60:[function(e,t,n){var r=e("./_getNative"),a=function(){try{var e=r(Object,"defineProperty");return e({},"",{}),e}catch(e){}}();t.exports=a},{"./_getNative":68}],61:[function(e,t,n){var v=e("./_SetCache"),g=e("./_arraySome"),y=e("./_cacheHas");t.exports=function(e,t,n,r,a,o){var i=1&n,c=e.length,s=t.length;if(c!=s&&!(i&&c<s))return!1;var u=o.get(e);if(u&&o.get(t))return u==t;var l=-1,f=!0,d=2&n?new v:void 0;for(o.set(e,t),o.set(t,e);++l<c;){var p=e[l],h=t[l];if(r)var m=i?r(h,p,l,t,e,o):r(p,h,l,e,t,o);if(void 0!==m){if(m)continue;f=!1;break}if(d){if(!g(t,function(e,t){if(!y(d,t)&&(p===e||a(p,e,n,r,o)))return d.push(t)})){f=!1;break}}else if(p!==h&&!a(p,h,n,r,o)){f=!1;break}}return o.delete(e),o.delete(t),f}},{"./_SetCache":12,"./_arraySome":23,"./_cacheHas":53}],62:[function(e,t,n){var r=e("./_Symbol"),f=e("./_Uint8Array"),d=e("./eq"),p=e("./_equalArrays"),h=e("./_mapToArray"),m=e("./_setToArray"),a=r?r.prototype:void 0,v=a?a.valueOf:void 0;t.exports=function(e,t,n,r,a,o,i){switch(n){case"[object DataView]":if(e.byteLength!=t.byteLength||e.byteOffset!=t.byteOffset)return!1;e=e.buffer,t=t.buffer;case"[object ArrayBuffer]":return!(e.byteLength!=t.byteLength||!o(new f(e),new f(t)));case"[object Boolean]":case"[object Date]":case"[object Number]":return d(+e,+t);case"[object Error]":return e.name==t.name&&e.message==t.message;case"[object RegExp]":case"[object String]":return e==t+"";case"[object Map]":var c=h;case"[object Set]":var s=1&r;if(c||(c=m),e.size!=t.size&&!s)return!1;var u=i.get(e);if(u)return u==t;r|=2,i.set(e,t);var l=p(c(e),c(t),r,a,o,i);return i.delete(e),l;case"[object Symbol]":if(v)return v.call(e)==v.call(t)}return!1}},{"./_Symbol":14,"./_Uint8Array":15,"./_equalArrays":61,"./_mapToArray":97,"./_setToArray":108,"./eq":118}],63:[function(e,t,n){var _=e("./_getAllKeys"),b=Object.prototype.hasOwnProperty;t.exports=function(e,t,n,r,a,o){var i=1&n,c=_(e),s=c.length;if(s!=_(t).length&&!i)return!1;for(var u=s;u--;){var l=c[u];if(!(i?l in t:b.call(t,l)))return!1}var f=o.get(e);if(f&&o.get(t))return f==t;var d=!0;o.set(e,t),o.set(t,e);for(var p=i;++u<s;){var h=e[l=c[u]],m=t[l];if(r)var v=i?r(m,h,l,t,e,o):r(h,m,l,e,t,o);if(!(void 0===v?h===m||a(h,m,n,r,o):v)){d=!1;break}p||(p="constructor"==l)}if(d&&!p){var g=e.constructor,y=t.constructor;g!=y&&"constructor"in e&&"constructor"in t&&!("function"==typeof g&&g instanceof g&&"function"==typeof y&&y instanceof y)&&(d=!1)}return o.delete(e),o.delete(t),d}},{"./_getAllKeys":65}],64:[function(e,n,t){(function(e){var t="object"==_typeof(e)&&e&&e.Object===Object&&e;n.exports=t}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],65:[function(e,t,n){var r=e("./_baseGetAllKeys"),a=e("./_getSymbols"),o=e("./keys");t.exports=function(e){return r(e,o,a)}},{"./_baseGetAllKeys":33,"./_getSymbols":71,"./keys":142}],66:[function(e,t,n){var r=e("./_isKeyable");t.exports=function(e,t){var n=e.__data__;return r(t)?n["string"==typeof t?"string":"hash"]:n.map}},{"./_isKeyable":83}],67:[function(e,t,n){var o=e("./_isStrictComparable"),i=e("./keys");t.exports=function(e){for(var t=i(e),n=t.length;n--;){var r=t[n],a=e[r];t[n]=[r,a,o(a)]}return t}},{"./_isStrictComparable":86,"./keys":142}],68:[function(e,t,n){var r=e("./_baseIsNative"),a=e("./_getValue");t.exports=function(e,t){var n=a(e,t);return r(n)?n:void 0}},{"./_baseIsNative":41,"./_getValue":73}],69:[function(e,t,n){var r=e("./_overArg")(Object.getPrototypeOf,Object);t.exports=r},{"./_overArg":104}],70:[function(e,t,n){var r=e("./_Symbol"),a=Object.prototype,o=a.hasOwnProperty,i=a.toString,c=r?r.toStringTag:void 0;t.exports=function(e){var t=o.call(e,c),n=e[c];try{var r=!(e[c]=void 0)}catch(e){}var a=i.call(e);return r&&(t?e[c]=n:delete e[c]),a}},{"./_Symbol":14}],71:[function(e,t,n){var r=e("./_arrayFilter"),a=e("./stubArray"),o=Object.prototype.propertyIsEnumerable,i=Object.getOwnPropertySymbols,c=i?function(t){return null==t?[]:(t=Object(t),r(i(t),function(e){return o.call(t,e)}))}:a;t.exports=c},{"./_arrayFilter":19,"./stubArray":147}],72:[function(e,t,n){var r=e("./_DataView"),a=e("./_Map"),o=e("./_Promise"),i=e("./_Set"),c=e("./_WeakMap"),s=e("./_baseGetTag"),u=e("./_toSource"),l="[object Map]",f="[object Promise]",d="[object Set]",p="[object WeakMap]",h="[object DataView]",m=u(r),v=u(a),g=u(o),y=u(i),_=u(c),b=s;(r&&b(new r(new ArrayBuffer(1)))!=h||a&&b(new a)!=l||o&&b(o.resolve())!=f||i&&b(new i)!=d||c&&b(new c)!=p)&&(b=function(e){var t=s(e),n="[object Object]"==t?e.constructor:void 0,r=n?u(n):"";if(r)switch(r){case m:return h;case v:return l;case g:return f;case y:return d;case _:return p}return t}),t.exports=b},{"./_DataView":5,"./_Map":8,"./_Promise":10,"./_Set":11,"./_WeakMap":16,"./_baseGetTag":34,"./_toSource":116}],73:[function(e,t,n){t.exports=function(e,t){return null==e?void 0:e[t]}},{}],74:[function(e,t,n){var c=e("./_castPath"),s=e("./isArguments"),u=e("./isArray"),l=e("./_isIndex"),f=e("./isLength"),d=e("./_toKey");t.exports=function(e,t,n){for(var r=-1,a=(t=c(t,e)).length,o=!1;++r<a;){var i=d(t[r]);if(!(o=null!=e&&n(e,i)))break;e=e[i]}return o||++r!=a?o:!!(a=null==e?0:e.length)&&f(a)&&l(i,a)&&(u(e)||s(e))}},{"./_castPath":55,"./_isIndex":80,"./_toKey":115,"./isArguments":128,"./isArray":129,"./isLength":134}],75:[function(e,t,n){var r=e("./_nativeCreate");t.exports=function(){this.__data__=r?r(null):{},this.size=0}},{"./_nativeCreate":100}],76:[function(e,t,n){t.exports=function(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t}},{}],77:[function(e,t,n){var r=e("./_nativeCreate"),a=Object.prototype.hasOwnProperty;t.exports=function(e){var t=this.__data__;if(r){var n=t[e];return"__lodash_hash_undefined__"===n?void 0:n}return a.call(t,e)?t[e]:void 0}},{"./_nativeCreate":100}],78:[function(e,t,n){var r=e("./_nativeCreate"),a=Object.prototype.hasOwnProperty;t.exports=function(e){var t=this.__data__;return r?void 0!==t[e]:a.call(t,e)}},{"./_nativeCreate":100}],79:[function(e,t,n){var r=e("./_nativeCreate");t.exports=function(e,t){var n=this.__data__;return this.size+=this.has(e)?0:1,n[e]=r&&void 0===t?"__lodash_hash_undefined__":t,this}},{"./_nativeCreate":100}],80:[function(e,t,n){var r=/^(?:0|[1-9]\d*)$/;t.exports=function(e,t){var n=_typeof(e);return!!(t=null==t?9007199254740991:t)&&("number"==n||"symbol"!=n&&r.test(e))&&-1<e&&e%1==0&&e<t}},{}],81:[function(e,t,n){var a=e("./eq"),o=e("./isArrayLike"),i=e("./_isIndex"),c=e("./isObject");t.exports=function(e,t,n){if(!c(n))return!1;var r=_typeof(t);return!!("number"==r?o(n)&&i(t,n.length):"string"==r&&t in n)&&a(n[t],e)}},{"./_isIndex":80,"./eq":118,"./isArrayLike":130,"./isObject":135}],82:[function(e,t,n){var r=e("./isArray"),a=e("./isSymbol"),o=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,i=/^\w*$/;t.exports=function(e,t){if(r(e))return!1;var n=_typeof(e);return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!a(e))||i.test(e)||!o.test(e)||null!=t&&e in Object(t)}},{"./isArray":129,"./isSymbol":139}],83:[function(e,t,n){t.exports=function(e){var t=_typeof(e);return"string"==t||"number"==t||"symbol"==t||"boolean"==t?"__proto__"!==e:null===e}},{}],84:[function(e,t,n){var r,a=e("./_coreJsData"),o=(r=/[^.]+$/.exec(a&&a.keys&&a.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";t.exports=function(e){return!!o&&o in e}},{"./_coreJsData":56}],85:[function(e,t,n){var r=Object.prototype;t.exports=function(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||r)}},{}],86:[function(e,t,n){var r=e("./isObject");t.exports=function(e){return e==e&&!r(e)}},{"./isObject":135}],87:[function(e,t,n){t.exports=function(){this.__data__=[],this.size=0}},{}],88:[function(e,t,n){var r=e("./_assocIndexOf"),a=Array.prototype.splice;t.exports=function(e){var t=this.__data__,n=r(t,e);return!(n<0||(n==t.length-1?t.pop():a.call(t,n,1),--this.size,0))}},{"./_assocIndexOf":24}],89:[function(e,t,n){var r=e("./_assocIndexOf");t.exports=function(e){var t=this.__data__,n=r(t,e);return n<0?void 0:t[n][1]}},{"./_assocIndexOf":24}],90:[function(e,t,n){var r=e("./_assocIndexOf");t.exports=function(e){return-1<r(this.__data__,e)}},{"./_assocIndexOf":24}],91:[function(e,t,n){var a=e("./_assocIndexOf");t.exports=function(e,t){var n=this.__data__,r=a(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}},{"./_assocIndexOf":24}],92:[function(e,t,n){var r=e("./_Hash"),a=e("./_ListCache"),o=e("./_Map");t.exports=function(){this.size=0,this.__data__={hash:new r,map:new(o||a),string:new r}}},{"./_Hash":6,"./_ListCache":7,"./_Map":8}],93:[function(e,t,n){var r=e("./_getMapData");t.exports=function(e){var t=r(this,e).delete(e);return this.size-=t?1:0,t}},{"./_getMapData":66}],94:[function(e,t,n){var r=e("./_getMapData");t.exports=function(e){return r(this,e).get(e)}},{"./_getMapData":66}],95:[function(e,t,n){var r=e("./_getMapData");t.exports=function(e){return r(this,e).has(e)}},{"./_getMapData":66}],96:[function(e,t,n){var a=e("./_getMapData");t.exports=function(e,t){var n=a(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this}},{"./_getMapData":66}],97:[function(e,t,n){t.exports=function(e){var n=-1,r=Array(e.size);return e.forEach(function(e,t){r[++n]=[t,e]}),r}},{}],98:[function(e,t,n){t.exports=function(t,n){return function(e){return null!=e&&e[t]===n&&(void 0!==n||t in Object(e))}}},{}],99:[function(e,t,n){var r=e("./memoize");t.exports=function(e){var t=r(e,function(e){return 500===n.size&&n.clear(),e}),n=t.cache;return t}},{"./memoize":145}],100:[function(e,t,n){var r=e("./_getNative")(Object,"create");t.exports=r},{"./_getNative":68}],101:[function(e,t,n){var r=e("./_overArg")(Object.keys,Object);t.exports=r},{"./_overArg":104}],102:[function(e,t,n){var r=e("./_freeGlobal"),a="object"==_typeof(n)&&n&&!n.nodeType&&n,o=a&&"object"==_typeof(t)&&t&&!t.nodeType&&t,i=o&&o.exports===a&&r.process,c=function(){try{var e=o&&o.require&&o.require("util").types;return e||i&&i.binding&&i.binding("util")}catch(e){}}();t.exports=c},{"./_freeGlobal":64}],103:[function(e,t,n){var r=Object.prototype.toString;t.exports=function(e){return r.call(e)}},{}],104:[function(e,t,n){t.exports=function(t,n){return function(e){return t(n(e))}}},{}],105:[function(e,t,n){var r=e("./_freeGlobal"),a="object"==("undefined"==typeof self?"undefined":_typeof(self))&&self&&self.Object===Object&&self,o=r||a||Function("return this")();t.exports=o},{"./_freeGlobal":64}],106:[function(e,t,n){t.exports=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this}},{}],107:[function(e,t,n){t.exports=function(e){return this.__data__.has(e)}},{}],108:[function(e,t,n){t.exports=function(e){var t=-1,n=Array(e.size);return e.forEach(function(e){n[++t]=e}),n}},{}],109:[function(e,t,n){var r=e("./_ListCache");t.exports=function(){this.__data__=new r,this.size=0}},{"./_ListCache":7}],110:[function(e,t,n){t.exports=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n}},{}],111:[function(e,t,n){t.exports=function(e){return this.__data__.get(e)}},{}],112:[function(e,t,n){t.exports=function(e){return this.__data__.has(e)}},{}],113:[function(e,t,n){var a=e("./_ListCache"),o=e("./_Map"),i=e("./_MapCache");t.exports=function(e,t){var n=this.__data__;if(n instanceof a){var r=n.__data__;if(!o||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new i(r)}return n.set(e,t),this.size=n.size,this}},{"./_ListCache":7,"./_Map":8,"./_MapCache":9}],114:[function(e,t,n){var r=e("./_memoizeCapped"),o=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,i=/\\(\\)?/g,a=r(function(e){var a=[];return 46===e.charCodeAt(0)&&a.push(""),e.replace(o,function(e,t,n,r){a.push(n?r.replace(i,"$1"):t||e)}),a});t.exports=a},{"./_memoizeCapped":99}],115:[function(e,t,n){var r=e("./isSymbol");t.exports=function(e){if("string"==typeof e||r(e))return e;var t=e+"";return"0"==t&&1/e==-1/0?"-0":t}},{"./isSymbol":139}],116:[function(e,t,n){var r=Function.prototype.toString;t.exports=function(e){if(null!=e){try{return r.call(e)}catch(e){}try{return e+""}catch(e){}}return""}},{}],117:[function(e,t,n){t.exports=function(e){for(var t=-1,n=null==e?0:e.length,r=0,a=[];++t<n;){var o=e[t];o&&(a[r++]=o)}return a}},{}],118:[function(e,t,n){t.exports=function(e,t){return e===t||e!=e&&t!=t}},{}],119:[function(e,t,n){var a=e("./_arrayEvery"),o=e("./_baseEvery"),i=e("./_baseIteratee"),c=e("./isArray"),s=e("./_isIterateeCall");t.exports=function(e,t,n){var r=c(e)?a:o;return n&&s(e,t,n)&&(t=void 0),r(e,i(t,3))}},{"./_arrayEvery":18,"./_baseEvery":27,"./_baseIteratee":43,"./_isIterateeCall":81,"./isArray":129}],120:[function(e,t,n){var r=e("./_arrayFilter"),a=e("./_baseFilter"),o=e("./_baseIteratee"),i=e("./isArray");t.exports=function(e,t){return(i(e)?r:a)(e,o(t,3))}},{"./_arrayFilter":19,"./_baseFilter":28,"./_baseIteratee":43,"./isArray":129}],121:[function(e,t,n){var r=e("./_createFind")(e("./findIndex"));t.exports=r},{"./_createFind":59,"./findIndex":122}],122:[function(e,t,n){var o=e("./_baseFindIndex"),i=e("./_baseIteratee"),c=e("./toInteger"),s=Math.max;t.exports=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var a=null==n?0:c(n);return a<0&&(a=s(r+a,0)),o(e,i(t,3),a)}},{"./_baseFindIndex":29,"./_baseIteratee":43,"./toInteger":150}],123:[function(e,t,n){var r=e("./_arrayEach"),a=e("./_baseEach"),o=e("./_castFunction"),i=e("./isArray");t.exports=function(e,t){return(i(e)?r:a)(e,o(t))}},{"./_arrayEach":17,"./_baseEach":26,"./_castFunction":54,"./isArray":129}],124:[function(e,t,n){var a=e("./_baseGet");t.exports=function(e,t,n){var r=null==e?void 0:a(e,t);return void 0===r?n:r}},{"./_baseGet":32}],125:[function(e,t,n){var r=e("./_baseHas"),a=e("./_hasPath");t.exports=function(e,t){return null!=e&&a(e,t,r)}},{"./_baseHas":35,"./_hasPath":74}],126:[function(e,t,n){var r=e("./_baseHasIn"),a=e("./_hasPath");t.exports=function(e,t){return null!=e&&a(e,t,r)}},{"./_baseHasIn":36,"./_hasPath":74}],127:[function(e,t,n){t.exports=function(e){return e}},{}],128:[function(e,t,n){var r=e("./_baseIsArguments"),a=e("./isObjectLike"),o=Object.prototype,i=o.hasOwnProperty,c=o.propertyIsEnumerable,s=r(function(){return arguments}())?r:function(e){return a(e)&&i.call(e,"callee")&&!c.call(e,"callee")};t.exports=s},{"./_baseIsArguments":37,"./isObjectLike":136}],129:[function(e,t,n){var r=Array.isArray;t.exports=r},{}],130:[function(e,t,n){var r=e("./isFunction"),a=e("./isLength");t.exports=function(e){return null!=e&&a(e.length)&&!r(e)}},{"./isFunction":133,"./isLength":134}],131:[function(e,t,n){var r=e("./_root"),a=e("./stubFalse"),o="object"==_typeof(n)&&n&&!n.nodeType&&n,i=o&&"object"==_typeof(t)&&t&&!t.nodeType&&t,c=i&&i.exports===o?r.Buffer:void 0,s=(c?c.isBuffer:void 0)||a;t.exports=s},{"./_root":105,"./stubFalse":148}],132:[function(e,t,n){var r=e("./_baseIsEqual");t.exports=function(e,t){return r(e,t)}},{"./_baseIsEqual":38}],133:[function(e,t,n){var r=e("./_baseGetTag"),a=e("./isObject");t.exports=function(e){if(!a(e))return!1;var t=r(e);return"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t}},{"./_baseGetTag":34,"./isObject":135}],134:[function(e,t,n){t.exports=function(e){return"number"==typeof e&&-1<e&&e%1==0&&e<=9007199254740991}},{}],135:[function(e,t,n){t.exports=function(e){var t=_typeof(e);return null!=e&&("object"==t||"function"==t)}},{}],136:[function(e,t,n){t.exports=function(e){return null!=e&&"object"==_typeof(e)}},{}],137:[function(e,t,n){var r=e("./_baseGetTag"),a=e("./_getPrototype"),o=e("./isObjectLike"),i=Function.prototype,c=Object.prototype,s=i.toString,u=c.hasOwnProperty,l=s.call(Object);t.exports=function(e){if(!o(e)||"[object Object]"!=r(e))return!1;var t=a(e);if(null===t)return!0;var n=u.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&s.call(n)==l}},{"./_baseGetTag":34,"./_getPrototype":69,"./isObjectLike":136}],138:[function(e,t,n){var r=e("./_baseGetTag"),a=e("./isArray"),o=e("./isObjectLike");t.exports=function(e){return"string"==typeof e||!a(e)&&o(e)&&"[object String]"==r(e)}},{"./_baseGetTag":34,"./isArray":129,"./isObjectLike":136}],139:[function(e,t,n){var r=e("./_baseGetTag"),a=e("./isObjectLike");t.exports=function(e){return"symbol"==_typeof(e)||a(e)&&"[object Symbol]"==r(e)}},{"./_baseGetTag":34,"./isObjectLike":136}],140:[function(e,t,n){var r=e("./_baseIsTypedArray"),a=e("./_baseUnary"),o=e("./_nodeUtil"),i=o&&o.isTypedArray,c=i?a(i):r;t.exports=c},{"./_baseIsTypedArray":42,"./_baseUnary":52,"./_nodeUtil":102}],141:[function(e,t,n){t.exports=function(e){return void 0===e}},{}],142:[function(e,t,n){var r=e("./_arrayLikeKeys"),a=e("./_baseKeys"),o=e("./isArrayLike");t.exports=function(e){return o(e)?r(e):a(e)}},{"./_arrayLikeKeys":20,"./_baseKeys":44,"./isArrayLike":130}],143:[function(e,t,n){var r=e("./_arrayMap"),a=e("./_baseIteratee"),o=e("./_baseMap"),i=e("./isArray");t.exports=function(e,t){return(i(e)?r:o)(e,a(t,3))}},{"./_arrayMap":21,"./_baseIteratee":43,"./_baseMap":45,"./isArray":129}],144:[function(e,t,n){var o=e("./_baseAssignValue"),i=e("./_baseForOwn"),c=e("./_baseIteratee");t.exports=function(e,r){var a={};return r=c(r,3),i(e,function(e,t,n){o(a,t,r(e,t,n))}),a}},{"./_baseAssignValue":25,"./_baseForOwn":31,"./_baseIteratee":43}],145:[function(e,t,n){var r=e("./_MapCache"),a="Expected a function";function c(o,i){if("function"!=typeof o||null!=i&&"function"!=typeof i)throw new TypeError(a);var e=function e(){var t=arguments,n=i?i.apply(this,t):t[0],r=e.cache;if(r.has(n))return r.get(n);var a=o.apply(this,t);return e.cache=r.set(n,a)||r,a};return e.cache=new(c.Cache||r),e}c.Cache=r,t.exports=c},{"./_MapCache":9}],146:[function(e,t,n){var r=e("./_baseProperty"),a=e("./_basePropertyDeep"),o=e("./_isKey"),i=e("./_toKey");t.exports=function(e){return o(e)?r(i(e)):a(e)}},{"./_baseProperty":48,"./_basePropertyDeep":49,"./_isKey":82,"./_toKey":115}],147:[function(e,t,n){t.exports=function(){return[]}},{}],148:[function(e,t,n){t.exports=function(){return!1}},{}],149:[function(e,t,n){var r=e("./toNumber");t.exports=function(e){return e?(e=r(e))!==1/0&&e!==-1/0?e==e?e:0:17976931348623157e292*(e<0?-1:1):0===e?e:0}},{"./toNumber":151}],150:[function(e,t,n){var r=e("./toFinite");t.exports=function(e){var t=r(e),n=t%1;return t==t?n?t-n:t:0}},{"./toFinite":149}],151:[function(e,t,n){var r=e("./isObject"),a=e("./isSymbol"),o=/^\s+|\s+$/g,i=/^[-+]0x[0-9a-f]+$/i,c=/^0b[01]+$/i,s=/^0o[0-7]+$/i,u=parseInt;t.exports=function(e){if("number"==typeof e)return e;if(a(e))return NaN;if(r(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=r(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(o,"");var n=c.test(e);return n||s.test(e)?u(e.slice(2),n?2:8):i.test(e)?NaN:+e}},{"./isObject":135,"./isSymbol":139}],152:[function(e,t,n){var r=e("./_baseToString");t.exports=function(e){return null==e?"":r(e)}},{"./_baseToString":51}],153:[function(e,a,t){!function(){var e=this;function t(e,t){var n,r,a,o,i,c,s,u;for(n=3&e.length,r=e.length-n,a=t,i=3432918353,c=461845907,u=0;u<r;)s=255&e.charCodeAt(u)|(255&e.charCodeAt(++u))<<8|(255&e.charCodeAt(++u))<<16|(255&e.charCodeAt(++u))<<24,++u,a=27492+(65535&(o=5*(65535&(a=(a^=s=(65535&(s=(s=(65535&s)*i+(((s>>>16)*i&65535)<<16)&4294967295)<<15|s>>>17))*c+(((s>>>16)*c&65535)<<16)&4294967295)<<13|a>>>19))+((5*(a>>>16)&65535)<<16)&4294967295))+((58964+(o>>>16)&65535)<<16);switch(s=0,n){case 3:s^=(255&e.charCodeAt(u+2))<<16;case 2:s^=(255&e.charCodeAt(u+1))<<8;case 1:a^=s=(65535&(s=(s=(65535&(s^=255&e.charCodeAt(u)))*i+(((s>>>16)*i&65535)<<16)&4294967295)<<15|s>>>17))*c+(((s>>>16)*c&65535)<<16)&4294967295}return a^=e.length,a=2246822507*(65535&(a^=a>>>16))+((2246822507*(a>>>16)&65535)<<16)&4294967295,a=3266489909*(65535&(a^=a>>>13))+((3266489909*(a>>>16)&65535)<<16)&4294967295,(a^=a>>>16)>>>0}var n=t;if(n.v2=function(e,t){for(var n,r=e.length,a=t^r,o=0;4<=r;)n=1540483477*(65535&(n=255&e.charCodeAt(o)|(255&e.charCodeAt(++o))<<8|(255&e.charCodeAt(++o))<<16|(255&e.charCodeAt(++o))<<24))+((1540483477*(n>>>16)&65535)<<16),a=1540483477*(65535&a)+((1540483477*(a>>>16)&65535)<<16)^(n=1540483477*(65535&(n^=n>>>24))+((1540483477*(n>>>16)&65535)<<16)),r-=4,++o;switch(r){case 3:a^=(255&e.charCodeAt(o+2))<<16;case 2:a^=(255&e.charCodeAt(o+1))<<8;case 1:a=1540483477*(65535&(a^=255&e.charCodeAt(o)))+((1540483477*(a>>>16)&65535)<<16)}return a=1540483477*(65535&(a^=a>>>13))+((1540483477*(a>>>16)&65535)<<16),(a^=a>>>15)>>>0},n.v3=t,void 0!==a)a.exports=n;else{var r=e.murmur;n.noConflict=function(){return e.murmur=r,n},e.murmur=n}}()},{}],154:[function(e,t,n){var y,_,r,a;y=e("crypt"),_=e("charenc").utf8,r=e("charenc").bin,(a=function(e,t){var n=y.wordsToBytes(function(e){e.constructor==String&&(e=_.stringToBytes(e));var t=y.bytesToWords(e),n=8*e.length,r=[],a=1732584193,o=-271733879,i=-1732584194,c=271733878,s=-1009589776;t[n>>5]|=128<<24-n%32,t[15+(n+64>>>9<<4)]=n;for(var u=0;u<t.length;u+=16){for(var l=a,f=o,d=i,p=c,h=s,m=0;m<80;m++){if(m<16)r[m]=t[u+m];else{var v=r[m-3]^r[m-8]^r[m-14]^r[m-16];r[m]=v<<1|v>>>31}var g=(a<<5|a>>>27)+s+(r[m]>>>0)+(m<20?1518500249+(o&i|~o&c):m<40?1859775393+(o^i^c):m<60?(o&i|o&c|i&c)-1894007588:(o^i^c)-899497514);s=c,c=i,i=o<<30|o>>>2,o=a,a=g}a+=l,o+=f,i+=d,c+=p,s+=h}return[a,o,i,c,s]}(e));return t&&t.asBytes?n:t&&t.asString?r.bytesToString(n):y.bytesToHex(n)})._blocksize=16,a._digestsize=20,t.exports=a},{charenc:2,crypt:3}],155:[function(e,t,n){Object.defineProperty(n,"__esModule",{value:!0});var r=e("./lib/core");n.trackerCore=r.trackerCore},{"./lib/core":158}],156:[function(e,t,n){function r(e){var t,n,r,a,o,i,c,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",u=0,l=0,f="",d=[];if(!e)return e;for(e+="";t=(i=s.indexOf(e.charAt(u++))<<18|s.indexOf(e.charAt(u++))<<12|(a=s.indexOf(e.charAt(u++)))<<6|(o=s.indexOf(e.charAt(u++))))>>16&255,n=i>>8&255,r=255&i,d[l++]=64===a?String.fromCharCode(t):64===o?String.fromCharCode(t,n):String.fromCharCode(t,n,r),u<e.length;);return f=d.join(""),c=f.replace(/\0+$/,""),decodeURIComponent(c.split("").map(function(e){return"%"+("00"+e.charCodeAt(0).toString(16)).slice(-2)}).join(""))}Object.defineProperty(n,"__esModule",{value:!0}),n.base64urldecode=function(e){if(!e)return e;switch(4-e.length%4){case 2:e+="==";break;case 3:e+="="}return r(e.replace(/-/g,"+").replace(/_/g,"/"))},n.base64encode=function(e){var t,n,r,a,o,i,c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",s=0,u=0,l=[];if(!e)return e;for(e=unescape(encodeURIComponent(e));t=(o=e.charCodeAt(s++)<<16|e.charCodeAt(s++)<<8|e.charCodeAt(s++))>>18&63,n=o>>12&63,r=o>>6&63,a=63&o,l[u++]=c.charAt(t)+c.charAt(n)+c.charAt(r)+c.charAt(a),s<e.length;);i=l.join("");var f=e.length%3;return(f?i.slice(0,f-3):i)+"===".slice(f||3)},n.base64decode=r},{}],157:[function(e,t,n){var r=this&&this.__assign||Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var a in t=arguments[n])Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a]);return e};Object.defineProperty(n,"__esModule",{value:!0});var a=e("./payload"),o=e("./base64"),s=e("lodash/isEqual"),i=e("lodash/has"),c=e("lodash/get"),u=e("lodash/isPlainObject"),l=e("lodash/every"),f=e("lodash/compact"),d=e("lodash/map");function p(e){var t=new RegExp("^iglu:([a-zA-Z0-9-_.]+)/([a-zA-Z0-9-_]+)/jsonschema/([1-9][0-9]*)-(0|[1-9][0-9]*)-(0|[1-9][0-9]*)$").exec(e);if(null!==t)return t.slice(1,6)}function h(e){if("*"===e[0]||"*"===e[1])return!1;if(0<e.slice(2).length){for(var t=!1,n=0,r=e.slice(2);n<r.length;n++){if("*"===r[n])t=!0;else if(t)return!1}return!0}return 2==e.length}function m(e){var t=e.split(".");return!!(t&&1<t.length)&&h(t)}function v(e){var t=new RegExp("^iglu:((?:(?:[a-zA-Z0-9-_]+|\\*).)+(?:[a-zA-Z0-9-_]+|\\*))/([a-zA-Z0-9-_.]+|\\*)/jsonschema/([1-9][0-9]*|\\*)-(0|[1-9][0-9]*|\\*)-(0|[1-9][0-9]*|\\*)$").exec(e);if(null!==t&&m(t[1]))return t.slice(1,6)}function g(e){var t=v(e);if(t){var n=t[0];return 5===t.length&&m(n)}return!1}function y(e){return Array.isArray(e)&&e.every(function(e){return"string"==typeof e})}function _(e){return y(e)?e.every(function(e){return g(e)}):"string"==typeof e&&g(e)}function b(e){return!!(a.isNonEmptyJson(e)&&"schema"in e&&"data"in e)&&("string"==typeof e.schema&&"object"===_typeof(e.data))}function w(e){return!!(a.isNonEmptyJson(e)&&"e"in e)&&"string"==typeof e.e}function k(e){var t=0;if(u(e)){if(i(e,"accept")){if(!_(e.accept))return!1;t+=1}if(i(e,"reject")){if(!_(e.reject))return!1;t+=1}return 0<t&&t<=2}return!1}function A(e){return"function"==typeof e&&e.length<=1}function x(e){return"function"==typeof e&&e.length<=1}function C(e){return A(e)||b(e)}function S(e){return!(!Array.isArray(e)||2!==e.length)&&(Array.isArray(e[1])?x(e[0])&&l(e[1],C):x(e[0])&&C(e[1]))}function j(e){return!(!Array.isArray(e)||2!==e.length)&&(!!k(e[0])&&(Array.isArray(e[1])?l(e[1],C):C(e[1])))}function T(e){return S(e)||j(e)}function O(e,t){if(!g(e))return!1;var n=v(e),r=p(t);if(n&&r){if(!P(n[0],r[0]))return!1;for(var a=1;a<5;a++)if(!I(n[a],r[a]))return!1;return!0}return!1}function P(e,t){var n=t.split("."),r=e.split(".");if(n&&r){if(n.length!==r.length)return!1;for(var a=0;a<r.length;a++)if(!I(n[a],r[a]))return!1;return!0}return!1}function I(e,t){return e&&t&&"*"===e||e===t}function E(e,t){var n=0,r=0,a=c(e,"accept");Array.isArray(a)?e.accept.some(function(e){return O(e,t)})&&r++:"string"==typeof a&&O(a,t)&&r++;var o=c(e,"reject");return Array.isArray(o)?e.reject.some(function(e){return O(e,t)})&&n++:"string"==typeof o&&O(o,t)&&n++,0<r&&0===n}function L(e){return"string"==typeof c(e,"ue_px.data.schema")?c(e,"ue_px.data.schema"):"string"==typeof c(e,"ue_pr.data.schema")?c(e,"ue_pr.data.schema"):"string"==typeof c(e,"schema")?c(e,"schema"):""}function D(e){var t=r({},e);try{i(t,"ue_px")&&(t.ue_px=JSON.parse(o.base64urldecode(c(t,["ue_px"]))))}catch(e){}return t}function M(e){return c(e,"e","")}function N(e,t,n,r){var a=void 0;try{return b(a=e({event:t,eventType:n,eventSchema:r}))?a:Array.isArray(a)&&l(a,b)?a:void 0}catch(e){a=void 0}return a}function F(e){return Array.isArray(e)?e:Array.of(e)}function z(e,n,r,a){var t=F(e),o=d(t,function(e){var t=U(e,n,r,a);if(t&&0!==t.length)return t});return[].concat.apply([],f(o))}function U(e,t,n,r){if(b(e))return[e];if(A(e)){var a=N(e,t,n,r);if(b(a))return[a];if(Array.isArray(a))return a}}function B(e,t,n,r){if(S(e)){var a=e[0],o=!1;try{o=a({event:t,eventType:n,eventSchema:r})}catch(e){o=!1}if(!0===o)return z(e[1],t,n,r)}else if(j(e)&&E(e[0],r))return z(e[1],t,n,r);return[]}function G(e,n,r,a){var t=F(e),o=d(t,function(e){var t=B(e,n,r,a);if(t&&0!==t.length)return t});return[].concat.apply([],f(o))}n.getSchemaParts=p,n.validateVendorParts=h,n.validateVendor=m,n.getRuleParts=v,n.isValidRule=g,n.isStringArray=y,n.isValidRuleSetArg=_,n.isSelfDescribingJson=b,n.isEventJson=w,n.isRuleSet=k,n.isContextGenerator=A,n.isContextFilter=x,n.isContextPrimitive=C,n.isFilterProvider=S,n.isRuleSetProvider=j,n.isConditionalContextProvider=T,n.matchSchemaAgainstRule=O,n.matchVendor=P,n.matchPart=I,n.matchSchemaAgainstRuleSet=E,n.getUsefulSchema=L,n.getDecodedEvent=D,n.getEventType=M,n.buildGenerator=N,n.normalizeToArray=F,n.generatePrimitives=z,n.evaluatePrimitive=U,n.evaluateProvider=B,n.generateConditionals=G,n.contextModule=function(){var i=[],c=[];return{getGlobalPrimitives:function(){return i},getConditionalProviders:function(){return c},addGlobalContexts:function(e){for(var t=[],n=[],r=0,a=e;r<a.length;r++){var o=a[r];T(o)?t.push(o):C(o)&&n.push(o)}i=i.concat(n),c=c.concat(t)},clearGlobalContexts:function(){c=[],i=[]},removeGlobalContexts:function(e){for(var t=function(t){T(t)?c=c.filter(function(e){return!s(e,t)}):C(t)&&(i=i.filter(function(e){return!s(e,t)}))},n=0,r=e;n<r.length;n++)t(r[n])},getApplicableContexts:function(e){var t=e.build();return w(t)?function(e){var t=L(e),n=M(e),r=[],a=z(i,e,n,t);r.push.apply(r,a);var o=G(c,e,n,t);return r.push.apply(r,o),r}(D(t)):[]}}}},{"./base64":156,"./payload":159,"lodash/compact":117,"lodash/every":119,"lodash/get":124,"lodash/has":125,"lodash/isEqual":132,"lodash/isPlainObject":137,"lodash/map":143}],158:[function(e,t,n){Object.defineProperty(n,"__esModule",{value:!0});var u=e("uuid"),m=e("./payload"),r=e("./contexts");n.trackerCore=function(d,i){void 0===d&&(d=!0);var c={},o=r.contextModule();function n(e,t){c[e]=t}function f(e,t){var n={};for(var r in t=t||{},e)(t[r]||null!==e[r]&&void 0!==e[r])&&(n[r]=e[r]);return n}function s(e,t){var n,r=(n=e,o.getApplicableContexts(n)),a=[];return t&&t.length&&a.push.apply(a,t),r&&r.length&&a.push.apply(a,r),a}function p(e,t,n){e.addDict(c),e.add("eid",u.v4());var r,a=null==(r=n)?{type:"dtm",value:(new Date).getTime()}:"number"==typeof r?{type:"dtm",value:r}:"ttm"===r.type?{type:"ttm",value:r.value}:{type:"dtm",value:r.value||(new Date).getTime()};e.add(a.type,a.value.toString());var o=function(e){if(e&&e.length)return{schema:"iglu:com.snowplowanalytics.snowplow/contexts/jsonschema/1-0-0",data:e}}(s(e,t));return void 0!==o&&e.addJson("cx","co",o),"function"==typeof i&&i(e),e}function h(e,t,n){var r=m.payloadBuilder(d),a={schema:"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0",data:e};return r.add("e","ue"),r.addJson("ue_px","ue_pr",a),p(r,t,n)}return{setBase64Encoding:function(e){d=e},addPayloadPair:n,addPayloadDict:function(e){for(var t in e)e.hasOwnProperty(t)&&(c[t]=e[t])},resetPayloadPairs:function(e){c=m.isJson(e)?e:{}},setTrackerVersion:function(e){n("tv",e)},setTrackerNamespace:function(e){n("tna",e)},setAppId:function(e){n("aid",e)},setPlatform:function(e){n("p",e)},setUserId:function(e){n("uid",e)},setScreenResolution:function(e,t){n("res",e+"x"+t)},setViewport:function(e,t){n("vp",e+"x"+t)},setColorDepth:function(e){n("cd",e)},setTimezone:function(e){n("tz",e)},setLang:function(e){n("lang",e)},setIpAddress:function(e){n("ip",e)},trackUnstructEvent:h,trackSelfDescribingEvent:h,trackPageView:function(e,t,n,r,a){var o=m.payloadBuilder(d);return o.add("e","pv"),o.add("url",e),o.add("page",t),o.add("refr",n),p(o,r,a)},trackPagePing:function(e,t,n,r,a,o,i,c,s){var u=m.payloadBuilder(d);return u.add("e","pp"),u.add("url",e),u.add("page",t),u.add("refr",n),u.add("pp_mix",r.toString()),u.add("pp_max",a.toString()),u.add("pp_miy",o.toString()),u.add("pp_may",i.toString()),p(u,c,s)},trackStructEvent:function(e,t,n,r,a,o,i){var c=m.payloadBuilder(d);return c.add("e","se"),c.add("se_ca",e),c.add("se_ac",t),c.add("se_la",n),c.add("se_pr",r),c.add("se_va",null==a?void 0:a.toString()),p(c,o,i)},trackEcommerceTransaction:function(e,t,n,r,a,o,i,c,s,u,l){var f=m.payloadBuilder(d);return f.add("e","tr"),f.add("tr_id",e),f.add("tr_af",t),f.add("tr_tt",n),f.add("tr_tx",r),f.add("tr_sh",a),f.add("tr_ci",o),f.add("tr_st",i),f.add("tr_co",c),f.add("tr_cu",s),p(f,u,l)},trackEcommerceTransactionItem:function(e,t,n,r,a,o,i,c,s){var u=m.payloadBuilder(d);return u.add("e","ti"),u.add("ti_id",e),u.add("ti_sk",t),u.add("ti_nm",n),u.add("ti_ca",r),u.add("ti_pr",a),u.add("ti_qu",o),u.add("ti_cu",i),p(u,c,s)},trackScreenView:function(e,t,n,r){return h({schema:"iglu:com.snowplowanalytics.snowplow/screen_view/jsonschema/1-0-0",data:f({name:e,id:t})},n,r)},trackLinkClick:function(e,t,n,r,a,o,i){return h({schema:"iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1",data:f({targetUrl:e,elementId:t,elementClasses:n,elementTarget:r,elementContent:a})},o,i)},trackAdImpression:function(e,t,n,r,a,o,i,c,s,u){return h({schema:"iglu:com.snowplowanalytics.snowplow/ad_impression/jsonschema/1-0-0",data:f({impressionId:e,costModel:t,cost:n,targetUrl:r,bannerId:a,zoneId:o,advertiserId:i,campaignId:c})},s,u)},trackAdClick:function(e,t,n,r,a,o,i,c,s,u,l){return h({schema:"iglu:com.snowplowanalytics.snowplow/ad_click/jsonschema/1-0-0",data:f({targetUrl:e,clickId:t,costModel:n,cost:r,bannerId:a,zoneId:o,impressionId:i,advertiserId:c,campaignId:s})},u,l)},trackAdConversion:function(e,t,n,r,a,o,i,c,s,u,l){return h({schema:"iglu:com.snowplowanalytics.snowplow/ad_conversion/jsonschema/1-0-0",data:f({conversionId:e,costModel:t,cost:n,category:r,action:a,property:o,initialValue:i,advertiserId:c,campaignId:s})},u,l)},trackSocialInteraction:function(e,t,n,r,a){return h({schema:"iglu:com.snowplowanalytics.snowplow/social_interaction/jsonschema/1-0-0",data:f({action:e,network:t,target:n})},r,a)},trackAddToCart:function(e,t,n,r,a,o,i,c){return h({schema:"iglu:com.snowplowanalytics.snowplow/add_to_cart/jsonschema/1-0-0",data:f({sku:e,name:t,category:n,unitPrice:r,quantity:a,currency:o})},i,c)},trackRemoveFromCart:function(e,t,n,r,a,o,i,c){return h({schema:"iglu:com.snowplowanalytics.snowplow/remove_from_cart/jsonschema/1-0-0",data:f({sku:e,name:t,category:n,unitPrice:r,quantity:a,currency:o})},i,c)},trackFormFocusOrChange:function(e,t,n,r,a,o,i,c,s){var u="";return"change_form"===e?u="iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0":"focus_form"===e&&(u="iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0"),h({schema:u,data:f({formId:t,elementId:n,nodeName:r,type:a,elementClasses:o,value:i},{value:!0})},c,s)},trackFormSubmission:function(e,t,n,r,a){return h({schema:"iglu:com.snowplowanalytics.snowplow/submit_form/jsonschema/1-0-0",data:f({formId:e,formClasses:t,elements:n})},r,a)},trackSiteSearch:function(e,t,n,r,a,o){return h({schema:"iglu:com.snowplowanalytics.snowplow/site_search/jsonschema/1-0-0",data:f({terms:e,filters:t,totalResults:n,pageResults:r})},a,o)},trackConsentWithdrawn:function(e,t,n,r,a,o,i){var c={schema:"iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0",data:f({id:t,version:n,name:r,description:a})};return h({schema:"iglu:com.snowplowanalytics.snowplow/consent_withdrawn/jsonschema/1-0-0",data:f({all:e})},c.data&&o?o.concat([c]):o,i)},trackConsentGranted:function(e,t,n,r,a,o,i){var c={schema:"iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0",data:f({id:e,version:t,name:n,description:r})};return h({schema:"iglu:com.snowplowanalytics.snowplow/consent_granted/jsonschema/1-0-0",data:f({expiry:a})},o?o.concat([c]):[c],i)},addGlobalContexts:function(e){o.addGlobalContexts(e)},clearGlobalContexts:function(){o.clearGlobalContexts()},removeGlobalContexts:function(e){o.removeGlobalContexts(e)}}}},{"./contexts":157,"./payload":159,uuid:161}],159:[function(e,t,n){Object.defineProperty(n,"__esModule",{value:!0});var c=e("./base64");function s(e){if(!r(e))return!1;for(var t in e)if(e.hasOwnProperty(t))return!0;return!1}function r(e){return null!=e&&(e.constructor==={}.constructor||e.constructor===[].constructor)}n.isNonEmptyJson=s,n.isJson=r,n.payloadBuilder=function(o){var n={},i=function(e,t){null!=t&&""!==t&&(n[e]=t)};return{add:i,addDict:function(e){for(var t in e)e.hasOwnProperty(t)&&i(t,e[t])},addJson:function(e,t,n){if(s(n)){var r=JSON.stringify(n);o?i(e,(a=r)?c.base64encode(a).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_"):a):i(t,r)}var a},build:function(){return n}}}},{"./base64":156}],160:[function(e,o,t){(function(e){var t,n=e.crypto||e.msCrypto;if(n&&n.getRandomValues){var r=new Uint8Array(16);t=function(){return n.getRandomValues(r),r}}if(!t){var a=new Array(16);t=function(){for(var e,t=0;t<16;t++)0==(3&t)&&(e=4294967296*Math.random()),a[t]=e>>>((3&t)<<3)&255;return a}}o.exports=t}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],161:[function(e,t,n){for(var i=e("./rng"),a=[],o={},r=0;r<256;r++)a[r]=(r+256).toString(16).substr(1),o[a[r]]=r;function p(e,t){var n=t||0,r=a;return r[e[n++]]+r[e[n++]]+r[e[n++]]+r[e[n++]]+"-"+r[e[n++]]+r[e[n++]]+"-"+r[e[n++]]+r[e[n++]]+"-"+r[e[n++]]+r[e[n++]]+"-"+r[e[n++]]+r[e[n++]]+r[e[n++]]+r[e[n++]]+r[e[n++]]+r[e[n++]]}var c=i(),h=[1|c[0],c[1],c[2],c[3],c[4],c[5]],m=16383&(c[6]<<8|c[7]),v=0,g=0;function s(e,t,n){var r=t&&n||0;"string"==typeof e&&(t="binary"==e?new Array(16):null,e=null);var a=(e=e||{}).random||(e.rng||i)();if(a[6]=15&a[6]|64,a[8]=63&a[8]|128,t)for(var o=0;o<16;o++)t[r+o]=a[o];return t||p(a)}var u=s;u.v1=function(e,t,n){var r=t&&n||0,a=t||[],o=void 0!==(e=e||{}).clockseq?e.clockseq:m,i=void 0!==e.msecs?e.msecs:(new Date).getTime(),c=void 0!==e.nsecs?e.nsecs:g+1,s=i-v+(c-g)/1e4;if(s<0&&void 0===e.clockseq&&(o=o+1&16383),(s<0||v<i)&&void 0===e.nsecs&&(c=0),1e4<=c)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");v=i,m=o;var u=(1e4*(268435455&(i+=122192928e5))+(g=c))%4294967296;a[r++]=u>>>24&255,a[r++]=u>>>16&255,a[r++]=u>>>8&255,a[r++]=255&u;var l=i/4294967296*1e4&268435455;a[r++]=l>>>8&255,a[r++]=255&l,a[r++]=l>>>24&15|16,a[r++]=l>>>16&255,a[r++]=o>>>8|128,a[r++]=255&o;for(var f=e.node||h,d=0;d<6;d++)a[r+d]=f[d];return t||p(a)},u.v4=s,u.parse=function(e,t,n){var r=t&&n||0,a=0;for(t=t||[],e.toLowerCase().replace(/[0-9a-f]{2}/g,function(e){a<16&&(t[r+a++]=o[e])});a<16;)t[r+a++]=0;return t},u.unparse=p,t.exports=u},{"./rng":160}],162:[function(e,t,n){var u=e("lodash/isFunction"),r=e("./lib/helpers"),a=window;(void 0!==n?n:this).errorManager=function(c){function s(e,t,n,r,a,o){var i=a&&a.stack?a.stack:null;c.trackSelfDescribingEvent({schema:"iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1",data:{programmingLanguage:"JAVASCRIPT",message:e||"JS Exception. Browser doesn't support ErrorEvent API",stackTrace:i,lineNumber:n,lineColumn:r,fileName:t}},o)}return{trackError:s,enableErrorTracking:function(o,i,c){r.addEventListener(a,"error",function(e){var t,n,r,a;(u(o)&&o(e)||null==o)&&(t=e,n=c,a=u(r=i)?n.concat(r(t)):n,s(t.message,t.filename,t.lineno,t.colno,t.error,a))},!0)}}}},{"./lib/helpers":168,"lodash/isFunction":133}],163:[function(e,t,n){var p=e("lodash/forEach"),h=e("lodash/filter"),m=e("lodash/find"),v=e("./lib/helpers");(void 0!==n?n:this).getFormTrackingManager=function(i,e,c){var s=["textarea","input","select"],u=e+"form",r=function(){return!0},a=function(){return!0},l=function(e){return e};function f(t){return t[m(["name","id","type","nodeName"],function(e){return t[e]&&"string"==typeof t[e]})]}function o(a,o){return function(e){var t=e.target,n=t.nodeName&&"INPUT"===t.nodeName.toUpperCase()?t.type:null,r="checkbox"!==t.type||t.checked?l(t.value):null;("change_form"===a||"checkbox"!==n&&"radio"!==n)&&i.trackFormFocusOrChange(a,function(e){for(;e&&e.nodeName&&"HTML"!==e.nodeName.toUpperCase()&&"FORM"!==e.nodeName.toUpperCase();)e=e.parentNode;if(e&&e.nodeName&&"FORM"===e.nodeName.toUpperCase())return f(e)}(t),f(t),t.nodeName,n,v.getCssClasses(t),r,c(v.resolveDynamicContexts(o,t,n,r)))}}function d(o){return function(e){var n,r,t=e.target,a=(n=t,r=[],p(s,function(e){var t=h(n.getElementsByTagName(e),function(e){return e.hasOwnProperty(u)});p(t,function(e){if("submit"!==e.type){var t={name:f(e),value:e.value,nodeName:e.nodeName};e.type&&"INPUT"===e.nodeName.toUpperCase()&&(t.type=e.type),"checkbox"!==e.type&&"radio"!==e.type||e.checked||(t.value=null),r.push(t)}})}),r);p(a,function(e){e.value=l(e.value)}),i.trackFormSubmission(f(t),v.getCssClasses(t),a,c(v.resolveDynamicContexts(o,t,a)))}}return{configureFormTracking:function(e){e&&(r=v.getFilter(e.forms,!0),a=v.getFilter(e.fields,!1),l=v.getTransform(e.fields))},addFormListeners:function(n){p(document.getElementsByTagName("form"),function(t){r(t)&&!t[u]&&(p(s,function(e){p(t.getElementsByTagName(e),function(e){a(e)&&!e[u]&&"password"!==e.type.toLowerCase()&&(v.addEventListener(e,"focus",o("focus_form",n),!1),v.addEventListener(e,"change",o("change_form",n),!1),e[u]=!0)})}),v.addEventListener(t,"submit",d(n)),t[u]=!0)})}}}},{"./lib/helpers":168,"lodash/filter":120,"lodash/find":121,"lodash/forEach":123}],164:[function(e,t,n){n.productionize=function(a){var o={};return"object"===_typeof(a)&&null!==a&&Object.getOwnPropertyNames(a).forEach(function(e,t,n){var r;"function"==typeof a[e]&&(o[e]=(r=a[e],function(){try{return r.apply(this,arguments)}catch(e){}}))}),o}},{}],165:[function(e,t,n){!function(){var c=e("lodash/map"),v=e("lodash/isUndefined"),g=e("lodash/isFunction"),y=e("./lib/helpers");(void 0!==n?n:this).InQueueManager=function(r,a,o,e,i){var p={};function h(e){var t=[];if(e&&0!==e.length)for(var n=0;n<e.length;n++)p.hasOwnProperty(e[n])?t.push(p[e[n]]):y.warn('Warning: Tracker namespace "'+e[n]+'" not configured');else t=c(p);return 0===t.length&&y.warn("Warning: No tracker configured"),t}function m(e,t,n){n=n||{},p.hasOwnProperty(e)?y.warn("Tracker namespace "+e+" already exists."):(p[e]=new r(i,e,a,o,n),p[e].setCollectorUrl(t))}function t(){var e,t,n,r,a,o,i,c,s,u,l,f,d;for(e=0;e<arguments.length;e+=1)if(r=arguments[e],a=Array.prototype.shift.call(r),g(a))a.apply(p,r);else if(d=void 0,i=(o=[(d=a.split(":"))[0],1<d.length?d[1].split(";"):[]])[1],"newTracker"!==(n=o[0]))if("setCollectorCf"!==n&&"setCollectorUrl"!==n||i&&0!==i.length)for(c=h(i),t=0;t<c.length;t++)c[t][n].apply(c[t],r);else s=n,u=r[0],l=r[1],f=void 0,y.warn(s+" is deprecated. Set the collector when a new tracker instance using newTracker."),m(f=v(l)?"sp":l),p[f][s](u);else m(r[0],r[1],r[2])}for(var n=0;n<e.length;n++)t(e[n]);return{push:t}}}()},{"./lib/helpers":168,"lodash/isFunction":133,"lodash/isUndefined":141,"lodash/map":143}],166:[function(e,t,n){var r,a,o=e("./snowplow"),i=window;i.GlobalSnowplowNamespace&&0<i.GlobalSnowplowNamespace.length?(r=i.GlobalSnowplowNamespace.shift(),(a=i[r]).q=new o.Snowplow(a.q,r)):(i._snaq=i._snaq||[],i._snaq=new o.Snowplow(i._snaq,"_snaq"))},{"./snowplow":172}],167:[function(t,e,r){!function(){var i=t("lodash/isFunction"),c=t("lodash/isUndefined"),s=t("murmurhash").v3,e=t("jstimezonedetect").jstz.determine(),n=t("browser-cookie-lite"),u=void 0!==r?r:this,l=window,f=navigator,d=screen,o=document;u.hasSessionStorage=function(){try{return!!l.sessionStorage}catch(e){return!0}},u.hasLocalStorage=function(){try{return!!l.localStorage}catch(e){return!0}},u.localStorageAccessible=function(){var e="modernizr";if(!u.hasLocalStorage())return!1;try{return l.localStorage.setItem(e,e),l.localStorage.removeItem(e),!0}catch(e){return!1}},u.hasCookies=function(e){var t=e||"testcookie";return c(f.cookieEnabled)?(n.cookie(t,"1"),"1"===n.cookie(t)?"1":"0"):f.cookieEnabled?"1":"0"},u.detectSignature=function(e){var t=[f.userAgent,[d.height,d.width,d.colorDepth].join("x"),(new Date).getTimezoneOffset(),u.hasSessionStorage(),u.hasLocalStorage()],n=[];if(f.plugins)for(var r=0;r<f.plugins.length;r++)if(f.plugins[r]){for(var a=[],o=0;o<f.plugins[r].length;o++)a.push([f.plugins[r][o].type,f.plugins[r][o].suffixes]);n.push([f.plugins[r].name+"::"+f.plugins[r].description,a.join("~")])}return s(t.join("###")+"###"+n.sort().join(";"),e)},u.detectTimezone=function(){return void 0===e?"":e.name()},u.detectViewport=function(){var e=l,t="inner";"innerWidth"in l||(t="client",e=o.documentElement||o.body);var n=e[t+"Width"],r=e[t+"Height"];return 0<=n&&0<=r?n+"x"+r:null},u.detectDocumentSize=function(){var e=o.documentElement,t=o.body,n=t?Math.max(t.offsetHeight,t.scrollHeight):0,r=Math.max(e.clientWidth,e.offsetWidth,e.scrollWidth),a=Math.max(e.clientHeight,e.offsetHeight,e.scrollHeight,n);return isNaN(r)||isNaN(a)?"":r+"x"+a},u.detectBrowserFeatures=function(e,t){var n,r,a={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"},o={};if(f.mimeTypes&&f.mimeTypes.length)for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(r=f.mimeTypes[a[n]],o[n]=r&&r.enabledPlugin?"1":"0");return f.constructor===window.Navigator&&"unknown"!=typeof f.javaEnabled&&!c(f.javaEnabled)&&f.javaEnabled()&&(o.java="1"),i(l.GearsFactory)&&(o.gears="1"),o.res=d.width+"x"+d.height,o.cd=d.colorDepth,e&&(o.cookie=u.hasCookies(t)),o}}()},{"browser-cookie-lite":1,jstimezonedetect:4,"lodash/isFunction":133,"lodash/isUndefined":141,murmurhash:153}],168:[function(e,t,o){!function(){var n=e("lodash/isString"),r=e("lodash/isUndefined"),i=e("lodash/isObject"),a=e("lodash/map"),s=e("browser-cookie-lite"),u=void 0!==o?o:this;u.fixupTitle=function(e){if(!n(e)){e=e.text||"";var t=document.getElementsByTagName("title");t&&!r(t[0])&&(e=t[0].text)}return e},u.getHostName=function(e){var t=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)").exec(e);return t?t[1]:e},u.fixupDomain=function(e){var t=e.length;return"."===e.charAt(--t)&&(e=e.slice(0,t)),"*."===e.slice(0,2)&&(e=e.slice(1)),e},u.getReferrer=function(e){var t="",n=u.fromQuerystring("referrer",window.location.href)||u.fromQuerystring("referer",window.location.href);if(n)return n;if(e)return e;try{t=window.top.document.referrer}catch(e){if(window.parent)try{t=window.parent.document.referrer}catch(e){t=""}}return""===t&&(t=document.referrer),t},u.addEventListener=function(e,t,n,r){return e.addEventListener?(e.addEventListener(t,n,r),!0):e.attachEvent?e.attachEvent("on"+t,n):void(e["on"+t]=n)},u.fromQuerystring=function(e,t){var n=new RegExp("^[^#]*[?&]"+e+"=([^&#]*)").exec(t);return n?decodeURIComponent(n[1].replace(/\+/g," ")):null},u.resolveDynamicContexts=function(e){var t=Array.prototype.slice.call(arguments,1);return a(e,function(e){if("function"!=typeof e)return e;try{return e.apply(null,t)}catch(e){}})},u.warn=function(e){"undefined"!=typeof console&&console.warn("Snowplow: "+e)},u.getCssClasses=function(e){return e.className.match(/\S+/g)||[]},u.getFilter=function(e,t){if(Array.isArray(e)||!i(e))return function(){return!0};if(e.hasOwnProperty("filter"))return e.filter;var n=e.hasOwnProperty("whitelist"),r=e.whitelist||e.blacklist;Array.isArray(r)||(r=[r]);for(var a={},o=0;o<r.length;o++)a[r[o]]=!0;return t?function(e){return function(e,t){var n,r=u.getCssClasses(e);for(n=0;n<r.length;n++)if(t[r[n]])return!0;return!1}(e,a)===n}:function(e){return e.name in a===n}},u.getTransform=function(e){return i(e)?e.hasOwnProperty("transform")?e.transform:function(e){return e}:function(e){return e}},u.decorateQuerystring=function(e,t,n){var r=t+"="+n,a=e.split("#"),o=a[0].split("?"),i=o.shift(),c=o.join("?");if(c){for(var s=!0,u=c.split("&"),l=0;l<u.length;l++)if(u[l].substr(0,t.length+1)===t+"="){s=!1,u[l]=r,c=u.join("&");break}s&&(c=r+"&"+c)}else c=r;return a[0]=i+"?"+c,a.join("#")},u.attemptGetLocalStorage=function(e){try{return localStorage.getItem(e)}catch(e){}},u.attemptWriteLocalStorage=function(e,t){try{return localStorage.setItem(e,t),!0}catch(e){return!1}},u.findRootDomain=function(){for(var e="_sp_root_domain_test_",t=e+(new Date).getTime(),n="_test_value_"+(new Date).getTime(),r=window.location.hostname.split("."),a=r.length-1;0<=a;){var o=r.slice(a,r.length).join(".");if(s.cookie(t,n,0,"/",o),s.cookie(t)===n){u.deleteCookie(t,o);for(var i=u.getCookiesWithPrefix(e),c=0;c<i.length;c++)u.deleteCookie(i[c],o);return o}a-=1}return window.location.hostname},u.isValueInArray=function(e,t){for(var n=0;n<t.length;n++)if(t[n]===e)return!0;return!1},u.deleteCookie=function(e,t){s.cookie(e,"",-1,"/",t)},u.getCookiesWithPrefix=function(e){for(var t=document.cookie.split("; "),n=[],r=0;r<t.length;r++)t[r].substring(0,e.length)===e&&n.push(t[r]);return n},u.parseInt=function(e){var t=parseInt(e);return isNaN(t)?void 0:t},u.parseFloat=function(e){var t=parseFloat(e);return isNaN(t)?void 0:t}}()},{"browser-cookie-lite":1,"lodash/isObject":135,"lodash/isString":138,"lodash/isUndefined":141,"lodash/map":143}],169:[function(e,t,n){!function(){var i=e("./helpers");function c(e){var t,n,r;if(r=e,new RegExp("^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$").test(r))try{return t=document.body.children[0].children[0].children[0].children[0].children[0].children[0].innerHTML,n="You have reached the cached page for",t.slice(0,n.length)===n}catch(e){return!1}}(void 0!==n?n:this).fixupUrl=function(e,t,n){var r,a,o;return"translate.googleusercontent.com"===e?(""===n&&(n=t),r=t,a="u",o=new RegExp("^(?:https?|ftp)(?::/*(?:[^?]+))([?][^#]+)").exec(r),t=i.fromQuerystring(a,o[1]),e=i.getHostName(t)):("cc.bingj.com"===e||"webcache.googleusercontent.com"===e||c(e))&&(t=document.links[0].href,e=i.getHostName(t)),[e,t,n]}}()},{"./helpers":168}],170:[function(e,t,n){var h=e("lodash/isUndefined"),m=e("./lib/helpers");(void 0!==n?n:this).getLinkTrackingManager=function(f,r,d){var a,o,p,i,c,s;function u(e,t){for(var n,r,a,o,i,c;null!==(n=e.parentNode)&&!h(n)&&"A"!==(r=e.tagName.toUpperCase())&&"AREA"!==r;)e=n;if(!h(e.href)){var s=e.hostname||m.getHostName(e.href),u=s.toLowerCase(),l=e.href.replace(s,u);new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto):","i").test(l)||(a=e.id,o=m.getCssClasses(e),i=e.target,c=p?e.innerHTML:null,l=unescape(l),f.trackLinkClick(l,a,o,i,c,d(m.resolveDynamicContexts(t,e))))}}function l(r){return function(e){var t,n;t=(e=e||window.event).which||e.button,n=e.target||e.srcElement,"click"===e.type?n&&u(n,r):"mousedown"===e.type?1!==t&&2!==t||!n?c=s=null:(c=t,s=n):"mouseup"===e.type&&(t===c&&n===s&&u(n,r),c=s=null)}}return{configureLinkClickTracking:function(e,t,n,r){p=n,i=r,o=t,a=m.getFilter(e,!0)},addClickListeners:function(){var e,t,n=document.links;for(e=0;e<n.length;e++)a(n[e])&&!n[e][r]&&(t=n[e],o?(m.addEventListener(t,"mouseup",l(i),!1),m.addEventListener(t,"mousedown",l(i),!1)):m.addEventListener(t,"click",l(i),!1),n[e][r]=!0)}}}},{"./lib/helpers":168,"lodash/isUndefined":141}],171:[function(e,t,n){!function(){var b=e("lodash/mapValues"),w=e("lodash/isString"),k=e("lodash/map"),A=e("./lib/detectors").localStorageAccessible,x=e("./lib/helpers");(void 0!==n?n:this).OutQueueManager=function(e,t,n,s,r,a,o,u){var l,f,d,p=!1,i=null===(r=r.toLowerCase?r.toLowerCase():r)||!0===r||"beacon"===r||"true"===r,h=Boolean(i&&navigator&&navigator.sendBeacon)&&i,m=("post"===r||h)&&!("get"===r),c=(m=m&&Boolean(window.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest))?a:"/i";if(o=A()&&s&&m&&o||1,l=["snowplowOutQueue",e,t,m?"post2":"get"].join("_"),s)try{d=JSON.parse(localStorage.getItem(l))}catch(e){}function v(){for(;d.length&&"string"!=typeof d[0]&&"object"!==_typeof(d[0]);)d.shift();if(d.length<1)p=!1;else{if(!w(f))throw"No Snowplow collector configured, cannot track";p=!0;var e=d[0];if(m){var t=g(f),n=setTimeout(function(){t.abort(),p=!1},5e3),r=function(e){for(var t=0,n=0;t<e.length&&(n+=e[t].bytes,!(u<=n));)t+=1;return t}(d);t.onreadystatechange=function(){if(4===t.readyState&&200<=t.status&&t.status<400){for(var e=0;e<r;e++)d.shift();s&&x.attemptWriteLocalStorage(l,JSON.stringify(d)),clearTimeout(n),v()}else 4===t.readyState&&400<=t.status&&(clearTimeout(n),p=!1)};var a=k(d.slice(0,r),function(e){return e.evt});if(0<a.length){var o;if(h){var i=new Blob([y(_(a))],{type:"application/json"});o=navigator.sendBeacon(f,i)}h&&o||t.send(y(_(a)))}}else{var c=new Image(1,1);c.onload=function(){d.shift(),s&&x.attemptWriteLocalStorage(l,JSON.stringify(d)),v()},c.onerror=function(){p=!1},c.src=f+e.replace("?","?stm="+(new Date).getTime()+"&")}}}function g(e){var t=new XMLHttpRequest;return t.open("POST",e,!0),t.withCredentials=!0,t.setRequestHeader("Content-Type","application/json; charset=UTF-8"),t}function y(e){return JSON.stringify({schema:"iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-4",data:e})}function _(e){for(var t=(new Date).getTime().toString(),n=0;n<e.length;n++)e[n].stm=t;return e}return Array.isArray(d)||(d=[]),n.outQueues.push(d),m&&1<o&&n.bufferFlushers.push(function(){p||v()}),{enqueueRequest:function(e,t){if(f=t+c,m){var n={evt:r=b(e,function(e){return e.toString()}),bytes:function(e){for(var t=0,n=0;n<e.length;n++){var r=e.charCodeAt(n);r<=127?t+=1:r<=2047?t+=2:55296<=r&&r<=57343?(t+=4,n++):t+=r<65535?3:4}return t}(JSON.stringify(r))};if(u<=n.bytes)return x.warn("Event of size "+n.bytes+" is too long - the maximum size is "+u),void g(f).send(y(_([n.evt])));d.push(n)}else d.push(function(e){var t="?",n={co:!0,cx:!0},r=!0;for(var a in e)e.hasOwnProperty(a)&&!n.hasOwnProperty(a)&&(r?r=!1:t+="&",t+=encodeURIComponent(a)+"="+encodeURIComponent(e[a]));for(var o in n)e.hasOwnProperty(o)&&n.hasOwnProperty(o)&&(t+="&"+o+"="+encodeURIComponent(e[o]));return t}(e));var r,a=!1;s&&(a=x.attemptWriteLocalStorage(l,JSON.stringify(d))),p||a&&!(d.length>=o)||v()},executeQueue:v}}}()},{"./lib/detectors":167,"./lib/helpers":168,"lodash/isString":138,"lodash/map":143,"lodash/mapValues":144}],172:[function(e,t,n){!function(){e("uuid");var s=e("lodash/forEach"),u=e("lodash/filter"),l=e("./lib/helpers"),f=e("./in_queue"),d=e("./tracker");(void 0!==n?n:this).Snowplow=function(e,n){var t,r=document,a=window,o="js-2.10.0",i={outQueues:[],bufferFlushers:[],expireDateTime:null,hasLoaded:!1,registeredOnLoadHandlers:[],pageViewId:null};function c(){var e;if(!i.hasLoaded)for(i.hasLoaded=!0,e=0;e<i.registeredOnLoadHandlers.length;e++)i.registeredOnLoadHandlers[e]();return!0}return a.Snowplow={getTrackerCf:function(e){var t=new d.Tracker(n,"",o,i,{});return t.setCollectorCf(e),t},getTrackerUrl:function(e){var t=new d.Tracker(n,"",o,i,{});return t.setCollectorUrl(e),t},getAsyncTracker:function(){return new d.Tracker(n,"",o,i,{})}},l.addEventListener(a,"beforeunload",function(){var e;if(s(i.bufferFlushers,function(e){e()}),i.expireDateTime)do{if(e=new Date,0===u(i.outQueues,function(e){return 0<e.length}).length)break}while(e.getTime()<i.expireDateTime)},!1),r.addEventListener?l.addEventListener(r,"DOMContentLoaded",function e(){r.removeEventListener("DOMContentLoaded",e,!1),c()}):r.attachEvent&&(r.attachEvent("onreadystatechange",function e(){"complete"===r.readyState&&(r.detachEvent("onreadystatechange",e),c())}),r.documentElement.doScroll&&a===a.top&&function t(){if(!i.hasLoaded){try{r.documentElement.doScroll("left")}catch(e){return void setTimeout(t,0)}c()}}()),new RegExp("WebKit").test(navigator.userAgent)&&(t=setInterval(function(){(i.hasLoaded||/loaded|complete/.test(r.readyState))&&(clearInterval(t),c())},10)),l.addEventListener(a,"load",c,!1),new f.InQueueManager(d.Tracker,o,i,e,n)}}()},{"./in_queue":165,"./lib/helpers":168,"./tracker":173,"lodash/filter":120,"lodash/forEach":123,uuid:161}],173:[function(e,t,n){!function(){var Xe=e("lodash/forEach"),et=e("lodash/map"),tt=e("./lib/helpers"),nt=e("./lib/proxies"),rt=e("browser-cookie-lite"),at=e("./lib/detectors"),ot=e("sha1"),it=e("./links"),ct=e("./forms"),st=e("./errors"),ut=e("./out_queue"),lt=e("snowplow-tracker-core").trackerCore,ft=e("./guard").productionize,dt=e("uuid");(void 0!==n?n:this).Tracker=function(e,t,n,s,r){(r=r||{}).hasOwnProperty("post")?r.eventMethod=!0===r.post?"post":"get":r.eventMethod=r.eventMethod||"beacon";var u,h,a,m,l,f,d,o,v,i,p,g,y,_,b,c,w,k,A,x,C,S,j=lt(!0,function(e){!function(e){var t,n=Math.round((new Date).getTime()/1e3),r=Pe("id"),a=Pe("ses"),o=Ie("ses"),i=Ge(),c=i[0],s=i[1],u=i[2],l=i[3],f=i[4],d=i[5],p=i[6];t=!!v&&!!rt.cookie(v);if((Q||t)&&"none"!=ae)return"localStorage"==ae?(tt.attemptWriteLocalStorage(r,""),tt.attemptWriteLocalStorage(a,"")):"cookie"!=ae&&"cookieAndLocalStorage"!=ae||(rt.cookie(r,"",-1,J,K),rt.cookie(a,"",-1,J,K));"0"===c?(k=p,o||"none"==ae||(l++,d=f,k=dt.v4()),de=l):(new Date).getTime()-le>1e3*Y&&(k=dt.v4(),de++);e.add("vp",at.detectViewport()),e.add("ds",at.detectDocumentSize()),e.add("vid",de),e.add("sid",k),e.add("duid",s),e.add("fp",ce),e.add("uid",A),Ce(),e.add("refr",Te(h||z)),e.add("url",Te(m||F)),"none"!=ae&&(Ue(s,u,de,n,d,k),ze());le=(new Date).getTime()}(e),function(e,t){var n,r=new Date;n=!!v&&!!rt.cookie(v);Q||n||(ge.enqueueRequest(e.build(),a),s.expireDateTime=r.getTime()+t)}(e,R)}),T=!1,O={},P={},I={},E=document,L=window,D=navigator,M=nt.fixupUrl(E.domain,L.location.href,tt.getReferrer()),N=tt.fixupDomain(M[0]),F=M[1],z=M[2],U=r.hasOwnProperty("platform")?r.platform:"web",B=r.hasOwnProperty("postPath")?r.postPath:"/com.snowplowanalytics.snowplow/tp2",G=r.hasOwnProperty("appId")?r.appId:"",q=E.title,R=r.hasOwnProperty("pageUnloadTimer")?r.pageUnloadTimer:500,H=!1,V=r.hasOwnProperty("cookieName")?r.cookieName:"_sp_",K=r.hasOwnProperty("cookieDomain")?r.cookieDomain:null,J="/",W=D.doNotTrack||D.msDoNotTrack||L.doNotTrack,Q=!!r.hasOwnProperty("respectDoNotTrack")&&(r.respectDoNotTrack&&("yes"===W||"1"===W)),$=r.hasOwnProperty("cookieLifetime")?r.cookieLifetime:63072e3,Y=r.hasOwnProperty("sessionCookieTimeout")?r.sessionCookieTimeout:1800,Z=r.hasOwnProperty("userFingerprintSeed")?r.userFingerprintSeed:123412414,X=E.characterSet||E.charset,ee=!!r.hasOwnProperty("forceSecureTracker")&&!0===r.forceSecureTracker,te=!(ee||!r.hasOwnProperty("forceUnsecureTracker"))&&!0===r.forceUnsecureTracker,ne=!r.hasOwnProperty("useLocalStorage")||(tt.warn("argmap.useLocalStorage is deprecated. Use argmap.stateStorageStrategy instead."),r.useLocalStorage),re=!r.hasOwnProperty("useCookies")||(tt.warn("argmap.useCookies is deprecated. Use argmap.stateStorageStrategy instead."),r.useCookies),ae=r.hasOwnProperty("stateStorageStrategy")?r.stateStorageStrategy:re||ne?re&&ne?"cookieAndLocalStorage":re?"cookie":"localStorage":"none",oe=D.userLanguage||D.language,ie=at.detectBrowserFeatures("cookie"==ae||"cookieAndLocalStorage"==ae,Pe("testcookie")),ce=!1===r.userFingerprint?"":at.detectSignature(Z),se=e+"_"+t,ue=!1,le=(new Date).getTime(),fe=ot,de=1,pe={transaction:{},items:[]},he=it.getLinkTrackingManager(j,se,Re),me=ct.getFormTrackingManager(j,se,Re),ve=st.errorManager(j),ge=new ut.OutQueueManager(e,t,s,"localStorage"==ae||"cookieAndLocalStorage"==ae,r.eventMethod,B,r.bufferSize,r.maxPostBytes||4e4),ye=!1,_e=r.contexts||{},be=[],we=[],ke=!1,Ae=!1;for(var xe in r.hasOwnProperty("discoverRootDomain")&&r.discoverRootDomain&&(K=tt.findRootDomain()),_e.gaCookies&&be.push((x={},Xe(["__utma","__utmb","__utmc","__utmv","__utmz","_ga"],function(e){var t=rt.cookie(e);t&&(x[e]=t)}),{schema:"iglu:com.google.analytics/cookies/jsonschema/1-0-0",data:x})),_e.geolocation&&Je(),j.setBase64Encoding(!r.hasOwnProperty("encodeBase64")||r.encodeBase64),j.setTrackerVersion(n),j.setTrackerNamespace(t),j.setAppId(G),j.setPlatform(U),j.setTimezone(at.detectTimezone()),j.addPayloadPair("lang",oe),j.addPayloadPair("cs",X),ie)Object.prototype.hasOwnProperty.call(ie,xe)&&("res"===xe||"cd"===xe||"cookie"===xe?j.addPayloadPair(xe,ie[xe]):j.addPayloadPair("f_"+xe,ie[xe]));function Ce(){(M=nt.fixupUrl(E.domain,L.location.href,tt.getReferrer()))[1]!==F&&(z=tt.getReferrer(F)),N=tt.fixupDomain(M[0]),F=M[1]}function Se(){var e=(new Date).getTime();this.href&&(this.href=tt.decorateQuerystring(this.href,"_sp",w+"."+e))}function je(e){for(var t=0;t<E.links.length;t++){var n=E.links[t];!n.spDecorationEnabled&&e(n)&&(tt.addEventListener(n,"click",Se,!0),tt.addEventListener(n,"mousedown",Se,!0),n.spDecorationEnabled=!0)}}function Te(e){var t;return o?(t=new RegExp("#.*"),e.replace(t,"")):e}function Oe(e){var t=new RegExp("^([a-z]+):").exec(e);return t?t[1]:null}function Pe(e){return V+e+"."+c}function Ie(e){var t=Pe(e);return"localStorage"==ae?tt.attemptGetLocalStorage(t):"cookie"==ae||"cookieAndLocalStorage"==ae?rt.cookie(t):void 0}function Ee(){Ce(),c=fe((K||N)+(J||"/")).slice(0,4)}function Le(){var e=new Date;p=e.getTime()}function De(){!function(){var e=Me(),t=e[0];t<g?g=t:y<t&&(y=t);var n=e[1];n<_?_=n:b<n&&(b=n)}(),Le()}function Me(){var e=E.compatMode&&"BackCompat"!==E.compatMode?E.documentElement:E.body;return[e.scrollLeft||L.pageXOffset,e.scrollTop||L.pageYOffset]}function Ne(){var e=Me(),t=e[0];y=g=t;var n=e[1];b=_=n}function Fe(e){var t=Math.round(e);if(!isNaN(t))return t}function ze(){Be(Pe("ses"),"*",Y)}function Ue(e,t,n,r,a,o){Be(Pe("id"),e+"."+t+"."+n+"."+r+"."+a+"."+o,$)}function Be(e,t,n){"localStorage"==ae?tt.attemptWriteLocalStorage(e,t):"cookie"!=ae&&"cookieAndLocalStorage"!=ae||rt.cookie(e,t,n,J,K)}function Ge(){if("none"==ae)return[];var e,t=new Date,n=Math.round(t.getTime()/1e3),r=Ie("id");return r?(e=r.split(".")).unshift("0"):e=["1",w,n,0,n,""],e[6]||(e[6]=dt.v4()),e}function qe(e){return ee?"https://"+e:te?"http://"+e:("https:"===E.location.protocol?"https":"http")+"://"+e}function Re(e){var t,n,r,a,o,i=be.concat(e||[]);if(_e.webPage&&i.push({schema:"iglu:com.snowplowanalytics.snowplow/web_page/jsonschema/1-0-0",data:{id:He()}}),_e.performanceTiming){var c=function(){var e=["navigationStart","redirectStart","redirectEnd","fetchStart","domainLookupStart","domainLookupEnd","connectStart","secureConnectionStart","connectEnd","requestStart","responseStart","responseEnd","unloadEventStart","unloadEventEnd","domLoading","domInteractive","domContentLoadedEventStart","domContentLoadedEventEnd","domComplete","loadEventStart","loadEventEnd","msFirstPaint","chromeFirstPaint","requestEnd","proxyStart","proxyEnd"],t=L.performance||L.mozPerformance||L.msPerformance||L.webkitPerformance;if(t){var n={};for(var r in t.timing)tt.isValueInArray(r,e)&&null!==t.timing[r]&&(n[r]=t.timing[r]);return delete n.requestEnd,L.chrome&&L.chrome.loadTimes&&"number"==typeof L.chrome.loadTimes().firstPaintTime&&(n.chromeFirstPaint=Math.round(1e3*L.chrome.loadTimes().firstPaintTime)),{schema:"iglu:org.w3/PerformanceTiming/jsonschema/1-0-0",data:n}}}();c&&i.push(c)}if(L.optimizely){if(_e.optimizelySummary){var s=et((a=Ve("state"),o=Ve("experiments"),et(a&&o&&a.activeExperiments,function(e){var t=o[e];return{activeExperimentId:e.toString(),variation:a.variationIdsMap[e][0].toString(),conditional:t&&t.conditional,manual:t&&t.manual,name:t&&t.name}})),function(e){return{schema:"iglu:com.optimizely.snowplow/optimizely_summary/jsonschema/1-0-0",data:e}});Xe(s,function(e){i.push(e)})}if(_e.optimizelyXSummary){s=et((t=Ke("state"),n=t.getActiveExperimentIds(),Ke("data","experiments"),r=Ke("visitor"),et(n,function(e){return variation=t.getVariationMap()[e],variationName=variation.name,variationId=variation.id,visitorId=r.visitorId,{experimentId:parseInt(e),variationName:variationName,variation:parseInt(variationId),visitorId:visitorId}})),function(e){return{schema:"iglu:com.optimizely.optimizelyx/summary/jsonschema/1-0-0",data:e}});Xe(s,function(e){i.push(e)})}if(_e.optimizelyExperiments)for(var u=function(){var e=Ve("experiments");if(e){var t=[];for(var n in e)if(e.hasOwnProperty(n)){var r={};r.id=n;var a=e[n];r.code=a.code,r.manual=a.manual,r.conditional=a.conditional,r.name=a.name,r.variationIds=a.variation_ids,t.push({schema:"iglu:com.optimizely/experiment/jsonschema/1-0-0",data:r})}return t}return[]}(),l=0;l<u.length;l++)i.push(u[l]);if(_e.optimizelyStates){var f=function(){var e=[],t=Ve("experiments");if(t)for(var n in t)t.hasOwnProperty(n)&&e.push(n);var r=Ve("state");if(r){for(var a=[],o=r.activeExperiments||[],i=0;i<e.length;i++){var c=e[i],s={};s.experimentId=c,s.isActive=tt.isValueInArray(e[i],o);var u=r.variationMap||{};s.variationIndex=u[c];var l=r.variationNamesMap||{};s.variationName=l[c];var f=r.variationIdsMap||{};f[c]&&1===f[c].length&&(s.variationId=f[c][0]),a.push({schema:"iglu:com.optimizely/state/jsonschema/1-0-0",data:s})}return a}return[]}();for(l=0;l<f.length;l++)i.push(f[l])}if(_e.optimizelyVariations){var d=function(){var e=Ve("variations");if(e){var t=[];for(var n in e)if(e.hasOwnProperty(n)){var r={};r.id=n;var a=e[n];r.name=a.name,r.code=a.code,t.push({schema:"iglu:com.optimizely/variation/jsonschema/1-0-0",data:r})}return t}return[]}();for(l=0;l<d.length;l++)i.push(d[l])}if(_e.optimizelyVisitor){var p=function(){var e=Ve("visitor");if(e){var t={};t.browser=e.browser,t.browserVersion=e.browserVersion,t.device=e.device,t.deviceType=e.deviceType,t.ip=e.ip;var n=e.platform||{};t.platformId=n.id,t.platformVersion=n.version;var r=e.location||{};return t.locationCity=r.city,t.locationRegion=r.region,t.locationCountry=r.country,t.mobile=e.mobile,t.mobileId=e.mobileId,t.referrer=e.referrer,t.os=e.os,{schema:"iglu:com.optimizely/visitor/jsonschema/1-0-0",data:t}}}();p&&i.push(p)}if(_e.optimizelyAudiences){var h=function(){var e=Ve("visitor","audiences");if(e){var t=[];for(var n in e)if(e.hasOwnProperty(n)){var r={id:n,isMember:e[n]};t.push({schema:"iglu:com.optimizely/visitor_audience/jsonschema/1-0-0",data:r})}return t}return[]}();for(l=0;l<h.length;l++)i.push(h[l])}if(_e.optimizelyDimensions){var m=function(){var e=Ve("visitor","dimensions");if(e){var t=[];for(var n in e)if(e.hasOwnProperty(n)){var r={id:n,value:e[n]};t.push({schema:"iglu:com.optimizely/visitor_dimension/jsonschema/1-0-0",data:r})}return t}return[]}();for(l=0;l<m.length;l++)i.push(m[l])}}if(_e.augurIdentityLite){var v=function(){var e=L.augur;if(e){var t={consumer:{},device:{}},n=e.consumer||{};t.consumer.UUID=n.UID;var r=e.device||{};t.device.ID=r.ID,t.device.isBot=r.isBot,t.device.isProxied=r.isProxied,t.device.isTor=r.isTor;var a=r.fingerprint||{};return t.device.isIncognito=a.browserHasIncognitoEnabled,{schema:"iglu:io.augur.snowplow/identity_lite/jsonschema/1-0-0",data:t}}}();v&&i.push(v)}if(_e.parrable){var g=function(){var e=window._hawk;if(e){var t={encryptedId:null,optout:null};t.encryptedId=e.browserid;var n=new RegExp("(?:^|;)\\s?"+"_parrable_hawk_optout".replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")+"=(.*?)(?:;|$)","i"),r=document.cookie.match(n);return t.optout=r&&decodeURIComponent(r[1])?r&&decodeURIComponent(r[1]):"false",{schema:"iglu:com.parrable/encrypted_payload/jsonschema/1-0-0",data:t}}}();g&&i.push(g)}return i}function He(){return null==s.pageViewId&&(s.pageViewId=dt.v4()),s.pageViewId}function Ve(e,t){var n;return L.optimizely&&L.optimizely.data&&(n=L.optimizely.data[e],void 0!==t&&void 0!==n&&(n=n[t])),n}function Ke(e,t){var n;return L.optimizely&&(n=L.optimizely.get(e),void 0!==t&&void 0!==n&&(n=n[t])),n}function Je(){!ye&&D.geolocation&&D.geolocation.getCurrentPosition&&(ye=!0,D.geolocation.getCurrentPosition(function(e){var t=e.coords,n={schema:"iglu:com.snowplowanalytics.snowplow/geolocation_context/jsonschema/1-1-0",data:{latitude:t.latitude,longitude:t.longitude,latitudeLongitudeAccuracy:t.accuracy,altitude:t.altitude,altitudeAccuracy:t.altitudeAccuracy,bearing:t.heading,speed:t.speed,timestamp:Math.round(e.timestamp)}};be.push(n)}))}function We(e,t){return(e||[]).concat(t?t():[])}function Qe(e,t,n,r){Ce(),Ae&&(ke&&null!=s.pageViewId||(s.pageViewId=dt.v4())),Ae=!0,q=E.title,l=e;var a=tt.fixupTitle(l||q);j.trackPageView(Te(m||F),a,Te(h||z),Re(We(t,n)),r);var o=new Date;if(H&&!ue){ue=!0;var i={update:function(){if("undefined"!=typeof window&&"function"==typeof window.addEventListener){var e=!1,t=Object.defineProperty({},"passive",{get:function(){e=!0}}),n=function(){};window.addEventListener("testPassiveEventSupport",n,t),window.removeEventListener("testPassiveEventSupport",n,t),i.hasSupport=e}}};i.update();var c="onwheel"in document.createElement("div")?"wheel":void 0!==document.onmousewheel?"mousewheel":"DOMMouseScroll";Object.prototype.hasOwnProperty.call(i,"hasSupport")?tt.addEventListener(E,c,Le,{passive:!0}):tt.addEventListener(E,c,Le),Ne(),tt.addEventListener(E,"click",Le),tt.addEventListener(E,"mouseup",Le),tt.addEventListener(E,"mousedown",Le),tt.addEventListener(E,"mousemove",Le),tt.addEventListener(L,"scroll",De),tt.addEventListener(E,"keypress",Le),tt.addEventListener(E,"keydown",Le),tt.addEventListener(E,"keyup",Le),tt.addEventListener(L,"resize",Le),tt.addEventListener(L,"focus",Le),tt.addEventListener(L,"blur",Le),p=o.getTime(),clearInterval(u),u=setInterval(function(){var e=new Date;p+d>e.getTime()&&f<e.getTime()&&function(e){Ce();var t=E.title;t!==q&&(q=t,l=null);j.trackPagePing(Te(m||F),tt.fixupTitle(l||q),Te(h||z),Fe(g),Fe(y),Fe(_),Fe(b),Re(e)),Ne()}(We(t,n))},d)}}function $e(e,t){return""!==e?e+t.charAt(0).toUpperCase()+t.slice(1):t}function Ye(t){var e,n,r,a=["","webkit","ms","moz"];if(!i)for(n=0;n<a.length;n++){if(E[$e(r=a[n],"hidden")]){"prerender"===E[$e(r,"visibilityState")]&&(e=!0);break}if(!1===E[$e(r,"hidden")])break}e?tt.addEventListener(E,r+"visibilitychange",function e(){E.removeEventListener(r+"visibilitychange",e,!1),t()}):t()}function Ze(){I=T?O:P}return Ee(),C="none"!=ae&&!!Ie("ses"),(S=Ge())[1]?w=S[1]:(w=dt.v4(),S[1]=w),k=S[6],C||(S[3]++,k=dt.v4(),S[6]=k,S[5]=S[4]),"none"!=ae&&(ze(),S[4]=Math.round((new Date).getTime()/1e3),S.shift(),Ue.apply(null,S)),r.crossDomainLinker&&je(r.crossDomainLinker),O.getDomainSessionIndex=function(){return de},O.getPageViewId=function(){return He()},O.newSession=function(){var e=Math.round((new Date).getTime()/1e3),t=(Ie("ses"),Ge()),n=t[0],r=t[1],a=t[2],o=t[3],i=t[4],c=t[5],s=t[6];"0"===n?(k=s,"none"!=ae&&(o++,c=i,k=dt.v4()),de=o,ze()):(k=dt.v4(),de++),"none"!=ae&&(Ue(r,a,de,e,c,k),ze()),le=(new Date).getTime()},O.getCookieName=function(e){return Pe(e)},O.getUserId=function(){return A},O.getDomainUserId=function(){return Ge()[1]},O.getDomainUserInfo=function(){return Ge()},O.getUserFingerprint=function(){return ce},O.setAppId=function(e){tt.warn('setAppId is deprecated. Instead add an "appId" field to the argmap argument of newTracker.'),j.setAppId(e)},O.setReferrerUrl=function(e){h=e},O.setCustomUrl=function(e){var t,n,r;Ce(),t=F,m=Oe(n=e)?n:"/"===n.slice(0,1)?Oe(t)+"://"+tt.getHostName(t)+n:(0<=(r=(t=Te(t)).indexOf("?"))&&(t=t.slice(0,r)),(r=t.lastIndexOf("/"))!==t.length-1&&(t=t.slice(0,r+1)),t+n)},O.setDocumentTitle=function(e){q=E.title,l=e},O.discardHashTag=function(e){o=e},O.setCookieNamePrefix=function(e){tt.warn('setCookieNamePrefix is deprecated. Instead add a "cookieName" field to the argmap argument of newTracker.'),V=e},O.setCookieDomain=function(e){tt.warn('setCookieDomain is deprecated. Instead add a "cookieDomain" field to the argmap argument of newTracker.'),K=tt.fixupDomain(e),Ee()},O.setCookiePath=function(e){J=e,Ee()},O.setVisitorCookieTimeout=function(e){$=e},O.setSessionCookieTimeout=function(e){tt.warn('setSessionCookieTimeout is deprecated. Instead add a "sessionCookieTimeout" field to the argmap argument of newTracker.'),Y=e},O.setUserFingerprintSeed=function(e){tt.warn('setUserFingerprintSeed is deprecated. Instead add a "userFingerprintSeed" field to the argmap argument of newTracker.'),Z=e,ce=at.detectSignature(Z)},O.enableUserFingerprint=function(e){tt.warn('enableUserFingerprintSeed is deprecated. Instead add a "userFingerprint" field to the argmap argument of newTracker.'),e||(ce="")},O.respectDoNotTrack=function(e){tt.warn('This usage of respectDoNotTrack is deprecated. Instead add a "respectDoNotTrack" field to the argmap argument of newTracker.');var t=D.doNotTrack||D.msDoNotTrack;Q=e&&("yes"===t||"1"===t)},O.crossDomainLinker=function(e){je(e)},O.enableLinkClickTracking=function(e,t,n,r){s.hasLoaded?(he.configureLinkClickTracking(e,t,n,r),he.addClickListeners()):s.registeredOnLoadHandlers.push(function(){he.configureLinkClickTracking(e,t,n,r),he.addClickListeners()})},O.refreshLinkClickTracking=function(){s.hasLoaded?he.addClickListeners():s.registeredOnLoadHandlers.push(function(){he.addClickListeners()})},O.enableActivityTracking=function(e,t){e===parseInt(e,10)&&t===parseInt(t,10)?(H=!0,f=(new Date).getTime()+1e3*e,d=1e3*t):tt.warn("Activity tracking not enabled, please provide integer values for minimumVisitLength and heartBeatDelay.")},O.updatePageActivity=function(){Le()},O.enableFormTracking=function(e,t){s.hasLoaded?(me.configureFormTracking(e),me.addFormListeners(t)):s.registeredOnLoadHandlers.push(function(){me.configureFormTracking(e),me.addFormListeners(t)})},O.killFrame=function(){L.location!==L.top.location&&(L.top.location=L.location)},O.redirectFile=function(e){"file:"===L.location.protocol&&(L.location=e)},O.setOptOutCookie=function(e){v=e},O.setCountPreRendered=function(e){i=e},O.setUserId=function(e){A=e},O.identifyUser=function(e){setUserId(e)},O.setUserIdFromLocation=function(e){Ce(),A=tt.fromQuerystring(e,F)},O.setUserIdFromReferrer=function(e){Ce(),A=tt.fromQuerystring(e,z)},O.setUserIdFromCookie=function(e){A=rt.cookie(e)},O.setCollectorCf=function(e){a=qe(e+".cloudfront.net")},O.setCollectorUrl=function(e){a=qe(e)},O.setPlatform=function(e){tt.warn('setPlatform is deprecated. Instead add a "platform" field to the argmap argument of newTracker.'),j.setPlatform(e)},O.encodeBase64=function(e){tt.warn('This usage of encodeBase64 is deprecated. Instead add an "encodeBase64" field to the argmap argument of newTracker.'),j.setBase64Encoding(e)},O.flushBuffer=function(){ge.executeQueue()},O.enableGeolocationContext=Je,O.trackPageView=function(e,t,n,r){Ye(function(){Qe(e,t,n,r)})},O.trackStructEvent=function(e,t,n,r,a,o,i){Ye(function(){j.trackStructEvent(e,t,n,r,a,Re(o),i)})},O.trackSelfDescribingEvent=function(e,t,n){Ye(function(){j.trackSelfDescribingEvent(e,Re(t),n)})},O.trackUnstructEvent=function(e,t,n){Ye(function(){j.trackSelfDescribingEvent(e,Re(t),n)})},O.addTrans=function(e,t,n,r,a,o,i,c,s,u,l){pe.transaction={orderId:e,affiliation:t,total:n,tax:r,shipping:a,city:o,state:i,country:c,currency:s,context:u,tstamp:l}},O.addItem=function(e,t,n,r,a,o,i,c,s){pe.items.push({orderId:e,sku:t,name:n,category:r,price:a,quantity:o,currency:i,context:c,tstamp:s})},O.trackTrans=function(){Ye(function(){var e,t,n,r,a,o,i,c,s,u,l,f,d,p,h,m,v,g,y,_;e=pe.transaction.orderId,t=pe.transaction.affiliation,n=pe.transaction.total,r=pe.transaction.tax,a=pe.transaction.shipping,o=pe.transaction.city,i=pe.transaction.state,c=pe.transaction.country,s=pe.transaction.currency,u=pe.transaction.context,l=pe.transaction.tstamp,j.trackEcommerceTransaction(e,t,n,r,a,o,i,c,s,Re(u),l);for(var b=0;b<pe.items.length;b++){var w=pe.items[b];f=w.orderId,d=w.sku,p=w.name,h=w.category,m=w.price,v=w.quantity,g=w.currency,y=w.context,_=w.tstamp,j.trackEcommerceTransactionItem(f,d,p,h,m,v,g,Re(y),_)}pe={transaction:{},items:[]}})},O.trackLinkClick=function(e,t,n,r,a,o,i){Ye(function(){j.trackLinkClick(e,t,n,r,a,Re(o),i)})},O.trackAdImpression=function(e,t,n,r,a,o,i,c,s,u){Ye(function(){j.trackAdImpression(e,t,n,r,a,o,i,c,Re(s),u)})},O.trackAdClick=function(e,t,n,r,a,o,i,c,s,u,l){Ye(function(){j.trackAdClick(e,t,n,r,a,o,i,c,s,Re(u),l)})},O.trackAdConversion=function(e,t,n,r,a,o,i,c,s,u,l){Ye(function(){j.trackAdConversion(e,t,n,r,a,o,i,c,s,Re(u),l)})},O.trackSocialInteraction=function(e,t,n,r,a){Ye(function(){j.trackSocialInteraction(e,t,n,Re(r),a)})},O.trackAddToCart=function(e,t,n,r,a,o,i,c){Ye(function(){j.trackAddToCart(e,t,n,r,a,o,Re(i),c)})},O.trackRemoveFromCart=function(e,t,n,r,a,o,i,c){Ye(function(){j.trackRemoveFromCart(e,t,n,r,a,o,Re(i),c)})},O.trackSiteSearch=function(e,t,n,r,a,o){Ye(function(){j.trackSiteSearch(e,t,n,r,Re(a),o)})},O.trackTiming=function(e,t,n,r,a,o){Ye(function(){j.trackSelfDescribingEvent({schema:"iglu:com.snowplowanalytics.snowplow/timing/jsonschema/1-0-0",data:{category:e,variable:t,timing:n,label:r}},Re(a),o)})},O.trackConsentWithdrawn=function(e,t,n,r,a,o,i){Ye(function(){j.trackConsentWithdrawn(e,t,n,r,a,Re(o),i)})},O.trackConsentGranted=function(e,t,n,r,a,o,i){Ye(function(){j.trackConsentGranted(e,t,n,r,a,Re(o),i)})},O.trackEnhancedEcommerceAction=function(e,t,n){var r=we.concat(t||[]);we.length=0,Ye(function(){j.trackSelfDescribingEvent({schema:"iglu:com.google.analytics.enhanced-ecommerce/action/jsonschema/1-0-0",data:{action:e}},Re(r),n)})},O.addEnhancedEcommerceActionContext=function(e,t,n,r,a,o,i,c,s,u){we.push({schema:"iglu:com.google.analytics.enhanced-ecommerce/actionFieldObject/jsonschema/1-0-0",data:{id:e,affiliation:t,revenue:tt.parseFloat(n),tax:tt.parseFloat(r),shipping:tt.parseFloat(a),coupon:o,list:i,step:tt.parseInt(c),option:s,currency:u}})},O.addEnhancedEcommerceImpressionContext=function(e,t,n,r,a,o,i,c,s){we.push({schema:"iglu:com.google.analytics.enhanced-ecommerce/impressionFieldObject/jsonschema/1-0-0",data:{id:e,name:t,list:n,brand:r,category:a,variant:o,position:tt.parseInt(i),price:tt.parseFloat(c),currency:s}})},O.addEnhancedEcommerceProductContext=function(e,t,n,r,a,o,i,c,s,u,l){we.push({schema:"iglu:com.google.analytics.enhanced-ecommerce/productFieldObject/jsonschema/1-0-0",data:{id:e,name:t,list:n,brand:r,category:a,variant:o,price:tt.parseFloat(i),quantity:tt.parseInt(c),coupon:s,position:tt.parseInt(u),currency:l}})},O.addEnhancedEcommercePromoContext=function(e,t,n,r,a){we.push({schema:"iglu:com.google.analytics.enhanced-ecommerce/promoFieldObject/jsonschema/1-0-0",data:{id:e,name:t,creative:n,position:r,currency:a}})},O.addGlobalContexts=function(e){j.addGlobalContexts(e)},O.removeGlobalContexts=function(e){j.removeGlobalContexts(e)},O.clearGlobalContexts=function(){j.clearGlobalContexts()},O.enableErrorTracking=function(e,t){ve.enableErrorTracking(e,t,Re())},O.trackError=function(e,t,n,r,a,o){var i=Re(o);ve.trackError(e,t,n,r,a,i)},O.preservePageViewId=function(){ke=!0},O.setDebug=function(e){T=Boolean(e).valueOf(),Ze()},P=ft(O),Ze(),I}}()},{"./errors":162,"./forms":163,"./guard":164,"./lib/detectors":167,"./lib/helpers":168,"./lib/proxies":169,"./links":170,"./out_queue":171,"browser-cookie-lite":1,"lodash/forEach":123,"lodash/map":143,sha1:154,"snowplow-tracker-core":155,uuid:161}]},{},[166]);
diff --git a/yarn.lock b/yarn.lock
index b6d31ea08e6..dddf01414b2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -693,6 +693,13 @@
eslint-plugin-promise "^4.1.1"
eslint-plugin-vue "^5.0.0"
+"@gitlab/eslint-plugin-i18n@^1.0.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin-i18n/-/eslint-plugin-i18n-1.1.0.tgz#e494d599e644ce3a094ea85f87dbbda41a924c5e"
+ integrity sha512-Cwm7sLtQnUDqvxE9Ez8UMslyosPCpMVLxBnFb+2n6QcBZmXRao4aNSVRkmlsDZYgYegWhOGn3Qq3MLy4BSqauQ==
+ dependencies:
+ requireindex "~1.1.0"
+
"@gitlab/svgs@^1.63.0":
version "1.63.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.63.0.tgz#9dd544026d203e4ce6efed72b05db68f710c4d49"
@@ -9439,6 +9446,11 @@ require-uncached@^1.0.3:
caller-path "^0.1.0"
resolve-from "^1.0.0"
+requireindex@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162"
+ integrity sha1-5UBLgVV+91225JxacgBIk/4D4WI=
+
requires-port@1.x.x, requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"